-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement LocaleSwitcher Component #273
Draft
jcq
wants to merge
9
commits into
main
Choose a base branch
from
jc/237-select-a-language-control
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
ca6b2b4
initial implementation of a LocaleSwitcher component
jcq eab4c4d
added utils for mocking next/router and next/navigation
jcq 26f29bf
added comments to LocaleSwitcher
jcq bb1b060
Merge branch 'main' into jc/237-select-a-language-control
jcq 9f33c6e
code formatting
jcq 519294c
style workarounds for extra class on LanguageSelector
jcq a0a9a1a
added a11y test for LocaleSwitcher
jcq b004b62
Merge branch 'main' into jc/237-select-a-language-control
jcq 2182989
utilize existing `locales` export for next-intl/navigation
jcq File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
"use client"; | ||
|
||
import { usePathname, useRouter } from "src/i18n/navigation"; | ||
|
||
import { useLocale } from "next-intl"; | ||
import { CSSProperties, useTransition } from "react"; | ||
import { LanguageDefinition, LanguageSelector } from "@trussworks/react-uswds"; | ||
|
||
// Currently, the `react-uswds` component erroneously sets 'usa-language-container' class | ||
// on both the container and the button, which causes incorrect positioning relative to nav items | ||
const styleFixes: CSSProperties = { | ||
display: "block", | ||
top: "auto", | ||
marginLeft: "auto", | ||
marginTop: "auto", | ||
}; | ||
|
||
export default function LocaleSwitcher() { | ||
const locale = useLocale(); | ||
const router = useRouter(); | ||
const [, startTransition] = useTransition(); | ||
const pathname = usePathname(); | ||
|
||
const selectLocale = (newLocale: string) => { | ||
startTransition(() => { | ||
router.replace(pathname, { locale: newLocale }); | ||
}); | ||
}; | ||
|
||
// This should be modified to fit the project's language requirements | ||
// If you have more than two languages, it will render as a dropdown | ||
// The react-uswds component will just display the `label` for the current language; | ||
// USWDS guidance is to display "Language" in the current language as the label, which isn't currently possible | ||
// See https://designsystem.digital.gov/components/language-selector/ | ||
// We're using two languages by default here, but implementing such that it displays the language to which it will switch rather than the current language | ||
const langs: LanguageDefinition[] = [ | ||
{ | ||
attr: "en-US", | ||
label: "Español", | ||
label_local: "Spanish", | ||
on_click: () => selectLocale("es-US"), | ||
}, | ||
{ | ||
attr: "es-US", | ||
label: "English", | ||
on_click: () => selectLocale("en-US"), | ||
}, | ||
]; | ||
|
||
return ( | ||
<LanguageSelector displayLang={locale} langs={langs} style={styleFixes} /> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { createSharedPathnamesNavigation } from "next-intl/navigation"; | ||
|
||
import { locales } from "./config"; | ||
|
||
export const localePrefix = "always"; // Default | ||
|
||
export const { Link, redirect, usePathname, useRouter } = | ||
createSharedPathnamesNavigation({ locales, localePrefix }); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import userEvent from "@testing-library/user-event"; | ||
import { axe } from "jest-axe"; | ||
import { mockRouter } from "tests/next-router-utils"; | ||
import { render, screen } from "tests/react-utils"; | ||
|
||
import LocaleSwitcher from "src/components/LocaleSwitcher"; | ||
|
||
describe("LocaleSwitcher", () => { | ||
it("renders the language selector and updates routes when switching language", async () => { | ||
// Set the initial url | ||
await mockRouter.push("/initial-path"); | ||
|
||
render(<LocaleSwitcher />); | ||
|
||
// We start in English, so we see the toggle to switch to Spanish | ||
await userEvent.click(screen.getByRole("button", { name: "Español" })); | ||
|
||
// Ensure the router was updated | ||
// This is the best we can do for testing given constraints listed below | ||
expect(mockRouter).toMatchObject({ | ||
asPath: "/es-US/initial-path", | ||
pathname: "/es-US/initial-path", | ||
}); | ||
|
||
// This won't actually work because the NextIntlProvider relies on middleware that isn't available in tests | ||
// expect( | ||
// await screen.findByRole("button", { name: "English" }) | ||
// ).toBeInTheDocument(); | ||
}); | ||
Comment on lines
+25
to
+29
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Left this in to note an a11y issue with the current version of the |
||
|
||
// This fails when in 2-language mode because the react-uswds component sets aria-controls | ||
// w/o corresponding element in the DOM | ||
it("passes accessibility scan", async () => { | ||
const { container } = render(<LocaleSwitcher />); | ||
const results = await axe(container); | ||
|
||
expect(results).toHaveNoViolations(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
/* eslint-disable */ | ||
|
||
// Taken from https://github.com/vercel/next.js/discussions/42527#discussioncomment-7234041 | ||
// This mocks important pieces from both next/router and next/navigation to enable testing of components | ||
// that use next/router and next/navigation hooks. | ||
|
||
import mockRouter from "next-router-mock"; | ||
import { createDynamicRouteParser } from "next-router-mock/dynamic-routes"; | ||
|
||
jest.mock("next/router", () => jest.requireActual("next-router-mock")); | ||
|
||
mockRouter.useParser( | ||
createDynamicRouteParser([ | ||
// @see https://github.com/scottrippey/next-router-mock#dynamic-routes | ||
]) | ||
); | ||
|
||
jest.mock<typeof import("next/navigation")>("next/navigation", () => { | ||
const actual = jest.requireActual("next/navigation"); | ||
const nextRouterMock = jest.requireActual("next-router-mock"); | ||
const { useRouter } = nextRouterMock; | ||
const usePathname = jest.fn().mockImplementation(() => { | ||
const router = useRouter(); | ||
return router.asPath; | ||
}); | ||
|
||
const useSearchParams = jest.fn().mockImplementation(() => { | ||
const router = useRouter(); | ||
return new URLSearchParams(router.query); | ||
}); | ||
|
||
return { | ||
...actual, | ||
useRouter: jest.fn().mockImplementation(useRouter), | ||
usePathname, | ||
useSearchParams, | ||
}; | ||
}); | ||
|
||
export { mockRouter }; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we open a PR to fix that for them? They're usually pretty open to contributions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
trussworks/react-uswds#2893