@purpurds/table 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/LICENSE.txt +213 -0
- package/dist/cell-types/badge-cell.d.ts +8 -0
- package/dist/cell-types/badge-cell.d.ts.map +1 -0
- package/dist/cell-types/body-text-cell.d.ts +8 -0
- package/dist/cell-types/body-text-cell.d.ts.map +1 -0
- package/dist/cell-types/button-cell.d.ts +8 -0
- package/dist/cell-types/button-cell.d.ts.map +1 -0
- package/dist/cell-types/button-group-cell.d.ts +16 -0
- package/dist/cell-types/button-group-cell.d.ts.map +1 -0
- package/dist/cell-types/cta-link-cell.d.ts +8 -0
- package/dist/cell-types/cta-link-cell.d.ts.map +1 -0
- package/dist/cell-types/date-cell.d.ts +8 -0
- package/dist/cell-types/date-cell.d.ts.map +1 -0
- package/dist/cell-types/empty-cell.d.ts +4 -0
- package/dist/cell-types/empty-cell.d.ts.map +1 -0
- package/dist/cell-types/error-message-cell.d.ts +8 -0
- package/dist/cell-types/error-message-cell.d.ts.map +1 -0
- package/dist/cell-types/icon-text-cell.d.ts +8 -0
- package/dist/cell-types/icon-text-cell.d.ts.map +1 -0
- package/dist/cell-types/lead-text-cell.d.ts +8 -0
- package/dist/cell-types/lead-text-cell.d.ts.map +1 -0
- package/dist/cell-types/link-cell.d.ts +8 -0
- package/dist/cell-types/link-cell.d.ts.map +1 -0
- package/dist/cell-types/number-cell.d.ts +8 -0
- package/dist/cell-types/number-cell.d.ts.map +1 -0
- package/dist/cell-types/row-selection-cell.d.ts +8 -0
- package/dist/cell-types/row-selection-cell.d.ts.map +1 -0
- package/dist/cell-types/row-toggle-cell.d.ts +8 -0
- package/dist/cell-types/row-toggle-cell.d.ts.map +1 -0
- package/dist/cell-types/toggle-cell.d.ts +8 -0
- package/dist/cell-types/toggle-cell.d.ts.map +1 -0
- package/dist/cell-types/warning-message-cell.d.ts +8 -0
- package/dist/cell-types/warning-message-cell.d.ts.map +1 -0
- package/dist/metadata.js +17 -0
- package/dist/story-utils/column-def.d.ts +5 -0
- package/dist/story-utils/column-def.d.ts.map +1 -0
- package/dist/story-utils/table-data.d.ts +35 -0
- package/dist/story-utils/table-data.d.ts.map +1 -0
- package/dist/story-utils/use-fetch-table-data-hook.d.ts +11 -0
- package/dist/story-utils/use-fetch-table-data-hook.d.ts.map +1 -0
- package/dist/styles.css +1 -0
- package/dist/table-action-bar.d.ts +26 -0
- package/dist/table-action-bar.d.ts.map +1 -0
- package/dist/table-body.d.ts +10 -0
- package/dist/table-body.d.ts.map +1 -0
- package/dist/table-column-header-cell.d.ts +28 -0
- package/dist/table-column-header-cell.d.ts.map +1 -0
- package/dist/table-export-drawer.d.ts +17 -0
- package/dist/table-export-drawer.d.ts.map +1 -0
- package/dist/table-header.d.ts +11 -0
- package/dist/table-header.d.ts.map +1 -0
- package/dist/table-row-cell-skeleton.d.ts +14 -0
- package/dist/table-row-cell-skeleton.d.ts.map +1 -0
- package/dist/table-row-cell.d.ts +25 -0
- package/dist/table-row-cell.d.ts.map +1 -0
- package/dist/table-row.d.ts +11 -0
- package/dist/table-row.d.ts.map +1 -0
- package/dist/table-settings-drawer.d.ts +41 -0
- package/dist/table-settings-drawer.d.ts.map +1 -0
- package/dist/table-toolbar.d.ts +37 -0
- package/dist/table-toolbar.d.ts.map +1 -0
- package/dist/table.cjs.js +259 -0
- package/dist/table.cjs.js.map +1 -0
- package/dist/table.d.ts +20 -0
- package/dist/table.d.ts.map +1 -0
- package/dist/table.es.js +13585 -0
- package/dist/table.es.js.map +1 -0
- package/dist/test-utils/column-def.d.ts +6 -0
- package/dist/test-utils/column-def.d.ts.map +1 -0
- package/dist/test-utils/helpers.d.ts +138 -0
- package/dist/test-utils/helpers.d.ts.map +1 -0
- package/dist/test-utils/table-data.d.ts +33 -0
- package/dist/test-utils/table-data.d.ts.map +1 -0
- package/dist/types.d.ts +420 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/use-screen-size.hook.d.ts +7 -0
- package/dist/use-screen-size.hook.d.ts.map +1 -0
- package/dist/use-truncated-hook.d.ts +10 -0
- package/dist/use-truncated-hook.d.ts.map +1 -0
- package/dist/utils/custom-functions.d.ts +9 -0
- package/dist/utils/custom-functions.d.ts.map +1 -0
- package/dist/utils/unit-conversions.d.ts +19 -0
- package/dist/utils/unit-conversions.d.ts.map +1 -0
- package/dist/utils/unit-conversions.spec.d.ts +2 -0
- package/dist/utils/unit-conversions.spec.d.ts.map +1 -0
- package/eslint.config.mjs +2 -0
- package/package.json +82 -0
- package/src/cell-types/badge-cell.tsx +25 -0
- package/src/cell-types/body-text-cell.tsx +54 -0
- package/src/cell-types/button-cell.tsx +26 -0
- package/src/cell-types/button-group-cell.tsx +54 -0
- package/src/cell-types/cta-link-cell.tsx +25 -0
- package/src/cell-types/date-cell.tsx +33 -0
- package/src/cell-types/empty-cell.tsx +6 -0
- package/src/cell-types/error-message-cell.tsx +30 -0
- package/src/cell-types/icon-text-cell.tsx +30 -0
- package/src/cell-types/lead-text-cell.tsx +19 -0
- package/src/cell-types/link-cell.tsx +58 -0
- package/src/cell-types/number-cell.tsx +27 -0
- package/src/cell-types/row-selection-cell.tsx +22 -0
- package/src/cell-types/row-toggle-cell.tsx +23 -0
- package/src/cell-types/toggle-cell.tsx +19 -0
- package/src/cell-types/warning-message-cell.tsx +30 -0
- package/src/global.d.ts +4 -0
- package/src/story-utils/column-def.ts +148 -0
- package/src/story-utils/table-data.tsx +262 -0
- package/src/story-utils/use-fetch-table-data-hook.tsx +30 -0
- package/src/table-action-bar.module.scss +106 -0
- package/src/table-action-bar.test.tsx +111 -0
- package/src/table-action-bar.tsx +104 -0
- package/src/table-body.tsx +25 -0
- package/src/table-column-header-cell.tsx +305 -0
- package/src/table-export-drawer.module.scss +9 -0
- package/src/table-export-drawer.test.tsx +75 -0
- package/src/table-export-drawer.tsx +59 -0
- package/src/table-header.tsx +35 -0
- package/src/table-kitchen-sink.test.tsx +1196 -0
- package/src/table-row-cell-skeleton.tsx +61 -0
- package/src/table-row-cell.test.tsx +360 -0
- package/src/table-row-cell.tsx +188 -0
- package/src/table-row.tsx +30 -0
- package/src/table-settings-drawer.module.scss +25 -0
- package/src/table-settings-drawer.test.tsx +350 -0
- package/src/table-settings-drawer.tsx +254 -0
- package/src/table-toolbar.module.scss +17 -0
- package/src/table-toolbar.test.tsx +95 -0
- package/src/table-toolbar.tsx +136 -0
- package/src/table.module.scss +367 -0
- package/src/table.stories.tsx +1246 -0
- package/src/table.story.css +11 -0
- package/src/table.test.tsx +318 -0
- package/src/table.tsx +501 -0
- package/src/test-utils/column-def.ts +152 -0
- package/src/test-utils/helpers.ts +234 -0
- package/src/test-utils/table-data.tsx +318 -0
- package/src/types.ts +496 -0
- package/src/use-screen-size.hook.ts +23 -0
- package/src/use-truncated-hook.tsx +74 -0
- package/src/utils/custom-functions.ts +52 -0
- package/src/utils/unit-conversions.spec.ts +92 -0
- package/src/utils/unit-conversions.ts +30 -0
- package/vitest.setup.ts +60 -0
|
@@ -0,0 +1,1196 @@
|
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
|
+
import { Pagination } from "@purpurds/pagination";
|
|
3
|
+
import { ColumnFiltersState, RowSelectionState } from "@tanstack/react-table";
|
|
4
|
+
import { cleanup, fireEvent, render, screen, within } from "@testing-library/react";
|
|
5
|
+
import userEvent from "@testing-library/user-event";
|
|
6
|
+
import { axe } from "vitest-axe";
|
|
7
|
+
|
|
8
|
+
import { PaginationState,Table } from "./table.tsx";
|
|
9
|
+
import { createColumnDefKitchenSink } from "./test-utils/column-def.ts";
|
|
10
|
+
import {
|
|
11
|
+
copy,
|
|
12
|
+
filterOnSelect,
|
|
13
|
+
filterOnString,
|
|
14
|
+
getCellInRowContent,
|
|
15
|
+
getCheckBoxForTableBodyRow,
|
|
16
|
+
getCheckBoxForTableHeaderRow,
|
|
17
|
+
getComboBoxFilterForHeader,
|
|
18
|
+
getInputFilterForHeader,
|
|
19
|
+
getRadioToggleForTableBodyRow,
|
|
20
|
+
getTableBody,
|
|
21
|
+
getTableColumnHeaderByText,
|
|
22
|
+
getTableHead,
|
|
23
|
+
Selectors,
|
|
24
|
+
} from "./test-utils/helpers.ts";
|
|
25
|
+
import { tableDataLarge } from "./test-utils/table-data.tsx";
|
|
26
|
+
|
|
27
|
+
const toggleExpandMock = vi.fn();
|
|
28
|
+
const onExportDataMock = vi.fn();
|
|
29
|
+
const onPrimaryButtonClickMock = vi.fn();
|
|
30
|
+
const clickCellButtonMock = vi.fn();
|
|
31
|
+
const clickButtonGroupCellButtonMock = vi.fn();
|
|
32
|
+
|
|
33
|
+
const TestComponent = ({
|
|
34
|
+
enableMultiRowSelection = true,
|
|
35
|
+
}: {
|
|
36
|
+
enableMultiRowSelection: boolean;
|
|
37
|
+
}) => {
|
|
38
|
+
const exportFormats = ["csv", "xlsx"];
|
|
39
|
+
|
|
40
|
+
const [pagination, setPagination] = useState<PaginationState>({
|
|
41
|
+
pageIndex: 0,
|
|
42
|
+
pageSize: 5,
|
|
43
|
+
});
|
|
44
|
+
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
|
|
45
|
+
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
|
|
46
|
+
const [columnVisibility, setColumnVisibility] = useState({});
|
|
47
|
+
|
|
48
|
+
const [displayData, setDisplayData] = useState(tableDataLarge);
|
|
49
|
+
const [rowCount, setRowCount] = useState(tableDataLarge.length);
|
|
50
|
+
|
|
51
|
+
const [showOnlySelectedRows, setShowOnlySelectedRows] = useState(false);
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if (showOnlySelectedRows) {
|
|
55
|
+
const selectedRows = Object.keys(rowSelection).filter(
|
|
56
|
+
(key) => (rowSelection as Record<string, unknown>)[key]
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
const filteredData = tableDataLarge.filter((row) => selectedRows.includes(`${row.id}`));
|
|
60
|
+
|
|
61
|
+
setDisplayData(filteredData);
|
|
62
|
+
} else {
|
|
63
|
+
setDisplayData(tableDataLarge);
|
|
64
|
+
}
|
|
65
|
+
}, [showOnlySelectedRows, rowSelection]);
|
|
66
|
+
|
|
67
|
+
const handleOnPageChange = (page: { currentPage: number; pageSize: number }) => {
|
|
68
|
+
setPagination({ pageIndex: page.currentPage - 1, pageSize: page.pageSize });
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const paginationComponent = (
|
|
72
|
+
<Pagination
|
|
73
|
+
onPageChange={handleOnPageChange}
|
|
74
|
+
availablePageSizes={[5, 10, 50]}
|
|
75
|
+
currentPage={pagination.pageIndex + 1}
|
|
76
|
+
pageSize={pagination.pageSize}
|
|
77
|
+
totalItems={rowCount}
|
|
78
|
+
nextButtonAriaLabel="Go to next page"
|
|
79
|
+
nextButtonText="Next"
|
|
80
|
+
outOfLabel="of"
|
|
81
|
+
pageSelectorListBoxLabel="Select a page"
|
|
82
|
+
pageSelectorNoOptionsText="Page does not exist"
|
|
83
|
+
pageSizeLabel="Items per page"
|
|
84
|
+
previousButtonAriaLabel="Go to previous page"
|
|
85
|
+
previousButtonText="Previous"
|
|
86
|
+
stepNumberPrefix="Go to page"
|
|
87
|
+
/>
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<div style={{ width: "80rem" }}>
|
|
92
|
+
{/* @ts-expect-error Some props are required depending on if enableMultiRowSelection is true. */}
|
|
93
|
+
<Table
|
|
94
|
+
actionbarCopy={copy.actionBar}
|
|
95
|
+
columns={createColumnDefKitchenSink(clickCellButtonMock, clickButtonGroupCellButtonMock)}
|
|
96
|
+
data={displayData}
|
|
97
|
+
enableActionBar={enableMultiRowSelection} // Enables action bar only when multi row selection is enabled
|
|
98
|
+
enableFilters={true}
|
|
99
|
+
enableRowSelection={true}
|
|
100
|
+
enableMultiRowSelection={enableMultiRowSelection}
|
|
101
|
+
enableSorting={true}
|
|
102
|
+
enableToolbar={true}
|
|
103
|
+
exportDrawerCopy={copy.exportDrawer}
|
|
104
|
+
exportFormats={exportFormats}
|
|
105
|
+
getRowId={(row) => `${row.id}`}
|
|
106
|
+
onColumnFiltersChange={setColumnFilters}
|
|
107
|
+
onColumnVisibilityChange={setColumnVisibility}
|
|
108
|
+
onExportData={onExportDataMock}
|
|
109
|
+
onRowsCountChange={setRowCount}
|
|
110
|
+
onRowSelectionChange={setRowSelection}
|
|
111
|
+
onToggleExpand={toggleExpandMock}
|
|
112
|
+
onPrimaryButtonClick={onPrimaryButtonClickMock}
|
|
113
|
+
paginationComponent={paginationComponent}
|
|
114
|
+
showOnlySelectedRows={showOnlySelectedRows}
|
|
115
|
+
setShowOnlySelectedRows={setShowOnlySelectedRows}
|
|
116
|
+
settingsDrawerCopy={copy.settingsDrawer}
|
|
117
|
+
sortingAriaLabels={copy.sortingAriaLabels}
|
|
118
|
+
state={{ pagination, columnFilters, rowSelection, columnVisibility }}
|
|
119
|
+
toolbarCopy={copy.toolbar}
|
|
120
|
+
actionBarTotalRowCount={tableDataLarge.length}
|
|
121
|
+
rowSelectionAriaLabels={copy.rowSelectionAriaLabels}
|
|
122
|
+
variant="primary"
|
|
123
|
+
/>
|
|
124
|
+
</div>
|
|
125
|
+
);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
describe("Data Table - Kitchen sink", () => {
|
|
129
|
+
describe("With row selection", () => {
|
|
130
|
+
let table: HTMLTableElement;
|
|
131
|
+
|
|
132
|
+
beforeEach(async () => {
|
|
133
|
+
render(<TestComponent enableMultiRowSelection={true} />);
|
|
134
|
+
|
|
135
|
+
table = screen.getByRole("table");
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
afterEach(() => {
|
|
139
|
+
cleanup();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it("should have 10 headers", () => {
|
|
143
|
+
const expectedHeaders = [
|
|
144
|
+
"checkbox",
|
|
145
|
+
"ID",
|
|
146
|
+
"Name",
|
|
147
|
+
"Link",
|
|
148
|
+
"Age",
|
|
149
|
+
"Badge",
|
|
150
|
+
"Position",
|
|
151
|
+
"Action",
|
|
152
|
+
"Date",
|
|
153
|
+
"Buttons",
|
|
154
|
+
];
|
|
155
|
+
|
|
156
|
+
const headers = within(getTableHead(table)).getAllByRole("columnheader");
|
|
157
|
+
expect(headers).toHaveLength(expectedHeaders.length);
|
|
158
|
+
|
|
159
|
+
expectedHeaders.forEach((expectedHeader, index) => {
|
|
160
|
+
const withinHeader = within(headers[index]);
|
|
161
|
+
if (expectedHeader === "checkbox") {
|
|
162
|
+
expect(withinHeader.getByRole("checkbox")).toBeDefined();
|
|
163
|
+
} else {
|
|
164
|
+
const paragraph = withinHeader.getByRole("paragraph");
|
|
165
|
+
expect(paragraph.textContent?.trim()).toBe(expectedHeader);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("should have 5 rows of data", () => {
|
|
171
|
+
const tableRows = within(getTableBody(table)).getAllByRole("row");
|
|
172
|
+
expect(tableRows).toHaveLength(5);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it("should have data in first row", () => {
|
|
176
|
+
const tableRow1 = within(getTableBody(table)).getAllByRole("row")[0];
|
|
177
|
+
|
|
178
|
+
const tableRowCells = within(tableRow1).getAllByRole("cell");
|
|
179
|
+
|
|
180
|
+
const expectedCellsTextContent = [
|
|
181
|
+
"",
|
|
182
|
+
"12345",
|
|
183
|
+
"Name 1",
|
|
184
|
+
"Link 2",
|
|
185
|
+
"40",
|
|
186
|
+
"Information",
|
|
187
|
+
"1",
|
|
188
|
+
"ClickMe",
|
|
189
|
+
"2025-03-26 11:14",
|
|
190
|
+
[
|
|
191
|
+
{ testId: "download-icon", ariaLabeL: "ClickMe" },
|
|
192
|
+
{ testId: "remove-icon", ariaLabeL: "ClickMe" },
|
|
193
|
+
{ testId: "more-vertical-icon", ariaLabeL: "ClickMe" },
|
|
194
|
+
],
|
|
195
|
+
];
|
|
196
|
+
|
|
197
|
+
tableRowCells.forEach((tableRowCell, index) => {
|
|
198
|
+
if (typeof expectedCellsTextContent[index] === "string") {
|
|
199
|
+
expect(tableRowCell).toHaveTextContent(expectedCellsTextContent[index]);
|
|
200
|
+
} else {
|
|
201
|
+
expectedCellsTextContent[index].forEach((expectedElement, buttonIndex) => {
|
|
202
|
+
const button = within(tableRowCell).getAllByRole("button")[buttonIndex];
|
|
203
|
+
const svg = within(button).getByTestId(expectedElement.testId);
|
|
204
|
+
expect(svg).toBeInTheDocument();
|
|
205
|
+
expect(button).toHaveAttribute("aria-label", expectedElement.ariaLabeL);
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it.each(["Name", "Age", "Link", "Position"])(
|
|
212
|
+
"should have sorting icons on '%s' column header",
|
|
213
|
+
(column) => {
|
|
214
|
+
const header = getTableColumnHeaderByText(table, column);
|
|
215
|
+
const button = within(header).getByRole("button");
|
|
216
|
+
expect(button).toHaveAttribute("aria-label", "Sort column");
|
|
217
|
+
}
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
it.each([
|
|
221
|
+
["Search by id", "ID"],
|
|
222
|
+
["Search by name", "Name"],
|
|
223
|
+
["Search by age", "Age"],
|
|
224
|
+
])("should have placeholder '%s' on '%s' input filter", (expectedPlaceholder, column) => {
|
|
225
|
+
const input = getInputFilterForHeader(table, column);
|
|
226
|
+
expect(input).toHaveAttribute("placeholder", expectedPlaceholder);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it("should have right alignment on position column cells", () => {
|
|
230
|
+
const positionCell = getCellInRowContent(table, 1, "Position");
|
|
231
|
+
const paragraph = within(positionCell).getByRole("paragraph");
|
|
232
|
+
|
|
233
|
+
const hasRightAlignClass = paragraph.className
|
|
234
|
+
.split(" ")
|
|
235
|
+
.some((c) => c.includes("__right-align"));
|
|
236
|
+
|
|
237
|
+
expect(hasRightAlignClass).toBe(true);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it("should have disabled clear filters button when no filters are applied", () => {
|
|
241
|
+
const toolbar = screen.getByTestId(Selectors.TOOLBAR.ROOT);
|
|
242
|
+
const clearFiltersButton = within(toolbar).getByTestId(
|
|
243
|
+
Selectors.TOOLBAR.CLEAR_FILTERS_BUTTON
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
expect(clearFiltersButton).toHaveAttribute("aria-disabled", "true");
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
describe("Tool bar", () => {
|
|
250
|
+
let toolbar: HTMLElement;
|
|
251
|
+
|
|
252
|
+
beforeEach(() => {
|
|
253
|
+
toolbar = screen.getByTestId(Selectors.TOOLBAR.ROOT);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
describe("Clearing all filters", () => {
|
|
257
|
+
beforeEach(async () => {
|
|
258
|
+
await filterOnString(table, "Name", "Name");
|
|
259
|
+
await filterOnSelect(table, "Badge", "Special");
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it("should enable clear filters button when filters are applied", async () => {
|
|
263
|
+
const clearFiltersButton = within(toolbar).getByTestId(
|
|
264
|
+
Selectors.TOOLBAR.CLEAR_FILTERS_BUTTON
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
expect(clearFiltersButton).not.toHaveAttribute("aria-disabled", "true");
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it("should have 2 rows of data", () => {
|
|
271
|
+
const tableRows = within(getTableBody(table)).getAllByRole("row");
|
|
272
|
+
expect(tableRows).toHaveLength(2);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it("Name input filter should contain 'Name'", () => {
|
|
276
|
+
expect(getInputFilterForHeader(table, "Name")).toHaveValue("Name");
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it("Badge select should be on 'special'", () => {
|
|
280
|
+
expect(getComboBoxFilterForHeader(table, "Badge")).toHaveValue("special");
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
describe("Clearing all filters", () => {
|
|
284
|
+
beforeEach(async () => {
|
|
285
|
+
const clearFiltersButton = within(toolbar).getByTestId(
|
|
286
|
+
Selectors.TOOLBAR.CLEAR_FILTERS_BUTTON
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
await userEvent.click(clearFiltersButton);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it("should have 5 rows of data when clicking on 'Clear filters'", async () => {
|
|
293
|
+
const tableRows = within(getTableBody(table)).getAllByRole("row");
|
|
294
|
+
expect(tableRows).toHaveLength(5);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it("Name input filter should be empty", () => {
|
|
298
|
+
expect(getInputFilterForHeader(table, "Name")).toHaveValue("");
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it("Badge select should be on default", () => {
|
|
302
|
+
expect(getComboBoxFilterForHeader(table, "Badge")).toHaveValue("");
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
describe("Export table", () => {
|
|
308
|
+
describe("Export button when multiple export methods", () => {
|
|
309
|
+
beforeEach(async () => {
|
|
310
|
+
const exportButton = within(toolbar).getByTestId(Selectors.TOOLBAR.EXPORT_BUTTON);
|
|
311
|
+
|
|
312
|
+
await userEvent.click(exportButton);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it("should open export drawer", async () => {
|
|
316
|
+
expect(screen.getByTestId(Selectors.EXPORT_DRAWER.CONTENT)).toBeVisible();
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it("should call 'onExportData' when clicking on export button", async () => {
|
|
320
|
+
const exportLinks = screen.getAllByTestId(Selectors.EXPORT_DRAWER.EXPORT_BUTTON);
|
|
321
|
+
await userEvent.click(exportLinks[0]);
|
|
322
|
+
expect(onExportDataMock).toHaveBeenCalledWith("csv");
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
describe("Expand table", () => {
|
|
328
|
+
let expandButton: HTMLButtonElement;
|
|
329
|
+
|
|
330
|
+
beforeEach(() => {
|
|
331
|
+
expandButton = screen.getByTestId(Selectors.TOOLBAR.EXPAND_BUTTON);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
it("should have expand button", () => {
|
|
335
|
+
expect(expandButton).toHaveTextContent("Expand");
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
it("should send an event when clicking expand button", async () => {
|
|
339
|
+
await userEvent.click(expandButton);
|
|
340
|
+
expect(toggleExpandMock).toHaveBeenCalled();
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
describe("Settings drawer", () => {
|
|
345
|
+
let closeButton: HTMLButtonElement;
|
|
346
|
+
let withinDrawerContent: ReturnType<typeof within>;
|
|
347
|
+
let withinGeneralSettings: ReturnType<typeof within>;
|
|
348
|
+
let withinVisibleColumns: ReturnType<typeof within>;
|
|
349
|
+
|
|
350
|
+
beforeEach(async () => {
|
|
351
|
+
const settingsButton = within(toolbar).getByTestId(Selectors.TOOLBAR.SETTINGS_BUTTON);
|
|
352
|
+
|
|
353
|
+
await userEvent.click(settingsButton);
|
|
354
|
+
|
|
355
|
+
withinDrawerContent = within(screen.getByTestId(Selectors.SETTINGS_DRAWER.CONTENT));
|
|
356
|
+
closeButton = within(screen.getByTestId(Selectors.SETTINGS_DRAWER.HEADER_ROW)).getByRole(
|
|
357
|
+
"button"
|
|
358
|
+
);
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it("should open the drawer", () => {
|
|
362
|
+
expect(screen.getByTestId(Selectors.SETTINGS_DRAWER.CONTENT)).toBeVisible();
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
it("should be possible to close the drawer", async () => {
|
|
366
|
+
await userEvent.click(closeButton);
|
|
367
|
+
expect(screen.queryByTestId(Selectors.SETTINGS_DRAWER.CONTENT)).toBeNull();
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
describe("General settings", () => {
|
|
371
|
+
let toggleElements: HTMLElement[];
|
|
372
|
+
|
|
373
|
+
beforeEach(() => {
|
|
374
|
+
withinGeneralSettings = within(
|
|
375
|
+
withinDrawerContent.getByTestId(Selectors.SETTINGS_DRAWER.GENERAL_SETTINGS)
|
|
376
|
+
);
|
|
377
|
+
|
|
378
|
+
toggleElements = withinGeneralSettings.getAllByTestId(Selectors.SETTINGS_DRAWER.TOGGLE);
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
describe("Toggle show filters", () => {
|
|
382
|
+
beforeEach(async () => {
|
|
383
|
+
await userEvent.click(toggleElements[0]);
|
|
384
|
+
await userEvent.click(closeButton);
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
it.each(["ID", "Name", "Link", "Age", "Badge"])(
|
|
388
|
+
"should not have a filter on '%s'",
|
|
389
|
+
(column) => {
|
|
390
|
+
const header = getTableColumnHeaderByText(table, column);
|
|
391
|
+
const input = within(header).queryByRole("textbox");
|
|
392
|
+
expect(input).not.toBeInTheDocument();
|
|
393
|
+
const listbox = within(header).queryByRole("checkbox");
|
|
394
|
+
expect(listbox).not.toBeInTheDocument();
|
|
395
|
+
}
|
|
396
|
+
);
|
|
397
|
+
|
|
398
|
+
describe("Clear filters button", () => {
|
|
399
|
+
it("should not be visible when filters are hidden", async () => {
|
|
400
|
+
const toolbar = screen.getByTestId(Selectors.TOOLBAR.ROOT);
|
|
401
|
+
const clearFiltersButton = within(toolbar).queryByTestId(
|
|
402
|
+
Selectors.TOOLBAR.CLEAR_FILTERS_BUTTON
|
|
403
|
+
);
|
|
404
|
+
|
|
405
|
+
expect(clearFiltersButton).not.toBeInTheDocument();
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
describe("Visible columns", () => {
|
|
412
|
+
let toggleElements: HTMLElement[];
|
|
413
|
+
let toggleLabels: HTMLLabelElement[];
|
|
414
|
+
|
|
415
|
+
beforeEach(() => {
|
|
416
|
+
withinVisibleColumns = within(
|
|
417
|
+
withinDrawerContent.getByTestId(Selectors.SETTINGS_DRAWER.VISIBLE_COLUMNS)
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
toggleElements = withinVisibleColumns.getAllByTestId(Selectors.SETTINGS_DRAWER.TOGGLE);
|
|
421
|
+
toggleLabels = withinVisibleColumns.getAllByTestId(
|
|
422
|
+
Selectors.SETTINGS_DRAWER.TOGGLE_LABEL
|
|
423
|
+
);
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
it.each([
|
|
427
|
+
{ label: "ID", index: 0, dataState: "checked" },
|
|
428
|
+
{ label: "Name", index: 1, dataState: "checked" },
|
|
429
|
+
{ label: "Link", index: 2, dataState: "checked" },
|
|
430
|
+
{ label: "Age", index: 3, dataState: "checked" },
|
|
431
|
+
{ label: "Badge", index: 4, dataState: "checked" },
|
|
432
|
+
{ label: "Position", index: 5, dataState: "checked" },
|
|
433
|
+
{ label: "Action", index: 6, dataState: "checked" },
|
|
434
|
+
{ label: "Date", index: 7, dataState: "checked" },
|
|
435
|
+
{ label: "Buttons", index: 8, dataState: "checked" },
|
|
436
|
+
])("should have '%s' toggle", ({ label, index, dataState }) => {
|
|
437
|
+
expect(toggleElements[index]).toHaveRole("switch");
|
|
438
|
+
expect(toggleElements[index]).toHaveAttribute("data-state", dataState);
|
|
439
|
+
expect(toggleElements[index]).not.toBeDisabled();
|
|
440
|
+
expect(toggleLabels[index]).toHaveTextContent(label);
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
it("should disable last visible column toggle", () => {
|
|
444
|
+
const toggleIndex = 2;
|
|
445
|
+
toggleElements.forEach((toggleElement, index) => {
|
|
446
|
+
if (index === toggleIndex) {
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
fireEvent.click(toggleElement);
|
|
450
|
+
});
|
|
451
|
+
expect(toggleElements[toggleIndex]).toHaveAttribute("data-state", "checked");
|
|
452
|
+
expect(toggleElements[toggleIndex]).toBeDisabled();
|
|
453
|
+
expect(toggleLabels[toggleIndex]).toHaveTextContent("Link");
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
describe("Toggle Age column visibility off", () => {
|
|
457
|
+
beforeEach(async () => {
|
|
458
|
+
const ageToggle = toggleElements[3];
|
|
459
|
+
await userEvent.click(ageToggle);
|
|
460
|
+
await userEvent.click(closeButton);
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
it("should have 9 headers", async () => {
|
|
464
|
+
const expectedHeaders = [
|
|
465
|
+
"checkbox",
|
|
466
|
+
"ID",
|
|
467
|
+
"Name",
|
|
468
|
+
"Link",
|
|
469
|
+
"Badge",
|
|
470
|
+
"Position",
|
|
471
|
+
"Action",
|
|
472
|
+
"Date",
|
|
473
|
+
"Buttons",
|
|
474
|
+
];
|
|
475
|
+
|
|
476
|
+
const headers = within(getTableHead(table)).getAllByRole("columnheader");
|
|
477
|
+
expect(headers).toHaveLength(expectedHeaders.length);
|
|
478
|
+
|
|
479
|
+
expectedHeaders.forEach((expectedHeader, index) => {
|
|
480
|
+
const withinHeader = within(headers[index]);
|
|
481
|
+
if (expectedHeader === "checkbox") {
|
|
482
|
+
expect(withinHeader.getByRole("checkbox")).toBeDefined();
|
|
483
|
+
} else {
|
|
484
|
+
const paragraph = withinHeader.getByRole("paragraph");
|
|
485
|
+
expect(paragraph.textContent?.trim()).toBe(expectedHeader);
|
|
486
|
+
}
|
|
487
|
+
});
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
it("should have data in first row", () => {
|
|
491
|
+
const tableRow1 = within(getTableBody(table)).getAllByRole("row")[0];
|
|
492
|
+
|
|
493
|
+
const tableRowCells = within(tableRow1).getAllByRole("cell");
|
|
494
|
+
|
|
495
|
+
const expectedCellsTextContent = [
|
|
496
|
+
"",
|
|
497
|
+
"12345",
|
|
498
|
+
"Name 1",
|
|
499
|
+
"Link 2",
|
|
500
|
+
"Information",
|
|
501
|
+
"1",
|
|
502
|
+
"ClickMe",
|
|
503
|
+
"2025-03-26 11:14",
|
|
504
|
+
[
|
|
505
|
+
{ testId: "download-icon", ariaLabeL: "ClickMe" },
|
|
506
|
+
{ testId: "remove-icon", ariaLabeL: "ClickMe" },
|
|
507
|
+
{ testId: "more-vertical-icon", ariaLabeL: "ClickMe" },
|
|
508
|
+
],
|
|
509
|
+
];
|
|
510
|
+
|
|
511
|
+
tableRowCells.forEach((tableRowCell, index) => {
|
|
512
|
+
if (typeof expectedCellsTextContent[index] === "string") {
|
|
513
|
+
expect(tableRowCell).toHaveTextContent(expectedCellsTextContent[index]);
|
|
514
|
+
} else {
|
|
515
|
+
expectedCellsTextContent[index].forEach((expectedElement, buttonIndex) => {
|
|
516
|
+
const button = within(tableRowCell).getAllByRole("button")[buttonIndex];
|
|
517
|
+
const svg = within(button).getByTestId(expectedElement.testId);
|
|
518
|
+
expect(svg).toBeInTheDocument();
|
|
519
|
+
expect(button).toHaveAttribute("aria-label", expectedElement.ariaLabeL);
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
});
|
|
523
|
+
});
|
|
524
|
+
});
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
describe("Reset settings", () => {
|
|
528
|
+
let visibleColumnsToggleElements: HTMLElement[];
|
|
529
|
+
let generalSettingsToggleElements: HTMLElement[];
|
|
530
|
+
let resetSettingsButton: HTMLButtonElement;
|
|
531
|
+
|
|
532
|
+
beforeEach(async () => {
|
|
533
|
+
withinVisibleColumns = within(
|
|
534
|
+
withinDrawerContent.getByTestId(Selectors.SETTINGS_DRAWER.VISIBLE_COLUMNS)
|
|
535
|
+
);
|
|
536
|
+
|
|
537
|
+
visibleColumnsToggleElements = withinVisibleColumns.getAllByTestId(
|
|
538
|
+
Selectors.SETTINGS_DRAWER.TOGGLE
|
|
539
|
+
);
|
|
540
|
+
|
|
541
|
+
const ageToggle = visibleColumnsToggleElements[4];
|
|
542
|
+
await userEvent.click(ageToggle);
|
|
543
|
+
|
|
544
|
+
withinGeneralSettings = within(
|
|
545
|
+
withinDrawerContent.getByTestId(Selectors.SETTINGS_DRAWER.GENERAL_SETTINGS)
|
|
546
|
+
);
|
|
547
|
+
|
|
548
|
+
generalSettingsToggleElements = withinGeneralSettings.getAllByTestId(
|
|
549
|
+
Selectors.SETTINGS_DRAWER.TOGGLE
|
|
550
|
+
);
|
|
551
|
+
await userEvent.click(generalSettingsToggleElements[0]);
|
|
552
|
+
await userEvent.click(generalSettingsToggleElements[1]);
|
|
553
|
+
await userEvent.click(generalSettingsToggleElements[2]);
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
it("should have toggled off general settings", () => {
|
|
557
|
+
generalSettingsToggleElements.forEach((toggleElement) => {
|
|
558
|
+
expect(toggleElement).toHaveAttribute("data-state", "unchecked");
|
|
559
|
+
});
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
describe("When clicking on reset button", () => {
|
|
563
|
+
beforeEach(async () => {
|
|
564
|
+
resetSettingsButton = withinDrawerContent.getByTestId(
|
|
565
|
+
Selectors.SETTINGS_DRAWER.FOOTER_RESET_SETTINGS_BUTTON
|
|
566
|
+
);
|
|
567
|
+
|
|
568
|
+
await userEvent.click(resetSettingsButton);
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
it("should have reset general settings", () => {
|
|
572
|
+
generalSettingsToggleElements.forEach((toggleElement) => {
|
|
573
|
+
expect(toggleElement).toHaveAttribute("data-state", "checked");
|
|
574
|
+
});
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
it("should have reset visible columns", () => {
|
|
578
|
+
visibleColumnsToggleElements.forEach((toggleElement) => {
|
|
579
|
+
expect(toggleElement).toHaveAttribute("data-state", "checked");
|
|
580
|
+
});
|
|
581
|
+
});
|
|
582
|
+
});
|
|
583
|
+
});
|
|
584
|
+
});
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
describe("Paginating table", () => {
|
|
588
|
+
describe("When selecting page 3", () => {
|
|
589
|
+
beforeEach(async () => {
|
|
590
|
+
const page3Button = within(screen.getByTestId(Selectors.PAGINATION.ROOT)).getByText("3");
|
|
591
|
+
|
|
592
|
+
await userEvent.click(page3Button);
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
it("should have 1 rows of data", () => {
|
|
596
|
+
const tableRows = within(getTableBody(table)).getAllByRole("row");
|
|
597
|
+
expect(tableRows).toHaveLength(1);
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
it("should have data in first row", () => {
|
|
601
|
+
const tableRow1 = within(getTableBody(table)).getAllByRole("row")[0];
|
|
602
|
+
|
|
603
|
+
const tableRowCells = within(tableRow1).getAllByRole("cell");
|
|
604
|
+
|
|
605
|
+
const expectedCellsTextContent = [
|
|
606
|
+
"checkbox",
|
|
607
|
+
"23414",
|
|
608
|
+
"Name 52",
|
|
609
|
+
"Link 12",
|
|
610
|
+
"30",
|
|
611
|
+
"Special",
|
|
612
|
+
"11",
|
|
613
|
+
"ClickMe",
|
|
614
|
+
"2025-03-26 11:14",
|
|
615
|
+
"-", // Button group is undefined for this table row
|
|
616
|
+
];
|
|
617
|
+
|
|
618
|
+
tableRowCells.forEach((tableRowCell, index) => {
|
|
619
|
+
if (expectedCellsTextContent[index] === "checkbox") {
|
|
620
|
+
expect(within(tableRowCell).getByRole("checkbox")).toBeInTheDocument();
|
|
621
|
+
} else {
|
|
622
|
+
expect(tableRowCell).toHaveTextContent(expectedCellsTextContent[index]);
|
|
623
|
+
}
|
|
624
|
+
});
|
|
625
|
+
});
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
describe("When changing page size", () => {
|
|
629
|
+
beforeEach(async () => {
|
|
630
|
+
const pageSizeSelector = within(
|
|
631
|
+
screen.getByTestId(Selectors.PAGINATION.ROOT)
|
|
632
|
+
).getByTestId(Selectors.PAGINATION.PAGE_SIZE_SELECT);
|
|
633
|
+
|
|
634
|
+
await userEvent.selectOptions(pageSizeSelector, "10");
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
it("should have 10 rows of data", () => {
|
|
638
|
+
const tableRows = within(getTableBody(table)).getAllByRole("row");
|
|
639
|
+
expect(tableRows).toHaveLength(10);
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
it("should have data in first row", () => {
|
|
643
|
+
const tableRow1 = within(getTableBody(table)).getAllByRole("row")[0];
|
|
644
|
+
|
|
645
|
+
const tableRowCells = within(tableRow1).getAllByRole("cell");
|
|
646
|
+
|
|
647
|
+
const expectedCellsTextContent = [
|
|
648
|
+
"checkbox",
|
|
649
|
+
"12345",
|
|
650
|
+
"Name 1",
|
|
651
|
+
"Link 2",
|
|
652
|
+
"40",
|
|
653
|
+
"Information",
|
|
654
|
+
"1",
|
|
655
|
+
"ClickMe",
|
|
656
|
+
"2025-03-26 11:14",
|
|
657
|
+
[
|
|
658
|
+
{ testId: "download-icon", ariaLabeL: "ClickMe" },
|
|
659
|
+
{ testId: "remove-icon", ariaLabeL: "ClickMe" },
|
|
660
|
+
{ testId: "more-vertical-icon", ariaLabeL: "ClickMe" },
|
|
661
|
+
],
|
|
662
|
+
];
|
|
663
|
+
|
|
664
|
+
tableRowCells.forEach((tableRowCell, index) => {
|
|
665
|
+
if (expectedCellsTextContent[index] === "checkbox") {
|
|
666
|
+
expect(within(tableRowCell).getByRole("checkbox")).toBeInTheDocument();
|
|
667
|
+
} else if (typeof expectedCellsTextContent[index] === "string") {
|
|
668
|
+
expect(tableRowCell).toHaveTextContent(expectedCellsTextContent[index]);
|
|
669
|
+
} else {
|
|
670
|
+
expectedCellsTextContent[index].forEach((expectedElement, buttonIndex) => {
|
|
671
|
+
const button = within(tableRowCell).getAllByRole("button")[buttonIndex];
|
|
672
|
+
const svg = within(button).getByTestId(expectedElement.testId);
|
|
673
|
+
expect(svg).toBeInTheDocument();
|
|
674
|
+
expect(button).toHaveAttribute("aria-label", expectedElement.ariaLabeL);
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
});
|
|
678
|
+
});
|
|
679
|
+
});
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
describe("Select table rows", () => {
|
|
683
|
+
it("should not display batch action bar", () => {
|
|
684
|
+
const checkbox = getCheckBoxForTableHeaderRow(table);
|
|
685
|
+
expect(checkbox).toHaveAttribute("aria-checked", "false");
|
|
686
|
+
const actionBar = screen.queryByTestId(Selectors.ACTION_BAR.ROOT);
|
|
687
|
+
expect(actionBar).not.toBeVisible();
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
it("should display batch action bar when user selects a row", async () => {
|
|
691
|
+
const checkbox = getCheckBoxForTableBodyRow(table, 0);
|
|
692
|
+
expect(checkbox).not.toBeChecked();
|
|
693
|
+
await userEvent.click(checkbox);
|
|
694
|
+
expect(checkbox).toBeChecked();
|
|
695
|
+
|
|
696
|
+
const actionBar = screen.getByTestId(Selectors.ACTION_BAR.ROOT);
|
|
697
|
+
expect(actionBar).toBeVisible();
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
describe("When user selects rows 1, 3 and 4", () => {
|
|
701
|
+
let actionBar: HTMLElement;
|
|
702
|
+
|
|
703
|
+
beforeEach(async () => {
|
|
704
|
+
const checkbox0 = getCheckBoxForTableBodyRow(table, 0);
|
|
705
|
+
await userEvent.click(checkbox0);
|
|
706
|
+
const checkbox2 = getCheckBoxForTableBodyRow(table, 2);
|
|
707
|
+
await userEvent.click(checkbox2);
|
|
708
|
+
const checkbox4 = getCheckBoxForTableBodyRow(table, 4);
|
|
709
|
+
await userEvent.click(checkbox4);
|
|
710
|
+
|
|
711
|
+
actionBar = screen.getByTestId(Selectors.ACTION_BAR.ROOT);
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
it("should display batch action bar with '3 of 11 selected", () => {
|
|
715
|
+
const selectedText = within(actionBar).getByTestId(Selectors.ACTION_BAR.SELECTED_TEXT);
|
|
716
|
+
expect(selectedText).toHaveTextContent("3 of 11 selected");
|
|
717
|
+
});
|
|
718
|
+
|
|
719
|
+
it("should be possible to clear the selection", async () => {
|
|
720
|
+
const cancelButton = within(actionBar).getByTestId(Selectors.ACTION_BAR.CANCEL_BUTTON);
|
|
721
|
+
await userEvent.click(cancelButton);
|
|
722
|
+
expect(actionBar).toHaveStyle("visibility: hidden");
|
|
723
|
+
});
|
|
724
|
+
|
|
725
|
+
it("should display 'indeterminate' state on header checkbox", () => {
|
|
726
|
+
const checkbox = getCheckBoxForTableHeaderRow(table);
|
|
727
|
+
expect(checkbox).toHaveAttribute("aria-checked", "mixed");
|
|
728
|
+
});
|
|
729
|
+
|
|
730
|
+
it("should send 'onPrimaryButtonClick' event when clicking on primary button", async () => {
|
|
731
|
+
const primaryButton = within(actionBar).getByTestId(Selectors.ACTION_BAR.PRIMARY_BUTTON);
|
|
732
|
+
await userEvent.click(primaryButton);
|
|
733
|
+
expect(onPrimaryButtonClickMock).toHaveBeenCalled();
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
describe("When user toggles show only selected rows", () => {
|
|
737
|
+
beforeEach(async () => {
|
|
738
|
+
const toggleSelected = within(actionBar).getByTestId(
|
|
739
|
+
Selectors.ACTION_BAR.TOGGLE_SELECTED
|
|
740
|
+
);
|
|
741
|
+
await userEvent.click(toggleSelected);
|
|
742
|
+
});
|
|
743
|
+
|
|
744
|
+
it("should only display selected rows", () => {
|
|
745
|
+
const tableRows = within(getTableBody(table)).getAllByRole("row");
|
|
746
|
+
expect(tableRows).toHaveLength(3);
|
|
747
|
+
});
|
|
748
|
+
|
|
749
|
+
it("should display 'Showing 3 of 3 rows'", () => {
|
|
750
|
+
const tableRowsCount = screen.getByTestId(Selectors.TOOLBAR.TABLE_ROWS_COUNT);
|
|
751
|
+
expect(tableRowsCount).toHaveTextContent("Showing 3 of 3 rows");
|
|
752
|
+
});
|
|
753
|
+
});
|
|
754
|
+
});
|
|
755
|
+
|
|
756
|
+
describe("When user selects header checkbox", () => {
|
|
757
|
+
let actionBar: HTMLElement;
|
|
758
|
+
|
|
759
|
+
beforeEach(async () => {
|
|
760
|
+
const checkbox = getCheckBoxForTableHeaderRow(table);
|
|
761
|
+
await userEvent.click(checkbox);
|
|
762
|
+
|
|
763
|
+
actionBar = screen.getByTestId(Selectors.ACTION_BAR.ROOT);
|
|
764
|
+
});
|
|
765
|
+
|
|
766
|
+
it("should display batch action bar with '5 of 11 selected'", () => {
|
|
767
|
+
// Checking select all checkbox only selects the rows for the page
|
|
768
|
+
const selectedText = within(actionBar).getByTestId(Selectors.ACTION_BAR.SELECTED_TEXT);
|
|
769
|
+
expect(selectedText).toHaveTextContent("5 of 11 selected");
|
|
770
|
+
});
|
|
771
|
+
});
|
|
772
|
+
});
|
|
773
|
+
|
|
774
|
+
describe("Table Actions", () => {
|
|
775
|
+
describe("Button", () => {
|
|
776
|
+
it("should send an event with correct values when clicking on button", async () => {
|
|
777
|
+
const cell = getCellInRowContent(table, 0, "Action");
|
|
778
|
+
await userEvent.click(within(cell).getByRole("button"));
|
|
779
|
+
|
|
780
|
+
expect(clickCellButtonMock).toHaveBeenCalled();
|
|
781
|
+
|
|
782
|
+
const lastCalledWith = clickCellButtonMock.mock.calls[0][0];
|
|
783
|
+
expect(lastCalledWith).toMatchObject({
|
|
784
|
+
id: "12345_button",
|
|
785
|
+
row: {
|
|
786
|
+
id: "12345",
|
|
787
|
+
index: 0,
|
|
788
|
+
original: tableDataLarge[0],
|
|
789
|
+
},
|
|
790
|
+
});
|
|
791
|
+
});
|
|
792
|
+
});
|
|
793
|
+
|
|
794
|
+
describe("Button groups", () => {
|
|
795
|
+
let buttons: HTMLButtonElement[];
|
|
796
|
+
|
|
797
|
+
beforeEach(() => {
|
|
798
|
+
clickButtonGroupCellButtonMock.mockClear();
|
|
799
|
+
const cell = getCellInRowContent(table, 0, "Buttons");
|
|
800
|
+
buttons = within(cell).getAllByRole("button");
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
it("click on button 1", async () => {
|
|
804
|
+
const button = buttons[0];
|
|
805
|
+
await userEvent.click(button);
|
|
806
|
+
|
|
807
|
+
expect(clickButtonGroupCellButtonMock).toHaveBeenCalled();
|
|
808
|
+
|
|
809
|
+
const lastCalledWith = clickButtonGroupCellButtonMock.mock.calls[0][0];
|
|
810
|
+
expect(lastCalledWith).toMatchObject({
|
|
811
|
+
id: "12345_buttonGroup",
|
|
812
|
+
buttonId: "12345_buttonGroup_button-1",
|
|
813
|
+
row: {
|
|
814
|
+
id: "12345",
|
|
815
|
+
index: 0,
|
|
816
|
+
original: tableDataLarge[0],
|
|
817
|
+
},
|
|
818
|
+
});
|
|
819
|
+
});
|
|
820
|
+
|
|
821
|
+
it("click on button 2", async () => {
|
|
822
|
+
const button = buttons[1];
|
|
823
|
+
await userEvent.click(button);
|
|
824
|
+
|
|
825
|
+
expect(clickButtonGroupCellButtonMock).toHaveBeenCalled();
|
|
826
|
+
|
|
827
|
+
const lastCalledWith = clickButtonGroupCellButtonMock.mock.calls[0][0];
|
|
828
|
+
expect(lastCalledWith).toMatchObject({
|
|
829
|
+
id: "12345_buttonGroup",
|
|
830
|
+
buttonId: "12345_buttonGroup_button-2",
|
|
831
|
+
row: {
|
|
832
|
+
id: "12345",
|
|
833
|
+
index: 0,
|
|
834
|
+
original: tableDataLarge[0],
|
|
835
|
+
},
|
|
836
|
+
});
|
|
837
|
+
});
|
|
838
|
+
|
|
839
|
+
it("click on button 3", async () => {
|
|
840
|
+
const button = buttons[2];
|
|
841
|
+
await userEvent.click(button);
|
|
842
|
+
|
|
843
|
+
expect(clickButtonGroupCellButtonMock).toHaveBeenCalled();
|
|
844
|
+
|
|
845
|
+
const lastCalledWith = clickButtonGroupCellButtonMock.mock.calls[0][0];
|
|
846
|
+
expect(lastCalledWith).toMatchObject({
|
|
847
|
+
id: "12345_buttonGroup",
|
|
848
|
+
buttonId: "12345_buttonGroup_button-3",
|
|
849
|
+
row: {
|
|
850
|
+
id: "12345",
|
|
851
|
+
index: 0,
|
|
852
|
+
original: tableDataLarge[0],
|
|
853
|
+
},
|
|
854
|
+
});
|
|
855
|
+
});
|
|
856
|
+
});
|
|
857
|
+
});
|
|
858
|
+
|
|
859
|
+
// TODO: Skipping this test since its failing on multiple issues, see Violations at bottom of file.
|
|
860
|
+
describe.skip("Accessibility", () => {
|
|
861
|
+
it("is accessible", async () => {
|
|
862
|
+
const { container } = render(<TestComponent enableMultiRowSelection={true} />);
|
|
863
|
+
const results = await axe(container, { elementRef: true });
|
|
864
|
+
|
|
865
|
+
const buttonNameViolation = results.violations.find(
|
|
866
|
+
(violation) => violation.id === "button-name"
|
|
867
|
+
);
|
|
868
|
+
|
|
869
|
+
// "Buttons must have discernible text (button-name)"
|
|
870
|
+
// This is rule is failing on the toggles and checkboxes due to them being buttons with roles instead of input elements
|
|
871
|
+
if (buttonNameViolation) {
|
|
872
|
+
buttonNameViolation.nodes = buttonNameViolation.nodes.filter(
|
|
873
|
+
(node) =>
|
|
874
|
+
!node.element?.matches('[role="switch"]') ||
|
|
875
|
+
!node.element?.matches('[role="checkbox"]')
|
|
876
|
+
);
|
|
877
|
+
|
|
878
|
+
// remove the violation if there are no more nodes
|
|
879
|
+
if (!buttonNameViolation.nodes.length) {
|
|
880
|
+
results.violations.splice(results.violations.indexOf(buttonNameViolation), 1);
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
// console.log("Violations", results.violations[2].nodes[0]);
|
|
885
|
+
|
|
886
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
887
|
+
// @ts-ignore
|
|
888
|
+
// expect(results).toHaveNoViolations();
|
|
889
|
+
expect(results.violations.length).toBe(0);
|
|
890
|
+
});
|
|
891
|
+
});
|
|
892
|
+
});
|
|
893
|
+
|
|
894
|
+
describe("With row toggle", () => {
|
|
895
|
+
let table: HTMLTableElement;
|
|
896
|
+
|
|
897
|
+
beforeEach(async () => {
|
|
898
|
+
render(<TestComponent enableMultiRowSelection={false} />);
|
|
899
|
+
|
|
900
|
+
table = screen.getByRole("table");
|
|
901
|
+
});
|
|
902
|
+
|
|
903
|
+
afterEach(() => {
|
|
904
|
+
cleanup();
|
|
905
|
+
});
|
|
906
|
+
|
|
907
|
+
it("should have 10 headers", () => {
|
|
908
|
+
const expectedHeaders = [
|
|
909
|
+
"rowSelectionAriaLabels header",
|
|
910
|
+
"ID",
|
|
911
|
+
"Name",
|
|
912
|
+
"Link",
|
|
913
|
+
"Age",
|
|
914
|
+
"Badge",
|
|
915
|
+
"Position",
|
|
916
|
+
"Action",
|
|
917
|
+
"Date",
|
|
918
|
+
"Buttons",
|
|
919
|
+
];
|
|
920
|
+
|
|
921
|
+
const headers = within(getTableHead(table)).getAllByRole("columnheader");
|
|
922
|
+
expect(headers).toHaveLength(expectedHeaders.length);
|
|
923
|
+
|
|
924
|
+
expectedHeaders.forEach((expectedHeader, index) => {
|
|
925
|
+
expect(headers[index]).toHaveTextContent(expectedHeader);
|
|
926
|
+
});
|
|
927
|
+
});
|
|
928
|
+
|
|
929
|
+
it("should have 5 rows of data", () => {
|
|
930
|
+
const tableRows = within(getTableBody(table)).getAllByRole("row");
|
|
931
|
+
expect(tableRows).toHaveLength(5);
|
|
932
|
+
});
|
|
933
|
+
|
|
934
|
+
it("should have data in first row", () => {
|
|
935
|
+
const tableRow1 = within(getTableBody(table)).getAllByRole("row")[0];
|
|
936
|
+
|
|
937
|
+
const tableRowCells = within(tableRow1).getAllByRole("cell");
|
|
938
|
+
|
|
939
|
+
const expectedCellsTextContent = [
|
|
940
|
+
"",
|
|
941
|
+
"12345",
|
|
942
|
+
"Name 1",
|
|
943
|
+
"Link 2",
|
|
944
|
+
"40",
|
|
945
|
+
"Information",
|
|
946
|
+
"1",
|
|
947
|
+
"ClickMe",
|
|
948
|
+
"2025-03-26 11:14",
|
|
949
|
+
[
|
|
950
|
+
{ testId: "download-icon", ariaLabeL: "ClickMe" },
|
|
951
|
+
{ testId: "remove-icon", ariaLabeL: "ClickMe" },
|
|
952
|
+
{ testId: "more-vertical-icon", ariaLabeL: "ClickMe" },
|
|
953
|
+
],
|
|
954
|
+
];
|
|
955
|
+
|
|
956
|
+
tableRowCells.forEach((tableRowCell, index) => {
|
|
957
|
+
if (typeof expectedCellsTextContent[index] === "string") {
|
|
958
|
+
expect(tableRowCell).toHaveTextContent(expectedCellsTextContent[index]);
|
|
959
|
+
} else {
|
|
960
|
+
expectedCellsTextContent[index].forEach((expectedElement, buttonIndex) => {
|
|
961
|
+
const button = within(tableRowCell).getAllByRole("button")[buttonIndex];
|
|
962
|
+
const svg = within(button).getByTestId(expectedElement.testId);
|
|
963
|
+
expect(svg).toBeInTheDocument();
|
|
964
|
+
expect(button).toHaveAttribute("aria-label", expectedElement.ariaLabeL);
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
});
|
|
968
|
+
});
|
|
969
|
+
|
|
970
|
+
describe("Select table rows", () => {
|
|
971
|
+
let toggleRow0: HTMLButtonElement;
|
|
972
|
+
|
|
973
|
+
beforeEach(async () => {
|
|
974
|
+
toggleRow0 = getRadioToggleForTableBodyRow(table, 0);
|
|
975
|
+
await userEvent.click(toggleRow0);
|
|
976
|
+
});
|
|
977
|
+
|
|
978
|
+
it("should not display batch action bar when user toggles a row", async () => {
|
|
979
|
+
expect(toggleRow0).toBeChecked();
|
|
980
|
+
|
|
981
|
+
const actionBar = screen.queryByTestId(Selectors.ACTION_BAR.ROOT);
|
|
982
|
+
expect(actionBar).not.toBeInTheDocument();
|
|
983
|
+
});
|
|
984
|
+
|
|
985
|
+
it("should only have 1 checked row", async () => {
|
|
986
|
+
const toggleRow3 = getRadioToggleForTableBodyRow(table, 2);
|
|
987
|
+
await userEvent.click(toggleRow3);
|
|
988
|
+
|
|
989
|
+
expect(toggleRow0).not.toBeChecked();
|
|
990
|
+
expect(toggleRow3).toBeChecked();
|
|
991
|
+
});
|
|
992
|
+
});
|
|
993
|
+
});
|
|
994
|
+
|
|
995
|
+
// TODO: Impement this around certain sections of the table
|
|
996
|
+
describe.skip("Accessibility", () => {
|
|
997
|
+
it("is accessible", async () => {
|
|
998
|
+
const { container } = render(<TestComponent enableMultiRowSelection={true} />);
|
|
999
|
+
const results = await axe(container, { elementRef: true });
|
|
1000
|
+
|
|
1001
|
+
const buttonNameViolation = results.violations.find(
|
|
1002
|
+
(violation) => violation.id === "button-name"
|
|
1003
|
+
);
|
|
1004
|
+
|
|
1005
|
+
// "Buttons must have discernible text (button-name)"
|
|
1006
|
+
// This is rule is failing on the toggles and checkboxes due to them being buttons with roles instead of input elements
|
|
1007
|
+
if (buttonNameViolation) {
|
|
1008
|
+
buttonNameViolation.nodes = buttonNameViolation.nodes.filter(
|
|
1009
|
+
(node) =>
|
|
1010
|
+
!node.element?.matches('[role="switch"]') || !node.element?.matches('[role="checkbox"]')
|
|
1011
|
+
);
|
|
1012
|
+
|
|
1013
|
+
// remove the violation if there are no more nodes
|
|
1014
|
+
if (!buttonNameViolation.nodes.length) {
|
|
1015
|
+
results.violations.splice(results.violations.indexOf(buttonNameViolation), 1);
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
// console.log("Violations", results.violations[2].nodes[0]);
|
|
1020
|
+
|
|
1021
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
1022
|
+
// @ts-ignore
|
|
1023
|
+
// expect(results).toHaveNoViolations();
|
|
1024
|
+
expect(results.violations.length).toBe(0);
|
|
1025
|
+
});
|
|
1026
|
+
});
|
|
1027
|
+
});
|
|
1028
|
+
|
|
1029
|
+
describe("With row toggle", () => {
|
|
1030
|
+
let table: HTMLTableElement;
|
|
1031
|
+
|
|
1032
|
+
beforeEach(async () => {
|
|
1033
|
+
render(<TestComponent enableMultiRowSelection={false} />);
|
|
1034
|
+
|
|
1035
|
+
table = screen.getByRole("table");
|
|
1036
|
+
});
|
|
1037
|
+
|
|
1038
|
+
afterEach(() => {
|
|
1039
|
+
cleanup();
|
|
1040
|
+
});
|
|
1041
|
+
|
|
1042
|
+
it("should have 10 headers", () => {
|
|
1043
|
+
const expectedHeaders = [
|
|
1044
|
+
"rowSelectionAriaLabels header",
|
|
1045
|
+
"ID",
|
|
1046
|
+
"Name",
|
|
1047
|
+
"Link",
|
|
1048
|
+
"Age",
|
|
1049
|
+
"Badge",
|
|
1050
|
+
"Position",
|
|
1051
|
+
"Action",
|
|
1052
|
+
"Date",
|
|
1053
|
+
"Buttons",
|
|
1054
|
+
];
|
|
1055
|
+
|
|
1056
|
+
const headers = within(getTableHead(table)).getAllByRole("columnheader");
|
|
1057
|
+
expect(headers).toHaveLength(expectedHeaders.length);
|
|
1058
|
+
|
|
1059
|
+
expectedHeaders.forEach((expectedHeader, index) => {
|
|
1060
|
+
expect(headers[index]).toHaveTextContent(expectedHeader);
|
|
1061
|
+
});
|
|
1062
|
+
});
|
|
1063
|
+
|
|
1064
|
+
it("should have 5 rows of data", () => {
|
|
1065
|
+
const tableRows = within(getTableBody(table)).getAllByRole("row");
|
|
1066
|
+
expect(tableRows).toHaveLength(5);
|
|
1067
|
+
});
|
|
1068
|
+
|
|
1069
|
+
it("should have data in first row", () => {
|
|
1070
|
+
const tableRow1 = within(getTableBody(table)).getAllByRole("row")[0];
|
|
1071
|
+
|
|
1072
|
+
const tableRowCells = within(tableRow1).getAllByRole("cell");
|
|
1073
|
+
|
|
1074
|
+
const expectedCellsTextContent = [
|
|
1075
|
+
"",
|
|
1076
|
+
"12345",
|
|
1077
|
+
"Name 1",
|
|
1078
|
+
"Link 2",
|
|
1079
|
+
"40",
|
|
1080
|
+
"Information",
|
|
1081
|
+
"1",
|
|
1082
|
+
"ClickMe",
|
|
1083
|
+
"2025-03-26 11:14",
|
|
1084
|
+
[
|
|
1085
|
+
{ testId: "download-icon", ariaLabeL: "ClickMe" },
|
|
1086
|
+
{ testId: "remove-icon", ariaLabeL: "ClickMe" },
|
|
1087
|
+
{ testId: "more-vertical-icon", ariaLabeL: "ClickMe" },
|
|
1088
|
+
],
|
|
1089
|
+
];
|
|
1090
|
+
|
|
1091
|
+
tableRowCells.forEach((tableRowCell, index) => {
|
|
1092
|
+
if (typeof expectedCellsTextContent[index] === "string") {
|
|
1093
|
+
expect(tableRowCell).toHaveTextContent(expectedCellsTextContent[index]);
|
|
1094
|
+
} else {
|
|
1095
|
+
expectedCellsTextContent[index].forEach((expectedElement, buttonIndex) => {
|
|
1096
|
+
const button = within(tableRowCell).getAllByRole("button")[buttonIndex];
|
|
1097
|
+
const svg = within(button).getByTestId(expectedElement.testId);
|
|
1098
|
+
expect(svg).toBeInTheDocument();
|
|
1099
|
+
expect(button).toHaveAttribute("aria-label", expectedElement.ariaLabeL);
|
|
1100
|
+
});
|
|
1101
|
+
}
|
|
1102
|
+
});
|
|
1103
|
+
});
|
|
1104
|
+
|
|
1105
|
+
describe("Select table rows", () => {
|
|
1106
|
+
let toggleRow0: HTMLButtonElement;
|
|
1107
|
+
|
|
1108
|
+
beforeEach(async () => {
|
|
1109
|
+
toggleRow0 = getRadioToggleForTableBodyRow(table, 0);
|
|
1110
|
+
await userEvent.click(toggleRow0);
|
|
1111
|
+
});
|
|
1112
|
+
|
|
1113
|
+
it("should not display batch action bar when user toggles a row", async () => {
|
|
1114
|
+
expect(toggleRow0).toBeChecked();
|
|
1115
|
+
|
|
1116
|
+
const actionBar = screen.queryByTestId(Selectors.ACTION_BAR.ROOT);
|
|
1117
|
+
expect(actionBar).not.toBeInTheDocument();
|
|
1118
|
+
});
|
|
1119
|
+
|
|
1120
|
+
it("should only have 1 checked row", async () => {
|
|
1121
|
+
const toggleRow3 = getRadioToggleForTableBodyRow(table, 2);
|
|
1122
|
+
await userEvent.click(toggleRow3);
|
|
1123
|
+
|
|
1124
|
+
expect(toggleRow0).not.toBeChecked();
|
|
1125
|
+
expect(toggleRow3).toBeChecked();
|
|
1126
|
+
});
|
|
1127
|
+
});
|
|
1128
|
+
});
|
|
1129
|
+
|
|
1130
|
+
// TODO: Impement this around certain sections of the table
|
|
1131
|
+
describe.skip("Accessibility", () => {
|
|
1132
|
+
it("is accessible", async () => {
|
|
1133
|
+
const { container } = render(<TestComponent enableMultiRowSelection={false} />);
|
|
1134
|
+
const results = await axe(container);
|
|
1135
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
1136
|
+
// @ts-ignore TODO fix this
|
|
1137
|
+
expect(results).toHaveNoViolations();
|
|
1138
|
+
});
|
|
1139
|
+
});
|
|
1140
|
+
|
|
1141
|
+
// Violations [
|
|
1142
|
+
// {
|
|
1143
|
+
// id: 'aria-allowed-role',
|
|
1144
|
+
// impact: 'minor',
|
|
1145
|
+
// tags: [ 'cat.aria', 'best-practice' ],
|
|
1146
|
+
// description: 'Ensure role attribute has an appropriate value for the element',
|
|
1147
|
+
// help: 'ARIA role should be appropriate for the element',
|
|
1148
|
+
// helpUrl: 'https://dequeuniversity.com/rules/axe/4.10/aria-allowed-role?application=axeAPI',
|
|
1149
|
+
// nodes: [ [Object] ]
|
|
1150
|
+
// html: <input aria-autocomplete="list" aria-controls=":r1a:-listbox" autocomplete="off" id=":r1a:-input" role="combobox" min="1" max="3" data-testid="purpur-pagination-page-selector-autocomplete-input-input" aria-invalid="false" class="_purpur-text-field__input_nj9f4_27" type="number" value="1"></input>
|
|
1151
|
+
// },
|
|
1152
|
+
// {
|
|
1153
|
+
// id: 'aria-required-attr',
|
|
1154
|
+
// impact: 'critical',
|
|
1155
|
+
// // should be no space between 9.4 and .1.2 but it fails @telia/pii/no-ip
|
|
1156
|
+
// tags: [ 'cat.aria', 'wcag2a', 'wcag412', 'EN-301-549', 'EN-9.4 .1.2' ],
|
|
1157
|
+
// description: 'Ensure elements with ARIA roles have all required ARIA attributes',
|
|
1158
|
+
// help: 'Required ARIA attributes must be provided',
|
|
1159
|
+
// helpUrl: 'https://dequeuniversity.com/rules/axe/4.10/aria-required-attr?application=axeAPI',
|
|
1160
|
+
// nodes: [ [Object] ]
|
|
1161
|
+
// html: <input aria-autocomplete="list" aria-controls=":r1a:-listbox" autocomplete="off" id=":r1a:-input" role="combobox" min="1" max="3" data-testid="purpur-pagination-page-selector-autocomplete-input-input" aria-invalid="false" class="_purpur-text-field__input_nj9f4_27" type="number" value="1">
|
|
1162
|
+
// },
|
|
1163
|
+
// {
|
|
1164
|
+
// id: 'aria-valid-attr-value',
|
|
1165
|
+
// impact: 'critical',
|
|
1166
|
+
// // should be no space between 9.4 and .1.2 but it fails @telia/pii/no-ip
|
|
1167
|
+
// tags: [ 'cat.aria', 'wcag2a', 'wcag412', 'EN-301-549', 'EN-9.4 .1.2' ],
|
|
1168
|
+
// description: 'Ensure all ARIA attributes have valid values',
|
|
1169
|
+
// help: 'ARIA attributes must conform to valid values',
|
|
1170
|
+
// helpUrl: 'https://dequeuniversity.com/rules/axe/4.10/aria-valid-attr-value?application=axeAPI',
|
|
1171
|
+
// nodes: [ [Object] ]
|
|
1172
|
+
// html:<input aria-autocomplete="list" aria-controls=":r1a:-listbox" autocomplete="off" id=":r1a:-input" role="combobox" min="1" max="3" data-testid="purpur-pagination-page-selector-autocomplete-input-input" aria-invalid="false" class="_purpur-text-field__input_nj9f4_27" type="number" value="1">
|
|
1173
|
+
// },
|
|
1174
|
+
// {
|
|
1175
|
+
// id: 'label',
|
|
1176
|
+
// impact: 'critical',
|
|
1177
|
+
// tags: [
|
|
1178
|
+
// 'cat.forms',
|
|
1179
|
+
// 'wcag2a',
|
|
1180
|
+
// 'wcag412',
|
|
1181
|
+
// 'section508',
|
|
1182
|
+
// 'section508.22.n',
|
|
1183
|
+
// 'TTv5',
|
|
1184
|
+
// 'TT5.c',
|
|
1185
|
+
// 'EN-301-549',
|
|
1186
|
+
// // should be no space between 9.4 and .1.2 but it fails @telia/pii/no-ip
|
|
1187
|
+
// 'EN-9.4 .1.2',
|
|
1188
|
+
// 'ACT'
|
|
1189
|
+
// ],
|
|
1190
|
+
// description: 'Ensure every form element has a label',
|
|
1191
|
+
// help: 'Form elements must have labels',
|
|
1192
|
+
// helpUrl: 'https://dequeuniversity.com/rules/axe/4.10/label?application=axeAPI',
|
|
1193
|
+
// nodes: [ [Object] ]
|
|
1194
|
+
// html:<input aria-autocomplete="list" aria-controls=":r1a:-listbox" autocomplete="off" id=":r1a:-input" role="combobox" min="1" max="3" data-testid="purpur-pagination-page-selector-autocomplete-input-input" aria-invalid="false" class="_purpur-text-field__input_nj9f4_27" type="number" value="1">
|
|
1195
|
+
// }
|
|
1196
|
+
// ]
|