Skip to content

Commit 2b65a92

Browse files
author
shleewhite
committed
feat: convert modifiers tests to gts
1 parent aa50f1c commit 2b65a92

File tree

8 files changed

+1160
-975
lines changed

8 files changed

+1160
-975
lines changed
Lines changed: 394 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,394 @@
1+
/**
2+
* Copyright (c) HashiCorp, Inc.
3+
* SPDX-License-Identifier: MPL-2.0
4+
*/
5+
6+
import { module, test } from 'qunit';
7+
import { setupRenderingTest } from 'showcase/tests/helpers';
8+
import {
9+
render,
10+
focus,
11+
tab,
12+
triggerKeyEvent,
13+
click,
14+
find,
15+
findAll,
16+
} from '@ember/test-helpers';
17+
import { focusable } from 'tabbable';
18+
import { get } from '@ember/helper';
19+
20+
import { HdsAdvancedTable } from '@hashicorp/design-system-components/components';
21+
22+
// we're using this for multiple tests so we'll declare context once and use it when we need it.
23+
const DEFAULT_SORTABLE_MODEL = [
24+
{
25+
id: '1',
26+
type: 'folk',
27+
artist: 'Nick Drake',
28+
album: 'Pink Moon',
29+
year: '1972',
30+
},
31+
{
32+
id: '2',
33+
type: 'folk',
34+
artist: 'The Beatles',
35+
album: 'Abbey Road',
36+
year: '1969',
37+
},
38+
{
39+
id: '3',
40+
type: 'folk',
41+
artist: 'Melanie',
42+
album: 'Candles in the Rain',
43+
year: '1971',
44+
},
45+
];
46+
47+
const DEFAULT_SORTABLE_COLUMNS = [
48+
{ key: 'artist', label: 'Artist', isSortable: true, tooltip: 'More info' },
49+
{ key: 'album', label: 'Album', isSortable: true },
50+
{ key: 'year', label: 'Year' },
51+
];
52+
53+
const DEFAULT_NESTED_MODEL = [
54+
{
55+
id: 1,
56+
name: 'Policy set 1',
57+
status: 'PASS',
58+
description: '',
59+
children: [
60+
{
61+
id: 11,
62+
name: 'test-advisory-pass.sentinel',
63+
status: 'PASS',
64+
description: 'Sample description for this thing.',
65+
},
66+
{
67+
id: 12,
68+
name: 'test-hard-mandatory-pass.sentinel',
69+
status: 'PASS',
70+
description: 'Sample description for this thing.',
71+
},
72+
],
73+
},
74+
{
75+
id: 2,
76+
name: 'Policy set 2',
77+
status: 'FAIL',
78+
description: '',
79+
children: [
80+
{
81+
id: 21,
82+
name: 'test-advisory-pass.sentinel',
83+
status: 'PASS',
84+
description: 'Sample description for this thing.',
85+
children: [
86+
{
87+
id: 211,
88+
name: 'test-advisory-pass.sentinel.primary',
89+
status: 'PASS',
90+
description: 'Sample description for this thing.',
91+
},
92+
],
93+
},
94+
],
95+
},
96+
];
97+
98+
const DEFAULT_NESTED_COLUMNS = [
99+
{ key: 'name', label: 'Name' },
100+
{ key: 'status', label: 'Status' },
101+
{ key: 'description', label: 'Description' },
102+
];
103+
104+
const createSortableTable = async () => {
105+
return await render(
106+
<template>
107+
<HdsAdvancedTable
108+
@model={{DEFAULT_SORTABLE_MODEL}}
109+
@columns={{DEFAULT_SORTABLE_COLUMNS}}
110+
id="data-test-advanced-table"
111+
>
112+
<:body as |B|>
113+
<B.Tr>
114+
{{! @glint-expect-error }}
115+
<B.Td>{{get B.data "artist"}}</B.Td>
116+
{{! @glint-expect-error }}
117+
<B.Td>{{get B.data "album"}}</B.Td>
118+
{{! @glint-expect-error }}
119+
<B.Td>{{get B.data "year"}}</B.Td>
120+
</B.Tr>
121+
</:body>
122+
</HdsAdvancedTable>
123+
</template>,
124+
);
125+
};
126+
127+
const createNestedTable = async () => {
128+
return await render(
129+
<template>
130+
<HdsAdvancedTable
131+
@model={{DEFAULT_NESTED_MODEL}}
132+
@columns={{DEFAULT_NESTED_COLUMNS}}
133+
id="data-test-nested-advanced-table"
134+
>
135+
<:body as |B|>
136+
{{! @glint-expect-error }}
137+
<B.Tr @selectionKey={{get B.data "id"}}>
138+
{{! @glint-expect-error }}
139+
<B.Th>{{get B.data "name"}}</B.Th>
140+
{{! @glint-expect-error }}
141+
<B.Td>{{get B.data "status"}}</B.Td>
142+
{{! @glint-expect-error }}
143+
<B.Td>{{get B.data "description"}}</B.Td>
144+
</B.Tr>
145+
</:body>
146+
</HdsAdvancedTable>
147+
</template>,
148+
);
149+
};
150+
151+
module('Integration | Modifier | hds-advanced-table-cell', function (hooks) {
152+
setupRenderingTest(hooks);
153+
154+
test('it should add correct tabindex to cells', async function (assert) {
155+
await createSortableTable();
156+
157+
const gridCells = findAll(
158+
'.hds-advanced-table__td, .hds-advanced-table__th',
159+
);
160+
161+
assert.dom(gridCells[0]).hasAttribute('tabindex', '0');
162+
163+
// should only be one focusable cell
164+
assert
165+
.dom(
166+
'.hds-advanced-table__td[tabindex="0"], .hds-advanced-table__th[tabindex="0"]',
167+
)
168+
.exists({ count: 1 });
169+
assert
170+
.dom(
171+
'.hds-advanced-table__td[tabindex="-1"], .hds-advanced-table__th[tabindex="-1"]',
172+
)
173+
.exists({ count: gridCells.length - 1 });
174+
});
175+
176+
test('it should add the data attribute to focusable content inside cells', async function (assert) {
177+
await createSortableTable();
178+
179+
const grid = find('#data-test-advanced-table');
180+
181+
if (grid) {
182+
const focusableElements = focusable(grid).filter(
183+
(element) =>
184+
!element.classList.contains('hds-advanced-table__th') &&
185+
!element.classList.contains('hds-advanced-table__td'),
186+
);
187+
188+
assert
189+
.dom('[data-advanced-table-child-focusable=""]')
190+
.exists({ count: focusableElements.length });
191+
}
192+
});
193+
194+
test('it should act as one tabstop', async function (assert) {
195+
await createSortableTable();
196+
197+
const gridCells = findAll(
198+
'.hds-advanced-table__td, .hds-advanced-table__th',
199+
);
200+
201+
await tab();
202+
assert.dom(gridCells[0]).isFocused();
203+
await tab();
204+
assert.dom('#after-advanced-table').isFocused();
205+
});
206+
207+
test('it should be navigable with arrow keys', async function (assert) {
208+
await createNestedTable();
209+
210+
const gridCells = findAll(
211+
'.hds-advanced-table__td, .hds-advanced-table__th',
212+
);
213+
214+
if (gridCells[0]) {
215+
await focus(gridCells[0]);
216+
assert
217+
.dom(document.activeElement)
218+
.hasText('Artist More information for Sort by ascending');
219+
220+
// check it doesn't break if you try to navigate to cell that doesn't exist
221+
await triggerKeyEvent(gridCells[0], 'keydown', 'ArrowUp');
222+
assert.dom(gridCells[0]).isFocused().hasAttribute('tabindex', '0');
223+
224+
await triggerKeyEvent(gridCells[0], 'keydown', 'ArrowDown');
225+
assert.dom('#artist-1').isFocused().hasAttribute('tabindex', '0');
226+
assert.dom(gridCells[0]).hasAttribute('tabindex', '-1');
227+
228+
// check it doesn't break if you try to navigate to cell that doesn't exist
229+
await triggerKeyEvent('#artist-1', 'keydown', 'ArrowLeft');
230+
assert.dom('#artist-1').isFocused().hasAttribute('tabindex', '0');
231+
232+
await triggerKeyEvent('#artist-1', 'keydown', 'ArrowRight');
233+
assert.dom('#album-1').isFocused().hasAttribute('tabindex', '0');
234+
assert.dom('#artist-1').hasAttribute('tabindex', '-1');
235+
}
236+
});
237+
238+
test('it should be navigable with arrow keys when there are nested rows', async function (assert) {
239+
await createNestedTable();
240+
241+
const gridCells = findAll(
242+
'.hds-advanced-table__td, .hds-advanced-table__th',
243+
);
244+
245+
if (gridCells[0]) {
246+
await focus(gridCells[0]);
247+
assert.dom(document.activeElement).hasText('Name');
248+
249+
// check it doesn't break if you try to navigate to cell that doesn't exist
250+
await triggerKeyEvent(gridCells[0], 'keydown', 'ArrowUp');
251+
assert.dom(gridCells[0]).isFocused().hasAttribute('tabindex', '0');
252+
253+
await triggerKeyEvent(gridCells[0], 'keydown', 'ArrowDown');
254+
assert.dom('#name-1').isFocused().hasAttribute('tabindex', '0');
255+
assert.dom(gridCells[0]).hasAttribute('tabindex', '-1');
256+
257+
// check it doesn't break if you try to navigate to cell that doesn't exist
258+
await triggerKeyEvent('#name-1', 'keydown', 'ArrowLeft');
259+
assert.dom('#name-1').isFocused().hasAttribute('tabindex', '0');
260+
261+
await triggerKeyEvent('#name-1', 'keydown', 'ArrowRight');
262+
assert.dom('#status-1').isFocused().hasAttribute('tabindex', '0');
263+
assert.dom('#name-1').hasAttribute('tabindex', '-1');
264+
265+
await triggerKeyEvent('#status-1', 'keydown', 'ArrowDown');
266+
assert.dom('#status-2').isFocused().hasAttribute('tabindex', '0');
267+
assert.dom('#status-1').hasAttribute('tabindex', '-1');
268+
269+
const expandRowButtonSelector = '.hds-advanced-table__th-button--expand';
270+
const rowToggles = findAll(expandRowButtonSelector);
271+
272+
if (rowToggles[0]) {
273+
await click(rowToggles[0]);
274+
275+
// check that can navigate to newly visible cells
276+
await triggerKeyEvent('#status-1', 'keydown', 'ArrowDown');
277+
assert.dom('#status-11').isFocused().hasAttribute('tabindex', '0');
278+
assert.dom('#status-1').hasAttribute('tabindex', '-1');
279+
}
280+
}
281+
});
282+
283+
test('it should have keyboard shortcuts', async function (assert) {
284+
await createSortableTable();
285+
const firstCell = find('.hds-advanced-table__th');
286+
287+
if (firstCell) {
288+
await triggerKeyEvent(firstCell, 'keydown', 'PageDown');
289+
assert.dom('#artist-3').isFocused();
290+
291+
await triggerKeyEvent('#artist-3', 'keydown', 'PageUp');
292+
assert.dom(firstCell).isFocused();
293+
294+
await triggerKeyEvent('#artist-3', 'keydown', 'End');
295+
assert.dom('#year-3').isFocused();
296+
297+
await triggerKeyEvent('#year-3', 'keydown', 'Home');
298+
assert.dom('#artist-3').isFocused();
299+
}
300+
});
301+
302+
test('it should have keyboard shortcuts when there are nested rows', async function (assert) {
303+
await createNestedTable();
304+
305+
const firstCell = find('.hds-advanced-table__th');
306+
307+
if (firstCell) {
308+
await triggerKeyEvent(firstCell, 'keydown', 'PageDown');
309+
assert.dom('#name-2').isFocused();
310+
311+
await triggerKeyEvent('#name-2', 'keydown', 'PageUp');
312+
assert.dom(firstCell).isFocused();
313+
314+
await triggerKeyEvent('#name-2', 'keydown', 'End');
315+
assert.dom('#description-2').isFocused();
316+
317+
await triggerKeyEvent('#description-2', 'keydown', 'Home');
318+
assert.dom('#name-2').isFocused();
319+
320+
const expandRowButtonSelector =
321+
'#data-test-nested-advanced-table .hds-advanced-table__tbody .hds-advanced-table__th[role="rowheader"] .hds-advanced-table__th-button--expand';
322+
const rowToggles = findAll(expandRowButtonSelector);
323+
const lastToggle = rowToggles[rowToggles.length - 1];
324+
325+
if (rowToggles.length > 0 && lastToggle) {
326+
await click(lastToggle);
327+
328+
// check that when the cells are expanded, the shortcuts jump to the new last cell in the column
329+
await triggerKeyEvent(firstCell, 'keydown', 'PageDown');
330+
assert.dom('#name-21').isFocused();
331+
332+
await triggerKeyEvent('#name-21', 'keydown', 'PageUp');
333+
assert.dom(firstCell).isFocused();
334+
}
335+
}
336+
});
337+
338+
test('it should switch navigation modes properly', async function (assert) {
339+
await createSortableTable();
340+
341+
const firstCell = find('.hds-advanced-table__th');
342+
const firstCellSortButton = find('.hds-advanced-table__th button');
343+
344+
assert.dom(firstCell).hasAttribute('tabindex', '0');
345+
assert.dom(firstCellSortButton).hasAttribute('tabindex', '-1');
346+
347+
if (firstCell && firstCellSortButton) {
348+
// content within cells should not be focusable
349+
assert
350+
.dom('[data-advanced-table-child-focusable=""][tabindex="0"]')
351+
.doesNotExist();
352+
353+
await triggerKeyEvent(firstCell, 'keydown', 'Enter');
354+
assert.dom(firstCellSortButton).isFocused().hasAttribute('tabindex', '0');
355+
356+
// cells should not be focusable anymore
357+
assert.dom('.hds-advanced-table__th[tabindex="0"]').doesNotExist();
358+
assert.dom('.hds-advanced-table__td[tabindex="0"]').doesNotExist();
359+
360+
await triggerKeyEvent(firstCellSortButton, 'keydown', 'Escape');
361+
assert.dom(firstCell).hasAttribute('tabindex', '0');
362+
assert.dom(firstCellSortButton).hasAttribute('tabindex', '-1');
363+
364+
// content within cells should not be focusable anymore
365+
assert
366+
.dom('[data-advanced-table-child-focusable=""][tabindex="0"]')
367+
.doesNotExist();
368+
}
369+
});
370+
371+
test('it should trap focus inside a cell when not in navigation mode', async function (assert) {
372+
await createSortableTable();
373+
374+
const firstCell = find('.hds-advanced-table__th');
375+
const cellButtons = firstCell?.querySelectorAll('button');
376+
377+
if (firstCell && cellButtons) {
378+
await triggerKeyEvent(firstCell, 'keydown', 'Enter');
379+
assert.dom(cellButtons[0]).isFocused().hasAttribute('tabindex', '0');
380+
381+
await tab();
382+
assert.dom(cellButtons[1]).isFocused().hasAttribute('tabindex', '0');
383+
384+
await tab();
385+
assert.dom(cellButtons[0]).isFocused().hasAttribute('tabindex', '0');
386+
387+
await tab({ backwards: true });
388+
assert.dom(cellButtons[1]).isFocused().hasAttribute('tabindex', '0');
389+
390+
await tab({ backwards: true });
391+
assert.dom(cellButtons[0]).isFocused().hasAttribute('tabindex', '0');
392+
}
393+
});
394+
});

0 commit comments

Comments
 (0)