From 8badbf932958952dc745a1150e2c5486ea944095 Mon Sep 17 00:00:00 2001 From: James Date: Mon, 24 Sep 2018 22:07:01 -0400 Subject: [PATCH 1/5] refactors form components to handle any number of nested field components --- .idea/codeStyles/Project.xml | 25 +++ .idea/codeStyles/codeStyleConfig.xml | 5 + .idea/inspectionProfiles/Project_Default.xml | 6 + .idea/misc.xml | 6 + .idea/modules.xml | 8 + .idea/payload.iml | 12 ++ .idea/vcs.xml | 6 + components.js | 4 + .../collections/Orders/Add/index.js | 42 ++++- .../components/collections/Pages/Add/index.js | 29 ++-- package-lock.json | 162 ++++++++++++------ .../components/controls/Button/index.js | 1 + .../components/field-types/Input/index.js | 50 +++--- .../components/field-types/Textarea/index.js | 43 +++-- src/client/components/forms/Form/index.js | 132 +++++--------- src/client/components/forms/Submit/index.js | 20 +++ 16 files changed, 347 insertions(+), 204 deletions(-) create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/codeStyles/codeStyleConfig.xml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/payload.iml create mode 100644 .idea/vcs.xml create mode 100644 src/client/components/forms/Submit/index.js diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000000..887e04243d --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,25 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000000..79ee123c2b --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000000..c6cc8c8196 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000000..24eb271ab3 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000000..b5a69e057e --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/payload.iml b/.idea/payload.iml new file mode 100644 index 0000000000..24643cc374 --- /dev/null +++ b/.idea/payload.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000000..94a25f7f4c --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/components.js b/components.js index d6ccdf3f19..f7eedd2906 100644 --- a/components.js +++ b/components.js @@ -18,6 +18,8 @@ import EditView from './src/client/components/views/collections/Edit'; import StickOnScroll from './src/client/components/layout/StickOnScroll'; import APIUrl from './src/client/components/modules/APIUrl'; import Form from './src/client/components/forms/Form'; +import { FormConsumer } from './src/client/components/forms/Form'; +import FormSubmit from './src/client/components/forms/Submit'; import PayloadIcon from './src/client/components/graphics/PayloadIcon'; import PayloadLogo from './src/client/components/graphics/PayloadLogo'; import Tooltip from './src/client/components/modules/Tooltip'; @@ -49,6 +51,8 @@ export { StickOnScroll, APIUrl, Form, + FormConsumer, + FormSubmit, PayloadIcon, PayloadLogo, Tooltip, diff --git a/demo/client/components/collections/Orders/Add/index.js b/demo/client/components/collections/Orders/Add/index.js index 56ca9b909e..3e38babb50 100644 --- a/demo/client/components/collections/Orders/Add/index.js +++ b/demo/client/components/collections/Orders/Add/index.js @@ -1,7 +1,15 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; - -import { AddView } from 'payload/components'; +import { + AddView, + StickOnScroll, + APIUrl, + Button, + Form, + Input, + Textarea, + Group +} from 'payload/components'; const mapStateToProps = state => ({ collections: state.collections.all @@ -14,12 +22,40 @@ class Add extends Component { this.collection = this.props.collections.find(collection => { return collection.slug === this.slug; }); + this.state = { + apiUrl: 'https://site.com/order?slug=test' + }; } render() { return ( -

Add New Order

+
+

Add New Order

+
+ + +
+ + +
+
+
+ + + - + + {context => { + return ( +
+ +
+ ) + }} +
); } } diff --git a/src/client/components/forms/Form/index.js b/src/client/components/forms/Form/index.js index cd1bd5b8af..58ec081924 100644 --- a/src/client/components/forms/Form/index.js +++ b/src/client/components/forms/Form/index.js @@ -1,70 +1,48 @@ -import React, { Component } from 'react'; -import { withRouter } from 'react-router-dom'; - +import React, { Component, createContext } from 'react'; import { ajax } from 'payload'; import './index.css'; +const FormContext = createContext({}); + class Form extends Component { constructor(props) { super(props); this.state = { - fields: this.buildFields(), + fields: {}, status: undefined, processing: false }; - // Fill from renderChildren - this.childRefs = {}; - - this.buildFields = this.buildFields.bind(this); - this.handleChange = this.handleChange.bind(this); this.submit = this.submit.bind(this); - this.renderChildren = this.renderChildren.bind(this); } - buildFields() { - let fields = {}; + handleChange(e, validate) { + let valid = validate(e.target.value); - React.Children.map(this.props.children, (child) => { - if (child.props.name) { - fields[child.props.name] = { - value: child.props.value ? child.props.value : '', - required: child.props.required - }; - } - }); - - return fields; - } - - handleChange(e) { - let newState = { ...this.state }; - newState.fields[e.target.name].value = e.target.value; - this.setState(newState); - } - - submit(e) { - let isValid = true; - let newState = { ...this.state }; - - Object.keys(this.childRefs).forEach((field) => { - if (this.childRefs[field].props.required) { - let current = this.childRefs[field]; - - let validated = current.validate(); - - newState.fields[field].valid = validated; - - if (!validated) { - isValid = false; + this.setState({ + fields: { + ...this.state.fields, + [e.target.name]: { + value: e.target.value, + valid: valid } } }); + } - // Update validated fields - this.setState(newState); + submit(e) { + + e.preventDefault(); + + let isValid = true; + + Object.keys(this.state.fields).forEach((field) => { + if (!this.state.fields[field].valid) { + isValid = false; + } + }); // If not valid, prevent submission if (!isValid) { @@ -76,7 +54,7 @@ class Form extends Component { this.props.onSubmit(this.state.fields); // If form is AJAX, fetch data - } else if (this.props.ajax) { + } else if (this.props.ajax !== false) { e.preventDefault(); let data = {}; @@ -118,52 +96,10 @@ class Form extends Component { ); } - if (this.props.clearAfterSubmit && isValid) { - // Loop through fields - if not valid, set to invalid, rerender with error - Object.keys(this.state.fields).forEach((field) => { - newState.fields[field].value = ''; - }); - } - // If valid and not AJAX submit as usual return; } - renderChildren() { - let children = React.Children.map(this.props.children, (child) => { - if (child.props.name) { - // Initialize validation as true - only show error class if error after blur - let valid = true; - - // If a valid value has been passed from field, set valid equal to that - if (typeof this.state.fields[child.props.name].valid !== 'undefined') { - valid = this.state.fields[child.props.name].valid; - } - - return React.cloneElement(child, { - ref: (el) => { - this.childRefs[child.props.name] = el; - }, - change: this.handleChange, - validate: this.validate, - valid: valid, - value: this.state.fields[child.props.name].value - }); - } - - if (child.props.type === 'submit') { - return React.cloneElement(child, { - disabled: this.state.processing || child.props.disabled === 'disabled' ? 'disabled' : false, - children: this.state.processing ? 'Processing...' : child.props.children - }); - } - - return child; - }); - - return children; - } - render() { let Status = () => { return null; @@ -186,10 +122,24 @@ class Form extends Component { action={this.props.action} className={this.props.className}> - {this.renderChildren()} + + {this.props.children} + ); } } -export default withRouter(Form); +export const FormConsumer = (props => { + return ( + + {props.children} + + ); +}); + +export default Form; diff --git a/src/client/components/forms/Submit/index.js b/src/client/components/forms/Submit/index.js new file mode 100644 index 0000000000..47528231fd --- /dev/null +++ b/src/client/components/forms/Submit/index.js @@ -0,0 +1,20 @@ +import React, { Component } from 'react'; +import { FormConsumer, Button } from 'payload/components'; + +class FormSubmit extends Component { + render() { + return ( + + {context => { + return ( + + ) + }} + + ); + } +} + +export default FormSubmit; From ec4ec73b3a93a2f17211142d83565675c19a000d Mon Sep 17 00:00:00 2001 From: James Date: Mon, 24 Sep 2018 22:07:43 -0400 Subject: [PATCH 2/5] removes idea folder - god knows what that is --- .idea/codeStyles/Project.xml | 25 -------------------- .idea/codeStyles/codeStyleConfig.xml | 5 ---- .idea/inspectionProfiles/Project_Default.xml | 6 ----- .idea/misc.xml | 6 ----- .idea/modules.xml | 8 ------- .idea/payload.iml | 12 ---------- .idea/vcs.xml | 6 ----- 7 files changed, 68 deletions(-) delete mode 100644 .idea/codeStyles/Project.xml delete mode 100644 .idea/codeStyles/codeStyleConfig.xml delete mode 100644 .idea/inspectionProfiles/Project_Default.xml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/payload.iml delete mode 100644 .idea/vcs.xml diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml deleted file mode 100644 index 887e04243d..0000000000 --- a/.idea/codeStyles/Project.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index 79ee123c2b..0000000000 --- a/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index c6cc8c8196..0000000000 --- a/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 24eb271ab3..0000000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index b5a69e057e..0000000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/payload.iml b/.idea/payload.iml deleted file mode 100644 index 24643cc374..0000000000 --- a/.idea/payload.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7f4c..0000000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From 10032744d772ca5d1ee4bed1677191c81ffd2519 Mon Sep 17 00:00:00 2001 From: James Date: Tue, 25 Sep 2018 00:11:57 -0400 Subject: [PATCH 3/5] adds Form component to Filter and rolled back frustrating Webpack bug dealing with FormConsumer --- src/client/components/modules/Filter/index.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/client/components/modules/Filter/index.js b/src/client/components/modules/Filter/index.js index 3df5486fdd..5fc48afe17 100644 --- a/src/client/components/modules/Filter/index.js +++ b/src/client/components/modules/Filter/index.js @@ -1,10 +1,12 @@ import React, { Component } from 'react'; -import { Input } from 'payload/components'; +import { Form, Input } from 'payload/components'; class Filter extends Component { render() { return ( - +
+ +
); } } From c6b0639c90e3440736ea3e00aff5c030af2956c1 Mon Sep 17 00:00:00 2001 From: James Date: Tue, 25 Sep 2018 10:55:01 -0400 Subject: [PATCH 4/5] resolves circular dependency --- components.js | 95 ++++++------------- package-lock.json | 6 ++ package.json | 1 + .../components/field-types/Input/index.js | 44 +++++---- .../components/field-types/Textarea/index.js | 42 ++++---- src/client/components/forms/Form/index.js | 10 +- src/client/components/forms/Submit/index.js | 23 ++--- 7 files changed, 95 insertions(+), 126 deletions(-) diff --git a/components.js b/components.js index f7eedd2906..1877cf6710 100644 --- a/components.js +++ b/components.js @@ -1,65 +1,30 @@ -import App from './src/client/components/App'; -import Button from './src/client/components/controls/Button'; -import Group from './src/client/components/field-types/Group'; -import Input from './src/client/components/field-types/Input'; -import Textarea from './src/client/components/field-types/Textarea'; -import MeasureWindow from './src/client/components/utilities/MeasureWindow'; -import MeasureScroll from './src/client/components/utilities/MeasureScroll'; -import LoadContent from './src/client/components/utilities/LoadContent'; -import Dashboard from './src/client/components/views/Dashboard'; -import CollectionRoutes from './src/client/components/routes/Collections'; -import DefaultTemplate from './src/client/components/layout/DefaultTemplate'; -import Login from './src/client/components/views/Login'; -import AddView from './src/client/components/views/collections/Add'; -import ArchiveView from './src/client/components/views/collections/Archive'; -import HeadingButton from './src/client/components/modules/HeadingButton'; -import Filter from './src/client/components/modules/Filter'; -import EditView from './src/client/components/views/collections/Edit'; -import StickOnScroll from './src/client/components/layout/StickOnScroll'; -import APIUrl from './src/client/components/modules/APIUrl'; -import Form from './src/client/components/forms/Form'; -import { FormConsumer } from './src/client/components/forms/Form'; -import FormSubmit from './src/client/components/forms/Submit'; -import PayloadIcon from './src/client/components/graphics/PayloadIcon'; -import PayloadLogo from './src/client/components/graphics/PayloadLogo'; -import Tooltip from './src/client/components/modules/Tooltip'; -import Sidebar from './src/client/components/layout/Sidebar'; -import StepNav from './src/client/components/modules/StepNav'; -import Arrow from './src/client/components/graphics/Arrow'; -import Label from './src/client/components/type/Label'; -import SetStepNav from './src/client/components/utilities/SetStepNav'; -import ContentBlock from './src/client/components/layout/ContentBlock'; - -export { - App, - Button, - Group, - Input, - Textarea, - MeasureWindow, - MeasureScroll, - LoadContent, - Dashboard, - CollectionRoutes, - DefaultTemplate, - Login, - AddView, - ArchiveView, - HeadingButton, - Filter, - EditView, - StickOnScroll, - APIUrl, - Form, - FormConsumer, - FormSubmit, - PayloadIcon, - PayloadLogo, - Tooltip, - Sidebar, - StepNav, - Arrow, - Label, - SetStepNav, - ContentBlock -}; +export { default as App } from './src/client/components/App'; +export { default as Button } from './src/client/components/controls/Button'; +export { default as MeasureWindow } from './src/client/components/utilities/MeasureWindow'; +export { default as MeasureScroll } from './src/client/components/utilities/MeasureScroll'; +export { default as LoadContent } from './src/client/components/utilities/LoadContent'; +export { default as Dashboard } from './src/client/components/views/Dashboard'; +export { default as CollectionRoutes } from './src/client/components/routes/Collections'; +export { default as DefaultTemplate } from './src/client/components/layout/DefaultTemplate'; +export { default as Login } from './src/client/components/views/Login'; +export { default as AddView } from './src/client/components/views/collections/Add'; +export { default as ArchiveView } from './src/client/components/views/collections/Archive'; +export { default as HeadingButton } from './src/client/components/modules/HeadingButton'; +export { default as Filter } from './src/client/components/modules/Filter'; +export { default as EditView } from './src/client/components/views/collections/Edit'; +export { default as StickOnScroll } from './src/client/components/layout/StickOnScroll'; +export { default as APIUrl } from './src/client/components/modules/APIUrl'; +export { default as Form, FormContext } from './src/client/components/forms/Form'; +export { default as FormSubmit } from './src/client/components/forms/Submit'; +export { default as Group } from './src/client/components/field-types/Group'; +export { default as Input } from './src/client/components/field-types/Input'; +export { default as Textarea } from './src/client/components/field-types/Textarea'; +export { default as PayloadIcon } from './src/client/components/graphics/PayloadIcon'; +export { default as PayloadLogo } from './src/client/components/graphics/PayloadLogo'; +export { default as Tooltip } from './src/client/components/modules/Tooltip'; +export { default as Sidebar } from './src/client/components/layout/Sidebar'; +export { default as StepNav } from './src/client/components/modules/StepNav'; +export { default as Arrow } from './src/client/components/graphics/Arrow'; +export { default as Label } from './src/client/components/type/Label'; +export { default as SetStepNav } from './src/client/components/utilities/SetStepNav'; +export { default as ContentBlock } from './src/client/components/layout/ContentBlock'; diff --git a/package-lock.json b/package-lock.json index acbbf7c632..2d4a1459c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2698,6 +2698,12 @@ "safe-buffer": "^5.0.1" } }, + "circular-dependency-plugin": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.0.2.tgz", + "integrity": "sha512-oC7/DVAyfcY3UWKm0sN/oVoDedQDQiw/vIiAnuTWTpE5s0zWf7l3WY417Xw/Fbi/QbAjctAkxgMiS9P0s3zkmA==", + "dev": true + }, "circular-json": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", diff --git a/package.json b/package.json index d58b828d27..b00dee6064 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "babel-core": "^7.0.0-bridge.0", "babel-jest": "^23.6.0", "babel-loader": "^8.0.2", + "circular-dependency-plugin": "^5.0.2", "css-loader": "^1.0.0", "eslint": "^4.19.1", "eslint-plugin-react": "^7.10.0", diff --git a/src/client/components/field-types/Input/index.js b/src/client/components/field-types/Input/index.js index 233da00e60..673d5a17ed 100644 --- a/src/client/components/field-types/Input/index.js +++ b/src/client/components/field-types/Input/index.js @@ -1,5 +1,5 @@ import React, { Component } from 'react'; -import { FormConsumer, Tooltip } from 'payload/components'; +import { Tooltip, FormContext } from 'payload/components'; import './index.css'; @@ -36,6 +36,10 @@ class Input extends Component { } } + componentDidMount() { + + } + render() { const Required = this.props.required ? () => * @@ -57,26 +61,26 @@ class Input extends Component { const validate = this.props.required ? this.validate : () => true; return ( - - {context => { - return ( -
- -
- ) - }} -
+
+ +
); } } -export default Input; +export default props => { + return ( + + {context => } + + ); +}; diff --git a/src/client/components/field-types/Textarea/index.js b/src/client/components/field-types/Textarea/index.js index 2cd4f2a190..8ba7d94a21 100644 --- a/src/client/components/field-types/Textarea/index.js +++ b/src/client/components/field-types/Textarea/index.js @@ -1,5 +1,5 @@ import React, { Component } from 'react'; -import { FormConsumer, Tooltip } from 'payload/components'; +import { FormContext, Tooltip } from 'payload/components'; import './index.css'; @@ -52,27 +52,27 @@ class Textarea extends Component { const validate = this.props.required ? this.validate : () => true; return ( - - {context => { - return ( -
- -
- ) - }} -
+
+ +
); } } -export default Textarea; +export default props => { + return ( + + {context =>