@arbor-education/design-system.components 0.1.0 → 0.1.2

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.
Files changed (120) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/components/button/Button.d.ts +1 -0
  3. package/dist/components/button/Button.d.ts.map +1 -1
  4. package/dist/components/button/Button.js +2 -1
  5. package/dist/components/button/Button.js.map +1 -1
  6. package/dist/components/formField/inputs/number/NumberInput.d.ts +2 -0
  7. package/dist/components/formField/inputs/number/NumberInput.d.ts.map +1 -1
  8. package/dist/components/formField/inputs/number/NumberInput.js +9 -4
  9. package/dist/components/formField/inputs/number/NumberInput.js.map +1 -1
  10. package/dist/components/table/BulkActionsDropdown.js +2 -2
  11. package/dist/components/table/BulkActionsDropdown.js.map +1 -1
  12. package/dist/components/table/GridApiContext.d.ts +1 -2
  13. package/dist/components/table/GridApiContext.d.ts.map +1 -1
  14. package/dist/components/table/GridApiContext.js +1 -1
  15. package/dist/components/table/GridApiContext.js.map +1 -1
  16. package/dist/components/table/Table.d.ts +9 -1
  17. package/dist/components/table/Table.d.ts.map +1 -1
  18. package/dist/components/table/Table.js +13 -5
  19. package/dist/components/table/Table.js.map +1 -1
  20. package/dist/components/table/Table.stories.d.ts +5 -1
  21. package/dist/components/table/Table.stories.d.ts.map +1 -1
  22. package/dist/components/table/Table.stories.js +36 -1
  23. package/dist/components/table/Table.stories.js.map +1 -1
  24. package/dist/components/table/Table.test.js +2 -0
  25. package/dist/components/table/Table.test.js.map +1 -1
  26. package/dist/components/table/pagination/PageSizeSelector.d.ts +7 -0
  27. package/dist/components/table/pagination/PageSizeSelector.d.ts.map +1 -0
  28. package/dist/components/table/pagination/PageSizeSelector.js +46 -0
  29. package/dist/components/table/pagination/PageSizeSelector.js.map +1 -0
  30. package/dist/components/table/pagination/Pagination.test.d.ts +2 -0
  31. package/dist/components/table/pagination/Pagination.test.d.ts.map +1 -0
  32. package/dist/components/table/pagination/Pagination.test.js +616 -0
  33. package/dist/components/table/pagination/Pagination.test.js.map +1 -0
  34. package/dist/components/table/pagination/PaginationControls.d.ts +6 -0
  35. package/dist/components/table/pagination/PaginationControls.d.ts.map +1 -0
  36. package/dist/components/table/pagination/PaginationControls.js +47 -0
  37. package/dist/components/table/pagination/PaginationControls.js.map +1 -0
  38. package/dist/components/table/pagination/PaginationPanel.d.ts +9 -0
  39. package/dist/components/table/pagination/PaginationPanel.d.ts.map +1 -0
  40. package/dist/components/table/pagination/PaginationPanel.js +11 -0
  41. package/dist/components/table/pagination/PaginationPanel.js.map +1 -0
  42. package/dist/components/table/pagination/RowCountInfo.d.ts +5 -0
  43. package/dist/components/table/pagination/RowCountInfo.d.ts.map +1 -0
  44. package/dist/components/table/pagination/RowCountInfo.js +53 -0
  45. package/dist/components/table/pagination/RowCountInfo.js.map +1 -0
  46. package/dist/components/tooltip/Tooltip.d.ts +7 -0
  47. package/dist/components/tooltip/Tooltip.d.ts.map +1 -0
  48. package/dist/components/tooltip/Tooltip.js +11 -0
  49. package/dist/components/tooltip/Tooltip.js.map +1 -0
  50. package/dist/components/tooltip/Tooltip.stories.d.ts +10 -0
  51. package/dist/components/tooltip/Tooltip.stories.d.ts.map +1 -0
  52. package/dist/components/tooltip/Tooltip.stories.js +24 -0
  53. package/dist/components/tooltip/Tooltip.stories.js.map +1 -0
  54. package/dist/components/tooltip/Tooltip.test.d.ts +2 -0
  55. package/dist/components/tooltip/Tooltip.test.d.ts.map +1 -0
  56. package/dist/components/tooltip/Tooltip.test.js +23 -0
  57. package/dist/components/tooltip/Tooltip.test.js.map +1 -0
  58. package/dist/components/tooltip/TooltipContent.d.ts +8 -0
  59. package/dist/components/tooltip/TooltipContent.d.ts.map +1 -0
  60. package/dist/components/tooltip/TooltipContent.js +11 -0
  61. package/dist/components/tooltip/TooltipContent.js.map +1 -0
  62. package/dist/components/tooltip/TooltipTrigger.d.ts +3 -0
  63. package/dist/components/tooltip/TooltipTrigger.d.ts.map +1 -0
  64. package/dist/components/tooltip/TooltipTrigger.js +8 -0
  65. package/dist/components/tooltip/TooltipTrigger.js.map +1 -0
  66. package/dist/components/tooltip/TooltipWrapper.d.ts +11 -0
  67. package/dist/components/tooltip/TooltipWrapper.d.ts.map +1 -0
  68. package/dist/components/tooltip/TooltipWrapper.js +8 -0
  69. package/dist/components/tooltip/TooltipWrapper.js.map +1 -0
  70. package/dist/components/tooltip/TooltipWrapper.stories.d.ts +11 -0
  71. package/dist/components/tooltip/TooltipWrapper.stories.d.ts.map +1 -0
  72. package/dist/components/tooltip/TooltipWrapper.stories.js +23 -0
  73. package/dist/components/tooltip/TooltipWrapper.stories.js.map +1 -0
  74. package/dist/components/tooltip/TooltipWrapper.test.d.ts +2 -0
  75. package/dist/components/tooltip/TooltipWrapper.test.d.ts.map +1 -0
  76. package/dist/components/tooltip/TooltipWrapper.test.js +42 -0
  77. package/dist/components/tooltip/TooltipWrapper.test.js.map +1 -0
  78. package/dist/index.css +41 -0
  79. package/dist/index.css.map +1 -1
  80. package/dist/index.d.ts +2 -0
  81. package/dist/index.d.ts.map +1 -1
  82. package/dist/index.js +2 -0
  83. package/dist/index.js.map +1 -1
  84. package/dist/utils/hooks/useGridApi.d.ts +5 -0
  85. package/dist/utils/hooks/useGridApi.d.ts.map +1 -0
  86. package/dist/utils/hooks/useGridApi.js +13 -0
  87. package/dist/utils/hooks/useGridApi.js.map +1 -0
  88. package/dist/utils/hooks/useIsMounted.d.ts +2 -0
  89. package/dist/utils/hooks/useIsMounted.d.ts.map +1 -0
  90. package/dist/utils/hooks/useIsMounted.js +12 -0
  91. package/dist/utils/hooks/useIsMounted.js.map +1 -0
  92. package/package.json +1 -1
  93. package/release/design-system.components.tgz +0 -0
  94. package/src/components/button/Button.tsx +3 -0
  95. package/src/components/button/button.scss +4 -0
  96. package/src/components/formField/inputs/number/NumberInput.tsx +39 -24
  97. package/src/components/table/BulkActionsDropdown.tsx +2 -2
  98. package/src/components/table/GridApiContext.ts +2 -2
  99. package/src/components/table/Table.stories.tsx +64 -2
  100. package/src/components/table/Table.test.tsx +2 -0
  101. package/src/components/table/Table.tsx +14 -4
  102. package/src/components/table/pagination/PageSizeSelector.tsx +73 -0
  103. package/src/components/table/pagination/Pagination.test.tsx +846 -0
  104. package/src/components/table/pagination/PaginationControls.tsx +116 -0
  105. package/src/components/table/pagination/PaginationPanel.tsx +30 -0
  106. package/src/components/table/pagination/RowCountInfo.tsx +67 -0
  107. package/src/components/table/pagination/pagination.scss +26 -0
  108. package/src/components/tooltip/Tooltip.stories.tsx +35 -0
  109. package/src/components/tooltip/Tooltip.test.tsx +44 -0
  110. package/src/components/tooltip/Tooltip.tsx +18 -0
  111. package/src/components/tooltip/TooltipContent.tsx +40 -0
  112. package/src/components/tooltip/TooltipTrigger.tsx +11 -0
  113. package/src/components/tooltip/TooltipWrapper.stories.tsx +24 -0
  114. package/src/components/tooltip/TooltipWrapper.test.tsx +100 -0
  115. package/src/components/tooltip/TooltipWrapper.tsx +32 -0
  116. package/src/components/tooltip/tooltip.scss +15 -0
  117. package/src/index.scss +2 -0
  118. package/src/index.ts +2 -0
  119. package/src/utils/hooks/useGridApi.ts +15 -0
  120. package/src/utils/hooks/useIsMounted.ts +12 -0
@@ -0,0 +1,616 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { describe, expect, test, vi, beforeEach } from 'vitest';
3
+ import { render, screen, waitFor } from '@testing-library/react';
4
+ import React from 'react';
5
+ import '@testing-library/jest-dom/vitest';
6
+ import userEvent from '@testing-library/user-event';
7
+ import { PaginationControls } from './PaginationControls';
8
+ import { RowCountInfo } from './RowCountInfo';
9
+ import { PageSizeSelector } from './PageSizeSelector';
10
+ import { PaginationPanel } from './PaginationPanel';
11
+ import { GridApiContext } from '../GridApiContext';
12
+ describe('Table Pagination', () => {
13
+ const createMockGridApi = (overrides) => {
14
+ const mockListeners = {
15
+ paginationChanged: [],
16
+ filterChanged: [],
17
+ rowDataUpdated: [],
18
+ };
19
+ const result = {
20
+ getDisplayedRowCount: vi.fn(() => 10),
21
+ paginationGetPageSize: vi.fn(() => 10),
22
+ paginationGetCurrentPage: vi.fn(() => 0),
23
+ paginationGetTotalPages: vi.fn(() => 1),
24
+ paginationGetRowCount: vi.fn(() => 10),
25
+ paginationGoToPage: vi.fn(),
26
+ addEventListener: vi.fn((event, callback) => {
27
+ if (!mockListeners[event]) {
28
+ mockListeners[event] = [];
29
+ }
30
+ mockListeners[event].push(callback);
31
+ }),
32
+ removeEventListener: vi.fn(),
33
+ dispatchEvent: (evtName) => {
34
+ mockListeners[evtName]?.forEach((callback) => {
35
+ callback();
36
+ });
37
+ },
38
+ ...overrides,
39
+ };
40
+ return result;
41
+ };
42
+ describe('PaginationControls', () => {
43
+ const renderWithContext = (component, gridApi = null) => {
44
+ return render(_jsx(GridApiContext.Provider, { value: gridApi, children: component }));
45
+ };
46
+ test('renders pagination controls container', () => {
47
+ const { container } = renderWithContext(_jsx(PaginationControls, { totalPages: 5 }));
48
+ expect(container.querySelector('.ds-table__pagination-controls')).toBeInTheDocument();
49
+ });
50
+ test('renders all navigation buttons', () => {
51
+ renderWithContext(_jsx(PaginationControls, { totalPages: 5 }));
52
+ expect(screen.getByText('Go to first page')).toBeInTheDocument();
53
+ expect(screen.getByText('Go to previous page')).toBeInTheDocument();
54
+ expect(screen.getByText('Go to next page')).toBeInTheDocument();
55
+ expect(screen.getByText('Go to last page')).toBeInTheDocument();
56
+ });
57
+ test('renders number input for page navigation', () => {
58
+ renderWithContext(_jsx(PaginationControls, { totalPages: 5 }));
59
+ const numberInput = screen.getByLabelText('Current page');
60
+ expect(numberInput).toBeInTheDocument();
61
+ expect(numberInput).toHaveValue('1');
62
+ });
63
+ test('uses totalPages prop when provided', () => {
64
+ renderWithContext(_jsx(PaginationControls, { totalPages: 20 }));
65
+ const numberInput = screen.getByLabelText('Current page');
66
+ expect(numberInput).toHaveAttribute('max', '20');
67
+ });
68
+ test('uses gridApi paginationGetTotalPages when totalPages prop is not provided', () => {
69
+ const mockGridApi = createMockGridApi({
70
+ paginationGetTotalPages: vi.fn(() => 10),
71
+ });
72
+ renderWithContext(_jsx(PaginationControls, {}), mockGridApi);
73
+ const numberInput = screen.getByLabelText('Current page');
74
+ expect(numberInput).toHaveAttribute('max', '10');
75
+ expect(mockGridApi.paginationGetTotalPages).toHaveBeenCalled();
76
+ });
77
+ test('defaults to 0 total pages when neither prop nor gridApi is provided', () => {
78
+ renderWithContext(_jsx(PaginationControls, {}), null);
79
+ const numberInput = screen.getByLabelText('Current page');
80
+ expect(numberInput).toHaveAttribute('max', '1');
81
+ });
82
+ test('calls onPageChange when provided and first page button is clicked', async () => {
83
+ const onPageChange = vi.fn();
84
+ renderWithContext(_jsx(PaginationControls, { totalPages: 5, onPageChange: onPageChange }));
85
+ // navigate to second page
86
+ const nextButton = screen.getByText('Go to next page');
87
+ await userEvent.click(nextButton);
88
+ onPageChange.mockClear();
89
+ const firstPageButton = screen.getByText('Go to first page');
90
+ await userEvent.click(firstPageButton);
91
+ expect(onPageChange).toHaveBeenCalledExactlyOnceWith(0); // page is zero-indexed
92
+ });
93
+ test('calls gridApi.paginationGoToPage when onPageChange is not provided', async () => {
94
+ const mockGridApi = createMockGridApi();
95
+ renderWithContext(_jsx(PaginationControls, { totalPages: 5 }), mockGridApi);
96
+ const nextPageButton = screen.getByText('Go to next page');
97
+ await userEvent.click(nextPageButton);
98
+ expect(mockGridApi.paginationGoToPage).toHaveBeenCalledExactlyOnceWith(1); // page 2 (1-indexed) becomes 1 (0-indexed)
99
+ });
100
+ test('navigates to next or previous page when next or previous button is clicked', async () => {
101
+ const onPageChange = vi.fn();
102
+ renderWithContext(_jsx(PaginationControls, { totalPages: 5, onPageChange: onPageChange }));
103
+ const nextPageButton = screen.getByText('Go to next page');
104
+ await userEvent.click(nextPageButton);
105
+ expect(onPageChange).toHaveBeenCalledExactlyOnceWith(1);
106
+ onPageChange.mockClear();
107
+ const prevPageButton = screen.getByText('Go to previous page');
108
+ await userEvent.click(prevPageButton);
109
+ expect(onPageChange).toHaveBeenCalledExactlyOnceWith(0);
110
+ });
111
+ test('navigates to next page when next button is clicked', async () => {
112
+ const onPageChange = vi.fn();
113
+ renderWithContext(_jsx(PaginationControls, { totalPages: 5, onPageChange: onPageChange }));
114
+ const nextPageButton = screen.getByText('Go to next page');
115
+ await userEvent.click(nextPageButton);
116
+ expect(onPageChange).toHaveBeenCalledExactlyOnceWith(1);
117
+ });
118
+ test('navigates to last page when last page button is clicked', async () => {
119
+ const onPageChange = vi.fn();
120
+ renderWithContext(_jsx(PaginationControls, { totalPages: 5, onPageChange: onPageChange }));
121
+ const lastPageButton = screen.getByText('Go to last page');
122
+ await userEvent.click(lastPageButton);
123
+ expect(onPageChange).toHaveBeenCalledExactlyOnceWith(4); // page 5 (1-indexed) becomes 4 (0-indexed)
124
+ });
125
+ test('updates input value when navigating via buttons', async () => {
126
+ const onPageChange = vi.fn();
127
+ renderWithContext(_jsx(PaginationControls, { totalPages: 5, onPageChange: onPageChange }));
128
+ const numberInput = screen.getByLabelText('Current page');
129
+ const nextPageButton = screen.getByText('Go to next page');
130
+ expect(numberInput).toHaveValue('1');
131
+ await userEvent.click(nextPageButton);
132
+ expect(numberInput).toHaveValue('2');
133
+ });
134
+ test('navigates to page when number input value changes', async () => {
135
+ const onPageChange = vi.fn();
136
+ renderWithContext(_jsx(PaginationControls, { totalPages: 5, onPageChange: onPageChange }));
137
+ const numberInput = screen.getByLabelText('Current page');
138
+ await userEvent.clear(numberInput);
139
+ await userEvent.type(numberInput, '3');
140
+ await userEvent.tab(); // Trigger onChange
141
+ expect(onPageChange).toHaveBeenCalledExactlyOnceWith(2); // page 3 (1-indexed) becomes 2 (0-indexed)
142
+ expect(numberInput).toHaveValue('3');
143
+ });
144
+ // TODO sort this out when we decide how number input handles inputs outside its min/max range
145
+ test.skip('does not navigate when number input value is 0 or negative', async () => {
146
+ const onPageChange = vi.fn();
147
+ renderWithContext(_jsx(PaginationControls, { totalPages: 5, onPageChange: onPageChange }));
148
+ const numberInput = screen.getByLabelText('Current page');
149
+ await userEvent.clear(numberInput);
150
+ await userEvent.type(numberInput, '0');
151
+ await userEvent.tab();
152
+ // Should not call onPageChange for invalid values
153
+ expect(onPageChange).not.toHaveBeenCalled();
154
+ });
155
+ test('disables first and previous buttons on page 1', () => {
156
+ renderWithContext(_jsx(PaginationControls, { totalPages: 5 }));
157
+ const firstPageButton = screen.getByText('Go to first page');
158
+ const prevPageButton = screen.getByText('Go to previous page');
159
+ expect(firstPageButton.parentElement).toBeDisabled();
160
+ expect(prevPageButton.parentElement).toBeDisabled();
161
+ });
162
+ test('disables next and last buttons on last page', async () => {
163
+ const onPageChange = vi.fn();
164
+ renderWithContext(_jsx(PaginationControls, { totalPages: 5, onPageChange: onPageChange }));
165
+ const lastPageButton = screen.getByText('Go to last page');
166
+ const nextPageButton = screen.getByText('Go to next page');
167
+ // Navigate to last page
168
+ await userEvent.click(lastPageButton);
169
+ expect(nextPageButton.parentElement).toBeDisabled();
170
+ expect(lastPageButton.parentElement).toBeDisabled();
171
+ });
172
+ test('enables all buttons on middle pages', async () => {
173
+ const onPageChange = vi.fn();
174
+ renderWithContext(_jsx(PaginationControls, { totalPages: 5, onPageChange: onPageChange }));
175
+ const numberInput = screen.getByLabelText('Current page');
176
+ // Navigate to page 3
177
+ await userEvent.clear(numberInput);
178
+ await userEvent.type(numberInput, '3');
179
+ await userEvent.tab();
180
+ const firstPageButton = screen.getByText('Go to first page');
181
+ const prevPageButton = screen.getByText('Go to previous page');
182
+ const nextPageButton = screen.getByText('Go to next page');
183
+ const lastPageButton = screen.getByText('Go to last page');
184
+ expect(firstPageButton).not.toBeDisabled();
185
+ expect(prevPageButton).not.toBeDisabled();
186
+ expect(nextPageButton).not.toBeDisabled();
187
+ expect(lastPageButton).not.toBeDisabled();
188
+ });
189
+ test('prefers totalPages prop over gridApi when both are provided', () => {
190
+ const mockGridApi = createMockGridApi();
191
+ renderWithContext(_jsx(PaginationControls, { totalPages: 15 }), mockGridApi);
192
+ const numberInput = screen.getByLabelText('Current page');
193
+ expect(numberInput).toHaveAttribute('max', '15');
194
+ // gridApi.paginationGetTotalPages should not be called when prop is provided
195
+ expect(mockGridApi.paginationGetTotalPages).not.toHaveBeenCalled();
196
+ });
197
+ test('handles single page scenario correctly', () => {
198
+ renderWithContext(_jsx(PaginationControls, { totalPages: 1 }));
199
+ const firstPageButton = screen.getByText('Go to first page');
200
+ const prevPageButton = screen.getByText('Go to previous page');
201
+ const nextPageButton = screen.getByText('Go to next page');
202
+ const lastPageButton = screen.getByText('Go to last page');
203
+ expect(screen.getByLabelText('Current page')).toHaveValue('1');
204
+ expect(firstPageButton.parentElement).toBeDisabled();
205
+ expect(prevPageButton.parentElement).toBeDisabled();
206
+ expect(nextPageButton.parentElement).toBeDisabled();
207
+ expect(lastPageButton.parentElement).toBeDisabled();
208
+ });
209
+ });
210
+ describe('RowCountInfo', () => {
211
+ const renderWithContext = (component, gridApi = null) => {
212
+ return render(_jsx(GridApiContext.Provider, { value: gridApi, children: component }));
213
+ };
214
+ beforeEach(() => {
215
+ vi.clearAllMocks();
216
+ });
217
+ test('renders no message when no gridApi is provided', () => {
218
+ renderWithContext(_jsx(RowCountInfo, {}), null);
219
+ expect(screen.queryByText(/Showing \d+ results/)).not.toBeInTheDocument();
220
+ });
221
+ test('renders message with displayed count and totalRows when totalRows prop is provided', async () => {
222
+ const mockGridApi = createMockGridApi({
223
+ paginationGetTotalPages: vi.fn(() => 5),
224
+ paginationGetRowCount: vi.fn(() => 50),
225
+ });
226
+ renderWithContext(_jsx(RowCountInfo, { totalRows: 100 }), mockGridApi);
227
+ await waitFor(() => {
228
+ expect(screen.getByText(/Showing \d+ of 100 results/)).toBeInTheDocument();
229
+ });
230
+ });
231
+ test('displays correct count when gridApi is ready with single page', async () => {
232
+ const mockGridApi = createMockGridApi({
233
+ getDisplayedRowCount: vi.fn(() => 5),
234
+ paginationGetPageSize: vi.fn(() => 10),
235
+ paginationGetCurrentPage: vi.fn(() => 0),
236
+ paginationGetTotalPages: vi.fn(() => 1),
237
+ });
238
+ renderWithContext(_jsx(RowCountInfo, {}), mockGridApi);
239
+ await waitFor(() => {
240
+ expect(screen.getByText('Showing 5 results')).toBeInTheDocument();
241
+ });
242
+ });
243
+ test('displays correct count when gridApi is ready with multiple pages', async () => {
244
+ const mockGridApi = createMockGridApi({
245
+ getDisplayedRowCount: vi.fn(() => 10),
246
+ paginationGetPageSize: vi.fn(() => 10),
247
+ paginationGetCurrentPage: vi.fn(() => 0),
248
+ paginationGetTotalPages: vi.fn(() => 5),
249
+ paginationGetRowCount: vi.fn(() => 50),
250
+ });
251
+ renderWithContext(_jsx(RowCountInfo, {}), mockGridApi);
252
+ await waitFor(() => {
253
+ expect(screen.getByText('Showing 10 of 50 results')).toBeInTheDocument();
254
+ });
255
+ });
256
+ test('displays correct count on last page with remainder', async () => {
257
+ const mockGridApi = createMockGridApi({
258
+ getDisplayedRowCount: vi.fn(() => 7),
259
+ paginationGetPageSize: vi.fn(() => 10),
260
+ paginationGetCurrentPage: vi.fn(() => 4), // page 5 (0-indexed)
261
+ paginationGetTotalPages: vi.fn(() => 5),
262
+ paginationGetRowCount: vi.fn(() => 47),
263
+ });
264
+ renderWithContext(_jsx(RowCountInfo, {}), mockGridApi);
265
+ await waitFor(() => {
266
+ expect(screen.getByText('Showing 7 of 47 results')).toBeInTheDocument();
267
+ });
268
+ });
269
+ test('displays correct count on last page when displayed count equals page size', async () => {
270
+ const mockGridApi = createMockGridApi({
271
+ getDisplayedRowCount: vi.fn(() => 10),
272
+ paginationGetPageSize: vi.fn(() => 10),
273
+ paginationGetCurrentPage: vi.fn(() => 4), // page 5 (0-indexed)
274
+ paginationGetTotalPages: vi.fn(() => 5),
275
+ paginationGetRowCount: vi.fn(() => 50),
276
+ });
277
+ renderWithContext(_jsx(RowCountInfo, {}), mockGridApi);
278
+ await waitFor(() => {
279
+ expect(screen.getByText('Showing 10 of 50 results')).toBeInTheDocument();
280
+ });
281
+ });
282
+ test('displays correct count when displayed count is less than page size on non-last page', async () => {
283
+ const mockGridApi = createMockGridApi({
284
+ getDisplayedRowCount: vi.fn(() => 5),
285
+ paginationGetPageSize: vi.fn(() => 10),
286
+ paginationGetCurrentPage: vi.fn(() => 0),
287
+ paginationGetTotalPages: vi.fn(() => 3),
288
+ paginationGetRowCount: vi.fn(() => 25),
289
+ });
290
+ renderWithContext(_jsx(RowCountInfo, {}), mockGridApi);
291
+ await waitFor(() => {
292
+ expect(screen.getByText('Showing 5 of 25 results')).toBeInTheDocument();
293
+ });
294
+ });
295
+ test('registers event listeners for paginationChanged, filterChanged, and rowDataUpdated', async () => {
296
+ const mockGridApi = createMockGridApi();
297
+ renderWithContext(_jsx(RowCountInfo, {}), mockGridApi);
298
+ await waitFor(() => {
299
+ expect(mockGridApi.addEventListener).toHaveBeenCalledWith('paginationChanged', expect.any(Function));
300
+ expect(mockGridApi.addEventListener).toHaveBeenCalledWith('filterChanged', expect.any(Function));
301
+ expect(mockGridApi.addEventListener).toHaveBeenCalledWith('rowDataUpdated', expect.any(Function));
302
+ });
303
+ });
304
+ test('updates displayed count when paginationChanged event is triggered', async () => {
305
+ const mockGridApi = createMockGridApi({
306
+ getDisplayedRowCount: vi.fn(() => 10),
307
+ paginationGetPageSize: vi.fn(() => 10),
308
+ paginationGetCurrentPage: vi.fn(() => 0),
309
+ paginationGetTotalPages: vi.fn(() => 2),
310
+ paginationGetRowCount: vi.fn(() => 20),
311
+ });
312
+ renderWithContext(_jsx(RowCountInfo, {}), mockGridApi);
313
+ await waitFor(() => {
314
+ expect(screen.getByText('Showing 10 of 20 results')).toBeInTheDocument();
315
+ });
316
+ // Update the mock to return different values
317
+ mockGridApi.getDisplayedRowCount.mockReturnValue(5);
318
+ mockGridApi.paginationGetCurrentPage.mockReturnValue(1);
319
+ // Trigger paginationChanged event
320
+ const evtName = 'paginationChanged';
321
+ mockGridApi?.dispatchEvent?.(evtName);
322
+ await waitFor(() => {
323
+ expect(screen.getByText('Showing 5 of 20 results')).toBeInTheDocument();
324
+ });
325
+ });
326
+ test('updates displayed count when filterChanged event is triggered', async () => {
327
+ const mockGridApi = createMockGridApi();
328
+ renderWithContext(_jsx(RowCountInfo, {}), mockGridApi);
329
+ await waitFor(() => {
330
+ expect(screen.getByText('Showing 10 results')).toBeInTheDocument();
331
+ });
332
+ // Update the mock to return different values
333
+ mockGridApi.getDisplayedRowCount.mockReturnValue(3);
334
+ // Trigger filterChanged event
335
+ const evtName = 'filterChanged';
336
+ mockGridApi?.dispatchEvent?.(evtName);
337
+ await waitFor(() => {
338
+ expect(screen.getByText('Showing 3 results')).toBeInTheDocument();
339
+ });
340
+ });
341
+ test('updates displayed count when rowDataUpdated event is triggered', async () => {
342
+ const mockGridApi = createMockGridApi();
343
+ renderWithContext(_jsx(RowCountInfo, {}), mockGridApi);
344
+ await waitFor(() => {
345
+ expect(screen.getByText('Showing 10 results')).toBeInTheDocument();
346
+ });
347
+ // Update the mock to return different values
348
+ mockGridApi.getDisplayedRowCount.mockReturnValue(8);
349
+ // Trigger rowDataUpdated event
350
+ const evtName = 'rowDataUpdated';
351
+ mockGridApi?.dispatchEvent?.(evtName);
352
+ await waitFor(() => {
353
+ expect(screen.getByText('Showing 8 results')).toBeInTheDocument();
354
+ });
355
+ });
356
+ test('handles case when displayedRowCount is undefined', async () => {
357
+ const mockGridApi = createMockGridApi({
358
+ getDisplayedRowCount: vi.fn(() => 0),
359
+ paginationGetPageSize: vi.fn(() => 10),
360
+ paginationGetCurrentPage: vi.fn(() => 0),
361
+ paginationGetTotalPages: vi.fn(() => 1),
362
+ });
363
+ renderWithContext(_jsx(RowCountInfo, {}), mockGridApi);
364
+ await waitFor(() => {
365
+ // Should show a message, but the count might be -Infinity or NaN
366
+ expect(screen.getByText(/Showing .* results/)).toBeInTheDocument();
367
+ });
368
+ });
369
+ test('handles case when pageSize is undefined', async () => {
370
+ const mockGridApi = createMockGridApi({
371
+ getDisplayedRowCount: vi.fn(() => 10),
372
+ paginationGetPageSize: vi.fn(() => 0),
373
+ paginationGetCurrentPage: vi.fn(() => 0),
374
+ paginationGetTotalPages: vi.fn(() => 1),
375
+ });
376
+ renderWithContext(_jsx(RowCountInfo, {}), mockGridApi);
377
+ await waitFor(() => {
378
+ expect(screen.getByText(/Showing .* results/)).toBeInTheDocument();
379
+ });
380
+ });
381
+ test('does not show totalRows when pagination has only one page and totalRows prop is not provided', async () => {
382
+ const mockGridApi = createMockGridApi({
383
+ getDisplayedRowCount: vi.fn(() => 5),
384
+ paginationGetPageSize: vi.fn(() => 10),
385
+ paginationGetCurrentPage: vi.fn(() => 0),
386
+ paginationGetTotalPages: vi.fn(() => 1),
387
+ paginationGetRowCount: vi.fn(() => 5),
388
+ });
389
+ renderWithContext(_jsx(RowCountInfo, {}), mockGridApi);
390
+ await waitFor(() => {
391
+ expect(screen.getByText('Showing 5 results')).toBeInTheDocument();
392
+ expect(screen.queryByText(/of \d+ results/)).not.toBeInTheDocument();
393
+ });
394
+ });
395
+ });
396
+ describe('PageSizeSelector', () => {
397
+ const renderWithContext = (component, gridApi = null) => {
398
+ return render(_jsx(GridApiContext.Provider, { value: gridApi, children: component }));
399
+ };
400
+ beforeEach(() => {
401
+ vi.clearAllMocks();
402
+ });
403
+ test('renders with default page size of 100', () => {
404
+ renderWithContext(_jsx(PageSizeSelector, {}));
405
+ expect(screen.getByText('100')).toBeInTheDocument();
406
+ });
407
+ test('renders with initialPageSize prop', () => {
408
+ renderWithContext(_jsx(PageSizeSelector, { initialPageSize: 50 }));
409
+ expect(screen.getByText('50')).toBeInTheDocument();
410
+ });
411
+ test('renders with initialPageSize of "All"', () => {
412
+ renderWithContext(_jsx(PageSizeSelector, { initialPageSize: "All" }));
413
+ expect(screen.getByText('All')).toBeInTheDocument();
414
+ });
415
+ test('opens dropdown when trigger is clicked', async () => {
416
+ renderWithContext(_jsx(PageSizeSelector, {}));
417
+ const trigger = screen.getByText('100');
418
+ await userEvent.click(trigger);
419
+ expect(await screen.findByText('10')).toBeInTheDocument();
420
+ expect(screen.getByText('20')).toBeInTheDocument();
421
+ });
422
+ test('renders all default page sizes in dropdown', async () => {
423
+ renderWithContext(_jsx(PageSizeSelector, {}));
424
+ const trigger = screen.getByText('100');
425
+ await userEvent.click(trigger);
426
+ expect(await screen.findByText('10')).toBeInTheDocument();
427
+ expect(screen.getByText('20')).toBeInTheDocument();
428
+ expect(screen.getByText('30')).toBeInTheDocument();
429
+ expect(screen.getByText('50')).toBeInTheDocument();
430
+ expect(screen.getAllByText('100').length).toBe(2);
431
+ expect(screen.getByText('200')).toBeInTheDocument();
432
+ expect(screen.getByText('300')).toBeInTheDocument();
433
+ expect(screen.getByText('500')).toBeInTheDocument();
434
+ expect(screen.getByText('1000')).toBeInTheDocument();
435
+ expect(screen.getByText('All')).toBeInTheDocument();
436
+ });
437
+ test('renders custom availableSizes', async () => {
438
+ renderWithContext(_jsx(PageSizeSelector, { availableSizes: [5, 10, 25] }));
439
+ const trigger = screen.getByText('100');
440
+ await userEvent.click(trigger);
441
+ expect(await screen.findByText('5')).toBeInTheDocument();
442
+ expect(screen.getByText('10')).toBeInTheDocument();
443
+ expect(screen.getByText('25')).toBeInTheDocument();
444
+ expect(screen.queryByText('20')).not.toBeInTheDocument();
445
+ });
446
+ test('calls onPageSizeChange when page size is selected', async () => {
447
+ const onPageSizeChange = vi.fn();
448
+ renderWithContext(_jsx(PageSizeSelector, { onPageSizeChange: onPageSizeChange }));
449
+ const trigger = screen.getByText('100');
450
+ await userEvent.click(trigger);
451
+ const item = await screen.findByText('50');
452
+ await userEvent.click(item);
453
+ expect(onPageSizeChange).toHaveBeenCalledExactlyOnceWith(50);
454
+ });
455
+ test('updates displayed page size when selection changes', async () => {
456
+ renderWithContext(_jsx(PageSizeSelector, {}));
457
+ const trigger = screen.getByText('100');
458
+ await userEvent.click(trigger);
459
+ const item = await screen.findByText('20');
460
+ await userEvent.click(item);
461
+ await waitFor(() => {
462
+ expect(screen.getByText('20')).toBeInTheDocument();
463
+ });
464
+ });
465
+ test('calls gridApi.setGridOption with pagination true and paginationPageSize when number is selected', async () => {
466
+ const mockGridApi = createMockGridApi({
467
+ setGridOption: vi.fn(),
468
+ });
469
+ renderWithContext(_jsx(PageSizeSelector, {}), mockGridApi);
470
+ const trigger = screen.getByText('100');
471
+ await userEvent.click(trigger);
472
+ const item = await screen.findByText('50');
473
+ await userEvent.click(item);
474
+ await waitFor(() => {
475
+ expect(mockGridApi.setGridOption).toHaveBeenCalledWith('pagination', true);
476
+ expect(mockGridApi.setGridOption).toHaveBeenCalledWith('paginationPageSize', 50);
477
+ });
478
+ });
479
+ test('calls gridApi.setGridOption with pagination false when "All" is selected', async () => {
480
+ const mockGridApi = createMockGridApi({
481
+ setGridOption: vi.fn(),
482
+ });
483
+ renderWithContext(_jsx(PageSizeSelector, {}), mockGridApi);
484
+ const trigger = screen.getByText('100');
485
+ await userEvent.click(trigger);
486
+ const item = await screen.findByText('All');
487
+ await userEvent.click(item);
488
+ await waitFor(() => {
489
+ expect(mockGridApi.setGridOption).toHaveBeenCalledWith('pagination', false);
490
+ });
491
+ });
492
+ test('handles "All" option in onPageSizeChange callback', async () => {
493
+ const onPageSizeChange = vi.fn();
494
+ renderWithContext(_jsx(PageSizeSelector, { onPageSizeChange: onPageSizeChange }));
495
+ const trigger = screen.getByText('100');
496
+ await userEvent.click(trigger);
497
+ const item = await screen.findByText('All');
498
+ await userEvent.click(item);
499
+ expect(onPageSizeChange).toHaveBeenCalledExactlyOnceWith('All');
500
+ });
501
+ test('updates page size when initialPageSize is provided and gridApi is ready', async () => {
502
+ const mockGridApi = createMockGridApi({
503
+ setGridOption: vi.fn(),
504
+ });
505
+ renderWithContext(_jsx(PageSizeSelector, { initialPageSize: 25 }), mockGridApi);
506
+ await waitFor(() => {
507
+ expect(mockGridApi.setGridOption).toHaveBeenCalledWith('pagination', true);
508
+ expect(mockGridApi.setGridOption).toHaveBeenCalledWith('paginationPageSize', 25);
509
+ });
510
+ });
511
+ test('does not update gridApi when initialPageSize is "All" and gridApi is ready', async () => {
512
+ const mockGridApi = createMockGridApi({
513
+ setGridOption: vi.fn(),
514
+ });
515
+ renderWithContext(_jsx(PageSizeSelector, { initialPageSize: "All" }), mockGridApi);
516
+ await waitFor(() => {
517
+ expect(mockGridApi.setGridOption).toHaveBeenCalledWith('pagination', false);
518
+ });
519
+ });
520
+ test('does not call gridApi methods when gridApi is not available', async () => {
521
+ const onPageSizeChange = vi.fn();
522
+ renderWithContext(_jsx(PageSizeSelector, { onPageSizeChange: onPageSizeChange }), null);
523
+ const trigger = screen.getByText('100');
524
+ await userEvent.click(trigger);
525
+ const item = await screen.findByText('50');
526
+ await userEvent.click(item);
527
+ expect(onPageSizeChange).toHaveBeenCalledExactlyOnceWith(50);
528
+ });
529
+ test('normalizes string numbers to numbers when updating page size', async () => {
530
+ const mockGridApi = createMockGridApi({
531
+ setGridOption: vi.fn(),
532
+ });
533
+ renderWithContext(_jsx(PageSizeSelector, {}), mockGridApi);
534
+ const trigger = screen.getByLabelText('Select page size');
535
+ await userEvent.click(trigger);
536
+ // Click on a numeric option - it should be normalized to a number
537
+ const item = await screen.findByText('10');
538
+ await userEvent.click(item);
539
+ expect(mockGridApi.setGridOption).toHaveBeenCalledWith('paginationPageSize', 10);
540
+ });
541
+ test('handles custom availableSizes with "All" option', async () => {
542
+ const onPageSizeChange = vi.fn();
543
+ renderWithContext(_jsx(PageSizeSelector, { availableSizes: [15, 30, 'All'], onPageSizeChange: onPageSizeChange }));
544
+ const trigger = screen.getByText('100');
545
+ await userEvent.click(trigger);
546
+ expect(await screen.findByText('15')).toBeInTheDocument();
547
+ expect(screen.getByText('30')).toBeInTheDocument();
548
+ expect(screen.getByText('All')).toBeInTheDocument();
549
+ const allItem = screen.getByText('All');
550
+ await userEvent.click(allItem);
551
+ expect(onPageSizeChange).toHaveBeenCalledExactlyOnceWith('All');
552
+ });
553
+ });
554
+ describe('PaginationPanel', () => {
555
+ const renderWithContext = (component, gridApi = createMockGridApi({
556
+ getDisplayedRowCount: vi.fn(() => 10),
557
+ paginationGetPageSize: vi.fn(() => 10),
558
+ paginationGetCurrentPage: vi.fn(() => 0),
559
+ paginationGetTotalPages: vi.fn(() => 5),
560
+ paginationGetRowCount: vi.fn(() => 50),
561
+ setGridOption: vi.fn(),
562
+ })) => {
563
+ return render(_jsx(GridApiContext.Provider, { value: gridApi, children: component }));
564
+ };
565
+ beforeEach(() => {
566
+ vi.clearAllMocks();
567
+ });
568
+ test('renders correctly', () => {
569
+ const { container } = renderWithContext(_jsx(PaginationPanel, {}));
570
+ const nav = container.querySelector('nav[aria-label="Pagination"]');
571
+ expect(nav).toBeInTheDocument();
572
+ expect(nav).toHaveClass('ds-table__pagination-panel');
573
+ });
574
+ test('renders the pagination components', async () => {
575
+ renderWithContext(_jsx(PaginationPanel, {}));
576
+ await waitFor(() => {
577
+ expect(screen.getByText(/Showing \d+ of \d+ results/)).toBeInTheDocument();
578
+ expect(screen.getByText('Go to first page')).toBeInTheDocument();
579
+ expect(screen.getByText('Go to previous page')).toBeInTheDocument();
580
+ expect(screen.getByText('Go to next page')).toBeInTheDocument();
581
+ expect(screen.getByText('Go to last page')).toBeInTheDocument();
582
+ expect(screen.getByLabelText('Select page size')).toBeInTheDocument();
583
+ });
584
+ });
585
+ test('passes props to RowCountInfo', async () => {
586
+ renderWithContext(_jsx(PaginationPanel, { totalRows: 100 }));
587
+ await waitFor(() => {
588
+ expect(screen.getByText(/Showing \d+ of 100 results/)).toBeInTheDocument();
589
+ });
590
+ });
591
+ test('passes props to PaginationControls', async () => {
592
+ const onPageChange = vi.fn();
593
+ renderWithContext(_jsx(PaginationPanel, { totalPages: 20, onPageChange: onPageChange }));
594
+ const numberInput = screen.getByLabelText('Current page');
595
+ expect(numberInput).toHaveAttribute('max', '20');
596
+ const nextPageButton = screen.getByText('Go to next page');
597
+ await userEvent.click(nextPageButton);
598
+ expect(onPageChange).toHaveBeenCalledExactlyOnceWith(1);
599
+ });
600
+ test('passes props to PageSizeSelector', async () => {
601
+ const onPageSizeChange = vi.fn();
602
+ renderWithContext(_jsx(PaginationPanel, { availableSizes: [5, 15, 25], initialPageSize: 5, onPageSizeChange: onPageSizeChange }));
603
+ const trigger = screen.getByLabelText('Select page size');
604
+ expect(trigger.textContent).toContain('5');
605
+ await userEvent.click(trigger);
606
+ expect(await screen.findByText('15')).toBeInTheDocument();
607
+ expect(screen.getAllByText('5').length).toBe(2);
608
+ expect(screen.getByText('15')).toBeInTheDocument();
609
+ const item = await screen.findByText('25');
610
+ expect(item).toBeInTheDocument();
611
+ await userEvent.click(item);
612
+ expect(onPageSizeChange).toHaveBeenLastCalledWith(25);
613
+ });
614
+ });
615
+ });
616
+ //# sourceMappingURL=Pagination.test.js.map