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
+
+
+
+
+
+
+
+
+
);
}
diff --git a/demo/client/components/collections/Pages/Add/index.js b/demo/client/components/collections/Pages/Add/index.js
index 1fc63aa907..4bcd2c0985 100644
--- a/demo/client/components/collections/Pages/Add/index.js
+++ b/demo/client/components/collections/Pages/Add/index.js
@@ -8,7 +8,8 @@ import {
Form,
Input,
Textarea,
- Group
+ Group,
+ FormSubmit
} from 'payload/components';
const mapStateToProps = state => ({
@@ -33,27 +34,17 @@ class Add extends Component {
-
-
-
-
-
-
-
diff --git a/package-lock.json b/package-lock.json
index 6306feefae..acbbf7c632 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1734,42 +1734,10 @@
}
},
"babel-core": {
- "version": "6.26.3",
- "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz",
- "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==",
- "dev": true,
- "requires": {
- "babel-code-frame": "^6.26.0",
- "babel-generator": "^6.26.0",
- "babel-helpers": "^6.24.1",
- "babel-messages": "^6.23.0",
- "babel-register": "^6.26.0",
- "babel-runtime": "^6.26.0",
- "babel-template": "^6.26.0",
- "babel-traverse": "^6.26.0",
- "babel-types": "^6.26.0",
- "babylon": "^6.18.0",
- "convert-source-map": "^1.5.1",
- "debug": "^2.6.9",
- "json5": "^0.5.1",
- "lodash": "^4.17.4",
- "minimatch": "^3.0.4",
- "path-is-absolute": "^1.0.1",
- "private": "^0.1.8",
- "slash": "^1.0.0",
- "source-map": "^0.5.7"
- },
- "dependencies": {
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "dev": true,
- "requires": {
- "ms": "2.0.0"
- }
- }
- }
+ "version": "7.0.0-bridge.0",
+ "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz",
+ "integrity": "sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==",
+ "dev": true
},
"babel-generator": {
"version": "6.26.1",
@@ -1806,13 +1774,13 @@
}
},
"babel-jest": {
- "version": "22.4.4",
- "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-22.4.4.tgz",
- "integrity": "sha512-A9NB6/lZhYyypR9ATryOSDcqBaqNdzq4U+CN+/wcMsLcmKkPxQEoTKLajGfd3IkxNyVBT8NewUK2nWyGbSzHEQ==",
+ "version": "23.6.0",
+ "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-23.6.0.tgz",
+ "integrity": "sha512-lqKGG6LYXYu+DQh/slrQ8nxXQkEkhugdXsU6St7GmhVS7Ilc/22ArwqXNJrf0QaOBjZB0360qZMwXqDYQHXaew==",
"dev": true,
"requires": {
- "babel-plugin-istanbul": "^4.1.5",
- "babel-preset-jest": "^22.4.4"
+ "babel-plugin-istanbul": "^4.1.6",
+ "babel-preset-jest": "^23.2.0"
}
},
"babel-loader": {
@@ -1849,9 +1817,9 @@
}
},
"babel-plugin-jest-hoist": {
- "version": "22.4.4",
- "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-22.4.4.tgz",
- "integrity": "sha512-DUvGfYaAIlkdnygVIEl0O4Av69NtuQWcrjMOv6DODPuhuGLDnbsARz3AwiiI/EkIMMlxQDUcrZ9yoyJvTNjcVQ==",
+ "version": "23.2.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-23.2.0.tgz",
+ "integrity": "sha1-5h+uBaHKiAGq3uV6bWa4zvr0QWc=",
"dev": true
},
"babel-plugin-syntax-object-rest-spread": {
@@ -1861,12 +1829,12 @@
"dev": true
},
"babel-preset-jest": {
- "version": "22.4.4",
- "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-22.4.4.tgz",
- "integrity": "sha512-+dxMtOFwnSYWfum0NaEc0O03oSdwBsjx4tMSChRDPGwu/4wSY6Q6ANW3wkjKpJzzguaovRs/DODcT4hbSN8yiA==",
+ "version": "23.2.0",
+ "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-23.2.0.tgz",
+ "integrity": "sha1-jsegOhOPABoaj7HoETZSvxpV2kY=",
"dev": true,
"requires": {
- "babel-plugin-jest-hoist": "^22.4.4",
+ "babel-plugin-jest-hoist": "^23.2.0",
"babel-plugin-syntax-object-rest-spread": "^6.13.0"
}
},
@@ -1885,6 +1853,42 @@
"source-map-support": "^0.4.15"
},
"dependencies": {
+ "babel-core": {
+ "version": "6.26.3",
+ "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz",
+ "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==",
+ "dev": true,
+ "requires": {
+ "babel-code-frame": "^6.26.0",
+ "babel-generator": "^6.26.0",
+ "babel-helpers": "^6.24.1",
+ "babel-messages": "^6.23.0",
+ "babel-register": "^6.26.0",
+ "babel-runtime": "^6.26.0",
+ "babel-template": "^6.26.0",
+ "babel-traverse": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "babylon": "^6.18.0",
+ "convert-source-map": "^1.5.1",
+ "debug": "^2.6.9",
+ "json5": "^0.5.1",
+ "lodash": "^4.17.4",
+ "minimatch": "^3.0.4",
+ "path-is-absolute": "^1.0.1",
+ "private": "^0.1.8",
+ "slash": "^1.0.0",
+ "source-map": "^0.5.7"
+ }
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
"source-map-support": {
"version": "0.4.18",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
@@ -7464,6 +7468,68 @@
"yargs": "^10.0.3"
},
"dependencies": {
+ "babel-core": {
+ "version": "6.26.3",
+ "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz",
+ "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==",
+ "dev": true,
+ "requires": {
+ "babel-code-frame": "^6.26.0",
+ "babel-generator": "^6.26.0",
+ "babel-helpers": "^6.24.1",
+ "babel-messages": "^6.23.0",
+ "babel-register": "^6.26.0",
+ "babel-runtime": "^6.26.0",
+ "babel-template": "^6.26.0",
+ "babel-traverse": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "babylon": "^6.18.0",
+ "convert-source-map": "^1.5.1",
+ "debug": "^2.6.9",
+ "json5": "^0.5.1",
+ "lodash": "^4.17.4",
+ "minimatch": "^3.0.4",
+ "path-is-absolute": "^1.0.1",
+ "private": "^0.1.8",
+ "slash": "^1.0.0",
+ "source-map": "^0.5.7"
+ }
+ },
+ "babel-jest": {
+ "version": "22.4.4",
+ "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-22.4.4.tgz",
+ "integrity": "sha512-A9NB6/lZhYyypR9ATryOSDcqBaqNdzq4U+CN+/wcMsLcmKkPxQEoTKLajGfd3IkxNyVBT8NewUK2nWyGbSzHEQ==",
+ "dev": true,
+ "requires": {
+ "babel-plugin-istanbul": "^4.1.5",
+ "babel-preset-jest": "^22.4.4"
+ }
+ },
+ "babel-plugin-jest-hoist": {
+ "version": "22.4.4",
+ "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-22.4.4.tgz",
+ "integrity": "sha512-DUvGfYaAIlkdnygVIEl0O4Av69NtuQWcrjMOv6DODPuhuGLDnbsARz3AwiiI/EkIMMlxQDUcrZ9yoyJvTNjcVQ==",
+ "dev": true
+ },
+ "babel-preset-jest": {
+ "version": "22.4.4",
+ "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-22.4.4.tgz",
+ "integrity": "sha512-+dxMtOFwnSYWfum0NaEc0O03oSdwBsjx4tMSChRDPGwu/4wSY6Q6ANW3wkjKpJzzguaovRs/DODcT4hbSN8yiA==",
+ "dev": true,
+ "requires": {
+ "babel-plugin-jest-hoist": "^22.4.4",
+ "babel-plugin-syntax-object-rest-spread": "^6.13.0"
+ }
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
"strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
diff --git a/src/client/components/controls/Button/index.js b/src/client/components/controls/Button/index.js
index cc71c08f1e..b0d1fccccd 100644
--- a/src/client/components/controls/Button/index.js
+++ b/src/client/components/controls/Button/index.js
@@ -24,6 +24,7 @@ class Button extends Component {
this.buttonProps = {
className: classes,
onClick: this.props.onClick,
+ disabled: this.props.disabled,
...this.props
};
}
diff --git a/src/client/components/field-types/Input/index.js b/src/client/components/field-types/Input/index.js
index 364e1da76d..233da00e60 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 { Tooltip } from 'payload/components';
+import { FormConsumer, Tooltip } from 'payload/components';
import './index.css';
@@ -15,18 +15,18 @@ class Input extends Component {
this.validate = this.validate.bind(this);
}
- validate() {
+ validate(value) {
let emailTest = /\S+@\S+\.\S+/;
switch (this.props.type) {
case 'text':
- return this.el.value.length > 0;
+ return value.length > 0;
case 'password':
- return this.el.value.length > 0;
+ return value.length > 0;
case 'email':
- return emailTest.test(this.el.value);
+ return emailTest.test(value);
case 'hidden':
return true;
@@ -36,12 +36,6 @@ class Input extends Component {
}
}
- componentDidUpdate() {
- if (this.props.autoFocus) {
- this.el.focus();
- }
- }
-
render() {
const Required = this.props.required
? () => *
@@ -60,21 +54,27 @@ class Input extends Component {
? className
: `${className} error`;
+ const validate = this.props.required ? this.validate : () => true;
+
return (
-
-
-
- { this.el = el; } }
- disabled={this.props.disabled}
- placeholder={this.props.placeholder}
- onChange={this.props.change}
- onFocus={this.props.onFocus}
- type={this.props.type}
- id={this.props.id ? this.props.id : this.props.name}
- name={this.props.name}
- value={this.props.value} />
-
+
+ {context => {
+ return (
+
+
+
+ { context.handleChange(e, validate) }}
+ disabled={this.props.disabled}
+ placeholder={this.props.placeholder}
+ type={this.props.type}
+ id={this.props.id ? this.props.id : this.props.name}
+ name={this.props.name} />
+
+ )
+ }}
+
);
}
}
diff --git a/src/client/components/field-types/Textarea/index.js b/src/client/components/field-types/Textarea/index.js
index a69afba70d..2cd4f2a190 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 { Tooltip } from 'payload/components';
+import { FormConsumer, Tooltip } from 'payload/components';
import './index.css';
@@ -14,12 +14,12 @@ class Textarea extends Component {
this.validate = this.validate.bind(this);
}
- validate() {
+ validate(value) {
switch (this.props.type) {
case 'honeypot':
- return this.el.value.length === 0;
+ return value.length === 0;
default:
- return this.el.value.length > 0;
+ return value.length > 0;
}
}
@@ -49,21 +49,28 @@ class Textarea extends Component {
className = 'interact';
}
+ const validate = this.props.required ? this.validate : () => true;
+
return (
-
-
-
-
-
+
+ {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;