Skip to content

Commit 309dc26

Browse files
WIP that doesnt compile :)
1 parent 7ec9032 commit 309dc26

File tree

3 files changed

+199
-27
lines changed

3 files changed

+199
-27
lines changed

packages/forms/src/UIForm/UIForm.component.js

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const TRIGGER_AFTER = 'after';
1111
class UIForm extends React.Component {
1212
constructor(props) {
1313
super(props);
14-
const { jsonSchema, uiSchema, properties } = props.data;
14+
const { jsonSchema, uiSchema, properties } = props;
1515
this.state = {
1616
mergedSchema: merge(jsonSchema, uiSchema),
1717
properties: { ...properties },
@@ -29,13 +29,12 @@ class UIForm extends React.Component {
2929
* @param uiSchema
3030
* @param properties
3131
*/
32-
componentWillReceiveProps({ jsonSchema, uiSchema, properties }) {
33-
if (!jsonSchema || !uiSchema || !properties) {
32+
componentWillReceiveProps({ jsonSchema, uiSchema }) {
33+
if (!jsonSchema || !uiSchema) {
3434
return;
3535
}
3636
this.setState({
3737
mergedSchema: merge(jsonSchema, uiSchema),
38-
properties: { ...properties },
3938
});
4039
}
4140

@@ -119,9 +118,7 @@ class UIForm extends React.Component {
119118
}
120119

121120
render() {
122-
const { formName } = this.props;
123-
const { properties, validations } = this.state;
124-
121+
const { errors, formName, properties } = this.props;
125122
return (
126123
<form onSubmit={this.submit}>
127124
{
@@ -132,7 +129,7 @@ class UIForm extends React.Component {
132129
onChange={this.consolidate}
133130
schema={nextSchema}
134131
properties={properties}
135-
validations={validations}
132+
errors={errors}
136133
/>
137134
))
138135
}
@@ -144,17 +141,12 @@ class UIForm extends React.Component {
144141

145142
if (process.env.NODE_ENV !== 'production') {
146143
UIForm.propTypes = {
147-
/** Form schema configuration */
148-
data: PropTypes.shape({
149-
/** Json schema that specify the data model */
150-
jsonSchema: PropTypes.object,
151-
/** UI schema that specify how to render the fields */
152-
uiSchema: PropTypes.array,
153-
/** Form fields values. Note that it should contains @definitionName for triggers. */
154-
properties: PropTypes.object,
155-
}),
144+
/** The forms errors { [fieldKey]: errorMessage } */
145+
errors: PropTypes.object, // eslint-disable-line react/forbid-prop-types
156146
/** The form name that will be used to create ids */
157147
formName: PropTypes.string,
148+
/** Json schema that specify the data model */
149+
jsonSchema: PropTypes.object, // eslint-disable-line react/forbid-prop-types
158150
/** The change callback. It takes */
159151
onChange: PropTypes.func,
160152
/** Form submit callback */
@@ -165,6 +157,10 @@ if (process.env.NODE_ENV !== 'production') {
165157
* This is executed on changes on fields with uiSchema > triggers : ['after']
166158
*/
167159
onTrigger: PropTypes.func,
160+
/** Form fields values. Note that it should contains @definitionName for triggers. */
161+
properties: PropTypes.object, // eslint-disable-line react/forbid-prop-types
162+
/** UI schema that specify how to render the fields */
163+
uiSchema: PropTypes.array, // eslint-disable-line react/forbid-prop-types
168164
/**
169165
* Custom validation function.
170166
* Prototype: function validation(properties, fieldName, value)
@@ -174,7 +170,3 @@ if (process.env.NODE_ENV !== 'production') {
174170
validation: PropTypes.func,
175171
};
176172
}
177-
178-
export default reduxForm({
179-
form: 'form', // a unique name for this form
180-
})(UIForm);
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
import React, { PropTypes } from 'react';
2+
import { reduxForm } from 'redux-form';
3+
import { merge } from 'talend-json-schema-form-core';
4+
5+
import Widget from './Widget';
6+
import { validate, validateAll } from './utils/validation';
7+
import { mutateValue } from './utils/properties';
8+
9+
const TRIGGER_AFTER = 'after';
10+
11+
class UIForm extends React.Component {
12+
constructor(props) {
13+
super(props);
14+
const { jsonSchema, uiSchema, properties } = props.data;
15+
this.state = {
16+
mergedSchema: merge(jsonSchema, uiSchema),
17+
properties: { ...properties },
18+
validations: {},
19+
};
20+
console.log(this.state.mergedSchema)
21+
22+
this.consolidate = this.consolidate.bind(this);
23+
this.submit = this.submit.bind(this);
24+
}
25+
26+
/**
27+
* Update the state with the new schema.
28+
* @param jsonSchema
29+
* @param uiSchema
30+
* @param properties
31+
*/
32+
componentWillReceiveProps({ jsonSchema, uiSchema, properties }) {
33+
if (!jsonSchema || !uiSchema || !properties) {
34+
return;
35+
}
36+
this.setState({
37+
mergedSchema: merge(jsonSchema, uiSchema),
38+
properties: { ...properties },
39+
});
40+
}
41+
42+
/**
43+
* Consolidate form with the new value.
44+
* - it updates the validation on the modified field.
45+
* - it triggers onChange / onTrigger callbacks
46+
* @param event The change event
47+
* @param schema The schema of the changed field
48+
* @param value The new field value
49+
*/
50+
consolidate(event, schema, value) {
51+
this.setState(
52+
(prevState) => {
53+
const properties = mutateValue(prevState.properties, schema.key, value);
54+
const validations = {
55+
...prevState.validations,
56+
[schema.key]: validate(schema, value, properties, this.props.validation),
57+
};
58+
return { properties, validations };
59+
},
60+
() => this.handleChangesCallbacks(schema, value)
61+
);
62+
}
63+
64+
/**
65+
* Triggers the onTrigger and onChange if needed
66+
* - onChange : at each field change
67+
* - onTrigger : when schema.trigger : ['after']
68+
* @param schema The field schema
69+
* @param value The new value
70+
*/
71+
handleChangesCallbacks(schema, value) {
72+
const { onChange, onTrigger } = this.props;
73+
74+
if (onChange) {
75+
onChange({
76+
jsonSchema: this.props.data.jsonSchema, // original jsonSchema
77+
uiSchema: this.props.data.uiSchema, // original uiSchema
78+
properties: this.state.properties, // current properties values
79+
});
80+
}
81+
82+
const { key, triggers } = schema;
83+
if (onTrigger && triggers && triggers.indexOf(TRIGGER_AFTER) !== -1) {
84+
onTrigger(
85+
this.state.properties, // current properties values
86+
key[key.length - 1], // field name
87+
value // field value
88+
);
89+
}
90+
}
91+
92+
/**
93+
* Triggers a validation and update state.
94+
* @returns {boolean} true if the form is valid, false otherwise
95+
*/
96+
isValid() {
97+
const validations = validateAll(
98+
this.state.mergedSchema,
99+
this.state.properties,
100+
this.props.validation
101+
);
102+
103+
const isValid = Object.keys(validations).every(key => validations[key].valid);
104+
if (!isValid) {
105+
this.setState({ validations });
106+
}
107+
return isValid;
108+
}
109+
110+
/**
111+
* Triggers submit callback if form is valid
112+
* @param event the submit event
113+
*/
114+
submit(event) {
115+
event.preventDefault();
116+
if (this.isValid()) {
117+
this.props.onSubmit(event, this.state.properties);
118+
}
119+
}
120+
121+
render() {
122+
const { formName } = this.props;
123+
const { properties, validations } = this.state;
124+
125+
return (
126+
<form onSubmit={this.submit}>
127+
{
128+
this.state.mergedSchema.map((nextSchema, index) => (
129+
<Widget
130+
key={index}
131+
formName={formName}
132+
onChange={this.consolidate}
133+
schema={nextSchema}
134+
properties={properties}
135+
validations={validations}
136+
/>
137+
))
138+
}
139+
<button type="submit" className="btn btn-primary">Submit</button>
140+
</form>
141+
);
142+
}
143+
}
144+
145+
if (process.env.NODE_ENV !== 'production') {
146+
UIForm.propTypes = {
147+
/** Form schema configuration */
148+
data: PropTypes.shape({
149+
/** Json schema that specify the data model */
150+
jsonSchema: PropTypes.object,
151+
/** UI schema that specify how to render the fields */
152+
uiSchema: PropTypes.array,
153+
/** Form fields values. Note that it should contains @definitionName for triggers. */
154+
properties: PropTypes.object,
155+
}),
156+
/** The form name that will be used to create ids */
157+
formName: PropTypes.string,
158+
/** The change callback. It takes */
159+
onChange: PropTypes.func,
160+
/** Form submit callback */
161+
onSubmit: PropTypes.func.isRequired,
162+
/**
163+
* Tigger > after callback.
164+
* Prototype: function onTrigger(properties, fieldName, value)
165+
* This is executed on changes on fields with uiSchema > triggers : ['after']
166+
*/
167+
onTrigger: PropTypes.func,
168+
/**
169+
* Custom validation function.
170+
* Prototype: function validation(properties, fieldName, value)
171+
* Return format : { valid: true|false, error: { message: 'my validation message' } }
172+
* This is triggered on fields that has their uiSchema > customValidation : true
173+
*/
174+
validation: PropTypes.func,
175+
};
176+
}
177+
178+
export default reduxForm({
179+
form: 'form', // a unique name for this form
180+
})(UIForm);

packages/forms/src/UIForm/Widget/Widget.component.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ import { sfPath } from 'talend-json-schema-form-core';
44
import widgets from '../utils/widgets';
55
import { getValue } from '../utils/properties';
66

7-
export default function Widget({ formName, onChange, properties, schema, validations }) {
7+
export default function Widget({ errors, formName, onChange, properties, schema }) {
88
const { key, type, validationMessage } = schema;
99
const id = sfPath.name(key, '-', formName);
10-
const { error, valid } = validations[key] || {};
11-
const errorMessage = validationMessage || (error && error.message);
10+
const error = errors[key];
11+
const errorMessage = validationMessage || error;
1212
const WidgetImpl = widgets[type];
1313
return WidgetImpl ?
1414
(
@@ -17,18 +17,19 @@ export default function Widget({ formName, onChange, properties, schema, validat
1717
key={id}
1818
errorMessage={errorMessage}
1919
formName={formName}
20-
isValid={valid}
20+
isValid={!error}
2121
onChange={onChange}
2222
properties={properties}
2323
schema={schema}
24-
validations={validations}
24+
errors={errors}
2525
value={getValue(properties, key)}
2626
/>
2727
) : null;
2828
}
2929

3030
if (process.env.NODE_ENV !== 'production') {
3131
Widget.propTypes = {
32+
errors: PropTypes.object, // eslint-disable-line react/forbid-prop-types
3233
formName: PropTypes.string,
3334
onChange: PropTypes.func,
3435
schema: PropTypes.shape({
@@ -37,6 +38,5 @@ if (process.env.NODE_ENV !== 'production') {
3738
validationMessage: PropTypes.string,
3839
}).isRequired,
3940
properties: PropTypes.object, // eslint-disable-line react/forbid-prop-types
40-
validations: PropTypes.object, // eslint-disable-line react/forbid-prop-types
4141
};
4242
}

0 commit comments

Comments
 (0)