From f7fc2d05a61ac82fea30d587c599aa232daf9279 Mon Sep 17 00:00:00 2001 From: jsomsanith Date: Thu, 11 May 2017 15:10:41 +0200 Subject: [PATCH] Fieldsets --- packages/forms/src/FieldMessage/index.js | 3 - .../Message/Message.component.js} | 4 +- packages/forms/src/UIForm/Message/index.js | 3 + packages/forms/src/UIForm/UIForm.component.js | 41 +++----- .../src/UIForm/Widget/Widget.component.js | 40 ++++++++ packages/forms/src/UIForm/Widget/index.js | 3 + .../TextField.js => UIForm/fields/Text.js} | 10 +- .../forms/src/UIForm/fieldsets/Fieldset.js | 27 ++++++ .../src/{ => UIForm}/utils/properties.js | 4 + .../src/{ => UIForm}/utils/validation.js | 17 +++- packages/forms/src/UIForm/utils/widgets.js | 10 ++ packages/forms/src/utils/widgets.js | 8 -- .../forms/stories/json/core-fieldset.json | 93 +++++++++++++++++++ 13 files changed, 213 insertions(+), 50 deletions(-) delete mode 100644 packages/forms/src/FieldMessage/index.js rename packages/forms/src/{FieldMessage/FieldMessage.component.js => UIForm/Message/Message.component.js} (82%) create mode 100644 packages/forms/src/UIForm/Message/index.js create mode 100644 packages/forms/src/UIForm/Widget/Widget.component.js create mode 100644 packages/forms/src/UIForm/Widget/index.js rename packages/forms/src/{fields/TextField.js => UIForm/fields/Text.js} (89%) create mode 100644 packages/forms/src/UIForm/fieldsets/Fieldset.js rename packages/forms/src/{ => UIForm}/utils/properties.js (95%) rename packages/forms/src/{ => UIForm}/utils/validation.js (58%) create mode 100644 packages/forms/src/UIForm/utils/widgets.js delete mode 100644 packages/forms/src/utils/widgets.js create mode 100644 packages/forms/stories/json/core-fieldset.json diff --git a/packages/forms/src/FieldMessage/index.js b/packages/forms/src/FieldMessage/index.js deleted file mode 100644 index e4ccd6b9510..00000000000 --- a/packages/forms/src/FieldMessage/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import FieldMessage from './FieldMessage.component'; - -export default FieldMessage; diff --git a/packages/forms/src/FieldMessage/FieldMessage.component.js b/packages/forms/src/UIForm/Message/Message.component.js similarity index 82% rename from packages/forms/src/FieldMessage/FieldMessage.component.js rename to packages/forms/src/UIForm/Message/Message.component.js index e4cd3d17ed5..5e2452fcce6 100644 --- a/packages/forms/src/FieldMessage/FieldMessage.component.js +++ b/packages/forms/src/UIForm/Message/Message.component.js @@ -1,6 +1,6 @@ import React, { PropTypes } from 'react'; -export default function FieldMessage(props) { +export default function Message(props) { const { errorMessage, description, @@ -17,7 +17,7 @@ export default function FieldMessage(props) { null; } -FieldMessage.propTypes = { +Message.propTypes = { errorMessage: PropTypes.string, description: PropTypes.string, isValid: PropTypes.bool, diff --git a/packages/forms/src/UIForm/Message/index.js b/packages/forms/src/UIForm/Message/index.js new file mode 100644 index 00000000000..14b3a36cdf9 --- /dev/null +++ b/packages/forms/src/UIForm/Message/index.js @@ -0,0 +1,3 @@ +import Message from './Message.component'; + +export default Message; diff --git a/packages/forms/src/UIForm/UIForm.component.js b/packages/forms/src/UIForm/UIForm.component.js index a4d4c34805b..bde67b67c75 100644 --- a/packages/forms/src/UIForm/UIForm.component.js +++ b/packages/forms/src/UIForm/UIForm.component.js @@ -1,15 +1,10 @@ import React, { PropTypes } from 'react'; import { reduxForm } from 'redux-form'; +import { merge, validate } from 'talend-json-schema-form-core'; -import { - merge, - sfPath, - validate, -} from 'talend-json-schema-form-core'; - -import validateAll from '../utils/validation'; -import widgets from '../utils/widgets'; -import { getValue, mutateValue } from '../utils/properties'; +import Widget from './Widget'; +import validateAll from './utils/validation'; +import { mutateValue } from './utils/properties'; class UIForm extends React.Component { constructor(props) { @@ -93,24 +88,16 @@ class UIForm extends React.Component { return (
{ - this.state.mergedSchema.map((nextSchema) => { - const { key, type, validationMessage } = nextSchema; - const id = sfPath.name(key, '-', formName); - const { error, valid } = validations[nextSchema.key] || {}; - const errorMessage = validationMessage || (error && error.message); - const Widget = widgets[type]; - return Widget && ( - - ); - }) + this.state.mergedSchema.map((nextSchema, index) => ( + + )) } diff --git a/packages/forms/src/UIForm/Widget/Widget.component.js b/packages/forms/src/UIForm/Widget/Widget.component.js new file mode 100644 index 00000000000..f690f278299 --- /dev/null +++ b/packages/forms/src/UIForm/Widget/Widget.component.js @@ -0,0 +1,40 @@ +import React, { PropTypes } from 'react'; +import { sfPath } from 'talend-json-schema-form-core'; + +import widgets from '../utils/widgets'; +import { getValue } from '../utils/properties'; + +export default function Widget({ formName, onChange, properties, schema, validations }) { + const { key, type, validationMessage } = schema; + const id = sfPath.name(key, '-', formName); + const { error, valid } = validations[key] || {}; + const errorMessage = validationMessage || (error && error.message); + const WidgetImpl = widgets[type]; + return WidgetImpl ? + ( + + ) : null; +} + +Widget.propTypes = { + formName: PropTypes.string, + onChange: PropTypes.func, + schema: PropTypes.shape({ + key: PropTypes.array, + type: PropTypes.string.isRequired, + validationMessage: PropTypes.string, + }).isRequired, + properties: PropTypes.object, // eslint-disable-line react/forbid-prop-types + validations: PropTypes.object, // eslint-disable-line react/forbid-prop-types +}; diff --git a/packages/forms/src/UIForm/Widget/index.js b/packages/forms/src/UIForm/Widget/index.js new file mode 100644 index 00000000000..3317235a36b --- /dev/null +++ b/packages/forms/src/UIForm/Widget/index.js @@ -0,0 +1,3 @@ +import Widget from './Widget.component'; + +export default Widget; diff --git a/packages/forms/src/fields/TextField.js b/packages/forms/src/UIForm/fields/Text.js similarity index 89% rename from packages/forms/src/fields/TextField.js rename to packages/forms/src/UIForm/fields/Text.js index 4f03ff43177..12a885dec73 100644 --- a/packages/forms/src/fields/TextField.js +++ b/packages/forms/src/UIForm/fields/Text.js @@ -1,7 +1,7 @@ import React, { PropTypes } from 'react'; import classNames from 'classnames'; -import FieldMessage from '../FieldMessage'; +import Message from '../Message'; function convertValue(type, value) { if (type === 'number') { @@ -10,7 +10,7 @@ function convertValue(type, value) { return value; } -export default function TextField(props) { +export default function Text(props) { const { id, isValid, errorMessage, onChange, schema, value } = props; const { description, placeholder, readOnly, title, type } = schema; @@ -31,7 +31,7 @@ export default function TextField(props) { value={value} /> - + {title && ({title})} + {items.map((itemSchema, index) => ( + + ))} + + ); +} + +Fieldset.propTypes = { + schema: PropTypes.shape({ + items: PropTypes.array.isRequired, + title: PropTypes.string, + }).isRequired, +}; diff --git a/packages/forms/src/utils/properties.js b/packages/forms/src/UIForm/utils/properties.js similarity index 95% rename from packages/forms/src/utils/properties.js rename to packages/forms/src/UIForm/utils/properties.js index add849a6bbd..0dfd46622f0 100644 --- a/packages/forms/src/utils/properties.js +++ b/packages/forms/src/UIForm/utils/properties.js @@ -4,6 +4,10 @@ * @param {array} key The key chain (array of strings) to access to the value */ export function getValue(properties, key) { + if (!key) { + return undefined; + } + return key.reduce( (accu, nextKey) => accu[nextKey], properties diff --git a/packages/forms/src/utils/validation.js b/packages/forms/src/UIForm/utils/validation.js similarity index 58% rename from packages/forms/src/utils/validation.js rename to packages/forms/src/UIForm/utils/validation.js index 12fbe547eb0..1dabecd2ef7 100644 --- a/packages/forms/src/utils/validation.js +++ b/packages/forms/src/UIForm/utils/validation.js @@ -3,7 +3,7 @@ import { validate } from 'talend-json-schema-form-core'; import { getValue } from './properties'; /** - * Validate values. This supports only 1 level of fields for now + * Validate values. * @param mergedSchema The merged schema. * @param properties The values. * @returns {object} The validation result by field. @@ -11,10 +11,17 @@ import { getValue } from './properties'; export default function validateAll(mergedSchema, properties) { const validations = {}; mergedSchema.forEach((schema) => { - validations[schema.key] = validate( - schema, - getValue(properties, schema.key) - ); + const { key, items } = schema; + if (key) { + validations[key] = validate( + schema, + getValue(properties, key) + ); + } + if (items) { + const subValidations = validateAll(items, properties); + Object.assign(validations, subValidations); + } }); return validations; } diff --git a/packages/forms/src/UIForm/utils/widgets.js b/packages/forms/src/UIForm/utils/widgets.js new file mode 100644 index 00000000000..b7e22b5c36f --- /dev/null +++ b/packages/forms/src/UIForm/utils/widgets.js @@ -0,0 +1,10 @@ +import Fieldset from '../fieldsets/Fieldset'; +import Text from '../fields/Text'; + +const widgets = { + fieldset: Fieldset, + number: Text, + text: Text, +}; + +export default widgets; diff --git a/packages/forms/src/utils/widgets.js b/packages/forms/src/utils/widgets.js deleted file mode 100644 index 69b58159ae2..00000000000 --- a/packages/forms/src/utils/widgets.js +++ /dev/null @@ -1,8 +0,0 @@ -import TextField from '../fields/TextField'; - -const widgets = { - text: TextField, - number: TextField, -}; - -export default widgets; diff --git a/packages/forms/stories/json/core-fieldset.json b/packages/forms/stories/json/core-fieldset.json new file mode 100644 index 00000000000..68c5b22dc29 --- /dev/null +++ b/packages/forms/stories/json/core-fieldset.json @@ -0,0 +1,93 @@ +{ + "jsonSchema": { + "type": "object", + "title": "Comment", + "properties": { + "name": { + "type": "string" + }, + "lastname": { + "type": "string" + }, + "firstname": { + "type": "string" + }, + "age": { + "type": "number" + }, + "nochange": { + "type": "string" + }, + "email": { + "type": "string", + "pattern": "^\\S+@\\S+$" + }, + "comment": { + "type": "string", + "maxLength": 20 + } + }, + "required": [ + "name", + "firstname", + "email", + "comment" + ] + }, + "uiSchema": [ + { + "type": "fieldset", + "title": "My awesome USER form", + "items": [ + { + "key": "name", + "title": "Name" + }, + { + "key": "lastname", + "title": "Last Name (with description)", + "description": "Hint: this is the last name" + }, + { + "key": "firstname", + "title": "First Name (with placeholder)", + "placeholder": "Enter your firstname here" + }, + { + "key": "age", + "title": "Age" + } + ] + }, + { + "type": "fieldset", + "title": "My awesome OTHER form", + "items": [ + { + "key": "email", + "title": "Email (with pattern validation and custom validation message)", + "description": "Email will be used for evil.", + "validationMessage": "Please enter a valid email address, e.g. user@email.com" + }, + { + "key": "nochange", + "title": "Field (read only mode)", + "readOnly": true + }, + { + "key": "comment", + "type": "textarea", + "title": "Comment", + "placeholder": "Make a comment", + "validationMessage": "Don't be greedy!" + } + ] + } + ], + "properties": { + "name": "Chuck Norris", + "nochange": "You can't change that", + "email": "ChuckyFTW@gmail.com", + "comment": "lol" + } +}