From b1ab523def46c4f6a8e2b747def6bf54f0efc682 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 10 Sep 2020 11:15:38 -0400 Subject: [PATCH 1/4] feat(ApiUserForm): Add form validation. --- components/ApiUserForm.js | 278 ++++++++++++++++++++++------------ components/LayoutWithAuth0.js | 16 +- package.json | 4 +- util/middleware.js | 3 +- yarn.lock | 76 +++++++++- 5 files changed, 272 insertions(+), 105 deletions(-) diff --git a/components/ApiUserForm.js b/components/ApiUserForm.js index 10d5c0e..e454875 100644 --- a/components/ApiUserForm.js +++ b/components/ApiUserForm.js @@ -1,12 +1,25 @@ +import clone from 'clone' +import { Formik } from 'formik' import { Component } from 'react' import { Button, Card, Col, Container, Form, Row } from 'react-bootstrap' import { withAuth } from 'use-auth0-hooks' +import * as yup from 'yup' import { AUTH0_SCOPE } from '../util/constants' +// The validation schema for the form. +const validationSchema = yup.object({ + appName: yup.string().required('Please enter your application name.'), + appPurpose: yup.string(), + appUrl: yup.string().url('Please enter a valid URL (should start with http:// or https://), or leave blank if unknown.'), + company: yup.string().required('Please enter your company name.'), + hasConsentedToTerms: yup.boolean().oneOf([true], 'You must agree to the terms to continue.'), + name: yup.string().required('Please enter your name.') +}) + /** - * The basic form for creating an ApiUser. This can also be used to show a - * disabled view of the form (for viewing user details). + * The basic form for creating an ApiUser, including input validation. + * This can also be used to show a disabled view of the form (for viewing user details). * * TODO: Add the ability to update a user? */ @@ -15,12 +28,12 @@ class ApiUserForm extends Component { super(props) this.state = { apiUser: { - appName: null, - appPurpose: null, - appUrl: null, - company: null, + appName: '', + appPurpose: '', + appUrl: '', + company: '', hasConsentedToTerms: false, - name: null + name: '' } } } @@ -35,13 +48,15 @@ class ApiUserForm extends Component { this.updateUserState({ hasConsentedToTerms: e.target.checked }) } - handleCreateAccount = async e => { + handleCreateAccount = async apiUserData => { const { auth, createUser } = this.props if (auth.user) { - const { apiUser } = this.state + const apiUser = clone(apiUserData) + // Add required attributes for middleware storage. apiUser.auth0UserId = auth.user.sub apiUser.email = auth.user.email + createUser(apiUser) } else { alert('Could not save your data (Auth0 id was not available).') @@ -58,6 +73,10 @@ class ApiUserForm extends Component { }) } + dummy () { + + } + render () { const { createUser } = this.props // Default values to apiUser passed from props. Otherwise, use original state. @@ -72,99 +91,162 @@ class ApiUserForm extends Component { name } = apiUser + // We display validation for a particular field on blur (after the user finishes typing in it), + // so it is not too disruptive to the user. + // The onBlur/onHandleBlur and touched props are used to that effect. + // All field validation errors are also shown when the user clicks Create Account. + return (
{createUser &&

Sign up for API access

} -
- - - - - Developer information - - - Developer name - - - - - Company - - - - - - - - Application information - - - Application name - - - - - Application purpose - - - - - Application URL - - - - - - - - - - - I have read and consent to the{' '} - Terms of Service{' '} - for using the {process.env.API_NAME}. - + + {({ + handleBlur, + handleSubmit, + handleChange, + values, + touched, + isValid, + errors + }) => ( + + + + + + + Developer information + + + Developer name + + + {errors.name} + + + + + Company + + + {errors.company} + + + + + + + + Application information + + + Application name + + + {errors.appName} + + + + + Application purpose + + + {errors.appPurpose} + + + + + Application URL + + + {errors.appUrl} + + + + + + + + +
+ + + I have read and consent to the{' '} + Terms of Service{' '} + for using the {process.env.API_NAME}. + + } + name='hasConsentedToTerms' + onBlur={handleBlur} + onChange={handleChange} + type='checkbox' + /> + +
+ {createUser && + } - onChange={this.handleTermsChange} - type='checkbox' - checked={hasConsentedToTerms} - /> - You must agree to the terms to continue. -
- {createUser && - - } -
+ + )} + +
) } diff --git a/components/LayoutWithAuth0.js b/components/LayoutWithAuth0.js index d11a8fd..fda622f 100644 --- a/components/LayoutWithAuth0.js +++ b/components/LayoutWithAuth0.js @@ -59,8 +59,20 @@ class LayoutWithAuth0 extends Component { isUserRequested: true }) // TODO: Combine into a single fetch fromToken or use SWR - const adminUser = await secureFetch(`${ADMIN_USER_URL}/fromtoken`, accessToken) - const apiUser = await secureFetch(`${API_USER_URL}/fromtoken`, accessToken) + const adminUserFetchResult = await secureFetch(`${ADMIN_USER_URL}/fromtoken`, accessToken) + const apiUserFetchResult = await secureFetch(`${API_USER_URL}/fromtoken`, accessToken) + + // Check that the contents of the fetch result for admin user and api user is valid + // This means for instance checking for existence of a data.id field. + // If the user was not found, something else is returned of the form + // data: { + // "result": "ERR", + // "message": "No user with id=000000 found.", + // "code": 404, + // "detail": null + // } + const adminUser = adminUserFetchResult.data.id ? adminUserFetchResult.data : null + const apiUser = apiUserFetchResult.data.id ? apiUserFetchResult.data : null this.setState({ ...state, diff --git a/package.json b/package.json index 6cc8be6..1638d06 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "bootstrap": "^4.5.0", "clone": "^2.1.2", "dotenv": "^8.2.0", + "formik": "^2.1.5", "isomorphic-unfetch": "^3.0.0", "moment": "^2.24.0", "next": "^9.3.2", @@ -25,7 +26,8 @@ "styled-components": "^5.0.1", "styled-icons": "^10.2.1", "swr": "^0.3.2", - "use-auth0-hooks": "^0.7.0" + "use-auth0-hooks": "^0.7.0", + "yup": "^0.29.3" }, "devDependencies": { "mastarm": "^5.3.1", diff --git a/util/middleware.js b/util/middleware.js index 9452784..878173f 100644 --- a/util/middleware.js +++ b/util/middleware.js @@ -44,7 +44,8 @@ export async function createOrUpdateUser (url, userData, isNew, accessToken) { } // TODO: improve the UI feedback messages for this. - if (result.status === 'success' && result.data) { + // A successful call has the user record (with id) in the data field. + if (result.data.id) { return result.data } else { alert(`An error was encountered:\n${JSON.stringify(result)}`) diff --git a/yarn.lock b/yarn.lock index 0e1785b..a416f01 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4256,6 +4256,11 @@ deep-is@~0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= +deepmerge@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.2.1.tgz#5d3ff22a01c00f645405a2fbc17d0778a1801170" + integrity sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA== + default-gateway@^2.6.0: version "2.7.2" resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-2.7.2.tgz#b7ef339e5e024b045467af403d50348db4642d0f" @@ -5343,6 +5348,11 @@ flush-write-stream@^1.0.0: inherits "^2.0.3" readable-stream "^2.3.6" +fn-name@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/fn-name/-/fn-name-3.0.0.tgz#0596707f635929634d791f452309ab41558e3c5c" + integrity sha512-eNMNr5exLoavuAMhIUVsOKF79SWd/zG104ef6sxBTSw+cZc6BXdQXDvYcGvp0VbxVVSp1XDUNoz7mg1xMtSznA== + for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -5362,6 +5372,20 @@ form-data@~2.3.2: combined-stream "^1.0.6" mime-types "^2.1.12" +formik@^2.1.5: + version "2.1.5" + resolved "https://registry.yarnpkg.com/formik/-/formik-2.1.5.tgz#de5bbbe35543fa6d049fe96b8ee329d6cd6892b8" + integrity sha512-bWpo3PiqVDYslvrRjTq0Isrm0mFXHiO33D8MS6t6dWcqSFGeYF52nlpCM2xwOJ6tRVRznDkL+zz/iHPL4LDuvQ== + dependencies: + deepmerge "^2.1.1" + hoist-non-react-statics "^3.3.0" + lodash "^4.17.14" + lodash-es "^4.17.14" + react-fast-compare "^2.0.1" + scheduler "^0.18.0" + tiny-warning "^1.0.2" + tslib "^1.10.0" + fragment-cache@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" @@ -5767,7 +5791,7 @@ hoek@4.2.1: resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.1.tgz#9634502aa12c445dd5a7c5734b572bb8738aacbb" integrity sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA== -hoist-non-react-statics@^3.0.0: +hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.0: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== @@ -7154,7 +7178,7 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" -lodash-es@^4.17.15: +lodash-es@^4.17.11, lodash-es@^4.17.14, lodash-es@^4.17.15: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78" integrity sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ== @@ -9398,6 +9422,11 @@ prop-types@15.7.2, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.2, p object-assign "^4.1.1" react-is "^16.8.1" +property-expr@^2.0.2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.4.tgz#37b925478e58965031bb612ec5b3260f8241e910" + integrity sha512-sFPkHQjVKheDNnPvotjQmm3KD3uk1fWKUN7CrpdbwmUx3CrG3QiM8QpTSimvig5vTXmTvjz7+TDvXOI9+4rkcg== + prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" @@ -9576,6 +9605,11 @@ react-dom@^16.13.0: prop-types "^15.6.2" scheduler "^0.19.1" +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" + integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw== + react-input-autosize@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/react-input-autosize/-/react-input-autosize-2.2.2.tgz#fcaa7020568ec206bc04be36f4eb68e647c4d8c2" @@ -10236,6 +10270,14 @@ sax@>=0.6.0, sax@^1.2.4, sax@~1.2.4: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== +scheduler@^0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.18.0.tgz#5901ad6659bc1d8f3fdaf36eb7a67b0d6746b1c4" + integrity sha512-agTSHR1Nbfi6ulI0kYNK0203joW2Y5W4po4l+v03tOoiJKpTBbxpNhWDvqc/4IcOw+KLmSiQLTasZ4cab2/UWQ== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + scheduler@^0.19.1: version "0.19.1" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196" @@ -11092,6 +11134,11 @@ symbol-tree@^3.2.2: resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== +synchronous-promise@^2.0.13: + version "2.0.13" + resolved "https://registry.yarnpkg.com/synchronous-promise/-/synchronous-promise-2.0.13.tgz#9d8c165ddee69c5a6542862b405bc50095926702" + integrity sha512-R9N6uDkVsghHePKh1TEqbnLddO2IY25OcsksyFp/qBe7XYd0PVbKEWxhcdMhpLzE1I6skj5l4aEZ3CRxcbArlA== + syntax-error@^1.1.1: version "1.4.0" resolved "https://registry.yarnpkg.com/syntax-error/-/syntax-error-1.4.0.tgz#2d9d4ff5c064acb711594a3e3b95054ad51d907c" @@ -11241,6 +11288,11 @@ timsort@^0.3.0: resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= +tiny-warning@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" + integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -11300,6 +11352,11 @@ toidentifier@1.0.0: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== +toposort@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330" + integrity sha1-riF2gXXRVZ1IvvNUILL0li8JwzA= + tough-cookie@^2.3.3, tough-cookie@^2.3.4, tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" @@ -11350,7 +11407,7 @@ tsconfig-paths@^3.9.0: minimist "^1.2.0" strip-bom "^3.0.0" -tslib@^1.9.0: +tslib@^1.10.0, tslib@^1.9.0: version "1.13.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== @@ -12078,3 +12135,16 @@ yargs@^13.2.4, yargs@^13.3.0: which-module "^2.0.0" y18n "^4.0.0" yargs-parser "^13.1.2" + +yup@^0.29.3: + version "0.29.3" + resolved "https://registry.yarnpkg.com/yup/-/yup-0.29.3.tgz#69a30fd3f1c19f5d9e31b1cf1c2b851ce8045fea" + integrity sha512-RNUGiZ/sQ37CkhzKFoedkeMfJM0vNQyaz+wRZJzxdKE7VfDeVKH8bb4rr7XhRLbHJz5hSjoDNwMEIaKhuMZ8gQ== + dependencies: + "@babel/runtime" "^7.10.5" + fn-name "~3.0.0" + lodash "^4.17.15" + lodash-es "^4.17.11" + property-expr "^2.0.2" + synchronous-promise "^2.0.13" + toposort "^2.0.2" From 622baa4f5ccaa7c54edb2325ceb06e4e81f76f70 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 10 Sep 2020 11:28:28 -0400 Subject: [PATCH 2/4] refactor(ApiUserForm): Remove old state mgmt. --- components/ApiUserForm.js | 72 +++++++++++---------------------------- 1 file changed, 19 insertions(+), 53 deletions(-) diff --git a/components/ApiUserForm.js b/components/ApiUserForm.js index e454875..52a3bb5 100644 --- a/components/ApiUserForm.js +++ b/components/ApiUserForm.js @@ -7,7 +7,7 @@ import * as yup from 'yup' import { AUTH0_SCOPE } from '../util/constants' -// The validation schema for the form. +// The validation schema for the form fields. const validationSchema = yup.object({ appName: yup.string().required('Please enter your application name.'), appPurpose: yup.string(), @@ -17,6 +17,20 @@ const validationSchema = yup.object({ name: yup.string().required('Please enter your name.') }) +/** + * Creates a blank ApiUser object to be filled out. + */ +function createBlankApiUser () { + return { + appName: '', + appPurpose: '', + appUrl: '', + company: '', + hasConsentedToTerms: false, + name: '' + } +} + /** * The basic form for creating an ApiUser, including input validation. * This can also be used to show a disabled view of the form (for viewing user details). @@ -24,30 +38,6 @@ const validationSchema = yup.object({ * TODO: Add the ability to update a user? */ class ApiUserForm extends Component { - constructor (props) { - super(props) - this.state = { - apiUser: { - appName: '', - appPurpose: '', - appUrl: '', - company: '', - hasConsentedToTerms: false, - name: '' - } - } - } - - handleChange = field => e => { - const newData = {} - newData[field] = e.target.value - this.updateUserState(newData) - } - - handleTermsChange = e => { - this.updateUserState({ hasConsentedToTerms: e.target.checked }) - } - handleCreateAccount = async apiUserData => { const { auth, createUser } = this.props if (auth.user) { @@ -63,45 +53,22 @@ class ApiUserForm extends Component { } } - updateUserState = newUserData => { - const { apiUser } = this.state - this.setState({ - apiUser: { - ...apiUser, - ...newUserData - } - }) - } - - dummy () { - - } - render () { const { createUser } = this.props - // Default values to apiUser passed from props. Otherwise, use original state. - // It is assumed that if coming from props, the apiUser already exists. - const apiUser = this.props.apiUser || this.state.apiUser - const { - appName, - appPurpose, - appUrl, - company, - hasConsentedToTerms, - name - } = apiUser + // If the ApiUser already exists, it is passed from props. + // Otherwise, it is a new ApiUser, and a blank one is created. + const apiUser = this.props.apiUser || createBlankApiUser() // We display validation for a particular field on blur (after the user finishes typing in it), // so it is not too disruptive to the user. // The onBlur/onHandleBlur and touched props are used to that effect. // All field validation errors are also shown when the user clicks Create Account. - return (
{createUser &&

Sign up for API access

} ( From d2d6dd952df9669feced1cd73b77bc940a1a1828 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 10 Sep 2020 11:52:46 -0400 Subject: [PATCH 3/4] refactor(ApiUserForm): Combine logic for text field layout. --- components/ApiUserForm.js | 165 ++++++++++++++++---------------------- 1 file changed, 68 insertions(+), 97 deletions(-) diff --git a/components/ApiUserForm.js b/components/ApiUserForm.js index 52a3bb5..9ff1b8b 100644 --- a/components/ApiUserForm.js +++ b/components/ApiUserForm.js @@ -17,6 +17,40 @@ const validationSchema = yup.object({ name: yup.string().required('Please enter your name.') }) +// Field layout (assumes all text fields) +const fieldLayout = [ + { + title: 'Developer information', + fields: [ + { + title: 'Developer name', + field: 'name' + }, + { + title: 'Company', + field: 'company' + } + ] + }, + { + title: 'Application information', + fields: [ + { + title: 'Application name', + field: 'appName' + }, + { + title: 'Application purpose', + field: 'appPurpose' + }, + { + title: 'Application URL', + field: 'appUrl' + } + ] + } +] + /** * Creates a blank ApiUser object to be filled out. */ @@ -85,98 +119,39 @@ class ApiUserForm extends Component {
- - - Developer information - - - Developer name - - - {errors.name} - - - - - Company - - - {errors.company} - - - - - - - - Application information - - - Application name - - - {errors.appName} - - - - - Application purpose - - - {errors.appPurpose} - - - - - Application URL - - - {errors.appUrl} - - - - - + { + fieldLayout.map((col, colIndex) => ( + + + {col.title} + + { + col.fields.map((field, fieldIndex) => { + const fieldName = field.field + return ( + + {field.title} + + + {errors[fieldName]} + + + ) + }) + } + + + + )) + } @@ -202,16 +177,12 @@ class ApiUserForm extends Component {
{createUser && - } )} - ) From eee3104c1816ed5091567950615166cfca10f464 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Wed, 30 Sep 2020 09:28:34 -0400 Subject: [PATCH 4/4] refactor(ApiUserForm): Use Formik.Field to wire inputs automatically. --- components/ApiUserForm.js | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/components/ApiUserForm.js b/components/ApiUserForm.js index 9ff1b8b..07f30a0 100644 --- a/components/ApiUserForm.js +++ b/components/ApiUserForm.js @@ -1,5 +1,5 @@ import clone from 'clone' -import { Formik } from 'formik' +import { Field, Formik } from 'formik' import { Component } from 'react' import { Button, Card, Col, Container, Form, Row } from 'react-bootstrap' import { withAuth } from 'use-auth0-hooks' @@ -108,10 +108,7 @@ class ApiUserForm extends Component { initialValues={apiUser} > {({ - handleBlur, handleSubmit, - handleChange, - values, touched, errors }) => ( @@ -131,14 +128,12 @@ class ApiUserForm extends Component { return ( {field.title} - {errors[fieldName]} @@ -157,8 +152,8 @@ class ApiUserForm extends Component {
- } name='hasConsentedToTerms' - onBlur={handleBlur} - onChange={handleChange} - type='checkbox' + // onBlur, onChange, and value are passed automatically. />