@windstream/react-shared-components 0.2.2 → 0.2.3
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/contentful/index.d.ts +2 -3
- package/dist/contentful/index.esm.js +2 -2
- package/dist/contentful/index.esm.js.map +1 -1
- package/dist/contentful/index.js +2 -2
- package/dist/contentful/index.js.map +1 -1
- package/dist/core.d.ts +3 -3
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/components/pagination/index.test.tsx +117 -79
- package/src/components/pagination/index.tsx +41 -19
- package/src/components/pagination/types.ts +2 -1
- package/src/contentful/blocks/blogs-grid/index.tsx +24 -3
- package/src/contentful/blocks/blogs-grid-base/index.tsx +2 -0
- package/src/contentful/blocks/blogs-grid-base/types.ts +1 -0
- package/src/contentful/blocks/breadcrumbs/index.tsx +95 -95
- package/src/contentful/blocks/callout/index.tsx +1 -1
- package/src/contentful/blocks/cards/blog-card/index.test.tsx +4 -9
- package/src/contentful/blocks/cards/blog-card/index.tsx +1 -4
- package/src/contentful/blocks/cards/blog-card/types.ts +0 -2
- package/src/contentful/blocks/cards/floating-image-card/index.tsx +2 -2
- package/src/contentful/blocks/find-kinetic/index.tsx +147 -147
|
@@ -4,16 +4,11 @@ import { Pagination } from "./index";
|
|
|
4
4
|
import { fireEvent, render, screen } from "@testing-library/react";
|
|
5
5
|
|
|
6
6
|
// Mock dependencies
|
|
7
|
-
jest.mock("@shared/components/
|
|
8
|
-
|
|
9
|
-
<
|
|
10
|
-
onClick={onClick}
|
|
11
|
-
disabled={disabled}
|
|
12
|
-
className={className}
|
|
13
|
-
{...rest}
|
|
14
|
-
>
|
|
7
|
+
jest.mock("@shared/components/link", () => ({
|
|
8
|
+
Link: ({ children, href, onClick, className, ...rest }: any) => (
|
|
9
|
+
<a href={href} onClick={onClick} className={className} {...rest}>
|
|
15
10
|
{children}
|
|
16
|
-
</
|
|
11
|
+
</a>
|
|
17
12
|
),
|
|
18
13
|
}));
|
|
19
14
|
|
|
@@ -27,6 +22,7 @@ jest.mock("@shared/components/material-icon", () => ({
|
|
|
27
22
|
|
|
28
23
|
describe("Pagination", () => {
|
|
29
24
|
const mockOnPageChange = jest.fn();
|
|
25
|
+
const mockGetPageHref = (page: number) => `/blog?page=${page}`;
|
|
30
26
|
|
|
31
27
|
beforeEach(() => {
|
|
32
28
|
mockOnPageChange.mockClear();
|
|
@@ -39,6 +35,7 @@ describe("Pagination", () => {
|
|
|
39
35
|
currentPage={1}
|
|
40
36
|
totalPages={5}
|
|
41
37
|
onPageChange={mockOnPageChange}
|
|
38
|
+
getPageHref={mockGetPageHref}
|
|
42
39
|
/>
|
|
43
40
|
);
|
|
44
41
|
expect(screen.getByRole("navigation")).toHaveAttribute(
|
|
@@ -53,6 +50,7 @@ describe("Pagination", () => {
|
|
|
53
50
|
currentPage={1}
|
|
54
51
|
totalPages={5}
|
|
55
52
|
onPageChange={mockOnPageChange}
|
|
53
|
+
getPageHref={mockGetPageHref}
|
|
56
54
|
ariaLabel="Blog pages"
|
|
57
55
|
/>
|
|
58
56
|
);
|
|
@@ -62,28 +60,30 @@ describe("Pagination", () => {
|
|
|
62
60
|
);
|
|
63
61
|
});
|
|
64
62
|
|
|
65
|
-
it("renders previous and next navigation
|
|
63
|
+
it("renders previous and next navigation links", () => {
|
|
66
64
|
render(
|
|
67
65
|
<Pagination
|
|
68
66
|
currentPage={3}
|
|
69
67
|
totalPages={5}
|
|
70
68
|
onPageChange={mockOnPageChange}
|
|
69
|
+
getPageHref={mockGetPageHref}
|
|
71
70
|
/>
|
|
72
71
|
);
|
|
73
72
|
expect(
|
|
74
|
-
screen.getByRole("
|
|
73
|
+
screen.getByRole("link", { name: "Go to previous page" })
|
|
75
74
|
).toBeInTheDocument();
|
|
76
75
|
expect(
|
|
77
|
-
screen.getByRole("
|
|
76
|
+
screen.getByRole("link", { name: "Go to next page" })
|
|
78
77
|
).toBeInTheDocument();
|
|
79
78
|
});
|
|
80
79
|
|
|
81
|
-
it("renders chevron icons in nav
|
|
80
|
+
it("renders chevron icons in nav links", () => {
|
|
82
81
|
render(
|
|
83
82
|
<Pagination
|
|
84
83
|
currentPage={1}
|
|
85
84
|
totalPages={5}
|
|
86
85
|
onPageChange={mockOnPageChange}
|
|
86
|
+
getPageHref={mockGetPageHref}
|
|
87
87
|
/>
|
|
88
88
|
);
|
|
89
89
|
expect(screen.getByTestId("icon-chevron_left")).toBeInTheDocument();
|
|
@@ -91,57 +91,59 @@ describe("Pagination", () => {
|
|
|
91
91
|
});
|
|
92
92
|
});
|
|
93
93
|
|
|
94
|
-
describe("
|
|
95
|
-
it("disables previous
|
|
94
|
+
describe("Link states", () => {
|
|
95
|
+
it("disables previous link on first page", () => {
|
|
96
96
|
render(
|
|
97
97
|
<Pagination
|
|
98
98
|
currentPage={1}
|
|
99
99
|
totalPages={5}
|
|
100
100
|
onPageChange={mockOnPageChange}
|
|
101
|
+
getPageHref={mockGetPageHref}
|
|
101
102
|
/>
|
|
102
103
|
);
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
).
|
|
104
|
+
const prevLink = screen.getByRole("link", { name: "Go to previous page" });
|
|
105
|
+
expect(prevLink).toHaveAttribute("aria-disabled", "true");
|
|
106
|
+
expect(prevLink).toHaveAttribute("tabindex", "-1");
|
|
106
107
|
});
|
|
107
108
|
|
|
108
|
-
it("enables previous
|
|
109
|
+
it("enables previous link when not on first page", () => {
|
|
109
110
|
render(
|
|
110
111
|
<Pagination
|
|
111
112
|
currentPage={3}
|
|
112
113
|
totalPages={5}
|
|
113
114
|
onPageChange={mockOnPageChange}
|
|
115
|
+
getPageHref={mockGetPageHref}
|
|
114
116
|
/>
|
|
115
117
|
);
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
).not.toBeDisabled();
|
|
118
|
+
const prevLink = screen.getByRole("link", { name: "Go to previous page" });
|
|
119
|
+
expect(prevLink).not.toHaveAttribute("aria-disabled", "true");
|
|
119
120
|
});
|
|
120
121
|
|
|
121
|
-
it("disables next
|
|
122
|
+
it("disables next link on last page", () => {
|
|
122
123
|
render(
|
|
123
124
|
<Pagination
|
|
124
125
|
currentPage={5}
|
|
125
126
|
totalPages={5}
|
|
126
127
|
onPageChange={mockOnPageChange}
|
|
128
|
+
getPageHref={mockGetPageHref}
|
|
127
129
|
/>
|
|
128
130
|
);
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
).
|
|
131
|
+
const nextLink = screen.getByRole("link", { name: "Go to next page" });
|
|
132
|
+
expect(nextLink).toHaveAttribute("aria-disabled", "true");
|
|
133
|
+
expect(nextLink).toHaveAttribute("tabindex", "-1");
|
|
132
134
|
});
|
|
133
135
|
|
|
134
|
-
it("enables next
|
|
136
|
+
it("enables next link when not on last page", () => {
|
|
135
137
|
render(
|
|
136
138
|
<Pagination
|
|
137
139
|
currentPage={3}
|
|
138
140
|
totalPages={5}
|
|
139
141
|
onPageChange={mockOnPageChange}
|
|
142
|
+
getPageHref={mockGetPageHref}
|
|
140
143
|
/>
|
|
141
144
|
);
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
).not.toBeDisabled();
|
|
145
|
+
const nextLink = screen.getByRole("link", { name: "Go to next page" });
|
|
146
|
+
expect(nextLink).not.toHaveAttribute("aria-disabled", "true");
|
|
145
147
|
});
|
|
146
148
|
|
|
147
149
|
it("marks current page with aria-current='page'", () => {
|
|
@@ -150,10 +152,11 @@ describe("Pagination", () => {
|
|
|
150
152
|
currentPage={3}
|
|
151
153
|
totalPages={5}
|
|
152
154
|
onPageChange={mockOnPageChange}
|
|
155
|
+
getPageHref={mockGetPageHref}
|
|
153
156
|
/>
|
|
154
157
|
);
|
|
155
158
|
expect(
|
|
156
|
-
screen.getByRole("
|
|
159
|
+
screen.getByRole("link", { name: "Go to page 3" })
|
|
157
160
|
).toHaveAttribute("aria-current", "page");
|
|
158
161
|
});
|
|
159
162
|
|
|
@@ -163,65 +166,84 @@ describe("Pagination", () => {
|
|
|
163
166
|
currentPage={3}
|
|
164
167
|
totalPages={5}
|
|
165
168
|
onPageChange={mockOnPageChange}
|
|
169
|
+
getPageHref={mockGetPageHref}
|
|
166
170
|
/>
|
|
167
171
|
);
|
|
168
172
|
expect(
|
|
169
|
-
screen.getByRole("
|
|
173
|
+
screen.getByRole("link", { name: "Go to page 1" })
|
|
170
174
|
).not.toHaveAttribute("aria-current");
|
|
171
175
|
});
|
|
172
176
|
|
|
173
|
-
it("applies active class to current page
|
|
177
|
+
it("applies active class to current page link", () => {
|
|
174
178
|
render(
|
|
175
179
|
<Pagination
|
|
176
180
|
currentPage={2}
|
|
177
181
|
totalPages={5}
|
|
178
182
|
onPageChange={mockOnPageChange}
|
|
183
|
+
getPageHref={mockGetPageHref}
|
|
179
184
|
/>
|
|
180
185
|
);
|
|
181
|
-
const
|
|
182
|
-
expect(
|
|
183
|
-
expect(
|
|
186
|
+
const activeLink = screen.getByRole("link", { name: "Go to page 2" });
|
|
187
|
+
expect(activeLink.className).toContain("bg-bg-surface-active");
|
|
188
|
+
expect(activeLink.className).toContain("font-bold");
|
|
184
189
|
});
|
|
185
190
|
|
|
186
|
-
it("does not apply active class to non-current page
|
|
191
|
+
it("does not apply active class to non-current page links", () => {
|
|
187
192
|
render(
|
|
188
193
|
<Pagination
|
|
189
194
|
currentPage={2}
|
|
190
195
|
totalPages={5}
|
|
191
196
|
onPageChange={mockOnPageChange}
|
|
197
|
+
getPageHref={mockGetPageHref}
|
|
192
198
|
/>
|
|
193
199
|
);
|
|
194
|
-
const
|
|
200
|
+
const inactiveLink = screen.getByRole("link", {
|
|
195
201
|
name: "Go to page 1",
|
|
196
202
|
});
|
|
197
|
-
expect(
|
|
203
|
+
expect(inactiveLink.className).not.toContain("bg-bg-surface-active");
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it("renders correct href for page links", () => {
|
|
207
|
+
render(
|
|
208
|
+
<Pagination
|
|
209
|
+
currentPage={1}
|
|
210
|
+
totalPages={5}
|
|
211
|
+
onPageChange={mockOnPageChange}
|
|
212
|
+
getPageHref={mockGetPageHref}
|
|
213
|
+
/>
|
|
214
|
+
);
|
|
215
|
+
expect(
|
|
216
|
+
screen.getByRole("link", { name: "Go to page 3" })
|
|
217
|
+
).toHaveAttribute("href", "/blog?page=3");
|
|
198
218
|
});
|
|
199
219
|
});
|
|
200
220
|
|
|
201
221
|
describe("Page navigation clicks", () => {
|
|
202
|
-
it("calls onPageChange with previous page when previous
|
|
222
|
+
it("calls onPageChange with previous page when previous link clicked", () => {
|
|
203
223
|
render(
|
|
204
224
|
<Pagination
|
|
205
225
|
currentPage={3}
|
|
206
226
|
totalPages={5}
|
|
207
227
|
onPageChange={mockOnPageChange}
|
|
228
|
+
getPageHref={mockGetPageHref}
|
|
208
229
|
/>
|
|
209
230
|
);
|
|
210
231
|
fireEvent.click(
|
|
211
|
-
screen.getByRole("
|
|
232
|
+
screen.getByRole("link", { name: "Go to previous page" })
|
|
212
233
|
);
|
|
213
234
|
expect(mockOnPageChange).toHaveBeenCalledWith(2);
|
|
214
235
|
});
|
|
215
236
|
|
|
216
|
-
it("calls onPageChange with next page when next
|
|
237
|
+
it("calls onPageChange with next page when next link clicked", () => {
|
|
217
238
|
render(
|
|
218
239
|
<Pagination
|
|
219
240
|
currentPage={3}
|
|
220
241
|
totalPages={5}
|
|
221
242
|
onPageChange={mockOnPageChange}
|
|
243
|
+
getPageHref={mockGetPageHref}
|
|
222
244
|
/>
|
|
223
245
|
);
|
|
224
|
-
fireEvent.click(screen.getByRole("
|
|
246
|
+
fireEvent.click(screen.getByRole("link", { name: "Go to next page" }));
|
|
225
247
|
expect(mockOnPageChange).toHaveBeenCalledWith(4);
|
|
226
248
|
});
|
|
227
249
|
|
|
@@ -231,35 +253,38 @@ describe("Pagination", () => {
|
|
|
231
253
|
currentPage={1}
|
|
232
254
|
totalPages={5}
|
|
233
255
|
onPageChange={mockOnPageChange}
|
|
256
|
+
getPageHref={mockGetPageHref}
|
|
234
257
|
/>
|
|
235
258
|
);
|
|
236
|
-
fireEvent.click(screen.getByRole("
|
|
259
|
+
fireEvent.click(screen.getByRole("link", { name: "Go to page 4" }));
|
|
237
260
|
expect(mockOnPageChange).toHaveBeenCalledWith(4);
|
|
238
261
|
});
|
|
239
262
|
|
|
240
|
-
it("does not call onPageChange when disabled previous
|
|
263
|
+
it("does not call onPageChange when disabled previous link is clicked", () => {
|
|
241
264
|
render(
|
|
242
265
|
<Pagination
|
|
243
266
|
currentPage={1}
|
|
244
267
|
totalPages={5}
|
|
245
268
|
onPageChange={mockOnPageChange}
|
|
269
|
+
getPageHref={mockGetPageHref}
|
|
246
270
|
/>
|
|
247
271
|
);
|
|
248
272
|
fireEvent.click(
|
|
249
|
-
screen.getByRole("
|
|
273
|
+
screen.getByRole("link", { name: "Go to previous page" })
|
|
250
274
|
);
|
|
251
275
|
expect(mockOnPageChange).not.toHaveBeenCalled();
|
|
252
276
|
});
|
|
253
277
|
|
|
254
|
-
it("does not call onPageChange when disabled next
|
|
278
|
+
it("does not call onPageChange when disabled next link is clicked", () => {
|
|
255
279
|
render(
|
|
256
280
|
<Pagination
|
|
257
281
|
currentPage={5}
|
|
258
282
|
totalPages={5}
|
|
259
283
|
onPageChange={mockOnPageChange}
|
|
284
|
+
getPageHref={mockGetPageHref}
|
|
260
285
|
/>
|
|
261
286
|
);
|
|
262
|
-
fireEvent.click(screen.getByRole("
|
|
287
|
+
fireEvent.click(screen.getByRole("link", { name: "Go to next page" }));
|
|
263
288
|
expect(mockOnPageChange).not.toHaveBeenCalled();
|
|
264
289
|
});
|
|
265
290
|
});
|
|
@@ -271,10 +296,11 @@ describe("Pagination", () => {
|
|
|
271
296
|
currentPage={1}
|
|
272
297
|
totalPages={1}
|
|
273
298
|
onPageChange={mockOnPageChange}
|
|
299
|
+
getPageHref={mockGetPageHref}
|
|
274
300
|
/>
|
|
275
301
|
);
|
|
276
302
|
expect(
|
|
277
|
-
screen.getByRole("
|
|
303
|
+
screen.getByRole("link", { name: "Go to page 1" })
|
|
278
304
|
).toBeInTheDocument();
|
|
279
305
|
expect(screen.queryByText("…")).not.toBeInTheDocument();
|
|
280
306
|
});
|
|
@@ -285,11 +311,12 @@ describe("Pagination", () => {
|
|
|
285
311
|
currentPage={1}
|
|
286
312
|
totalPages={5}
|
|
287
313
|
onPageChange={mockOnPageChange}
|
|
314
|
+
getPageHref={mockGetPageHref}
|
|
288
315
|
/>
|
|
289
316
|
);
|
|
290
317
|
for (let i = 1; i <= 5; i++) {
|
|
291
318
|
expect(
|
|
292
|
-
screen.getByRole("
|
|
319
|
+
screen.getByRole("link", { name: `Go to page ${i}` })
|
|
293
320
|
).toBeInTheDocument();
|
|
294
321
|
}
|
|
295
322
|
expect(screen.queryByText("…")).not.toBeInTheDocument();
|
|
@@ -301,11 +328,12 @@ describe("Pagination", () => {
|
|
|
301
328
|
currentPage={4}
|
|
302
329
|
totalPages={7}
|
|
303
330
|
onPageChange={mockOnPageChange}
|
|
331
|
+
getPageHref={mockGetPageHref}
|
|
304
332
|
/>
|
|
305
333
|
);
|
|
306
334
|
for (let i = 1; i <= 7; i++) {
|
|
307
335
|
expect(
|
|
308
|
-
screen.getByRole("
|
|
336
|
+
screen.getByRole("link", { name: `Go to page ${i}` })
|
|
309
337
|
).toBeInTheDocument();
|
|
310
338
|
}
|
|
311
339
|
expect(screen.queryByText("…")).not.toBeInTheDocument();
|
|
@@ -319,19 +347,20 @@ describe("Pagination", () => {
|
|
|
319
347
|
currentPage={1}
|
|
320
348
|
totalPages={10}
|
|
321
349
|
onPageChange={mockOnPageChange}
|
|
350
|
+
getPageHref={mockGetPageHref}
|
|
322
351
|
/>
|
|
323
352
|
);
|
|
324
353
|
for (let i = 1; i <= 5; i++) {
|
|
325
354
|
expect(
|
|
326
|
-
screen.getByRole("
|
|
355
|
+
screen.getByRole("link", { name: `Go to page ${i}` })
|
|
327
356
|
).toBeInTheDocument();
|
|
328
357
|
}
|
|
329
358
|
expect(screen.getByText("…")).toBeInTheDocument();
|
|
330
359
|
expect(
|
|
331
|
-
screen.getByRole("
|
|
360
|
+
screen.getByRole("link", { name: "Go to page 10" })
|
|
332
361
|
).toBeInTheDocument();
|
|
333
362
|
expect(
|
|
334
|
-
screen.queryByRole("
|
|
363
|
+
screen.queryByRole("link", { name: "Go to page 6" })
|
|
335
364
|
).not.toBeInTheDocument();
|
|
336
365
|
});
|
|
337
366
|
|
|
@@ -341,16 +370,17 @@ describe("Pagination", () => {
|
|
|
341
370
|
currentPage={4}
|
|
342
371
|
totalPages={10}
|
|
343
372
|
onPageChange={mockOnPageChange}
|
|
373
|
+
getPageHref={mockGetPageHref}
|
|
344
374
|
/>
|
|
345
375
|
);
|
|
346
376
|
for (let i = 1; i <= 5; i++) {
|
|
347
377
|
expect(
|
|
348
|
-
screen.getByRole("
|
|
378
|
+
screen.getByRole("link", { name: `Go to page ${i}` })
|
|
349
379
|
).toBeInTheDocument();
|
|
350
380
|
}
|
|
351
381
|
expect(screen.getByText("…")).toBeInTheDocument();
|
|
352
382
|
expect(
|
|
353
|
-
screen.getByRole("
|
|
383
|
+
screen.getByRole("link", { name: "Go to page 10" })
|
|
354
384
|
).toBeInTheDocument();
|
|
355
385
|
});
|
|
356
386
|
});
|
|
@@ -362,15 +392,16 @@ describe("Pagination", () => {
|
|
|
362
392
|
currentPage={10}
|
|
363
393
|
totalPages={10}
|
|
364
394
|
onPageChange={mockOnPageChange}
|
|
395
|
+
getPageHref={mockGetPageHref}
|
|
365
396
|
/>
|
|
366
397
|
);
|
|
367
398
|
expect(
|
|
368
|
-
screen.getByRole("
|
|
399
|
+
screen.getByRole("link", { name: "Go to page 1" })
|
|
369
400
|
).toBeInTheDocument();
|
|
370
401
|
expect(screen.getByText("…")).toBeInTheDocument();
|
|
371
402
|
for (let i = 6; i <= 10; i++) {
|
|
372
403
|
expect(
|
|
373
|
-
screen.getByRole("
|
|
404
|
+
screen.getByRole("link", { name: `Go to page ${i}` })
|
|
374
405
|
).toBeInTheDocument();
|
|
375
406
|
}
|
|
376
407
|
});
|
|
@@ -381,15 +412,16 @@ describe("Pagination", () => {
|
|
|
381
412
|
currentPage={7}
|
|
382
413
|
totalPages={10}
|
|
383
414
|
onPageChange={mockOnPageChange}
|
|
415
|
+
getPageHref={mockGetPageHref}
|
|
384
416
|
/>
|
|
385
417
|
);
|
|
386
418
|
expect(
|
|
387
|
-
screen.getByRole("
|
|
419
|
+
screen.getByRole("link", { name: "Go to page 1" })
|
|
388
420
|
).toBeInTheDocument();
|
|
389
421
|
expect(screen.getByText("…")).toBeInTheDocument();
|
|
390
422
|
for (let i = 6; i <= 10; i++) {
|
|
391
423
|
expect(
|
|
392
|
-
screen.getByRole("
|
|
424
|
+
screen.getByRole("link", { name: `Go to page ${i}` })
|
|
393
425
|
).toBeInTheDocument();
|
|
394
426
|
}
|
|
395
427
|
});
|
|
@@ -402,22 +434,23 @@ describe("Pagination", () => {
|
|
|
402
434
|
currentPage={5}
|
|
403
435
|
totalPages={10}
|
|
404
436
|
onPageChange={mockOnPageChange}
|
|
437
|
+
getPageHref={mockGetPageHref}
|
|
405
438
|
/>
|
|
406
439
|
);
|
|
407
440
|
expect(
|
|
408
|
-
screen.getByRole("
|
|
441
|
+
screen.getByRole("link", { name: "Go to page 1" })
|
|
409
442
|
).toBeInTheDocument();
|
|
410
443
|
expect(
|
|
411
|
-
screen.getByRole("
|
|
444
|
+
screen.getByRole("link", { name: "Go to page 4" })
|
|
412
445
|
).toBeInTheDocument();
|
|
413
446
|
expect(
|
|
414
|
-
screen.getByRole("
|
|
447
|
+
screen.getByRole("link", { name: "Go to page 5" })
|
|
415
448
|
).toBeInTheDocument();
|
|
416
449
|
expect(
|
|
417
|
-
screen.getByRole("
|
|
450
|
+
screen.getByRole("link", { name: "Go to page 6" })
|
|
418
451
|
).toBeInTheDocument();
|
|
419
452
|
expect(
|
|
420
|
-
screen.getByRole("
|
|
453
|
+
screen.getByRole("link", { name: "Go to page 10" })
|
|
421
454
|
).toBeInTheDocument();
|
|
422
455
|
const ellipses = screen.getAllByText("…");
|
|
423
456
|
expect(ellipses).toHaveLength(2);
|
|
@@ -429,6 +462,7 @@ describe("Pagination", () => {
|
|
|
429
462
|
currentPage={5}
|
|
430
463
|
totalPages={10}
|
|
431
464
|
onPageChange={mockOnPageChange}
|
|
465
|
+
getPageHref={mockGetPageHref}
|
|
432
466
|
/>
|
|
433
467
|
);
|
|
434
468
|
const ellipses = screen.getAllByText("…");
|
|
@@ -444,42 +478,44 @@ describe("Pagination", () => {
|
|
|
444
478
|
currentPage={6}
|
|
445
479
|
totalPages={10}
|
|
446
480
|
onPageChange={mockOnPageChange}
|
|
481
|
+
getPageHref={mockGetPageHref}
|
|
447
482
|
/>
|
|
448
483
|
);
|
|
449
484
|
expect(
|
|
450
|
-
screen.getByRole("
|
|
485
|
+
screen.getByRole("link", { name: "Go to page 1" })
|
|
451
486
|
).toBeInTheDocument();
|
|
452
487
|
expect(
|
|
453
|
-
screen.getByRole("
|
|
488
|
+
screen.getByRole("link", { name: "Go to page 5" })
|
|
454
489
|
).toBeInTheDocument();
|
|
455
490
|
expect(
|
|
456
|
-
screen.getByRole("
|
|
491
|
+
screen.getByRole("link", { name: "Go to page 6" })
|
|
457
492
|
).toBeInTheDocument();
|
|
458
493
|
expect(
|
|
459
|
-
screen.getByRole("
|
|
494
|
+
screen.getByRole("link", { name: "Go to page 7" })
|
|
460
495
|
).toBeInTheDocument();
|
|
461
496
|
expect(
|
|
462
|
-
screen.getByRole("
|
|
497
|
+
screen.getByRole("link", { name: "Go to page 10" })
|
|
463
498
|
).toBeInTheDocument();
|
|
464
499
|
expect(screen.getAllByText("…")).toHaveLength(2);
|
|
465
500
|
});
|
|
466
501
|
});
|
|
467
502
|
|
|
468
503
|
describe("Edge cases", () => {
|
|
469
|
-
it("handles single page (both nav
|
|
504
|
+
it("handles single page (both nav links disabled)", () => {
|
|
470
505
|
render(
|
|
471
506
|
<Pagination
|
|
472
507
|
currentPage={1}
|
|
473
508
|
totalPages={1}
|
|
474
509
|
onPageChange={mockOnPageChange}
|
|
510
|
+
getPageHref={mockGetPageHref}
|
|
475
511
|
/>
|
|
476
512
|
);
|
|
477
513
|
expect(
|
|
478
|
-
screen.getByRole("
|
|
479
|
-
).
|
|
514
|
+
screen.getByRole("link", { name: "Go to previous page" })
|
|
515
|
+
).toHaveAttribute("aria-disabled", "true");
|
|
480
516
|
expect(
|
|
481
|
-
screen.getByRole("
|
|
482
|
-
).
|
|
517
|
+
screen.getByRole("link", { name: "Go to next page" })
|
|
518
|
+
).toHaveAttribute("aria-disabled", "true");
|
|
483
519
|
});
|
|
484
520
|
|
|
485
521
|
it("handles totalPages=8 with current=4 (near-start boundary)", () => {
|
|
@@ -488,15 +524,16 @@ describe("Pagination", () => {
|
|
|
488
524
|
currentPage={4}
|
|
489
525
|
totalPages={8}
|
|
490
526
|
onPageChange={mockOnPageChange}
|
|
527
|
+
getPageHref={mockGetPageHref}
|
|
491
528
|
/>
|
|
492
529
|
);
|
|
493
530
|
for (let i = 1; i <= 5; i++) {
|
|
494
531
|
expect(
|
|
495
|
-
screen.getByRole("
|
|
532
|
+
screen.getByRole("link", { name: `Go to page ${i}` })
|
|
496
533
|
).toBeInTheDocument();
|
|
497
534
|
}
|
|
498
535
|
expect(
|
|
499
|
-
screen.getByRole("
|
|
536
|
+
screen.getByRole("link", { name: "Go to page 8" })
|
|
500
537
|
).toBeInTheDocument();
|
|
501
538
|
});
|
|
502
539
|
|
|
@@ -506,14 +543,15 @@ describe("Pagination", () => {
|
|
|
506
543
|
currentPage={5}
|
|
507
544
|
totalPages={8}
|
|
508
545
|
onPageChange={mockOnPageChange}
|
|
546
|
+
getPageHref={mockGetPageHref}
|
|
509
547
|
/>
|
|
510
548
|
);
|
|
511
549
|
expect(
|
|
512
|
-
screen.getByRole("
|
|
550
|
+
screen.getByRole("link", { name: "Go to page 1" })
|
|
513
551
|
).toBeInTheDocument();
|
|
514
552
|
for (let i = 4; i <= 8; i++) {
|
|
515
553
|
expect(
|
|
516
|
-
screen.getByRole("
|
|
554
|
+
screen.getByRole("link", { name: `Go to page ${i}` })
|
|
517
555
|
).toBeInTheDocument();
|
|
518
556
|
}
|
|
519
557
|
});
|
|
@@ -3,24 +3,29 @@
|
|
|
3
3
|
import React from "react";
|
|
4
4
|
import { PaginationProps } from "./types";
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { Link } from "@shared/components/link";
|
|
7
7
|
import { MaterialIcon } from "@shared/components/material-icon";
|
|
8
8
|
|
|
9
|
+
const defaultGetPageHref = (page: number) => `?page=${page}`;
|
|
10
|
+
|
|
9
11
|
export const Pagination: React.FC<PaginationProps> = ({
|
|
10
12
|
currentPage,
|
|
11
13
|
totalPages,
|
|
12
14
|
onPageChange,
|
|
15
|
+
getPageHref = defaultGetPageHref,
|
|
13
16
|
ariaLabel = "Pagination",
|
|
14
17
|
}: PaginationProps) => {
|
|
15
18
|
const pageItems = buildPageItems(currentPage, totalPages);
|
|
16
19
|
|
|
17
|
-
const
|
|
18
|
-
"inline-flex items-center justify-center w-9 h-9 rounded-full bg-transparent cursor-pointer transition-colors duration-150 hover:border-text-brand hover:text-text-brand
|
|
20
|
+
const navLinkBase =
|
|
21
|
+
"inline-flex items-center justify-center w-9 h-9 rounded-full bg-transparent cursor-pointer transition-colors duration-150 hover:border-text-brand hover:text-text-brand";
|
|
22
|
+
|
|
23
|
+
const navLinkDisabled = "opacity-40 pointer-events-none";
|
|
19
24
|
|
|
20
|
-
const
|
|
25
|
+
const pageLinkBase =
|
|
21
26
|
"inline-flex items-center justify-center w-9 h-9 rounded-lg text-body2 text-gray-700 bg-transparent border-none cursor-pointer transition-colors duration-150 hover:bg-gray-100 hover:text-gray-900";
|
|
22
27
|
|
|
23
|
-
const
|
|
28
|
+
const pageLinkActive = "bg-bg-surface-active font-bold hover:bg-bg-gray-300";
|
|
24
29
|
|
|
25
30
|
return (
|
|
26
31
|
<nav
|
|
@@ -28,14 +33,22 @@ export const Pagination: React.FC<PaginationProps> = ({
|
|
|
28
33
|
aria-label={ariaLabel}
|
|
29
34
|
>
|
|
30
35
|
{/* Previous */}
|
|
31
|
-
<
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
disabled={currentPage === 1}
|
|
36
|
+
<Link
|
|
37
|
+
href={currentPage === 1 ? "#" : getPageHref(currentPage - 1)}
|
|
38
|
+
className={`${navLinkBase} ${currentPage === 1 ? navLinkDisabled : ""}`}
|
|
35
39
|
aria-label="Go to previous page"
|
|
40
|
+
aria-disabled={currentPage === 1}
|
|
41
|
+
tabIndex={currentPage === 1 ? -1 : undefined}
|
|
42
|
+
onClick={e => {
|
|
43
|
+
if (currentPage === 1) {
|
|
44
|
+
e.preventDefault();
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
onPageChange?.(currentPage - 1);
|
|
48
|
+
}}
|
|
36
49
|
>
|
|
37
50
|
<MaterialIcon name="chevron_left" size={32} aria-hidden="true" />
|
|
38
|
-
</
|
|
51
|
+
</Link>
|
|
39
52
|
|
|
40
53
|
{/* Page numbers */}
|
|
41
54
|
{pageItems.map((item, idx) =>
|
|
@@ -48,27 +61,36 @@ export const Pagination: React.FC<PaginationProps> = ({
|
|
|
48
61
|
…
|
|
49
62
|
</span>
|
|
50
63
|
) : (
|
|
51
|
-
<
|
|
64
|
+
<Link
|
|
52
65
|
key={item}
|
|
53
|
-
|
|
54
|
-
|
|
66
|
+
href={getPageHref(item)}
|
|
67
|
+
className={`${pageLinkBase} ${item === currentPage ? pageLinkActive : ""}`}
|
|
55
68
|
aria-label={`Go to page ${item}`}
|
|
56
69
|
aria-current={item === currentPage ? "page" : undefined}
|
|
70
|
+
onClick={() => onPageChange?.(item)}
|
|
57
71
|
>
|
|
58
72
|
{item}
|
|
59
|
-
</
|
|
73
|
+
</Link>
|
|
60
74
|
)
|
|
61
75
|
)}
|
|
62
76
|
|
|
63
77
|
{/* Next */}
|
|
64
|
-
<
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
disabled={currentPage === totalPages}
|
|
78
|
+
<Link
|
|
79
|
+
href={currentPage === totalPages ? "#" : getPageHref(currentPage + 1)}
|
|
80
|
+
className={`${navLinkBase} ${currentPage === totalPages ? navLinkDisabled : ""}`}
|
|
68
81
|
aria-label="Go to next page"
|
|
82
|
+
aria-disabled={currentPage === totalPages}
|
|
83
|
+
tabIndex={currentPage === totalPages ? -1 : undefined}
|
|
84
|
+
onClick={e => {
|
|
85
|
+
if (currentPage === totalPages) {
|
|
86
|
+
e.preventDefault();
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
onPageChange?.(currentPage + 1);
|
|
90
|
+
}}
|
|
69
91
|
>
|
|
70
92
|
<MaterialIcon name="chevron_right" size={32} aria-hidden="true" />
|
|
71
|
-
</
|
|
93
|
+
</Link>
|
|
72
94
|
</nav>
|
|
73
95
|
);
|
|
74
96
|
};
|