UNPKG

@talend/react-forms

Version:

React forms library based on json schema form.

295 lines (292 loc) 10.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; exports.escapeRegexCharacters = escapeRegexCharacters; var _react = require("react"); var _reactI18next = require("react-i18next"); var _propTypes = _interopRequireDefault(require("prop-types")); var _constants = require("../../../constants"); var _translate = _interopRequireDefault(require("../../../translate")); var _generateId = require("../../Message/generateId"); var _trigger = _interopRequireDefault(require("../../trigger")); var _properties = require("../../utils/properties"); var _FieldTemplate = _interopRequireDefault(require("../FieldTemplate")); var _constants2 = require("./constants"); var _reactComponents = require("@talend/react-components"); var _lodash = require("lodash"); var _jsxRuntime = require("react/jsx-runtime"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function escapeRegexCharacters(str) { return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } const PROPS_TO_OMIT = ['schema', 'errorMessage', 'errors', 'isValid', 'onChange', 'onFinish', 'onTrigger', 'properties', 'resolveName']; class Datalist extends _react.Component { constructor(props) { super(props); this.state = { isValid: true }; this.onChange = this.onChange.bind(this); this.getTitleMap = this.getTitleMap.bind(this); this.callTrigger = this.callTrigger.bind(this); this.onTrigger = this.onTrigger.bind(this); this.checkValueInTitleMap = this.checkValueInTitleMap.bind(this); } componentDidMount() { this.callTrigger({ type: _constants2.DID_MOUNT }); if (this.props.initialCheckValue) { this.checkValueInTitleMap(); } } /** * On change callback * We call onFinish to trigger validation on datalist item selection * @param event * @param payload */ onChange(event, payload) { if (this.props.initialCheckValue) { this.setState({ isValid: true, errorMessage: undefined }); } let mergedSchema = this.props.schema; // with the possibility to have async suggestions, on restricted values inputs // the validation doesn't have the enum list as it is not in the jsonSchema // so we rebuild it with current titleMap from async call if (mergedSchema.restricted && !mergedSchema.schema.enum) { mergedSchema = { ...mergedSchema, schema: { ...mergedSchema.schema, enum: this.getTitleMap().map(entry => entry.value) } }; } let payloadWithSchema = { ...payload, schema: { ...mergedSchema } }; if (this.hasTitleMap()) { payloadWithSchema = { ...payloadWithSchema, schema: { ...payloadWithSchema.schema, titleMap: this.getTitleMap() } }; } this.callTrigger(event); this.props.onChange(event, payloadWithSchema); this.props.onFinish(event, payloadWithSchema); } onTrigger(event, trigger) { return this.props.onTrigger(event, { trigger, schema: this.props.schema, errors: this.props.errors, properties: this.props.properties }); } getTitleMap() { const titleMap = this.state.titleMap || (0, _lodash.get)(this.props, 'schema.options.titleMap') || (0, _lodash.get)(this.props, 'schema.titleMap') || []; const isMultiSection = (0, _lodash.get)(this.props, 'schema.options.isMultiSection', false); const restricted = this.props.schema.restricted; const type = this.props.schema.schema.type; const propsValue = this.props.value; if (restricted || !propsValue) { return titleMap; } let titleMapFind = titleMap; const isMultiple = type === 'array'; const values = isMultiple ? propsValue : [propsValue]; if (isMultiSection) { titleMapFind = titleMap.reduce((prev, current) => { prev.push(...current.suggestions); return prev; }, []); } const additionalOptions = values.filter(value => !titleMapFind.find(option => option.value === value)).map(value => this.addCustomValue(value, isMultiSection)).reduce((acc, titleMapEntry) => { acc.push(titleMapEntry); return acc; }, []); return titleMap.concat(additionalOptions); } hasTitleMap() { return (0, _lodash.has)(this.state, 'titleMap') || (0, _lodash.has)(this.props, 'schema.options.titleMap') || (0, _lodash.has)(this.props, 'schema.titleMap'); } /** * checkValueInTitleMap checks if the current value exists in the given titleMap. * If the value is not found it sets a new state for the 'isValid' and * 'errorMessage' values. */ checkValueInTitleMap() { if (this.hasTitleMap() && this.props.schema.restricted) { const isMultiSection = (0, _lodash.get)(this.props, 'schema.options.isMultiSection', false); const titleMap = this.getTitleMap(); if (!isMultiSection) { if (!titleMap.some(el => el.value === this.props.value)) { this.setState({ isValid: false, errorMessage: this.props.missingValueErrorMessage }); } } else { const found = titleMap.some(tm => { if (tm.suggestions && tm.suggestions.length) { return tm.suggestions.some(el => el.value === this.props.value); } return false; }); if (!found) { this.setState({ isValid: false, errorMessage: this.props.missingValueErrorMessage }); } } } } addCustomValue(value, isMultiSection) { if (isMultiSection) { return { title: this.props.t('TF_DATALIST_CUSTOM_SECTION', { defaultValue: 'CUSTOM' }), suggestions: [{ name: this.props.resolveName(value), value }] }; } return { name: this.props.resolveName(value), value }; } callTrigger(event) { (0, _trigger.default)(event, { eventNames: [event.type], triggersDefinitions: this.props.schema.triggers, onTrigger: this.onTrigger, onLoading: isLoading => this.setState({ isLoading }), onResponse: data => this.setState(data) }); } render() { const props = (0, _lodash.omit)(this.props, PROPS_TO_OMIT); const descriptionId = (0, _generateId.generateDescriptionId)(this.props.id); const errorId = (0, _generateId.generateErrorId)(this.props.id); return /*#__PURE__*/(0, _jsxRuntime.jsx)(_FieldTemplate.default, { hint: this.props.schema.hint, className: this.props.schema.className, description: this.props.schema.description, descriptionId: descriptionId, errorId: errorId, errorMessage: this.state.errorMessage || this.props.errorMessage, id: this.props.id, isValid: this.state.isValid && this.props.isValid, label: this.props.schema.title, labelProps: this.props.schema.labelProps, required: this.props.schema.required, valueIsUpdating: this.props.valueIsUpdating, children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactComponents.Datalist, { ...props, ...this.state, dataFeature: this.props.schema.dataFeature // eslint-disable-next-line jsx-a11y/no-autofocus , autoFocus: this.props.schema.autoFocus, disabled: this.props.schema.disabled || this.props.valueIsUpdating, multiSection: (0, _lodash.get)(this.props, 'schema.options.isMultiSection', false), onChange: this.onChange, onFocus: this.callTrigger, placeholder: this.props.schema.placeholder, readOnly: this.props.schema.readOnly || false, titleMap: this.getTitleMap(), inputProps: { hasError: !this.props.isValid, 'aria-invalid': !this.props.isValid, 'aria-required': this.props.schema.required, 'aria-describedby': `${descriptionId} ${errorId}`, ...(0, _properties.extractDataAttributes)(this.props.schema) } }) }); } } Datalist.displayName = 'Datalist field'; Datalist.defaultProps = { resolveName: value => value, value: '', t: (0, _translate.default)() }; if (process.env.NODE_ENV !== 'production') { Datalist.propTypes = { id: _propTypes.default.string, isValid: _propTypes.default.bool, errorMessage: _propTypes.default.string, onChange: _propTypes.default.func.isRequired, onFinish: _propTypes.default.func.isRequired, onTrigger: _propTypes.default.func, errors: _propTypes.default.object, properties: _propTypes.default.object, resolveName: _propTypes.default.func, schema: _propTypes.default.shape({ schema: _propTypes.default.shape({ enum: _propTypes.default.array, type: _propTypes.default.string }), triggers: _propTypes.default.arrayOf(_propTypes.default.shape({ onEvent: _propTypes.default.string })), autoFocus: _propTypes.default.bool, dataFeature: _propTypes.default.string, description: _propTypes.default.string, disabled: _propTypes.default.bool, placeholder: _propTypes.default.string, readOnly: _propTypes.default.bool, required: _propTypes.default.bool, restricted: _propTypes.default.bool, className: _propTypes.default.string, title: _propTypes.default.string, labelProps: _propTypes.default.object, titleMap: _propTypes.default.arrayOf(_propTypes.default.shape({ name: _propTypes.default.string.isRequired, value: _propTypes.default.string.isRequired })), hint: _propTypes.default.shape({ icon: _propTypes.default.string, className: _propTypes.default.string, overlayComponent: _propTypes.default.oneOfType([_propTypes.default.node, _propTypes.default.string]).isRequired, overlayPlacement: _propTypes.default.string }), options: _propTypes.default.shape({ isMultiSection: _propTypes.default.bool, titleMap: _propTypes.default.arrayOf(_propTypes.default.shape({ title: _propTypes.default.string.isRequired, suggestions: _propTypes.default.arrayOf(_propTypes.default.shape({ name: _propTypes.default.string.isRequired, value: _propTypes.default.string.isRequired })) })) }) }), value: _propTypes.default.string, valueIsUpdating: _propTypes.default.bool, t: _propTypes.default.func, initialCheckValue: _propTypes.default.bool, missingValueErrorMessage: _propTypes.default.string }; } var _default = exports.default = (0, _reactI18next.withTranslation)(_constants.I18N_DOMAIN_FORMS)(Datalist); //# sourceMappingURL=Datalist.component.js.map