From 5d183b513cabf5885419dee4c2613e07370438c9 Mon Sep 17 00:00:00 2001 From: Christian Samaniego <199278128+christiansamaniego-okta@users.noreply.github.com> Date: Tue, 14 Oct 2025 23:46:46 -0500 Subject: [PATCH 1/5] fix property names on svg components --- src/app/apple-icon.tsx | 40 +++++++++---------- src/app/icon.tsx | 40 +++++++++---------- .../assets/arrow-head-icon.component.tsx | 6 +-- .../common/assets/download-icon.component.tsx | 12 +++--- .../ribbon/assets/dark-icon.component.tsx | 4 +- .../ribbon/assets/light-icon.component.tsx | 6 +-- .../ribbon/assets/system-icon.component.tsx | 12 +++--- 7 files changed, 60 insertions(+), 60 deletions(-) diff --git a/src/app/apple-icon.tsx b/src/app/apple-icon.tsx index f594f156..85294345 100644 --- a/src/app/apple-icon.tsx +++ b/src/app/apple-icon.tsx @@ -86,62 +86,62 @@ export default function Icon({ id }: { id: string }) { xmlns="http://www.w3.org/2000/svg" > diff --git a/src/app/icon.tsx b/src/app/icon.tsx index 61cc18cc..1b981273 100644 --- a/src/app/icon.tsx +++ b/src/app/icon.tsx @@ -66,62 +66,62 @@ export default function Icon({ id }: { id: string }) { xmlns="http://www.w3.org/2000/svg" > diff --git a/src/features/common/assets/arrow-head-icon.component.tsx b/src/features/common/assets/arrow-head-icon.component.tsx index a0a25859..0910b453 100644 --- a/src/features/common/assets/arrow-head-icon.component.tsx +++ b/src/features/common/assets/arrow-head-icon.component.tsx @@ -12,9 +12,9 @@ export const ArrowHeadIconComponent: React.FC = () => { ); diff --git a/src/features/common/assets/download-icon.component.tsx b/src/features/common/assets/download-icon.component.tsx index c7eb7ec2..f401a279 100644 --- a/src/features/common/assets/download-icon.component.tsx +++ b/src/features/common/assets/download-icon.component.tsx @@ -12,16 +12,16 @@ export const DownloadIconComponent: React.FC = () => { ); diff --git a/src/features/common/components/bars/ribbon/assets/dark-icon.component.tsx b/src/features/common/components/bars/ribbon/assets/dark-icon.component.tsx index 7607d241..3961a806 100644 --- a/src/features/common/components/bars/ribbon/assets/dark-icon.component.tsx +++ b/src/features/common/components/bars/ribbon/assets/dark-icon.component.tsx @@ -12,8 +12,8 @@ export const DarkIconComponent: React.FC = () => { ); diff --git a/src/features/common/components/bars/ribbon/assets/light-icon.component.tsx b/src/features/common/components/bars/ribbon/assets/light-icon.component.tsx index d1f78402..05f3eebe 100644 --- a/src/features/common/components/bars/ribbon/assets/light-icon.component.tsx +++ b/src/features/common/components/bars/ribbon/assets/light-icon.component.tsx @@ -16,11 +16,11 @@ export const LightIconComponent: React.FC = () => { height="6" rx="3" stroke="currentColor" - stroke-width="1.5" + strokeWidth="1.5" > diff --git a/src/features/common/components/bars/ribbon/assets/system-icon.component.tsx b/src/features/common/components/bars/ribbon/assets/system-icon.component.tsx index dbc0ed77..17d5c982 100644 --- a/src/features/common/components/bars/ribbon/assets/system-icon.component.tsx +++ b/src/features/common/components/bars/ribbon/assets/system-icon.component.tsx @@ -12,7 +12,7 @@ export const SystemIconComponent: React.FC = () => { { width="8" height="5" stroke="currentColor" - stroke-width="1.5" - stroke-linejoin="round" + strokeWidth="1.5" + strokeLinejoin="round" > { ); From 41739a3316a198cc374f2fc6d8603ab48fa2fadf Mon Sep 17 00:00:00 2001 From: Christian Samaniego <199278128+christiansamaniego-okta@users.noreply.github.com> Date: Wed, 15 Oct 2025 09:01:02 -0500 Subject: [PATCH 2/5] use react-select to add dropdown --- .../bars/ribbon/ribbon.component.tsx | 39 ---------- .../components/footer/footer.component.tsx | 73 ++++++++++++++++++- .../components/footer/footer.module.scss | 4 + .../common/models/ui-language.model.ts | 2 +- .../localization/dictionaries/layout/en.tsx | 32 ++++---- .../localization/dictionaries/layout/ja.tsx | 32 ++++---- .../models/layout-dictionary.model.ts | 2 +- 7 files changed, 107 insertions(+), 77 deletions(-) diff --git a/src/features/common/components/bars/ribbon/ribbon.component.tsx b/src/features/common/components/bars/ribbon/ribbon.component.tsx index 3403d15e..4827cb2f 100644 --- a/src/features/common/components/bars/ribbon/ribbon.component.tsx +++ b/src/features/common/components/bars/ribbon/ribbon.component.tsx @@ -14,7 +14,6 @@ import { import { RibbonPickerComponent } from "@/features/common/components/bars/ribbon/ribbon-picker/ribbon-picker.component"; import { LightIconComponent } from "@/features/common/components/bars/ribbon/assets/light-icon.component"; import { DarkIconComponent } from "@/features/common/components/bars/ribbon/assets/dark-icon.component"; -import { GlobeIconComponent } from "@/features/common/components/bars/ribbon/assets/globe-icon.component"; import { ThemeModel } from "@/features/common/models/theme.model"; import { useAppStore } from "@/features/common/services/app.store"; import { SystemIconComponent } from "@/features/common/components/bars/ribbon/assets/system-icon.component"; @@ -37,10 +36,6 @@ export const RibbonComponent: React.FC = ({ }) => { const theme$ = useAppStore((state) => state.theme$); - const currentLanguage = dictionary.languagePicker.options.filter( - (element) => element.code === languageCode, - )[0]; - const sanitizedThemePickerCodeValue = useMemo(() => { return getSanitizedThemePickerCodeValue(themeCode); }, [themeCode]); @@ -85,25 +80,6 @@ export const RibbonComponent: React.FC = ({ [dictionary.themePicker.options], ); - const languageOptions = useMemo( - () => - dictionary.languagePicker.options.map((option) => { - return { - code: option.code, - full: { - ...option, - icon: null, - }, - compact: { - ...option, - label: option.code.toUpperCase(), - icon: null, - }, - }; - }), - [dictionary.languagePicker.options], - ); - const handleThemeSelection = useCallback( async (value: ThemePickerCodeValues) => { const themePreference = await savePreferredThemeInCookie( @@ -202,21 +178,6 @@ export const RibbonComponent: React.FC = ({ listLabel: dictionary.themePicker.list.ariaLabel, }} /> - {dictionary.languagePicker.options.length > 1 && ( - } - label={currentLanguage.label} - compactLabel={currentLanguage.code.toUpperCase()} - languageCode={languageCode} - selectedOptionCode={languageCode} - handleSelection={handleLanguageSelection} - options={languageOptions} - aria={{ - buttonLabel: dictionary.languagePicker.button.ariaLabel, - listLabel: dictionary.languagePicker.list.ariaLabel, - }} - /> - )} diff --git a/src/features/common/components/footer/footer.component.tsx b/src/features/common/components/footer/footer.component.tsx index ad06e00d..f4e24ecf 100644 --- a/src/features/common/components/footer/footer.component.tsx +++ b/src/features/common/components/footer/footer.component.tsx @@ -1,6 +1,7 @@ "use client"; import React, { MouseEvent, useState } from "react"; +import Select, { SingleValue, OptionsOrGroups, GroupBase, NonceProvider } from "react-select"; import { FooterIconsComponent } from "./footer-Icons.component"; import { MonoFont, SecondaryFont } from "@/libs/theme/fonts"; import Image from "next/image"; @@ -18,6 +19,8 @@ import { SiteBrandComponent } from "@/features/common/components/site-brand/site import { Button } from "react-aria-components"; import { Auth0LogoComponent } from "../../assets/auth0-logo.component"; import { getBrandDictionary } from "@/features/localization/services/brand-dictionary.service"; +import { savePreferredLanguage } from "@/features/localization/services/ui-language.utils"; +import { UiLanguageModel } from "../../models/ui-language.model"; interface FooterComponentProps { languageCode: string; @@ -28,8 +31,19 @@ export const FooterComponent: React.FC = ({ languageCode, dictionary, }) => { + const currentLanguage = dictionary.languagePicker.options.filter( + (element) => element.value === languageCode + )[0]; + + const handleChange = (selection: SingleValue) => { + if (!selection) { + return; + } + savePreferredLanguage(selection.value); + }; + const [modalState, setModalState] = useState( - ModalStateValues.CLOSED, + ModalStateValues.CLOSED ); const images = getBrandDictionary(languageCode); @@ -64,7 +78,10 @@ export const FooterComponent: React.FC = ({ contentClassName={styles.content} >
- +
@@ -110,7 +127,7 @@ export const FooterComponent: React.FC = ({ }} className={clsx( styles.resource__button, - SecondaryFont.className, + SecondaryFont.className )} > {trigger.text} @@ -158,11 +175,59 @@ export const FooterComponent: React.FC = ({ target="_blank" href="https://auth0.com/" > - + {dictionary.copyright} + {dictionary.languagePicker.options.length > 1 && ( + + )}
diff --git a/src/features/common/components/footer/footer.module.scss b/src/features/common/components/footer/footer.module.scss index 83710432..16324bd8 100644 --- a/src/features/common/components/footer/footer.module.scss +++ b/src/features/common/components/footer/footer.module.scss @@ -267,3 +267,7 @@ color: var(--color_fg_default); } + +.languageSelect__container { + color: var(--color_fg_default); +} \ No newline at end of file diff --git a/src/features/common/models/ui-language.model.ts b/src/features/common/models/ui-language.model.ts index 2c33f718..0d12bf65 100644 --- a/src/features/common/models/ui-language.model.ts +++ b/src/features/common/models/ui-language.model.ts @@ -1,4 +1,4 @@ export interface UiLanguageModel { - code: string; + value: string; label: string; } diff --git a/src/features/localization/dictionaries/layout/en.tsx b/src/features/localization/dictionaries/layout/en.tsx index 1bbe8fa3..14da086d 100644 --- a/src/features/localization/dictionaries/layout/en.tsx +++ b/src/features/localization/dictionaries/layout/en.tsx @@ -39,20 +39,6 @@ export const enLayoutDictionary: LayoutDictionaryModel = { }, ], }, - languagePicker: { - button: { - ariaLabel: "Select page language", - }, - list: { - ariaLabel: "list of page languages", - }, - options: [ - { - code: "en", - label: "English", - }, - ], - }, }, header: { links: [ @@ -162,6 +148,20 @@ export const enLayoutDictionary: LayoutDictionaryModel = { }, ], }, + languagePicker: { + button: { + ariaLabel: "Select page language", + }, + list: { + ariaLabel: "list of page languages", + }, + options: [ + { + value: "en", + label: "English", + }, + ], + }, }, errors: { notFound: { @@ -187,8 +187,8 @@ export const enLayoutDictionary: LayoutDictionaryModel = { }; if (withJapanese) { - enLayoutDictionary.ribbon.languagePicker.options.push({ + enLayoutDictionary.footer.languagePicker.options.push({ label: "日本語", - code: "ja", + value: "ja", }); } diff --git a/src/features/localization/dictionaries/layout/ja.tsx b/src/features/localization/dictionaries/layout/ja.tsx index c24af750..de2f0697 100644 --- a/src/features/localization/dictionaries/layout/ja.tsx +++ b/src/features/localization/dictionaries/layout/ja.tsx @@ -39,20 +39,6 @@ export const jaLayoutDictionary: LayoutDictionaryModel = { }, ], }, - languagePicker: { - button: { - ariaLabel: "ページの言語を選択してください", - }, - list: { - ariaLabel: "ページ言語の一覧", - }, - options: [ - { - code: "en", - label: "English", - }, - ], - }, }, header: { links: [ @@ -162,6 +148,20 @@ export const jaLayoutDictionary: LayoutDictionaryModel = { }, ], }, + languagePicker: { + button: { + ariaLabel: "ページの言語を選択してください", + }, + list: { + ariaLabel: "ページ言語の一覧", + }, + options: [ + { + value: "en", + label: "English", + }, + ], + }, }, errors: { notFound: { @@ -187,8 +187,8 @@ export const jaLayoutDictionary: LayoutDictionaryModel = { }; if (withJapanese) { - jaLayoutDictionary.ribbon.languagePicker.options.push({ + jaLayoutDictionary.footer.languagePicker.options.push({ label: "日本語", - code: "ja", + value: "ja", }); } diff --git a/src/features/localization/models/layout-dictionary.model.ts b/src/features/localization/models/layout-dictionary.model.ts index df259944..3b57231a 100644 --- a/src/features/localization/models/layout-dictionary.model.ts +++ b/src/features/localization/models/layout-dictionary.model.ts @@ -26,7 +26,6 @@ export interface LayoutDictionaryModel { }; }; themePicker: RibbonPickerModel; - languagePicker: RibbonPickerModel; }; header: { links: LinkMetadataModel[]; @@ -69,6 +68,7 @@ export interface LayoutDictionaryModel { id: string; }[]; }; + languagePicker: RibbonPickerModel; }; errors: { notFound: { From 37c41b6ec168cbe18477b7e37620e691443d8036 Mon Sep 17 00:00:00 2001 From: Christian Samaniego <199278128+christiansamaniego-okta@users.noreply.github.com> Date: Wed, 15 Oct 2025 11:52:06 -0500 Subject: [PATCH 3/5] style language selector --- .../components/footer/footer.component.tsx | 29 ++++++++++++++++--- .../components/footer/footer.module.scss | 4 --- src/libs/theme/styles/globals.scss | 12 +++++--- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/features/common/components/footer/footer.component.tsx b/src/features/common/components/footer/footer.component.tsx index f4e24ecf..06e64bf1 100644 --- a/src/features/common/components/footer/footer.component.tsx +++ b/src/features/common/components/footer/footer.component.tsx @@ -1,7 +1,12 @@ "use client"; import React, { MouseEvent, useState } from "react"; -import Select, { SingleValue, OptionsOrGroups, GroupBase, NonceProvider } from "react-select"; +import Select, { + SingleValue, + OptionsOrGroups, + GroupBase, + NonceProvider, +} from "react-select"; import { FooterIconsComponent } from "./footer-Icons.component"; import { MonoFont, SecondaryFont } from "@/libs/theme/fonts"; import Image from "next/image"; @@ -185,7 +190,12 @@ export const FooterComponent: React.FC = ({ aria-label={"Language picker"} className={styles.languageSelect__container} onChange={handleChange} - options={dictionary.languagePicker.options as OptionsOrGroups>} + options={ + dictionary.languagePicker.options as OptionsOrGroups< + UiLanguageModel, + GroupBase + > + } menuPortalTarget={document.body} classNamePrefix={"language-select"} isSearchable={false} @@ -194,6 +204,8 @@ export const FooterComponent: React.FC = ({ styles={{ control: (base) => ({ ...base, + color: "var(--color_fg_default)", + fontSize: "0.875rem", background: "transparent", border: "none", borderRadius: "0px", @@ -203,7 +215,6 @@ export const FooterComponent: React.FC = ({ minHeight: "2.5rem", boxSizing: "border-box", }), - input: (base) => ({ ...base, margin: "0px", @@ -217,14 +228,24 @@ export const FooterComponent: React.FC = ({ alignSelf: "center", }), dropdownIndicator: (base) => ({ + ...base, padding: "0px", height: "100%", alignSelf: "center", }), + valueContainer: (base) => ({ + ...base, + padding: "0px" + }), singleValue: (base) => ({ ...base, color: "unset", - }) + }), + menu: (base) => ({ + ...base, + top: "unset", + bottom: "1.75rem", + }), }} > )} diff --git a/src/features/common/components/footer/footer.module.scss b/src/features/common/components/footer/footer.module.scss index 16324bd8..83710432 100644 --- a/src/features/common/components/footer/footer.module.scss +++ b/src/features/common/components/footer/footer.module.scss @@ -267,7 +267,3 @@ color: var(--color_fg_default); } - -.languageSelect__container { - color: var(--color_fg_default); -} \ No newline at end of file diff --git a/src/libs/theme/styles/globals.scss b/src/libs/theme/styles/globals.scss index b3c577ce..5b23baa0 100644 --- a/src/libs/theme/styles/globals.scss +++ b/src/libs/theme/styles/globals.scss @@ -553,7 +553,8 @@ $picker-list-offset-lg: calc(($picker-list-width-lg - $picker-width-lg) / 2); height: 100% !important; } -.react-select__menu { +.react-select__menu, +.language-select__menu { background-color: var(--color_bg_layer) !important; border-radius: 0.75rem !important; box-shadow: rgba(0, 0, 0, 0.1) 0px 0px 0px 1px, rgba(0, 0, 0, 0.1) 0px 4px 11px !important; @@ -570,7 +571,8 @@ $picker-list-offset-lg: calc(($picker-list-width-lg - $picker-width-lg) / 2); .react-select__menu-portal { } -.react-select__menu-list { +.react-select__menu-list, +.language-select__menu-list { font-family: var(--font-primary), monospace !important; padding: 0 !important; @@ -596,7 +598,8 @@ $picker-list-offset-lg: calc(($picker-list-width-lg - $picker-width-lg) / 2); } } -.react-select__option { +.react-select__option, +.language-select__option { cursor: pointer !important; display: flex !important; font-size: 0.875rem !important; @@ -622,7 +625,8 @@ $picker-list-offset-lg: calc(($picker-list-width-lg - $picker-width-lg) / 2); } } -.react-select__option--is-selected { +.react-select__option--is-selected, +.language-select__option--is-selected { color: var(--color_fg_bold) !important; font-weight: 500; From a5903bf86021acc1fd38b46d0e0318d2ff401958 Mon Sep 17 00:00:00 2001 From: Christian Samaniego <199278128+christiansamaniego-okta@users.noreply.github.com> Date: Wed, 15 Oct 2025 12:24:49 -0500 Subject: [PATCH 4/5] add globe icon and minor style changes --- .../ribbon/assets/globe-icon.component.tsx | 6 +- .../components/footer/footer.component.tsx | 129 +++++++++--------- .../components/footer/footer.module.scss | 11 ++ 3 files changed, 83 insertions(+), 63 deletions(-) diff --git a/src/features/common/components/bars/ribbon/assets/globe-icon.component.tsx b/src/features/common/components/bars/ribbon/assets/globe-icon.component.tsx index 7552b6c2..71ee0194 100644 --- a/src/features/common/components/bars/ribbon/assets/globe-icon.component.tsx +++ b/src/features/common/components/bars/ribbon/assets/globe-icon.component.tsx @@ -3,10 +3,11 @@ import React from "react"; export const GlobeIconComponent: React.FC = () => { return (
+ +