From 9da11400d9aa68b0d6dc420503bd644a6433463b Mon Sep 17 00:00:00 2001 From: Mirlan Date: Thu, 27 Aug 2020 20:04:50 +0600 Subject: [PATCH] Ott 318 sport type filter clear btn (#107) * chore(#318): fixed eslint rules order * feat(#318): added sport type filter clear button * fix(#318): fixed useLocalStore hook bug when validator was not specified * refactor(#318): replaced lang useState to useLocalStore hook Co-authored-by: mirlan.maksitaliev --- .eslintrc | 4 ++-- .../components/SportTypeFilter/hooks.tsx | 7 +++++++ .../components/SportTypeFilter/index.tsx | 3 +++ .../components/TournamentFilter/styled.tsx | 7 +++---- src/features/LexicsStore/helpers/index.tsx | 15 --------------- .../isSupportedLang/__tests__/index.tsx | 13 +++++++++++++ .../helpers/isSupportedLang/index.tsx | 9 +++++++++ src/features/LexicsStore/hooks/useLang.tsx | 18 +++++++++++++----- src/hooks/useStorage/helpers.tsx | 6 +++++- src/hooks/useStorage/index.tsx | 10 +++++++--- 10 files changed, 62 insertions(+), 30 deletions(-) create mode 100644 src/features/LexicsStore/helpers/isSupportedLang/__tests__/index.tsx create mode 100644 src/features/LexicsStore/helpers/isSupportedLang/index.tsx diff --git a/.eslintrc b/.eslintrc index 6aeabb19..099cf2e1 100644 --- a/.eslintrc +++ b/.eslintrc @@ -26,6 +26,7 @@ "requireLast": false } }], + "@typescript-eslint/semi": ["error", "never"], "import/extensions": [ "warn", "never", @@ -87,7 +88,6 @@ "no-unused-vars": "off", "react/jsx-one-expression-per-line": "off", "react/jsx-fragments": "off", - "semi": "off", - "@typescript-eslint/semi": ["error", "never"] + "semi": "off" } } diff --git a/src/features/HeaderFilters/components/SportTypeFilter/hooks.tsx b/src/features/HeaderFilters/components/SportTypeFilter/hooks.tsx index 9fc75402..5b43980b 100644 --- a/src/features/HeaderFilters/components/SportTypeFilter/hooks.tsx +++ b/src/features/HeaderFilters/components/SportTypeFilter/hooks.tsx @@ -1,3 +1,4 @@ +import type { MouseEvent } from 'react' import { useState, useEffect } from 'react' import find from 'lodash/find' @@ -31,10 +32,16 @@ export const useSportTypeFilter = () => { close() } + const onResetSelectedSport = (e: MouseEvent) => { + e.stopPropagation() + setSelectedSportTypeId(null) + } + const selectedSportType = find(sportList, (sport) => sport.id === selectedSportTypeId) return { close, isOpen, + onResetSelectedSport, onSelect, open, selectedSportType, diff --git a/src/features/HeaderFilters/components/SportTypeFilter/index.tsx b/src/features/HeaderFilters/components/SportTypeFilter/index.tsx index 9c527d73..08c35e36 100644 --- a/src/features/HeaderFilters/components/SportTypeFilter/index.tsx +++ b/src/features/HeaderFilters/components/SportTypeFilter/index.tsx @@ -14,6 +14,7 @@ import { import { DropdownButton, ButtonTitle, + ClearButton, Arrows, } from '../TournamentFilter/styled' @@ -21,6 +22,7 @@ export const SportTypeFilter = () => { const { close, isOpen, + onResetSelectedSport, onSelect, open, selectedSportType, @@ -38,6 +40,7 @@ export const SportTypeFilter = () => { + {selectedSportType && } diff --git a/src/features/HeaderFilters/components/TournamentFilter/styled.tsx b/src/features/HeaderFilters/components/TournamentFilter/styled.tsx index a7796954..4a8aea9c 100644 --- a/src/features/HeaderFilters/components/TournamentFilter/styled.tsx +++ b/src/features/HeaderFilters/components/TournamentFilter/styled.tsx @@ -113,11 +113,10 @@ export const Wrapper = styled.div` } ` -export const ClearButton = styled.button` - outline: none; - border: none; +export const ClearButton = styled.span` + display: inline-block; cursor: pointer; - width: 10px; + min-width: 10px; height: 10px; margin-left: 10px; background-color: transparent; diff --git a/src/features/LexicsStore/helpers/index.tsx b/src/features/LexicsStore/helpers/index.tsx index 449408b4..bad471da 100644 --- a/src/features/LexicsStore/helpers/index.tsx +++ b/src/features/LexicsStore/helpers/index.tsx @@ -8,21 +8,6 @@ import type { Translations } from 'requests' import type { LexicsId, LexicsConfig } from '../types' -const LANG_KEY = 'lang' -const defaultLang = 'en' - -export const writeLang = (lang: string) => { - localStorage.setItem(LANG_KEY, lang) -} - -export const readLang = () => { - const lang = localStorage.getItem(LANG_KEY) - if (lang) return lang - - writeLang(defaultLang) - return defaultLang -} - export const getSuffix = (lang: string) => ( lang === 'ru' ? 'rus' : 'eng' ) diff --git a/src/features/LexicsStore/helpers/isSupportedLang/__tests__/index.tsx b/src/features/LexicsStore/helpers/isSupportedLang/__tests__/index.tsx new file mode 100644 index 00000000..9ccb22df --- /dev/null +++ b/src/features/LexicsStore/helpers/isSupportedLang/__tests__/index.tsx @@ -0,0 +1,13 @@ +import { isSupportedLang } from '..' + +it('returns true for supported languages', () => { + expect(isSupportedLang('ru')).toBe(true) + expect(isSupportedLang('en')).toBe(true) +}) + +it('returns false for not supported languages', () => { + expect(isSupportedLang('es')).toBe(false) + expect(isSupportedLang('ja')).toBe(false) + expect(isSupportedLang('fr')).toBe(false) + expect(isSupportedLang('de')).toBe(false) +}) diff --git a/src/features/LexicsStore/helpers/isSupportedLang/index.tsx b/src/features/LexicsStore/helpers/isSupportedLang/index.tsx new file mode 100644 index 00000000..062d35a8 --- /dev/null +++ b/src/features/LexicsStore/helpers/isSupportedLang/index.tsx @@ -0,0 +1,9 @@ +import map from 'lodash/map' +import includes from 'lodash/includes' + +import { langsList } from 'features/LanguageSelect/config' + +export const isSupportedLang = (lang: string) => { + const supportedLangs = map(langsList, 'locale') + return includes(supportedLangs, lang) +} diff --git a/src/features/LexicsStore/hooks/useLang.tsx b/src/features/LexicsStore/hooks/useLang.tsx index e9099ec6..97990e4e 100644 --- a/src/features/LexicsStore/hooks/useLang.tsx +++ b/src/features/LexicsStore/hooks/useLang.tsx @@ -1,17 +1,25 @@ -import { useState, useCallback } from 'react' +import { useCallback } from 'react' -import { readLang, writeLang } from 'features/LexicsStore/helpers' +import { useLocalStore } from 'hooks' + +import { isSupportedLang } from '../helpers/isSupportedLang' + +const LANG_KEY = 'lang' +const DEFAULT_LANG = 'en' export const useLang = () => { - const [lang, setLang] = useState(readLang) + const [lang, setLang] = useLocalStore({ + defaultValue: DEFAULT_LANG, + key: LANG_KEY, + validator: isSupportedLang, + }) const changeLang = useCallback( (newLang: string) => { if (newLang === lang) return - writeLang(newLang) setLang(newLang) }, - [lang], + [lang, setLang], ) return { changeLang, lang } diff --git a/src/hooks/useStorage/helpers.tsx b/src/hooks/useStorage/helpers.tsx index 0ca2e99b..fd0e2c6d 100644 --- a/src/hooks/useStorage/helpers.tsx +++ b/src/hooks/useStorage/helpers.tsx @@ -39,7 +39,11 @@ export const dateReplacer = (key: string, value: string) => { export const readStorageInitialValue = (storage: Storage, key: string) => { const rawValue = storage.getItem(key) if (rawValue) { - return JSON.parse(rawValue, dateReviver) + try { + return JSON.parse(rawValue, dateReviver) + } catch (error) { + return null + } } return null } diff --git a/src/hooks/useStorage/index.tsx b/src/hooks/useStorage/index.tsx index e28262c5..b5b0d617 100644 --- a/src/hooks/useStorage/index.tsx +++ b/src/hooks/useStorage/index.tsx @@ -11,13 +11,17 @@ import { readStorageInitialValue, } from './helpers' +const defaultValidator = () => true + type Args = { defaultValue: T, key: string, /** * функция для валидации полученного значения из стора - * если значение не валидно то используется defaultValue + * если значение не валидно то используется defaultValue. + * Если не передан валидатор то полученное значение из стореджа + * считается валидным. */ validator?: (value: T) => boolean, } @@ -32,10 +36,10 @@ const createHook = (storage: Storage) => ( ({ defaultValue, key, - validator, + validator = defaultValidator, }: Args) => { const storeValue = readStorageInitialValue(storage, key) - const isValid = validator && validator(storeValue) + const isValid = validator(storeValue) const initialState = isValid ? storeValue : defaultValue const [state, setState] = useState(initialState)