Add basic Formik setup and example for update password

This commit is contained in:
Dane Everitt 2019-06-22 23:25:38 -07:00
parent 403a1e79fa
commit d43b7ea5bc
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
7 changed files with 275 additions and 40 deletions

View file

@ -8,6 +8,7 @@
"date-fns": "^1.29.0",
"easy-peasy": "^2.5.0",
"feather-icons": "^4.10.0",
"formik": "^1.5.7",
"jquery": "^3.3.1",
"lodash": "^4.17.11",
"query-string": "^6.7.0",
@ -19,7 +20,8 @@
"socket.io-client": "^2.2.0",
"use-react-router": "^1.0.7",
"ws-wrapper": "^2.0.0",
"xterm": "^3.5.1"
"xterm": "^3.5.1",
"yup": "^0.27.0"
},
"devDependencies": {
"@babel/core": "^7.2.2",
@ -35,6 +37,7 @@
"@types/react-router-dom": "^4.3.3",
"@types/react-transition-group": "^2.9.2",
"@types/webpack-env": "^1.13.6",
"@types/yup": "^0.26.17",
"@typescript-eslint/eslint-plugin": "^1.10.1",
"@typescript-eslint/parser": "^1.10.1",
"babel-loader": "^8.0.5",

View file

@ -0,0 +1,21 @@
import http from '@/api/http';
interface Data {
current: string;
password: string;
confirmPassword: string;
}
export default ({ current, password, confirmPassword }: Data): Promise<void> => {
return new Promise((resolve, reject) => {
http.put('/account/password', {
// eslint-disable-next-line @typescript-eslint/camelcase
current_password: current,
password: password,
// eslint-disable-next-line @typescript-eslint/camelcase
password_confirmation: confirmPassword,
})
.then(() => resolve())
.catch(reject);
});
};

View file

@ -1,47 +1,81 @@
import React, { useState } from 'react';
import { State, useStoreState } from 'easy-peasy';
import { ApplicationState } from '@/state/types';
import { Form, Formik, FormikActions } from 'formik';
import Field from '@/components/elements/Field';
import * as Yup from 'yup';
import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
interface Values {
current: string;
password: string;
confirmPassword: string;
}
const schema = Yup.object().shape({
current: Yup.string().min(1).required('You must provide your current password.'),
password: Yup.string().min(8).required(),
confirmPassword: Yup.string().test('password', 'Password confirmation does not match the password you entered.', function (value) {
return value === this.parent.password;
}),
});
export default () => {
const [ isLoading, setIsLoading ] = useState(false);
const user = useStoreState((state: State<ApplicationState>) => state.user.data);
if (!user) {
return null;
}
const submit = (values: Values, { setSubmitting }: FormikActions<Values>) => {
setTimeout(() => setSubmitting(false), 1500);
};
return (
<form className={'m-0'}>
<label htmlFor={'current_password'} className={'input-dark-label'}>Current Password</label>
<input
<React.Fragment>
<Formik
onSubmit={submit}
validationSchema={schema}
initialValues={{ current: '', password: '', confirmPassword: '' }}
>
{
({ isSubmitting, isValid }) => (
<React.Fragment>
<SpinnerOverlay large={true} visible={isSubmitting}/>
<Form className={'m-0'}>
<Field
id={'current_password'}
type={'password'}
className={'input-dark'}
name={'current'}
label={'Current Password'}
/>
<div className={'mt-6'}>
<label htmlFor={'new_password'} className={'input-dark-label'}>New Password</label>
<input
<Field
id={'new_password'}
type={'password'}
className={'input-dark'}
name={'password'}
label={'New Password'}
description={'Your new password should be at least 8 characters in length and unique to this website.'}
/>
<p className={'input-help'}>
Your new password must be at least 8 characters in length.
</p>
</div>
<div className={'mt-6'}>
<label htmlFor={'new_password_confirm'} className={'input-dark-label'}>Confirm New Password</label>
<input
id={'new_password_confirm'}
<Field
id={'confirm_password'}
type={'password'}
className={'input-dark'}
name={'confirmPassword'}
label={'Confirm New Password'}
/>
</div>
<div className={'mt-6'}>
<button className={'btn btn-primary btn-sm'} disabled={true}>
<button className={'btn btn-primary btn-sm'} disabled={isSubmitting || !isValid}>
Update Password
</button>
</div>
</form>
</Form>
</React.Fragment>
)
}
</Formik>
</React.Fragment>
);
};

View file

@ -8,8 +8,8 @@ type Props = Readonly<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElemen
export default ({ title, borderColor, children, ...props }: Props) => (
<div {...props}>
{title && <h2 className={'text-neutral-300 mb-2 px-4'}>{title}</h2>}
<div className={classNames('bg-neutral-700 p-4 rounded shadow-lg', borderColor, {
{title && <h2 className={'text-neutral-300 mb-4 px-4'}>{title}</h2>}
<div className={classNames('bg-neutral-700 p-4 rounded shadow-lg relative', borderColor, {
'border-t-4': !!borderColor,
})}>
{children}

View file

@ -0,0 +1,41 @@
import React from 'react';
import { Field, FieldProps } from 'formik';
import classNames from 'classnames';
interface Props {
id?: string;
type: string;
name: string;
label?: string;
description?: string;
validate?: (value: any) => undefined | string | Promise<any>;
}
export default ({ id, type, name, label, description, validate }: Props) => (
<Field name={name} validate={validate}>
{
({ field, form: { errors, touched } }: FieldProps) => (
<React.Fragment>
{label &&
<label htmlFor={id} className={'input-dark-label'}>{label}</label>
}
<input
id={id}
type={type}
{...field}
className={classNames('input-dark', {
error: touched[field.name] && errors[field.name],
})}
/>
{touched[field.name] && errors[field.name] ?
<p className={'input-help'}>
{(errors[field.name] as string).charAt(0).toUpperCase() + (errors[field.name] as string).slice(1)}
</p>
:
description ? <p className={'input-help'}>{description}</p> : null
}
</React.Fragment>
)
}
</Field>
);

View file

@ -0,0 +1,14 @@
import React from 'react';
import classNames from 'classnames';
import { CSSTransition } from 'react-transition-group';
export default ({ large, visible }: { visible: boolean; large?: boolean }) => (
<CSSTransition timeout={150} classNames={'fade'} in={visible} unmountOnExit={true}>
<div
className={classNames('absolute pin-t pin-l flex items-center justify-center w-full h-full rounded')}
style={{ background: 'rgba(0, 0, 0, 0.45)' }}
>
<div className={classNames('spinner-circle spinner-white', { 'spinner-lg': large })}></div>
</div>
</CSSTransition>
);

130
yarn.lock
View file

@ -605,7 +605,7 @@
"@babel/plugin-transform-react-jsx-self" "^7.0.0"
"@babel/plugin-transform-react-jsx-source" "^7.0.0"
"@babel/runtime@^7.1.2", "@babel/runtime@^7.4.0":
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.4.0":
version "7.4.5"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.5.tgz#582bb531f5f9dc67d2fcb682979894f75e253f12"
dependencies:
@ -803,6 +803,10 @@
version "1.13.6"
resolved "http://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.13.6.tgz#128d1685a7c34d31ed17010fc87d6a12c1de6976"
"@types/yup@^0.26.17":
version "0.26.17"
resolved "https://registry.yarnpkg.com/@types/yup/-/yup-0.26.17.tgz#5cb7cfc211d8e985b21d88289542591c92cad9dc"
"@typescript-eslint/eslint-plugin@^1.10.1":
version "1.10.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.10.1.tgz#0a9e41f375d082363e63169049cd03ef0b6dd85e"
@ -1291,6 +1295,10 @@ arraybuffer.slice@~0.0.7:
version "0.0.7"
resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675"
asap@~2.0.3:
version "2.0.6"
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
asn1.js@^4.0.0:
version "4.10.1"
resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0"
@ -2113,6 +2121,10 @@ copy-descriptor@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
core-js@^1.0.0:
version "1.2.7"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
core-js@^2.4.0:
version "2.5.7"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e"
@ -2170,6 +2182,13 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
safe-buffer "^5.0.1"
sha.js "^2.4.8"
create-react-context@^0.2.2:
version "0.2.3"
resolved "https://registry.yarnpkg.com/create-react-context/-/create-react-context-0.2.3.tgz#9ec140a6914a22ef04b8b09b7771de89567cb6f3"
dependencies:
fbjs "^0.8.0"
gud "^1.0.0"
cross-spawn@^5.0.1:
version "5.1.0"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
@ -2461,6 +2480,10 @@ deep-is@~0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
deepmerge@^2.1.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.2.1.tgz#5d3ff22a01c00f645405a2fbc17d0778a1801170"
default-gateway@^2.6.0:
version "2.7.2"
resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-2.7.2.tgz#b7ef339e5e024b045467af403d50348db4642d0f"
@ -2695,6 +2718,12 @@ encodeurl@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
encoding@^0.1.11:
version "0.1.12"
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb"
dependencies:
iconv-lite "~0.4.13"
end-of-stream@^1.0.0, end-of-stream@^1.1.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
@ -3173,6 +3202,18 @@ faye-websocket@~0.11.1:
dependencies:
websocket-driver ">=0.5.1"
fbjs@^0.8.0:
version "0.8.17"
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd"
dependencies:
core-js "^1.0.0"
isomorphic-fetch "^2.1.1"
loose-envify "^1.0.0"
object-assign "^4.1.0"
promise "^7.1.1"
setimmediate "^1.0.5"
ua-parser-js "^0.7.18"
feather-icons@^4.10.0:
version "4.10.0"
resolved "https://registry.yarnpkg.com/feather-icons/-/feather-icons-4.10.0.tgz#5c2ba00ecf6f0529e99ab445d054fa6bd0b9c8d6"
@ -3268,6 +3309,10 @@ flush-write-stream@^1.0.0:
inherits "^2.0.1"
readable-stream "^2.0.4"
fn-name@~2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/fn-name/-/fn-name-2.0.1.tgz#5214d7537a4d06a4a301c0cc262feb84188002e7"
follow-redirects@^1.0.0:
version "1.6.1"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.6.1.tgz#514973c44b5757368bad8bddfe52f81f015c94cb"
@ -3299,6 +3344,20 @@ fork-ts-checker-webpack-plugin@^0.5.2:
minimatch "^3.0.4"
tapable "^1.0.0"
formik@^1.5.7:
version "1.5.7"
resolved "https://registry.yarnpkg.com/formik/-/formik-1.5.7.tgz#2fc5fc2f0c693cdc4e8c9dad3a10eb4c4e131ff5"
dependencies:
create-react-context "^0.2.2"
deepmerge "^2.1.1"
hoist-non-react-statics "^3.3.0"
lodash "^4.17.11"
lodash-es "^4.17.11"
prop-types "^15.6.1"
react-fast-compare "^2.0.1"
tiny-warning "^1.0.2"
tslib "^1.9.3"
forwarded@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
@ -3719,7 +3778,7 @@ iconv-lite@0.4.23, iconv-lite@^0.4.22, iconv-lite@^0.4.4:
dependencies:
safer-buffer ">= 2.1.2 < 3"
iconv-lite@^0.4.24:
iconv-lite@^0.4.24, iconv-lite@~0.4.13:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
dependencies:
@ -4083,7 +4142,7 @@ is-resolvable@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88"
is-stream@^1.1.0:
is-stream@^1.0.1, is-stream@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
@ -4137,6 +4196,13 @@ isobject@^3.0.0, isobject@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
isomorphic-fetch@^2.1.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9"
dependencies:
node-fetch "^1.0.1"
whatwg-fetch ">=0.10.0"
jquery@^3.3.1:
version "3.3.1"
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.3.1.tgz#958ce29e81c9790f31be7792df5d4d95fc57fbca"
@ -4339,6 +4405,10 @@ locate-path@^3.0.0:
p-locate "^3.0.0"
path-exists "^3.0.0"
lodash-es@^4.17.11:
version "4.17.11"
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.11.tgz#145ab4a7ac5c5e52a3531fb4f310255a152b4be0"
lodash._baseassign@^3.0.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e"
@ -4829,6 +4899,13 @@ node-emoji@^1.8.1:
dependencies:
lodash.toarray "^4.4.0"
node-fetch@^1.0.1:
version "1.7.3"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"
dependencies:
encoding "^0.1.11"
is-stream "^1.0.1"
node-forge@0.7.5:
version "0.7.5"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.5.tgz#6c152c345ce11c52f465c2abd957e8639cd674df"
@ -5976,6 +6053,12 @@ promise-inflight@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
promise@^7.1.1:
version "7.3.1"
resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
dependencies:
asap "~2.0.3"
prop-types@^15.6.1, prop-types@^15.6.2:
version "15.7.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
@ -5984,6 +6067,10 @@ prop-types@^15.6.1, prop-types@^15.6.2:
object-assign "^4.1.1"
react-is "^16.8.1"
property-expr@^1.5.0:
version "1.5.1"
resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-1.5.1.tgz#22e8706894a0c8e28d58735804f6ba3a3673314f"
proxy-addr@~2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.4.tgz#ecfc733bf22ff8c6f407fa275327b9ab67e48b93"
@ -6132,6 +6219,10 @@ react-dom@^16.8.6:
prop-types "^15.6.2"
scheduler "^0.13.6"
react-fast-compare@^2.0.1:
version "2.0.4"
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9"
react-hot-loader@^4.9.0:
version "4.9.0"
resolved "https://registry.yarnpkg.com/react-hot-loader/-/react-hot-loader-4.9.0.tgz#498e5f76aae28bfd420efdabddf5af156745b4dd"
@ -6706,7 +6797,7 @@ set-value@^2.0.0:
is-plain-object "^2.0.3"
split-string "^3.0.1"
setimmediate@^1.0.4:
setimmediate@^1.0.4, setimmediate@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
@ -7142,6 +7233,10 @@ symbol-observable@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
synchronous-promise@^2.0.6:
version "2.0.9"
resolved "https://registry.yarnpkg.com/synchronous-promise/-/synchronous-promise-2.0.9.tgz#b83db98e9e7ae826bf9c8261fd8ac859126c780a"
table@^5.2.3:
version "5.4.0"
resolved "https://registry.yarnpkg.com/table/-/table-5.4.0.tgz#d772a3216e68829920a41a32c18eda286c95d780"
@ -7320,6 +7415,10 @@ toposort@^1.0.0:
version "1.0.7"
resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.7.tgz#2e68442d9f64ec720b8cc89e6443ac6caa950029"
toposort@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330"
trim-right@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003"
@ -7342,6 +7441,10 @@ tslib@^1.9.0:
version "1.9.2"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.2.tgz#8be0cc9a1f6dc7727c38deb16c2ebd1a2892988e"
tslib@^1.9.3:
version "1.10.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
tsutils@^3.7.0:
version "3.14.0"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.14.0.tgz#bf8d5a7bae5369331fa0f2b0a5a10bd7f7396c77"
@ -7381,6 +7484,10 @@ typescript@^3.3.1:
version "3.3.1"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.1.tgz#6de14e1db4b8a006ac535e482c8ba018c55f750b"
ua-parser-js@^0.7.18:
version "0.7.20"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.20.tgz#7527178b82f6a62a0f243d1f94fd30e3e3c21098"
uglify-es@^3.3.4:
version "3.3.9"
resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677"
@ -7800,6 +7907,10 @@ websocket-extensions@>=0.1.1:
version "0.1.3"
resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29"
whatwg-fetch@>=0.10.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb"
which-module@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
@ -7993,3 +8104,14 @@ yargs@~1.2.6:
yeast@0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419"
yup@^0.27.0:
version "0.27.0"
resolved "https://registry.yarnpkg.com/yup/-/yup-0.27.0.tgz#f8cb198c8e7dd2124beddc2457571329096b06e7"
dependencies:
"@babel/runtime" "^7.0.0"
fn-name "~2.0.1"
lodash "^4.17.11"
property-expr "^1.5.0"
synchronous-promise "^2.0.6"
toposort "^2.0.2"