Add aria-current="page" to clickable last breadcrumb (a11y fix per WAI-ARIA)
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
// @vitest-environment jsdom
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { Breadcrumbs } from "./Breadcrumbs.js";
|
||||
|
||||
vi.mock("@/i18n/provider.js", () => ({
|
||||
useTranslation: () => ({ t: (k: string) => k }),
|
||||
}));
|
||||
|
||||
// Suppress SCSS import errors in jsdom
|
||||
vi.mock("./Breadcrumbs.scss", () => ({}));
|
||||
|
||||
describe("Breadcrumbs", () => {
|
||||
it("renders the nav with data-testid breadcrumbs", () => {
|
||||
render(<Breadcrumbs items={[{ label: "Flights" }]} />);
|
||||
expect(screen.getByTestId("breadcrumbs")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("last crumb rendered as <span> has aria-current='page'", () => {
|
||||
render(<Breadcrumbs items={[{ label: "Flights" }]} />);
|
||||
const span = screen.getByText("Flights");
|
||||
expect(span.tagName.toLowerCase()).toBe("span");
|
||||
expect(span.getAttribute("aria-current")).toBe("page");
|
||||
});
|
||||
|
||||
it("last crumb rendered as <a> (with url) has aria-current='page'", () => {
|
||||
render(
|
||||
<Breadcrumbs
|
||||
items={[
|
||||
{ label: "Online Board", url: "/ru/onlineboard" },
|
||||
{ label: "Results", url: "/ru/onlineboard/route/MOW-LED" },
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
const link = screen.getByText("Results");
|
||||
expect(link.tagName.toLowerCase()).toBe("a");
|
||||
expect(link.getAttribute("aria-current")).toBe("page");
|
||||
});
|
||||
|
||||
it("non-last crumb rendered as <a> does NOT have aria-current", () => {
|
||||
render(
|
||||
<Breadcrumbs
|
||||
items={[
|
||||
{ label: "Online Board", url: "/ru/onlineboard" },
|
||||
{ label: "Results" },
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
const link = screen.getByText("Online Board");
|
||||
expect(link.tagName.toLowerCase()).toBe("a");
|
||||
expect(link.getAttribute("aria-current")).toBeNull();
|
||||
});
|
||||
|
||||
it("non-last crumb rendered as <span> does NOT have aria-current", () => {
|
||||
// The home crumb (index 0) has url so it won't be a span, but we can
|
||||
// check an intermediate span by using items without url at non-last position
|
||||
render(
|
||||
<Breadcrumbs
|
||||
items={[
|
||||
{ label: "Intermediate" },
|
||||
{ label: "Last" },
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
const intermediate = screen.getByText("Intermediate");
|
||||
expect(intermediate.getAttribute("aria-current")).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -46,7 +46,13 @@ export const Breadcrumbs: FC<BreadcrumbsProps> = ({ items = [] }) => {
|
||||
className={`breadcrumbs__item${isLast ? " breadcrumbs__item--active" : ""}`}
|
||||
>
|
||||
{showAsLink ? (
|
||||
<a href={item.url} className="breadcrumbs__link">{item.label}</a>
|
||||
<a
|
||||
href={item.url}
|
||||
className="breadcrumbs__link"
|
||||
{...(isLast ? { "aria-current": "page" } : {})}
|
||||
>
|
||||
{item.label}
|
||||
</a>
|
||||
) : (
|
||||
<span
|
||||
className="breadcrumbs__text"
|
||||
|
||||
Reference in New Issue
Block a user