fix(web): place aria-selected on the role=tab element (#32) + assert it
Per WAI-ARIA, aria-selected must be on the same element carrying role="tab". The previous impl placed it on an inner <span> via the render-prop, making it invisible to assistive technology. Move both role="tab" and aria-selected to the NavLink, compute currentKind once from useParams, drop the render-prop. Add a Vitest assertion that the selected tab has aria-selected="true" and an unselected tab has aria-selected="false". Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -18,8 +18,9 @@ export function AuthoritiesPage() {
|
||||
const lang = i18n.language.startsWith("sv") ? "sv" : "en";
|
||||
|
||||
const isValidKind = (KINDS as readonly string[]).includes(kind ?? "");
|
||||
const currentKind = isValidKind ? (kind as string) : "person";
|
||||
|
||||
const { data: authorities, isLoading, isError } = useAuthorities(isValidKind ? (kind as string) : "person");
|
||||
const { data: authorities, isLoading, isError } = useAuthorities(currentKind);
|
||||
const create = useCreateAuthority();
|
||||
|
||||
const [labels, setLabels] = useState<LabelInput[]>([]);
|
||||
@@ -50,11 +51,12 @@ export function AuthoritiesPage() {
|
||||
key={k}
|
||||
to={`/authorities/${k}`}
|
||||
role="tab"
|
||||
aria-selected={k === currentKind}
|
||||
className={({ isActive }) =>
|
||||
`rounded px-3 py-1 text-sm ${isActive ? "bg-neutral-800 text-white" : "border"}`
|
||||
}
|
||||
>
|
||||
{({ isActive }) => <span aria-selected={isActive}>{t(`authorities.${k}`)}</span>}
|
||||
{t(`authorities.${k}`)}
|
||||
</NavLink>
|
||||
))}
|
||||
</div>
|
||||
@@ -78,7 +80,7 @@ export function AuthoritiesPage() {
|
||||
|
||||
<form onSubmit={onCreate} className="space-y-2 border-t pt-3">
|
||||
<div className="text-sm font-medium">
|
||||
{t("authorities.new")} · {t(`authorities.${kind}`)}
|
||||
{t("authorities.new")} · {t(`authorities.${currentKind}`)}
|
||||
</div>
|
||||
|
||||
<LabelEditor value={labels} onChange={setLabels} />
|
||||
|
||||
@@ -36,6 +36,12 @@ test("kind tabs link to the other kinds", async () => {
|
||||
expect(await screen.findByRole("tab", { name: /place/i })).toHaveAttribute("href", "/authorities/place");
|
||||
});
|
||||
|
||||
test("aria-selected is on the tab element and reflects the active kind", async () => {
|
||||
renderApp(tree(), { route: "/authorities/person" });
|
||||
expect(await screen.findByRole("tab", { name: /^person$/i })).toHaveAttribute("aria-selected", "true");
|
||||
expect(screen.getByRole("tab", { name: /^place$/i })).toHaveAttribute("aria-selected", "false");
|
||||
});
|
||||
|
||||
test("create without EN label shows required alert and does not POST", async () => {
|
||||
let posted = false;
|
||||
server.use(
|
||||
|
||||
Reference in New Issue
Block a user