diff --git a/.flowconfig b/.flowconfig index 9ec9d6af0..b056b0415 100644 --- a/.flowconfig +++ b/.flowconfig @@ -16,5 +16,6 @@ unsafe-getters-setters=warn [options] include_warnings=false +esproposal.optional_chaining=enable [strict] diff --git a/.jest-setup.js b/.jest-setup.js index a5df84b2e..fd269f88a 100644 --- a/.jest-setup.js +++ b/.jest-setup.js @@ -1,5 +1,7 @@ import Enzyme from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; +import "core-js/stable"; +import "regenerator-runtime/runtime"; Enzyme.configure({ adapter: new Adapter() }); diff --git a/package.json b/package.json index 39feff21d..5894e2b28 100644 --- a/package.json +++ b/package.json @@ -51,10 +51,13 @@ }, "dependencies": { "@fortawesome/fontawesome-free": "5.7.2", + "@hapi/joi": "^17.1.1", + "@hookform/resolvers": "^0.1.0", "@scality/core-ui": "github:scality/core-ui.git#add-dist-folder", "async": "^3.2.0", "aws-sdk": "^2.616.0", "connected-react-router": "^6.7.0", + "core-js": "^3.6.5", "file-loader": "^5.0.2", "history": "^4.10.1", "immutable": "^4.0.0-rc.12", @@ -65,6 +68,7 @@ "react": "^16.12.0", "react-debounce-input": "3.2.0", "react-dom": "^16.12.0", + "react-hook-form": "^6.3.3", "react-redux": "^7.1.3", "react-router-dom": "^5.1.2", "react-select": "3.0.3", diff --git a/src/react/account/AccountCreate.jsx b/src/react/account/AccountCreate.jsx index 7a342eecc..b3e07665a 100644 --- a/src/react/account/AccountCreate.jsx +++ b/src/react/account/AccountCreate.jsx @@ -1,52 +1,44 @@ // @flow import { Banner, Button } from '@scality/core-ui'; import Form, * as F from '../ui-elements/FormLayout'; -import { accountEmailValidation, accountNameValidation, accountQuotaValidation } from '../utils/validator'; import { clearError, createAccount } from '../actions'; import { useDispatch, useSelector } from 'react-redux'; import type { AppState } from '../../types/state'; +import Joi from '@hapi/joi'; import React from 'react'; import { goBack } from 'connected-react-router'; -import { useInput } from '../utils/hooks'; +import { joiResolver } from '@hookform/resolvers'; +import { useForm } from 'react-hook-form'; +const regexpEmailAddress = /^\S+@\S+.\S+$/; +const regexpName = /^[\w+=,.@ -]+$/; + +const schema = Joi.object({ + name: Joi.string().label('Name').required().min(2).max(64).regex(regexpName).message('Invalid Name'), + email: Joi.string().label('Root Account Email').required().max(256).regex(regexpEmailAddress).message('Invalid Root Account Email'), + quota: Joi.number().label('Quota').allow('').optional().positive().integer(), +}); function AccountCreate() { - const { value: name, onChange: onChangeName, errorMessage: errorName, hasError: hasErrorName, validation: validationName } = useInput('', accountNameValidation); - const { value: email, onChange: onChangeEmail, errorMessage: errorEmail, hasError: hasErrorEmail, validation: validationEmail } = useInput('', accountEmailValidation); - const { value: quotaMax, onChange: onChangeQuotaMax, errorMessage: errorQuota, hasError: hasErrorQuota, validation: validationQuota } = useInput('', accountQuotaValidation); + const { register, handleSubmit, errors } = useForm({ + resolver: joiResolver(schema), + }); const hasError = useSelector((state: AppState) => !!state.uiErrors.errorMsg && state.uiErrors.errorType === 'byComponent'); const errorMessage = useSelector((state: AppState) => state.uiErrors.errorMsg); const dispatch = useDispatch(); - const isValid = (): boolean => { - const isValidName = validationName(name); - const isValidEmail = validationEmail(email); - const isValidQuota = validationQuota(quotaMax); - return isValidName && isValidEmail && isValidQuota; - }; - - const submit = (e: SyntheticInputEvent) => { - if (e) { - e.preventDefault(); - } - if (hasError) { - dispatch(clearError()); - } - if (!isValid()) { - return; - } - const quotaMaxInt = quotaMax ? parseInt(quotaMax, 10) : 0; + const onSubmit = ({ email, name, quota }) => { + const quotaMaxInt = quota || 0; const payload = { userName: name, email, quotaMax: quotaMaxInt }; dispatch(createAccount(payload)); }; - const onChangeWithClearError = (e, onChange) => { + const clearServerError = () => { if (hasError) { dispatch(clearError()); } - onChange(e); }; return
@@ -58,12 +50,11 @@ function AccountCreate() { onChangeWithClearError(e, onChangeName)} - onBlur={e => validationName(e.target.value)} - hasError={hasErrorName} + name='name' + innerRef={register} + onChange={clearServerError} autoComplete='new-password' /> - {errorName} + {errors.name?.message} @@ -72,12 +63,11 @@ function AccountCreate() { onChangeWithClearError(e, onChangeEmail)} - onBlur={e => validationEmail(e.target.value)} - hasError={hasErrorEmail} + name='email' + innerRef={register} + onChange={clearServerError} autoComplete='off' /> - {errorEmail} + {errors.email?.message} @@ -86,13 +76,12 @@ function AccountCreate() { onChangeWithClearError(e, onChangeQuotaMax)} min="0" - onBlur={e => validationQuota(e.target.value)} - hasError={hasErrorQuota} + name='quota' + innerRef={register} + onChange={clearServerError} autoComplete='off' /> - {errorQuota} + {errors.quota?.message} @@ -108,7 +97,7 @@ function AccountCreate() {