Skip to content

Commit 358e797

Browse files
vaadin-botugur-vaadinvursen
authored
fix: reflect row aria-expanded state for toggle cells (#10156) (#10185)
Co-authored-by: Ugur Saglam <106508695+ugur-vaadin@users.noreply.github.com> Co-authored-by: Sergey Vinogradov <mr.vursen@gmail.com>
1 parent 6affc2a commit 358e797

File tree

3 files changed

+118
-1
lines changed

3 files changed

+118
-1
lines changed

packages/grid/src/vaadin-grid-a11y-mixin.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Copyright (c) 2016 - 2025 Vaadin Ltd.
44
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
55
*/
6-
import { iterateChildren, iterateRowCells } from './vaadin-grid-helpers.js';
6+
import { findTreeToggleCell, iterateChildren, iterateRowCells } from './vaadin-grid-helpers.js';
77

88
/**
99
* @polymerMixin
@@ -101,12 +101,22 @@ export const A11yMixin = (superClass) =>
101101
* @protected
102102
*/
103103
_a11yUpdateRowExpanded(row) {
104+
const toggleCell = findTreeToggleCell(row);
104105
if (this.__isRowExpandable(row)) {
105106
row.setAttribute('aria-expanded', 'false');
107+
if (toggleCell) {
108+
toggleCell.setAttribute('aria-expanded', 'false');
109+
}
106110
} else if (this.__isRowCollapsible(row)) {
107111
row.setAttribute('aria-expanded', 'true');
112+
if (toggleCell) {
113+
toggleCell.setAttribute('aria-expanded', 'true');
114+
}
108115
} else {
109116
row.removeAttribute('aria-expanded');
117+
if (toggleCell) {
118+
toggleCell.removeAttribute('aria-expanded');
119+
}
110120
}
111121
}
112122

packages/grid/src/vaadin-grid-helpers.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,15 @@ export function updateCellState(cell, attribute, value, part, oldPart) {
173173
updatePart(cell, value, part || `${attribute}-cell`);
174174
}
175175

176+
/**
177+
* Finds the cell containing the tree toggle element
178+
* @param {!HTMLElement} row
179+
* @return {HTMLElement | null}
180+
*/
181+
export function findTreeToggleCell(row) {
182+
return getBodyRowCells(row).find((cell) => cell._content.querySelector('vaadin-grid-tree-toggle'));
183+
}
184+
176185
/**
177186
* A helper for observing flattened child column list of an element.
178187
*/

packages/grid/test/accessibility.test.js

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { expect } from '@vaadin/chai-plugins';
2+
import { sendKeys } from '@vaadin/test-runner-commands';
23
import { fixtureSync, nextFrame } from '@vaadin/testing-helpers';
34
import '../all-imports.js';
45
import { flushGrid } from './helpers.js';
@@ -218,6 +219,103 @@ describe('accessibility', () => {
218219
grid.collapseItem({ name: '0' });
219220
expect(grid.$.items.children[1].getAttribute('aria-level')).to.be.null;
220221
});
222+
223+
describe('toggle cell', () => {
224+
function setupTreeGrid(additionalColumns = []) {
225+
grid = fixtureSync(`
226+
<vaadin-grid item-id-path="name">
227+
${additionalColumns.join('')}
228+
<vaadin-grid-tree-column path="name" width="200px" header="Name" flex-shrink="0"></vaadin-grid-tree-column>
229+
<vaadin-grid-column path="value" header="Value"></vaadin-grid-column>
230+
</vaadin-grid>
231+
`);
232+
grid.dataProvider = ({ parentItem }, callback) => {
233+
const itemsOnEachLevel = 5;
234+
const items = [...Array(itemsOnEachLevel)].map((_, i) => {
235+
return {
236+
name: `${parentItem ? `${parentItem.name}-` : ''}${i}`,
237+
value: `value-${i}`,
238+
children: i % 2 === 0,
239+
id: `${parentItem ? `${parentItem.id}-` : ''}${i}`,
240+
};
241+
});
242+
callback(items, itemsOnEachLevel);
243+
};
244+
flushGrid(grid);
245+
return grid;
246+
}
247+
248+
function getToggleCell(row) {
249+
for (const cell of row.querySelectorAll('td')) {
250+
if (cell._content && cell._content.querySelector('vaadin-grid-tree-toggle')) {
251+
return cell;
252+
}
253+
}
254+
return null;
255+
}
256+
257+
function getExpandableRows() {
258+
return Array.from(grid.$.items.children).filter((row) => row.getAttribute('aria-expanded') !== null);
259+
}
260+
261+
describe('aria-expanded', () => {
262+
beforeEach(() => {
263+
setupTreeGrid();
264+
});
265+
266+
it('should reflect aria-expanded state of rows', () => {
267+
Array.from(grid.$.items.children).forEach((row) => {
268+
const toggleCell = getToggleCell(row);
269+
expect(toggleCell).to.exist;
270+
expect(toggleCell.getAttribute('aria-expanded')).to.equal(row.getAttribute('aria-expanded'));
271+
});
272+
});
273+
274+
it('should update cell aria-expanded when row expanded state changes', () => {
275+
const row = getExpandableRows()[0];
276+
const toggleCell = getToggleCell(row);
277+
const itemName = row._item.name;
278+
279+
expect(row.getAttribute('aria-expanded')).to.equal('false');
280+
expect(toggleCell.getAttribute('aria-expanded')).to.equal('false');
281+
282+
grid.expandItem({ name: itemName });
283+
expect(row.getAttribute('aria-expanded')).to.equal('true');
284+
expect(toggleCell.getAttribute('aria-expanded')).to.equal('true');
285+
286+
grid.collapseItem({ name: itemName });
287+
expect(row.getAttribute('aria-expanded')).to.equal('false');
288+
expect(toggleCell.getAttribute('aria-expanded')).to.equal('false');
289+
});
290+
291+
it('should handle expansion via keyboard interaction', async () => {
292+
const toggleCell = getToggleCell(getExpandableRows()[0]);
293+
toggleCell.focus();
294+
await sendKeys({ press: 'Space' });
295+
expect(toggleCell.getAttribute('aria-expanded')).to.equal('true');
296+
await sendKeys({ press: 'Space' });
297+
expect(toggleCell.getAttribute('aria-expanded')).to.equal('false');
298+
});
299+
});
300+
301+
describe('with selection column', () => {
302+
beforeEach(() => {
303+
setupTreeGrid(['<vaadin-grid-selection-column></vaadin-grid-selection-column>']);
304+
});
305+
306+
it('should update correct cell when selection column is present', () => {
307+
const expandableRows = getExpandableRows();
308+
expect(expandableRows.length).to.be.above(0);
309+
expandableRows.forEach((row) => {
310+
const toggleCell = getToggleCell(row);
311+
expect(toggleCell).to.exist;
312+
expect(toggleCell).to.not.equal(row.querySelector('td'));
313+
expect(toggleCell._content.querySelector('vaadin-grid-tree-toggle')).to.exist;
314+
expect(toggleCell.getAttribute('aria-expanded')).to.equal(row.getAttribute('aria-expanded'));
315+
});
316+
});
317+
});
318+
});
221319
});
222320

223321
describe('details', () => {

0 commit comments

Comments
 (0)