From eb9cfd65bbf4662908ad0898b6dd39fc71248c96 Mon Sep 17 00:00:00 2001 From: Jon Thompson Date: Fri, 25 May 2018 11:48:44 +0100 Subject: [PATCH 01/13] refactor(FormInput): export props type --- src/components/Form/FormInput.react.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Form/FormInput.react.js b/src/components/Form/FormInput.react.js index b3ae622a..0c93389e 100644 --- a/src/components/Form/FormInput.react.js +++ b/src/components/Form/FormInput.react.js @@ -22,7 +22,7 @@ type FormStyle = {| +checked?: boolean, |}; -type Props = {| +export type Props = {| ...FormStyle, +onChange?: (event: SyntheticInputEvent) => void, +onBlur?: (event: SyntheticInputEvent) => void, From 492cbbfd74e75a616633c2c23dd7d0de226b7528 Mon Sep 17 00:00:00 2001 From: Jon Thompson Date: Sun, 27 May 2018 01:22:41 +0100 Subject: [PATCH 02/13] refactor(FormTextInput): Use imported FormInput type, simplify internals --- src/forms/FormTextInput.react.js | 34 ++++++++------------------------ 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/src/forms/FormTextInput.react.js b/src/forms/FormTextInput.react.js index a4c54361..e6e98452 100644 --- a/src/forms/FormTextInput.react.js +++ b/src/forms/FormTextInput.react.js @@ -4,12 +4,11 @@ import * as React from "react"; import Form from "../components/Form"; +import type { Props as FormInputProps } from "../components/Form/FormInput.react"; + type Props = {| - +type?: "checkbox" | "text" | "email" | "password", - +label: string, - +placeHolder: string, - +onChange?: (SyntheticInputEvent) => void, - +onBlur?: (SyntheticInputEvent) => void, + ...FormInputProps, + +label?: string, |}; type State = {| @@ -17,29 +16,12 @@ type State = {| |}; class FormTextInput extends React.PureComponent { - state = { - value: "", - }; + render(): React.Node { + const { label, ...props } = this.props; - _handleChange = (event: SyntheticInputEvent): void => { - this.setState({ value: event.target.value }); - }; + const formInputComponent = React.createElement(Form.Input, props); - render(): React.Node { - const type = this.props.type || "text"; - const { label, placeHolder, onChange, onBlur } = this.props; - const { value } = this.state; - return ( - - - - ); + return {formInputComponent}; } } From 40645d7b43d5f4d99cfa749a83a04dbbb2edc801 Mon Sep 17 00:00:00 2001 From: Jon Thompson Date: Sun, 27 May 2018 14:08:49 +0100 Subject: [PATCH 03/13] feat(withTouchedErrors): Add HOC to help simplify form libs integration --- src/helpers/withTouchedErrors.react.js | 39 ++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/helpers/withTouchedErrors.react.js diff --git a/src/helpers/withTouchedErrors.react.js b/src/helpers/withTouchedErrors.react.js new file mode 100644 index 00000000..6f6c1a85 --- /dev/null +++ b/src/helpers/withTouchedErrors.react.js @@ -0,0 +1,39 @@ +// @flow + +import * as React from "react"; + +/** + * Returns an object of fields with values set based on the touched and error values + * If a value is both touched and has a non-empty error string it is returned as the fields value + */ +function touchedErrors( + touched: { [string]: boolean } = {}, + errors: { [string]: string } = {}, + fields: Array = [] +): { [string]: string } { + return fields.reduce( + (acc, cur) => + Object.assign(acc, { + [cur]: touched && touched[cur] && errors ? errors[cur] : "", + }), + {} + ); +} + +/** + * A HOC that modifies the errors propso that it only returns errors if the the field + * has also been touched + * First takes an array of the field names, followed by the component + */ +function withTouchedErrors(fields: Array = []) { + return function withComponent( + Component: React.ComponentType + ): React.ComponentType { + return function WithTouchedErrors(props: A) { + const errors = touchedErrors(props.touched, props.errors, fields); + return ; + }; + }; +} + +export default withTouchedErrors; From b10296f501b8829ada8567ccb39e1fd1b151e061 Mon Sep 17 00:00:00 2001 From: Jon Thompson Date: Sun, 27 May 2018 17:13:44 +0100 Subject: [PATCH 04/13] feat(LoginPage): add reusable Login page --- src/index.js | 1 + .../account/LoginPage/LoginPage.react.js | 90 +++++++++++++++++++ .../account/LoginPage/LoginPage.strings.js | 13 +++ src/page_templates/account/LoginPage/index.js | 3 + 4 files changed, 107 insertions(+) create mode 100644 src/page_templates/account/LoginPage/LoginPage.react.js create mode 100644 src/page_templates/account/LoginPage/LoginPage.strings.js create mode 100644 src/page_templates/account/LoginPage/index.js diff --git a/src/index.js b/src/index.js index 7f7726ea..99860cc8 100644 --- a/src/index.js +++ b/src/index.js @@ -30,3 +30,4 @@ export { default as FormCard } from "./forms/FormCard.react"; export { default as FormTextInput } from "./forms/FormTextInput.react"; export { default as FormCheckboxInput } from "./forms/FormCheckboxInput.react"; export { default as colors } from "./colors"; +export { default as LoginPage } from "./page_templates/account/LoginPage"; diff --git a/src/page_templates/account/LoginPage/LoginPage.react.js b/src/page_templates/account/LoginPage/LoginPage.react.js new file mode 100644 index 00000000..dbb4a600 --- /dev/null +++ b/src/page_templates/account/LoginPage/LoginPage.react.js @@ -0,0 +1,90 @@ +// @flow + +import * as React from "react"; + +import { FormCard, FormTextInput, StandaloneFormPage } from "../../../"; +import withTouchedErrors from "../../../helpers/withTouchedErrors.react"; + +import defaultStrings from "./LoginPage.strings"; +import type { stringTypes } from "./LoginPage.strings"; + +type fieldTypes = {| + email?: string, + password?: string, +|}; + +type touchedTypes = {| + email?: boolean, + password?: boolean, +|}; + +type Props = {| + +strings?: stringTypes, + +action?: string, + +method?: string, + +onSubmit?: Function, + +onChange?: (SyntheticInputEvent) => void, + +onBlur?: (SyntheticInputEvent) => void, + +values?: fieldTypes, + +errors?: fieldTypes, + +touched?: touchedTypes, +|}; + +/** + * A login page + * Can be easily wrapped with form libraries like formik and redux-form + */ +function LoginPage(props: Props): React.Node { + const { + action, + method, + onSubmit, + onChange, + onBlur, + values, + strings = {}, + errors, + } = props; + + return ( + + + + + + + ); +} + +const LoginPageWithTouchedErrors: React.ComponentType< + Props +> = withTouchedErrors(["email", "password"])(LoginPage); + +export default LoginPageWithTouchedErrors; diff --git a/src/page_templates/account/LoginPage/LoginPage.strings.js b/src/page_templates/account/LoginPage/LoginPage.strings.js new file mode 100644 index 00000000..adecfa59 --- /dev/null +++ b/src/page_templates/account/LoginPage/LoginPage.strings.js @@ -0,0 +1,13 @@ +//@flow +const strings = { + title: "Login to your Account", + buttonText: "Login", + emailLabel: "Email Address", + emailPlaceholder: "Enter email", + passwordLabel: "Password", + passwordPlaceholder: "Password", +}; + +export default strings; + +export type stringTypes = { [$Keys]: string }; diff --git a/src/page_templates/account/LoginPage/index.js b/src/page_templates/account/LoginPage/index.js new file mode 100644 index 00000000..8d802995 --- /dev/null +++ b/src/page_templates/account/LoginPage/index.js @@ -0,0 +1,3 @@ +import LoginPage from "./LoginPage.react"; + +export default LoginPage; From 097b2abb5ae58c52089b175c982f3ca2b3964c27 Mon Sep 17 00:00:00 2001 From: Jon Thompson Date: Sun, 27 May 2018 17:14:41 +0100 Subject: [PATCH 05/13] docs(LoginPage): Add formik to LoginPage example --- example/package.json | 1 + example/src/pages/LoginPage.react.js | 59 +++++++++++---- example/yarn.lock | 108 ++++++++++++++++++++++++++- 3 files changed, 150 insertions(+), 18 deletions(-) diff --git a/example/package.json b/example/package.json index 915dd1a0..698cd6c0 100644 --- a/example/package.json +++ b/example/package.json @@ -6,6 +6,7 @@ "license": "MIT", "dependencies": { "d3-scale": "^2.0.0", + "formik": "^0.11.11", "prop-types": "^15.6.1", "react": "^16.2.0", "react-c3js": "^0.1.20", diff --git a/example/src/pages/LoginPage.react.js b/example/src/pages/LoginPage.react.js index 0ac19840..007f001f 100644 --- a/example/src/pages/LoginPage.react.js +++ b/example/src/pages/LoginPage.react.js @@ -1,28 +1,55 @@ // @flow import * as React from "react"; - -import { FormCard, FormTextInput, StandaloneFormPage } from "tabler-react"; +import { Formik } from "formik"; +import { LoginPage as TablerLoginPage } from "tabler-react"; type Props = {||}; function LoginPage(props: Props): React.Node { return ( - - - - { + // same as above, but feel free to move this into a class method now. + let errors = {}; + if (!values.email) { + errors.email = "Required"; + } else if ( + !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email) + ) { + errors.email = "Invalid email address"; + } + return errors; + }} + onSubmit={( + values, + { setSubmitting, setErrors /* setValues and other goodies */ } + ) => { + alert("Done!"); + }} + render={({ + values, + errors, + touched, + handleChange, + handleBlur, + handleSubmit, + isSubmitting, + }) => ( + - - + )} + /> ); } diff --git a/example/yarn.lock b/example/yarn.lock index 454c4759..12184c6a 100644 --- a/example/yarn.lock +++ b/example/yarn.lock @@ -128,6 +128,33 @@ mkdirp "^0.5.1" rimraf "^2.5.2" +"@semantic-release/changelog@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@semantic-release/changelog/-/changelog-2.0.2.tgz#9972738dc97cf8b516f4200b8ee92e708e415d65" + dependencies: + "@semantic-release/error" "^2.1.0" + aggregate-error "^1.0.0" + fs-extra "^6.0.0" + lodash "^4.17.4" + +"@semantic-release/error@^2.1.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@semantic-release/error/-/error-2.2.0.tgz#ee9d5a09c9969eade1ec864776aeda5c5cddbbf0" + +"@semantic-release/git@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@semantic-release/git/-/git-5.0.0.tgz#13703fca8f8c8c03066b0d005ee6a8c0119c3164" + dependencies: + "@semantic-release/error" "^2.1.0" + aggregate-error "^1.0.0" + debug "^3.1.0" + dir-glob "^2.0.0" + execa "^0.10.0" + fs-extra "^6.0.0" + lodash "^4.17.4" + micromatch "^3.1.4" + p-reduce "^1.0.0" + "@vxna/mini-html-webpack-template@^0.1.7": version "0.1.7" resolved "https://registry.yarnpkg.com/@vxna/mini-html-webpack-template/-/mini-html-webpack-template-0.1.7.tgz#2a8270e513ee14f395cc17c2ce22ced383c45d22" @@ -202,6 +229,13 @@ address@1.0.3, address@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/address/-/address-1.0.3.tgz#b5f50631f8d6cec8bd20c963963afb55e06cbce9" +aggregate-error@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-1.0.0.tgz#888344dad0220a72e3af50906117f48771925fac" + dependencies: + clean-stack "^1.0.0" + indent-string "^3.0.0" + ajv-keywords@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762" @@ -1711,6 +1745,10 @@ clean-css@4.1.x: dependencies: source-map "0.5.x" +clean-stack@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-1.3.0.tgz#9e821501ae979986c46b1d66d2d432db2fd4ae31" + clean-webpack-plugin@^0.1.19: version "0.1.19" resolved "https://registry.yarnpkg.com/clean-webpack-plugin/-/clean-webpack-plugin-0.1.19.tgz#ceda8bb96b00fe168e9b080272960d20fdcadd6d" @@ -2120,6 +2158,16 @@ cross-spawn@5.1.0, cross-spawn@^5.0.1, cross-spawn@^5.1.0: shebang-command "^1.2.0" which "^1.2.9" +cross-spawn@^6.0.0: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + crypto-browserify@^3.11.0: version "3.12.0" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" @@ -3042,6 +3090,18 @@ exec-sh@^0.2.0: dependencies: merge "^1.1.3" +execa@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.10.0.tgz#ff456a8f53f90f8eccc71a96d11bdfc7f082cb50" + dependencies: + cross-spawn "^6.0.0" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + execa@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" @@ -3400,6 +3460,16 @@ format@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/format/-/format-0.2.2.tgz#d6170107e9efdc4ed30c9dc39016df942b5cb58b" +formik@^0.11.11: + version "0.11.11" + resolved "https://registry.yarnpkg.com/formik/-/formik-0.11.11.tgz#4b02838133c0196b1ef443aa973766cd097ec4a5" + dependencies: + lodash.clonedeep "^4.5.0" + lodash.isequal "4.5.0" + lodash.topath "4.5.2" + prop-types "^15.5.10" + warning "^3.0.0" + forwarded@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" @@ -3439,6 +3509,14 @@ fs-extra@^0.30.0: path-is-absolute "^1.0.0" rimraf "^2.2.8" +fs-extra@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-6.0.1.tgz#8abc128f7946e310135ddc93b98bddb410e7a34b" + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs-minipass@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" @@ -4836,6 +4914,12 @@ jsonfile@^3.0.0: optionalDependencies: graceful-fs "^4.1.6" +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + optionalDependencies: + graceful-fs "^4.1.6" + jsonify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" @@ -5035,6 +5119,10 @@ lodash.camelcase@4.3.0, lodash.camelcase@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + lodash.cond@^4.3.0: version "4.5.2" resolved "https://registry.yarnpkg.com/lodash.cond/-/lodash.cond-4.5.2.tgz#f471a1da486be60f6ab955d17115523dd1d255d5" @@ -5043,6 +5131,10 @@ lodash.defaults@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" +lodash.isequal@4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + lodash.kebabcase@4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36" @@ -5092,6 +5184,10 @@ lodash.topairs@4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.topairs/-/lodash.topairs-4.3.0.tgz#3b6deaa37d60fb116713c46c5f17ea190ec48d64" +lodash.topath@4.5.2: + version "4.5.2" + resolved "https://registry.yarnpkg.com/lodash.topath/-/lodash.topath-4.5.2.tgz#3616351f3bba61994a0931989660bd03254fd009" + lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" @@ -5536,6 +5632,10 @@ next-tick@1: version "1.0.0" resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" +nice-try@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.4.tgz#d93962f6c52f2c1558c0fbda6d512819f1efe1c4" + no-case@^2.2.0: version "2.3.2" resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac" @@ -5863,6 +5963,10 @@ p-map@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b" +p-reduce@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa" + p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" @@ -5979,7 +6083,7 @@ path-is-inside@^1.0.1, path-is-inside@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" -path-key@^2.0.0: +path-key@^2.0.0, path-key@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" @@ -7408,7 +7512,7 @@ semver-diff@^2.0.0: dependencies: semver "^5.0.3" -"semver@2 || 3 || 4 || 5", semver@5.5.0, semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1: +"semver@2 || 3 || 4 || 5", semver@5.5.0, semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" From 6d0fff4be0d3fd6152aac31afefd2caa45013069 Mon Sep 17 00:00:00 2001 From: Jon Thompson Date: Mon, 28 May 2018 16:48:59 +0100 Subject: [PATCH 06/13] refactor(FormTextInput): make pure component --- src/forms/FormTextInput.react.js | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/src/forms/FormTextInput.react.js b/src/forms/FormTextInput.react.js index e6e98452..351f7507 100644 --- a/src/forms/FormTextInput.react.js +++ b/src/forms/FormTextInput.react.js @@ -11,30 +11,12 @@ type Props = {| +label?: string, |}; -type State = {| - value: string | number, -|}; - -class FormTextInput extends React.PureComponent { - render(): React.Node { - const { label, ...props } = this.props; +function FormTextInput(props: Props): React.Node { + const { label, ...propsForInput } = props; - const formInputComponent = React.createElement(Form.Input, props); + const formInputComponent = React.createElement(Form.Input, propsForInput); - return {formInputComponent}; - } + return {formInputComponent}; } -/* - - - -*/ - export default FormTextInput; From b533b0d37c2f85e4e5c7a8c26785b8be730090cb Mon Sep 17 00:00:00 2001 From: Jon Thompson Date: Mon, 28 May 2018 17:16:32 +0100 Subject: [PATCH 07/13] refactor(FormCheckbox): export props type --- src/components/Form/FormCheckbox.react.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Form/FormCheckbox.react.js b/src/components/Form/FormCheckbox.react.js index c25412dd..632e757d 100644 --- a/src/components/Form/FormCheckbox.react.js +++ b/src/components/Form/FormCheckbox.react.js @@ -4,7 +4,7 @@ import * as React from "react"; import cn from "classnames"; import Form from "./"; -type Props = {| +export type Props = {| +className?: string, +label?: string, +value?: string | number | boolean, From 7caaf824830a2a0cbe38722fd0361788d2293945 Mon Sep 17 00:00:00 2001 From: Jon Thompson Date: Mon, 28 May 2018 17:18:05 +0100 Subject: [PATCH 08/13] refactor(FormCheckboxInput): use imported types and Form.Checkbox --- src/forms/FormCheckboxInput.react.js | 38 +++------------------------- 1 file changed, 4 insertions(+), 34 deletions(-) diff --git a/src/forms/FormCheckboxInput.react.js b/src/forms/FormCheckboxInput.react.js index e5740908..03ba2033 100644 --- a/src/forms/FormCheckboxInput.react.js +++ b/src/forms/FormCheckboxInput.react.js @@ -3,42 +3,12 @@ import * as React from "react"; import Form from "../components/Form"; +import type { Props as FormCheckboxProps } from "../components/Form/FormCheckbox.react"; -type Props = {| - +label: string, -|}; +function FormCheckboxInput(props: FormCheckboxProps) { + const formCheckboxComponent = React.createElement(Form.Checkbox, props); -type State = { - value: boolean, -}; - -class FormCheckboxInput extends React.PureComponent { - state = { - value: false, - }; - - _handleChange = (event: SyntheticInputEvent): void => { - this.setState((prevState, _) => ({ - value: !prevState.value, - })); - }; - - render(): React.Node { - const { label } = this.props; - const { value } = this.state; - return ( - - - - ); - } + return {formCheckboxComponent}; } export default FormCheckboxInput; From b418d1a938c62ad19795b53e2f0f5935b96362fd Mon Sep 17 00:00:00 2001 From: Jon Thompson Date: Mon, 28 May 2018 17:32:23 +0100 Subject: [PATCH 09/13] fix(FormCheckbox): add onBlur prop --- src/components/Form/FormCheckbox.react.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Form/FormCheckbox.react.js b/src/components/Form/FormCheckbox.react.js index 632e757d..ef00e97c 100644 --- a/src/components/Form/FormCheckbox.react.js +++ b/src/components/Form/FormCheckbox.react.js @@ -13,6 +13,7 @@ export type Props = {| +disabled?: boolean, +readOnly?: boolean, +onChange?: (event: SyntheticInputEvent) => void, + +onBlur?: (event: SyntheticInputEvent) => void, +isInline?: boolean, |}; From 06f5d020b6e845bad53713cbfbe302ad787ff6f4 Mon Sep 17 00:00:00 2001 From: Jon Thompson Date: Mon, 28 May 2018 17:34:16 +0100 Subject: [PATCH 10/13] feat(RegisterPage): add RegisterPage component --- example/src/pages/RegisterPage.react.js | 27 +--- src/index.js | 1 + .../RegisterPage/RegisterPage.react.js | 117 ++++++++++++++++++ .../RegisterPage/RegisterPage.strings.js | 16 +++ .../account/RegisterPage/index.js | 3 + 5 files changed, 139 insertions(+), 25 deletions(-) create mode 100644 src/page_templates/account/RegisterPage/RegisterPage.react.js create mode 100644 src/page_templates/account/RegisterPage/RegisterPage.strings.js create mode 100644 src/page_templates/account/RegisterPage/index.js diff --git a/example/src/pages/RegisterPage.react.js b/example/src/pages/RegisterPage.react.js index 46bcece9..b8feb3ec 100644 --- a/example/src/pages/RegisterPage.react.js +++ b/example/src/pages/RegisterPage.react.js @@ -2,35 +2,12 @@ import * as React from "react"; -import { - FormCard, - FormTextInput, - FormCheckboxInput, - StandaloneFormPage, -} from "tabler-react"; +import { RegisterPage as TablerRegisterPage } from "tabler-react"; type Props = {||}; function RegisterPage(props: Props): React.Node { - return ( - - - - - - - - - ); + return ; } export default RegisterPage; diff --git a/src/index.js b/src/index.js index 99860cc8..ddf40da0 100644 --- a/src/index.js +++ b/src/index.js @@ -31,3 +31,4 @@ export { default as FormTextInput } from "./forms/FormTextInput.react"; export { default as FormCheckboxInput } from "./forms/FormCheckboxInput.react"; export { default as colors } from "./colors"; export { default as LoginPage } from "./page_templates/account/LoginPage"; +export { default as RegisterPage } from "./page_templates/account/RegisterPage"; diff --git a/src/page_templates/account/RegisterPage/RegisterPage.react.js b/src/page_templates/account/RegisterPage/RegisterPage.react.js new file mode 100644 index 00000000..c4c38185 --- /dev/null +++ b/src/page_templates/account/RegisterPage/RegisterPage.react.js @@ -0,0 +1,117 @@ +// @flow + +import * as React from "react"; + +import { + FormCard, + FormTextInput, + FormCheckboxInput, + StandaloneFormPage, +} from "../../../"; +import withTouchedErrors from "../../../helpers/withTouchedErrors.react"; + +import defaultStrings from "./RegisterPage.strings"; +import type { stringTypes } from "./RegisterPage.strings"; + +type fieldTypes = {| + name?: string, + email?: string, + password?: string, + terms?: string, +|}; + +type touchedTypes = {| + name?: boolean, + email?: boolean, + password?: boolean, + terms?: string, +|}; + +type Props = {| + +strings?: stringTypes, + +action?: string, + +method?: string, + +onSubmit?: Function, + +onChange?: (SyntheticInputEvent) => void, + +onBlur?: (SyntheticInputEvent) => void, + +values?: fieldTypes, + +errors?: fieldTypes, + +touched?: touchedTypes, +|}; + +/** + * A register page + * Can be easily wrapped with form libraries like formik and redux-form + */ +function RegisterPage(props: Props): React.Node { + const { + action, + method, + onSubmit, + onChange, + onBlur, + values, + strings = {}, + errors, + } = props; + + return ( + + + + + + + + + ); +} + +const RegisterPageWithTouchedErrors: React.ComponentType< + Props +> = withTouchedErrors(["name", "email", "password", "terms"])(RegisterPage); + +export default RegisterPageWithTouchedErrors; diff --git a/src/page_templates/account/RegisterPage/RegisterPage.strings.js b/src/page_templates/account/RegisterPage/RegisterPage.strings.js new file mode 100644 index 00000000..6c2e1757 --- /dev/null +++ b/src/page_templates/account/RegisterPage/RegisterPage.strings.js @@ -0,0 +1,16 @@ +//@flow +const strings = { + title: "Create New Account", + buttonText: "Create Account", + nameLabel: "Name", + namePlaceholder: "Enter name", + emailLabel: "Email Address", + emailPlaceholder: "Enter email", + passwordLabel: "Password", + passwordPlaceholder: "Password", + termsLabel: "Agree to the terms and policy", +}; + +export default strings; + +export type stringTypes = { [$Keys]: string }; diff --git a/src/page_templates/account/RegisterPage/index.js b/src/page_templates/account/RegisterPage/index.js new file mode 100644 index 00000000..270673fa --- /dev/null +++ b/src/page_templates/account/RegisterPage/index.js @@ -0,0 +1,3 @@ +import RegisterPage from "./RegisterPage.react"; + +export default RegisterPage; From d03e4fe6860aa0b7c59a192467a6fa2890728810 Mon Sep 17 00:00:00 2001 From: Jon Thompson Date: Mon, 28 May 2018 19:50:04 +0100 Subject: [PATCH 11/13] fix(RegisterPage): fix incorrect flow type --- src/page_templates/account/RegisterPage/RegisterPage.react.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/page_templates/account/RegisterPage/RegisterPage.react.js b/src/page_templates/account/RegisterPage/RegisterPage.react.js index c4c38185..f1615e44 100644 --- a/src/page_templates/account/RegisterPage/RegisterPage.react.js +++ b/src/page_templates/account/RegisterPage/RegisterPage.react.js @@ -24,7 +24,7 @@ type touchedTypes = {| name?: boolean, email?: boolean, password?: boolean, - terms?: string, + terms?: boolean, |}; type Props = {| From 7958867a170bcbf1bebb0aa42b2b71054b743660 Mon Sep 17 00:00:00 2001 From: Jon Thompson Date: Mon, 28 May 2018 20:05:05 +0100 Subject: [PATCH 12/13] feat(ForgotPasswordPage): Add forgotton password page --- example/src/pages/ForgotPasswordPage.react.js | 19 +---- src/index.js | 3 + .../ForgotPasswordPage.react.js | 79 +++++++++++++++++++ .../ForgotPasswordPage.strings.js | 13 +++ .../account/ForgotPasswordPage/index.js | 3 + 5 files changed, 100 insertions(+), 17 deletions(-) create mode 100644 src/page_templates/account/ForgotPasswordPage/ForgotPasswordPage.react.js create mode 100644 src/page_templates/account/ForgotPasswordPage/ForgotPasswordPage.strings.js create mode 100644 src/page_templates/account/ForgotPasswordPage/index.js diff --git a/example/src/pages/ForgotPasswordPage.react.js b/example/src/pages/ForgotPasswordPage.react.js index 78e0a832..aff9c7a0 100644 --- a/example/src/pages/ForgotPasswordPage.react.js +++ b/example/src/pages/ForgotPasswordPage.react.js @@ -2,27 +2,12 @@ import * as React from "react"; -import { FormCard, FormTextInput, StandaloneFormPage } from "tabler-react"; +import { ForgotPasswordPage as TablerForgotPasswordPage } from "tabler-react"; type Props = {||}; function ForgotPasswordPage(props: Props): React.Node { - return ( - - -

- Enter your email address and your password will be reset and emailed - to you. -

- -
-
- ); + return ; } export default ForgotPasswordPage; diff --git a/src/index.js b/src/index.js index ddf40da0..5aa16b14 100644 --- a/src/index.js +++ b/src/index.js @@ -32,3 +32,6 @@ export { default as FormCheckboxInput } from "./forms/FormCheckboxInput.react"; export { default as colors } from "./colors"; export { default as LoginPage } from "./page_templates/account/LoginPage"; export { default as RegisterPage } from "./page_templates/account/RegisterPage"; +export { + default as ForgotPasswordPage, +} from "./page_templates/account/ForgotPasswordPage"; diff --git a/src/page_templates/account/ForgotPasswordPage/ForgotPasswordPage.react.js b/src/page_templates/account/ForgotPasswordPage/ForgotPasswordPage.react.js new file mode 100644 index 00000000..f99a91df --- /dev/null +++ b/src/page_templates/account/ForgotPasswordPage/ForgotPasswordPage.react.js @@ -0,0 +1,79 @@ +// @flow + +import * as React from "react"; + +import { FormCard, FormTextInput, StandaloneFormPage } from "../../../"; +import withTouchedErrors from "../../../helpers/withTouchedErrors.react"; + +import defaultStrings from "./ForgotPasswordPage.strings"; +import type { stringTypes } from "./ForgotPasswordPage.strings"; + +type fieldTypes = {| + email?: string, +|}; + +type touchedTypes = {| + email?: boolean, +|}; + +type Props = {| + +strings?: stringTypes, + +action?: string, + +method?: string, + +onSubmit?: Function, + +onChange?: (SyntheticInputEvent) => void, + +onBlur?: (SyntheticInputEvent) => void, + +values?: fieldTypes, + +errors?: fieldTypes, + +touched?: touchedTypes, +|}; + +/** + * A forgot password page + * Can be easily wrapped with form libraries like formik and redux-form + */ +function ForgotPasswordPage(props: Props): React.Node { + const { + action, + method, + onSubmit, + onChange, + onBlur, + values, + strings = {}, + errors, + } = props; + + return ( + + +

+ {strings.instructions || defaultStrings.instructions} +

+ +
+
+ ); +} + +const ForgotPasswordPageWithTouchedErrors: React.ComponentType< + Props +> = withTouchedErrors(["email"])(ForgotPasswordPage); + +export default ForgotPasswordPageWithTouchedErrors; diff --git a/src/page_templates/account/ForgotPasswordPage/ForgotPasswordPage.strings.js b/src/page_templates/account/ForgotPasswordPage/ForgotPasswordPage.strings.js new file mode 100644 index 00000000..8bfc2862 --- /dev/null +++ b/src/page_templates/account/ForgotPasswordPage/ForgotPasswordPage.strings.js @@ -0,0 +1,13 @@ +//@flow +const strings = { + title: "Forgot Password", + buttonText: "Request Password Change", + emailLabel: "Email Address", + emailPlaceholder: "Enter email", + instructions: + "Enter your email address and your password will be reset and emailed to you.", +}; + +export default strings; + +export type stringTypes = { [$Keys]: string }; diff --git a/src/page_templates/account/ForgotPasswordPage/index.js b/src/page_templates/account/ForgotPasswordPage/index.js new file mode 100644 index 00000000..786e78a2 --- /dev/null +++ b/src/page_templates/account/ForgotPasswordPage/index.js @@ -0,0 +1,3 @@ +import ForgotPasswordPage from "./ForgotPasswordPage.react"; + +export default ForgotPasswordPage; From 6fac4d8499a97579225e6dcdda8a437ac7f06861 Mon Sep 17 00:00:00 2001 From: Jon Thompson Date: Mon, 28 May 2018 21:21:29 +0100 Subject: [PATCH 13/13] docs(page templates): add examples for account pages --- .../ForgotPasswordPage.examples.md | 3 +++ .../account/LoginPage/LoginPage.examples.md | 3 +++ .../account/RegisterPage/RegisterPage.examples.md | 3 +++ styleguide.config.js | 14 ++++++++++++++ 4 files changed, 23 insertions(+) create mode 100644 src/page_templates/account/ForgotPasswordPage/ForgotPasswordPage.examples.md create mode 100644 src/page_templates/account/LoginPage/LoginPage.examples.md create mode 100644 src/page_templates/account/RegisterPage/RegisterPage.examples.md diff --git a/src/page_templates/account/ForgotPasswordPage/ForgotPasswordPage.examples.md b/src/page_templates/account/ForgotPasswordPage/ForgotPasswordPage.examples.md new file mode 100644 index 00000000..f88be28d --- /dev/null +++ b/src/page_templates/account/ForgotPasswordPage/ForgotPasswordPage.examples.md @@ -0,0 +1,3 @@ +```jsx + +``` diff --git a/src/page_templates/account/LoginPage/LoginPage.examples.md b/src/page_templates/account/LoginPage/LoginPage.examples.md new file mode 100644 index 00000000..4091e7cf --- /dev/null +++ b/src/page_templates/account/LoginPage/LoginPage.examples.md @@ -0,0 +1,3 @@ +```jsx + +``` diff --git a/src/page_templates/account/RegisterPage/RegisterPage.examples.md b/src/page_templates/account/RegisterPage/RegisterPage.examples.md new file mode 100644 index 00000000..8d89b147 --- /dev/null +++ b/src/page_templates/account/RegisterPage/RegisterPage.examples.md @@ -0,0 +1,3 @@ +```jsx + +``` diff --git a/styleguide.config.js b/styleguide.config.js index de5833bc..17bac1a5 100644 --- a/styleguide.config.js +++ b/styleguide.config.js @@ -185,6 +185,20 @@ module.exports = { { name: "Page Templates", sections: [ + { + name: "Login Page", + components: "src/page_templates/account/LoginPage/*.react.{js,jsx}", + }, + { + name: "Register Page", + components: + "src/page_templates/account/RegisterPage/*.react.{js,jsx}", + }, + { + name: "Forgot Password Page", + components: + "src/page_templates/account/ForgotPasswordPage/*.react.{js,jsx}", + }, { name: "Errors", components: "src/page_templates/errors/**/*.react.{js,jsx}",