From df68d0d8bc273533dcb5a85a5a699aca12404d67 Mon Sep 17 00:00:00 2001 From: Joe Kleinschmidt Date: Wed, 18 Oct 2023 09:19:00 -0500 Subject: [PATCH] fix: Language selector: add prop for display lang for bug fix (#2622) --- .../LanguageSelector.stories.tsx | 76 +++++++++++++++++- .../LanguageSelector/LanguageSelector.tsx | 66 +++------------ .../LanguageSelectorDropdown.tsx | 80 +++++++++++++++++++ 3 files changed, 165 insertions(+), 57 deletions(-) create mode 100644 src/components/LanguageSelector/LanguageSelectorDropdown.tsx diff --git a/src/components/LanguageSelector/LanguageSelector.stories.tsx b/src/components/LanguageSelector/LanguageSelector.stories.tsx index d09ab6dd61..8f75319fab 100644 --- a/src/components/LanguageSelector/LanguageSelector.stories.tsx +++ b/src/components/LanguageSelector/LanguageSelector.stories.tsx @@ -1,10 +1,12 @@ -import React from 'react' +import React, { useState } from 'react' import { LanguageSelector, LanguageDefinition } from './LanguageSelector' +import { ComponentStory } from '@storybook/react' export default { title: 'Components/LanguageSelector', component: LanguageSelector, argTypes: { + displayLang: { control: 'string' }, small: { control: 'boolean' }, }, parameters: { @@ -19,6 +21,78 @@ Source: https://designsystem.digital.gov/components/language-selector/ }, }, } + +const TwoLanguagesTemplate: ComponentStory = ( + args +) => { + const [lang, setLang] = useState(args.displayLang) + + const languagesDisplayProp: LanguageDefinition[] = [ + { + label: '简体字', + label_local: 'Chinese - Simplified', + attr: 'zh', + on_click: () => { + setLang(`en`) + }, + }, + { + label: 'English', + attr: 'en', + on_click: () => { + setLang(`zh`) + }, + }, + ] + + return +} + +export const LanguagesDisplayPropSandbox = TwoLanguagesTemplate.bind({}) +LanguagesDisplayPropSandbox.args = { + displayLang: `en`, +} + +const MoreThanTwoLanguagesTemplate: ComponentStory = ( + args +) => { + const [lang, setLang] = useState(args.displayLang) + + const languagesDisplayProp: LanguageDefinition[] = [ + { + label: 'العربية', + label_local: 'Arabic', + attr: 'ar', + on_click: () => { + setLang(`ar`) + }, + }, + { + label: '简体字', + label_local: 'Chinese - Simplified', + attr: 'zh', + on_click: () => { + setLang(`zh`) + }, + }, + { + label: 'English', + attr: 'en', + on_click: () => { + setLang(`en`) + }, + }, + ] + + return +} + +export const LanguagesDisplayMoreThanTwoLanguagesPropSandbox = + MoreThanTwoLanguagesTemplate.bind({}) +LanguagesDisplayPropSandbox.args = { + displayLang: `en`, +} + type StorybookArguments = { small?: boolean } diff --git a/src/components/LanguageSelector/LanguageSelector.tsx b/src/components/LanguageSelector/LanguageSelector.tsx index 976c4141db..f8a01e6349 100644 --- a/src/components/LanguageSelector/LanguageSelector.tsx +++ b/src/components/LanguageSelector/LanguageSelector.tsx @@ -1,8 +1,7 @@ import React, { useState } from 'react' import classnames from 'classnames' -import { Menu } from '../header/Menu/Menu' import { LanguageSelectorButton } from './LanguageSelectorButton' -import { Button } from '../Button/Button' +import LanguageSelectorDropdown from './LanguageSelectorDropdown' export type LanguageDefinition = { label: string @@ -11,11 +10,12 @@ export type LanguageDefinition = { on_click: string | (() => void) } -type LanguageSelectorProps = { +export type LanguageSelectorProps = { label?: string langs: LanguageDefinition[] small?: boolean className?: string + displayLang?: string } export const LanguageSelector = ({ @@ -23,6 +23,7 @@ export const LanguageSelector = ({ langs, small, className, + displayLang, ...divProps }: LanguageSelectorProps & JSX.IntrinsicElements['div']): React.ReactElement => { @@ -34,66 +35,19 @@ export const LanguageSelector = ({ className ) - const [isOpen, setIsOpen] = useState(false) const [langIndex, setLangIndex] = useState(false) if (langs.length > 2) { - const items = [] - for (let i = 0; i < langs.length; i++) { - // eslint-disable-next-line security/detect-object-injection - const lang: LanguageDefinition = langs[i] - if (typeof lang.on_click === 'string') { - items.push( - - - {lang.label} - - {lang.label_local && ` (${lang.label_local})`} - - ) - } else { - items.push( - - ) - } - } - return ( -
-
    -
  • - { - setIsOpen((prevIsOpen) => !prevIsOpen) - }} - /> - -
  • -
-
- ) + const dropdownProps = { label, langs, small, displayLang } + return } else { if (label) { console.warn( "LanguageSelector's label is not used when only two languages are available." ) } - const curLang = langs[Number(langIndex)] + const curLang = + langs.find((langDef) => langDef.attr === displayLang) || + langs[Number(langIndex)] const onClickString: string = typeof curLang.on_click === 'string' ? curLang.on_click : '' const onClick = @@ -110,7 +64,7 @@ export const LanguageSelector = ({ labelAttr={curLang.attr} onToggle={() => { onClick() - setLangIndex((prevLangIndex) => !prevLangIndex) + if (!displayLang) setLangIndex((prevLangIndex) => !prevLangIndex) }} /> diff --git a/src/components/LanguageSelector/LanguageSelectorDropdown.tsx b/src/components/LanguageSelector/LanguageSelectorDropdown.tsx new file mode 100644 index 0000000000..7b22236ca7 --- /dev/null +++ b/src/components/LanguageSelector/LanguageSelectorDropdown.tsx @@ -0,0 +1,80 @@ +import React, { useState } from 'react' +import { Menu } from '../header/Menu/Menu' +import { LanguageSelectorButton } from './LanguageSelectorButton' +import classnames from 'classnames' +import { LanguageDefinition, LanguageSelectorProps } from './LanguageSelector' +import { Button } from '../Button/Button' + +const generateMenuItems = (langs: LanguageDefinition[]) => { + return langs.map((lang, index) => { + const label = ( + <> + + {lang.label} + + {lang.label_local && ` (${lang.label_local})`} + + ) + if (typeof lang.on_click === 'string') { + return ( + + {label} + + ) + } else { + return ( + + ) + } + }) +} + +const LanguageSelectorDropdown: React.FC = ({ + label, + langs, + small, + className, + displayLang, + ...divProps +}) => { + const [isOpen, setIsOpen] = useState(false) + + const classes = classnames( + 'usa-language-container', + { + [`usa-language--small`]: small !== undefined, + }, + className + ) + const displayLabel = langs.find((langDef) => langDef.attr === displayLang) + + return ( +
+
    +
  • + setIsOpen((prevIsOpen) => !prevIsOpen)} + /> + +
  • +
+
+ ) +} + +export default LanguageSelectorDropdown