@talend/react-forms
Version:
React forms library based on json schema form.
295 lines (292 loc) • 10.8 kB
JavaScript
"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