UNPKG

@talend/react-forms

Version:

React forms library based on json schema form.

287 lines (285 loc) 9.75 kB
import { Component } from 'react'; import { withTranslation } from 'react-i18next'; import PropTypes from 'prop-types'; import { I18N_DOMAIN_FORMS } from '../../../constants'; import getDefaultT from '../../../translate'; import { generateDescriptionId, generateErrorId } from '../../Message/generateId'; import callTrigger from '../../trigger'; import { extractDataAttributes } from '../../utils/properties'; import FieldTemplate from '../FieldTemplate'; import { DID_MOUNT } from './constants'; import { Datalist as DataListComponent } from "@talend/react-components"; import { get, has, omit } from "lodash"; import { jsx as _jsx } from "react/jsx-runtime"; export function escapeRegexCharacters(str) { return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } const PROPS_TO_OMIT = ['schema', 'errorMessage', 'errors', 'isValid', 'onChange', 'onFinish', 'onTrigger', 'properties', 'resolveName']; class Datalist extends 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: 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 || get(this.props, 'schema.options.titleMap') || get(this.props, 'schema.titleMap') || []; const isMultiSection = 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 has(this.state, 'titleMap') || has(this.props, 'schema.options.titleMap') || 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 = 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) { callTrigger(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 = omit(this.props, PROPS_TO_OMIT); const descriptionId = generateDescriptionId(this.props.id); const errorId = generateErrorId(this.props.id); return /*#__PURE__*/_jsx(FieldTemplate, { 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__*/_jsx(DataListComponent, { ...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: 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}`, ...extractDataAttributes(this.props.schema) } }) }); } } Datalist.displayName = 'Datalist field'; Datalist.defaultProps = { resolveName: value => value, value: '', t: getDefaultT() }; if (process.env.NODE_ENV !== 'production') { Datalist.propTypes = { id: PropTypes.string, isValid: PropTypes.bool, errorMessage: PropTypes.string, onChange: PropTypes.func.isRequired, onFinish: PropTypes.func.isRequired, onTrigger: PropTypes.func, errors: PropTypes.object, properties: PropTypes.object, resolveName: PropTypes.func, schema: PropTypes.shape({ schema: PropTypes.shape({ enum: PropTypes.array, type: PropTypes.string }), triggers: PropTypes.arrayOf(PropTypes.shape({ onEvent: PropTypes.string })), autoFocus: PropTypes.bool, dataFeature: PropTypes.string, description: PropTypes.string, disabled: PropTypes.bool, placeholder: PropTypes.string, readOnly: PropTypes.bool, required: PropTypes.bool, restricted: PropTypes.bool, className: PropTypes.string, title: PropTypes.string, labelProps: PropTypes.object, titleMap: PropTypes.arrayOf(PropTypes.shape({ name: PropTypes.string.isRequired, value: PropTypes.string.isRequired })), hint: PropTypes.shape({ icon: PropTypes.string, className: PropTypes.string, overlayComponent: PropTypes.oneOfType([PropTypes.node, PropTypes.string]).isRequired, overlayPlacement: PropTypes.string }), options: PropTypes.shape({ isMultiSection: PropTypes.bool, titleMap: PropTypes.arrayOf(PropTypes.shape({ title: PropTypes.string.isRequired, suggestions: PropTypes.arrayOf(PropTypes.shape({ name: PropTypes.string.isRequired, value: PropTypes.string.isRequired })) })) }) }), value: PropTypes.string, valueIsUpdating: PropTypes.bool, t: PropTypes.func, initialCheckValue: PropTypes.bool, missingValueErrorMessage: PropTypes.string }; } export default withTranslation(I18N_DOMAIN_FORMS)(Datalist); //# sourceMappingURL=Datalist.component.js.map