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
6 changes: 6 additions & 0 deletions .changeset/a11y-table-pagination-both-labels.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@astryxdesign/core': patch
---

[fix] useTablePagination: with `position='both'` the two pagination `<nav>` landmarks now get distinct accessible names — "{label} (top)" above the table and "{label} (bottom)" below it (axe landmark-unique). Consumer-supplied `label` values are interpolated into both names; single-position labels are unchanged.
@AKnassa
40 changes: 0 additions & 40 deletions .github/a11y-baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -678,11 +678,6 @@
"impact": "serious",
"helpUrl": "https://dequeuniversity.com/rules/axe/4.12/color-contrast?application=playwright"
},
{
"key": "Layout::Content Width — Responsive Panels::landmark-unique",
"impact": "moderate",
"helpUrl": "https://dequeuniversity.com/rules/axe/4.12/landmark-unique?application=playwright"
},
{
"key": "Layout::Content Width — Start Panel::color-contrast",
"impact": "serious",
Expand Down Expand Up @@ -753,11 +748,6 @@
"impact": "serious",
"helpUrl": "https://dequeuniversity.com/rules/axe/4.12/color-contrast?application=playwright"
},
{
"key": "Pagination::All Variants::landmark-unique",
"impact": "moderate",
"helpUrl": "https://dequeuniversity.com/rules/axe/4.12/landmark-unique?application=playwright"
},
{
"key": "PopArt::Debug Projection::color-contrast",
"impact": "serious",
Expand Down Expand Up @@ -943,16 +933,6 @@
"impact": "serious",
"helpUrl": "https://dequeuniversity.com/rules/axe/4.12/color-contrast?application=playwright"
},
{
"key": "TabList::Divider Gap (sm / md / lg)::landmark-unique",
"impact": "moderate",
"helpUrl": "https://dequeuniversity.com/rules/axe/4.12/landmark-unique?application=playwright"
},
{
"key": "TabList::Size Variants::landmark-unique",
"impact": "moderate",
"helpUrl": "https://dequeuniversity.com/rules/axe/4.12/landmark-unique?application=playwright"
},
{
"key": "Table::In Card Densities::color-contrast",
"impact": "serious",
Expand Down Expand Up @@ -998,16 +978,6 @@
"impact": "serious",
"helpUrl": "https://dequeuniversity.com/rules/axe/4.12/color-contrast?application=playwright"
},
{
"key": "TablePagination::Options Matrix::landmark-unique",
"impact": "moderate",
"helpUrl": "https://dequeuniversity.com/rules/axe/4.12/landmark-unique?application=playwright"
},
{
"key": "TablePagination::Position Both::landmark-unique",
"impact": "moderate",
"helpUrl": "https://dequeuniversity.com/rules/axe/4.12/landmark-unique?application=playwright"
},
{
"key": "TableRowStatus::Default::empty-table-header",
"impact": "minor",
Expand Down Expand Up @@ -1103,16 +1073,6 @@
"impact": "serious",
"helpUrl": "https://dequeuniversity.com/rules/axe/4.12/color-contrast?application=playwright"
},
{
"key": "Toolbar::Composition: Tab Navigation::landmark-unique",
"impact": "moderate",
"helpUrl": "https://dequeuniversity.com/rules/axe/4.12/landmark-unique?application=playwright"
},
{
"key": "ToolbarEdgeCompensation::Tabs in toolbar (all sizes)::landmark-unique",
"impact": "moderate",
"helpUrl": "https://dequeuniversity.com/rules/axe/4.12/landmark-unique?application=playwright"
},
{
"key": "useContainerReveal::Inverted Conceal::color-contrast",
"impact": "serious",
Expand Down
26 changes: 17 additions & 9 deletions apps/storybook/stories/Layout.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -743,9 +743,7 @@ export const ThemedLayout: Story = {
render: () => (
<HStack gap={6} xstyle={styles.storySection}>
<VStack gap={3}>
<p {...stylex.props(styles.sectionLabel)}>
Stone Theme
</p>
<p {...stylex.props(styles.sectionLabel)}>Stone Theme</p>
<Theme theme={stoneTheme}>
<Card width={400}>
<Layout
Expand Down Expand Up @@ -779,9 +777,7 @@ export const ThemedLayout: Story = {
</VStack>

<VStack gap={3}>
<p {...stylex.props(styles.sectionLabel)}>
Neutral Theme
</p>
<p {...stylex.props(styles.sectionLabel)}>Neutral Theme</p>
<Theme theme={neutralTheme}>
<Card width={400}>
<Layout
Expand Down Expand Up @@ -1229,7 +1225,11 @@ export const ContentWidthResponsive: Story = {
</LayoutHeader>
}
start={
<LayoutPanel width={160} hasDivider role="navigation">
<LayoutPanel
width={160}
hasDivider
role="navigation"
label="Wide navigation">
<NavItem active>Dashboard</NavItem>
<NavItem>Settings</NavItem>
</LayoutPanel>
Expand Down Expand Up @@ -1262,7 +1262,11 @@ export const ContentWidthResponsive: Story = {
</LayoutHeader>
}
start={
<LayoutPanel width={160} hasDivider role="navigation">
<LayoutPanel
width={160}
hasDivider
role="navigation"
label="Medium navigation">
<NavItem active>Dashboard</NavItem>
<NavItem>Settings</NavItem>
</LayoutPanel>
Expand Down Expand Up @@ -1295,7 +1299,11 @@ export const ContentWidthResponsive: Story = {
</LayoutHeader>
}
start={
<LayoutPanel width={160} hasDivider role="navigation">
<LayoutPanel
width={160}
hasDivider
role="navigation"
label="Narrow navigation">
<NavItem active>Dashboard</NavItem>
<NavItem>Settings</NavItem>
</LayoutPanel>
Expand Down
23 changes: 20 additions & 3 deletions apps/storybook/stories/Pagination.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ export const AllVariants: Story = {
totalItems={100}
pageSize={10}
variant="pages"
label="Pagination (pages)"
/>
</div>
<div>
Expand All @@ -165,19 +166,35 @@ export const AllVariants: Story = {
totalItems={100}
pageSize={10}
variant="count"
label="Pagination (count)"
/>
</div>
<div>
<p style={{marginBottom: 8, fontWeight: 500}}>compact</p>
<PaginationDemo page={3} totalPages={10} variant="compact" />
<PaginationDemo
page={3}
totalPages={10}
variant="compact"
label="Pagination (compact)"
/>
</div>
<div>
<p style={{marginBottom: 8, fontWeight: 500}}>dots</p>
<PaginationDemo page={3} totalPages={8} variant="dots" />
<PaginationDemo
page={3}
totalPages={8}
variant="dots"
label="Pagination (dots)"
/>
</div>
<div>
<p style={{marginBottom: 8, fontWeight: 500}}>none</p>
<PaginationDemo page={3} totalPages={10} variant="none" />
<PaginationDemo
page={3}
totalPages={10}
variant="none"
label="Pagination (none)"
/>
</div>
</div>
),
Expand Down
13 changes: 11 additions & 2 deletions apps/storybook/stories/TabList.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,11 @@ export const SizeVariants: Story = {
size=\"{size}\"
</div>
<div style={{border: '1px dashed #ccc', display: 'inline-flex'}}>
<TabList value={value} onChange={setValue} size={size}>
<TabList
value={value}
onChange={setValue}
size={size}
aria-label={`Tabs (${size})`}>
<Tab value="home" label="Home" />
<Tab value="projects" label="Projects" />
<Tab value="settings" label="Settings" />
Expand Down Expand Up @@ -242,7 +246,12 @@ export const DividerGap: Story = {
<span style={{font: '600 12px system-ui', color: '#4E606F'}}>
size=&quot;{size}&quot; · hasDivider · matched Button size
</span>
<TabList value={value} onChange={setValue} size={size} hasDivider>
<TabList
value={value}
onChange={setValue}
size={size}
hasDivider
aria-label={`Tabs (${size})`}>
<Tab value="overview" label="Overview" />
<Tab value="activity" label="Activity" />
<Tab value="settings" label="Settings" />
Expand Down
4 changes: 4 additions & 0 deletions apps/storybook/stories/TablePagination.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,12 @@ function PaginatedDemo({
variant = 'pages',
position = 'below',
align = 'start',
label,
}: {
variant?: Variant;
position?: Position;
align?: Align;
label?: string;
}) {
const [page, setPage] = useState(1);
const pageSize = 10;
Expand All @@ -67,6 +69,7 @@ function PaginatedDemo({
variant,
position,
align,
label,
});

return (
Expand Down Expand Up @@ -394,6 +397,7 @@ export const OptionsMatrix: Story = {
variant={variant}
position={position}
align={align}
label={`Table pagination (${variant}, ${position}, ${align})`}
/>
</div>
)),
Expand Down
5 changes: 4 additions & 1 deletion apps/storybook/stories/Toolbar.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,10 @@ export const WithTabNavigation: Story = {
size={size}
dividers={['bottom']}
startContent={
<TabList value={tab} onChange={setTab}>
<TabList
value={tab}
onChange={setTab}
aria-label={`Tab navigation (${size})`}>
<Tab value="overview" label="Overview" />
<Tab value="analytics" label="Analytics" />
<Tab value="settings" label="Settings" />
Expand Down
9 changes: 5 additions & 4 deletions apps/storybook/stories/ToolbarEdgeCompensation.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1053,9 +1053,7 @@ export const CardLayoutContentWidthToolbar: Story = {
<LayoutHeader hasDivider padding={0}>
<Toolbar
label="Card layout header"
startContent={
<Heading level={4}>Notifications</Heading>
}
startContent={<Heading level={4}>Notifications</Heading>}
/>
</LayoutHeader>
}
Expand Down Expand Up @@ -1144,7 +1142,10 @@ export const WithTabs: Story = {
size={size}
dividers={['bottom']}
startContent={
<TabList value={tab} onChange={setTab}>
<TabList
value={tab}
onChange={setTab}
aria-label={`Tab toolbar tabs (${size})`}>
<Tab value="overview" label="Overview" />
<Tab value="analytics" label="Analytics" />
<Tab value="settings" label="Settings" />
Expand Down
10 changes: 9 additions & 1 deletion packages/core/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,15 @@
},
"@astryx.table.pagination.label": {
"defaultMessage": "Table pagination",
"description": "Screen-reader-only accessible name for the pagination `<nav>` appended below a data table. Kept separate from `@astryx.pagination.label` so translations may diverge."
"description": "Screen-reader-only accessible name for a table's pagination `<nav>` (rendered below and/or above the table per the plugin's position config). Kept separate from `@astryx.pagination.label` so translations may diverge. When position='both', this string is interpolated as the {label} value into `@astryx.table.pagination.labelAbove`/`labelBelow` — keep it compatible with those suffix templates."
},
"@astryx.table.pagination.labelAbove": {
"defaultMessage": "{label} (top)",
"description": "Accessible name for the pagination `<nav>` rendered above the table when position='both' renders two navs. `label` is the resolved base label (default \"Table pagination\" or consumer-supplied); the suffix keeps the two same-type landmarks distinguishable."
},
"@astryx.table.pagination.labelBelow": {
"defaultMessage": "{label} (bottom)",
"description": "Accessible name for the pagination `<nav>` rendered below the table when position='both' renders two navs. `label` is the resolved base label (default \"Table pagination\" or consumer-supplied); the suffix keeps the two same-type landmarks distinguishable."
},
"@astryx.timeInput.placeholder": {
"defaultMessage": "Select a time",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,18 +229,48 @@ describe('useTablePagination', () => {
).toBeTruthy();
});

it('transformTableContext renders Pagination above and below', () => {
it('transformTableContext renders distinctly named Pagination above and below', () => {
render(
<PaginatedTable
data={generateItems(30)}
pageSize={10}
position="both"
/>,
);
const navs = screen.getAllByRole('navigation', {
name: 'Table pagination',
const table = screen.getByRole('table');
// Each nav landmark gets a unique accessible name (axe landmark-unique)
const topNav = screen.getByRole('navigation', {
name: 'Table pagination (top)',
});
const bottomNav = screen.getByRole('navigation', {
name: 'Table pagination (bottom)',
});
expect(navs).toHaveLength(2);
// Top nav comes before the table, bottom nav after it in DOM order
expect(
table.compareDocumentPosition(topNav) &
Node.DOCUMENT_POSITION_PRECEDING,
).toBeTruthy();
expect(
table.compareDocumentPosition(bottomNav) &
Node.DOCUMENT_POSITION_FOLLOWING,
).toBeTruthy();
});

it('position="both" interpolates a consumer label into distinct nav names', () => {
render(
<PaginatedTable
data={generateItems(30)}
pageSize={10}
position="both"
label="Users table"
/>,
);
expect(
screen.getByRole('navigation', {name: 'Users table (top)'}),
).toBeInTheDocument();
expect(
screen.getByRole('navigation', {name: 'Users table (bottom)'}),
).toBeInTheDocument();
});

it('transformTableContext does not render Pagination when position is none', () => {
Expand Down
Loading
Loading