Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const CSR_ONLY_COMPONENTS = new Set([
"nested-select",
"nested-multi-select",
"input-group",
"language-switcher",
]);

export default async function Page({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"use client";

import {
LanguageSwitcher,
type LanguageSwitcherCode,
} from "@lifesg/react-design-system/language-switcher";
import { useState } from "react";

export default function Story() {
const [language, setLanguage] = useState<LanguageSwitcherCode | undefined>(
undefined
);

return (
<div className="story-row-container">
<LanguageSwitcher
data-testid="language-switcher"
variant="dropdown"
selectedLanguage={language}
onSelectLanguage={setLanguage}
/>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"use client";

import {
LanguageSwitcher,
type LanguageSwitcherCode,
} from "@lifesg/react-design-system/language-switcher";
import { useState } from "react";

export default function Story() {
const [language, setLanguage] = useState<LanguageSwitcherCode>("zh");

return (
<div className="story-row-container">
<LanguageSwitcher
data-testid="language-switcher"
variant="dropdown"
selectedLanguage={language}
onSelectLanguage={setLanguage}
/>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"use client";

import {
LanguageSwitcher,
type LanguageSwitcherCode,
} from "@lifesg/react-design-system/language-switcher";
import { useState } from "react";

export default function Story() {
const [language, setLanguage] = useState<LanguageSwitcherCode | undefined>(
undefined
);

return (
<div className="story-row-container">
<LanguageSwitcher
data-testid="language-switcher"
variant="link-container"
selectedLanguage={language}
Comment thread
qroll marked this conversation as resolved.
onSelectLanguage={setLanguage}
/>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"use client";

import {
LanguageSwitcher,
type LanguageSwitcherCode,
} from "@lifesg/react-design-system/language-switcher";
import { useState } from "react";

export default function Story() {
const [language, setLanguage] = useState<LanguageSwitcherCode>("ta");

return (
<div className="story-row-container">
<LanguageSwitcher
data-testid="language-switcher"
variant="link-container"
selectedLanguage={language}
onSelectLanguage={setLanguage}
/>
</div>
);
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
180 changes: 180 additions & 0 deletions e2e/tests/components/language-switcher/language-switcher.e2e.spec.ts
Comment thread
qroll marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import { test as base, expect, Locator, Page } from "@playwright/test";
import { AbstractStoryPage, compareScreenshot } from "../../utils";

class StoryPage extends AbstractStoryPage {
protected readonly component = "language-switcher";

public readonly locators: {
internal: {
trigger: Locator;
panel: Locator;
};
languageSwitcher: Locator;
};

constructor(page: Page) {
super(page);

this.locators = {
internal: {
trigger: page.getByTestId("language-switcher--trigger"),
panel: page.getByTestId("language-switcher--panel"),
},
languageSwitcher: page.getByTestId("language-switcher"),
};
}

public getDropdownOption(name: string) {
return this.page.getByRole("option", { name });
}

public getLinkButton(name: string) {
return this.page.getByRole("button", { name, exact: true });
}
}

const test = base.extend<{ story: StoryPage }>({
story: async ({ page }, use) => {
const story = new StoryPage(page);
await use(story);
},
});

test.describe("LanguageSwitcher", () => {
test.describe("Dropdown", () => {
test.describe("", () => {
test.beforeEach(async ({ story }) => {
await story.init("dropdown-default");
});

test("Default", async ({ story }) => {
await compareScreenshot(story, "mount", {
locator: story.locators.languageSwitcher,
});

await expect(story.locators.languageSwitcher)
.toMatchAriaSnapshot(`
- combobox "Choose language / 选择语言 / Pilih bahasa / மொழியை தேர்ந்தெடுக்கவும், English"
`);

await story.locators.internal.trigger.click();
await compareScreenshot(story, "open", {
fullscreen: true,
});

await expect(story.locators.languageSwitcher)
.toMatchAriaSnapshot(`
- combobox "Choose language / 选择语言 / Pilih bahasa / மொழியை தேர்ந்தெடுக்கவும், English" [expanded]
- listbox "Choose language / 选择语言 / Pilih bahasa / மொழியை தேர்ந்தெடுக்கவும்":
- option "English" [selected]
- option "中文"
- option "Melayu"
- option "தமிழ்"
`);
});

test("Keyboard Interaction", async ({ story }) => {
await story.locators.internal.trigger.focus();
await story.page.keyboard.press("Enter");
await expect(story.locators.internal.panel).toBeVisible();

await story.page.keyboard.press("ArrowDown");
await expect(story.getDropdownOption("中文")).toBeFocused();

await story.page.keyboard.press("End");
await expect(story.getDropdownOption("தமிழ்")).toBeFocused();

await story.page.keyboard.press("Home");
await expect(story.getDropdownOption("English")).toBeFocused();

await story.page.keyboard.press("ArrowDown");
await story.page.keyboard.press("Enter");
await expect(story.locators.internal.panel).not.toBeVisible();
await expect(story.locators.internal.trigger).toContainText(
"中文"
);

await story.page.keyboard.press("Enter");
await expect(story.locators.internal.panel).toBeVisible();
await story.page.keyboard.press("Escape");
await expect(story.locators.internal.panel).not.toBeVisible();
});
});

test.describe("", () => {
test.beforeEach(async ({ story }) => {
await story.init("dropdown-default", { mode: "dark" });
});

test("Default Dark Mode", async ({ story }) => {
await compareScreenshot(story, "mount", {
locator: story.locators.languageSwitcher,
});

await story.locators.internal.trigger.click();
await compareScreenshot(story, "open", {
fullscreen: true,
});
});
});

test.describe("", () => {
test.beforeEach(async ({ story }) => {
await story.init("dropdown-preselected");
});

test("Preselected", async ({ story }) => {
await expect(story.locators.internal.trigger).toContainText(
"中文"
);

await compareScreenshot(story, "mount", {
locator: story.locators.languageSwitcher,
});
});
});
});

test.describe("Link", () => {
test.describe("", () => {
test.beforeEach(async ({ story }) => {
await story.init("link-default");
});

test("Default", async ({ story }) => {
await expect(story.locators.languageSwitcher)
.toMatchAriaSnapshot(`
- group "Choose language / 选择语言 / Pilih bahasa / மொழியை தேர்ந்தெடுக்கவும்":
- listitem:
- button "English" [pressed]
- listitem:
- button "中文"
- listitem:
- button "Melayu"
- listitem:
- button "தமிழ்"
`);

await compareScreenshot(story, "mount", {
locator: story.locators.languageSwitcher,
});
});
});

test.describe("", () => {
test.beforeEach(async ({ story }) => {
await story.init("link-preselected");
});

test("Preselected", async ({ story }) => {
const tamil = story.getLinkButton("தமிழ்");

await expect(tamil).toHaveAttribute("aria-pressed", "true");

await compareScreenshot(story, "mount", {
locator: story.locators.languageSwitcher,
});
});
});
});
});
47 changes: 47 additions & 0 deletions src/language-switcher/dropdown-panel.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { css } from "@linaria/core";

import { Border, Colour, Font, Radius, Spacing } from "../theme";

export const tokens = {
width: "--fds-internal-languageSwitcher-dropdown-width",
} as const;

export const dropdownPanel = css`
${tokens.width}: initial;

width: var(${tokens.width});
border-radius: ${Radius["sm"]};
border: ${Border["width-010"]} ${Border["solid"]} ${Colour["border"]};
background: ${Colour["bg"]};
overflow: hidden;
`;

export const dropdownList = css`
list-style: none;
margin: 0;
padding: ${Spacing["spacing-8"]};
`;

export {
baseIndicatorStyle,
selectedIndicator,
} from "../shared/dropdown-list/dropdown-list.styles";

export const dropdownItem = css`
align-items: center;
${Font["body-md-regular"]}
color: ${Colour["text"]};

&:hover {
background: ${Colour["bg-hover-subtle"]};
}
`;

export const dropdownItemSelected = css`
background: transparent;
color: ${Colour["text-selected"]};

&:hover {
background: ${Colour["bg-hover"]};
}
`;
Loading
Loading