@sproutsocial/seeds-react-modal 1.1.1 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/.turbo/turbo-build.log +23 -23
  2. package/CHANGELOG.md +182 -0
  3. package/dist/ModalAction-BB7qJtQj.d.mts +445 -0
  4. package/dist/ModalAction-BB7qJtQj.d.ts +445 -0
  5. package/dist/esm/chunk-ETVICNHP.js +1353 -0
  6. package/dist/esm/chunk-ETVICNHP.js.map +1 -0
  7. package/dist/esm/index.js +11 -11
  8. package/dist/esm/index.js.map +1 -1
  9. package/dist/esm/v2/index.js +12 -20
  10. package/dist/index.d.mts +2 -1
  11. package/dist/index.d.ts +2 -1
  12. package/dist/index.js +1154 -546
  13. package/dist/index.js.map +1 -1
  14. package/dist/v2/index.d.mts +4 -11
  15. package/dist/v2/index.d.ts +4 -11
  16. package/dist/v2/index.js +1152 -545
  17. package/dist/v2/index.js.map +1 -1
  18. package/package.json +8 -7
  19. package/src/index.ts +11 -12
  20. package/src/shared/constants.ts +11 -7
  21. package/src/v2/Modal.tsx +169 -0
  22. package/src/v2/ModalTypes.ts +343 -0
  23. package/src/v2/ModalV2.stories.tsx +413 -128
  24. package/src/v2/MotionConfig.ts +185 -0
  25. package/src/v2/components/ModalAction.tsx +94 -0
  26. package/src/v2/components/ModalBody.tsx +54 -0
  27. package/src/v2/components/ModalCloseWrapper.tsx +35 -0
  28. package/src/v2/components/ModalContent.tsx +288 -11
  29. package/src/v2/components/ModalDescription.tsx +14 -2
  30. package/src/v2/components/ModalFooter.tsx +94 -13
  31. package/src/v2/components/ModalHeader.tsx +77 -34
  32. package/src/v2/components/ModalOverlay.tsx +52 -0
  33. package/src/v2/components/ModalRail.tsx +35 -99
  34. package/src/v2/components/index.ts +11 -7
  35. package/src/v2/index.ts +13 -16
  36. package/dist/ModalRail-5PeilhW7.d.mts +0 -186
  37. package/dist/ModalRail-5PeilhW7.d.ts +0 -186
  38. package/dist/esm/chunk-4ITF4DBY.js +0 -717
  39. package/dist/esm/chunk-4ITF4DBY.js.map +0 -1
  40. package/src/v2/ModalV2.tsx +0 -388
  41. package/src/v2/ModalV2Styles.tsx +0 -180
  42. package/src/v2/ModalV2Types.ts +0 -154
  43. package/src/v2/components/ModalClose.tsx +0 -29
  44. package/src/v2/components/ModalCloseButton.tsx +0 -100
  45. package/src/v2/components/ModalTrigger.tsx +0 -39
@@ -4,10 +4,17 @@ import { Box } from "@sproutsocial/seeds-react-box";
4
4
  import { Button } from "@sproutsocial/seeds-react-button";
5
5
  import Text from "@sproutsocial/seeds-react-text";
6
6
  import { FormField } from "@sproutsocial/seeds-react-form-field";
7
- import { Modal, ModalHeader, ModalFooter, ModalContent, ModalClose } from "./";
7
+ import {
8
+ Modal,
9
+ ModalHeader,
10
+ ModalFooter,
11
+ ModalBody,
12
+ ModalCloseWrapper,
13
+ ModalCustomFooter,
14
+ } from ".";
8
15
 
9
16
  const meta: Meta<typeof Modal> = {
10
- title: "Components/Modal (Radix UI)",
17
+ title: "Components/Modal V2",
11
18
  component: Modal,
12
19
  parameters: {
13
20
  docs: {
@@ -51,16 +58,6 @@ const meta: Meta<typeof Modal> = {
51
58
  control: { type: "text" },
52
59
  description: "Modal description (auto-wrapped in Dialog.Description)",
53
60
  },
54
- size: {
55
- control: { type: "select" },
56
- options: ["small", "medium", "large", "full", "400px", "800px"],
57
- description: "Modal size preset or custom width",
58
- },
59
- priority: {
60
- control: { type: "select" },
61
- options: ["low", "medium", "high"],
62
- description: "Priority level for z-index",
63
- },
64
61
  draggable: {
65
62
  control: { type: "boolean" },
66
63
  description: "Enable draggable functionality",
@@ -69,7 +66,6 @@ const meta: Meta<typeof Modal> = {
69
66
  args: {
70
67
  "aria-label": "Example Modal",
71
68
  showOverlay: true,
72
- size: "medium",
73
69
  draggable: false,
74
70
  },
75
71
  };
@@ -82,34 +78,19 @@ export const Default: Story = {
82
78
  return (
83
79
  <Modal
84
80
  {...args}
81
+ closeButtonAriaLabel="Close Modal"
85
82
  modalTrigger={<Button appearance="primary">Open Modal</Button>}
86
83
  >
87
- <ModalContent>
88
- <ModalHeader title="Modal Title" subtitle="This is a subtitle" />
89
- <Box
90
- border="1px dashed"
91
- borderColor="purple.500"
92
- p={500}
93
- bg="purple.200"
94
- borderRadius="6px"
95
- >
96
- <Text>
97
- This modal uses uncontrolled state - no need to manage open/close
98
- state! The modalTrigger prop and floating close button handle
99
- everything.
100
- </Text>
101
- </Box>
102
- <ModalFooter>
103
- <Box display="flex" justifyContent="flex-end" gap={300}>
104
- <ModalClose asChild>
105
- <Button>Cancel</Button>
106
- </ModalClose>
107
- <ModalClose asChild>
108
- <Button appearance="primary">Confirm</Button>
109
- </ModalClose>
110
- </Box>
111
- </ModalFooter>
112
- </ModalContent>
84
+ <ModalHeader title="Modal Title" subtitle="This is a subtitle" />
85
+ <ModalBody>
86
+ This modal uses uncontrolled state - no need to manage open/close
87
+ state! The modalTrigger prop and floating close button handle
88
+ everything.
89
+ </ModalBody>
90
+ <ModalFooter
91
+ cancelButton={<Button>Cancel</Button>}
92
+ primaryButton={<Button appearance="primary">Confirm</Button>}
93
+ />
113
94
  </Modal>
114
95
  );
115
96
  },
@@ -119,7 +100,7 @@ export const WithActions: Story = {
119
100
  render: () => {
120
101
  return (
121
102
  <Modal
122
- size="medium"
103
+ aria-label="Modal With Actions"
123
104
  modalTrigger={
124
105
  <Button appearance="primary">Open Modal With Actions</Button>
125
106
  }
@@ -136,35 +117,21 @@ export const WithActions: Story = {
136
117
  },
137
118
  ]}
138
119
  >
139
- <ModalContent>
140
- <ModalHeader
141
- title="Modal With Actions"
142
- subtitle="This modal has extra floating actions"
143
- />
144
- <Box
145
- border="1px dashed"
146
- borderColor="purple.500"
147
- p={500}
148
- bg="purple.200"
149
- borderRadius="6px"
150
- >
151
- <Text>
152
- This modal shows the floating close by default and includes extra
153
- actions on the rail via the actions prop. The floating close
154
- button is always present, and additional actions appear below it.
155
- </Text>
156
- </Box>
157
- <ModalFooter>
158
- <Box display="flex" justifyContent="flex-end" gap={300}>
159
- <ModalClose asChild>
160
- <Button>Cancel</Button>
161
- </ModalClose>
162
- <ModalClose asChild>
163
- <Button appearance="primary">Confirm</Button>
164
- </ModalClose>
165
- </Box>
166
- </ModalFooter>
167
- </ModalContent>
120
+ <ModalHeader
121
+ title="Modal With Actions"
122
+ subtitle="This modal has extra floating actions"
123
+ />
124
+ <ModalBody>
125
+ <Text>
126
+ This modal shows the floating close by default and includes extra
127
+ actions on the rail via the actions prop. The floating close button
128
+ is always present, and additional actions appear below it.
129
+ </Text>
130
+ </ModalBody>
131
+ <ModalFooter
132
+ cancelButton={<Button>Cancel</Button>}
133
+ primaryButton={<Button appearance="primary">Confirm</Button>}
134
+ />
168
135
  </Modal>
169
136
  );
170
137
  },
@@ -175,23 +142,22 @@ export const DraggableModal: Story = {
175
142
  return (
176
143
  <Modal
177
144
  aria-label="Draggable Modal"
178
- size="medium"
179
145
  draggable={true}
180
146
  showOverlay={false}
181
147
  modalTrigger={
182
148
  <Button appearance="primary">Open Draggable Modal</Button>
183
149
  }
184
150
  >
185
- <ModalContent>
186
- <ModalHeader
187
- title="Draggable Modal"
188
- subtitle="Click and drag anywhere on this modal to move it around"
189
- />
151
+ <ModalHeader
152
+ title="Draggable Modal"
153
+ subtitle="Click and drag anywhere on this modal to move it around"
154
+ />
155
+ <ModalBody>
190
156
  <Box
191
157
  border="1px dashed"
192
- borderColor="blue.500"
158
+ borderColor="container.border.info"
193
159
  p={500}
194
- bg="blue.200"
160
+ bg="container.background.info"
195
161
  borderRadius="6px"
196
162
  >
197
163
  <Text>
@@ -201,14 +167,11 @@ export const DraggableModal: Story = {
201
167
  different corners of the screen.
202
168
  </Text>
203
169
  </Box>
204
- <ModalFooter>
205
- <Box display="flex" justifyContent="flex-end" gap={300}>
206
- <ModalClose asChild>
207
- <Button appearance="primary">Got it!</Button>
208
- </ModalClose>
209
- </Box>
210
- </ModalFooter>
211
- </ModalContent>
170
+ </ModalBody>
171
+ <ModalFooter
172
+ cancelButton={<Button>Cancel</Button>}
173
+ primaryButton={<Button appearance="primary">Got it!</Button>}
174
+ />
212
175
  </Modal>
213
176
  );
214
177
  },
@@ -235,41 +198,46 @@ export const ControlledState: Story = {
235
198
  if (open) setCount((c) => c + 1);
236
199
  }}
237
200
  aria-label="Controlled Modal"
238
- size="medium"
239
201
  modalTrigger={
240
202
  <Button appearance="primary">Open Controlled Modal</Button>
241
203
  }
242
204
  >
243
- <ModalContent>
244
- <ModalHeader
245
- title="Controlled Modal"
246
- subtitle="This modal's state is managed externally"
247
- />
205
+ <ModalHeader
206
+ title="Controlled Modal"
207
+ subtitle="This modal's state is managed externally"
208
+ />
209
+ <ModalBody>
248
210
  <Box
249
211
  border="1px dashed"
250
- borderColor="purple.500"
212
+ borderColor="container.border.base"
251
213
  p={500}
252
- bg="purple.200"
214
+ bg="container.background.decorative.neutral"
253
215
  borderRadius="6px"
254
216
  >
255
217
  <Text>
256
218
  This modal's state is controlled by the parent component. You
257
- can track when it opens/closes and perform side effects. The
258
- floating close button still works, but the state is managed
259
- externally.
219
+ can track when it opens/closes and perform side effects. This
220
+ example uses ModalCustomFooter for full control over the footer
221
+ layout and behavior.
260
222
  </Text>
261
223
  </Box>
262
- <ModalFooter>
263
- <Box display="flex" justifyContent="flex-end" gap={300}>
264
- <ModalClose asChild>
265
- <Button>Cancel</Button>
266
- </ModalClose>
267
- <ModalClose asChild>
268
- <Button appearance="primary">Confirm</Button>
269
- </ModalClose>
270
- </Box>
271
- </ModalFooter>
272
- </ModalContent>
224
+ </ModalBody>
225
+ <ModalCustomFooter>
226
+ <Box display="flex" justifyContent="flex-end" gap={300}>
227
+ <ModalCloseWrapper>
228
+ <Button>Cancel</Button>
229
+ </ModalCloseWrapper>
230
+ <Button
231
+ appearance="primary"
232
+ onClick={() => {
233
+ console.log("Confirmed!");
234
+ setIsOpen(false);
235
+ }}
236
+ >
237
+ Confirm
238
+ </Button>
239
+ </Box>
240
+ </ModalCustomFooter>
273
241
  </Modal>
274
242
 
275
243
  <Box mt={300}>
@@ -319,10 +287,10 @@ export const DraggableWithFormInteraction: Story = {
319
287
 
320
288
  <Box
321
289
  p={500}
322
- bg="neutral.100"
290
+ bg="container.background.decorative.neutral"
323
291
  borderRadius="8px"
324
292
  border="2px solid"
325
- borderColor="neutral.400"
293
+ borderColor="container.border.base"
326
294
  >
327
295
  <Text fontSize={400} fontWeight="bold" mb={300}>
328
296
  Contact Form
@@ -442,18 +410,17 @@ export const DraggableWithFormInteraction: Story = {
442
410
 
443
411
  <Modal
444
412
  aria-label="Reference Modal"
445
- size="medium"
446
413
  draggable={true}
447
- showOverlay={true}
414
+ showOverlay={false}
448
415
  modalTrigger={
449
416
  <Button appearance="secondary">Open Reference Modal</Button>
450
417
  }
451
418
  >
452
- <ModalContent>
453
- <ModalHeader
454
- title="Reference Information"
455
- subtitle="Drag this modal by the header to move it around"
456
- />
419
+ <ModalHeader
420
+ title="Reference Information"
421
+ subtitle="Drag this modal by the header to move it around"
422
+ />
423
+ <ModalBody>
457
424
  <Box p={300}>
458
425
  <Text fontSize={400} fontWeight="bold" mb={200}>
459
426
  Instructions for filling out the form:
@@ -484,10 +451,10 @@ export const DraggableWithFormInteraction: Story = {
484
451
  <Box
485
452
  mt={400}
486
453
  p={300}
487
- bg="blue.200"
454
+ bg="container.background.info"
488
455
  borderRadius="6px"
489
456
  border="1px solid"
490
- borderColor="blue.400"
457
+ borderColor="container.border.info"
491
458
  >
492
459
  <Text fontWeight="bold" mb={100}>
493
460
  Try this:
@@ -505,14 +472,13 @@ export const DraggableWithFormInteraction: Story = {
505
472
  </Text>
506
473
  </Box>
507
474
  </Box>
508
- <ModalFooter>
509
- <Box display="flex" justifyContent="flex-end">
510
- <ModalClose asChild>
511
- <Button appearance="primary">Got it!</Button>
512
- </ModalClose>
513
- </Box>
514
- </ModalFooter>
515
- </ModalContent>
475
+ </ModalBody>
476
+ <ModalFooter
477
+ cancelButton={<Button>Cancel</Button>}
478
+ primaryButton={
479
+ <Button appearance="primary">Got it!</Button>
480
+ }
481
+ />
516
482
  </Modal>
517
483
  </Box>
518
484
  </Box>
@@ -522,10 +488,10 @@ export const DraggableWithFormInteraction: Story = {
522
488
  <Box
523
489
  mt={400}
524
490
  p={400}
525
- bg="green.200"
491
+ bg="container.background.success"
526
492
  borderRadius="6px"
527
493
  border="2px solid"
528
- borderColor="green.500"
494
+ borderColor="container.border.success"
529
495
  >
530
496
  <Text fontSize={400} fontWeight="bold" mb={200}>
531
497
  Form Submitted!
@@ -551,3 +517,322 @@ export const DraggableWithFormInteraction: Story = {
551
517
  );
552
518
  },
553
519
  };
520
+
521
+ export const TallContentMobile: Story = {
522
+ render: () => {
523
+ return (
524
+ <Modal
525
+ aria-label="Tall Content Modal"
526
+ modalTrigger={
527
+ <Button appearance="primary">Open Tall Modal (Test Mobile)</Button>
528
+ }
529
+ >
530
+ <ModalHeader
531
+ title="Very Tall Modal"
532
+ subtitle="Resize your browser to ≤780px to see mobile bottom sheet behavior"
533
+ />
534
+ <ModalBody>
535
+ <Box p={300}>
536
+ <Text fontSize={400} fontWeight="bold" mb={300}>
537
+ Testing Mobile Responsiveness
538
+ </Text>
539
+ <Text mb={300}>
540
+ This modal has a lot of content to test how it behaves on mobile
541
+ screens. The modal should:
542
+ </Text>
543
+ <Box as="ul" pl={400} mb={400}>
544
+ <li>
545
+ <Text mb={200}>
546
+ ✅ Anchor to the bottom of the screen on mobile (≤780px)
547
+ </Text>
548
+ </li>
549
+ <li>
550
+ <Text mb={200}>
551
+ ✅ Have rounded top corners and flat bottom corners
552
+ </Text>
553
+ </li>
554
+ <li>
555
+ <Text mb={200}>✅ Show the action rail above the modal</Text>
556
+ </li>
557
+ <li>
558
+ <Text mb={200}>✅ Hug the content height</Text>
559
+ </li>
560
+ <li>
561
+ <Text mb={200}>
562
+ ✅ Scroll the content area when content exceeds max-height
563
+ </Text>
564
+ </li>
565
+ </Box>
566
+
567
+ <Box
568
+ bg="container.background.decorative.purple"
569
+ border="1px solid"
570
+ borderColor="container.border.decorative.purple"
571
+ borderRadius="8px"
572
+ p={400}
573
+ mb={400}
574
+ >
575
+ <Text fontWeight="bold" mb={200}>
576
+ Section 1: Lorem Ipsum
577
+ </Text>
578
+ <Text mb={200}>
579
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
580
+ eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
581
+ enim ad minim veniam, quis nostrud exercitation ullamco laboris
582
+ nisi ut aliquip ex ea commodo consequat.
583
+ </Text>
584
+ <Text>
585
+ Duis aute irure dolor in reprehenderit in voluptate velit esse
586
+ cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
587
+ cupidatat non proident, sunt in culpa qui officia deserunt
588
+ mollit anim id est laborum.
589
+ </Text>
590
+ </Box>
591
+
592
+ <Box
593
+ bg="container.background.decorative.blue"
594
+ border="1px solid"
595
+ borderColor="container.border.decorative.blue"
596
+ borderRadius="8px"
597
+ p={400}
598
+ mb={400}
599
+ >
600
+ <Text fontWeight="bold" mb={200}>
601
+ Section 2: More Content
602
+ </Text>
603
+ <Text mb={200}>
604
+ Pellentesque habitant morbi tristique senectus et netus et
605
+ malesuada fames ac turpis egestas. Vestibulum tortor quam,
606
+ feugiat vitae, ultricies eget, tempor sit amet, ante.
607
+ </Text>
608
+ <Text>
609
+ Donec eu libero sit amet quam egestas semper. Aenean ultricies
610
+ mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est
611
+ et sapien ullamcorper pharetra.
612
+ </Text>
613
+ </Box>
614
+
615
+ <Box
616
+ bg="container.background.decorative.green"
617
+ border="1px solid"
618
+ borderColor="container.border.decorative.green"
619
+ borderRadius="8px"
620
+ p={400}
621
+ mb={400}
622
+ >
623
+ <Text fontWeight="bold" mb={200}>
624
+ Section 3: Even More Content
625
+ </Text>
626
+ <Text mb={200}>
627
+ Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit
628
+ amet, wisi. Aenean fermentum, elit eget tincidunt condimentum,
629
+ eros ipsum rutrum orci, sagittis tempus lacus enim ac dui.
630
+ </Text>
631
+ <Text>
632
+ Donec non enim in turpis pulvinar facilisis. Ut felis. Praesent
633
+ dapibus, neque id cursus faucibus, tortor neque egestas augue,
634
+ eu vulputate magna eros eu erat.
635
+ </Text>
636
+ </Box>
637
+
638
+ <Box
639
+ bg="container.background.decorative.orange"
640
+ border="1px solid"
641
+ borderColor="container.border.decorative.orange"
642
+ borderRadius="8px"
643
+ p={400}
644
+ mb={400}
645
+ >
646
+ <Text fontWeight="bold" mb={200}>
647
+ Section 4: Final Section
648
+ </Text>
649
+ <Text mb={200}>
650
+ Aliquam erat volutpat. Nunc eleifend leo vitae magna. In id erat
651
+ non orci commodo lobortis. Proin neque massa, cursus ut, gravida
652
+ ut, lobortis eget, lacus.
653
+ </Text>
654
+ <Text>
655
+ Sed diam. Praesent fermentum tempor tellus. Nullam tempus.
656
+ Mauris ac felis vel velit tristique imperdiet. Donec at pede.
657
+ Etiam vel neque nec dui dignissim bibendum.
658
+ </Text>
659
+ </Box>
660
+
661
+ <Box
662
+ bg="container.background.decorative.red"
663
+ border="1px solid"
664
+ borderColor="container.border.decorative.red"
665
+ borderRadius="8px"
666
+ p={400}
667
+ >
668
+ <Text fontWeight="bold" mb={200}>
669
+ 🎯 How to Test
670
+ </Text>
671
+ <Text fontSize={200} mb={200}>
672
+ 1. Open your browser's DevTools (F12 or Cmd+Option+I on Mac)
673
+ </Text>
674
+ <Text fontSize={200} mb={200}>
675
+ 2. Toggle device toolbar (mobile view icon)
676
+ </Text>
677
+ <Text fontSize={200} mb={200}>
678
+ 3. Set width to 780px or less (tablet/mobile size)
679
+ </Text>
680
+ <Text fontSize={200}>
681
+ 4. Observe the bottom sheet behavior with flat bottom corners
682
+ </Text>
683
+ </Box>
684
+ </Box>
685
+ </ModalBody>
686
+ <ModalFooter
687
+ cancelButton={<Button>Cancel</Button>}
688
+ primaryButton={<Button appearance="primary">Confirm</Button>}
689
+ />
690
+ </Modal>
691
+ );
692
+ },
693
+ };
694
+
695
+ export const SimplifiedFooterAPI: Story = {
696
+ render: () => {
697
+ return (
698
+ <Modal
699
+ aria-label="Simplified Footer API Example"
700
+ modalTrigger={
701
+ <Button appearance="primary">Open Modal with Simple Footer</Button>
702
+ }
703
+ >
704
+ <ModalHeader
705
+ title="Simplified Footer API"
706
+ subtitle="No need to wrap buttons or manage layout"
707
+ />
708
+ <ModalBody>
709
+ <Text mb={300}>
710
+ The new ModalFooter API makes it easy to add standard footer buttons
711
+ without boilerplate code.
712
+ </Text>
713
+ <Text>
714
+ Simply pass your Button elements to cancelButton and primaryButton
715
+ props. Both buttons automatically close the modal. You can add
716
+ custom onClick handlers that run before closing (e.g., for
717
+ validation or saving data).
718
+ </Text>
719
+ </ModalBody>
720
+ <ModalFooter
721
+ cancelButton={<Button>Cancel</Button>}
722
+ primaryButton={
723
+ <Button
724
+ appearance="primary"
725
+ onClick={() => {
726
+ console.log("Saving data...");
727
+ // Your custom logic here runs before the modal closes
728
+ }}
729
+ >
730
+ Save
731
+ </Button>
732
+ }
733
+ />
734
+ </Modal>
735
+ );
736
+ },
737
+ };
738
+
739
+ export const FooterWithLeftAction: Story = {
740
+ render: () => {
741
+ const [isOpen, setIsOpen] = useState(false);
742
+
743
+ return (
744
+ <Modal
745
+ open={isOpen}
746
+ onOpenChange={setIsOpen}
747
+ aria-label="Footer with Left Action"
748
+ modalTrigger={
749
+ <Button appearance="primary">Open Modal with Delete Action</Button>
750
+ }
751
+ >
752
+ <ModalHeader
753
+ title="Edit Document"
754
+ subtitle="Make changes or delete this document"
755
+ />
756
+ <ModalBody>
757
+ <Text mb={300}>
758
+ This modal showcases the leftAction prop, perfect for destructive
759
+ actions like Delete that should be separated from the primary
760
+ actions.
761
+ </Text>
762
+ <Text>
763
+ The Delete button appears on the far left, while Cancel and Save are
764
+ grouped together on the right. Note that leftAction is NOT
765
+ auto-wrapped, so you control when/how to close the modal.
766
+ </Text>
767
+ </ModalBody>
768
+ <ModalFooter
769
+ leftAction={
770
+ <Button
771
+ appearance="destructive"
772
+ onClick={() => {
773
+ if (confirm("Are you sure you want to delete?")) {
774
+ console.log("Deleting...");
775
+ setIsOpen(false);
776
+ }
777
+ }}
778
+ >
779
+ Delete
780
+ </Button>
781
+ }
782
+ cancelButton={<Button>Cancel</Button>}
783
+ primaryButton={
784
+ <Button
785
+ appearance="primary"
786
+ onClick={() => {
787
+ console.log("Saving...");
788
+ // Modal auto-closes after this runs
789
+ }}
790
+ >
791
+ Save Changes
792
+ </Button>
793
+ }
794
+ />
795
+ </Modal>
796
+ );
797
+ },
798
+ };
799
+
800
+ export const CustomFooterOverride: Story = {
801
+ render: () => {
802
+ return (
803
+ <Modal
804
+ aria-label="Custom Footer Override"
805
+ modalTrigger={<Button appearance="primary">Open Custom Footer</Button>}
806
+ >
807
+ <ModalHeader
808
+ title="Custom Footer Layout"
809
+ subtitle="Use ModalCustomFooter for complete control"
810
+ />
811
+ <ModalBody>
812
+ <Text>
813
+ For edge cases where you need complete control over the footer
814
+ layout, you can use ModalCustomFooter instead of ModalFooter. This
815
+ gives you the base styled footer component with full control over
816
+ the content.
817
+ </Text>
818
+ </ModalBody>
819
+ <ModalCustomFooter>
820
+ <Box display="flex" justifyContent="space-between" width="100%">
821
+ <Box display="flex" gap={200}>
822
+ <Button appearance="secondary">Help</Button>
823
+ <Button appearance="secondary">More Info</Button>
824
+ </Box>
825
+ <Box display="flex" gap={300}>
826
+ <ModalCloseWrapper>
827
+ <Button>Cancel</Button>
828
+ </ModalCloseWrapper>
829
+ <ModalCloseWrapper>
830
+ <Button appearance="primary">Confirm</Button>
831
+ </ModalCloseWrapper>
832
+ </Box>
833
+ </Box>
834
+ </ModalCustomFooter>
835
+ </Modal>
836
+ );
837
+ },
838
+ };