Ott 167 validation errors (#38)

keep-around/af30b88d367751c9e05a735e4a0467a96238ef47
Mirlan 5 years ago committed by GitHub
parent b592faea5f
commit 5bfa0d7b7e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      .eslintrc
  2. 7
      src/config/lexics/public.tsx
  3. 84
      src/features/Combobox/index.tsx
  4. 1
      src/features/Combobox/types.tsx
  5. 73
      src/features/Common/Input/index.tsx
  6. 9
      src/features/Common/Input/stories.tsx
  7. 40
      src/features/Common/Input/styled.tsx
  8. 64
      src/features/FormStore/hooks/useFormState.tsx
  9. 23
      src/features/FormStore/hooks/useFormValidators.tsx
  10. 42
      src/features/FormStore/index.tsx
  11. 1
      src/features/Login/styled.tsx
  12. 25
      src/features/Register/components/RegistrationStep/config.tsx
  13. 43
      src/features/Register/components/RegistrationStep/hooks/useCities.tsx
  14. 4
      src/features/Register/components/RegistrationStep/hooks/useCountries.tsx
  15. 105
      src/features/Register/components/RegistrationStep/hooks/useForm.tsx
  16. 65
      src/features/Register/components/RegistrationStep/hooks/useSubmitHandler.tsx
  17. 62
      src/features/Register/components/RegistrationStep/hooks/useValidateForm.tsx
  18. 95
      src/features/Register/components/RegistrationStep/index.tsx

@ -82,6 +82,7 @@
"import/no-unresolved": "off",
"import/prefer-default-export": "off",
"indent": "off",
"no-plusplus": "off",
"no-underscore-dangle": "off",
"no-unused-vars": "off",
"react/jsx-one-expression-per-line": "off",

@ -1,4 +1,10 @@
export const publicLexics = {
error_empty_email: 2498,
error_fill_out_required_fields: 12911,
error_fill_out_this_field: 12933,
error_invalid_email_format: 12908,
error_select_country_first: 12910,
error_simple_password: 12940,
form_address1: 12914,
form_address2: 12915,
form_card_code: 12918,
@ -15,7 +21,6 @@ export const publicLexics = {
form_region: 12932,
login: 1367,
next: 12916,
please_fill_out_this_field: 12933,
register: 1305,
select_language: 1005,
step_title_card: 12917,

@ -5,7 +5,11 @@ import map from 'lodash/map'
import '@reach/combobox/styles.css'
import { T9n } from 'features/T9n'
import { Label } from 'features/Common/Input/styled'
import {
Label,
Column,
Error,
} from 'features/Common/Input/styled'
import { Props, Option } from './types'
import { useCombobox } from './hooks'
@ -21,6 +25,7 @@ import {
export const Combobox = <T extends Option>(props: Props<T>) => {
const {
disabled,
error,
id,
label,
labelLexic,
@ -42,41 +47,48 @@ export const Combobox = <T extends Option>(props: Props<T>) => {
} = useCombobox(props)
return (
<ComboboxStyled openOnFocus={openOnFocus} onSelect={onOptionSelect}>
<Label
labelWidth={labelWidth}
htmlFor={id}
<Column>
<ComboboxStyled
error={error}
openOnFocus={openOnFocus}
onSelect={onOptionSelect}
>
{
labelLexic
? <T9n t={labelLexic} />
: label
}
{withArrow && <Arrow />}
</Label>
<ComboboxInputStyled
id={id}
required={required}
disabled={disabled}
value={query}
title={title}
pattern={pattern}
onChange={onQueryChange}
onBlur={onInputBlur}
onKeyDown={onKeyDown}
/>
{!isEmpty(options) && (
<ComboboxPopoverStyled>
<ComboboxListStyled ref={popoverRef}>
{map(options, ({ id: optionId, name }) => (
<ComboboxOptionStyled
key={optionId}
value={name}
/>
))}
</ComboboxListStyled>
</ComboboxPopoverStyled>
)}
</ComboboxStyled>
<Label
labelWidth={labelWidth}
htmlFor={id}
>
{
labelLexic
? <T9n t={labelLexic} />
: label
}
{withArrow && <Arrow />}
</Label>
<ComboboxInputStyled
id={id}
required={required}
disabled={disabled}
value={query}
title={title}
pattern={pattern}
onChange={onQueryChange}
onBlur={onInputBlur}
onKeyDown={onKeyDown}
/>
{!isEmpty(options) && (
<ComboboxPopoverStyled>
<ComboboxListStyled ref={popoverRef}>
{map(options, ({ id: optionId, name }) => (
<ComboboxOptionStyled
key={optionId}
value={name}
/>
))}
</ComboboxListStyled>
</ComboboxPopoverStyled>
)}
</ComboboxStyled>
<Error>{error && <T9n t={error} />}</Error>
</Column>
)
}

@ -13,6 +13,7 @@ export type Props<T> = Pick<InputHTMLAttributes<HTMLInputElement>, (
| 'pattern'
| 'title'
)> & {
error?: string | null,
label?: string,
labelLexic?: string,
labelWidth?: number,

@ -1,15 +1,18 @@
import React, { ChangeEvent } from 'react'
import type { ChangeEvent, FocusEvent } from 'react'
import React from 'react'
import { T9n } from 'features/T9n'
import {
TInputWrapper,
WrapperProps,
InputWrapper,
InputStyled,
Label,
Error,
Column,
} from './styled'
type TInput = {
type Props = {
defaultValue?: string,
id: string,
inputWidth?: number,
@ -17,22 +20,25 @@ type TInput = {
labelLexic?: string,
labelWidth?: number,
maxLength?: number,
onBlur?: (event: FocusEvent<HTMLInputElement>) => void,
onChange?: (event: ChangeEvent<HTMLInputElement>) => void,
pattern?: string,
required?: boolean,
title?: string,
type?: string,
value?: string,
} & TInputWrapper
} & WrapperProps
export const Input = ({
defaultValue,
error,
id,
inputWidth,
label,
labelLexic,
labelWidth,
maxLength,
onBlur,
onChange,
paddingX,
pattern,
@ -41,32 +47,37 @@ export const Input = ({
type,
value,
wrapperWidth,
}: TInput) => (
<InputWrapper
wrapperWidth={wrapperWidth}
paddingX={paddingX}
>
<Label
htmlFor={id}
labelWidth={labelWidth}
}: Props) => (
<Column>
<InputWrapper
wrapperWidth={wrapperWidth}
paddingX={paddingX}
error={error}
>
{
labelLexic
? <T9n t={labelLexic} />
: label
}
</Label>
<InputStyled
id={id}
type={type}
required={required}
value={value}
defaultValue={defaultValue}
onChange={onChange}
maxLength={maxLength}
inputWidth={inputWidth}
pattern={pattern}
title={title}
/>
</InputWrapper>
<Label
htmlFor={id}
labelWidth={labelWidth}
>
{
labelLexic
? <T9n t={labelLexic} />
: label
}
</Label>
<InputStyled
id={id}
type={type}
required={required}
value={value}
defaultValue={defaultValue}
onChange={onChange}
onBlur={onBlur}
maxLength={maxLength}
inputWidth={inputWidth}
pattern={pattern}
title={title}
/>
</InputWrapper>
<Error>{error && <T9n t={error} />}</Error>
</Column>
)

@ -33,3 +33,12 @@ export const EmailPassword = () => (
/>
</Fragment>
)
export const WithError = () => (
<Input
id='email'
type='email'
label='Email'
error='Error message'
/>
)

@ -1,14 +1,17 @@
import styled, { css } from 'styled-components/macro'
export type TInputWrapper = {
import isNil from 'lodash/isNil'
export type WrapperProps = {
error?: string | null,
paddingX?: number,
wrapperWidth?: number,
}
export const wrapperStyles = css<TInputWrapper>`
export const wrapperStyles = css<WrapperProps>`
width: ${({ wrapperWidth }) => (wrapperWidth ? `${wrapperWidth}px` : '100%')};
height: 48px;
margin: 20px 0;
margin-top: 20px;
padding-left: ${({ paddingX = 24 }) => (paddingX ? `${paddingX}px` : '')};
padding-right: ${({ paddingX = 24 }) => (paddingX ? `${paddingX}px` : '')};
padding-top: 13px;
@ -18,17 +21,19 @@ export const wrapperStyles = css<TInputWrapper>`
background-color: #3F3F3F;
box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3);
border-radius: 2px;
border: 1px solid ${(({ error }) => (isNil(error) ? 'transparent' : '#E64646'))};
border-width: 1px;
`
export const InputWrapper = styled.div<TInputWrapper>`
export const InputWrapper = styled.div<WrapperProps>`
${wrapperStyles}
`
type TLabel = {
type LabelProps = {
labelWidth?: number,
}
export const Label = styled.label<TLabel>`
export const Label = styled.label<LabelProps>`
font-style: normal;
font-weight: normal;
font-size: 16px;
@ -39,11 +44,11 @@ export const Label = styled.label<TLabel>`
width: ${({ labelWidth }) => (labelWidth ? `${labelWidth}px` : '')};
`
type TInputStyled = {
type InputProps = {
inputWidth?: number,
}
export const inputStyles = css<TInputStyled>`
export const inputStyles = css<InputProps>`
flex-grow: 1;
font-weight: bold;
font-size: 20px;
@ -73,6 +78,23 @@ export const inputStyles = css<TInputStyled>`
}
`
export const InputStyled = styled.input<TInputStyled>`
export const InputStyled = styled.input<InputProps>`
${inputStyles}
`
export const Column = styled.div`
width: 100%;
display: flex;
flex-direction: column;
`
export const Error = styled.span`
min-height: 16px;
margin-top: 5px;
font-style: normal;
font-weight: normal;
font-size: 13px;
line-height: 16px;
letter-spacing: -0.078px;
color: #E64646;
`

@ -0,0 +1,64 @@
import type { ChangeEvent } from 'react'
import { useState, useCallback } from 'react'
import isString from 'lodash/isString'
type FieldState = {
error: string | null,
value: string,
}
type FormState = {[formId: string]: FieldState}
export const useFormState = () => {
const [formState, setFormState] = useState<FormState>({})
const readFormValue = useCallback(
(fieldName: string) => formState[fieldName]?.value ?? '',
[formState],
)
const readFormError = useCallback(
(fieldName: string) => formState[fieldName]?.error ?? null,
[formState],
)
const updateFormValue = useCallback(
(fieldName: string) => (
(event: ChangeEvent<HTMLInputElement> | string) => {
const value = isString(event) ? event : event.target.value
setFormState(((state) => {
const newState = {
...state,
[fieldName]: {
error: null,
value,
},
}
return newState
}))
}
),
[],
)
const updateFormError = useCallback(
(fieldName: string, error: string) => {
setFormState(((state) => {
const newState = {
...state,
[fieldName]: {
error,
value: state[fieldName]?.value,
},
}
return newState
}))
},
[],
)
return {
readFormError,
readFormValue,
updateFormError,
updateFormValue,
}
}

@ -0,0 +1,23 @@
import trim from 'lodash/trim'
import every from 'lodash/every'
import isEmpty from 'lodash/isEmpty'
import { useFormState } from './useFormState'
type Args = ReturnType<typeof useFormState>
export const useFormValidators = ({
readFormValue,
updateFormError,
}: Args) => {
const isFieldEmpty = (fieldName: string) => isEmpty(trim(readFormValue(fieldName)))
const allFieldsEmpty = (fieldNames: Array<string>) => (
every(fieldNames, isFieldEmpty)
)
return {
allFieldsEmpty,
isFieldEmpty,
}
}

@ -0,0 +1,42 @@
import type { ReactNode } from 'react'
import React, {
createContext,
useContext,
useMemo,
} from 'react'
import { useFormState } from './hooks/useFormState'
import { useFormValidators } from './hooks/useFormValidators'
type FormStore = (
ReturnType<typeof useFormState>
& ReturnType<typeof useFormValidators>
)
type Props = {
children: ReactNode,
}
const FormContext = createContext({} as FormStore)
/**
* стор формы, содержит состояние формы[значение полей, ошибки]
* дает доступ к функциям чтения и обновления состояния
* также часто используемым функциям для валидации полей
*/
export const FormStore = ({
children,
}: Props) => {
const formState = useFormState()
const validators = useFormValidators(formState)
const value = useMemo(
() => ({ ...formState, ...validators }),
[formState, validators],
)
return (
<FormContext.Provider value={value}>
{children}
</FormContext.Provider>
)
}
export const useForm = () => useContext(FormContext)

@ -33,6 +33,7 @@ export const BlockTitle = styled.span`
export const ButtonsBlock = styled.div<{forSubsPage?: boolean}>`
display: flex;
flex-direction: column;
align-items: center;
margin-top: ${({ forSubsPage }) => (forSubsPage ? '80px' : '60px')};
margin-bottom: ${({ forSubsPage }) => (forSubsPage ? '96px' : '')};
position: relative;

@ -5,9 +5,34 @@ export const formIds = {
country: 'country',
email: 'email',
firstname: 'firstname',
formError: 'formError',
lastname: 'lastname',
password: 'password',
phone: 'phone',
postalCode: 'postalCode',
region: 'region',
}
export const requiredFields = [
formIds.address1,
formIds.city,
formIds.country,
formIds.email,
formIds.firstname,
formIds.lastname,
formIds.password,
formIds.phone,
formIds.postalCode,
formIds.region,
]
export const simpleValidationFields = [
formIds.address1,
formIds.city,
formIds.country,
formIds.firstname,
formIds.lastname,
formIds.phone,
formIds.postalCode,
formIds.region,
]

@ -1,15 +1,15 @@
import type { ChangeEvent } from 'react'
import {
useState,
useCallback,
useEffect,
} from 'react'
import debounce from 'lodash/debounce'
import trim from 'lodash/trim'
import type { Cities, City } from 'requests'
import { getCountryCities } from 'requests'
import { useForm } from 'features/FormStore'
import { formIds } from '../config'
const useCitiesList = () => {
const [cities, setCities] = useState<Cities>([])
@ -23,7 +23,7 @@ const useCitiesList = () => {
[],
)
const resetCities = useCallback(() => setCities([]), [])
const resetCities = () => setCities([])
return {
cities,
@ -32,31 +32,18 @@ const useCitiesList = () => {
}
}
export const useCities = (selectedCountryId?: number) => {
const [cityQuery, setCityQuery] = useState('')
export const useCities = () => {
const { updateFormValue } = useForm()
const [selectedCity, setSelectedCity] = useState<City | null>(null)
const setCityQuery = useCallback(updateFormValue(formIds.city), [])
const {
cities,
getCities,
resetCities,
} = useCitiesList()
const trimmedCity = trim(cityQuery)
useEffect(() => {
if (trimmedCity && selectedCountryId) {
getCities(trimmedCity, selectedCountryId)
}
}, [
trimmedCity,
selectedCountryId,
getCities,
])
const onCityQueryChange = ({ target }: ChangeEvent<HTMLInputElement>) => {
setCityQuery(target.value)
}
const onCitySelect = (newCity: City | null) => {
if (newCity) {
setCityQuery(newCity.name)
@ -66,18 +53,14 @@ export const useCities = (selectedCountryId?: number) => {
}
}
const resetSelectedCity = useCallback(
() => {
setCityQuery('')
setSelectedCity(null)
},
[setCityQuery, setSelectedCity],
)
const resetSelectedCity = () => {
setCityQuery('')
setSelectedCity(null)
}
return {
cities,
cityQuery,
onCityQueryChange,
getCities,
onCitySelect,
resetCities,
resetSelectedCity,

@ -11,7 +11,7 @@ import type { Countries } from 'requests'
import { getCountries } from 'requests'
import { useLexicsStore } from 'features/LexicsStore'
type Country = {
export type Country = {
id: number,
name: string,
}
@ -48,7 +48,7 @@ export const useCountries = () => {
return {
countries: transformedCountries,
onCountrySelect: setSelectedCountry,
selectedCountry,
setSelectedCountry,
}
}

@ -1,91 +1,88 @@
import type { FormEvent } from 'react'
import type { ChangeEvent, FocusEvent } from 'react'
import { useEffect } from 'react'
import trim from 'lodash/trim'
import { useAuthStore } from 'features/AuthStore'
import { useForm } from 'features/FormStore'
import { isValidEmail } from 'features/Register/helpers/isValidEmail'
import { formIds } from '../config'
import type { Country } from './useCountries'
import { useCountries } from './useCountries'
import { useCities } from './useCities'
import { useSubmitHandler } from './useSubmitHandler'
const readFormValue = (event: FormEvent<HTMLFormElement>) => (
(fieldName: string) => trim(event.currentTarget[fieldName]?.value)
)
export const useForm = () => {
const { register } = useAuthStore()
export const useRegistrationForm = () => {
const {
readFormError,
readFormValue,
updateFormError,
updateFormValue,
} = useForm()
const {
countries,
onCountrySelect,
selectedCountry,
setSelectedCountry,
} = useCountries()
const {
cities,
cityQuery,
onCityQueryChange,
getCities,
onCitySelect,
resetCities,
resetSelectedCity,
selectedCity,
} = useCities(selectedCountry?.id)
} = useCities()
useEffect(() => {
resetSelectedCity()
resetCities()
}, [
const handleSubmit = useSubmitHandler({
selectedCity,
selectedCountry,
resetSelectedCity,
resetCities,
])
})
const getCityParams = () => {
if (selectedCity) return { cityId: selectedCity.id }
return { city: cityQuery }
const onEmailBlur = ({ target }: FocusEvent<HTMLInputElement>) => {
const email = trim(target.value)
if (email && !isValidEmail(email)) {
updateFormError(formIds.email, 'error_invalid_email_format')
}
}
const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault()
if (!selectedCountry) return
const onCountrySelect = (country: Country | null) => {
setSelectedCountry(country)
updateFormValue(formIds.country)(country?.name || '')
resetCities()
resetSelectedCity()
}
const readFieldValue = readFormValue(event)
const firstname = readFieldValue(formIds.firstname)
const lastname = readFieldValue(formIds.lastname)
const phone = readFieldValue(formIds.phone)
const email = readFieldValue(formIds.email)
const password = readFieldValue(formIds.password)
const postalCode = Number(readFieldValue(formIds.postalCode))
const region = readFieldValue(formIds.region)
const address1 = readFieldValue(formIds.address1)
const address2 = readFieldValue(formIds.address2)
const onRegionOrCityChange = (fieldName: string) => (
({ target }: ChangeEvent<HTMLInputElement>) => {
if (selectedCountry) {
updateFormValue(fieldName)(target.value)
} else {
updateFormError(formIds.country, 'error_select_country_first')
}
}
)
if (isValidEmail(email)) {
register({
address1,
address2,
...getCityParams(),
countryId: selectedCountry.id,
email,
firstname,
lastname,
password,
phone,
postalCode,
region,
})
const trimmedCity = trim(readFormValue(formIds.city))
useEffect(() => {
if (trimmedCity && selectedCountry?.id) {
getCities(trimmedCity, selectedCountry.id)
}
}
}, [
trimmedCity,
selectedCountry,
getCities,
])
return {
cities,
cityQuery,
countries,
handleSubmit,
onCityQueryChange,
onCitySelect,
onCountrySelect,
selectedCountry,
onEmailBlur,
onRegionOrCityChange,
readFormError,
readFormValue,
updateFormValue,
}
}

@ -0,0 +1,65 @@
import type { FormEvent } from 'react'
import trim from 'lodash/trim'
import type { City } from 'requests'
import { useAuthStore } from 'features/AuthStore'
import { useForm } from 'features/FormStore'
import { formIds } from '../config'
import type { Country } from './useCountries'
import { useValidateForm } from './useValidateForm'
type Args = {
selectedCity: City | null,
selectedCountry: Country | null,
}
export const useSubmitHandler = ({
selectedCity,
selectedCountry,
}: Args) => {
const { register } = useAuthStore()
const { readFormValue } = useForm()
const validateForm = useValidateForm()
const readTrimmedValue = (fieldName: string) => trim(readFormValue(fieldName))
const getCityParams = () => {
if (selectedCity) return { cityId: selectedCity.id }
return { city: readTrimmedValue(formIds.city) }
}
const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault()
if (validateForm() && selectedCountry) {
const firstname = readTrimmedValue(formIds.firstname)
const lastname = readTrimmedValue(formIds.lastname)
const phone = readTrimmedValue(formIds.phone)
const email = readTrimmedValue(formIds.email)
const password = readTrimmedValue(formIds.password)
const postalCode = Number(readTrimmedValue(formIds.postalCode))
const region = readTrimmedValue(formIds.region)
const address1 = readTrimmedValue(formIds.address1)
const address2 = readTrimmedValue(formIds.address2)
register({
address1,
address2,
...getCityParams(),
countryId: selectedCountry.id,
email,
firstname,
lastname,
password,
phone,
postalCode,
region,
})
}
}
return handleSubmit
}

@ -0,0 +1,62 @@
import trim from 'lodash/trim'
import reduce from 'lodash/reduce'
import { useForm } from 'features/FormStore'
import { isValidEmail } from 'features/Register/helpers/isValidEmail'
import { isValidPassword } from 'features/Register/helpers/isValidPassword'
import {
formIds,
requiredFields,
simpleValidationFields,
} from '../config'
export const useValidateForm = () => {
const {
allFieldsEmpty,
isFieldEmpty,
readFormValue,
updateFormError,
} = useForm()
const readTrimmedValue = (fieldName: string) => trim(readFormValue(fieldName))
const setErrorOnEmptyFields = (fieldNames: Array<string>, message: string) => (
reduce(
fieldNames,
(acc, fieldName) => {
if (isFieldEmpty(fieldName)) {
updateFormError(fieldName, message)
return acc + 1
}
return acc
},
0,
)
)
const validateForm = () => {
let errorsCount = 0
const email = readTrimmedValue(formIds.email)
const password = readTrimmedValue(formIds.password)
if (allFieldsEmpty(requiredFields)) {
updateFormError(formIds.formError, 'error_fill_out_required_fields')
setErrorOnEmptyFields(requiredFields, '')
return false
}
if (isFieldEmpty(formIds.email)) {
updateFormError(formIds.email, 'error_empty_email')
errorsCount++
} else if (!isValidEmail(email)) {
updateFormError(formIds.email, 'error_invalid_email_format')
}
if (!isValidPassword(password)) {
updateFormError(formIds.password, 'error_simple_password')
errorsCount++
}
errorsCount += setErrorOnEmptyFields(simpleValidationFields, 'error_fill_out_this_field')
return errorsCount === 0
}
return validateForm
}

@ -3,34 +3,32 @@ import React from 'react'
import { T9n } from 'features/T9n'
import { Combobox } from 'features/Combobox'
import { Input, ButtonSolid } from 'features/Common'
import { Error } from 'features/Common/Input/styled'
import {
BlockTitle,
ButtonsBlock,
Form,
} from 'features/Login/styled'
import { useLexicsStore } from 'features/LexicsStore'
import { FormStore } from 'features/FormStore'
import { formIds } from './config'
import { passwordRegex } from '../../helpers/isValidPassword'
import { emailRegex } from '../../helpers/isValidEmail'
import { useForm } from './hooks/useForm'
import { useRegistrationForm } from './hooks/useForm'
const commonFieldRegex = '^.{0,500}$'
const labelWidth = 116
export const RegistrationStep = () => {
const Registration = () => {
const {
cities,
cityQuery,
countries,
handleSubmit,
onCityQueryChange,
onCitySelect,
onCountrySelect,
selectedCountry,
} = useForm()
const { translate } = useLexicsStore()
const defaultMessage = translate('please_fill_out_this_field')
onEmailBlur,
onRegionOrCityChange,
readFormError,
readFormValue,
updateFormValue,
} = useRegistrationForm()
return (
<Form onSubmit={handleSubmit}>
@ -39,107 +37,116 @@ export const RegistrationStep = () => {
</BlockTitle>
<Input
required
id={formIds.firstname}
value={readFormValue(formIds.firstname)}
error={readFormError(formIds.firstname)}
labelLexic='form_firstname'
labelWidth={labelWidth}
pattern={commonFieldRegex}
title={defaultMessage}
onChange={updateFormValue(formIds.firstname)}
/>
<Input
required
id={formIds.lastname}
value={readFormValue(formIds.lastname)}
error={readFormError(formIds.lastname)}
labelLexic='form_lastname'
labelWidth={labelWidth}
pattern={commonFieldRegex}
title={defaultMessage}
onChange={updateFormValue(formIds.lastname)}
/>
<Input
required
id={formIds.phone}
value={readFormValue(formIds.phone)}
error={readFormError(formIds.phone)}
type='tel'
labelLexic='form_phone'
labelWidth={labelWidth}
pattern='^[0-9]{8,100}$'
title={defaultMessage}
onChange={updateFormValue(formIds.phone)}
/>
<Input
required
id={formIds.email}
value={readFormValue(formIds.email)}
error={readFormError(formIds.email)}
type='email'
labelLexic='form_email'
labelWidth={labelWidth}
pattern={emailRegex}
title={defaultMessage}
onChange={updateFormValue(formIds.email)}
onBlur={onEmailBlur}
/>
<Input
required
id={formIds.password}
value={readFormValue(formIds.password)}
error={readFormError(formIds.password)}
type='password'
labelLexic='form_password'
labelWidth={labelWidth}
pattern={passwordRegex}
title={defaultMessage}
onChange={updateFormValue(formIds.password)}
/>
<Combobox
required
openOnFocus
withArrow
id={formIds.country}
value={readFormValue(formIds.country)}
error={readFormError(formIds.country)}
labelLexic='form_country'
labelWidth={labelWidth}
options={countries}
onChange={updateFormValue(formIds.country)}
onSelect={onCountrySelect}
title={defaultMessage}
/>
<Combobox
required
id={formIds.city}
value={readFormValue(formIds.city)}
error={readFormError(formIds.city)}
labelLexic='form_city'
labelWidth={labelWidth}
disabled={!selectedCountry}
value={cityQuery}
onChange={onCityQueryChange}
onChange={onRegionOrCityChange(formIds.city)}
options={cities}
onSelect={onCitySelect}
pattern={commonFieldRegex}
title={defaultMessage}
/>
<Input
required
id={formIds.postalCode}
value={readFormValue(formIds.postalCode)}
error={readFormError(formIds.postalCode)}
labelLexic='form_postal_code'
labelWidth={labelWidth}
title={defaultMessage}
onChange={updateFormValue(formIds.postalCode)}
/>
<Input
required
id={formIds.region}
value={readFormValue(formIds.region)}
error={readFormError(formIds.region)}
labelLexic='form_region'
labelWidth={labelWidth}
pattern={commonFieldRegex}
title={defaultMessage}
onChange={onRegionOrCityChange(formIds.region)}
/>
<Input
required
id={formIds.address1}
value={readFormValue(formIds.address1)}
error={readFormError(formIds.address1)}
labelLexic='form_address1'
labelWidth={labelWidth}
pattern={commonFieldRegex}
title={defaultMessage}
onChange={updateFormValue(formIds.address1)}
/>
<Input
id={formIds.address2}
value={readFormValue(formIds.address2)}
error={readFormError(formIds.address2)}
labelLexic='form_address2'
labelWidth={labelWidth}
pattern={commonFieldRegex}
onChange={updateFormValue(formIds.address2)}
/>
<ButtonsBlock>
<ButtonSolid type='submit'>
<T9n t='next' />
</ButtonSolid>
<Error><T9n t={readFormError(formIds.formError) || ''} /></Error>
</ButtonsBlock>
</Form>
)
}
export const RegistrationStep = () => (
<FormStore>
<Registration />
</FormStore>
)

Loading…
Cancel
Save