From ff823c9fdbbc1502820ff8b56eca311eed6ff985 Mon Sep 17 00:00:00 2001 From: John Mitchell Date: Tue, 18 Feb 2020 14:30:59 -0500 Subject: [PATCH] update forms to useField fomik hook --- .../CodeMirrorInput/VariablesField.jsx | 24 +- .../components/FormField/CheckboxField.jsx | 49 +- .../src/components/FormField/FormField.jsx | 14 +- .../components/FormField/PasswordField.jsx | 76 ++- .../src/screens/Host/shared/HostForm.jsx | 66 +-- .../Inventory/shared/InventoryForm.jsx | 143 +++-- .../Organization/shared/OrganizationForm.jsx | 139 ++--- .../shared/OrganizationForm.test.jsx | 5 + .../screens/Project/shared/ProjectForm.jsx | 488 +++++++++--------- .../ProjectSubForms/InsightsSubForm.jsx | 54 +- .../shared/ProjectSubForms/ManualSubForm.jsx | 56 +- .../shared/ProjectSubForms/SharedFields.jsx | 17 +- .../src/screens/Team/shared/TeamForm.jsx | 87 ++-- .../src/screens/User/shared/UserForm.jsx | 197 ++++--- 14 files changed, 692 insertions(+), 723 deletions(-) diff --git a/awx/ui_next/src/components/CodeMirrorInput/VariablesField.jsx b/awx/ui_next/src/components/CodeMirrorInput/VariablesField.jsx index 79bd41a826..34d6e8608e 100644 --- a/awx/ui_next/src/components/CodeMirrorInput/VariablesField.jsx +++ b/awx/ui_next/src/components/CodeMirrorInput/VariablesField.jsx @@ -1,6 +1,6 @@ import React, { useState } from 'react'; import { string, bool } from 'prop-types'; -import { Field, useFormikContext } from 'formik'; +import { useField } from 'formik'; import { Split, SplitItem } from '@patternfly/react-core'; import { yamlToJson, jsonToYaml, isJson } from '@util/yaml'; import CodeMirrorInput from './CodeMirrorInput'; @@ -8,9 +8,8 @@ import YamlJsonToggle from './YamlJsonToggle'; import { JSON_MODE, YAML_MODE } from './constants'; function VariablesField({ id, name, label, readOnly }) { - const { values } = useFormikContext(); - const value = values[name]; - const [mode, setMode] = useState(isJson(value) ? JSON_MODE : YAML_MODE); + const [field, meta, helpers] = useField(name); + const [mode, setMode] = useState(isJson(field.value) ? JSON_MODE : YAML_MODE); return ( @@ -31,10 +30,10 @@ function VariablesField({ id, name, label, readOnly }) { newMode === YAML_MODE ? jsonToYaml(field.value) : yamlToJson(field.value); - form.setFieldValue(name, newVal); + helpers.setValue(newVal); setMode(newMode); } catch (err) { - form.setFieldError(name, err.message); + helpers.setError(err.message); } }} /> @@ -45,16 +44,13 @@ function VariablesField({ id, name, label, readOnly }) { readOnly={readOnly} {...field} onChange={newVal => { - form.setFieldValue(name, newVal); + helpers.setValue(newVal); }} - hasErrors={!!form.errors[field.name]} + hasErrors={!!meta.error} /> - {form.errors[field.name] ? ( -
- {form.errors[field.name]} + {meta.error ? ( +
+ {meta.error}
) : null}
diff --git a/awx/ui_next/src/components/FormField/CheckboxField.jsx b/awx/ui_next/src/components/FormField/CheckboxField.jsx index 9e854d803a..a04a78b02c 100644 --- a/awx/ui_next/src/components/FormField/CheckboxField.jsx +++ b/awx/ui_next/src/components/FormField/CheckboxField.jsx @@ -1,6 +1,6 @@ import React from 'react'; import { string, func } from 'prop-types'; -import { Field } from 'formik'; +import { useField } from 'formik'; import { Checkbox, Tooltip } from '@patternfly/react-core'; import { QuestionCircleIcon as PFQuestionCircleIcon } from '@patternfly/react-icons'; import styled from 'styled-components'; @@ -10,32 +10,29 @@ const QuestionCircleIcon = styled(PFQuestionCircleIcon)` `; function CheckboxField({ id, name, label, tooltip, validate, ...rest }) { + const [field] = useField({ name, validate }); return ( - - {({ field }) => ( - - {label} -   - {tooltip && ( - - - - )} - - } - id={id} - {...rest} - isChecked={field.value} - {...field} - onChange={(value, event) => { - field.onChange(event); - }} - /> - )} - + + {label} +   + {tooltip && ( + + + + )} + + } + id={id} + {...rest} + isChecked={field.value} + {...field} + onChange={(value, event) => { + field.onChange(event); + }} + /> ); } CheckboxField.propTypes = { diff --git a/awx/ui_next/src/components/FormField/FormField.jsx b/awx/ui_next/src/components/FormField/FormField.jsx index f55eabb131..b92fee5484 100644 --- a/awx/ui_next/src/components/FormField/FormField.jsx +++ b/awx/ui_next/src/components/FormField/FormField.jsx @@ -1,7 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Field } from 'formik'; -import { FormGroup, TextInput, Tooltip } from '@patternfly/react-core'; +import { useField } from 'formik'; import { QuestionCircleIcon as PFQuestionCircleIcon } from '@patternfly/react-icons'; import styled from 'styled-components'; @@ -21,16 +20,13 @@ function FormField(props) { ...rest } = props; - return ( - - {({ field, form }) => { - const isValid = - form && (!form.touched[field.name] || !form.errors[field.name]); + const [field, meta] = useField({ name, validate }); + const isValid = !(meta.touched && meta.error); return ( )} + isValid={isValid} + helperTextInvalid={meta.error} { setInputType(inputType === 'text' ? 'password' : 'text'); }; return ( - - {({ field, form }) => { - const isValid = - form && (!form.touched[field.name] || !form.errors[field.name]); - return ( - + + + - - { - field.onChange(event); - }} - /> - - - ); - }} - + {inputType === 'password' && } + {inputType === 'text' && } + + + { + field.onChange(event); + }} + /> + + ); } diff --git a/awx/ui_next/src/screens/Host/shared/HostForm.jsx b/awx/ui_next/src/screens/Host/shared/HostForm.jsx index 0e1919be58..fd947f839d 100644 --- a/awx/ui_next/src/screens/Host/shared/HostForm.jsx +++ b/awx/ui_next/src/screens/Host/shared/HostForm.jsx @@ -2,7 +2,7 @@ import React, { useState } from 'react'; import { func, shape } from 'prop-types'; import { useRouteMatch } from 'react-router-dom'; -import { Formik, Field } from 'formik'; +import { Formik, useField } from 'formik'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; @@ -15,26 +15,21 @@ import { VariablesField } from '@components/CodeMirrorInput'; import { required } from '@util/validators'; import { InventoryLookup } from '@components/Lookup'; -function HostForm({ handleSubmit, handleCancel, host, submitError, i18n }) { +function HostFormFields({ host, i18n }) { const [inventory, setInventory] = useState( host ? host.summary_fields.inventory : '' ); const hostAddMatch = useRouteMatch('/hosts/add'); + const inventoryFieldArr = useField({ + name: 'inventory', + validate: required(i18n._(t`Select aĆ„ value for this field`), i18n), + }); + const inventoryMeta = inventoryFieldArr[1]; + const inventoryHelpers = inventoryFieldArr[2]; return ( - - {formik => ( -
- + <> {hostAddMatch && ( - - {({ form }) => ( form.setFieldTouched('inventory')} + onBlur={() => inventoryHelpers.setTouched()} tooltip={i18n._( t`Select the inventory that this host will belong to.` )} - isValid={!form.touched.inventory || !form.errors.inventory} - helperTextInvalid={form.errors.inventory} + isValid={!inventoryMeta.touched || !inventoryMeta.error} + helperTextInvalid={inventoryMeta.error} onChange={value => { - form.setFieldValue('inventory', value.id); + inventoryHelpers.setValuealue(value.id); setInventory(value); }} required - touched={form.touched.inventory} - error={form.errors.inventory} + touched={inventoryMeta.touched} + error={inventoryMeta.error} /> )} - - )} - - - + + ); +} + +function HostForm({ handleSubmit, host, submitError, handleCancel, ...rest }) { + return ( + + {formik => ( + + { - onSubmit(values); - }} - > - {formik => ( - - + <> - - {({ form, field }) => ( form.setFieldTouched('organization')} + helperTextInvalid={organizationMeta.error} + isValid={!organizationMeta.touched || !organizationMeta.error} + onBlur={() => organizationHelpers.setTouched()} onChange={value => { - form.setFieldValue('organization', value); + organizationHelpers.setValue(value); }} - value={field.value} - touched={form.touched.organization} - error={form.errors.organization} + value={organizationField.value} + touched={organizationMeta.touched} + error={organizationMeta.error} required /> - )} - - - {({ field, form }) => ( - form.setFieldValue('insights_credential', value) - } - value={field.value} + onChange={value => insightsCredentialHelpers.setValue(value)} + value={insightsCredentialField.value} /> - )} - - - - - {({ field, form }) => ( { - form.setFieldValue('instanceGroups', value); + instanceGroupsHelpers.setValue(value); }} /> - )} - - - - - + + ); +} + +function InventoryForm({ + inventory = {}, + onSubmit, + onCancel, + submitError, + instanceGroups, + ...rest +}) { + const initialValues = { + name: inventory.name || '', + description: inventory.description || '', + variables: inventory.variables || '---', + organization: + (inventory.summary_fields && inventory.summary_fields.organization) || + null, + instanceGroups: instanceGroups || [], + insights_credential: + (inventory.summary_fields && + inventory.summary_fields.insights_credential) || + null, + }; + + return ( + { + onSubmit(values); + }} + > + {formik => ( + + + + + + {custom_virtualenvs && custom_virtualenvs.length > 1 && ( + + value !== defaultVenv.value) + .map(value => ({ value, label: value, key: value })), + ]} + {...venvField} + /> + + )} + + + ); +} + +function OrganizationForm({ + organization, + onCancel, + onSubmit, + submitError, + ...rest +}) { const [contentError, setContentError] = useState(null); const [hasContentLoading, setHasContentLoading] = useState(true); const [initialInstanceGroups, setInitialInstanceGroups] = useState([]); @@ -100,64 +166,11 @@ function OrganizationForm({ > {formik => ( - - - - - {custom_virtualenvs && custom_virtualenvs.length > 1 && ( - - {({ field }) => ( - - value !== defaultVenv.value) - .map(value => ({ value, label: value, key: value })), - ]} - {...field} - /> - - )} - - )} - - ', () => { }); test('changing inputs and saving triggers expected callback', async () => { + OrganizationsAPI.readInstanceGroups.mockReturnValue({ + data: { + results: mockInstanceGroups, + }, + }); let wrapper; const onSubmit = jest.fn(); await act(async () => { diff --git a/awx/ui_next/src/screens/Project/shared/ProjectForm.jsx b/awx/ui_next/src/screens/Project/shared/ProjectForm.jsx index 768624dc7c..e18eb7fa3a 100644 --- a/awx/ui_next/src/screens/Project/shared/ProjectForm.jsx +++ b/awx/ui_next/src/screens/Project/shared/ProjectForm.jsx @@ -3,9 +3,9 @@ import React, { useState, useEffect } from 'react'; import PropTypes from 'prop-types'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; -import { Formik, Field } from 'formik'; +import { Formik, useField } from 'formik'; import { Config } from '@contexts/Config'; -import { Form, FormGroup } from '@patternfly/react-core'; +import { Form, FormGroup, Title } from '@patternfly/react-core'; import AnsibleSelect from '@components/AnsibleSelect'; import ContentError from '@components/ContentError'; import ContentLoading from '@components/ContentLoading'; @@ -24,17 +24,9 @@ import { HgSubForm, SvnSubForm, InsightsSubForm, - SubFormTitle, ManualSubForm, } from './ProjectSubForms'; -const ScmTypeFormRow = styled(FormRow)` - background-color: #f5f5f5; - grid-column: 1 / -1; - margin: 0 -24px; - padding: 24px; -`; - const fetchCredentials = async credential => { const [ { @@ -73,14 +65,238 @@ const fetchCredentials = async credential => { }; }; -function ProjectForm({ project, submitError, ...props }) { - const { i18n, handleCancel, handleSubmit } = props; +function ProjectFormFields({ + project_base_dir, + project_local_paths, + formik, + i18n, + setCredentials, + credentials, + scmTypeOptions, + setScmSubFormState, + scmSubFormState, + setOrganization, + organization, +}) { + const scmFormFields = { + scm_url: '', + scm_branch: '', + scm_refspec: '', + credential: '', + scm_clean: false, + scm_delete_on_update: false, + scm_update_on_launch: false, + allow_override: false, + scm_update_cache_timeout: 0, + }; + + const [scmTypeField, scmTypeMeta, scmTypeHelpers] = useField({ + name: 'scm_type', + validate: required(i18n._(t`Set a value for this field`), i18n), + }); + const [venvField] = useField('custom_virtualenv'); + const orgFieldArr = useField({ + name: 'organization', + validate: required(i18n._(t`Select a value for this field`), i18n), + }); + const organizationMeta = orgFieldArr[1]; + const organizationHelpers = orgFieldArr[2]; + + /* Save current scm subform field values to state */ + const saveSubFormState = form => { + const currentScmFormFields = { ...scmFormFields }; + + Object.keys(currentScmFormFields).forEach(label => { + currentScmFormFields[label] = form.values[label]; + }); + + setScmSubFormState(currentScmFormFields); + }; + + /** + * If scm type is !== the initial scm type value, + * reset scm subform field values to defaults. + * If scm type is === the initial scm type value, + * reset scm subform field values to scmSubFormState. + */ + const resetScmTypeFields = (value, form) => { + if (form.values.scm_type === form.initialValues.scm_type) { + saveSubFormState(formik); + } + + Object.keys(scmFormFields).forEach(label => { + if (value === form.initialValues.scm_type) { + form.setFieldValue(label, scmSubFormState[label]); + } else { + form.setFieldValue(label, scmFormFields[label]); + } + form.setFieldTouched(label, false); + }); + }; + + const handleCredentialSelection = (type, value) => { + setCredentials({ + ...credentials, + [type]: { + ...credentials[type], + value, + }, + }); + }; + + return ( + <> + + + organizationHelpers.setTouched()} + onChange={value => { + organizationHelpers.setValue(value.id); + setOrganization(value); + }} + value={organization} + required + /> + + { + if (label === 'Manual') { + value = 'manual'; + } + return { + label, + value, + key: value, + }; + }), + ]} + onChange={(event, value) => { + scmTypeHelpers.setValue(value); + resetScmTypeFields(value, formik); + }} + /> + + {formik.values.scm_type !== '' && ( + + + {i18n._(t`Type Details`)} + + { + { + manual: ( + + ), + git: ( + + ), + hg: ( + + ), + svn: ( + + ), + insights: ( + + ), + }[formik.values.scm_type] + } + + )} + + {({ custom_virtualenvs }) => + custom_virtualenvs && + custom_virtualenvs.length > 1 && ( + + {({ field }) => ( + + + datum !== '/venv/ansible/') + .map(datum => ({ + label: datum, + value: datum, + key: datum, + })), + ]} + {...venvField} + /> + + ) + } + + + ); +} + +function ProjectForm({ i18n, project, submitError, ...props }) { + const { handleCancel, handleSubmit } = props; const { summary_fields = {} } = project; const [contentError, setContentError] = useState(null); const [isLoading, setIsLoading] = useState(true); - const [organization, setOrganization] = useState( - summary_fields.organization || null - ); + const [organization, setOrganization] = useState(null); const [scmSubFormState, setScmSubFormState] = useState(null); const [scmTypeOptions, setScmTypeOptions] = useState(null); const [credentials, setCredentials] = useState({ @@ -114,60 +330,6 @@ function ProjectForm({ project, submitError, ...props }) { fetchData(); }, [summary_fields.credential]); - const scmFormFields = { - scm_url: '', - scm_branch: '', - scm_refspec: '', - credential: '', - scm_clean: false, - scm_delete_on_update: false, - scm_update_on_launch: false, - allow_override: false, - scm_update_cache_timeout: 0, - }; - - /* Save current scm subform field values to state */ - const saveSubFormState = form => { - const currentScmFormFields = { ...scmFormFields }; - - Object.keys(currentScmFormFields).forEach(label => { - currentScmFormFields[label] = form.values[label]; - }); - - setScmSubFormState(currentScmFormFields); - }; - - /** - * If scm type is !== the initial scm type value, - * reset scm subform field values to defaults. - * If scm type is === the initial scm type value, - * reset scm subform field values to scmSubFormState. - */ - const resetScmTypeFields = (value, form) => { - if (form.values.scm_type === form.initialValues.scm_type) { - saveSubFormState(form); - } - - Object.keys(scmFormFields).forEach(label => { - if (value === form.initialValues.scm_type) { - form.setFieldValue(label, scmSubFormState[label]); - } else { - form.setFieldValue(label, scmFormFields[label]); - } - form.setFieldTouched(label, false); - }); - }; - - const handleCredentialSelection = (type, value) => { - setCredentials({ - ...credentials, - [type]: { - ...credentials[type], - value, - }, - }); - }; - if (isLoading) { return ; } @@ -211,183 +373,19 @@ function ProjectForm({ project, submitError, ...props }) { onSubmit={formik.handleSubmit} css="padding: 0 24px" > - - - - - {({ form }) => ( - form.setFieldTouched('organization')} - onChange={value => { - form.setFieldValue('organization', value.id); - setOrganization(value); - }} - value={organization} - required - /> - )} - - - {({ field, form }) => ( - - { - if (label === 'Manual') { - value = 'manual'; - } - return { - label, - value, - key: value, - }; - }), - ]} - onChange={(event, value) => { - form.setFieldValue('scm_type', value); - resetScmTypeFields(value, form); - }} - /> - - )} - - {formik.values.scm_type !== '' && ( - - - {i18n._(t`Type Details`)} - - { - { - manual: ( - - ), - git: ( - - ), - hg: ( - - ), - svn: ( - - ), - insights: ( - - ), - }[formik.values.scm_type] - } - - )} - - {({ custom_virtualenvs }) => - custom_virtualenvs && - custom_virtualenvs.length > 1 && ( - - {({ field }) => ( - - - datum !== '/venv/ansible/') - .map(datum => ({ - label: datum, - value: datum, - key: datum, - })), - ]} - {...field} - /> - - )} - - ) - } - - ( - <> - - {({ form }) => ( - form.setFieldTouched('credential')} - onChange={value => { - onCredentialSelection('insights', value); - form.setFieldValue('credential', value.id); - }} - value={credential.value} - required - /> - )} - - - -); +}) => { + const credFieldArr = useField({ + name: 'credential', + validate: required(i18n._(t`Select a value for this field`), i18n), + }); + const credMeta = credFieldArr[1]; + const credHelpers = credFieldArr[2]; + + return ( + <> + credHelpers.setTouched()} + onChange={value => { + onCredentialSelection('insights', value); + credHelpers.setValue(value.id); + }} + value={credential.value} + required + /> + + + ); +}; export default withI18n()(InsightsSubForm); diff --git a/awx/ui_next/src/screens/Project/shared/ProjectSubForms/ManualSubForm.jsx b/awx/ui_next/src/screens/Project/shared/ProjectSubForms/ManualSubForm.jsx index 94a624b380..adf1d2fb15 100644 --- a/awx/ui_next/src/screens/Project/shared/ProjectSubForms/ManualSubForm.jsx +++ b/awx/ui_next/src/screens/Project/shared/ProjectSubForms/ManualSubForm.jsx @@ -1,7 +1,7 @@ import React from 'react'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; -import { Field } from 'formik'; +import { useField } from 'formik'; import { required } from '@util/validators'; import AnsibleSelect from '@components/AnsibleSelect'; import FormField, { FieldTooltip } from '@components/FormField'; @@ -34,6 +34,10 @@ const ManualSubForm = ({ label: path, })), ]; + const [pathField, pathMeta, pathHelpers] = useField({ + name: 'local_path', + validate: required(i18n._(t`Select a value for this field`), i18n), + }); return ( <> @@ -72,35 +76,27 @@ const ManualSubForm = ({ } /> - {options.length !== 1 && ( - - {({ field, form }) => ( - - - { - form.setFieldValue('local_path', value); - }} - /> - - )} - + + + { + pathHelpers.setValue(value); + }} + /> + )} ); diff --git a/awx/ui_next/src/screens/Project/shared/ProjectSubForms/SharedFields.jsx b/awx/ui_next/src/screens/Project/shared/ProjectSubForms/SharedFields.jsx index 1268cd7230..199c1b2d2a 100644 --- a/awx/ui_next/src/screens/Project/shared/ProjectSubForms/SharedFields.jsx +++ b/awx/ui_next/src/screens/Project/shared/ProjectSubForms/SharedFields.jsx @@ -1,11 +1,10 @@ import React from 'react'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; -import { Field } from 'formik'; +import { useField } from 'formik'; import CredentialLookup from '@components/Lookup/CredentialLookup'; import FormField, { CheckboxField } from '@components/FormField'; import { required } from '@util/validators'; -import FormRow from '@components/FormRow'; import { FormGroup, Title } from '@patternfly/react-core'; import styled from 'styled-components'; @@ -41,21 +40,21 @@ export const BranchFormField = withI18n()(({ i18n, label }) => ( )); export const ScmCredentialFormField = withI18n()( - ({ i18n, credential, onCredentialSelection }) => ( - - {({ form }) => ( + ({ i18n, credential, onCredentialSelection }) => { + const credHelpers = useField('credential')[2]; + + return ( { onCredentialSelection('scm', value); - form.setFieldValue('credential', value ? value.id : ''); + credHelpers.setValue(value ? value.id : ''); }} /> - )} - - ) + ); + } ); export const ScmTypeOptions = withI18n()( diff --git a/awx/ui_next/src/screens/Team/shared/TeamForm.jsx b/awx/ui_next/src/screens/Team/shared/TeamForm.jsx index 040d6d4dca..d5ba307fa2 100644 --- a/awx/ui_next/src/screens/Team/shared/TeamForm.jsx +++ b/awx/ui_next/src/screens/Team/shared/TeamForm.jsx @@ -2,19 +2,58 @@ import React, { useState } from 'react'; import PropTypes from 'prop-types'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; -import { Formik, Field } from 'formik'; +import { Formik, useField } from 'formik'; import { Form } from '@patternfly/react-core'; import FormActionGroup from '@components/FormActionGroup/FormActionGroup'; import FormField, { FormSubmitError } from '@components/FormField'; -import FormRow from '@components/FormRow'; import OrganizationLookup from '@components/Lookup/OrganizationLookup'; import { required } from '@util/validators'; -function TeamForm(props) { - const { team, handleCancel, handleSubmit, submitError, i18n } = props; +function TeamFormFields(props) { + const { team, i18n } = props; const [organization, setOrganization] = useState( team.summary_fields ? team.summary_fields.organization : null ); + const orgFieldArr = useField({ + name: 'organization', + validate: required(i18n._(t`Select a value for this field`), i18n), + }); + const orgMeta = orgFieldArr[1]; + const orgHelpers = orgFieldArr[2]; + + return ( + <> + + + orgHelpers.setTouched('organization')} + onChange={value => { + orgHelpers.setValue(value.id); + setOrganization(value); + }} + value={organization} + required + /> + + ); +} + +function TeamForm(props) { + const { team, handleCancel, handleSubmit, submitError, ...rest } = props; return ( - - - - - {({ form }) => ( - form.setFieldTouched('organization')} - onChange={value => { - form.setFieldValue('organization', value.id); - setOrganization(value); - }} - value={organization} - required - /> - )} - - + + + + undefined + } + isRequired={!user.id} + /> + undefined + } + isRequired={!user.id} + /> + + + {!user.id && ( + organizationHelpers.setTouched()} + onChange={value => { + organizationHelpers.setValue(value.id); + setOrganization(value); + }} + value={organization} + required + /> + )} + + + + + ); +} + +function UserForm({ user, handleCancel, handleSubmit, submitError, i18n }) { const handleValidateAndSubmit = (values, { setErrors }) => { if (values.password !== values.confirm_password) { setErrors({ @@ -81,106 +173,7 @@ function UserForm({ user, handleCancel, handleSubmit, submitError, i18n }) { > {formik => ( - - - - undefined - } - isRequired={!user.id} - /> - undefined - } - isRequired={!user.id} - /> - - - - - {!user.id && ( - - {({ form }) => ( - form.setFieldTouched('organization')} - onChange={value => { - form.setFieldValue('organization', value.id); - setOrganization(value); - }} - value={organization} - required - /> - )} - - )} - - {({ form, field }) => { - const isValid = - !form.touched.user_type || !form.errors.user_type; - return ( - - - - ); - }} - - +