@sproutsocial/seeds-react-modal 2.1.2 → 2.2.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.
- package/.turbo/turbo-build.log +14 -14
- package/CHANGELOG.md +29 -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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sproutsocial/seeds-react-modal",
|
|
3
|
-
"version": "2.1
|
|
3
|
+
"version": "2.2.1",
|
|
4
4
|
"description": "Seeds React Modal",
|
|
5
5
|
"author": "Sprout Social, Inc.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -36,12 +36,12 @@
|
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@radix-ui/react-dialog": "^1.1.14",
|
|
39
|
-
"@sproutsocial/seeds-react-box": "^1.1.
|
|
40
|
-
"@sproutsocial/seeds-react-button": "^1.3.
|
|
41
|
-
"@sproutsocial/seeds-react-icon": "^2.0.
|
|
39
|
+
"@sproutsocial/seeds-react-box": "^1.1.7",
|
|
40
|
+
"@sproutsocial/seeds-react-button": "^1.3.7",
|
|
41
|
+
"@sproutsocial/seeds-react-icon": "^2.0.2",
|
|
42
42
|
"@sproutsocial/seeds-react-system-props": "^3.0.1",
|
|
43
43
|
"@sproutsocial/seeds-react-text": "^1.3.2",
|
|
44
|
-
"@sproutsocial/seeds-react-theme": "^3.2.
|
|
44
|
+
"@sproutsocial/seeds-react-theme": "^3.2.1",
|
|
45
45
|
"motion": "^12.6.3",
|
|
46
46
|
"react-dnd": "^16.0.1",
|
|
47
47
|
"react-dnd-html5-backend": "^16.0.1",
|
|
@@ -50,8 +50,8 @@
|
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@sproutsocial/eslint-config-seeds": "*",
|
|
53
|
-
"@sproutsocial/seeds-react-form-field": "^1.0.
|
|
54
|
-
"@sproutsocial/seeds-react-input": "^1.4.
|
|
53
|
+
"@sproutsocial/seeds-react-form-field": "^1.0.6",
|
|
54
|
+
"@sproutsocial/seeds-react-input": "^1.4.13",
|
|
55
55
|
"@sproutsocial/seeds-react-testing-library": "*",
|
|
56
56
|
"@sproutsocial/seeds-testing": "*",
|
|
57
57
|
"@sproutsocial/seeds-tsconfig": "*",
|
package/src/v2/Modal.tsx
CHANGED
|
@@ -59,6 +59,8 @@ const Modal = (props: TypeModalProps) => {
|
|
|
59
59
|
showOverlay = true,
|
|
60
60
|
actions,
|
|
61
61
|
closeButtonAriaLabel = "Close",
|
|
62
|
+
closeButtonProps,
|
|
63
|
+
zIndex = 6,
|
|
62
64
|
...rest
|
|
63
65
|
} = props;
|
|
64
66
|
|
|
@@ -119,12 +121,17 @@ const Modal = (props: TypeModalProps) => {
|
|
|
119
121
|
{showOverlay && (
|
|
120
122
|
<Dialog.Overlay asChild>
|
|
121
123
|
<StyledMotionOverlay
|
|
124
|
+
$zIndex={zIndex}
|
|
122
125
|
variants={overlayVariants}
|
|
123
126
|
initial="initial"
|
|
124
127
|
animate="animate"
|
|
125
128
|
exit="exit"
|
|
126
129
|
>
|
|
127
|
-
<StyledOverlay
|
|
130
|
+
<StyledOverlay
|
|
131
|
+
data-slot="modal-overlay"
|
|
132
|
+
data-qa-modal-overlay
|
|
133
|
+
allowInteraction={draggable}
|
|
134
|
+
/>
|
|
128
135
|
</StyledMotionOverlay>
|
|
129
136
|
</Dialog.Overlay>
|
|
130
137
|
)}
|
|
@@ -132,14 +139,19 @@ const Modal = (props: TypeModalProps) => {
|
|
|
132
139
|
label={label}
|
|
133
140
|
dataAttributes={dataAttributes}
|
|
134
141
|
draggable={draggable}
|
|
142
|
+
zIndex={zIndex}
|
|
135
143
|
rest={rest}
|
|
136
144
|
>
|
|
137
145
|
{/* Floating actions rail - always show a close by default */}
|
|
138
146
|
<ModalRail>
|
|
139
147
|
<ModalAction
|
|
140
148
|
actionType="close"
|
|
141
|
-
aria-label={closeButtonAriaLabel}
|
|
142
149
|
iconName="x-outline"
|
|
150
|
+
{...closeButtonProps}
|
|
151
|
+
aria-label={
|
|
152
|
+
(closeButtonProps as any)?.["aria-label"] ??
|
|
153
|
+
closeButtonAriaLabel
|
|
154
|
+
}
|
|
143
155
|
/>
|
|
144
156
|
{actions?.map((action, idx) => (
|
|
145
157
|
<ModalAction key={idx} {...action} />
|
package/src/v2/ModalTypes.ts
CHANGED
|
@@ -6,43 +6,36 @@ import type {
|
|
|
6
6
|
} from "@sproutsocial/seeds-react-box";
|
|
7
7
|
import type { TypeIconName } from "@sproutsocial/seeds-react-icon";
|
|
8
8
|
|
|
9
|
+
// =============================================================================
|
|
10
|
+
// COMPONENT PROPS - Individual modal component types
|
|
11
|
+
// =============================================================================
|
|
12
|
+
|
|
9
13
|
/**
|
|
10
14
|
* Props for ModalHeader component.
|
|
11
15
|
*
|
|
12
|
-
* Renders the modal's header with title and optional subtitle.
|
|
13
|
-
* when the modal has draggable enabled.
|
|
16
|
+
* Renders the modal's header with title and optional subtitle.
|
|
17
|
+
* Supports draggable functionality when the modal has draggable enabled.
|
|
14
18
|
*
|
|
15
|
-
* Note: This component only supports slots (title/subtitle props).
|
|
16
|
-
* use ModalCustomHeader instead.
|
|
19
|
+
* Note: This component only supports slots (title/subtitle props).
|
|
20
|
+
* For custom headers, use ModalCustomHeader instead.
|
|
17
21
|
*/
|
|
18
22
|
export interface TypeModalHeaderProps extends TypeBoxProps {
|
|
19
23
|
/** Modal title text displayed as a headline */
|
|
20
24
|
title?: string;
|
|
25
|
+
|
|
21
26
|
/**
|
|
22
27
|
* Modal subtitle text.
|
|
23
|
-
*
|
|
24
|
-
* This is automatically wrapped in `Dialog.Description` from Radix UI,
|
|
25
|
-
* which provides accessible description text for screen readers. The subtitle
|
|
26
|
-
* provides additional context about the modal and is announced alongside
|
|
27
|
-
* the title when the dialog opens. Radix UI automatically connects this to
|
|
28
|
-
* the dialog via ARIA attributes (typically `aria-describedby` on the dialog root).
|
|
28
|
+
* Automatically wrapped in Dialog.Description for accessibility.
|
|
29
29
|
*/
|
|
30
30
|
subtitle?: string;
|
|
31
31
|
|
|
32
|
-
/** Additional props for
|
|
32
|
+
/** Additional props for Dialog.Title when title is provided */
|
|
33
33
|
titleProps?: Omit<
|
|
34
34
|
React.ComponentPropsWithoutRef<typeof Dialog.Title>,
|
|
35
35
|
"asChild" | "children"
|
|
36
36
|
>;
|
|
37
37
|
|
|
38
|
-
/**
|
|
39
|
-
* Additional props for the Dialog.Description when subtitle is provided.
|
|
40
|
-
*
|
|
41
|
-
* Dialog.Description provides accessible description text for the modal that
|
|
42
|
-
* screen readers announce when the dialog opens. It complements Dialog.Title
|
|
43
|
-
* by providing additional context about the modal. Radix UI automatically
|
|
44
|
-
* connects this to the dialog via ARIA attributes.
|
|
45
|
-
*/
|
|
38
|
+
/** Additional props for Dialog.Description when subtitle is provided */
|
|
46
39
|
subtitleProps?: Omit<
|
|
47
40
|
React.ComponentPropsWithoutRef<typeof Dialog.Description>,
|
|
48
41
|
"asChild" | "children"
|
|
@@ -50,294 +43,291 @@ export interface TypeModalHeaderProps extends TypeBoxProps {
|
|
|
50
43
|
}
|
|
51
44
|
|
|
52
45
|
/**
|
|
53
|
-
* Props for
|
|
54
|
-
*
|
|
55
|
-
* Provides automatic button wrapping and layout management. At least one action
|
|
56
|
-
* (cancelButton, primaryButton, or leftAction) must be provided.
|
|
57
|
-
*
|
|
58
|
-
* Note: This component only supports slots (button props). For custom footers,
|
|
59
|
-
* use ModalCustomFooter instead.
|
|
46
|
+
* Props for ModalBody component.
|
|
60
47
|
*
|
|
61
|
-
*
|
|
62
|
-
|
|
63
|
-
|
|
48
|
+
* Renders the scrollable main content area of the modal.
|
|
49
|
+
*/
|
|
50
|
+
export interface TypeModalBodyProps extends TypeBoxProps {
|
|
51
|
+
/** The main content of the modal body */
|
|
52
|
+
children?: React.ReactNode;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Props for ModalDescription component.
|
|
64
57
|
*
|
|
65
|
-
*
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
58
|
+
* Wraps content with Dialog.Description for accessible descriptions.
|
|
59
|
+
*/
|
|
60
|
+
export interface TypeModalDescriptionProps extends TypeBoxProps {
|
|
61
|
+
/** The description text content */
|
|
62
|
+
children: React.ReactNode;
|
|
63
|
+
|
|
64
|
+
/** Additional props for Dialog.Description */
|
|
65
|
+
descriptionProps?: Omit<
|
|
66
|
+
React.ComponentPropsWithoutRef<typeof Dialog.Description>,
|
|
67
|
+
"asChild" | "children"
|
|
68
|
+
>;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Props for ModalFooter component.
|
|
71
73
|
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* <ModalFooter leftAction={<Button>Delete</Button>} />
|
|
74
|
+
* Provides automatic button wrapping and layout management.
|
|
75
|
+
* At least one action (cancelButton, primaryButton, or leftAction) must be provided.
|
|
75
76
|
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* <ModalFooter />
|
|
77
|
+
* Note: This component only supports slots (button props).
|
|
78
|
+
* For custom footers, use ModalCustomFooter instead.
|
|
79
79
|
*/
|
|
80
80
|
export type TypeModalFooterProps = TypeBoxProps &
|
|
81
81
|
(
|
|
82
82
|
| {
|
|
83
|
-
/** Primary action button - automatically wrapped in ModalCloseWrapper
|
|
83
|
+
/** Primary action button - automatically wrapped in ModalCloseWrapper */
|
|
84
84
|
primaryButton: React.ReactNode;
|
|
85
|
-
/** Cancel/secondary button - automatically wrapped in ModalCloseWrapper
|
|
85
|
+
/** Cancel/secondary button - automatically wrapped in ModalCloseWrapper */
|
|
86
86
|
cancelButton?: React.ReactNode;
|
|
87
|
-
/** Optional action on the far left (e.g., Delete
|
|
87
|
+
/** Optional action on the far left (e.g., Delete) - NOT automatically wrapped */
|
|
88
88
|
leftAction?: React.ReactNode;
|
|
89
89
|
}
|
|
90
90
|
| {
|
|
91
|
-
/** Primary action button - automatically wrapped in ModalCloseWrapper to close the modal */
|
|
92
91
|
primaryButton?: React.ReactNode;
|
|
93
|
-
/** Cancel/secondary button - automatically wrapped in ModalCloseWrapper to close the modal */
|
|
94
92
|
cancelButton: React.ReactNode;
|
|
95
|
-
/** Optional action on the far left (e.g., Delete button) - NOT automatically wrapped */
|
|
96
93
|
leftAction?: React.ReactNode;
|
|
97
94
|
}
|
|
98
95
|
| {
|
|
99
|
-
/** Primary action button - automatically wrapped in ModalCloseWrapper to close the modal */
|
|
100
96
|
primaryButton?: React.ReactNode;
|
|
101
|
-
/** Cancel/secondary button - automatically wrapped in ModalCloseWrapper to close the modal */
|
|
102
97
|
cancelButton?: React.ReactNode;
|
|
103
|
-
/** Optional action on the far left (e.g., Delete button) - NOT automatically wrapped */
|
|
104
98
|
leftAction: React.ReactNode;
|
|
105
99
|
}
|
|
106
100
|
);
|
|
107
101
|
|
|
108
102
|
/**
|
|
109
|
-
* Props for
|
|
103
|
+
* Props for ModalRail component.
|
|
110
104
|
*
|
|
111
|
-
*
|
|
105
|
+
* Container for floating action buttons displayed alongside the modal.
|
|
112
106
|
*/
|
|
113
|
-
export
|
|
114
|
-
/**
|
|
107
|
+
export type TypeModalRailProps = {
|
|
108
|
+
/** ModalAction components to display in the rail */
|
|
115
109
|
children?: React.ReactNode;
|
|
116
|
-
}
|
|
110
|
+
};
|
|
117
111
|
|
|
118
112
|
/**
|
|
119
|
-
* Props for
|
|
113
|
+
* Props for ModalAction component.
|
|
120
114
|
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
115
|
+
* Action buttons that appear in the floating rail.
|
|
116
|
+
* Supports two action types:
|
|
117
|
+
* - "close": Automatically closes the modal (uses Dialog.Close)
|
|
118
|
+
* - "button": Custom action with user-defined onClick handler
|
|
119
|
+
*
|
|
120
|
+
* **IMPORTANT**: aria-label is required for accessibility.
|
|
123
121
|
*/
|
|
124
|
-
export
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
122
|
+
export type TypeModalActionProps =
|
|
123
|
+
| ({
|
|
124
|
+
/** Action type - close triggers modal close */
|
|
125
|
+
actionType: "close";
|
|
126
|
+
/** REQUIRED: Accessible label for the button */
|
|
127
|
+
"aria-label": string;
|
|
128
|
+
/** Icon name from the Seeds icon set */
|
|
129
|
+
iconName?: TypeIconName;
|
|
130
|
+
/** Optional click handler called before closing */
|
|
131
|
+
onClick?: () => void;
|
|
132
|
+
} & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "onClick">)
|
|
133
|
+
| ({
|
|
134
|
+
/** Action type - button for custom actions (default) */
|
|
135
|
+
actionType?: "button";
|
|
136
|
+
/** REQUIRED: Accessible label for the button */
|
|
137
|
+
"aria-label": string;
|
|
138
|
+
/** Icon name from the Seeds icon set */
|
|
139
|
+
iconName?: TypeIconName;
|
|
140
|
+
/** Click handler for the button */
|
|
141
|
+
onClick?: () => void;
|
|
142
|
+
} & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "onClick">);
|
|
143
|
+
|
|
144
|
+
// =============================================================================
|
|
145
|
+
// MODAL PROPS - Main Modal component types
|
|
146
|
+
// =============================================================================
|
|
133
147
|
|
|
134
|
-
/**
|
|
135
|
-
|
|
148
|
+
/**
|
|
149
|
+
* Base common props shared by all modal variants (without close button props).
|
|
150
|
+
*/
|
|
151
|
+
type TypeModalCommonPropsBase = TypeContainerProps &
|
|
136
152
|
Omit<React.ComponentPropsWithoutRef<"div">, keyof TypeContainerProps> & {
|
|
153
|
+
/** Modal content */
|
|
154
|
+
children: React.ReactNode;
|
|
155
|
+
|
|
137
156
|
/** Controls whether the modal is open (controlled mode) */
|
|
138
157
|
open?: boolean;
|
|
139
158
|
|
|
140
159
|
/** Default open state for uncontrolled mode */
|
|
141
160
|
defaultOpen?: boolean;
|
|
142
161
|
|
|
143
|
-
/** body content of the modal */
|
|
144
|
-
children: React.ReactNode;
|
|
145
|
-
|
|
146
162
|
/** Callback when open state changes */
|
|
147
163
|
onOpenChange?: (open: boolean) => void;
|
|
148
164
|
|
|
149
|
-
/**
|
|
150
|
-
*
|
|
165
|
+
/**
|
|
166
|
+
* Element that triggers the modal when clicked.
|
|
167
|
+
* Can be any React element like a button, link, or custom component.
|
|
168
|
+
*/
|
|
151
169
|
modalTrigger?: React.ReactElement<any>;
|
|
152
170
|
|
|
153
|
-
/**
|
|
171
|
+
/** Modal description (automatically wrapped in Dialog.Description) */
|
|
154
172
|
description?: string;
|
|
155
173
|
|
|
156
174
|
/**
|
|
157
|
-
* Custom attributes
|
|
158
|
-
* Each key will be prepended with "data-" when rendered
|
|
175
|
+
* Custom data attributes added to the modal container.
|
|
176
|
+
* Each key will be prepended with "data-" when rendered.
|
|
159
177
|
*/
|
|
160
178
|
data?: Record<string, string | boolean | number>;
|
|
161
179
|
|
|
162
|
-
/**
|
|
180
|
+
/** Quick actions to render on the modal side rail */
|
|
163
181
|
actions?: TypeModalActionProps[];
|
|
164
182
|
|
|
165
|
-
/**
|
|
166
|
-
|
|
183
|
+
/** Controls the z-index CSS property (defaults to 6 to match Modal v1) */
|
|
184
|
+
zIndex?: number;
|
|
167
185
|
};
|
|
168
186
|
|
|
187
|
+
/**
|
|
188
|
+
* Common props with close button accessibility enforcement.
|
|
189
|
+
*
|
|
190
|
+
* Ensures close button always has an accessible label through either:
|
|
191
|
+
* - closeButtonAriaLabel prop (required unless closeButtonProps has aria-label)
|
|
192
|
+
* - closeButtonProps with aria-label
|
|
193
|
+
*
|
|
194
|
+
* @example
|
|
195
|
+
* // ✅ Valid - has closeButtonAriaLabel
|
|
196
|
+
* <Modal closeButtonAriaLabel="Close dialog" />
|
|
197
|
+
*
|
|
198
|
+
* @example
|
|
199
|
+
* // ✅ Valid - has aria-label in closeButtonProps
|
|
200
|
+
* <Modal closeButtonProps={{ "aria-label": "Close dialog", onClick: handler }} />
|
|
201
|
+
*
|
|
202
|
+
* @example
|
|
203
|
+
* // ✅ Valid - has both (closeButtonProps aria-label takes precedence)
|
|
204
|
+
* <Modal
|
|
205
|
+
* closeButtonAriaLabel="Close"
|
|
206
|
+
* closeButtonProps={{ onClick: handler }}
|
|
207
|
+
* />
|
|
208
|
+
*
|
|
209
|
+
* @example
|
|
210
|
+
* // ❌ TypeScript Error - missing aria-label in both places
|
|
211
|
+
* <Modal closeButtonProps={{ onClick: handler }} />
|
|
212
|
+
*/
|
|
213
|
+
type TypeModalCommonProps =
|
|
214
|
+
| (TypeModalCommonPropsBase & {
|
|
215
|
+
/**
|
|
216
|
+
* Accessible label for the close button.
|
|
217
|
+
* Required unless closeButtonProps includes aria-label.
|
|
218
|
+
*/
|
|
219
|
+
closeButtonAriaLabel: string;
|
|
220
|
+
/**
|
|
221
|
+
* Props to pass to the default floating close button.
|
|
222
|
+
* Use this for advanced customization; for simple aria-label changes, use closeButtonAriaLabel instead.
|
|
223
|
+
*/
|
|
224
|
+
closeButtonProps?: Omit<
|
|
225
|
+
TypeModalActionProps,
|
|
226
|
+
"actionType" | "aria-label"
|
|
227
|
+
>;
|
|
228
|
+
})
|
|
229
|
+
| (TypeModalCommonPropsBase & {
|
|
230
|
+
/**
|
|
231
|
+
* Accessible label for the close button.
|
|
232
|
+
* Optional when closeButtonProps includes aria-label.
|
|
233
|
+
*/
|
|
234
|
+
closeButtonAriaLabel?: string;
|
|
235
|
+
/**
|
|
236
|
+
* Props to pass to the default floating close button.
|
|
237
|
+
* Must include aria-label when closeButtonAriaLabel is not provided.
|
|
238
|
+
*/
|
|
239
|
+
closeButtonProps: Omit<TypeModalActionProps, "actionType"> & {
|
|
240
|
+
"aria-label": string;
|
|
241
|
+
};
|
|
242
|
+
});
|
|
243
|
+
|
|
169
244
|
/**
|
|
170
245
|
* Base props with draggable and showOverlay relationship enforced.
|
|
171
246
|
*
|
|
172
|
-
* When draggable is true, showOverlay must be false
|
|
173
|
-
*
|
|
174
|
-
* defeating the purpose of being able to drag the modal aside.
|
|
247
|
+
* When draggable is true, showOverlay must be false because the overlay
|
|
248
|
+
* would block interaction with content behind the modal.
|
|
175
249
|
*/
|
|
176
250
|
type TypeModalBaseProps =
|
|
177
251
|
| (TypeModalCommonProps & {
|
|
178
|
-
/** Enable draggable functionality */
|
|
179
252
|
draggable: true;
|
|
180
|
-
/**
|
|
181
|
-
* Whether to show the background overlay.
|
|
182
|
-
* Must be false when draggable is true to allow interaction with background content.
|
|
183
|
-
*/
|
|
184
253
|
showOverlay?: false;
|
|
185
254
|
})
|
|
186
255
|
| (TypeModalCommonProps & {
|
|
187
|
-
/** Enable draggable functionality */
|
|
188
256
|
draggable?: false;
|
|
189
|
-
/** Whether to show the background overlay (defaults to true) */
|
|
190
257
|
showOverlay?: boolean;
|
|
191
258
|
});
|
|
192
259
|
|
|
193
260
|
/**
|
|
194
261
|
* Modal props with title provided.
|
|
195
|
-
* aria-label is optional since Dialog.Title can serve as the accessible name.
|
|
196
262
|
*/
|
|
197
|
-
|
|
198
|
-
/**
|
|
263
|
+
type TypeModalPropsWithTitle = TypeModalBaseProps & {
|
|
264
|
+
/** Modal title (creates ModalHeader automatically) */
|
|
199
265
|
title: string;
|
|
200
|
-
/**
|
|
201
|
-
* Simplified API: Modal subtitle (creates ModalHeader automatically).
|
|
202
|
-
*
|
|
203
|
-
* This is automatically wrapped in `Dialog.Description` from Radix UI,
|
|
204
|
-
* which provides accessible description text for screen readers. The subtitle
|
|
205
|
-
* provides additional context about the modal and is announced alongside
|
|
206
|
-
* the title when the dialog opens.
|
|
207
|
-
*/
|
|
266
|
+
/** Modal subtitle (creates ModalHeader automatically) */
|
|
208
267
|
subtitle?: string;
|
|
209
|
-
/** Accessible label for the modal dialog (optional when title
|
|
268
|
+
/** Accessible label for the modal dialog (optional when title is provided) */
|
|
210
269
|
"aria-label"?: string;
|
|
211
270
|
};
|
|
212
271
|
|
|
213
272
|
/**
|
|
214
|
-
* Modal props with subtitle
|
|
215
|
-
* aria-label is optional since Dialog.Description can help identify the modal.
|
|
273
|
+
* Modal props with subtitle only (no title).
|
|
216
274
|
*/
|
|
217
|
-
|
|
218
|
-
/** Simplified API: Modal title (creates ModalHeader automatically) */
|
|
275
|
+
type TypeModalPropsWithSubtitleOnly = TypeModalBaseProps & {
|
|
219
276
|
title?: never;
|
|
220
|
-
/**
|
|
221
|
-
* Simplified API: Modal subtitle (creates ModalHeader automatically).
|
|
222
|
-
*
|
|
223
|
-
* This is automatically wrapped in `Dialog.Description` from Radix UI,
|
|
224
|
-
* which provides accessible description text for screen readers. The subtitle
|
|
225
|
-
* provides additional context about the modal and is announced alongside
|
|
226
|
-
* the title when the dialog opens.
|
|
227
|
-
*/
|
|
277
|
+
/** Modal subtitle (creates ModalHeader automatically) */
|
|
228
278
|
subtitle: string;
|
|
229
|
-
/** Accessible label for the modal dialog (optional when
|
|
279
|
+
/** Accessible label for the modal dialog (optional when subtitle is provided) */
|
|
230
280
|
"aria-label"?: string;
|
|
231
281
|
};
|
|
232
282
|
|
|
233
283
|
/**
|
|
234
284
|
* Modal props without title or subtitle.
|
|
235
285
|
*
|
|
236
|
-
*
|
|
237
|
-
*
|
|
238
|
-
*
|
|
239
|
-
*
|
|
240
|
-
* what the modal dialog is about.
|
|
241
|
-
*
|
|
242
|
-
* @example
|
|
243
|
-
* // ✅ Valid - provides aria-label when no header
|
|
244
|
-
* <Modal aria-label="Delete confirmation dialog">
|
|
245
|
-
* <p>Are you sure you want to delete this item?</p>
|
|
246
|
-
* </Modal>
|
|
286
|
+
* aria-label is optional because users can provide accessible labeling through:
|
|
287
|
+
* - ModalHeader component as children
|
|
288
|
+
* - Dialog.Title or Dialog.Description components
|
|
289
|
+
* - Custom accessible content
|
|
247
290
|
*
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
*
|
|
252
|
-
* </Modal>
|
|
291
|
+
* However, for best accessibility, provide one of:
|
|
292
|
+
* - `aria-label` prop
|
|
293
|
+
* - ModalHeader component with title
|
|
294
|
+
* - Dialog.Title component in children
|
|
253
295
|
*/
|
|
254
|
-
|
|
255
|
-
/** Simplified API: Modal title (creates ModalHeader automatically) - not allowed when no header */
|
|
296
|
+
type TypeModalPropsWithoutHeader = TypeModalBaseProps & {
|
|
256
297
|
title?: never;
|
|
257
|
-
/** Simplified API: Modal subtitle (creates ModalHeader automatically) - not allowed when no header */
|
|
258
298
|
subtitle?: never;
|
|
259
299
|
/**
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
* Accessible label for the modal dialog. This is required for accessibility
|
|
263
|
-
* when the modal has no visible header. Screen readers use this to announce
|
|
264
|
-
* what the modal is about.
|
|
265
|
-
*
|
|
266
|
-
* **Error**: If you see a TypeScript error saying this property is missing,
|
|
267
|
-
* it means you need to either:
|
|
268
|
-
* 1. Provide `aria-label` (required when no header), OR
|
|
269
|
-
* 2. Provide `title` or `subtitle` (which makes aria-label optional)
|
|
270
|
-
*
|
|
271
|
-
* If you have a title or subtitle, this prop is optional.
|
|
300
|
+
* Accessible label for the modal dialog.
|
|
301
|
+
* Optional, but recommended when no title/subtitle props are provided.
|
|
272
302
|
*/
|
|
273
|
-
"aria-label"
|
|
303
|
+
"aria-label"?: string;
|
|
274
304
|
};
|
|
275
305
|
|
|
276
306
|
/**
|
|
277
|
-
* Modal component props
|
|
307
|
+
* Modal component props.
|
|
278
308
|
*
|
|
279
|
-
* **Accessibility
|
|
280
|
-
* -
|
|
281
|
-
* -
|
|
282
|
-
*
|
|
283
|
-
* TypeScript will error if you forget to provide `aria-label` when no header is present.
|
|
284
|
-
* This ensures accessibility compliance and better screen reader support.
|
|
309
|
+
* **Accessibility Recommendations:**
|
|
310
|
+
* - With title/subtitle props: aria-label is optional (automatically accessible)
|
|
311
|
+
* - With ModalHeader as children: aria-label is optional (header provides labeling)
|
|
312
|
+
* - Without header props or components: aria-label is optional but strongly recommended
|
|
285
313
|
*
|
|
286
314
|
* @example
|
|
287
|
-
* //
|
|
315
|
+
* // With title prop
|
|
288
316
|
* <Modal title="Delete Item" />
|
|
289
317
|
*
|
|
290
318
|
* @example
|
|
291
|
-
* //
|
|
292
|
-
* <Modal
|
|
293
|
-
*
|
|
294
|
-
*
|
|
295
|
-
* // ✅ Valid - no header, aria-label provided
|
|
296
|
-
* <Modal aria-label="Delete confirmation dialog" />
|
|
319
|
+
* // With ModalHeader as children
|
|
320
|
+
* <Modal>
|
|
321
|
+
* <ModalHeader title="Delete Item" />
|
|
322
|
+
* </Modal>
|
|
297
323
|
*
|
|
298
324
|
* @example
|
|
299
|
-
* //
|
|
300
|
-
* <Modal
|
|
325
|
+
* // With aria-label
|
|
326
|
+
* <Modal aria-label="Delete confirmation">
|
|
327
|
+
* <ModalBody>Are you sure?</ModalBody>
|
|
328
|
+
* </Modal>
|
|
301
329
|
*/
|
|
302
330
|
export type TypeModalProps =
|
|
303
331
|
| TypeModalPropsWithTitle
|
|
304
332
|
| TypeModalPropsWithSubtitleOnly
|
|
305
333
|
| TypeModalPropsWithoutHeader;
|
|
306
|
-
|
|
307
|
-
/**
|
|
308
|
-
* Props for ModalRail component.
|
|
309
|
-
*
|
|
310
|
-
* Container for floating action buttons displayed alongside the modal.
|
|
311
|
-
*/
|
|
312
|
-
export type TypeModalRailProps = {
|
|
313
|
-
/** ModalAction components to display in the rail */
|
|
314
|
-
children?: React.ReactNode;
|
|
315
|
-
};
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* Base props for modal action buttons.
|
|
319
|
-
*
|
|
320
|
-
* Extends standard button HTML attributes with modal-specific properties.
|
|
321
|
-
*/
|
|
322
|
-
type ModalActionBase = Omit<
|
|
323
|
-
React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
324
|
-
"onClick"
|
|
325
|
-
> & {
|
|
326
|
-
/** Icon name from the Seeds icon set */
|
|
327
|
-
iconName?: TypeIconName;
|
|
328
|
-
/** Optional click handler; ignored for type "close" */
|
|
329
|
-
onClick?: () => void;
|
|
330
|
-
/** Accessible name for the action button */
|
|
331
|
-
"aria-label": string;
|
|
332
|
-
};
|
|
333
|
-
|
|
334
|
-
/**
|
|
335
|
-
* Props for ModalAction component.
|
|
336
|
-
*
|
|
337
|
-
* Discriminated union supporting two action types:
|
|
338
|
-
* - "close": Automatically closes the modal when clicked (uses Dialog.Close)
|
|
339
|
-
* - "button": Custom action with user-defined onClick handler
|
|
340
|
-
*/
|
|
341
|
-
export type TypeModalActionProps =
|
|
342
|
-
| (ModalActionBase & { actionType: "close" }) // uses Dialog.Close
|
|
343
|
-
| (ModalActionBase & { actionType?: "button" }); // regular button action
|