@sproutsocial/seeds-react-modal 2.1.1 → 2.2.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/.turbo/turbo-build.log +16 -16
- package/CHANGELOG.md +30 -0
- package/dist/{ModalAction-BB7qJtQj.d.mts → ModalAction-gIgCE73I.d.mts} +163 -189
- package/dist/{ModalAction-BB7qJtQj.d.ts → ModalAction-gIgCE73I.d.ts} +163 -189
- package/dist/esm/{chunk-ETVICNHP.js → chunk-52SXX6AG.js} +65 -10
- package/dist/esm/chunk-52SXX6AG.js.map +1 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/v2/index.js +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +64 -9
- package/dist/index.js.map +1 -1
- package/dist/v2/index.d.mts +1 -1
- package/dist/v2/index.d.ts +1 -1
- package/dist/v2/index.js +64 -9
- package/dist/v2/index.js.map +1 -1
- package/package.json +7 -7
- package/src/v2/Modal.tsx +14 -2
- package/src/v2/ModalTypes.ts +191 -201
- package/src/v2/ModalV2.stories.tsx +106 -66
- package/src/v2/components/ModalAction.tsx +4 -0
- package/src/v2/components/ModalBody.tsx +6 -1
- package/src/v2/components/ModalCloseWrapper.tsx +7 -1
- package/src/v2/components/ModalContent.tsx +19 -4
- package/src/v2/components/ModalDescription.tsx +6 -1
- package/src/v2/components/ModalFooter.tsx +1 -1
- package/src/v2/components/ModalHeader.tsx +2 -0
- package/src/v2/components/ModalOverlay.tsx +2 -1
- package/dist/esm/chunk-ETVICNHP.js.map +0 -1
|
@@ -101,6 +101,7 @@ export const WithActions: Story = {
|
|
|
101
101
|
return (
|
|
102
102
|
<Modal
|
|
103
103
|
aria-label="Modal With Actions"
|
|
104
|
+
closeButtonAriaLabel="Close Modal"
|
|
104
105
|
modalTrigger={
|
|
105
106
|
<Button appearance="primary">Open Modal With Actions</Button>
|
|
106
107
|
}
|
|
@@ -137,11 +138,56 @@ export const WithActions: Story = {
|
|
|
137
138
|
},
|
|
138
139
|
};
|
|
139
140
|
|
|
141
|
+
export const CustomCloseButton: Story = {
|
|
142
|
+
render: () => {
|
|
143
|
+
const [clickCount, setClickCount] = useState(0);
|
|
144
|
+
|
|
145
|
+
return (
|
|
146
|
+
<Box>
|
|
147
|
+
<Text mb={400}>Close button clicked {clickCount} times</Text>
|
|
148
|
+
<Modal
|
|
149
|
+
aria-label="Modal With Custom Close Button"
|
|
150
|
+
modalTrigger={
|
|
151
|
+
<Button appearance="primary">Open Modal With Custom Close</Button>
|
|
152
|
+
}
|
|
153
|
+
closeButtonProps={{
|
|
154
|
+
"aria-label": "Close this dialog",
|
|
155
|
+
onClick: () => {
|
|
156
|
+
console.log("Close button clicked!");
|
|
157
|
+
setClickCount((prev) => prev + 1);
|
|
158
|
+
},
|
|
159
|
+
id: "custom-close-btn",
|
|
160
|
+
iconName: "x-outline",
|
|
161
|
+
}}
|
|
162
|
+
>
|
|
163
|
+
<ModalHeader
|
|
164
|
+
title="Custom Close Button"
|
|
165
|
+
subtitle="The close button has custom props"
|
|
166
|
+
/>
|
|
167
|
+
<ModalBody>
|
|
168
|
+
<Text>
|
|
169
|
+
This modal demonstrates the closeButtonProps API. The close button
|
|
170
|
+
has a custom onClick handler that tracks clicks, a custom id
|
|
171
|
+
attribute, and a custom aria-label. You can pass any button
|
|
172
|
+
attributes like disabled, className, or data attributes.
|
|
173
|
+
</Text>
|
|
174
|
+
</ModalBody>
|
|
175
|
+
<ModalFooter
|
|
176
|
+
cancelButton={<Button>Cancel</Button>}
|
|
177
|
+
primaryButton={<Button appearance="primary">Confirm</Button>}
|
|
178
|
+
/>
|
|
179
|
+
</Modal>
|
|
180
|
+
</Box>
|
|
181
|
+
);
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
|
|
140
185
|
export const DraggableModal: Story = {
|
|
141
186
|
render: () => {
|
|
142
187
|
return (
|
|
143
188
|
<Modal
|
|
144
189
|
aria-label="Draggable Modal"
|
|
190
|
+
closeButtonAriaLabel="Close Modal"
|
|
145
191
|
draggable={true}
|
|
146
192
|
showOverlay={false}
|
|
147
193
|
modalTrigger={
|
|
@@ -184,12 +230,10 @@ export const ControlledState: Story = {
|
|
|
184
230
|
|
|
185
231
|
return (
|
|
186
232
|
<div>
|
|
187
|
-
<
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
</Text>
|
|
192
|
-
</Box>
|
|
233
|
+
<Text mb={400}>
|
|
234
|
+
Use controlled state when you need to manage modal state externally.
|
|
235
|
+
Modal opened count: {count}
|
|
236
|
+
</Text>
|
|
193
237
|
|
|
194
238
|
<Modal
|
|
195
239
|
open={isOpen}
|
|
@@ -198,6 +242,7 @@ export const ControlledState: Story = {
|
|
|
198
242
|
if (open) setCount((c) => c + 1);
|
|
199
243
|
}}
|
|
200
244
|
aria-label="Controlled Modal"
|
|
245
|
+
closeButtonAriaLabel="Close Modal"
|
|
201
246
|
modalTrigger={
|
|
202
247
|
<Button appearance="primary">Open Controlled Modal</Button>
|
|
203
248
|
}
|
|
@@ -274,16 +319,14 @@ export const DraggableWithFormInteraction: Story = {
|
|
|
274
319
|
|
|
275
320
|
return (
|
|
276
321
|
<Box p={500}>
|
|
277
|
-
<
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
</Text>
|
|
286
|
-
</Box>
|
|
322
|
+
<Text fontSize={500} fontWeight="bold" mb={400}>
|
|
323
|
+
Test Draggable Modal with Form Interaction
|
|
324
|
+
</Text>
|
|
325
|
+
<Text fontSize={200} color="text.subtle" mb={400}>
|
|
326
|
+
Open the draggable modal, move it to the side, then try interacting
|
|
327
|
+
with the form below. You should be able to fill out the form while the
|
|
328
|
+
modal is open and moved aside.
|
|
329
|
+
</Text>
|
|
287
330
|
|
|
288
331
|
<Box
|
|
289
332
|
p={500}
|
|
@@ -410,6 +453,7 @@ export const DraggableWithFormInteraction: Story = {
|
|
|
410
453
|
|
|
411
454
|
<Modal
|
|
412
455
|
aria-label="Reference Modal"
|
|
456
|
+
closeButtonAriaLabel="Close Reference Modal"
|
|
413
457
|
draggable={true}
|
|
414
458
|
showOverlay={false}
|
|
415
459
|
modalTrigger={
|
|
@@ -420,57 +464,49 @@ export const DraggableWithFormInteraction: Story = {
|
|
|
420
464
|
title="Reference Information"
|
|
421
465
|
subtitle="Drag this modal by the header to move it around"
|
|
422
466
|
/>
|
|
423
|
-
<ModalBody>
|
|
424
|
-
<
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
<
|
|
429
|
-
<
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
<Text mb={100}>
|
|
441
|
-
Email: Use your work email address
|
|
442
|
-
</Text>
|
|
443
|
-
</li>
|
|
444
|
-
<li>
|
|
445
|
-
<Text mb={100}>
|
|
446
|
-
Company: Enter your organization name
|
|
447
|
-
</Text>
|
|
448
|
-
</li>
|
|
449
|
-
</Box>
|
|
450
|
-
|
|
451
|
-
<Box
|
|
452
|
-
mt={400}
|
|
453
|
-
p={300}
|
|
454
|
-
bg="container.background.info"
|
|
455
|
-
borderRadius="6px"
|
|
456
|
-
border="1px solid"
|
|
457
|
-
borderColor="container.border.info"
|
|
458
|
-
>
|
|
459
|
-
<Text fontWeight="bold" mb={100}>
|
|
460
|
-
Try this:
|
|
467
|
+
<ModalBody p={300}>
|
|
468
|
+
<Text fontSize={400} fontWeight="bold" mb={200}>
|
|
469
|
+
Instructions for filling out the form:
|
|
470
|
+
</Text>
|
|
471
|
+
<Box as="ul" pl={400}>
|
|
472
|
+
<li>
|
|
473
|
+
<Text mb={100}>First Name: Enter your given name</Text>
|
|
474
|
+
</li>
|
|
475
|
+
<li>
|
|
476
|
+
<Text mb={100}>Last Name: Enter your family name</Text>
|
|
477
|
+
</li>
|
|
478
|
+
<li>
|
|
479
|
+
<Text mb={100}>Email: Use your work email address</Text>
|
|
480
|
+
</li>
|
|
481
|
+
<li>
|
|
482
|
+
<Text mb={100}>
|
|
483
|
+
Company: Enter your organization name
|
|
461
484
|
</Text>
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
485
|
+
</li>
|
|
486
|
+
</Box>
|
|
487
|
+
|
|
488
|
+
<Box
|
|
489
|
+
mt={400}
|
|
490
|
+
p={300}
|
|
491
|
+
bg="container.background.info"
|
|
492
|
+
borderRadius="6px"
|
|
493
|
+
border="1px solid"
|
|
494
|
+
borderColor="container.border.info"
|
|
495
|
+
>
|
|
496
|
+
<Text fontWeight="bold" mb={100}>
|
|
497
|
+
Try this:
|
|
498
|
+
</Text>
|
|
499
|
+
<Text fontSize={100}>
|
|
500
|
+
1. Grab this modal by the header (you'll see a grab
|
|
501
|
+
cursor)
|
|
502
|
+
<br />
|
|
503
|
+
2. Drag it to the side of the screen
|
|
504
|
+
<br />
|
|
505
|
+
3. Click on the form inputs in the background
|
|
506
|
+
<br />
|
|
507
|
+
4. Notice you can interact with the form while the modal
|
|
508
|
+
is open!
|
|
509
|
+
</Text>
|
|
474
510
|
</Box>
|
|
475
511
|
</ModalBody>
|
|
476
512
|
<ModalFooter
|
|
@@ -523,6 +559,7 @@ export const TallContentMobile: Story = {
|
|
|
523
559
|
return (
|
|
524
560
|
<Modal
|
|
525
561
|
aria-label="Tall Content Modal"
|
|
562
|
+
closeButtonAriaLabel="Close Modal"
|
|
526
563
|
modalTrigger={
|
|
527
564
|
<Button appearance="primary">Open Tall Modal (Test Mobile)</Button>
|
|
528
565
|
}
|
|
@@ -697,6 +734,7 @@ export const SimplifiedFooterAPI: Story = {
|
|
|
697
734
|
return (
|
|
698
735
|
<Modal
|
|
699
736
|
aria-label="Simplified Footer API Example"
|
|
737
|
+
closeButtonAriaLabel="Close Modal"
|
|
700
738
|
modalTrigger={
|
|
701
739
|
<Button appearance="primary">Open Modal with Simple Footer</Button>
|
|
702
740
|
}
|
|
@@ -745,6 +783,7 @@ export const FooterWithLeftAction: Story = {
|
|
|
745
783
|
open={isOpen}
|
|
746
784
|
onOpenChange={setIsOpen}
|
|
747
785
|
aria-label="Footer with Left Action"
|
|
786
|
+
closeButtonAriaLabel="Close Modal"
|
|
748
787
|
modalTrigger={
|
|
749
788
|
<Button appearance="primary">Open Modal with Delete Action</Button>
|
|
750
789
|
}
|
|
@@ -802,6 +841,7 @@ export const CustomFooterOverride: Story = {
|
|
|
802
841
|
return (
|
|
803
842
|
<Modal
|
|
804
843
|
aria-label="Custom Footer Override"
|
|
844
|
+
closeButtonAriaLabel="Close Modal"
|
|
805
845
|
modalTrigger={<Button appearance="primary">Open Custom Footer</Button>}
|
|
806
846
|
>
|
|
807
847
|
<ModalHeader
|
|
@@ -30,6 +30,7 @@ import { RAIL_BUTTON_SIZE } from "../../shared/constants";
|
|
|
30
30
|
* onClick={() => console.log('expand')}
|
|
31
31
|
* />
|
|
32
32
|
*/
|
|
33
|
+
|
|
33
34
|
const RailButton = styled.button`
|
|
34
35
|
width: ${RAIL_BUTTON_SIZE}px;
|
|
35
36
|
height: ${(props) => props.theme.space[500]};
|
|
@@ -74,6 +75,9 @@ export const ModalAction: React.FC<TypeModalActionProps> = ({
|
|
|
74
75
|
}) => {
|
|
75
76
|
const button = (
|
|
76
77
|
<RailButton
|
|
78
|
+
data-slot="modal-action"
|
|
79
|
+
data-qa-modal-action
|
|
80
|
+
data-qa-modal-action-type={actionType || "button"}
|
|
77
81
|
aria-label={ariaLabel}
|
|
78
82
|
title={ariaLabel}
|
|
79
83
|
disabled={disabled}
|
|
@@ -44,7 +44,12 @@ StyledModalBody.displayName = "ModalBody";
|
|
|
44
44
|
export const ModalBody = React.forwardRef<HTMLDivElement, TypeModalBodyProps>(
|
|
45
45
|
({ children, ...rest }, ref) => {
|
|
46
46
|
return (
|
|
47
|
-
<StyledModalBody
|
|
47
|
+
<StyledModalBody
|
|
48
|
+
data-slot="modal-body"
|
|
49
|
+
data-qa-modal-body
|
|
50
|
+
ref={ref}
|
|
51
|
+
{...rest}
|
|
52
|
+
>
|
|
48
53
|
{children}
|
|
49
54
|
</StyledModalBody>
|
|
50
55
|
);
|
|
@@ -27,7 +27,13 @@ export const ModalCloseWrapper = (props: ModalCloseWrapperProps) => {
|
|
|
27
27
|
|
|
28
28
|
return (
|
|
29
29
|
<Dialog.Close asChild={asChild} onClick={handleClick} {...rest}>
|
|
30
|
-
{children
|
|
30
|
+
{React.isValidElement(children)
|
|
31
|
+
? React.cloneElement(children as React.ReactElement<any>, {
|
|
32
|
+
"data-slot": "modal-close-wrapper",
|
|
33
|
+
"data-qa-modal-close-wrapper": "",
|
|
34
|
+
...((children as React.ReactElement<any>).props || {}),
|
|
35
|
+
})
|
|
36
|
+
: children}
|
|
31
37
|
</Dialog.Close>
|
|
32
38
|
);
|
|
33
39
|
};
|
|
@@ -32,11 +32,15 @@ interface StyledContentProps
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
// Styled motion.div wrapper that handles positioning
|
|
35
|
-
const StyledMotionWrapper = styled(motion.div)
|
|
35
|
+
const StyledMotionWrapper = styled(motion.div)<{
|
|
36
|
+
$isMobile: boolean;
|
|
37
|
+
$zIndex?: number;
|
|
38
|
+
}>`
|
|
36
39
|
position: fixed;
|
|
37
|
-
top: ${(props
|
|
40
|
+
top: ${(props) => (props.$isMobile ? "auto" : "50%")};
|
|
38
41
|
left: 50%;
|
|
39
|
-
bottom: ${(props
|
|
42
|
+
bottom: ${(props) => (props.$isMobile ? 0 : "auto")};
|
|
43
|
+
z-index: ${(props) => (props.$zIndex ? props.$zIndex + 1 : 7)};
|
|
40
44
|
`;
|
|
41
45
|
|
|
42
46
|
export const StyledContent = styled.div.withConfig({
|
|
@@ -113,6 +117,7 @@ interface ModalContentProps {
|
|
|
113
117
|
label?: string;
|
|
114
118
|
dataAttributes: Record<string, string>;
|
|
115
119
|
draggable?: boolean;
|
|
120
|
+
zIndex?: number;
|
|
116
121
|
rest: any;
|
|
117
122
|
}
|
|
118
123
|
|
|
@@ -124,6 +129,7 @@ export const StaticModalContent: React.FC<ModalContentProps> = ({
|
|
|
124
129
|
children,
|
|
125
130
|
label,
|
|
126
131
|
dataAttributes,
|
|
132
|
+
zIndex,
|
|
127
133
|
rest,
|
|
128
134
|
}) => {
|
|
129
135
|
const isMobile = useIsMobile();
|
|
@@ -134,12 +140,18 @@ export const StaticModalContent: React.FC<ModalContentProps> = ({
|
|
|
134
140
|
<Dialog.Content asChild aria-label={label}>
|
|
135
141
|
<StyledMotionWrapper
|
|
136
142
|
$isMobile={isMobile}
|
|
143
|
+
$zIndex={zIndex}
|
|
137
144
|
variants={contentVariants}
|
|
138
145
|
initial="initial"
|
|
139
146
|
animate="animate"
|
|
140
147
|
exit="exit"
|
|
141
148
|
>
|
|
142
|
-
<StyledContent
|
|
149
|
+
<StyledContent
|
|
150
|
+
data-slot="modal-content"
|
|
151
|
+
draggable={false}
|
|
152
|
+
{...dataAttributes}
|
|
153
|
+
{...rest}
|
|
154
|
+
>
|
|
143
155
|
{children}
|
|
144
156
|
</StyledContent>
|
|
145
157
|
</StyledMotionWrapper>
|
|
@@ -156,6 +168,7 @@ export const DraggableModalContent: React.FC<ModalContentProps> = ({
|
|
|
156
168
|
children,
|
|
157
169
|
label,
|
|
158
170
|
dataAttributes,
|
|
171
|
+
zIndex,
|
|
159
172
|
rest,
|
|
160
173
|
}) => {
|
|
161
174
|
const [position, setPosition] = React.useState({ x: 0, y: 0 });
|
|
@@ -265,6 +278,7 @@ export const DraggableModalContent: React.FC<ModalContentProps> = ({
|
|
|
265
278
|
>
|
|
266
279
|
<StyledMotionWrapper
|
|
267
280
|
$isMobile={isMobile}
|
|
281
|
+
$zIndex={zIndex}
|
|
268
282
|
variants={contentVariants}
|
|
269
283
|
initial="initial"
|
|
270
284
|
animate="animate"
|
|
@@ -278,6 +292,7 @@ export const DraggableModalContent: React.FC<ModalContentProps> = ({
|
|
|
278
292
|
}}
|
|
279
293
|
>
|
|
280
294
|
<StyledContent
|
|
295
|
+
data-slot="modal-content"
|
|
281
296
|
ref={contentRef}
|
|
282
297
|
draggable={true}
|
|
283
298
|
isDragging={isDragging}
|
|
@@ -21,7 +21,12 @@ export const ModalDescription = React.forwardRef<
|
|
|
21
21
|
>(({ children, descriptionProps = {}, ...rest }, ref) => {
|
|
22
22
|
return (
|
|
23
23
|
<Dialog.Description asChild {...descriptionProps}>
|
|
24
|
-
<Box
|
|
24
|
+
<Box
|
|
25
|
+
data-slot="modal-description"
|
|
26
|
+
data-qa-modal-description
|
|
27
|
+
ref={ref}
|
|
28
|
+
{...rest}
|
|
29
|
+
>
|
|
25
30
|
{children}
|
|
26
31
|
</Box>
|
|
27
32
|
</Dialog.Description>
|
|
@@ -83,7 +83,7 @@ export const ModalFooter = (props: TypeModalFooterProps) => {
|
|
|
83
83
|
|
|
84
84
|
// Build simplified API layout
|
|
85
85
|
return (
|
|
86
|
-
<ModalCustomFooter {...rest}>
|
|
86
|
+
<ModalCustomFooter data-slot="modal-footer" data-qa-modal-footer {...rest}>
|
|
87
87
|
{/* Left action (e.g., Delete button) */}
|
|
88
88
|
{leftAction ? leftAction : null}
|
|
89
89
|
|
|
@@ -77,6 +77,8 @@ export const ModalHeader = (props: TypeModalHeaderProps) => {
|
|
|
77
77
|
|
|
78
78
|
return (
|
|
79
79
|
<ModalCustomHeader
|
|
80
|
+
data-slot="modal-header"
|
|
81
|
+
data-qa-modal-header
|
|
80
82
|
{...rest}
|
|
81
83
|
onMouseDown={isDraggable ? dragContext?.onHeaderMouseDown : undefined}
|
|
82
84
|
draggable={isDraggable}
|
|
@@ -21,12 +21,13 @@ interface StyledOverlayProps
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
// Styled motion.div for the overlay wrapper
|
|
24
|
-
export const StyledMotionOverlay = styled(motion.div)
|
|
24
|
+
export const StyledMotionOverlay = styled(motion.div)<{ $zIndex?: number }>`
|
|
25
25
|
position: fixed;
|
|
26
26
|
top: 0px;
|
|
27
27
|
left: 0px;
|
|
28
28
|
right: 0px;
|
|
29
29
|
bottom: 0px;
|
|
30
|
+
z-index: ${(props) => props.$zIndex ?? 6};
|
|
30
31
|
`;
|
|
31
32
|
|
|
32
33
|
export const StyledOverlay = styled.div.withConfig({
|