fix(ui): harden theme toggle accessibility
This commit is contained in:
parent
cdb269e505
commit
b923ac8947
8 changed files with 75 additions and 9 deletions
|
|
@ -14,7 +14,6 @@ export function ThemeToggle({ theme, onThemeChange }: ThemeToggleProps) {
|
|||
return (
|
||||
<button
|
||||
aria-label={label}
|
||||
aria-pressed={theme === "dark"}
|
||||
className="theme-toggle"
|
||||
data-ui-theme-toggle="true"
|
||||
onClick={() => onThemeChange(nextTheme)}
|
||||
|
|
|
|||
|
|
@ -20,4 +20,9 @@ export const Light: Story = {
|
|||
args: {
|
||||
theme: "light",
|
||||
},
|
||||
parameters: {
|
||||
globals: {
|
||||
theme: "light",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { describe, expect, test } from "vitest";
|
|||
import {
|
||||
getNextUiTheme,
|
||||
isUiTheme,
|
||||
persistUiTheme,
|
||||
resolveInitialUiTheme,
|
||||
UI_THEME_STORAGE_KEY,
|
||||
} from "./theme";
|
||||
|
|
@ -25,6 +26,26 @@ describe("UI theme preference", () => {
|
|||
expect(resolveInitialUiTheme(storage)).toBe("dark");
|
||||
});
|
||||
|
||||
test("falls back when stored preferences cannot be read", () => {
|
||||
const storage = {
|
||||
getItem() {
|
||||
throw new Error("storage blocked");
|
||||
},
|
||||
};
|
||||
|
||||
expect(resolveInitialUiTheme(storage)).toBe("dark");
|
||||
});
|
||||
|
||||
test("ignores persistence failures", () => {
|
||||
const storage = {
|
||||
setItem() {
|
||||
throw new Error("quota exceeded");
|
||||
},
|
||||
};
|
||||
|
||||
expect(() => persistUiTheme("light", storage)).not.toThrow();
|
||||
});
|
||||
|
||||
test("detects and toggles supported themes", () => {
|
||||
expect(isUiTheme("light")).toBe(true);
|
||||
expect(isUiTheme("dark")).toBe(true);
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ export function resolveInitialUiTheme(
|
|||
storage?: ReadableThemeStorage | null,
|
||||
fallback: UiTheme = "dark",
|
||||
): UiTheme {
|
||||
const stored = readStoredTheme(storage);
|
||||
const stored = safeReadStoredTheme(storage);
|
||||
|
||||
return isUiTheme(stored) ? stored : fallback;
|
||||
}
|
||||
|
|
@ -33,12 +33,26 @@ export function persistUiTheme(
|
|||
): void {
|
||||
if (!storage) return;
|
||||
|
||||
if ("setItem" in storage) {
|
||||
storage.setItem(UI_THEME_STORAGE_KEY, theme);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if ("setItem" in storage) {
|
||||
storage.setItem(UI_THEME_STORAGE_KEY, theme);
|
||||
return;
|
||||
}
|
||||
|
||||
storage.set(UI_THEME_STORAGE_KEY, theme);
|
||||
storage.set(UI_THEME_STORAGE_KEY, theme);
|
||||
} catch {
|
||||
// Browser storage may be blocked or quota-constrained.
|
||||
}
|
||||
}
|
||||
|
||||
function safeReadStoredTheme(
|
||||
storage?: ReadableThemeStorage | null,
|
||||
): string | undefined {
|
||||
try {
|
||||
return readStoredTheme(storage);
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function readStoredTheme(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue