paris 0.21.3 → 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.
Files changed (31) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/package.json +1 -1
  3. package/src/helpers/OpenChangeEffect.tsx +21 -0
  4. package/src/helpers/useControllableState.test.ts +88 -0
  5. package/src/helpers/useControllableState.ts +59 -0
  6. package/src/stories/accordionselect/AccordionSelect.test.tsx +72 -0
  7. package/src/stories/accordionselect/AccordionSelect.tsx +22 -12
  8. package/src/stories/checkbox/Checkbox.test.tsx +53 -0
  9. package/src/stories/checkbox/Checkbox.tsx +21 -6
  10. package/src/stories/combobox/Combobox.test.tsx +111 -0
  11. package/src/stories/combobox/Combobox.tsx +165 -152
  12. package/src/stories/drawer/Drawer.module.scss +56 -15
  13. package/src/stories/drawer/Drawer.stories.tsx +287 -109
  14. package/src/stories/drawer/Drawer.test.tsx +486 -11
  15. package/src/stories/drawer/Drawer.tsx +366 -240
  16. package/src/stories/drawer/DrawerActions.tsx +28 -0
  17. package/src/stories/drawer/DrawerBottomPanel.tsx +55 -0
  18. package/src/stories/drawer/DrawerContext.tsx +31 -0
  19. package/src/stories/drawer/DrawerPage.tsx +37 -0
  20. package/src/stories/drawer/DrawerPageContext.tsx +35 -0
  21. package/src/stories/drawer/DrawerPaginationContext.tsx +22 -0
  22. package/src/stories/drawer/DrawerProgressBar.tsx +72 -0
  23. package/src/stories/drawer/DrawerSlotContext.tsx +172 -0
  24. package/src/stories/drawer/DrawerTitle.tsx +35 -0
  25. package/src/stories/drawer/index.ts +9 -0
  26. package/src/stories/menu/Menu.test.tsx +43 -0
  27. package/src/stories/menu/Menu.tsx +13 -2
  28. package/src/stories/popover/Popover.tsx +8 -5
  29. package/src/stories/select/Select.test.tsx +108 -0
  30. package/src/stories/select/Select.tsx +121 -92
  31. package/src/test/render.tsx +2 -2
@@ -6,7 +6,14 @@ import { ChevronRight, Ellipsis } from '../icon';
6
6
  import { Menu, MenuButton, MenuItem, MenuItems } from '../menu';
7
7
  import { usePagination } from '../pagination';
8
8
  import { Select } from '../select';
9
+ import { Text } from '../text';
9
10
  import { Drawer } from './Drawer';
11
+ import { DrawerActions } from './DrawerActions';
12
+ import { DrawerBottomPanel } from './DrawerBottomPanel';
13
+ import { useDrawer } from './DrawerContext';
14
+ import { DrawerPage } from './DrawerPage';
15
+ import { useDrawerPagination } from './DrawerPaginationContext';
16
+ import { DrawerTitle } from './DrawerTitle';
10
17
 
11
18
  const meta: Meta<typeof Drawer> = {
12
19
  title: 'Surfaces/Drawer',
@@ -118,55 +125,6 @@ export const Paginated: Story = {
118
125
  export const BottomPanel: Story = {
119
126
  args: {
120
127
  title: 'Transfer Out',
121
- children: (
122
- <div
123
- style={{
124
- width: '100%',
125
- display: 'flex',
126
- flexDirection: 'column',
127
- gap: '12px',
128
- }}
129
- >
130
- <h1>Transfer Policies:</h1>
131
- <Button kind="secondary">Read more...</Button>
132
- <p>
133
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla dignissim bibendum gravida. Donec
134
- pharetra, erat et semper luctus, dolor enim elementum est, eget cursus nisi libero sit amet purus.
135
- Fusce blandit leo in lectus blandit, sed elementum enim accumsan. Vestibulum ante ipsum primis in
136
- faucibus orci luctus et ultrices posuere cubilia curae; Pellentesque pretium erat at lacus ultricies
137
- tincidunt. Praesent non luctus magna, ac efficitur ligula. Sed a justo fermentum, feugiat mauris
138
- vel, ultrices turpis. Ut interdum malesuada lacus, ac posuere sapien feugiat et. Nulla dignissim
139
- bibendum gravida. Donec pharetra, erat et semper luctus, dolor enim elementum est, eget cursus nisi
140
- libero sit amet purus.
141
- </p>
142
- <p>
143
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla dignissim bibendum gravida. Donec
144
- pharetra, erat et semper luctus, dolor enim elementum est, eget cursus nisi libero sit amet purus.
145
- Fusce blandit leo in lectus blandit, sed elementum enim accumsan. Vestibulum ante ipsum primis in
146
- faucibus orci luctus et ultrices posuere cubilia curae; Pellentesque pretium erat at lacus ultricies
147
- tincidunt. Praesent non luctus magna, ac efficitur ligula. Sed a justo fermentum, feugiat mauris
148
- vel, ultrices turpis. Ut interdum malesuada lacus, ac posuere sapien feugiat et. Nulla dignissim
149
- bibendum gravida. Donec pharetra, erat et semper luctus, dolor enim elementum est, eget cursus nisi
150
- libero sit amet purus.
151
- </p>
152
- </div>
153
- ),
154
- bottomPanel: (
155
- <div
156
- style={{
157
- width: '100%',
158
- display: 'flex',
159
- flexDirection: 'column',
160
- gap: '12px',
161
- }}
162
- >
163
- <Callout>Transfer should arrive in your account within 2-3 business days.</Callout>
164
- <Button>Initiate</Button>
165
- <Button kind="secondary" theme="negative">
166
- Cancel
167
- </Button>
168
- </div>
169
- ),
170
128
  },
171
129
  render: function Render(args) {
172
130
  const [isOpen, setIsOpen] = useState(false);
@@ -194,66 +152,52 @@ export const BottomPanel: Story = {
194
152
  </Menu>
195
153
  }
196
154
  >
197
- {args.children}
198
- </Drawer>
199
- </>
200
- );
201
- },
202
- };
203
-
204
- export const BottomPanelMultiSection: Story = {
205
- args: {
206
- title: 'Order summary',
207
- children: (
208
- <div
209
- style={{
210
- width: '100%',
211
- display: 'flex',
212
- flexDirection: 'column',
213
- gap: '12px',
214
- }}
215
- >
216
- <p>Review your order before confirming.</p>
217
- </div>
218
- ),
219
- bottomPanelPadding: false,
220
- bottomPanel: (
221
- <div style={{ display: 'flex', flexDirection: 'column' }}>
222
- <div
223
- style={{
224
- display: 'flex',
225
- justifyContent: 'space-between',
226
- padding: '12px 20px',
227
- borderBottom: '1px solid var(--pte-new-colors-borderMedium)',
228
- background: 'var(--pte-new-colors-overlaySubtle)',
229
- }}
230
- >
231
- <span>Total</span>
232
- <strong>$249.00</strong>
233
- </div>
234
- <div
235
- style={{
236
- display: 'flex',
237
- flexDirection: 'column',
238
- gap: '12px',
239
- padding: '20px',
240
- }}
241
- >
242
- <Button>Confirm order</Button>
243
- <Button kind="secondary" theme="negative">
244
- Cancel
245
- </Button>
246
- </div>
247
- </div>
248
- ),
249
- },
250
- render: function Render(args) {
251
- const [isOpen, setIsOpen] = useState(false);
252
- return (
253
- <>
254
- <Button onClick={() => setIsOpen(true)}>Review order</Button>
255
- <Drawer {...args} isOpen={isOpen} onClose={setIsOpen}>
256
- {args.children}
155
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
156
+ <h1>Transfer Policies:</h1>
157
+ <Button kind="secondary">Read more...</Button>
158
+ <p>
159
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla dignissim bibendum gravida.
160
+ Donec pharetra, erat et semper luctus, dolor enim elementum est, eget cursus nisi libero sit
161
+ amet purus. Fusce blandit leo in lectus blandit, sed elementum enim accumsan.Lorem ipsum
162
+ dolor sit amet, consectetur adipiscing elit. Nulla dignissim bibendum gravida. Donec
163
+ pharetra, erat et semper luctus, dolor enim elementum est, eget cursus nisi libero sit amet
164
+ purus. Fusce blandit leo in lectus blandit, sed elementum enim accumsan.Lorem ipsum dolor
165
+ sit amet, consectetur adipiscing elit. Nulla dignissim bibendum gravida. Donec pharetra,
166
+ erat et semper luctus, dolor enim elementum est, eget cursus nisi libero sit amet purus.
167
+ Fusce blandit leo in lectus blandit, sed elementum enim accumsan.Lorem ipsum dolor sit amet,
168
+ consectetur adipiscing elit. Nulla dignissim bibendum gravida. Donec pharetra, erat et
169
+ semper luctus, dolor enim elementum est, eget cursus nisi libero sit amet purus. Fusce
170
+ blandit leo in lectus blandit, sed elementum enim accumsan.Lorem ipsum dolor sit amet,
171
+ consectetur adipiscing elit. Nulla dignissim bibendum gravida. Donec pharetra, erat et
172
+ semper luctus, dolor enim elementum est, eget cursus nisi libero sit amet purus. Fusce
173
+ blandit leo in lectus blandit, sed elementum enim accumsan.Lorem ipsum dolor sit amet,
174
+ consectetur adipiscing elit. Nulla dignissim bibendum gravida. Donec pharetra, erat et
175
+ semper luctus, dolor enim elementum est, eget cursus nisi libero sit amet purus. Fusce
176
+ blandit leo in lectus blandit, sed elementum enim accumsan.Lorem ipsum dolor sit amet,
177
+ consectetur adipiscing elit. Nulla dignissim bibendum gravida. Donec pharetra, erat et
178
+ semper luctus, dolor enim elementum est, eget cursus nisi libero sit amet purus. Fusce
179
+ blandit leo in lectus blandit, sed elementum enim accumsan.Lorem ipsum dolor sit amet,
180
+ consectetur adipiscing elit. Nulla dignissim bibendum gravida. Donec pharetra, erat et
181
+ semper luctus, dolor enim elementum est, eget cursus nisi libero sit amet purus. Fusce
182
+ blandit leo in lectus blandit, sed elementum enim accumsan.Lorem ipsum dolor sit amet,
183
+ consectetur adipiscing elit. Nulla dignissim bibendum gravida. Donec pharetra, erat et
184
+ semper luctus, dolor enim elementum est, eget cursus nisi libero sit amet purus. Fusce
185
+ blandit leo in lectus blandit, sed elementum enim accumsan.Lorem ipsum dolor sit amet,
186
+ consectetur adipiscing elit. Nulla dignissim bibendum gravida. Donec pharetra, erat et
187
+ semper luctus, dolor enim elementum est, eget cursus nisi libero sit amet purus. Fusce
188
+ blandit leo in lectus blandit, sed elementum enim accumsan.Lorem ipsum dolor sit amet,
189
+ consectetur adipiscing elit. Nulla dignissim bibendum gravida. Donec pharetra, erat et
190
+ semper luctus, dolor enim elementum est, eget cursus nisi libero sit amet purus. Fusce
191
+ blandit leo in lectus blandit, sed elementum enim accumsan.
192
+ </p>
193
+ </div>
194
+ <DrawerBottomPanel style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
195
+ <Callout>Transfer should arrive in your account within 2-3 business days.</Callout>
196
+ <Button>Initiate</Button>
197
+ <Button kind="secondary" theme="negative">
198
+ Cancel
199
+ </Button>
200
+ </DrawerBottomPanel>
257
201
  </Drawer>
258
202
  </>
259
203
  );
@@ -314,3 +258,237 @@ export const Full: Story = {
314
258
  );
315
259
  },
316
260
  };
261
+
262
+ export const CompoundAPI: Story = {
263
+ args: {},
264
+ render: function Render() {
265
+ const [isOpen, setIsOpen] = useState(false);
266
+ const pages = ['details', 'edit'] as const;
267
+ const pagination = usePagination<typeof pages>('details');
268
+
269
+ return (
270
+ <>
271
+ <Button onClick={() => setIsOpen(true)}>Open compound drawer</Button>
272
+ <Drawer
273
+ isOpen={isOpen}
274
+ onClose={setIsOpen}
275
+ title="Fallback Title"
276
+ pagination={pagination}
277
+ onAfterClose={() => pagination.reset()}
278
+ >
279
+ <DrawerPage id="details">
280
+ <DrawerTitle>Transaction Details</DrawerTitle>
281
+ <DrawerActions>
282
+ <Menu as="div">
283
+ <MenuButton>
284
+ <Button kind="tertiary" shape="circle" startEnhancer={<Ellipsis size={20} />}>
285
+ Action menu
286
+ </Button>
287
+ </MenuButton>
288
+ <MenuItems position="right">
289
+ <MenuItem as="button">Dispute</MenuItem>
290
+ </MenuItems>
291
+ </Menu>
292
+ </DrawerActions>
293
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
294
+ <p>This is the details page with its own title and actions.</p>
295
+ <Button onClick={() => pagination.open('edit')}>Go to Edit</Button>
296
+ </div>
297
+ <DrawerBottomPanel style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
298
+ <Button onClick={() => pagination.open('edit')}>Edit Transaction</Button>
299
+ </DrawerBottomPanel>
300
+ </DrawerPage>
301
+
302
+ <DrawerPage id="edit">
303
+ <DrawerTitle>Edit Transaction</DrawerTitle>
304
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
305
+ <p>This is the edit page. The title bar changed!</p>
306
+ <NestedFormExample />
307
+ </div>
308
+ </DrawerPage>
309
+ </Drawer>
310
+ </>
311
+ );
312
+ },
313
+ };
314
+
315
+ const NestedFormExample = () => {
316
+ const { close } = useDrawer();
317
+ const pagination = useDrawerPagination();
318
+ const [saving, setSaving] = useState(false);
319
+
320
+ return (
321
+ <>
322
+ <p>This form component uses useDrawer() and renders its own bottom panel.</p>
323
+ <DrawerBottomPanel style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
324
+ <Button
325
+ loading={saving}
326
+ onClick={() => {
327
+ setSaving(true);
328
+ setTimeout(() => {
329
+ setSaving(false);
330
+ close();
331
+ }, 1000);
332
+ }}
333
+ >
334
+ Save Changes
335
+ </Button>
336
+ <Button kind="secondary" onClick={() => pagination?.back()}>
337
+ Back
338
+ </Button>
339
+ </DrawerBottomPanel>
340
+ </>
341
+ );
342
+ };
343
+
344
+ // ─── Page Transition Stories ────────────────────────────────────────────────
345
+
346
+ const TransitionDemoPage = ({
347
+ step,
348
+ color,
349
+ onNext,
350
+ onPrev,
351
+ }: {
352
+ step: number;
353
+ color: string;
354
+ onNext?: () => void;
355
+ onPrev?: () => void;
356
+ }) => (
357
+ <div
358
+ style={{
359
+ display: 'flex',
360
+ flexDirection: 'column',
361
+ gap: '12px',
362
+ padding: '20px',
363
+ background: color,
364
+ borderRadius: '8px',
365
+ minHeight: '200px',
366
+ }}
367
+ >
368
+ <h3 style={{ margin: 0 }}>Step {step}</h3>
369
+ <p style={{ margin: 0 }}>This is step {step}. Navigate between pages to see the transition effect.</p>
370
+ <div style={{ display: 'flex', gap: '8px' }}>
371
+ {onPrev && (
372
+ <Button kind="secondary" onClick={onPrev}>
373
+ Back
374
+ </Button>
375
+ )}
376
+ {onNext && <Button onClick={onNext}>Next</Button>}
377
+ </div>
378
+ </div>
379
+ );
380
+
381
+ export const ProgressBar: Story = {
382
+ render: function Render() {
383
+ const [isOpen, setIsOpen] = useState(false);
384
+ const pages = ['account', 'details', 'confirm'] as const;
385
+ const pagination = usePagination<typeof pages>('account');
386
+
387
+ return (
388
+ <>
389
+ <Button onClick={() => setIsOpen(true)}>Open wizard with progress</Button>
390
+ <Drawer
391
+ isOpen={isOpen}
392
+ onClose={setIsOpen}
393
+ title="Wizard"
394
+ pagination={pagination}
395
+ pageTransition="slide"
396
+ progressBar
397
+ onAfterClose={() => pagination.reset()}
398
+ >
399
+ <DrawerPage id="account">
400
+ <DrawerTitle>Create Account</DrawerTitle>
401
+ <TransitionDemoPage
402
+ step={1}
403
+ color="var(--pte-new-colors-overlaySubtle)"
404
+ onNext={() => pagination.open('details')}
405
+ />
406
+ <DrawerBottomPanel style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
407
+ <Button style={{ width: '100%' }} onClick={() => pagination.open('details')}>
408
+ Continue
409
+ </Button>
410
+ </DrawerBottomPanel>
411
+ </DrawerPage>
412
+
413
+ <DrawerPage id="details">
414
+ <DrawerTitle>Add Details</DrawerTitle>
415
+ <TransitionDemoPage
416
+ step={2}
417
+ color="var(--pte-new-colors-overlaySubtle)"
418
+ onPrev={() => pagination.back()}
419
+ onNext={() => pagination.open('confirm')}
420
+ />
421
+ <DrawerBottomPanel style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
422
+ <Button style={{ width: '100%' }} onClick={() => pagination.open('confirm')}>
423
+ Continue
424
+ </Button>
425
+ <Button style={{ width: '100%' }} kind="secondary" onClick={() => pagination.back()}>
426
+ Back
427
+ </Button>
428
+ </DrawerBottomPanel>
429
+ </DrawerPage>
430
+
431
+ <DrawerPage id="confirm">
432
+ <DrawerTitle>Confirm</DrawerTitle>
433
+ <TransitionDemoPage
434
+ step={3}
435
+ color="var(--pte-new-colors-overlaySubtle)"
436
+ onPrev={() => pagination.back()}
437
+ />
438
+ <DrawerBottomPanel style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
439
+ <Button style={{ width: '100%' }} theme="positive">
440
+ Submit
441
+ </Button>
442
+ <Button style={{ width: '100%' }} kind="secondary" onClick={() => pagination.back()}>
443
+ Back
444
+ </Button>
445
+ </DrawerBottomPanel>
446
+ </DrawerPage>
447
+ </Drawer>
448
+ </>
449
+ );
450
+ },
451
+ };
452
+
453
+ export const AppendModeBottomPanel: Story = {
454
+ args: {},
455
+ render: function Render() {
456
+ const [isOpen, setIsOpen] = useState(false);
457
+
458
+ return (
459
+ <>
460
+ <Button onClick={() => setIsOpen(true)}>Open append mode drawer</Button>
461
+ <Drawer isOpen={isOpen} onClose={setIsOpen} title="Append Mode Demo">
462
+ <p>
463
+ The bottom panel uses multiple DrawerBottomPanel components with priority ordering and automatic
464
+ separator borders.
465
+ </p>
466
+ <DrawerBottomPanel
467
+ mode="append"
468
+ priority={10}
469
+ style={{
470
+ display: 'flex',
471
+ justifyContent: 'space-between',
472
+ padding: '14px 20px',
473
+ }}
474
+ >
475
+ <Text kind="paragraphXSmall" weight="medium">
476
+ Total
477
+ </Text>
478
+ <Text kind="paragraphSmall" weight="medium">
479
+ $249.00
480
+ </Text>
481
+ </DrawerBottomPanel>
482
+ <DrawerBottomPanel
483
+ mode="append"
484
+ priority={20}
485
+ style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}
486
+ >
487
+ <Callout>Free shipping on orders over $200</Callout>
488
+ <Button style={{ width: '100%' }}>Confirm Order</Button>
489
+ </DrawerBottomPanel>
490
+ </Drawer>
491
+ </>
492
+ );
493
+ },
494
+ };