Skip to content

Commit dfe6751

Browse files
committed
feat(table): implement table grouping with accessibility support
1 parent f9ad276 commit dfe6751

2 files changed

Lines changed: 200 additions & 3 deletions

File tree

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
import Table from "../../src/Table.js";
2+
import TableHeaderRow from "../../src/TableHeaderRow.js";
3+
import TableHeaderCell from "../../src/TableHeaderCell.js";
4+
import TableRow from "../../src/TableRow.js";
5+
import TableCell from "../../src/TableCell.js";
6+
import TableGroupRow from "../../src/TableGroupRow.js";
7+
import Text from "../../src/Text.js";
8+
9+
describe("Table - Group Rows", () => {
10+
function mountGroupedTable() {
11+
cy.mount(
12+
<Table id="table" accessible-name="Grouped Table">
13+
<TableHeaderRow slot="headerRow">
14+
<TableHeaderCell id="colA" width="200px">City</TableHeaderCell>
15+
<TableHeaderCell id="colB" width="200px">Country</TableHeaderCell>
16+
<TableHeaderCell id="colC" width="150px">Population</TableHeaderCell>
17+
</TableHeaderRow>
18+
<TableGroupRow id="group1">
19+
<Text>Country: Germany</Text>
20+
</TableGroupRow>
21+
<TableRow id="row1" rowKey="row-1">
22+
<TableCell><Text>Berlin</Text></TableCell>
23+
<TableCell><Text>Germany</Text></TableCell>
24+
<TableCell><Text>3,748,148</Text></TableCell>
25+
</TableRow>
26+
<TableRow id="row2" rowKey="row-2">
27+
<TableCell><Text>Munich</Text></TableCell>
28+
<TableCell><Text>Germany</Text></TableCell>
29+
<TableCell><Text>1,471,508</Text></TableCell>
30+
</TableRow>
31+
<TableGroupRow id="group2">
32+
<Text>Country: France</Text>
33+
</TableGroupRow>
34+
<TableRow id="row3" rowKey="row-3">
35+
<TableCell><Text>Paris</Text></TableCell>
36+
<TableCell><Text>France</Text></TableCell>
37+
<TableCell><Text>2,161,000</Text></TableCell>
38+
</TableRow>
39+
</Table>
40+
);
41+
}
42+
43+
it("should render group rows and data rows", () => {
44+
mountGroupedTable();
45+
46+
cy.get("[ui5-table-group-row]").should("have.length", 2);
47+
cy.get("[ui5-table-row]").should("have.length", 3);
48+
cy.get("#group1").should("contain.text", "Country: Germany");
49+
cy.get("#group2").should("contain.text", "Country: France");
50+
});
51+
52+
it("should use treegrid role when group rows are present", () => {
53+
mountGroupedTable();
54+
55+
cy.get("#table")
56+
.shadow()
57+
.find("#table")
58+
.should("have.attr", "role", "treegrid");
59+
});
60+
61+
it("should use grid role when no group rows are present", () => {
62+
cy.mount(
63+
<Table id="table">
64+
<TableHeaderRow slot="headerRow">
65+
<TableHeaderCell>City</TableHeaderCell>
66+
</TableHeaderRow>
67+
<TableRow rowKey="row-1">
68+
<TableCell><Text>Berlin</Text></TableCell>
69+
</TableRow>
70+
</Table>
71+
);
72+
73+
cy.get("#table")
74+
.shadow()
75+
.find("#table")
76+
.should("have.attr", "role", "grid");
77+
});
78+
79+
it("should set aria-level=1 on group rows and aria-level=2 on data rows", () => {
80+
mountGroupedTable();
81+
82+
cy.get("#group1").should("have.attr", "aria-level", "1");
83+
cy.get("#group2").should("have.attr", "aria-level", "1");
84+
cy.get("#row1").should("have.attr", "aria-level", "2");
85+
cy.get("#row2").should("have.attr", "aria-level", "2");
86+
cy.get("#row3").should("have.attr", "aria-level", "2");
87+
});
88+
89+
it("should set aria-expanded=true on group rows", () => {
90+
mountGroupedTable();
91+
92+
cy.get("#group1").should("have.attr", "aria-expanded", "true");
93+
cy.get("#group2").should("have.attr", "aria-expanded", "true");
94+
});
95+
96+
it("should have group cell spanning all columns", () => {
97+
mountGroupedTable();
98+
99+
cy.get("#group1")
100+
.shadow()
101+
.find("#group-cell")
102+
.should("have.attr", "role", "gridcell")
103+
.and("have.attr", "aria-colindex", "1")
104+
.and("have.attr", "aria-colspan", "3");
105+
});
106+
107+
it("should not have aria-level on data rows when no group rows exist", () => {
108+
cy.mount(
109+
<Table id="table">
110+
<TableHeaderRow slot="headerRow">
111+
<TableHeaderCell>City</TableHeaderCell>
112+
</TableHeaderRow>
113+
<TableRow id="row1" rowKey="row-1">
114+
<TableCell><Text>Berlin</Text></TableCell>
115+
</TableRow>
116+
</Table>
117+
);
118+
119+
cy.get("#row1").should("not.have.attr", "aria-level");
120+
});
121+
122+
it("should not throw when group rows are added dynamically", () => {
123+
cy.mount(
124+
<Table id="table">
125+
<TableHeaderRow slot="headerRow">
126+
<TableHeaderCell width="200px">City</TableHeaderCell>
127+
<TableHeaderCell width="200px">Country</TableHeaderCell>
128+
</TableHeaderRow>
129+
<TableRow id="row1" rowKey="row-1">
130+
<TableCell><Text>Berlin</Text></TableCell>
131+
<TableCell><Text>Germany</Text></TableCell>
132+
</TableRow>
133+
</Table>
134+
);
135+
136+
cy.get("#table")
137+
.shadow()
138+
.find("#table")
139+
.should("have.attr", "role", "grid");
140+
141+
// Dynamically add a group row
142+
cy.get("#table").then($table => {
143+
const table = $table[0] as Table;
144+
const groupRow = document.createElement("ui5-table-group-row") as TableGroupRow;
145+
groupRow.id = "dynamicGroup";
146+
groupRow.textContent = "Group: Germany";
147+
table.insertBefore(groupRow, table.querySelector("[ui5-table-row]"));
148+
});
149+
150+
// Should switch to treegrid and not throw errors
151+
cy.get("#table")
152+
.shadow()
153+
.find("#table")
154+
.should("have.attr", "role", "treegrid");
155+
156+
cy.get("#dynamicGroup").should("have.attr", "aria-level", "1");
157+
});
158+
159+
it("should not throw with popin mode and group rows", () => {
160+
cy.mount(
161+
<Table id="table" overflowMode="Popin">
162+
<TableHeaderRow slot="headerRow">
163+
<TableHeaderCell id="colA" minWidth="300px">City</TableHeaderCell>
164+
<TableHeaderCell id="colB" minWidth="200px">Country</TableHeaderCell>
165+
<TableHeaderCell id="colC" minWidth="200px">Population</TableHeaderCell>
166+
</TableHeaderRow>
167+
<TableGroupRow id="group1">
168+
<Text>Country: Germany</Text>
169+
</TableGroupRow>
170+
<TableRow id="row1" rowKey="row-1">
171+
<TableCell><Text>Berlin</Text></TableCell>
172+
<TableCell><Text>Germany</Text></TableCell>
173+
<TableCell><Text>3,748,148</Text></TableCell>
174+
</TableRow>
175+
<TableRow id="row2" rowKey="row-2">
176+
<TableCell><Text>Munich</Text></TableCell>
177+
<TableCell><Text>Germany</Text></TableCell>
178+
<TableCell><Text>1,471,508</Text></TableCell>
179+
</TableRow>
180+
</Table>
181+
);
182+
183+
// Shrink table to trigger popin - should not throw
184+
cy.get("#table").invoke("css", "width", "300px");
185+
186+
// Table and rows should still be intact
187+
cy.get("#table").should("exist");
188+
cy.get("#group1").should("exist");
189+
cy.get("#row1").should("exist");
190+
cy.get("#row2").should("exist");
191+
192+
// Expand again
193+
cy.get("#table").invoke("css", "width", "800px");
194+
195+
cy.get("#group1").should("contain.text", "Country: Germany");
196+
});
197+
});

packages/main/src/Table.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -587,10 +587,10 @@ class Table extends UI5Element {
587587
headerCell._popin = inPopin && this.overflowMode === TableOverflowMode.Popin;
588588
headerCell._popinWidth = popinWidth;
589589
this.rows.forEach(row => {
590-
const cell = row.cells[headerIndex];
590+
const cell = row.cells?.[headerIndex];
591591
if (cell) {
592-
row.cells[headerIndex]._popinHidden = headerCell.popinHidden;
593-
row.cells[headerIndex]._popin = headerCell._popin;
592+
cell._popinHidden = headerCell.popinHidden;
593+
cell._popin = headerCell._popin;
594594
}
595595
});
596596
}

0 commit comments

Comments
 (0)