Skip to content

Commit

Permalink
HBW-97 Add 'required' option for string inputs
Browse files Browse the repository at this point in the history
* Use `nullable: false` in select/select_table for the same purposes
  • Loading branch information
TimAle committed Jan 17, 2019
1 parent 51fe7a1 commit 5f6a8f9
Show file tree
Hide file tree
Showing 9 changed files with 219 additions and 39 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ v1.6.0 [unreleased]
- [#213](https://github.com/latera/homs/pull/213) Add payload to widget's requests.
- [#218](https://github.com/latera/homs/pull/218) Pass initial BP variables to widget initial payload.
- [#223](https://github.com/latera/homs/pull/223) Add directories for postgresql-client.
- [#227](https://github.com/latera/homs/pull/227) Add `required` option for string inputs. Use `nullable: false` in select/select_table for the same reason.

### Bugfixes
- [#194](https://github.com/latera/homs/pull/194) Set limit on the number of choices in lookup select from sql requests.
Expand Down
2 changes: 1 addition & 1 deletion hbw/app/javascript/packs/hbw/components/form.js.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ modulejs.define('HBWForm', ['React', 'jQuery', 'HBWError', 'HBWFormDatetime',
},

isFormValid () {
return this.getElement().find('input[type="text"].invalid').length === 0;
return this.getElement().find('.invalid').length === 0;
},

fileListPresent (fields) {
Expand Down
66 changes: 62 additions & 4 deletions hbw/app/javascript/packs/hbw/components/form/select.js.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

import Select from 'react-select';
import AsyncSelect from 'react-select/lib/Async';
import { withDeleteIf, withSelect, compose } from '../helpers';
import Tooltip from 'tooltip.js';
import { withDeleteIf, withSelect, withCallbacks, compose } from '../helpers';

modulejs.define('HBWFormSelect',
['React'],
Expand All @@ -19,13 +20,24 @@ modulejs.define('HBWFormSelect',
};
},

componentDidMount () {
this.tooltip = new Tooltip(this.select, {
title: this.props.env.translator('errors.field_is_required'),
container: this.tooltipContainer,
trigger: 'manual',
placement: 'top'
});

this.validateOnSubmit();
},

render () {
const opts = {
name: this.props.name,
options: this.buildOptions(),
defaultValue: this.getDefaultValue(),
placeholder: this.props.params.placeholder || '',
isClearable: this.props.params.nullable || this.props.value === null,
isClearable: this.props.params.nullable,
isDisabled: this.props.params.editable === false,
noOptionsMessage: this.noOptionsMessage,
loadingMessage: this.loadingMessage,
Expand All @@ -52,6 +64,11 @@ modulejs.define('HBWFormSelect',
selectErrorMessageCss += ' hidden';
}

const errorTooltip = <div
ref={(t) => { this.tooltipContainer = t; }}
className={`${!this.state.valid && 'tooltip-red'}`}
/>;

let formGroupCss = 'form-group';

if (this.state.error) {
Expand All @@ -61,12 +78,53 @@ modulejs.define('HBWFormSelect',
return <div className={cssClass} title={tooltip}>
<span className={labelCss}>{label}</span>
<div className={selectErrorMessageCss}>{selectErrorMessage}</div>
<div className={formGroupCss}>
<div className={formGroupCss} ref={(i) => { this.select = i; }}>
{this.selectComponent(opts)}
</div>
{errorTooltip}
</div>;
},

validateOnSubmit () {
this.props.bind('hbw:validate-form', this.onFormSubmit);
},

onFormSubmit () {
const el = this.select;

if (this.isValid()) {
el.classList.remove('invalid');
} else {
el.classList.add('invalid');

this.setValidationState();
this.controlValidationTooltip(this.isValid());
this.props.trigger('hbw:form-submitting-failed');
}
},

isValid () {
return this.props.params.nullable ? true : this.isFilled();
},

isFilled () {
const { value } = this.state;

return value !== null && value !== undefined && value.length > 0;
},

setValidationState () {
this.setState({ valid: this.isValid() });
},

controlValidationTooltip (toHide) {
if (toHide) {
this.tooltip.hide();
} else {
this.tooltip.show();
}
},

customStyles () {
const bgColor = (state) => {
if (state.isFocused) {
Expand Down Expand Up @@ -237,5 +295,5 @@ modulejs.define('HBWFormSelect',
}
});

return compose(withSelect, withDeleteIf)(FormSelect);
return compose(withSelect, withDeleteIf, withCallbacks)(FormSelect);
});
72 changes: 66 additions & 6 deletions hbw/app/javascript/packs/hbw/components/form/select_table.js.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { withDeleteIf, withSelect, compose } from '../helpers';
import Tooltip from 'tooltip';
import { withDeleteIf, withSelect, withCallbacks, compose } from '../helpers';

modulejs.define('HBWFormSelectTable',
['React'],
Expand All @@ -13,24 +14,40 @@ modulejs.define('HBWFormSelectTable',
return {
value: this.props.params.current_value,
choices: this.getChoices(),
error: (!this.props.hasValueInChoices(value) && value) || this.props.missFieldInVariables()
error: (!this.props.hasValueInChoices(value) && value) || this.props.missFieldInVariables(),
valid: true
};
},

componentDidMount () {
this.tooltip = new Tooltip(this.label, {
title: this.props.env.translator('errors.field_is_required'),
container: this.tooltipContainer,
trigger: 'manual',
placement: 'bottom'
});

this.validateOnSubmit();
},

render () {
let cssClass = this.props.params.css_class;

if (this.props.hidden) {
cssClass += ' hidden';
}

const { tooltip } = this.props.params;
const { label } = this.props.params;
const { tooltip, label } = this.props.params;
const labelCss = this.props.params.label_css;

const selectErrorMessage = this.props.env.translator('errors.field_not_defined_in_bp',
{ field_name: this.props.name });

const errorTooltip = <div
ref={(t) => { this.tooltipContainer = t; }}
className={`${!this.state.valid && 'tooltip-red'}`}
/>;

let selectErrorMessageCss = 'alert alert-danger';
if (!this.props.missFieldInVariables()) {
selectErrorMessageCss += ' hidden';
Expand All @@ -47,7 +64,7 @@ modulejs.define('HBWFormSelectTable',
}

return <div className={cssClass} title={tooltip}>
<span className={labelCss}>{label}</span>
<span className={labelCss} ref={(i) => { this.label = i; }}>{label}</span>
<div className={selectErrorMessageCss}>{selectErrorMessage}</div>
<div className={formGroupCss}>
<table className={tableCss}>
Expand All @@ -59,11 +76,52 @@ modulejs.define('HBWFormSelectTable',
<tbody>
{this.buildTableBody(this.state.choices, this.props.name, this.state.value)}
</tbody>
{errorTooltip}
</table>
</div>
</div>;
},

validateOnSubmit () {
this.props.bind('hbw:validate-form', this.onFormSubmit);
},

onFormSubmit () {
const el = this.label;

if (this.isValid()) {
el.classList.remove('invalid');
} else {
el.classList.add('invalid');

this.setValidationState();
this.controlValidationTooltip(this.isValid());
this.props.trigger('hbw:form-submitting-failed');
}
},

isValid () {
return this.props.params.nullable ? true : this.isFilled();
},

isFilled () {
const { value } = this.state;

return value !== null && value !== undefined && value.length > 0;
},

setValidationState () {
this.setState({ valid: this.isValid() });
},

controlValidationTooltip (toHide) {
if (toHide) {
this.tooltip.hide();
} else {
this.tooltip.show();
}
},

addNullChoice (choices) {
let hasNullValue = false;

Expand Down Expand Up @@ -99,6 +157,8 @@ modulejs.define('HBWFormSelectTable',
}

this.setState({ value: event.target.parentElement.getElementsByTagName('input')[0].value });
this.setValidationState();
this.controlValidationTooltip(this.isValid());
},

buildTableBody (choices, name, value) {
Expand Down Expand Up @@ -168,5 +228,5 @@ modulejs.define('HBWFormSelectTable',
}
});

return compose(withSelect, withDeleteIf)(FormSelectTable);
return compose(withSelect, withDeleteIf, withCallbacks)(FormSelectTable);
});
50 changes: 36 additions & 14 deletions hbw/app/javascript/packs/hbw/components/form/string.js.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import CustomFormatter from 'formatter';
import Tooltip from 'tooltip';
import { withCallbacks, withDeleteIf, compose } from '../helpers';

modulejs.define('HBWFormString', ['React', 'jQuery'], (React, jQuery) => {
modulejs.define('HBWFormString', ['React'], (React) => {
const FormString = React.createClass({
displayName: 'HBWFormString',

Expand Down Expand Up @@ -44,6 +44,11 @@ modulejs.define('HBWFormString', ['React', 'jQuery'], (React, jQuery) => {
readOnly: this.props.params.editable === false
};

const errorTooltip = <div
ref={(t) => { this.tooltipContainer = t; }}
className={`${!this.state.valid && 'tooltip-red'}`}
/>;

let inputCSS = this.props.params.css_class;

if (this.props.hidden) {
Expand All @@ -57,18 +62,18 @@ modulejs.define('HBWFormString', ['React', 'jQuery'], (React, jQuery) => {
ref={(i) => { this.input = i; }}
className={`form-control ${!this.state.valid && ' invalid'}`}
value={this.state.visualValue} />
<div ref={(t) => { this.tooltipContainer = t; }} className={`${!this.state.valid && 'tooltip-red'}`}></div>
{!opts.readOnly && <input name={this.props.name} value={this.state.value} type="hidden" />}
{errorTooltip}
</div>
</div>;
},

componentDidMount () {
this.tooltip = new Tooltip(this.input, {
title: this.props.params.message,
title: this.props.params.message || this.props.env.translator('errors.field_is_required'),
container: this.tooltipContainer,
trigger: 'manual',
placement: 'bottom'
placement: 'top'
});

this.validateOnSubmit();
Expand All @@ -94,17 +99,17 @@ modulejs.define('HBWFormString', ['React', 'jQuery'], (React, jQuery) => {
},

getElement () {
return jQuery(this.input);
return this.input;
},

onFormSubmit () {
if (this.validationRequired()) {
const $el = this.getElement();
const el = this.getElement();

if (this.isValid()) {
$el.removeClass('invalid');
el.classList.remove('invalid');
} else {
$el.addClass('invalid');
el.classList.add('invalid');

this.setValidationState();
this.controlValidationTooltip(this.isValid());
Expand All @@ -114,7 +119,7 @@ modulejs.define('HBWFormString', ['React', 'jQuery'], (React, jQuery) => {
},

onLoadValidation () {
if (this.validationRequired() && !!this.state.value) {
if (this.validationRequired() && this.isFilled()) {
this.controlValidationTooltip(this.isValid());
this.setValidationState();
}
Expand All @@ -139,19 +144,21 @@ modulejs.define('HBWFormString', ['React', 'jQuery'], (React, jQuery) => {
},

onKeyDown (event) {
if (this.validationRequired()) {
if (this.props.params.pattern) {
if ((event.keyCode === 8) || (event.keyCode === 46)) {
event.preventDefault();

this.updateValue(event.target, true);
} else {
this.updateValue(event.target, false);
}

this.runValidation();
} else {
this.updateValue(event.target, true);
}

if (this.validationRequired()) {
this.runValidation();
}
},

onBlur () {
Expand Down Expand Up @@ -185,11 +192,26 @@ modulejs.define('HBWFormString', ['React', 'jQuery'], (React, jQuery) => {
},

isValid () {
return (this.state.value || '').search(new RegExp(this.props.params.regex)) >= 0;
const requiredOK = this.props.params.required ? this.isFilled() : true;
const regexOK = this.props.params.regex ? this.regexMatched() : true;

return requiredOK && regexOK;
},

validationRequired () {
return !!this.props.params.regex || !!this.props.params.pattern;
const { required, regex } = this.props.params;

return required || !!regex;
},

isFilled () {
const { value } = this.state;

return value !== null && value !== undefined && value.length > 0;
},

regexMatched () {
return (this.state.value || '').search(new RegExp(this.props.params.regex)) >= 0;
},

buildVisualAndHiddenValues (extractValueRegexp, valueParts, pattern, nextVal) {
Expand Down
1 change: 1 addition & 0 deletions hbw/config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ en:
cannot_obtain_available_actions: Cannot obtain available actions
cannot_start_process: Cannot start process
file_list_field_required: Field of "file_list" type with name "homsOrderDataFileList" required
field_is_required: Field is required

components:
select:
Expand Down
1 change: 1 addition & 0 deletions hbw/config/locales/ru.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ ru:
cannot_obtain_available_actions: Не удалось получить список доступных действий
cannot_start_process: Не удалось начать процесс
file_list_field_required: Для загрузки файлов необходимо добавить поле типа "file_list" с именем "homsOrderDataFileList"
field_is_required: Обязательное поле

components:
select:
Expand Down
Loading

0 comments on commit 5f6a8f9

Please sign in to comment.