From 891ab5ef140b168cefc72c26b8f91af2b02a3568 Mon Sep 17 00:00:00 2001 From: Jared Palmer Date: Tue, 4 Sep 2018 15:31:02 -0700 Subject: [PATCH] Website! (#866) * Docusaurus! * Homepage * Attempt deploy * Add copy to cliboard to code blocks * setup now copy script * Deploy the correct folder * Fix trailing comma * Fix the deploy folder * Start on docs * Moar docs * Remove unused doc * Add form submission guide * Add guides on arrays and validation * Update sidebars * More validation docs * Simply the overview * Fix up react-native * Update React Native docs * More tweaks * Fix copyright * Make the site merge-able to master * Make the site deployable * Add resources tab * Update config * Update config * Add withFormik --- .gitignore | 12 + .travis.yml | 17 +- docs/api.md | 896 -- docs/api/connect.md | 30 + docs/api/field.md | 264 + docs/api/form.md | 10 + docs/api/formik.md | 378 + docs/api/withFormik.md | 136 + docs/get-started.md | 131 - docs/guides/arrays.md | 67 + docs/guides/form-submission.md | 56 + docs/guides/react-native.md | 47 + docs/guides/typescript.md | 128 + docs/guides/validation.md | 166 + docs/overview.md | 175 + docs/philosophy.md | 11 - docs/resources.md | 19 + docs/tutorial.md | 149 + website/.babelrc | 4 - website/.gitignore | 31 - website/.snyk | 22 - website/README.md | 4 +- website/core/Footer.js | 105 + website/gatsby-config.js | 38 - website/gatsby-node.js | 86 - website/now.json | 8 + website/package.json | 85 +- website/pages/en/examples.js | 66 + website/pages/en/help.js | 60 + website/pages/en/index.js | 192 + website/pages/en/users.js | 54 + website/sidebars.json | 7 + website/siteConfig.js | 161 + website/src/components/AppBar.tsx | 74 - website/src/components/Button.tsx | 93 - website/src/components/Card.tsx | 16 - website/src/components/Container.tsx | 8 - website/src/components/Debugger.css | 8 - website/src/components/Debugger.tsx | 100 - website/src/components/Example.tsx | 121 - website/src/components/Head.tsx | 30 - website/src/components/Icon/IcCapsule.tsx | 46 - website/src/components/Icon/IcDocker.tsx | 314 - website/src/components/Icon/IcOpenTable.tsx | 30 - website/src/components/Icon/IcSmyte.tsx | 14 - website/src/components/Icon/Icon.tsx | 25 - website/src/components/Icon/index.tsx | 1 - website/src/components/Link.tsx | 52 - website/src/components/Mark.tsx | 43 - website/src/components/Nav.tsx | 106 - website/src/components/Primitives.tsx | 10 - website/src/components/Sidebar.tsx | 148 - website/src/components/SidebarInner.tsx | 28 - website/src/components/Text.tsx | 9 - website/src/css/example.css | 121 - website/src/css/global.css | 84 - website/src/css/text-content.css | 230 - website/src/declarations.d.ts | 2 - website/src/examples/Arrays.js | 92 - website/src/examples/AsyncValidation.js | 55 - website/src/examples/Basic.js | 36 - website/src/examples/Basic.tsx | 43 - website/src/examples/CustomInputs.js | 139 - website/src/examples/MultiStep.js | 172 - website/src/examples/SchemaValidation.js | 69 - website/src/examples/SyncValidation.js | 46 - website/src/html.tsx | 63 - website/src/layouts/index.tsx | 63 - website/src/navigation.ts | 105 - website/src/pages/404.tsx | 11 - website/src/pages/examples/arrays.tsx | 18 - .../src/pages/examples/async-validation.tsx | 18 - website/src/pages/examples/basic.tsx | 20 - website/src/pages/examples/custom-inputs.tsx | 19 - website/src/pages/examples/multi-step.tsx | 18 - .../src/pages/examples/schema-validation.tsx | 19 - .../src/pages/examples/sync-validation.tsx | 19 - website/src/pages/gallery.tsx | 102 - website/src/pages/index.tsx | 41 - website/src/prism.js | 169 - website/src/templates/md.tsx | 74 - website/src/theme.tsx | 115 - website/src/utils.ts | 2 - website/static/css/code-blocks-buttons.css | 39 + website/static/css/custom.css | 374 + website/static/img/docusaurus.svg | 1 + website/static/img/favicon.png | Bin 0 -> 984 bytes website/static/img/favicon/favicon.ico | Bin 0 -> 9662 bytes website/static/img/formik.svg | 10 + website/static/img/logos/airbnb.svg | 29 + website/static/img/logos/docker.svg | 85 + website/static/img/logos/lyft.svg | 17 + website/static/img/logos/nokia.svg | 16 + website/static/img/logos/opentable.svg | 19 + website/static/img/logos/walmart.svg | 21 + website/static/img/oss_logo.png | Bin 0 -> 4370 bytes website/static/js/code-blocks-buttons.js | 49 + website/static/robots.txt | 2 - website/tsconfig.json | 24 - website/tslint.json | 3 - website/yarn.lock | 7970 +++-------------- 101 files changed, 4117 insertions(+), 11398 deletions(-) delete mode 100644 docs/api.md create mode 100644 docs/api/connect.md create mode 100644 docs/api/field.md create mode 100644 docs/api/form.md create mode 100644 docs/api/formik.md create mode 100644 docs/api/withFormik.md delete mode 100644 docs/get-started.md create mode 100644 docs/guides/arrays.md create mode 100644 docs/guides/form-submission.md create mode 100644 docs/guides/react-native.md create mode 100644 docs/guides/typescript.md create mode 100644 docs/guides/validation.md create mode 100644 docs/overview.md delete mode 100644 docs/philosophy.md create mode 100644 docs/resources.md create mode 100644 docs/tutorial.md delete mode 100644 website/.babelrc delete mode 100644 website/.gitignore delete mode 100644 website/.snyk create mode 100644 website/core/Footer.js delete mode 100644 website/gatsby-config.js delete mode 100644 website/gatsby-node.js create mode 100644 website/now.json create mode 100644 website/pages/en/examples.js create mode 100755 website/pages/en/help.js create mode 100755 website/pages/en/index.js create mode 100644 website/pages/en/users.js create mode 100644 website/sidebars.json create mode 100644 website/siteConfig.js delete mode 100644 website/src/components/AppBar.tsx delete mode 100644 website/src/components/Button.tsx delete mode 100644 website/src/components/Card.tsx delete mode 100644 website/src/components/Container.tsx delete mode 100644 website/src/components/Debugger.css delete mode 100644 website/src/components/Debugger.tsx delete mode 100644 website/src/components/Example.tsx delete mode 100644 website/src/components/Head.tsx delete mode 100644 website/src/components/Icon/IcCapsule.tsx delete mode 100644 website/src/components/Icon/IcDocker.tsx delete mode 100644 website/src/components/Icon/IcOpenTable.tsx delete mode 100644 website/src/components/Icon/IcSmyte.tsx delete mode 100644 website/src/components/Icon/Icon.tsx delete mode 100644 website/src/components/Icon/index.tsx delete mode 100644 website/src/components/Link.tsx delete mode 100644 website/src/components/Mark.tsx delete mode 100644 website/src/components/Nav.tsx delete mode 100644 website/src/components/Primitives.tsx delete mode 100644 website/src/components/Sidebar.tsx delete mode 100644 website/src/components/SidebarInner.tsx delete mode 100644 website/src/components/Text.tsx delete mode 100644 website/src/css/example.css delete mode 100644 website/src/css/global.css delete mode 100644 website/src/css/text-content.css delete mode 100644 website/src/declarations.d.ts delete mode 100644 website/src/examples/Arrays.js delete mode 100644 website/src/examples/AsyncValidation.js delete mode 100644 website/src/examples/Basic.js delete mode 100644 website/src/examples/Basic.tsx delete mode 100644 website/src/examples/CustomInputs.js delete mode 100644 website/src/examples/MultiStep.js delete mode 100644 website/src/examples/SchemaValidation.js delete mode 100644 website/src/examples/SyncValidation.js delete mode 100644 website/src/html.tsx delete mode 100644 website/src/layouts/index.tsx delete mode 100644 website/src/navigation.ts delete mode 100644 website/src/pages/404.tsx delete mode 100644 website/src/pages/examples/arrays.tsx delete mode 100644 website/src/pages/examples/async-validation.tsx delete mode 100644 website/src/pages/examples/basic.tsx delete mode 100644 website/src/pages/examples/custom-inputs.tsx delete mode 100644 website/src/pages/examples/multi-step.tsx delete mode 100644 website/src/pages/examples/schema-validation.tsx delete mode 100644 website/src/pages/examples/sync-validation.tsx delete mode 100644 website/src/pages/gallery.tsx delete mode 100644 website/src/pages/index.tsx delete mode 100644 website/src/prism.js delete mode 100644 website/src/templates/md.tsx delete mode 100644 website/src/theme.tsx delete mode 100644 website/src/utils.ts create mode 100644 website/static/css/code-blocks-buttons.css create mode 100644 website/static/css/custom.css create mode 100644 website/static/img/docusaurus.svg create mode 100644 website/static/img/favicon.png create mode 100644 website/static/img/favicon/favicon.ico create mode 100644 website/static/img/formik.svg create mode 100644 website/static/img/logos/airbnb.svg create mode 100644 website/static/img/logos/docker.svg create mode 100644 website/static/img/logos/lyft.svg create mode 100644 website/static/img/logos/nokia.svg create mode 100644 website/static/img/logos/opentable.svg create mode 100644 website/static/img/logos/walmart.svg create mode 100644 website/static/img/oss_logo.png create mode 100644 website/static/js/code-blocks-buttons.js delete mode 100644 website/static/robots.txt delete mode 100644 website/tsconfig.json delete mode 100644 website/tslint.json diff --git a/.gitignore b/.gitignore index 2d7456571..18991297b 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,15 @@ next.d.ts legacy.d.ts .idea *.orig +.DS_Store + +node_modules + +lib/core/metadata.js +lib/core/MetadataBlog.js + +website/translated_docs +website/build/ +website/yarn.lock +website/node_modules +website/i18n/* diff --git a/.travis.yml b/.travis.yml index 4efa669ff..bf9ea6bbb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,9 +9,16 @@ git: depth: 5 script: - yarn test -- --runInBand --no-cache --coverage && yarn size +before_deploy: npm i -g now # Install Now CLI on Travis deploy: - provider: script - script: curl -X POST -d '' $NETLIFY_BUILD_HOOK - skip_cleanup: true - on: - branch: master \ No newline at end of file + - provider: script # Run a custom deployment script which we will define below + script: cd website && yarn install && yarn build && now build --token $NOW_TOKEN --team palmer + skip_cleanup: true + on: + all_branches: true + master: false + - provider: script + script: cd website && yarn install && yarn build && now build --token=$NOW_TOKEN --team palmer && now alias --token=$NOW_TOKEN --team palmer + skip_cleanup: true + on: + master: true diff --git a/docs/api.md b/docs/api.md deleted file mode 100644 index 62be09990..000000000 --- a/docs/api.md +++ /dev/null @@ -1,896 +0,0 @@ ---- -path: "/docs/api" -date: "2015-07-01T22:12:03.284Z" -title: "API Reference" -description: "Formik API Reference documentation" -back: "/docs" ---- - -# API Reference - -## `` - -`` is a component that helps you with building forms. It uses a render -props pattern made popular by libraries like React Motion and React Router. - -```js -import React from 'react'; -import { Formik } from 'formik'; - -const BasicExample = () => ( -
-

My Form

- { - setTimeout(() => { - alert(JSON.stringify(values, null, 2)); - actions.setSubmitting(false); - }, 1000); - }} - render={props => ( -
- - {props.errors.name &&
{props.errors.name}
} - -
- )} - /> -
-); -``` - -### Formik render methods - -There are three ways to render things with `` - -* `` -* `` -* `` - -### Formik props - -All three render methods will be passed the same props: - -#### `dirty: boolean` - -Returns `true` if values are not deeply equal from initial values, `false` otherwise. -`dirty` is a readonly computed property and should not be mutated directly. - -#### `errors: { [field: string]: string }` - -Form validation errors. Should match the shape of your form's [`values`] defined -in `initialValues`. If you are using [`validationSchema`] (which you should be), -keys and shape will match your schema exactly. Internally, Formik transforms raw -[Yup validation errors](https://github.com/jquense/yup#validationerrorerrors-string--arraystring-value-any-path-string) -on your behalf. If you are using [`validate`], then that function will determine -the `errors` objects shape. - -#### `handleBlur: (e: any) => void` - -`onBlur` event handler. Useful for when you need to track whether an input has -been [`touched`] or not. This should be passed to `` - -DOM-only. Use [`setFieldTouched`] in React Native. - -#### `handleChange: (e: React.ChangeEvent) => void` - -General input change event handler. This will update the `values[key]` where -`key` is the event-emitting input's `name` attribute. If the `name` attribute is -not present, `handleChange` will look for an input's `id` attribute. Note: -"input" here means all HTML inputs. - -DOM-only. Use [`setFieldValue`] in React Native. - -#### `handleReset: () => void` - -Reset handler. Will reset the form to its initial state. This should be passed -to `` - -#### `handleSubmit: (e: React.FormEvent) => void` - -Submit handler. This should be passed to `
...
` - -#### `isSubmitting: boolean` - -Submitting state. Either `true` or `false`. Formik will set this to `true` on -your behalf before calling [`handleSubmit`] to reduce boilerplate. - -#### `isValid: boolean` - -Returns `true` if the there are no [`errors`], or the result of -[`isInitialValid`] the form if is in "pristine" condition (i.e. not [`dirty`])). - -#### `resetForm: (nextValues?: Values) => void` - -Imperatively reset the form. This will clear [`errors`] and [`touched`], set -[`isSubmitting`] to `false` and rerun `mapPropsToValues` with the current -`WrappedComponent`'s `props` or what's passed as an argument. The latter is -useful for calling `resetForm` within `componentWillReceiveProps`. - -#### `setErrors: (fields: { [field: string]: string }) => void` - -Set `errors` imperatively. - -#### `setFieldError: (field: string, errorMsg: string) => void` - -Set the error message of a field imperatively. `field` should match the key of -[`errors`] you wish to update. Useful for creating custom input error handlers. - -#### `setFieldTouched: (field: string, isTouched: boolean, shouldValidate?: boolean) => void` - -Set the touched state of a field imperatively. `field` should match the key of -[`touched`] you wish to update. Useful for creating custom input blur handlers. Calling this method will trigger validation to run if [`validateOnBlur`] is set to `true` (which it is by default). You can also explicitly prevent/skip validation by passing a third argument as `false`. - -#### `submitForm: () => void` - -Trigger a form submission. - -#### `submitCount: number` - -Number of times user tried to submit the form. Increases when [`handleSubmit`](#handlesubmit-values-values-formikbag-formikbag--void) is called, resets after calling -[`handleReset`](#handlereset---void). `submitCount` is readonly computed property and should not be mutated directly. - -#### `setFieldValue: (field: string, value: any, shouldValidate?: boolean) => void` - -Set the value of a field imperatively. `field` should match the key of -[`values`] you wish to update. Useful for creating custom input change handlers. Calling this will trigger validation to run if [`validateOnChange`] is set to `true` (which it is by default). You can also explicitly prevent/skip validation by passing a third argument as `false`. - -#### `setStatus: (status?: any) => void` - -Set a top-level [`status`] to anything you want imperatively. Useful for -controlling arbitrary top-level state related to your form. For example, you can -use it to pass API responses back into your component in [`handleSubmit`]. - -#### `setSubmitting: (isSubmitting: boolean) => void` - -Set [`isSubmitting`] imperatively. - -#### `setTouched: (fields: { [field: string]: boolean }) => void` - -Set [`touched`] imperatively. - -#### `setValues: (fields: { [field: string]: any }) => void` - -Set [`values`] imperatively. - -#### `status?: any` - -A top-level status object that you can use to represent form state that can't -otherwise be expressed/stored with other methods. This is useful for capturing -and passing through API responses to your inner component. - -`status` should only be modified by calling -[`setStatus: (status?: any) => void`](#setstatus-status-any--void) - -#### `touched: { [field: string]: boolean }` - -Touched fields. Each key corresponds to a field that has been touched/visited. - -#### `values: { [field: string]: any }` - -Your form's values. Will have the shape of the result of [`mapPropsToValues`] -(if specified) or all props that are not functions passed to your wrapped -component. - -#### `validateForm: (values?: any) => void` - -Imperatively call your [`validate`] or [`validateSchema`] depending on what was specified. You can optionally pass values to validate against and this modify Formik state accordingly, otherwise this will use the current `values` of the form. - -#### `validateField: (field: string) => void` - -Imperatively call field's [`validate`] function if specified for given field. Formik will use the current field value. - -### `component` - -```tsx -; - -const ContactForm = ({ - handleSubmit, - handleChange, - handleBlur, - values, - errors, -}) => ( -
- - {errors.name &&
{errors.name}
} - -
-}; -``` - -**Warning:** `` takes precedence over `` so -don’t use both in the same ``. - -### `render: (props: FormikProps) => ReactNode` - -```tsx - } /> - - ( -
- - {errors.name && -
- {errors.name} -
} - -
- )} -/> -``` - -### `children: func` - -```tsx - } /> - -// or... - - - {({ handleSubmit, handleChange, handleBlur, values, errors }) => ( -
- - {errors.name && -
- {errors.name} -
} - -
- )} -
-``` - -### `enableReinitialize?: boolean` - -Default is `false`. Control whether Formik should reset the form if -[`initialValues`] changes (using deep equality). - -### `isInitialValid?: boolean` - -Default is `false`. Control the initial value of [`isValid`] prop prior to -mount. You can also pass a function. Useful for situations when you want to -enable/disable a submit and reset buttons on initial mount. - -### `initialValues?: Values` - -Initial field values of the form, Formik will make these values available to -render methods component as [`props.values`][`values`]. - -Even if your form is empty by default, you must initialize all fields with -initial values otherwise React will throw an error saying that you have changed -an input from uncontrolled to controlled. - -Note: `initialValues` not available to the higher-order component, use -[`mapPropsToValues`] instead. - -### `onReset?: (values: Values, formikBag: FormikBag) => void` - -Your optional form reset handler. It is passed your forms [`values`] and the -"FormikBag". - -### `onSubmit: (values: Values, formikBag: FormikBag) => void` - -Your form submission handler. It is passed your forms [`values`] and the -"FormikBag", which includes an object containing a subset of the -[injected props and methods](#injected-props-and-methods) (i.e. all the methods -with names that start with `set` + `resetForm`) and any props that were -passed to the the wrapped component. - -Note: [`errors`], [`touched`], [`status`] and all event handlers are NOT -included in the `FormikBag`. - -### `validate?: (values: Values) => FormikErrors | Promise` - -_Note: I suggest using [`validationSchema`] and Yup for validation. However, -`validate` is a dependency-free, straightforward way to validate your forms._ - -Validate the form's [`values`] with function. This function can either be: - -1. Synchronous and return an [`errors`] object. - -```js -// Synchronous validation -const validate = (values, props) => { - 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; -}; -``` - -* Asynchronous and return a Promise that's error in an [`errors`] object - -```js -// Async Validation -const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)) - -const validate = (values, props) => { - return sleep(2000).then(() => { - let errors = {} - if (['admin', 'null', 'god']).includes(values.username) { - errors.username = 'Nice try' - } - // ... - if (Object.keys(errors).length) { - throw errors - } - }) -} -``` - -### `validateOnBlur?: boolean` - -Default is `true`. Use this option to run validations on `blur` events. More -specifically, when either [`handleBlur`], [`setFieldTouched`], or [`setTouched`] -are called. - -### `validateOnChange?: boolean` - -Default is `true`. Use this option to tell Formik to run validations on `change` -events and `change`-related methods. More specifically, when either -[`handleChange`], [`setFieldValue`], or [`setValues`] are called. - -### `validationSchema?: Schema | (() => Schema)` - -[A Yup schema](https://github.com/jquense/yup) or a function that returns a Yup -schema. This is used for validation. Errors are mapped by key to the inner -component's [`errors`]. Its keys should match those of [`values`]. - -## `` - -`` will automagically hook up inputs to Formik. It uses the `name` -attribute to match up with Formik state. `` will default to an -`` element. To change the underlying element of ``, specify a -`component` prop. It can either be a string like `select` or another React -component. `` can also take a `render` prop. - -```js -import React from 'react'; -import { Formik, Field } from 'formik'; - -const Example = () => ( -
-

My Form

- { - setTimeout(() => { - alert(JSON.stringify(values, null, 2)); - actions.setSubmitting(false); - }, 1000); - }} - render={(props: FormikProps) => ( -
- - - - - - - - ( - - )} - /> - - - )} - /> -
-); - -const CustomInputComponent: React.SFC< - FieldProps & CustomInputProps -> = ({ - field, // { name, value, onChange, onBlur } - form: { touched, errors }, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc. - ...props -}) => ( -
- - {touched[field.name] && - errors[field.name] &&
{errors[field.name]}
} -
-); -``` - -### `validate?: (value: any) => undefined | string | Promise` - -You can run independent field-level validations by passing a function to the -`validate>` prop. The function will respect the [`validateOnBlur`] and -[`validateOnChange`] config/props specified in the `'s` parent `` -/ `withFormik`. This function can be either be: - -* Synchronous and if invalid, return a `string` containing the error message or - return `undefined`. - -```js -// Synchronous validation for Field -const validate = value => { - let errorMessage; - if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value)) { - errorMessage = 'Invalid email address'; - } - return errorMessage; -}; -``` - -* async: Return a Promise that throws a `string` containing the error message. - This works like Formik's [`validate`], but instead of returning an [`errors`] - object, it's just a `string`. - -* Asynchronous and return a Promise that's error is an string with the error - message - -```js -// Async validation for Field -const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); - -const validate = value => { - return sleep(2000).then(() => { - if (['admin', 'null', 'god'].includes(value)) { - throw 'Nice try'; - } - }); -}; -``` - -Note: To allow for i18n libraries, the TypeScript typings for `validate` are -slightly relaxed and allow you to return a `Function` (e.g. `i18n('invalid')`). - -## `` - -`` is a component that helps with common array/list manipulations. You pass it a `name` property with the path to the key within `values` that holds the relevant array. `` will then give you access to array helper methods via render props. For convenience, calling these methods will trigger validation and also manage `touched` for you. - -```jsx -import React from 'react'; -import { Formik, Form, Field, FieldArray } from 'formik'; - -// Here is an example of a form with an editable list. -// Next to each input are buttons for insert and remove. -// If the list is empty, there is a button to add an item. -export const FriendList = () => ( -
-

Friend List

- - setTimeout(() => { - alert(JSON.stringify(values, null, 2)); - }, 500) - } - render={({ values }) => ( - ( -
- {values.friends && values.friends.length > 0 ? ( - values.friends.map((friend, index) => ( -
- - - -
- )) - ) : ( - - )} -
- -
-
- )} - /> - )} - /> -
-); -``` - -#### `name: string` - -The name or path to the relevant key in [`values`]. - -#### `validateOnChange?: boolean` - -Default is `true`. Determines if form validation should or should not be run _after_ any array manipulations. - -### FieldArray Validation Gotchas - -Validation can be tricky with ``. - -If you use [`validationSchema`] and your form has array validation requirements (like a min length) as well as nested array field requirements, displaying errors can be tricky. Formik/Yup will show validation errors inside out. For example, - -```js -const schema = Yup.object().shape({ - friends: Yup.array() - .of( - Yup.object().shape({ - name: Yup.string() - .min(4, 'too short') - .required('Required'), // these constraints take precedence - salary: Yup.string() - .min(3, 'cmon') - .required('Required'), // these constraints take precedence - }) - ) - .required('Must have friends') // these constraints are shown if and only if inner constraints are satisfied - .min(3, 'Minimum of 3 friends'), -}); -``` - -Since Yup and your custom validation function should always output error messages as strings, you'll need to sniff whether your nested error is an array or a string when you go to display it. - -So...to display `'Must have friends'` and `'Minimum of 3 friends'` (our example's array validation constraints)... - -**_Bad_** - -```js -// within a `FieldArray`'s render -const FriendArrayErrors = errors => - errors.friends ?
{errors.friends}
: null; // app will crash -``` - -**_Good_** - -```js -// within a `FieldArray`'s render -const FriendArrayErrors = errors => - typeof friends === 'string' ?
{errors.friends}
: null; -``` - -For the nested field errors, you should assume that no part of the object is defined unless you've checked for it. Thus, you may want to do yourself a favor and make a custom `` component that looks like this: - -```js -import { Field, getIn } from 'formik'; - -const ErrorMessage = ({ name }) => ( - { - const error = getIn(form.errors, name); - const touch = getIn(form.touched, name); - return touch && error ? error : null; - }} - /> -); - -// Usage -; // => null, 'too short', or 'required' -``` - -_NOTE_: In Formik v0.12 / 1.0, a new `meta` prop may be be added to `Field` and `FieldArray` that will give you relevant metadata such as `error` & `touch`, which will save you from having to use Formik or lodash's getIn or checking if the path is defined on your own. - -### FieldArray Helpers - -The following methods are made available via render props. - -* `push: (obj: any) => void`: Add a value to the end of an array -* `swap: (indexA: number, indexB: number) => void`: Swap two values in an array -* `move: (from: number, to: number) => void`: Move an element in an array to another index -* `insert: (index: number, value: any) => void`: Insert an element at a given index into the array -* `unshift: (value: any) => number`: Add an element to the beginning of an array and return its length -* `remove(index: number): T | undefined`: Remove an element at an index of an array and return it -* `pop(): T | undefined`: Remove and return value from the end of the array - -### FieldArray render methods - -There are three ways to render things with `` - -* `` -* `` - -#### `render: (arrayHelpers: ArrayHelpers) => React.ReactNode` - -```jsx -import React from 'react'; -import { Formik, Form, Field, FieldArray } from 'formik' - -export const FriendList = () => ( -
-

Friend List

- ( - ( -
- {/*... use these however you want */} -
- )} - /> - /> -
-); -``` - -#### `component: React.ReactNode` - -```jsx -import React from 'react'; -import { Formik, Form, Field, FieldArray } from 'formik' - - -export const FriendList = () => ( -
-

Friend List

- ( - - /> -
-); - - -// In addition to the array helpers, Formik state and helpers -// (values, touched, setXXX, etc) are provided through a `form` -// prop -export const MyDynamicForm = ({ - move, swap, push, insert, unshift, pop, form -}) => ( -
- {/** whatever you need to do */} -
-); -``` - -## `
` - -Like ``, `` is a helper component you can use to save time. It is -tiny wrapper around ``. This -means you don't need to explicitly type out `` if you don't want to. - -**ReactDOM only** - -```jsx -import React from 'react'; -import { Formik, Field, Form } from 'formik'; - -const Example = () => ( -
-

My Form

- { - setTimeout(() => { - alert(JSON.stringify(values, null, 2)); - actions.setSubmitting(false); - }, 1000); - }} - component={MyForm} - /> -
-); - -const MyForm = () => ( - - - - - - - - - -); -``` - -## `withFormik(options)` - -Create a higher-order React component class that passes props and form handlers -(the "`FormikBag`") into your component derived from supplied options. - -### `options` - -#### `displayName?: string` - -When your inner form component is a stateless functional component, you can use -the `displayName` option to give the component a proper name so you can more -easily find it in -[React DevTools](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en). -If specified, your wrapped form will show up as `Formik(displayName)`. If -omitted, it will show up as `Formik(Component)`. This option is not required for -class components (e.g. `class XXXXX extends React.Component {..}`). - -#### `enableReinitialize?: boolean` - -Default is `false`. Control whether Formik should reset the form if the wrapped -component props change (using deep equality). - -#### `handleSubmit: (values: Values, formikBag: FormikBag) => void` - -Your form submission handler. It is passed your forms [`values`] and the -"FormikBag", which includes an object containing a subset of the -[injected props and methods](#injected-props-and-methods) (i.e. all the methods -with names that start with `set` + `resetForm`) and any props that were -passed to the the wrapped component. - -#### The "FormikBag": - -* `props` (props passed to the wrapped component) -* [`resetForm`] -* [`setErrors`] -* [`setFieldError`] -* [`setFieldTouched`] -* [`setFieldValue`] -* [`setStatus`] -* [`setSubmitting`] -* [`setTouched`] -* [`setValues`] - -Note: [`errors`], [`touched`], [`status`] and all event handlers are NOT -included in the `FormikBag`. - -#### `isInitialValid?: boolean | (props: Props) => boolean` - -Default is `false`. Control the initial value of [`isValid`] prop prior to -mount. You can also pass a function. Useful for situations when you want to -enable/disable a submit and reset buttons on initial mount. - -#### `mapPropsToValues?: (props: Props) => Values` - -If this option is specified, then Formik will transfer its results into -updatable form state and make these values available to the new component as -[`props.values`][`values`]. If `mapPropsToValues` is not specified, then Formik -will map all props that are not functions to the inner component's -[`props.values`][`values`]. That is, if you omit it, Formik will only pass -`props` where `typeof props[k] !== 'function'`, where `k` is some key. - -Even if your form is not receiving any props from its parent, use -`mapPropsToValues` to initialize your forms empty state. - -#### `validate?: (values: Values, props: Props) => FormikErrors | Promise` - -_Note: I suggest using [`validationSchema`] and Yup for validation. However, -`validate` is a dependency-free, straightforward way to validate your forms._ - -Validate the form's [`values`] with function. This function can either be: - -1. Synchronous and return an [`errors`] object. - -```js -// Synchronous validation -const validate = (values, props) => { - 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; -}; -``` - -* Asynchronous and return a Promise that's error is an [`errors`] object - -```js -// Async Validation -const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); - -const validate = (values, props) => { - return sleep(2000).then(() => { - let errors = {}; - if (['admin', 'null', 'god'].includes(values.username)) { - errors.username = 'Nice try'; - } - // ... - if (Object.keys(errors).length) { - throw errors; - } - }); -}; -``` - -#### `validateOnBlur?: boolean` - -Default is `true`. Use this option to run validations on `blur` events. More -specifically, when either [`handleBlur`], [`setFieldTouched`], or [`setTouched`] -are called. - -#### `validateOnChange?: boolean` - -Default is `true`. Use this option to tell Formik to run validations on `change` -events and `change`-related methods. More specifically, when either -[`handleChange`], [`setFieldValue`], or [`setValues`] are called. - -#### `validationSchema?: Schema | ((props: Props) => Schema)` - -[A Yup schema](https://github.com/jquense/yup) or a function that returns a Yup -schema. This is used for validation. Errors are mapped by key to the inner -component's [`errors`]. Its keys should match those of [`values`]. - -### Injected props and methods - -These are identical to the props of ` ...} />` - -[`displayname`]: #displayname-string -[`handlesubmit`]: #handlesubmit-payload-formikbag--void -[`formikbag`]: #the-formikbag -[`isinitialvalid`]: #isinitialvalid-boolean--props-props--boolean -[`mappropstovalues`]: #mappropstovalues-props--props -[`validate`]: #validate-values-values-props-props--formikerrorvalues--promiseany -[`validateonblur`]: #validateonblur-boolean -[`validateonchange`]: #validateonchange-boolean -[`validationschema`]: #validationschema-schema--props-props--schema -[injected props and methods]: #injected-props-and-methods -[`dirty`]: #dirty-boolean -[`errors`]: #errors--field-string-string- -[`handleblur`]: #handleblur-e-any--void -[`handlechange`]: #handlechange-e-reactchangeeventany--void -[`handlereset`]: #handlereset---void -[`handlesubmit`]: #handlesubmit-e-reactformeventhtmlformevent--void -[`issubmitting`]: #issubmitting-boolean -[`isvalid`]: #isvalid-boolean -[`resetform`]: #resetform-nextprops-props--void -[`seterrors`]: #seterrors-fields--field-string-string---void -[`setfielderror`]: #setfielderror-field-string-errormsg-string--void -[`setfieldtouched`]: #setfieldtouched-field-string-istouched-boolean-shouldvalidate-boolean--void -[`setfieldvalue`]: #setfieldvalue-field-string-value-any-shouldvalidate-boolean--void -[`setstatus`]: #setstatus-status-any--void -[`setsubmitting`]: #setsubmitting-boolean--void -[`settouched`]: #settouched-fields--field-string-boolean---void -[`setvalues`]: #setvalues-fields--field-string-any---void -[`status`]: #status-any -[`touched`]: #touched--field-string-boolean- -[`values`]: #values--field-string-any- diff --git a/docs/api/connect.md b/docs/api/connect.md new file mode 100644 index 000000000..652213382 --- /dev/null +++ b/docs/api/connect.md @@ -0,0 +1,30 @@ +--- +id: connect +title: connect +--- + +`connect()` is a higher-order component (HoC) that allows you to hook anything into Formik's context. It is used internally to construct `` and `
`, but you can use it to build out new components as your needs change. + +## Type signature + +```tsx +connect(Comp: React.ComponentType>) => React.ComponentType +``` + +## Example + +```jsx +import React from 'react'; +import { connect } from 'formik'; + +// This component renders an error message if a field has +// an error and it's already been touched. +const ErrorMessage = props => { + // All FormikProps available on props.formik! + const error = props.formik.errors[props.name]; + const touch = props.formik.touched[props.name]; + return touch && error ? error : null; +}; + +export default connect(ErrorMessage); +``` diff --git a/docs/api/field.md b/docs/api/field.md new file mode 100644 index 000000000..392bc314c --- /dev/null +++ b/docs/api/field.md @@ -0,0 +1,264 @@ +--- +id: field +title: Field +--- + +`` will automagically hook up inputs to Formik. It uses the `name` +attribute to match up with Formik state. `` will default to an HTML +`` element. + +## Field render props + +There are 3 ways to render things with ``. + +* `` +* `` +* `` + +Aside from string-only `component`, each render prop is passed the same props for your convenience. + +Field's render props are an object containing: + +* `field`: An object containing `onChange`, `onBlur`, `name`, and `value` of the field +* `form`: The Formik bag. +* Any other props passed to field + +## Example + +```jsx +import React from 'react'; +import { Formik, Field } from 'formik'; + +const Example = () => ( +
+

My Form

+ { + setTimeout(() => { + alert(JSON.stringify(values, null, 2)); + actions.setSubmitting(false); + }, 1000); + }} + render={(props: FormikProps) => ( + + + + + + + + + ( + + )} + /> + + + )} + /> +
+); + +const CustomInputComponent = ({ + field, // { name, value, onChange, onBlur } + form: { touched, errors }, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc. + ...props +}) => ( +
+ + {touched[field.name] && + errors[field.name] &&
{errors[field.name]}
} +
+); +``` + +#### Props + + + +--- + +# Reference + +## Props + +### `children` + +`children?: React.ReactNode | ((props: FieldProps) => React.ReactNode)` + +Either JSX elements or callback function. Same as `render`. + +```jsx +// Children can be JSX elements + + + + + + +// Or a callback function + +{({ field, form }) => ( +
+ + {touched[field.name] && + errors[field.name] &&
{errors[field.name]}
} +
+)} +
+``` + +### `component` + +`component?: string | React.ComponentType` + +Either a React component or the name of an HTML element to render. That is, one of the following: + +* `input` +* `select` +* `textarea` +* A custom React Component + +Custom React Components will be passed `FieldProps` which is same `render` prop parameters of `` plus any other props passed to directly to ``. + +Default is `'input'` (so an `` is rendered by default) + +```jsx +// Renders an HTML by default + + +// Renders an HTML + {touched[field.name] && + errors[field.name] &&
{errors[field.name]}
} + +); +``` + +### `innerRef` + +`innerRef?: (el: React.HTMLElement => void)` + +When you are **not** using a custom component and you need to access the underlying DOM node created by `Field` (e.g. to call `focus`), pass the callback to the `innerRef` prop instead. + +### `name` + +`name: string` +**Required** + +A field's name in Formik state. To access nested objects or arrays, name can also accept lodash-like dot path like `social.facebook` or `friends[0].firstName` + +### `render` + +`render?: (props: FieldProps) => React.ReactNode` + +A function that returns a React. + +```jsx +// Renders an HTML by default + + +// Renders an HTML + {touched[field.name] && + errors[field.name] &&
{errors[field.name]}
} + +); +``` + +### `validate` + +`validate?: (value: any) => undefined | string | Promise` + +You can run independent field-level validations by passing a function to the +`validate` prop. The function will respect the `validateOnBlur` and +`validateOnChange` config/props specified in the `'s` parent `` +/ `withFormik`. This function can be either be synchronous or asynchronous: + +* Sync: if invalid, return a `string` containing the error message or + return `undefined`. + +* Async: return a Promise that throws a `string` containing the error message. + This works like Formik's `validate`, but instead of returning an `errors` + object, it's just a `string`. + +```js +import React from 'react'; +import { Formik, Form, Field } from 'formik'; + +// Synchronous validation function +const validate = value => { + let errorMessage; + if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value)) { + errorMessage = 'Invalid email address'; + } + return errorMessage; +}; + +// Async validation function +const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); + +const validateAsync = value => { + return sleep(2000).then(() => { + if (['admin', 'null', 'god'].includes(value)) { + throw 'Nice try'; + } + }); +}; + +// example usage +const MyForm = () => ( + alert(JSON.stringify(values, null, 2))} + > + {({ errors, touched }) => ( +
+ + {errors.email && touched.email ?
{errors.email}
: null} + + {errors.username && touched.username ? ( +
{errors.username}
+ ) : null} + + + )} +
+); +``` + +Note: To allow for i18n libraries, the TypeScript typings for `validate` are +slightly relaxed and allow you to return a `Function` (e.g. `i18n('invalid')`). diff --git a/docs/api/form.md b/docs/api/form.md new file mode 100644 index 000000000..49afa445a --- /dev/null +++ b/docs/api/form.md @@ -0,0 +1,10 @@ +--- +id: form +title: Form +--- + +Form is a small wrapper around an HTML `
` element that automatically hooks into Formik's `handleSubmit`. + +```jsx + +``` diff --git a/docs/api/formik.md b/docs/api/formik.md new file mode 100644 index 000000000..db95cd733 --- /dev/null +++ b/docs/api/formik.md @@ -0,0 +1,378 @@ +--- +id: formik +title: Formik +--- + +`` is a component that helps you with building forms. It uses a render +props pattern made popular by libraries like React Motion and React Router. + +## Example + +```jsx +import React from 'react'; +import { Formik } from 'formik'; + +const BasicExample = () => ( +
+

My Form

+ { + setTimeout(() => { + alert(JSON.stringify(values, null, 2)); + actions.setSubmitting(false); + }, 1000); + }} + render={props => ( + + + {props.errors.name &&
{props.errors.name}
} + + + )} + /> +
+); +``` + +#### Props + + + +--- + +# Reference + +## Props + +### Formik render methods and props + +There are three ways to render things with `` + +* `` +* `` +* `` + +All three render methods will be passed the same props: + +#### `dirty: boolean` + +Returns `true` if values are not deeply equal from initial values, `false` otherwise. +`dirty` is a readonly computed property and should not be mutated directly. + +#### `errors: { [field: string]: string }` + +Form validation errors. Should match the shape of your form's [`values`] defined +in `initialValues`. If you are using [`validationSchema`] (which you should be), +keys and shape will match your schema exactly. Internally, Formik transforms raw +[Yup validation errors](https://github.com/jquense/yup#validationerrorerrors-string--arraystring-value-any-path-string) +on your behalf. If you are using [`validate`], then that function will determine +the `errors` objects shape. + +#### `handleBlur: (e: any) => void` + +`onBlur` event handler. Useful for when you need to track whether an input has +been [`touched`] or not. This should be passed to `` + +DOM-only. Use [`setFieldTouched`] in React Native. + +#### `handleChange: (e: React.ChangeEvent) => void` + +General input change event handler. This will update the `values[key]` where +`key` is the event-emitting input's `name` attribute. If the `name` attribute is +not present, `handleChange` will look for an input's `id` attribute. Note: +"input" here means all HTML inputs. + +DOM-only. Use [`setFieldValue`] in React Native. + +#### `handleReset: () => void` + +Reset handler. Will reset the form to its initial state. This should be passed +to `` + +#### `handleSubmit: (e: React.FormEvent) => void` + +Submit handler. This should be passed to `
...
`. To learn more about the submission process, see [How Form Submission Works](#how-form-submission-works). + +#### `isSubmitting: boolean` + +Submitting state of the form. Returns `true` if submission is in progress and `false` otherwise. IMPORTANT: Formik will set this to `true` as soon as submission is _attempted_. To learn more about the submission process, see [How Form Submission Works](#how-form-submission-works). + +#### `isValid: boolean` + +Returns `true` if the there are no [`errors`], or the result of +[`isInitialValid`] the form if is in "pristine" condition (i.e. not [`dirty`])). + +#### `isValidating: boolean` + +Returns `true` if Formik is running any validation function, `false` otherwise. To learn more about what happens with `isValidating` during the submission process, see [How Form Submission Works](#how-form-submission-works). + +#### `resetForm: (nextValues?: Values) => void` + +Imperatively reset the form. This will clear [`errors`] and [`touched`], set +[`isSubmitting`] to `false`, `isValidating` to `false`, and rerun `mapPropsToValues` with the current +`WrappedComponent`'s `props` or what's passed as an argument. The latter is +useful for calling `resetForm` within `componentWillReceiveProps`. + +#### `setErrors: (fields: { [field: string]: string }) => void` + +Set `errors` imperatively. + +#### `setFieldError: (field: string, errorMsg: string) => void` + +Set the error message of a field imperatively. `field` should match the key of +[`errors`] you wish to update. Useful for creating custom input error handlers. + +#### `setFieldTouched: (field: string, isTouched: boolean, shouldValidate?: boolean) => void` + +Set the touched state of a field imperatively. `field` should match the key of +[`touched`] you wish to update. Useful for creating custom input blur handlers. Calling this method will trigger validation to run if [`validateOnBlur`] is set to `true` (which it is by default). You can also explicitly prevent/skip validation by passing a third argument as `false`. + +#### `submitForm: () => void` + +Trigger a form submission. + +#### `submitCount: number` + +Number of times user tried to submit the form. Increases when [`handleSubmit`](#handlesubmit-values-values-formikbag-formikbag--void) is called, resets after calling +[`handleReset`](#handlereset---void). `submitCount` is readonly computed property and should not be mutated directly. + +#### `setFieldValue: (field: string, value: any, shouldValidate?: boolean) => void` + +Set the value of a field imperatively. `field` should match the key of +[`values`] you wish to update. Useful for creating custom input change handlers. Calling this will trigger validation to run if [`validateOnChange`] is set to `true` (which it is by default). You can also explicitly prevent/skip validation by passing a third argument as `false`. + +#### `setStatus: (status?: any) => void` + +Set a top-level [`status`] to anything you want imperatively. Useful for +controlling arbitrary top-level state related to your form. For example, you can +use it to pass API responses back into your component in [`handleSubmit`]. + +#### `setSubmitting: (isSubmitting: boolean) => void` + +Set [`isSubmitting`] imperatively. + +#### `setTouched: (fields: { [field: string]: boolean }) => void` + +Set [`touched`] imperatively. + +#### `setValues: (fields: { [field: string]: any }) => void` + +Set [`values`] imperatively. + +#### `status?: any` + +A top-level status object that you can use to represent form state that can't +otherwise be expressed/stored with other methods. This is useful for capturing +and passing through API responses to your inner component. + +`status` should only be modifed by calling +[`setStatus: (status?: any) => void`](#setstatus-status-any--void) + +#### `touched: { [field: string]: boolean }` + +Touched fields. Each key corresponds to a field that has been touched/visited. + +#### `values: { [field: string]: any }` + +Your form's values. Will have the shape of the result of [`mapPropsToValues`] +(if specified) or all props that are not functions passed to your wrapped +component. + +#### `validateForm: (values?: any) => void` + +Imperatively call your [`validate`] or [`validateSchema`] depending on what was specified. You can optionally pass values to validate against and this modify Formik state accordingly, otherwise this will use the current `values` of the form. + +#### `validateField: (field: string) => void` + +Imperatively call field's [`validate`] function if specified for given field. Formik will use the current field value. + +### `component` + +```jsx +; + +const ContactForm = ({ + handleSubmit, + handleChange, + handleBlur, + values, + errors, +}) => ( +
+ + {errors.name &&
{errors.name}
} + +
+}; +``` + +**Warning:** `` takes precendence over `` so +don’t use both in the same ``. + +### `render: (props: FormikProps) => ReactNode` + +```jsx + } /> + + ( +
+ + {errors.name && +
+ {errors.name} +
} + +
+ )} +/> +``` + +### `children: func` + +```jsx + } /> + +// or... + + + {({ handleSubmit, handleChange, handleBlur, values, errors }) => ( +
+ + {errors.name && +
+ {errors.name} +
} + +
+ )} +
+``` + +### `enableReinitialize?: boolean` + +Default is `false`. Control whether Formik should reset the form if +[`initialValues`] changes (using deep equality). + +### `isInitialValid?: boolean` + +Default is `false`. Control the initial value of [`isValid`] prop prior to +mount. You can also pass a function. Useful for situations when you want to +enable/disable a submit and reset buttons on initial mount. + +### `initialValues?: Values` + +Initial field values of the form, Formik will make these values available to +render methods component as [`props.values`][`values`]. + +Even if your form is empty by default, you must initialize all fields with +initial values otherwise React will throw an error saying that you have changed +an input from uncontrolled to controlled. + +Note: `initialValues` not available to the higher-order component, use +[`mapPropsToValues`] instead. + +### `onReset?: (values: Values, formikBag: FormikBag) => void` + +Your optional form reset handler. It is passed your forms [`values`] and the +"FormikBag". + +### `onSubmit: (values: Values, formikBag: FormikBag) => void` + +Your form submission handler. It is passed your forms [`values`] and the +"FormikBag", which includes an object containing a subset of the +[injected props and methods](#injected-props-and-methods) (i.e. all the methods +with names that start with `set` + `resetForm`) and any props that were +passed to the the wrapped component. + +Note: [`errors`], [`touched`], [`status`] and all event handlers are NOT +included in the `FormikBag`. + +### `validate?: (values: Values) => FormikErrors | Promise` + +_Note: I suggest using [`validationSchema`] and Yup for validation. However, +`validate` is a dependency-free, straightforward way to validate your forms._ + +Validate the form's [`values`] with function. This function can either be: + +1. Synchronous and return an [`errors`] object. + +```js +// Synchronous validation +const validate = (values, props) => { + 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; +}; +``` + +* Asynchronous and return a Promise that's error in an [`errors`] object + +```js +// Async Validation +const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); + +const validate = (values, props) => { + return sleep(2000).then(() => { + let errors = {}; + if (['admin', 'null', 'god'].includes(values.username)) { + errors.username = 'Nice try'; + } + // ... + if (Object.keys(errors).length) { + throw errors; + } + }); +}; +``` + +### `validateOnBlur?: boolean` + +Default is `true`. Use this option to run validations on `blur` events. More +specifically, when either [`handleBlur`], [`setFieldTouched`], or [`setTouched`] +are called. + +### `validateOnChange?: boolean` + +Default is `true`. Use this option to tell Formik to run validations on `change` +events and `change`-related methods. More specifically, when either +[`handleChange`], [`setFieldValue`], or [`setValues`] are called. + +### `validationSchema?: Schema | (() => Schema)` + +[A Yup schema](https://github.com/jquense/yup) or a function that returns a Yup +schema. This is used for validation. Errors are mapped by key to the inner +component's [`errors`]. Its keys should match those of [`values`]. diff --git a/docs/api/withFormik.md b/docs/api/withFormik.md new file mode 100644 index 000000000..9b01927c6 --- /dev/null +++ b/docs/api/withFormik.md @@ -0,0 +1,136 @@ +--- +id: withFormik +title: withFormik +--- + +Create a higher-order React component class that passes props and form handlers +(the "`FormikBag`") into your component derived from supplied options. + +## `options` + + + +### `displayName?: string` + +When your inner form component is a stateless functional component, you can use +the `displayName` option to give the component a proper name so you can more +easily find it in +[React DevTools](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en). +If specified, your wrapped form will show up as `Formik(displayName)`. If +omitted, it will show up as `Formik(Component)`. This option is not required for +class components (e.g. `class XXXXX extends React.Component {..}`). + +### `enableReinitialize?: boolean` + +Default is `false`. Control whether Formik should reset the form if the wrapped +component props change (using deep equality). + +### `handleSubmit: (values: Values, formikBag: FormikBag) => void` + +Your form submission handler. It is passed your forms [`values`] and the +"FormikBag", which includes an object containing a subset of the +[injected props and methods](#injected-props-and-methods) (i.e. all the methods +with names that start with `set` + `resetForm`) and any props that were +passed to the the wrapped component. + +#### The "FormikBag": + +* `props` (props passed to the wrapped component) +* [`resetForm`] +* [`setErrors`] +* [`setFieldError`] +* [`setFieldTouched`] +* [`setFieldValue`] +* [`setStatus`] +* [`setSubmitting`] +* [`setTouched`] +* [`setValues`] + +Note: [`errors`], [`touched`], [`status`] and all event handlers are NOT +included in the `FormikBag`. + +### `isInitialValid?: boolean | (props: Props) => boolean` + +Default is `false`. Control the initial value of [`isValid`] prop prior to +mount. You can also pass a function. Useful for situations when you want to +enable/disable a submit and reset buttons on initial mount. + +### `mapPropsToValues?: (props: Props) => Values` + +If this option is specified, then Formik will transfer its results into +updatable form state and make these values available to the new component as +[`props.values`][`values`]. If `mapPropsToValues` is not specified, then Formik +will map all props that are not functions to the inner component's +[`props.values`][`values`]. That is, if you omit it, Formik will only pass +`props` where `typeof props[k] !== 'function'`, where `k` is some key. + +Even if your form is not receiving any props from its parent, use +`mapPropsToValues` to initialize your forms empty state. + +### `validate?: (values: Values, props: Props) => FormikErrors | Promise` + +_Note: I suggest using [`validationSchema`] and Yup for validation. However, +`validate` is a dependency-free, straightforward way to validate your forms._ + +Validate the form's [`values`] with function. This function can either be: + +1. Synchronous and return an [`errors`] object. + +```js +// Synchronous validation +const validate = (values, props) => { + 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; +}; +``` + +* Asynchronous and return a Promise that's error is an [`errors`] object + +```js +// Async Validation +const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); + +const validate = (values, props) => { + return sleep(2000).then(() => { + let errors = {}; + if (['admin', 'null', 'god'].includes(values.username)) { + errors.username = 'Nice try'; + } + // ... + if (Object.keys(errors).length) { + throw errors; + } + }); +}; +``` + +### `validateOnBlur?: boolean` + +Default is `true`. Use this option to run validations on `blur` events. More +specifically, when either [`handleBlur`], [`setFieldTouched`], or [`setTouched`] +are called. + +### `validateOnChange?: boolean` + +Default is `true`. Use this option to tell Formik to run validations on `change` +events and `change`-related methods. More specifically, when either +[`handleChange`], [`setFieldValue`], or [`setValues`] are called. + +### `validationSchema?: Schema | ((props: Props) => Schema)` + +[A Yup schema](https://github.com/jquense/yup) or a function that returns a Yup +schema. This is used for validation. Errors are mapped by key to the inner +component's [`errors`]. Its keys should match those of [`values`]. + +## Injected props and methods + +These are identical to the props of ` ...} />` diff --git a/docs/get-started.md b/docs/get-started.md deleted file mode 100644 index 4808c2e8c..000000000 --- a/docs/get-started.md +++ /dev/null @@ -1,131 +0,0 @@ ---- -path: "/docs/basics" -date: "2015-07-01T22:12:03.284Z" -title: "Get started" -description: "Get started with Formik" -back: "/docs" ---- - -## Overview - -Let's face it, forms are really verbose in -[React](https://github.com/facebook/react). To make matters worse, most form -helpers do wayyyy too much magic and often have a significant performance cost -associated with them. Formik is a small library that helps you with the 3 most -annoying parts: - -1. Getting values in and out of form state -2. Validation and error messages -3. Handling form submission - -By colocating all of the above in one place, Formik will keep things -organized--making testing, refactoring, and reasoning about your forms a breeze. - -## Developer Experience - -I ([@jaredpalmer](https://twitter.com/jaredpalmer)) wrote Formik while building a large internal administrative dashboard with -[@eonwhite](https://twitter.com/eonwhite). With around ~30 unique forms, it -quickly became obvious that we could benefit by standardizing not just our input -components but also the way in which data flowed through our forms. - -### Why not Redux-Form? - -By now, you might be thinking, "Why didn't you just use -[Redux-Form](https://github.com/erikras/redux-form)?" Good question. - -1. According to our prophet Dan Abramov, - [**form state is inherently ephemeral and local**, so tracking it in Redux (or any kind of Flux library) is unnecessary](https://github.com/reactjs/redux/issues/1287#issuecomment-175351978) -2. Redux-Form calls your entire top-level Redux reducer multiple times ON EVERY - SINGLE KEYSTROKE. This is fine for small apps, but as your Redux app grows, - input latency will continue to increase if you use Redux-Form. -3. Redux-Form is 22.5 kB minified gzipped (Formik is 12.7 kB) - -**My goal with Formik was to create a scalable, performant, form helper with a -minimal API that does the really really annoying stuff, and leaves the rest up -to you.** - -## Testimonials - -> "I can't believe people ever put forms in redux, or did anything else other than this." -> --[James Long](https://twitter.com/jlongster), Creator of Prettier and Actual Budget - -> ["Formik. All day. All long."](https://twitter.com/ken_wheeler/status/974676242501947397?s=20) -> --[Ken Wheeler](https://twitter.com/ken_wheeler), Director of Open Source at Formidable Labs - -## Influences - -Formik started by expanding on -[this little higher order component](https://github.com/jxnblk/rebass-recomposed/blob/master/src/withForm.js) -by [Brent Jackson](https://github.com/jxnblk), some naming conventions from -Redux-Form, and (most recently) the render props approach popularized by -[React-Motion](https://github.com/chenglou/react-motion) and -[React-Router 4](https://github.com/ReactTraining/react-router). Whether you -have used any of the above or not, Formik only takes a few minutes to get -started with. - -## Installation - -Add Formik to your project. - -```bash -npm i formik --save -``` - -You can also try before you buy with this -**[demo of Formik on CodeSandbox.io](https://codesandbox.io/s/zKrK5YLDZ)** - -## Demos - -* [Basics](https://codesandbox.io/s/zKrK5YLDZ) -* [Sync Validation](https://codesandbox.io/s/q8yRqQMp) -* [Building your own input primitives](https://codesandbox.io/s/qJR4ykJk) -* [Working with 3rd-party inputs #1: react-select](https://codesandbox.io/s/jRzE53pqR) -* [Working with 3rd-party inputs #2: Draft.js](https://codesandbox.io/s/QW1rqjBLl) -* [Accessing React lifecycle functions](https://codesandbox.io/s/pgD4DLypy) - -## Talks - -* [An Introduction to Formik](https://youtu.be/-tDy7ds0dag?t=33s) by - [Jared Palmer](https://twitter.com/jaredpalmer) @ Spotify NYC. August 15th, 2017. - -## Community Articles / Tutorials - -* [Better React Forms with Formik](https://mead.io/formik/?utm_source=github&utm_campaign=formikrepo) -* [The Joy of Forms with React and Formik](https://keyholesoftware.com/2017/10/23/the-joy-of-forms-with-react-and-formik/) -* [Painless React Forms with Formik](https://hackernoon.com/painless-react-forms-with-formik-e61b70473c60) - -## Installation - -Add Formik to your project. - -```bash -npm i formik --save -``` - -```jsx -import React from 'react'; -import { Formik, Field, Form } from 'formik/next'; - -const BasicExample = () => ( -
-

My Form

- { - setTimeout(() => { - alert(JSON.stringify(values, null, 2)); - }, 1000); - }} - component={MyForm} - /> -
-); - -const MyForm = props => ( -
- - - - - -); -``` diff --git a/docs/guides/arrays.md b/docs/guides/arrays.md new file mode 100644 index 000000000..c41f200f9 --- /dev/null +++ b/docs/guides/arrays.md @@ -0,0 +1,67 @@ +--- +id: arrays +title: Arrays and Nested Objects +--- + +Formik has support for nested objects and arrays out of the box. These 2 subjects are somewhat related because they both leverage the same syntax. + +## Nested Objects + +The `name` and `id` props in Formik can use lodash-like dot paths to reference nested Formik values. This means that you do not need to flatten out your form's values anymore. + +```jsx +import React from 'react'; +import { Formik, Form, Field } from 'formik'; + +export const NestedExample = () => ( +
+

Social Profiles

+ { + // same shape as initial values + console.log(values); + }} + > + + + + +
+); +``` + +## Arrays + +Formik also has support for arrays and arrays of objects out of the box. Using lodash-like bracket syntax for `name` string you can quickly build fields for items in a list. + +```jsx +import React from 'react'; +import { Formik, Form, Field } from 'formik'; + +export const BasicArrayExample = () => ( +
+

Friends

+ { + // same shape as initial values + console.log(values); + }} + > + + + + +
+); +``` + +For more information around manipulating (add/remove/etc) items in lists, see the API reference section on the `` component. diff --git a/docs/guides/form-submission.md b/docs/guides/form-submission.md new file mode 100644 index 000000000..d7bd47f89 --- /dev/null +++ b/docs/guides/form-submission.md @@ -0,0 +1,56 @@ +--- +id: form-submission +title: Form Submission +--- + +## Submission Phases + +To submit a form in Formik, you need to somehow fire off the provided `handleSubmit(e)` or `submitForm` prop. When you call either of these methods, Formik will execute the following _(pseudo code)_ each time: + +### Pre-submit + +* Touch all fields +* Set `isSubmitting` to `true` +* Increment `submitCount` + 1 + +### Validation + +* Set `isValidating` to `true` +* Run all field-level validations, `validate`, and `validationSchema` asynchronously and deeply merge results +* Are there any errors? + * Yes: Abort submission. Set `isValidating` to `false`, set `errors`, set `isSubmitting` to `false` + * No: Set `isValidating` to `false`, proceed to "Submission" + +### Submission + +* Proceed with running your submission handler (i.e.`onSubmit` or `handleSubmit`) +* _you call `setSubmitting(false)`_ in your handler to finish the cycle + +## Frequently Asked Questions + +
+How do I determine if my submission handler is executing? + +If `isValidating` is `false` and `isSubmitting` is `true`. + +
+ +
+Why does Formik touch all fields before submit? + +It is common practice to only show an input's errors in the UI if it has been visited (a.k.a "touched"). Before submitting a form, Formik touches all fields so that all errors that may have been hidden will now be visible. + +
+ +
+How do I protect against double submits? + +Disable whatever is triggering submission if `isSubmitting` is `true`. + +
+
+How do I know when my form is validating before submit? + +If `isValidating` is `true` and `isSubmitting` is `true`. + +
diff --git a/docs/guides/react-native.md b/docs/guides/react-native.md new file mode 100644 index 000000000..3dda9e43b --- /dev/null +++ b/docs/guides/react-native.md @@ -0,0 +1,47 @@ +--- +id: react-native +title: React Native +--- + +**Formik is 100% compatible with React Native and React Native Web.** However, +because of differences between ReactDOM's and React Native's handling of forms +and text input, there are some differences to be aware of. This section will walk +you through them and what we consider to be best practices. + +### The gist + +Before going any further, here's a super minimal gist of how to use Formik with +React Native that demonstrates the key differences: + +```jsx +// Formik x React Native example +import React from 'react'; +import { Button, TextInput, View } from 'react-native'; +import { Formik } from 'formik'; + +export const MyReactNativeForm = props => ( + console.log(values)} + > + {({ values, handleChange, handleBlur }) => ( + + + + + ); +}; + +// The type of props MyForm receives +interface MyFormProps { + initialEmail?: string; + message: string; // if this passed all the way through you might do this or make a union type +} + +// Wrap our form with the using withFormik HoC +const MyForm = withFormik({ + // Transform outer props into form values + mapPropsToValues: props => { + return { + email: props.initialEmail || '', + password: '', + }; + }, + + // Add a custom validation function (this can be async too!) + validate: (values: FormValues) => { + let errors: FormikErrors = {}; + if (!values.email) { + errors.email = 'Required'; + } else if (!isValidEmail(values.email)) { + errors.email = 'Invalid email address'; + } + return errors; + }, + + handleSubmit: values => { + // do submitting things + }, +})(InnerForm); + +// Use wherevs +const Basic = () => ( +
+

My App

+

This can be anywhere in your application

+ +
+); + +export default Basic; +``` diff --git a/docs/guides/validation.md b/docs/guides/validation.md new file mode 100644 index 000000000..240803591 --- /dev/null +++ b/docs/guides/validation.md @@ -0,0 +1,166 @@ +--- +id: validation +title: Validation +--- + +Formik is designed to manage forms with complex validation with ease. Formik supports synchronous and asynchronous +"top-down" form-level and "bottom-up" field-level validation. Furthermore, it comes with baked-in support for schema-based top-down form-level validation through Yup. This guide will describe the ins and outs of all of the above. + + + +## Flavors of Validation + +### Form-level Validation + +Form-level validation is useful because you have complete access to all of your form's `values` and props whenever the function runs, so you can validate dependent fields at the same time. + +There are 2 ways to do form-level validation with Formik: + +* `` and `withFormik({ validate: ... })` +* `` and `withFormik({ validationSchema: ... })` + +#### `validate` + +`` and `withFormik()` take has an prop/key called `validate` that accepts either a synchronous or asynchronous function. + +```js +// Synchronous validation +const validate = (values, props) => { + 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; +}; + +// Async Validation +const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); + +const validate = (values, props) => { + return sleep(2000).then(() => { + let errors = {}; + if (['admin', 'null', 'god'].includes(values.username)) { + errors.username = 'Nice try'; + } + // ... + if (Object.keys(errors).length) { + throw errors; + } + }); +}; +``` + +For more information about ``, see the API reference. + +#### `validationSchema` + +As you can see above, validation is left up to you. Feel free to write your own +validators or use a 3rd party library. At The Palmer Group, we use +[Yup](https://github.com/jquense/yup) for object schema validation. It has an +API that's pretty similar [Joi](https://github.com/hapijs/joi) and +[React PropTypes](https://github.com/facebook/prop-types) but is small enough +for the browser and fast enough for runtime usage. Because we :heart: Yup sooo +much, Formik has a special config option / prop for Yup object schemas called `validationSchema` which will automatically transform Yup's validation errors into a pretty object whose keys match `values` and `touched`. This symmetry makes it easy to manage business logic around error messages. + +To add Yup to your project, install it from NPM. + +```sh +npm install yup --save +# typescript users should add the @types/yup +``` + +```jsx +import React from 'react'; +import { Formik, Form, Field } from 'formik'; +import Yup from 'yup'; + +const SignupSchema = Yup.object().shape({ + firstName: Yup.string() + .min(2, 'Too Short!') + .max(50, 'Too Long!') + .required('Required'), + lastName: Yup.string() + .min(2, 'Too Short!') + .max(50, 'Too Long!') + .required('Required'), + email: Yup.string() + .email('Invalid email') + .required('Required'), +}); + +export const ValidationSchemaExample = () => ( +
+

Signup

+ { + // same shape as initial values + console.log(values); + }} + > + {({ errors, touched }) => ( +
+ + {errors.firstName && touched.firstName ? ( +
{errors.firstName}
+ ) : null} + + {errors.lastName && touched.lastName ? ( +
{errors.lastName}
+ ) : null} + + {errors.email && touched.email ?
{errors.email}
: null} + + + )} +
+
+); +``` + +For more information about ``, see the API reference. + +### Field-level Validation + +Formik supports field-level validation through via the `` component's `validate` prop. + +## When Does Validation Run? + +You can control when Formik runs validation by changing the values of `` and/or `` props depending on your needs. By default, Formik will run validation methods as follows: + +**After "change" events/methods** (things that update`values`) + +* `handleChange` +* `setFieldValue` +* `setValues` + +**After "blur" events/methods** (things that update `touched`) + +* `handleBlur` +* `setTouched` +* `setFieldTouched` + +**Whenever submission is attempted** + +* `handleSubmit` +* `submitForm` + +There are also imperative helper methods provided to you via Formik's render/injected props which you can use to imperatively call validation. + +* `validateForm` +* `validateField` + +## Displaying Error Messages + +@todo diff --git a/docs/overview.md b/docs/overview.md new file mode 100644 index 000000000..3d43ddee4 --- /dev/null +++ b/docs/overview.md @@ -0,0 +1,175 @@ +--- +id: overview +title: Overview +--- + +Let's face it, forms are really verbose in +[React](https://github.com/facebook/react). To make matters worse, most form +helpers do wayyyy too much magic and often have a significant performance cost +associated with them. Formik is a small library that helps you with the 3 most +annoying parts: + +1. Getting values in and out of form state +2. Validation and error messages +3. Handling form submission + +By colocating all of the above in one place, Formik will keep things +organized--making testing, refactoring, and reasoning about your forms a breeze. + +## Motivation + +I ([@jaredpalmer](https://twitter.com/jaredpalmer)) wrote Formik while building a large internal administrative dashboard with +[@eonwhite](https://twitter.com/eonwhite). With around ~30 unique forms, it +quickly became obvious that we could benefit by standardizing not just our input +components but also the way in which data flowed through our forms. + +### Why not Redux-Form? + +By now, you might be thinking, "Why didn't you just use +[Redux-Form](https://github.com/erikras/redux-form)?" Good question. + +1. According to our prophet Dan Abramov, + [**form state is inherently ephemeral and local**, so tracking it in Redux (or any kind of Flux library) is unnecessary](https://github.com/reactjs/redux/issues/1287#issuecomment-175351978) +2. Redux-Form calls your entire top-level Redux reducer multiple times ON EVERY + SINGLE KEYSTROKE. This is fine for small apps, but as your Redux app grows, + input latency will continue to increase if you use Redux-Form. +3. Redux-Form is 22.5 kB minified gzipped (Formik is 12.7 kB) + +**My goal with Formik was to create a scalable, performant, form helper with a +minimal API that does the really really annoying stuff, and leaves the rest up +to you.** + +## Influences + +Formik started by expanding on +[this little higher order component](https://github.com/jxnblk/rebass-recomposed/blob/master/src/withForm.js) +by [Brent Jackson](https://github.com/jxnblk), some naming conventions from +Redux-Form, and (most recently) the render props approach popularized by +[React-Motion](https://github.com/chenglou/react-motion) and +[React-Router 4](https://github.com/ReactTraining/react-router). Whether you +have used any of the above or not, Formik only takes a few minutes to get +started with. + +## Installation + +You can install Formik with [NPM](https://npmjs.com), +[Yarn](https://yarnpkg.com), or a good ol' ` +``` + +Once you've added this you will have access to the `window.Formik.` variables. For example, +to use ``, you would write `var Field = window.Formik.Field` and in your React code use `` or `React.createElement(Field, ...)`. + +> This installation/usage requires the [React CDN script bundles](https://reactjs.org/docs/cdn-links.html) to be on the page as well. + +### In-browser Playgrounds + +You can play with Formik in your web browser with these live online playgrounds. + +* CodeSandbox (ReactDOM) https://codesandbox.io/s/zKrK5YLDZ +* Expo Snack (React Native) https://snack.expo.io/Bk9pPK87X + +## The gist + +Formik keeps track of your form's state and then exposes it plus a few reusable +methods and event handlers (`handleChange`, `handleBlur`, and `handleSubmit`) to +your form via `props`. `handleChange` and `handleBlur` work exactly as +expected--they use a `name` or `id` attribute to figure out which field to +update. + +```jsx +import React from 'react'; +import { Formik } from 'formik'; + +const Basic = () => ( +
+

Anywher in your app!

+ { + // do something awesome + }} + > + {({ values, handleChange, handleBlur, handleSubmit, isSubmitting }) => ( +
+ + + +
+ )} +
+
+); + +export default Basic; +``` + +### Staying DRY + +The code above is very explicit about exactly what Formik is doing with respect to passing props. However, to save you time, Formik comes with a few extra components to make life easier: `
` and ``. + +```jsx +// Render Prop +import React from 'react'; +import { Formik, Form, Field } from 'formik'; + +const Basic = () => ( +
+

Any place in your app!

+ { + // do something awesome + }} + > + {({ isSubmitting }) => ( + + + + + + )} + +
+); + +export default Basic; +``` + +The combination of both props and context-based API's makes Formik very flexible. Next up, checkout some of the demos or start the tutorial. diff --git a/docs/philosophy.md b/docs/philosophy.md deleted file mode 100644 index 626514c96..000000000 --- a/docs/philosophy.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -path: "/docs/philosophy" -date: "2015-06-01T22:12:03.284Z" -title: "Philosophy" -description: "The philosophy, vision, and motivation behind Formik" -back: "/docs" ---- - -# Philosophy - -Todo diff --git a/docs/resources.md b/docs/resources.md new file mode 100644 index 000000000..06d353349 --- /dev/null +++ b/docs/resources.md @@ -0,0 +1,19 @@ +--- +id: resources +title: Resources +--- + +## Talks + +* [Taming Forms in React](https://2018.reactjsday.it/talks.html) by + [Jared Palmer](https://twitter.com/jaredpalmer) @ React.js Day Italy. October 5th, 2018. +* [Taming Forms in React](http://reactalicante.es/) by + [Jared Palmer](https://twitter.com/jaredpalmer) @ React Alicante. September 15th, 2018. +* [An Introduction to Formik](https://youtu.be/-tDy7ds0dag?t=33s) by + [Jared Palmer](https://twitter.com/jaredpalmer) @ Spotify NYC. August 15th, 2017. + +## Community Articles / Tutorials + +* [Better React Forms with Formik](https://mead.io/formik/?utm_source=github&utm_campaign=formikrepo) +* [The Joy of Forms with React and Formik](https://keyholesoftware.com/2017/10/23/the-joy-of-forms-with-react-and-formik/) +* [Painless React Forms with Formik](https://hackernoon.com/painless-react-forms-with-formik-e61b70473c60) diff --git a/docs/tutorial.md b/docs/tutorial.md new file mode 100644 index 000000000..e6a29f6de --- /dev/null +++ b/docs/tutorial.md @@ -0,0 +1,149 @@ +--- +id: tutorial +title: Tutorial +--- + +Imagine you want to build a form that lets you edit user data. However, your +user API has nested objects like so. + +```js +{ + id: string, + email: string, + social: { + facebook: string, + twitter: string, + // ... + } +} +``` + +When we are done we want our dialog to accept just a `user`, `updateUser`, and +`onClose` props. + +```jsx +// User.js +import React from 'react'; +import Dialog from 'MySuperDialog'; +import { Formik } from 'formik'; + +const EditUserDialog = ({ user, updateUser, onClose }) => { + return ( + +

Edit User

+ { + CallMyApi(user.id, values).then( + updatedUser => { + actions.setSubmitting(false); + updateUser(updatedUser), onClose(); + }, + error => { + actions.setSubmitting(false); + actions.setErrors(transformMyAPIErrorToAnObject(error)); + } + ); + }} + render={({ + values, + errors, + touched, + handleBlur, + handleChange, + handleSubmit, + isSubmitting, + }) => ( +
+ + {errors.email && touched.email &&
{errors.email}
} + + {errors.social && + errors.social.facebook && + touched.facebook &&
{errors.social.facebook}
} + + {errors.social && + errors.social.twitter && + touched.twitter &&
{errors.social.twitter}
} + +
+ )} + /> +
+ ); +}; +``` + +To make writing forms less verbose. Formik comes with a few helpers to save you +key strokes. + +* `` +* `
` + +This is the **exact** same form as before, but written with `` and +``: + +```jsx +// EditUserDialog.js +import React from 'react'; +import Dialog from 'MySuperDialog'; +import { Formik, Field, Form } from 'formik'; + +const EditUserDialog = ({ user, updateUser, onClose }) => { + return ( + +

Edit User

+ { + CallMyApi(user.id, values).then( + updatedUser => { + actions.setSubmitting(false); + updateUser(updatedUser), onClose(); + }, + error => { + actions.setSubmitting(false); + actions.setErrors(transformMyAPIErrorToAnObject(error)); + } + ); + }} + render={({ errors, touched, isSubmitting }) => ( + + + {errors.email && touched.email &&
{errors.email}
} + + {errors.social.facebook && + touched.social.facebook &&
{errors.social.facebook}
} + + {errors.social.twitter && + touched.social.twitter &&
{errors.social.twitter}
} + + + )} + /> +
+ ); +}; +``` diff --git a/website/.babelrc b/website/.babelrc deleted file mode 100644 index b5d91ec8d..000000000 --- a/website/.babelrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "presets": ["react", "es2015", "stage-1"], - "plugins": ["add-module-exports"] -} diff --git a/website/.gitignore b/website/.gitignore deleted file mode 100644 index 9673e03e5..000000000 --- a/website/.gitignore +++ /dev/null @@ -1,31 +0,0 @@ -# Logs -logs -*.log - -# Runtime data -pids -*.pid -*.seed - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage - -# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (http://nodejs.org/api/addons.html) -build/Release - -# Dependency directory -# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git -node_modules -.gatsby-context.js -.sass-cache/ -public/ -.cache/ diff --git a/website/.snyk b/website/.snyk deleted file mode 100644 index a39ceae10..000000000 --- a/website/.snyk +++ /dev/null @@ -1,22 +0,0 @@ -# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. -version: v1.12.0 -ignore: {} -# patches apply the minimum changes required to fix a vulnerability -patch: - 'npm:tunnel-agent:20170305': - - gatsby-plugin-sharp > imagemin-pngquant > pngquant-bin > bin-build > download > caw > tunnel-agent: - patched: '2018-06-26T08:11:24.289Z' - - gatsby-plugin-sharp > imagemin-pngquant > pngquant-bin > bin-wrapper > download > caw > tunnel-agent: - patched: '2018-06-26T08:11:24.289Z' - - gatsby-plugin-sharp > imagemin-webp > cwebp-bin > bin-build > download > caw > tunnel-agent: - patched: '2018-06-26T08:11:24.289Z' - - gatsby-plugin-sharp > imagemin-webp > cwebp-bin > bin-wrapper > download > caw > tunnel-agent: - patched: '2018-06-26T08:11:24.289Z' - - gatsby-remark-images > gatsby-plugin-sharp > imagemin-pngquant > pngquant-bin > bin-build > download > caw > tunnel-agent: - patched: '2018-06-26T08:11:24.289Z' - - gatsby-remark-images > gatsby-plugin-sharp > imagemin-pngquant > pngquant-bin > bin-wrapper > download > caw > tunnel-agent: - patched: '2018-06-26T08:11:24.289Z' - - gatsby-remark-images > gatsby-plugin-sharp > imagemin-webp > cwebp-bin > bin-build > download > caw > tunnel-agent: - patched: '2018-06-26T08:11:24.289Z' - - gatsby-remark-images > gatsby-plugin-sharp > imagemin-webp > cwebp-bin > bin-wrapper > download > caw > tunnel-agent: - patched: '2018-06-26T08:11:24.289Z' diff --git a/website/README.md b/website/README.md index f0a989a42..ae8d3e316 100644 --- a/website/README.md +++ b/website/README.md @@ -1,3 +1 @@ -# Formik Docs Website - -Built with <3 with Gatsby. +# Formik Website. diff --git a/website/core/Footer.js b/website/core/Footer.js new file mode 100644 index 000000000..a399367a5 --- /dev/null +++ b/website/core/Footer.js @@ -0,0 +1,105 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const React = require('react'); + +class Footer extends React.Component { + docUrl(doc, language) { + const baseUrl = this.props.config.baseUrl; + return `${baseUrl}docs/${language ? `${language}/` : ''}${doc}`; + } + + pageUrl(doc, language) { + const baseUrl = this.props.config.baseUrl; + return baseUrl + (language ? `${language}/` : '') + doc; + } + + render() { + return ( + + ); + } +} + +module.exports = Footer; diff --git a/website/gatsby-config.js b/website/gatsby-config.js deleted file mode 100644 index f5fdcc12d..000000000 --- a/website/gatsby-config.js +++ /dev/null @@ -1,38 +0,0 @@ -module.exports = { - pathPrefix: '/formik', - siteMetadata: { - siteName: `Formik`, - }, - plugins: [ - `gatsby-plugin-glamor`, - `gatsby-plugin-react-next`, - { - resolve: 'gatsby-source-filesystem', - options: { - path: `${__dirname}/src/pages`, - name: 'pages', - }, - }, - { - resolve: `gatsby-source-filesystem`, - options: { - path: `${__dirname}/../docs/`, - name: `pages`, - }, - }, - - // `gatsby-plugin-offline`, - `gatsby-plugin-typescript`, - { - resolve: 'gatsby-transformer-remark', - options: { - plugins: [ - 'gatsby-remark-autolink-headers', - 'gatsby-remark-prismjs', - 'gatsby-remark-copy-linked-files', - 'gatsby-remark-smartypants', - ], - }, - }, - ], -}; diff --git a/website/gatsby-node.js b/website/gatsby-node.js deleted file mode 100644 index 49d3b128a..000000000 --- a/website/gatsby-node.js +++ /dev/null @@ -1,86 +0,0 @@ -const path = require('path'); -const webpack = require('webpack'); -// @todo when online -// const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); - -exports.modifyWebpackConfig = ({ config, stage }) => { - // See https://github.com/FormidableLabs/react-live/issues/5 - // See https://github.com/facebook/react/blob/master/www/gatsby-node.js - config.plugin('ignore', () => new webpack.IgnorePlugin(/^(xor|props)$/)); - - config.merge({ - resolve: { - root: path.resolve(__dirname, './src'), - extensions: ['', '.js', '.jsx', '.tsx', '.ts', '.json'], - }, - plugins: [ - // new ForkTsCheckerWebpackPlugin({ - // checkSyntacticErrors: true, - // formatter: 'codeframe', - // tslint: './tslint.json', - // watch: './src', - // workers: 2, - // }), - ], - }); - return config; -}; - -exports.createPages = ({ graphql, boundActionCreators }) => { - const { createPage } = boundActionCreators; - - return new Promise((resolve, reject) => { - const blogPost = path.resolve('./src/templates/md.tsx'); - const blogPostQuery = ` - { - allMarkdownRemark(limit: 1000) { - edges { - node { - frontmatter { - path, - title - } - } - } - } - } - `; - - resolve( - graphql(blogPostQuery).then(result => { - if (result.errors) { - reject(result.errors); - } - - result.data.allMarkdownRemark.edges.forEach(edge => { - createPage({ - path: edge.node.frontmatter.path, - component: blogPost, - context: { - pathname: edge.node.frontmatter.path, - }, - }); - }); - }) - ); - }); -}; - -// exports.onCreatePage = ({ page, boundActionCreators }) => { -// const { createPage, deletePage } = boundActionCreators; - -// return new Promise((resolve, reject) => { -// // Remove trailing slash -// const oldPath = page.path; -// page.path = page.path === `/` ? page.path : page.path.replace(/\/$/, ``); -// if (page.path !== oldPath) { -// // Remove the old page -// deletePage({ path: oldPath }); - -// // Add the new page -// createPage(page); -// } - -// resolve(); -// }); -// }; diff --git a/website/now.json b/website/now.json new file mode 100644 index 000000000..5bc50bd79 --- /dev/null +++ b/website/now.json @@ -0,0 +1,8 @@ +{ + "name": "formik", + "type": "static", + "static": { + "trailingSlash": false + }, + "alias": ["formik"] +} \ No newline at end of file diff --git a/website/package.json b/website/package.json index bb1c77340..e5d4ecb15 100644 --- a/website/package.json +++ b/website/package.json @@ -1,77 +1,14 @@ { - "name": "gatsby-starter-default", - "description": "Gatsby default starter", - "version": "1.0.0", - "author": "Jared Palmer ", - "dependencies": { - "@gaearon/react-live": "1.7.1-with-safari-fix", - "classnames": "^2.2.5", - "formik": "file:../", - "gatsby": "^1.9.243", - "gatsby-link": "^1.6.9", - "gatsby-plugin-catch-links": "^1.0.15", - "gatsby-plugin-glamor": "^1.6.4", - "gatsby-plugin-google-analytics": "^1.0.4", - "gatsby-plugin-manifest": "^1.0.4", - "gatsby-plugin-nprogress": "^1.0.7", - "gatsby-plugin-react-helmet": "^1.0.3", - "gatsby-plugin-react-next": "^1.0.3", - "gatsby-plugin-sharp": "^1.6.2", - "gatsby-plugin-sitemap": "^1.2.11", - "gatsby-plugin-twitter": "^1.0.15", - "gatsby-plugin-typescript": "^1.0.1", - "gatsby-remark-autolink-headers": "^1.4.4", - "gatsby-remark-copy-linked-files": "^1.5.2", - "gatsby-remark-images": "^1.5.11", - "gatsby-remark-prismjs": "^1.2.1", - "gatsby-remark-responsive-iframe": "^1.4.3", - "gatsby-remark-smartypants": "^1.4.3", - "gatsby-source-filesystem": "^1.4.4", - "gatsby-starter-default": "file:", - "gatsby-transformer-remark": "^1.7.2", - "gatsby-transformer-sharp": "^1.6.1", - "glamor": "^2.20.40", - "palx": "^1.0.2", - "prismjs": "^1.9.0", - "react-json-tree": "^0.11.0", - "react-media": "^1.6.1", - "react-router-dom": "^4.1.2", - "typography": "^0.15.12", - "typography-breakpoint-constants": "^0.15.10", - "typography-plugin-code": "^0.15.11", - "yup": "^0.21.3", - "snyk": "^1.85.0" - }, - "devDependencies": { - "@types/classnames": "^2.2.0", - "@types/node": "^8.0.15", - "@types/react": "^16.3.14", - "@types/react-dom": "^16.0.4", - "@types/react-helmet": "^5.0.3", - "@types/react-router-dom": "^4.0.7", - "@types/webpack-env": "^1.13.0", - "tslint": "^5.5.0", - "tslint-react": "^3.0.0", - "typescript": "^2.8.1" - }, - "keywords": [ - "gatsby" - ], - "resolutions": { - "gatsby/graphql": "0.10.5", - "gatsby/react": "16.3.2", - "gatsby/react-dom": "16.3.2" - }, - "license": "MIT", "scripts": { - "build": "gatsby build", - "deploy": "gatsby build --prefix-paths", - "start": "gatsby develop", - "serve": "gatsby serve", - "format": "prettier --trailing-comma es5 --no-semi --single-quote --write \"pages/*.js\" \"utils/*.js\" \"wrappers/*.js\" \"html.js\"", - "test": "echo \"Error: no test specified\" && exit 1", - "snyk-protect": "snyk protect", - "prepare": "npm run snyk-protect" + "examples": "docusaurus-examples", + "start": "docusaurus-start", + "build": "docusaurus-build && cp now.json build/formik/now.json", + "publish-gh-pages": "docusaurus-publish", + "write-translations": "docusaurus-write-translations", + "version": "docusaurus-version", + "rename-version": "docusaurus-rename-version" }, - "snyk": true -} + "devDependencies": { + "docusaurus": "^1.3.3" + } +} \ No newline at end of file diff --git a/website/pages/en/examples.js b/website/pages/en/examples.js new file mode 100644 index 000000000..f47ea0a29 --- /dev/null +++ b/website/pages/en/examples.js @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const React = require('react'); + +const CompLibrary = require('../../core/CompLibrary.js'); + +const Container = CompLibrary.Container; +const GridBlock = CompLibrary.GridBlock; +const SideNav = CompLibrary.SideNav; + +const siteConfig = require(`${process.cwd()}/siteConfig.js`); + +function docUrl(doc, language) { + return `${siteConfig.baseUrl}docs/${language ? `${language}/` : ''}${doc}`; +} +class BlogSidebar extends React.Component { + render() { + const contents = [ + { + name: 'Examples', + links: [ + { + title: 'Basic', + id: 'Basic', + permalink: '/examples/basic.html', + }, + { + title: 'Basic', + id: 'Basic', + permalink: '/examples/basic.html', + }, + ], + }, + ]; + const title = this.props.current && this.props.current.title; + + const current = { + id: title || 'basic', + category: 'blogSidebarTitle', + }; + return ( +
+