feat: enhance typography and accessibility in design system
- Added new UI heading utility with specific styles in typography.css. - Updated typography tests to include new UI heading role and ensure proper size and styling. - Documented design system audit findings, focusing on accessibility improvements and component consistency. - Introduced CalendarExamples component to demonstrate calendar functionality with habit tracking. - Created a shared Field component for consistent labeling and hinting of form controls. - Implemented comprehensive accessibility tests for calendar and editor components, ensuring proper focus management and keyboard navigation. - Added structure tests for Field and Card components to verify correct ID generation and heading levels.
This commit is contained in:
52
src/components/design-system/structure.test.tsx
Normal file
52
src/components/design-system/structure.test.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
import { expect, test } from "bun:test";
|
||||
import { Window } from "happy-dom";
|
||||
import { renderToStaticMarkup } from "react-dom/server";
|
||||
import type { ReactNode } from "react";
|
||||
import { Card } from "./Card";
|
||||
import { ContainerShowcase } from "./ContainerShowcase";
|
||||
import { Field } from "./Field";
|
||||
|
||||
function render(content: ReactNode) {
|
||||
const window = new Window();
|
||||
window.document.body.innerHTML = renderToStaticMarkup(content);
|
||||
return window.document.body;
|
||||
}
|
||||
|
||||
test("Field connects its label and hint to the rendered control", () => {
|
||||
const body = render(
|
||||
<Field id="habit" label="Habit name" hint="Keep it short.">
|
||||
{(id, describedBy) => <input id={id} aria-describedby={describedBy} />}
|
||||
</Field>,
|
||||
);
|
||||
expect(body.querySelector("label")?.htmlFor).toBe("habit");
|
||||
expect(body.querySelector("input")?.getAttribute("aria-describedby")).toBe("habit-hint");
|
||||
expect(body.querySelector("#habit-hint")?.textContent).toBe("Keep it short.");
|
||||
});
|
||||
|
||||
test("Field generates unique control IDs and omits absent hint references", () => {
|
||||
const body = render(<>
|
||||
<Field label="First">{(id, describedBy) => <input id={id} aria-describedby={describedBy} />}</Field>
|
||||
<Field label="Second">{(id, describedBy) => <input id={id} aria-describedby={describedBy} />}</Field>
|
||||
</>);
|
||||
const controls = [...body.querySelectorAll("input")];
|
||||
expect(new Set(controls.map((control) => control.id)).size).toBe(2);
|
||||
expect(controls.every((control) => !control.hasAttribute("aria-describedby"))).toBe(true);
|
||||
expect(body.querySelectorAll("small")).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("Card preserves its title styling and accessible name at each heading level", () => {
|
||||
for (const headingLevel of [2, 3, 4, 5, 6] as const) {
|
||||
const body = render(<Card heading="A clear boundary" headingLevel={headingLevel}>Content</Card>);
|
||||
const heading = body.querySelector(`h${headingLevel}.ds-card-title`);
|
||||
expect(heading?.textContent).toBe("A clear boundary");
|
||||
expect(body.querySelector("article")?.getAttribute("aria-labelledby")).toBe(heading?.id);
|
||||
}
|
||||
expect(render(<Card heading="Default" />).querySelector("h3")?.textContent).toBe("Default");
|
||||
});
|
||||
|
||||
test("Layout uses subsection headings above the card titles", () => {
|
||||
const body = render(<ContainerShowcase />);
|
||||
expect(body.querySelectorAll("h2")).toHaveLength(1);
|
||||
expect(body.querySelectorAll("h3")).toHaveLength(2);
|
||||
expect(body.querySelectorAll("h4.ds-card-title")).toHaveLength(6);
|
||||
});
|
||||
Reference in New Issue
Block a user