feat(ott-91): added countrySelector component

keep-around/af30b88d367751c9e05a735e4a0467a96238ef47
mirlan.maksitaliev 6 years ago
parent f18b452fc1
commit d7dd4c4c32
  1. 1
      package.json
  2. 4
      public/images/arrowDown.svg
  3. 14
      src/features/Common/Input/styled.tsx
  4. 88
      src/features/CountrySelector/hooks.tsx
  5. 63
      src/features/CountrySelector/index.tsx
  6. 27
      src/features/CountrySelector/styled.tsx
  7. 11
      src/features/Register/Steps/Registration/index.tsx

@ -13,6 +13,7 @@
"build-storybook": "build-storybook -s public" "build-storybook": "build-storybook -s public"
}, },
"dependencies": { "dependencies": {
"@reach/combobox": "^0.10.4",
"history": "^4.10.1", "history": "^4.10.1",
"lodash": "^4.17.15", "lodash": "^4.17.15",
"react": "^16.13.1", "react": "^16.13.1",

@ -0,0 +1,4 @@
<svg width="12" height="8" viewBox="0 0 12 8" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.6 0.600098L6 5.2001L1.4 0.600098L0 2.0001L6 8.0001L12 2.0001L10.6 0.600098Z" fill="#999999"/>
</svg>

After

Width:  |  Height:  |  Size: 250 B

@ -1,11 +1,11 @@
import styled from 'styled-components/macro' import styled, { css } from 'styled-components/macro'
export type TInputWrapper = { export type TInputWrapper = {
paddingX?: number, paddingX?: number,
wrapperWidth?: number, wrapperWidth?: number,
} }
export const InputWrapper = styled.div<TInputWrapper>` export const wrapperStyles = css<TInputWrapper>`
width: ${({ wrapperWidth }) => (wrapperWidth ? `${wrapperWidth}px` : '100%')}; width: ${({ wrapperWidth }) => (wrapperWidth ? `${wrapperWidth}px` : '100%')};
height: 48px; height: 48px;
margin: 20px 0; margin: 20px 0;
@ -20,6 +20,10 @@ export const InputWrapper = styled.div<TInputWrapper>`
border-radius: 2px; border-radius: 2px;
` `
export const InputWrapper = styled.div<TInputWrapper>`
${wrapperStyles}
`
type TLabel = { type TLabel = {
labelWidth?: number, labelWidth?: number,
} }
@ -39,7 +43,7 @@ type TInputStyled = {
inputWidth?: number, inputWidth?: number,
} }
export const InputStyled = styled.input<TInputStyled>` export const inputStyles = css<TInputStyled>`
flex-grow: 1; flex-grow: 1;
font-weight: bold; font-weight: bold;
font-size: 20px; font-size: 20px;
@ -68,3 +72,7 @@ export const InputStyled = styled.input<TInputStyled>`
-webkit-text-fill-color: ${({ theme: { colors } }) => colors.text}; -webkit-text-fill-color: ${({ theme: { colors } }) => colors.text};
} }
` `
export const InputStyled = styled.input<TInputStyled>`
${inputStyles}
`

@ -0,0 +1,88 @@
import type { ChangeEvent, FocusEvent } from 'react'
import type { Countries } from 'requests'
import {
useEffect,
useState,
useCallback,
} from 'react'
import toLower from 'lodash/toLower'
import slice from 'lodash/slice'
import find from 'lodash/find'
import { getCountries } from 'requests'
import { matchSort } from './helpers'
// временно, будем считывать из стора(контекста) лексики
const useCurrentLang = () => 'eng'
const useCountriesList = () => {
const [countries, setCountries] = useState<Countries>([])
useEffect(() => {
getCountries().then(setCountries)
}, [])
return countries
}
const useQuery = () => {
const [query, setQuery] = useState('')
const onQueryChange = useCallback(({ target }: ChangeEvent<HTMLInputElement>) => {
setQuery(target.value)
}, [])
return {
onQueryChange,
query,
setQuery,
}
}
const isOptionClicked = (target: HTMLElement) => target?.getAttribute('role') === 'option'
export const useCountrySelector = () => {
const lang = useCurrentLang()
const countries = useCountriesList()
const {
onQueryChange,
query,
setQuery,
} = useQuery()
const keyToSortBy = `name_${lang}` as 'name_eng'
const results = matchSort(
countries,
keyToSortBy,
query,
)
const onBlur = (event: FocusEvent<HTMLInputElement>) => {
const target = event.relatedTarget as HTMLElement | null
// клик по элементу списка тоже вызывает onBlur
// если кликали элемент списка то событие обрабатывается onCountrySelect
if (target && isOptionClicked(target)) return
const found = find(
countries,
(country) => toLower(country[keyToSortBy]) === toLower(query),
)
setQuery(found ? found[keyToSortBy] : '')
}
return {
countries: slice(
results,
0,
20,
),
onBlur,
onCountrySelect: setQuery,
onQueryChange,
query,
}
}

@ -0,0 +1,63 @@
import React from 'react'
import isEmpty from 'lodash/isEmpty'
import map from 'lodash/map'
import {
ComboboxPopover,
ComboboxList,
ComboboxOption,
} from '@reach/combobox'
import '@reach/combobox/styles.css'
import { Label } from 'features/Common/Input/styled'
import { useCountrySelector } from './hooks'
import {
ComboboxStyled,
ComboboxInputStyled,
Arrow,
} from './styled'
type Props = {
labelWidth?: number,
}
export const CountrySelector = ({ labelWidth }: Props) => {
const {
countries,
onBlur,
onCountrySelect,
onQueryChange,
query,
} = useCountrySelector()
return (
<ComboboxStyled onSelect={onCountrySelect}>
<Label
labelWidth={labelWidth}
htmlFor='country'
>
Страна
<Arrow />
</Label>
<ComboboxInputStyled
id='country'
value={query}
onChange={onQueryChange}
onBlur={onBlur}
/>
{!isEmpty(countries) && (
<ComboboxPopover>
<ComboboxList>
{map(countries, ({ id, name_eng }) => (
<ComboboxOption
key={id}
value={name_eng}
/>
))}
</ComboboxList>
</ComboboxPopover>
)}
</ComboboxStyled>
)
}

@ -0,0 +1,27 @@
import styled from 'styled-components/macro'
import { Combobox, ComboboxInput } from '@reach/combobox'
import { wrapperStyles, inputStyles } from 'features/Common/Input/styled'
export const ComboboxStyled = styled(Combobox)`
${wrapperStyles}
position: relative;
`
export const ComboboxInputStyled = styled(ComboboxInput)`
${inputStyles}
padding-right: 24px;
`
export const Arrow = styled.div`
position: absolute;
right: 20px;
top: 50%;
transform: translateY(-50%);
width: 12px;
height: 12px;
background-image: url(/images/arrowDown.svg);
background-position: center;
background-repeat: no-repeat;
`

@ -2,6 +2,7 @@ import React from 'react'
import { PAGES } from 'config' import { PAGES } from 'config'
import { CountrySelector } from 'features/CountrySelector'
import { Input } from 'features/Common' import { Input } from 'features/Common'
import { import {
BlockTitle, BlockTitle,
@ -38,15 +39,15 @@ export const Registration = () => (
label='E-mail' label='E-mail'
labelWidth={labelWidth} labelWidth={labelWidth}
/> />
{/* TODO: it should be Dropdown */} <CountrySelector labelWidth={78} />
<Input <Input
id='country' id='address1'
label='Страна' label='Адрес 1'
labelWidth={labelWidth} labelWidth={labelWidth}
/> />
<Input <Input
id='address' id='address2'
label='Адрес' label='Адрес 2'
labelWidth={labelWidth} labelWidth={labelWidth}
/> />

Loading…
Cancel
Save