paris 0.21.2 → 0.22.0
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/CHANGELOG.md +39 -0
- package/package.json +1 -1
- package/src/helpers/OpenChangeEffect.tsx +21 -0
- package/src/helpers/useControllableState.test.ts +88 -0
- package/src/helpers/useControllableState.ts +59 -0
- package/src/stories/accordionselect/AccordionSelect.test.tsx +72 -0
- package/src/stories/accordionselect/AccordionSelect.tsx +22 -12
- package/src/stories/checkbox/Checkbox.test.tsx +53 -0
- package/src/stories/checkbox/Checkbox.tsx +21 -6
- package/src/stories/combobox/Combobox.test.tsx +111 -0
- package/src/stories/combobox/Combobox.tsx +192 -137
- package/src/stories/drawer/Drawer.module.scss +56 -15
- package/src/stories/drawer/Drawer.stories.tsx +287 -109
- package/src/stories/drawer/Drawer.test.tsx +486 -11
- package/src/stories/drawer/Drawer.tsx +366 -240
- package/src/stories/drawer/DrawerActions.tsx +28 -0
- package/src/stories/drawer/DrawerBottomPanel.tsx +55 -0
- package/src/stories/drawer/DrawerContext.tsx +31 -0
- package/src/stories/drawer/DrawerPage.tsx +37 -0
- package/src/stories/drawer/DrawerPageContext.tsx +35 -0
- package/src/stories/drawer/DrawerPaginationContext.tsx +22 -0
- package/src/stories/drawer/DrawerProgressBar.tsx +72 -0
- package/src/stories/drawer/DrawerSlotContext.tsx +172 -0
- package/src/stories/drawer/DrawerTitle.tsx +35 -0
- package/src/stories/drawer/index.ts +9 -0
- package/src/stories/menu/Menu.test.tsx +43 -0
- package/src/stories/menu/Menu.tsx +13 -2
- package/src/stories/popover/Popover.tsx +8 -5
- package/src/stories/select/Select.module.scss +1 -1
- package/src/stories/select/Select.test.tsx +108 -0
- package/src/stories/select/Select.tsx +121 -92
- package/src/test/render.tsx +2 -2
|
@@ -1,6 +1,16 @@
|
|
|
1
|
+
import { renderHook } from '@testing-library/react';
|
|
1
2
|
import { describe, expect, it, vi } from 'vitest';
|
|
2
3
|
import { getCloseButton, render, screen, waitFor } from '../../test/render';
|
|
4
|
+
import { usePagination } from '../pagination';
|
|
3
5
|
import { Drawer } from './Drawer';
|
|
6
|
+
import { DrawerActions } from './DrawerActions';
|
|
7
|
+
import { DrawerBottomPanel } from './DrawerBottomPanel';
|
|
8
|
+
import { useDrawer } from './DrawerContext';
|
|
9
|
+
import { DrawerPage } from './DrawerPage';
|
|
10
|
+
import { DrawerPageProvider, useIsPageActive } from './DrawerPageContext';
|
|
11
|
+
import { useDrawerPagination } from './DrawerPaginationContext';
|
|
12
|
+
import { DrawerProgressBar } from './DrawerProgressBar';
|
|
13
|
+
import { DrawerTitle } from './DrawerTitle';
|
|
4
14
|
|
|
5
15
|
describe('Drawer', () => {
|
|
6
16
|
it('renders when isOpen is true', async () => {
|
|
@@ -69,7 +79,7 @@ describe('Drawer', () => {
|
|
|
69
79
|
);
|
|
70
80
|
|
|
71
81
|
await waitFor(() => {
|
|
72
|
-
expect(getCloseButton()).toBeInTheDocument();
|
|
82
|
+
expect(getCloseButton('Close drawer')).toBeInTheDocument();
|
|
73
83
|
});
|
|
74
84
|
});
|
|
75
85
|
|
|
@@ -84,7 +94,7 @@ describe('Drawer', () => {
|
|
|
84
94
|
expect(screen.getByRole('dialog')).toBeInTheDocument();
|
|
85
95
|
});
|
|
86
96
|
|
|
87
|
-
expect(getCloseButton()).not.toBeInTheDocument();
|
|
97
|
+
expect(getCloseButton('Close drawer')).not.toBeInTheDocument();
|
|
88
98
|
});
|
|
89
99
|
|
|
90
100
|
it('calls onClose when the close button is clicked', async () => {
|
|
@@ -96,10 +106,10 @@ describe('Drawer', () => {
|
|
|
96
106
|
);
|
|
97
107
|
|
|
98
108
|
await waitFor(() => {
|
|
99
|
-
expect(getCloseButton()).toBeInTheDocument();
|
|
109
|
+
expect(getCloseButton('Close drawer')).toBeInTheDocument();
|
|
100
110
|
});
|
|
101
111
|
|
|
102
|
-
const closeButton = getCloseButton()!;
|
|
112
|
+
const closeButton = getCloseButton('Close drawer')!;
|
|
103
113
|
await user.click(closeButton);
|
|
104
114
|
|
|
105
115
|
expect(onClose).toHaveBeenCalledWith(false);
|
|
@@ -153,15 +163,13 @@ describe('Drawer', () => {
|
|
|
153
163
|
});
|
|
154
164
|
});
|
|
155
165
|
|
|
156
|
-
it('renders a bottom panel', async () => {
|
|
166
|
+
it('renders a bottom panel via DrawerBottomPanel', async () => {
|
|
157
167
|
render(
|
|
158
|
-
<Drawer
|
|
159
|
-
isOpen={true}
|
|
160
|
-
title="Test Drawer"
|
|
161
|
-
onClose={vi.fn()}
|
|
162
|
-
bottomPanel={<button type="button">Save</button>}
|
|
163
|
-
>
|
|
168
|
+
<Drawer isOpen={true} title="Test Drawer" onClose={vi.fn()}>
|
|
164
169
|
Content
|
|
170
|
+
<DrawerBottomPanel>
|
|
171
|
+
<button type="button">Save</button>
|
|
172
|
+
</DrawerBottomPanel>
|
|
165
173
|
</Drawer>,
|
|
166
174
|
);
|
|
167
175
|
|
|
@@ -256,4 +264,471 @@ describe('Drawer', () => {
|
|
|
256
264
|
expect(screen.getByTestId('custom-title')).toBeInTheDocument();
|
|
257
265
|
});
|
|
258
266
|
});
|
|
267
|
+
|
|
268
|
+
describe('useDrawer', () => {
|
|
269
|
+
function DrawerConsumer() {
|
|
270
|
+
const { close, isOpen } = useDrawer();
|
|
271
|
+
return (
|
|
272
|
+
<div>
|
|
273
|
+
<span data-testid="is-open">{String(isOpen)}</span>
|
|
274
|
+
<button type="button" onClick={close}>
|
|
275
|
+
Close via context
|
|
276
|
+
</button>
|
|
277
|
+
</div>
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
it('provides isOpen and close to children', async () => {
|
|
282
|
+
render(
|
|
283
|
+
<Drawer isOpen={true} title="Context Drawer" onClose={vi.fn()}>
|
|
284
|
+
<DrawerConsumer />
|
|
285
|
+
</Drawer>,
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
await waitFor(() => {
|
|
289
|
+
expect(screen.getByTestId('is-open')).toHaveTextContent('true');
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('calls onClose(false) when close is invoked from context', async () => {
|
|
294
|
+
const onClose = vi.fn();
|
|
295
|
+
const { user } = render(
|
|
296
|
+
<Drawer isOpen={true} title="Context Drawer" onClose={onClose}>
|
|
297
|
+
<DrawerConsumer />
|
|
298
|
+
</Drawer>,
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
await waitFor(() => {
|
|
302
|
+
expect(screen.getByText('Close via context')).toBeInTheDocument();
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
await user.click(screen.getByText('Close via context'));
|
|
306
|
+
|
|
307
|
+
expect(onClose).toHaveBeenCalledWith(false);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it('throws when useDrawer is used outside of a Drawer', () => {
|
|
311
|
+
// Suppress React error boundary console output
|
|
312
|
+
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
313
|
+
expect(() => render(<DrawerConsumer />)).toThrow('useDrawer must be used within a Drawer component');
|
|
314
|
+
consoleSpy.mockRestore();
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
describe('useDrawerPagination', () => {
|
|
319
|
+
function PaginationConsumer() {
|
|
320
|
+
const pagination = useDrawerPagination();
|
|
321
|
+
return (
|
|
322
|
+
<div>
|
|
323
|
+
<span data-testid="pagination-value">{pagination ? pagination.currentPage : 'null'}</span>
|
|
324
|
+
</div>
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
it('provides pagination state to children in a paginated drawer', async () => {
|
|
329
|
+
const Wrapper = () => {
|
|
330
|
+
const pages = ['step1'] as const;
|
|
331
|
+
const pagination = usePagination<typeof pages>('step1');
|
|
332
|
+
return (
|
|
333
|
+
<Drawer isOpen={true} title="Paginated Drawer" onClose={vi.fn()} pagination={pagination}>
|
|
334
|
+
<DrawerPage id="step1">
|
|
335
|
+
<PaginationConsumer />
|
|
336
|
+
</DrawerPage>
|
|
337
|
+
</Drawer>
|
|
338
|
+
);
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
render(<Wrapper />);
|
|
342
|
+
|
|
343
|
+
await waitFor(() => {
|
|
344
|
+
expect(screen.getByTestId('pagination-value')).toHaveTextContent('step1');
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
it('returns null when no pagination is provided', async () => {
|
|
349
|
+
render(
|
|
350
|
+
<Drawer isOpen={true} title="Non-paginated Drawer" onClose={vi.fn()}>
|
|
351
|
+
<PaginationConsumer />
|
|
352
|
+
</Drawer>,
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
await waitFor(() => {
|
|
356
|
+
expect(screen.getByTestId('pagination-value')).toHaveTextContent('null');
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
describe('useIsPageActive', () => {
|
|
362
|
+
it('returns true when page is active', () => {
|
|
363
|
+
const { result } = renderHook(() => useIsPageActive(), {
|
|
364
|
+
wrapper: ({ children }) => (
|
|
365
|
+
<DrawerPageProvider isActive={true} pageID="page1">
|
|
366
|
+
{children}
|
|
367
|
+
</DrawerPageProvider>
|
|
368
|
+
),
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
expect(result.current).toBe(true);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it('returns false when page is not active', () => {
|
|
375
|
+
const { result } = renderHook(() => useIsPageActive(), {
|
|
376
|
+
wrapper: ({ children }) => (
|
|
377
|
+
<DrawerPageProvider isActive={false} pageID="page1">
|
|
378
|
+
{children}
|
|
379
|
+
</DrawerPageProvider>
|
|
380
|
+
),
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
expect(result.current).toBe(false);
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
it('returns true when used outside DrawerPageProvider', () => {
|
|
387
|
+
const { result } = renderHook(() => useIsPageActive());
|
|
388
|
+
|
|
389
|
+
expect(result.current).toBe(true);
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
describe('DrawerTitle', () => {
|
|
394
|
+
it('overrides the drawer title prop', async () => {
|
|
395
|
+
render(
|
|
396
|
+
<Drawer isOpen={true} title="Fallback Title" onClose={vi.fn()}>
|
|
397
|
+
<DrawerTitle>Custom Title</DrawerTitle>
|
|
398
|
+
</Drawer>,
|
|
399
|
+
);
|
|
400
|
+
|
|
401
|
+
await waitFor(() => {
|
|
402
|
+
expect(screen.getByText('Custom Title')).toBeInTheDocument();
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
// Fallback title stays in DOM (visually hidden) for aria-labelledby
|
|
406
|
+
expect(screen.getByText('Fallback Title')).toBeInTheDocument();
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
it('shows fallback title when no DrawerTitle is used', async () => {
|
|
410
|
+
render(
|
|
411
|
+
<Drawer isOpen={true} title="Fallback Title" onClose={vi.fn()}>
|
|
412
|
+
Content
|
|
413
|
+
</Drawer>,
|
|
414
|
+
);
|
|
415
|
+
|
|
416
|
+
await waitFor(() => {
|
|
417
|
+
expect(screen.getByText('Fallback Title')).toBeInTheDocument();
|
|
418
|
+
});
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
it('only shows active page DrawerTitle in paginated drawer', async () => {
|
|
422
|
+
const Wrapper = () => {
|
|
423
|
+
const pages = ['a', 'b'] as const;
|
|
424
|
+
const pagination = usePagination<typeof pages>('a');
|
|
425
|
+
return (
|
|
426
|
+
<Drawer isOpen={true} title="Fallback" onClose={vi.fn()} pagination={pagination}>
|
|
427
|
+
<DrawerPage id="a">
|
|
428
|
+
<DrawerTitle>Title A</DrawerTitle>
|
|
429
|
+
Page A
|
|
430
|
+
</DrawerPage>
|
|
431
|
+
<DrawerPage id="b">
|
|
432
|
+
<DrawerTitle>Title B</DrawerTitle>
|
|
433
|
+
Page B
|
|
434
|
+
</DrawerPage>
|
|
435
|
+
</Drawer>
|
|
436
|
+
);
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
render(<Wrapper />);
|
|
440
|
+
|
|
441
|
+
await waitFor(() => {
|
|
442
|
+
expect(screen.getByText('Title A')).toBeInTheDocument();
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
expect(screen.queryByText('Title B')).not.toBeInTheDocument();
|
|
446
|
+
});
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
describe('DrawerActions', () => {
|
|
450
|
+
it('renders actions via slot component', async () => {
|
|
451
|
+
render(
|
|
452
|
+
<Drawer isOpen={true} title="Test" onClose={vi.fn()}>
|
|
453
|
+
<DrawerActions>
|
|
454
|
+
<button type="button">Slot Action</button>
|
|
455
|
+
</DrawerActions>
|
|
456
|
+
Content
|
|
457
|
+
</Drawer>,
|
|
458
|
+
);
|
|
459
|
+
|
|
460
|
+
await waitFor(() => {
|
|
461
|
+
expect(screen.getByText('Slot Action')).toBeInTheDocument();
|
|
462
|
+
});
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
it('falls back to additionalActions prop when no DrawerActions slot', async () => {
|
|
466
|
+
render(
|
|
467
|
+
<Drawer
|
|
468
|
+
isOpen={true}
|
|
469
|
+
title="Test"
|
|
470
|
+
onClose={vi.fn()}
|
|
471
|
+
additionalActions={<button type="button">Prop Action</button>}
|
|
472
|
+
>
|
|
473
|
+
Content
|
|
474
|
+
</Drawer>,
|
|
475
|
+
);
|
|
476
|
+
|
|
477
|
+
await waitFor(() => {
|
|
478
|
+
expect(screen.getByText('Prop Action')).toBeInTheDocument();
|
|
479
|
+
});
|
|
480
|
+
});
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
describe('DrawerPage', () => {
|
|
484
|
+
it('renders children inside a paginated drawer', async () => {
|
|
485
|
+
const Wrapper = () => {
|
|
486
|
+
const pages = ['a', 'b'] as const;
|
|
487
|
+
const pagination = usePagination<typeof pages>('a');
|
|
488
|
+
return (
|
|
489
|
+
<Drawer isOpen={true} title="Test" onClose={vi.fn()} pagination={pagination}>
|
|
490
|
+
<DrawerPage id="a">Page A Content</DrawerPage>
|
|
491
|
+
<DrawerPage id="b">Page B Content</DrawerPage>
|
|
492
|
+
</Drawer>
|
|
493
|
+
);
|
|
494
|
+
};
|
|
495
|
+
|
|
496
|
+
render(<Wrapper />);
|
|
497
|
+
|
|
498
|
+
await waitFor(() => {
|
|
499
|
+
expect(screen.getByText('Page A Content')).toBeInTheDocument();
|
|
500
|
+
});
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
it('does not render lazy page until it becomes active', async () => {
|
|
504
|
+
const LazyChild = () => <span data-testid="lazy-content">Lazy Loaded</span>;
|
|
505
|
+
|
|
506
|
+
const Wrapper = () => {
|
|
507
|
+
const pages = ['a', 'b'] as const;
|
|
508
|
+
const pagination = usePagination<typeof pages>('a');
|
|
509
|
+
return (
|
|
510
|
+
<Drawer isOpen={true} title="Test" onClose={vi.fn()} pagination={pagination}>
|
|
511
|
+
<DrawerPage id="a">
|
|
512
|
+
<button type="button" onClick={() => pagination.open('b')}>
|
|
513
|
+
Go to B
|
|
514
|
+
</button>
|
|
515
|
+
</DrawerPage>
|
|
516
|
+
<DrawerPage id="b" lazy>
|
|
517
|
+
<LazyChild />
|
|
518
|
+
</DrawerPage>
|
|
519
|
+
</Drawer>
|
|
520
|
+
);
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
const { user } = render(<Wrapper />);
|
|
524
|
+
|
|
525
|
+
await waitFor(() => {
|
|
526
|
+
expect(screen.getByText('Go to B')).toBeInTheDocument();
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
expect(screen.queryByTestId('lazy-content')).not.toBeInTheDocument();
|
|
530
|
+
|
|
531
|
+
await user.click(screen.getByText('Go to B'));
|
|
532
|
+
|
|
533
|
+
await waitFor(() => {
|
|
534
|
+
expect(screen.getByTestId('lazy-content')).toBeInTheDocument();
|
|
535
|
+
});
|
|
536
|
+
});
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
describe('onAfterClose', () => {
|
|
540
|
+
it('calls onAfterClose after the drawer close animation completes', async () => {
|
|
541
|
+
const onAfterClose = vi.fn();
|
|
542
|
+
const onClose = vi.fn();
|
|
543
|
+
|
|
544
|
+
const { rerender } = render(
|
|
545
|
+
<Drawer isOpen={true} title="Test" onClose={onClose} onAfterClose={onAfterClose}>
|
|
546
|
+
Content
|
|
547
|
+
</Drawer>,
|
|
548
|
+
);
|
|
549
|
+
|
|
550
|
+
await waitFor(() => {
|
|
551
|
+
expect(screen.getByRole('dialog')).toBeInTheDocument();
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
rerender(
|
|
555
|
+
<Drawer isOpen={false} title="Test" onClose={onClose} onAfterClose={onAfterClose}>
|
|
556
|
+
Content
|
|
557
|
+
</Drawer>,
|
|
558
|
+
);
|
|
559
|
+
|
|
560
|
+
await waitFor(() => {
|
|
561
|
+
expect(onAfterClose).toHaveBeenCalledTimes(1);
|
|
562
|
+
});
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
it('does not call onAfterClose on initial render when closed', () => {
|
|
566
|
+
const onAfterClose = vi.fn();
|
|
567
|
+
|
|
568
|
+
render(
|
|
569
|
+
<Drawer isOpen={false} title="Test" onClose={vi.fn()} onAfterClose={onAfterClose}>
|
|
570
|
+
Content
|
|
571
|
+
</Drawer>,
|
|
572
|
+
);
|
|
573
|
+
|
|
574
|
+
expect(onAfterClose).not.toHaveBeenCalled();
|
|
575
|
+
});
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
describe('DrawerBottomPanel', () => {
|
|
579
|
+
it('renders bottom panel content via slot component', async () => {
|
|
580
|
+
render(
|
|
581
|
+
<Drawer isOpen={true} title="Test" onClose={vi.fn()}>
|
|
582
|
+
<DrawerBottomPanel>
|
|
583
|
+
<button type="button">Slot Panel</button>
|
|
584
|
+
</DrawerBottomPanel>
|
|
585
|
+
Content
|
|
586
|
+
</Drawer>,
|
|
587
|
+
);
|
|
588
|
+
|
|
589
|
+
await waitFor(() => {
|
|
590
|
+
expect(screen.getByText('Slot Panel')).toBeInTheDocument();
|
|
591
|
+
});
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
it('renders multiple append-mode bottom panels', async () => {
|
|
595
|
+
render(
|
|
596
|
+
<Drawer isOpen={true} title="Test" onClose={vi.fn()}>
|
|
597
|
+
<DrawerBottomPanel mode="append">
|
|
598
|
+
<span>First Panel</span>
|
|
599
|
+
</DrawerBottomPanel>
|
|
600
|
+
<DrawerBottomPanel mode="append">
|
|
601
|
+
<span>Second Panel</span>
|
|
602
|
+
</DrawerBottomPanel>
|
|
603
|
+
Content
|
|
604
|
+
</Drawer>,
|
|
605
|
+
);
|
|
606
|
+
|
|
607
|
+
await waitFor(() => {
|
|
608
|
+
expect(screen.getByText('First Panel')).toBeInTheDocument();
|
|
609
|
+
expect(screen.getByText('Second Panel')).toBeInTheDocument();
|
|
610
|
+
});
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
it('orders multiple append slots by priority', async () => {
|
|
614
|
+
render(
|
|
615
|
+
<Drawer isOpen={true} title="Test" onClose={vi.fn()}>
|
|
616
|
+
<DrawerBottomPanel mode="append" priority={20}>
|
|
617
|
+
<span data-testid="p20">Priority 20</span>
|
|
618
|
+
</DrawerBottomPanel>
|
|
619
|
+
<DrawerBottomPanel mode="append" priority={10}>
|
|
620
|
+
<span data-testid="p10">Priority 10</span>
|
|
621
|
+
</DrawerBottomPanel>
|
|
622
|
+
Content
|
|
623
|
+
</Drawer>,
|
|
624
|
+
);
|
|
625
|
+
|
|
626
|
+
await waitFor(() => {
|
|
627
|
+
const p10 = screen.getByTestId('p10');
|
|
628
|
+
const p20 = screen.getByTestId('p20');
|
|
629
|
+
// CSS order controls visual ordering — lower priority = visually higher
|
|
630
|
+
const p10Order = Number((p10.parentElement as HTMLElement).style.order);
|
|
631
|
+
const p20Order = Number((p20.parentElement as HTMLElement).style.order);
|
|
632
|
+
expect(p10Order).toBeLessThan(p20Order);
|
|
633
|
+
});
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
it('only shows active page bottom panel in paginated drawer', async () => {
|
|
637
|
+
const Wrapper = () => {
|
|
638
|
+
const pages = ['a', 'b'] as const;
|
|
639
|
+
const pagination = usePagination<typeof pages>('a');
|
|
640
|
+
return (
|
|
641
|
+
<Drawer isOpen={true} title="Test" onClose={vi.fn()} pagination={pagination}>
|
|
642
|
+
<DrawerPage id="a">
|
|
643
|
+
<DrawerBottomPanel>
|
|
644
|
+
<span>Panel A</span>
|
|
645
|
+
</DrawerBottomPanel>
|
|
646
|
+
Page A
|
|
647
|
+
</DrawerPage>
|
|
648
|
+
<DrawerPage id="b">
|
|
649
|
+
<DrawerBottomPanel>
|
|
650
|
+
<span>Panel B</span>
|
|
651
|
+
</DrawerBottomPanel>
|
|
652
|
+
Page B
|
|
653
|
+
</DrawerPage>
|
|
654
|
+
</Drawer>
|
|
655
|
+
);
|
|
656
|
+
};
|
|
657
|
+
|
|
658
|
+
render(<Wrapper />);
|
|
659
|
+
|
|
660
|
+
await waitFor(() => {
|
|
661
|
+
expect(screen.getByText('Panel A')).toBeInTheDocument();
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
expect(screen.queryByText('Panel B')).not.toBeInTheDocument();
|
|
665
|
+
});
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
describe('DrawerProgressBar', () => {
|
|
669
|
+
it('renders progress bar with auto-calculated value from pagination', async () => {
|
|
670
|
+
const Wrapper = () => {
|
|
671
|
+
const pages = ['a', 'b', 'c'] as const;
|
|
672
|
+
const pagination = usePagination<typeof pages>('b');
|
|
673
|
+
return (
|
|
674
|
+
<Drawer isOpen={true} title="Test" onClose={vi.fn()} pagination={pagination} progressBar>
|
|
675
|
+
<DrawerPage id="a">Page A</DrawerPage>
|
|
676
|
+
<DrawerPage id="b">Page B</DrawerPage>
|
|
677
|
+
<DrawerPage id="c">Page C</DrawerPage>
|
|
678
|
+
</Drawer>
|
|
679
|
+
);
|
|
680
|
+
};
|
|
681
|
+
|
|
682
|
+
render(<Wrapper />);
|
|
683
|
+
|
|
684
|
+
await waitFor(() => {
|
|
685
|
+
const bar = screen.getByRole('progressbar');
|
|
686
|
+
expect(bar).toBeInTheDocument();
|
|
687
|
+
// Page 'b' is index 1, so (1+1)/3 * 100 ≈ 67
|
|
688
|
+
expect(bar).toHaveAttribute('aria-valuenow', '67');
|
|
689
|
+
});
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
it('renders progress bar with explicit value override', async () => {
|
|
693
|
+
const Wrapper = () => {
|
|
694
|
+
const pages = ['a', 'b'] as const;
|
|
695
|
+
const pagination = usePagination<typeof pages>('a');
|
|
696
|
+
return (
|
|
697
|
+
<Drawer isOpen={true} title="Test" onClose={vi.fn()} pagination={pagination}>
|
|
698
|
+
<DrawerPage id="a">Page A</DrawerPage>
|
|
699
|
+
<DrawerPage id="b">Page B</DrawerPage>
|
|
700
|
+
<DrawerProgressBar value={42} />
|
|
701
|
+
</Drawer>
|
|
702
|
+
);
|
|
703
|
+
};
|
|
704
|
+
|
|
705
|
+
render(<Wrapper />);
|
|
706
|
+
|
|
707
|
+
await waitFor(() => {
|
|
708
|
+
const bar = screen.getByRole('progressbar');
|
|
709
|
+
expect(bar).toBeInTheDocument();
|
|
710
|
+
expect(bar).toHaveAttribute('aria-valuenow', '42');
|
|
711
|
+
});
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
it('clamps explicit value to 0–100 range', async () => {
|
|
715
|
+
const Wrapper = () => {
|
|
716
|
+
const pages = ['a'] as const;
|
|
717
|
+
const pagination = usePagination<typeof pages>('a');
|
|
718
|
+
return (
|
|
719
|
+
<Drawer isOpen={true} title="Test" onClose={vi.fn()} pagination={pagination}>
|
|
720
|
+
<DrawerPage id="a">Page A</DrawerPage>
|
|
721
|
+
<DrawerProgressBar value={150} />
|
|
722
|
+
</Drawer>
|
|
723
|
+
);
|
|
724
|
+
};
|
|
725
|
+
|
|
726
|
+
render(<Wrapper />);
|
|
727
|
+
|
|
728
|
+
await waitFor(() => {
|
|
729
|
+
const bar = screen.getByRole('progressbar');
|
|
730
|
+
expect(bar).toHaveAttribute('aria-valuenow', '100');
|
|
731
|
+
});
|
|
732
|
+
});
|
|
733
|
+
});
|
|
259
734
|
});
|