@atlaskit/modal-dialog 16.3.2 → 16.3.4
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
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @atlaskit/modal-dialog
|
|
2
2
|
|
|
3
|
+
## 16.3.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies
|
|
8
|
+
|
|
9
|
+
## 16.3.3
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [`ccb9237bc10d9`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/ccb9237bc10d9) -
|
|
14
|
+
Add the `dismissedBy` API to control Dialog dismissal consistently across browsers. Align Dialog
|
|
15
|
+
with Popover by notifying `onClose` after native dismissal, ensure both primitives unmount after
|
|
16
|
+
native dismissal, and simplify Modal close handling behind `platform-dst-top-layer`.
|
|
17
|
+
- Updated dependencies
|
|
18
|
+
|
|
3
19
|
## 16.3.2
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
|
@@ -13,6 +13,7 @@ var React = _react;
|
|
|
13
13
|
var _runtime = require("@compiled/react/runtime");
|
|
14
14
|
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
|
|
15
15
|
var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
|
|
16
|
+
var _bindEventListener = require("bind-event-listener");
|
|
16
17
|
var _reactFocusLock = _interopRequireDefault(require("react-focus-lock"));
|
|
17
18
|
var _reactScrolllock = _interopRequireWildcard(require("react-scrolllock"));
|
|
18
19
|
var _analyticsNext = require("@atlaskit/analytics-next");
|
|
@@ -91,7 +92,7 @@ function useModalCloseHandler(providedOnClose) {
|
|
|
91
92
|
action: 'closed',
|
|
92
93
|
componentName: 'modalDialog',
|
|
93
94
|
packageName: "@atlaskit/modal-dialog",
|
|
94
|
-
packageVersion: "16.3.
|
|
95
|
+
packageVersion: "16.3.3"
|
|
95
96
|
});
|
|
96
97
|
}
|
|
97
98
|
function getScrollMode(_ref) {
|
|
@@ -149,6 +150,17 @@ function getTopLayerSurfaceHeight(input) {
|
|
|
149
150
|
}
|
|
150
151
|
return input;
|
|
151
152
|
}
|
|
153
|
+
function getDialogDismissedBy(_ref2) {
|
|
154
|
+
var shouldCloseOnEscapePress = _ref2.shouldCloseOnEscapePress,
|
|
155
|
+
shouldCloseOnOverlayClick = _ref2.shouldCloseOnOverlayClick;
|
|
156
|
+
if (shouldCloseOnEscapePress && shouldCloseOnOverlayClick) {
|
|
157
|
+
return 'escape-and-outside-click';
|
|
158
|
+
}
|
|
159
|
+
if (shouldCloseOnEscapePress) {
|
|
160
|
+
return 'escape';
|
|
161
|
+
}
|
|
162
|
+
return 'none';
|
|
163
|
+
}
|
|
152
164
|
|
|
153
165
|
/**
|
|
154
166
|
* Top-layer rendering path (platform-dst-top-layer).
|
|
@@ -158,8 +170,7 @@ function getTopLayerSurfaceHeight(input) {
|
|
|
158
170
|
*
|
|
159
171
|
* Key decisions:
|
|
160
172
|
* - Animation: CSS transitions via @starting-style / allow-discrete.
|
|
161
|
-
* - Close gating:
|
|
162
|
-
* (see notes/guides/dialog-close-flow.md).
|
|
173
|
+
* - Close gating: Dialog only forwards allowed reasons.
|
|
163
174
|
* - onClose event param: undefined - consumers should use close reason.
|
|
164
175
|
* - Focus restoration: native <dialog> behavior replaces react-focus-lock's
|
|
165
176
|
* returnFocus (see accessibility-criteria.md).
|
|
@@ -226,21 +237,38 @@ function ModalWrapperTopLayer(props) {
|
|
|
226
237
|
};
|
|
227
238
|
}, [defaultTestId, titleId, onCloseHandler, providedOnClose, isFullScreen]);
|
|
228
239
|
|
|
229
|
-
//
|
|
230
|
-
//
|
|
231
|
-
var onDialogClose = (0, _react.useCallback)(function (
|
|
232
|
-
var reason =
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
240
|
+
// Dialog has already applied the close behavior configured by `dismissedBy`.
|
|
241
|
+
// Pass a synthetic event to satisfy the KeyboardOrMouseEvent contract.
|
|
242
|
+
var onDialogClose = (0, _react.useCallback)(function (_ref3) {
|
|
243
|
+
var reason = _ref3.reason;
|
|
244
|
+
onCloseHandler((0, _dialog.createCloseEvent)({
|
|
245
|
+
reason: reason
|
|
246
|
+
}));
|
|
247
|
+
}, [onCloseHandler]);
|
|
248
|
+
var dismissedBy = getDialogDismissedBy({
|
|
249
|
+
shouldCloseOnEscapePress: shouldCloseOnEscapePress,
|
|
250
|
+
shouldCloseOnOverlayClick: shouldCloseOnOverlayClick
|
|
251
|
+
});
|
|
252
|
+
var shouldShimCloseOnOverlayClick = shouldCloseOnOverlayClick && !shouldCloseOnEscapePress;
|
|
253
|
+
|
|
254
|
+
// Required until we fully remove `shouldCloseOnEscapePress`.
|
|
255
|
+
// Dialog's `dismissedBy` options do not support outside click without Escape (because it is a bad pattern)
|
|
256
|
+
(0, _react.useEffect)(function () {
|
|
257
|
+
var dialog = dialogRef.current;
|
|
258
|
+
if (!dialog || !shouldShimCloseOnOverlayClick) {
|
|
259
|
+
return;
|
|
242
260
|
}
|
|
243
|
-
|
|
261
|
+
return (0, _bindEventListener.bind)(dialog, {
|
|
262
|
+
type: 'click',
|
|
263
|
+
listener: function listener(event) {
|
|
264
|
+
if (event.target === event.currentTarget) {
|
|
265
|
+
onDialogClose({
|
|
266
|
+
reason: 'overlay-click'
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
}, [onDialogClose, shouldShimCloseOnOverlayClick]);
|
|
244
272
|
|
|
245
273
|
// ExitingPersistence: isExiting → isOpen={false} → Dialog exit animation →
|
|
246
274
|
// onExitFinish → onCloseComplete + unmount.
|
|
@@ -293,6 +321,7 @@ function ModalWrapperTopLayer(props) {
|
|
|
293
321
|
return /*#__PURE__*/React.createElement(_dialog.Dialog, (0, _extends2.default)({
|
|
294
322
|
ref: dialogRef,
|
|
295
323
|
onClose: onDialogClose,
|
|
324
|
+
dismissedBy: dismissedBy,
|
|
296
325
|
onExitFinish: handleDialogExitFinish,
|
|
297
326
|
shouldAnimate: !isFullScreen,
|
|
298
327
|
isOpen: !isExiting,
|
|
@@ -4,6 +4,7 @@ import "./modal-wrapper.compiled.css";
|
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import { ax, ix } from "@compiled/react/runtime";
|
|
6
6
|
import { forwardRef, useCallback, useEffect, useMemo, useRef } from 'react';
|
|
7
|
+
import { bind } from 'bind-event-listener';
|
|
7
8
|
import FocusLock from 'react-focus-lock';
|
|
8
9
|
import ScrollLock, { TouchScrollable } from 'react-scrolllock';
|
|
9
10
|
import { usePlatformLeafEventHandler } from '@atlaskit/analytics-next';
|
|
@@ -81,7 +82,7 @@ function useModalCloseHandler(providedOnClose) {
|
|
|
81
82
|
action: 'closed',
|
|
82
83
|
componentName: 'modalDialog',
|
|
83
84
|
packageName: "@atlaskit/modal-dialog",
|
|
84
|
-
packageVersion: "16.3.
|
|
85
|
+
packageVersion: "16.3.3"
|
|
85
86
|
});
|
|
86
87
|
}
|
|
87
88
|
function getScrollMode({
|
|
@@ -140,6 +141,18 @@ function getTopLayerSurfaceHeight(input) {
|
|
|
140
141
|
}
|
|
141
142
|
return input;
|
|
142
143
|
}
|
|
144
|
+
function getDialogDismissedBy({
|
|
145
|
+
shouldCloseOnEscapePress,
|
|
146
|
+
shouldCloseOnOverlayClick
|
|
147
|
+
}) {
|
|
148
|
+
if (shouldCloseOnEscapePress && shouldCloseOnOverlayClick) {
|
|
149
|
+
return 'escape-and-outside-click';
|
|
150
|
+
}
|
|
151
|
+
if (shouldCloseOnEscapePress) {
|
|
152
|
+
return 'escape';
|
|
153
|
+
}
|
|
154
|
+
return 'none';
|
|
155
|
+
}
|
|
143
156
|
|
|
144
157
|
/**
|
|
145
158
|
* Top-layer rendering path (platform-dst-top-layer).
|
|
@@ -149,8 +162,7 @@ function getTopLayerSurfaceHeight(input) {
|
|
|
149
162
|
*
|
|
150
163
|
* Key decisions:
|
|
151
164
|
* - Animation: CSS transitions via @starting-style / allow-discrete.
|
|
152
|
-
* - Close gating:
|
|
153
|
-
* (see notes/guides/dialog-close-flow.md).
|
|
165
|
+
* - Close gating: Dialog only forwards allowed reasons.
|
|
154
166
|
* - onClose event param: undefined - consumers should use close reason.
|
|
155
167
|
* - Focus restoration: native <dialog> behavior replaces react-focus-lock's
|
|
156
168
|
* returnFocus (see accessibility-criteria.md).
|
|
@@ -211,22 +223,39 @@ function ModalWrapperTopLayer(props) {
|
|
|
211
223
|
isFullScreen: isFullScreen !== null && isFullScreen !== void 0 ? isFullScreen : false
|
|
212
224
|
}), [defaultTestId, titleId, onCloseHandler, providedOnClose, isFullScreen]);
|
|
213
225
|
|
|
214
|
-
//
|
|
215
|
-
//
|
|
226
|
+
// Dialog has already applied the close behavior configured by `dismissedBy`.
|
|
227
|
+
// Pass a synthetic event to satisfy the KeyboardOrMouseEvent contract.
|
|
216
228
|
const onDialogClose = useCallback(({
|
|
217
229
|
reason
|
|
218
230
|
}) => {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
231
|
+
onCloseHandler(createCloseEvent({
|
|
232
|
+
reason
|
|
233
|
+
}));
|
|
234
|
+
}, [onCloseHandler]);
|
|
235
|
+
const dismissedBy = getDialogDismissedBy({
|
|
236
|
+
shouldCloseOnEscapePress,
|
|
237
|
+
shouldCloseOnOverlayClick
|
|
238
|
+
});
|
|
239
|
+
const shouldShimCloseOnOverlayClick = shouldCloseOnOverlayClick && !shouldCloseOnEscapePress;
|
|
240
|
+
|
|
241
|
+
// Required until we fully remove `shouldCloseOnEscapePress`.
|
|
242
|
+
// Dialog's `dismissedBy` options do not support outside click without Escape (because it is a bad pattern)
|
|
243
|
+
useEffect(() => {
|
|
244
|
+
const dialog = dialogRef.current;
|
|
245
|
+
if (!dialog || !shouldShimCloseOnOverlayClick) {
|
|
246
|
+
return;
|
|
228
247
|
}
|
|
229
|
-
|
|
248
|
+
return bind(dialog, {
|
|
249
|
+
type: 'click',
|
|
250
|
+
listener(event) {
|
|
251
|
+
if (event.target === event.currentTarget) {
|
|
252
|
+
onDialogClose({
|
|
253
|
+
reason: 'overlay-click'
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
}, [onDialogClose, shouldShimCloseOnOverlayClick]);
|
|
230
259
|
|
|
231
260
|
// ExitingPersistence: isExiting → isOpen={false} → Dialog exit animation →
|
|
232
261
|
// onExitFinish → onCloseComplete + unmount.
|
|
@@ -279,6 +308,7 @@ function ModalWrapperTopLayer(props) {
|
|
|
279
308
|
return /*#__PURE__*/React.createElement(Dialog, _extends({
|
|
280
309
|
ref: dialogRef,
|
|
281
310
|
onClose: onDialogClose,
|
|
311
|
+
dismissedBy: dismissedBy,
|
|
282
312
|
onExitFinish: handleDialogExitFinish,
|
|
283
313
|
shouldAnimate: !isFullScreen,
|
|
284
314
|
isOpen: !isExiting,
|
|
@@ -5,6 +5,7 @@ import "./modal-wrapper.compiled.css";
|
|
|
5
5
|
import * as React from 'react';
|
|
6
6
|
import { ax, ix } from "@compiled/react/runtime";
|
|
7
7
|
import { forwardRef, useCallback, useEffect, useMemo, useRef } from 'react';
|
|
8
|
+
import { bind } from 'bind-event-listener';
|
|
8
9
|
import FocusLock from 'react-focus-lock';
|
|
9
10
|
import ScrollLock, { TouchScrollable } from 'react-scrolllock';
|
|
10
11
|
import { usePlatformLeafEventHandler } from '@atlaskit/analytics-next';
|
|
@@ -82,7 +83,7 @@ function useModalCloseHandler(providedOnClose) {
|
|
|
82
83
|
action: 'closed',
|
|
83
84
|
componentName: 'modalDialog',
|
|
84
85
|
packageName: "@atlaskit/modal-dialog",
|
|
85
|
-
packageVersion: "16.3.
|
|
86
|
+
packageVersion: "16.3.3"
|
|
86
87
|
});
|
|
87
88
|
}
|
|
88
89
|
function getScrollMode(_ref) {
|
|
@@ -140,6 +141,17 @@ function getTopLayerSurfaceHeight(input) {
|
|
|
140
141
|
}
|
|
141
142
|
return input;
|
|
142
143
|
}
|
|
144
|
+
function getDialogDismissedBy(_ref2) {
|
|
145
|
+
var shouldCloseOnEscapePress = _ref2.shouldCloseOnEscapePress,
|
|
146
|
+
shouldCloseOnOverlayClick = _ref2.shouldCloseOnOverlayClick;
|
|
147
|
+
if (shouldCloseOnEscapePress && shouldCloseOnOverlayClick) {
|
|
148
|
+
return 'escape-and-outside-click';
|
|
149
|
+
}
|
|
150
|
+
if (shouldCloseOnEscapePress) {
|
|
151
|
+
return 'escape';
|
|
152
|
+
}
|
|
153
|
+
return 'none';
|
|
154
|
+
}
|
|
143
155
|
|
|
144
156
|
/**
|
|
145
157
|
* Top-layer rendering path (platform-dst-top-layer).
|
|
@@ -149,8 +161,7 @@ function getTopLayerSurfaceHeight(input) {
|
|
|
149
161
|
*
|
|
150
162
|
* Key decisions:
|
|
151
163
|
* - Animation: CSS transitions via @starting-style / allow-discrete.
|
|
152
|
-
* - Close gating:
|
|
153
|
-
* (see notes/guides/dialog-close-flow.md).
|
|
164
|
+
* - Close gating: Dialog only forwards allowed reasons.
|
|
154
165
|
* - onClose event param: undefined - consumers should use close reason.
|
|
155
166
|
* - Focus restoration: native <dialog> behavior replaces react-focus-lock's
|
|
156
167
|
* returnFocus (see accessibility-criteria.md).
|
|
@@ -217,21 +228,38 @@ function ModalWrapperTopLayer(props) {
|
|
|
217
228
|
};
|
|
218
229
|
}, [defaultTestId, titleId, onCloseHandler, providedOnClose, isFullScreen]);
|
|
219
230
|
|
|
220
|
-
//
|
|
221
|
-
//
|
|
222
|
-
var onDialogClose = useCallback(function (
|
|
223
|
-
var reason =
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
231
|
+
// Dialog has already applied the close behavior configured by `dismissedBy`.
|
|
232
|
+
// Pass a synthetic event to satisfy the KeyboardOrMouseEvent contract.
|
|
233
|
+
var onDialogClose = useCallback(function (_ref3) {
|
|
234
|
+
var reason = _ref3.reason;
|
|
235
|
+
onCloseHandler(createCloseEvent({
|
|
236
|
+
reason: reason
|
|
237
|
+
}));
|
|
238
|
+
}, [onCloseHandler]);
|
|
239
|
+
var dismissedBy = getDialogDismissedBy({
|
|
240
|
+
shouldCloseOnEscapePress: shouldCloseOnEscapePress,
|
|
241
|
+
shouldCloseOnOverlayClick: shouldCloseOnOverlayClick
|
|
242
|
+
});
|
|
243
|
+
var shouldShimCloseOnOverlayClick = shouldCloseOnOverlayClick && !shouldCloseOnEscapePress;
|
|
244
|
+
|
|
245
|
+
// Required until we fully remove `shouldCloseOnEscapePress`.
|
|
246
|
+
// Dialog's `dismissedBy` options do not support outside click without Escape (because it is a bad pattern)
|
|
247
|
+
useEffect(function () {
|
|
248
|
+
var dialog = dialogRef.current;
|
|
249
|
+
if (!dialog || !shouldShimCloseOnOverlayClick) {
|
|
250
|
+
return;
|
|
233
251
|
}
|
|
234
|
-
|
|
252
|
+
return bind(dialog, {
|
|
253
|
+
type: 'click',
|
|
254
|
+
listener: function listener(event) {
|
|
255
|
+
if (event.target === event.currentTarget) {
|
|
256
|
+
onDialogClose({
|
|
257
|
+
reason: 'overlay-click'
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
}, [onDialogClose, shouldShimCloseOnOverlayClick]);
|
|
235
263
|
|
|
236
264
|
// ExitingPersistence: isExiting → isOpen={false} → Dialog exit animation →
|
|
237
265
|
// onExitFinish → onCloseComplete + unmount.
|
|
@@ -284,6 +312,7 @@ function ModalWrapperTopLayer(props) {
|
|
|
284
312
|
return /*#__PURE__*/React.createElement(Dialog, _extends({
|
|
285
313
|
ref: dialogRef,
|
|
286
314
|
onClose: onDialogClose,
|
|
315
|
+
dismissedBy: dismissedBy,
|
|
287
316
|
onExitFinish: handleDialogExitFinish,
|
|
288
317
|
shouldAnimate: !isFullScreen,
|
|
289
318
|
isOpen: !isExiting,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/modal-dialog",
|
|
3
|
-
"version": "16.3.
|
|
3
|
+
"version": "16.3.4",
|
|
4
4
|
"description": "A modal dialog displays content that requires user interaction, in a layer above the page.",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org/"
|
|
@@ -41,15 +41,15 @@
|
|
|
41
41
|
"@atlaskit/ds-lib": "^8.0.0",
|
|
42
42
|
"@atlaskit/icon": "^37.3.0",
|
|
43
43
|
"@atlaskit/layering": "^5.0.0",
|
|
44
|
-
"@atlaskit/motion": "^8.
|
|
44
|
+
"@atlaskit/motion": "^8.1.0",
|
|
45
45
|
"@atlaskit/platform-feature-flags": "^2.1.0",
|
|
46
46
|
"@atlaskit/portal": "^6.2.0",
|
|
47
|
-
"@atlaskit/pragmatic-drag-and-drop": "^
|
|
47
|
+
"@atlaskit/pragmatic-drag-and-drop": "^3.0.0",
|
|
48
48
|
"@atlaskit/primitives": "^22.2.0",
|
|
49
49
|
"@atlaskit/react-compiler-gating": "^0.2.0",
|
|
50
50
|
"@atlaskit/theme": "^28.0.0",
|
|
51
51
|
"@atlaskit/tokens": "^16.7.0",
|
|
52
|
-
"@atlaskit/top-layer": "^4.
|
|
52
|
+
"@atlaskit/top-layer": "^4.2.0",
|
|
53
53
|
"@babel/runtime": "^7.0.0",
|
|
54
54
|
"@compiled/react": "^1.0.2",
|
|
55
55
|
"bind-event-listener": "^3.0.0",
|
package/modal-dialog.docs.tsx
DELETED
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
|
|
3
|
-
import type { StructuredContentSource } from '@atlassian/structured-docs-types/types';
|
|
4
|
-
|
|
5
|
-
const documentation: StructuredContentSource = {
|
|
6
|
-
components: [
|
|
7
|
-
{
|
|
8
|
-
name: 'Modal',
|
|
9
|
-
description: 'A modal dialog component for important content.',
|
|
10
|
-
status: 'general-availability',
|
|
11
|
-
designSource: {
|
|
12
|
-
figmaUrl: 'https://go.atlassian.com/figma-library-ads-82128-80670',
|
|
13
|
-
},
|
|
14
|
-
import: {
|
|
15
|
-
name: 'Modal',
|
|
16
|
-
package: '@atlaskit/modal-dialog',
|
|
17
|
-
type: 'default',
|
|
18
|
-
packagePath: path.resolve(__dirname),
|
|
19
|
-
packageJson: require('./package.json'),
|
|
20
|
-
},
|
|
21
|
-
usageGuidelines: [
|
|
22
|
-
'Use for immediate task or critical/warning requiring a response; blocking until closed',
|
|
23
|
-
'Use sparingly—modals are invasive',
|
|
24
|
-
'One task per modal; limit interactions; no nested modals (inaccessible)',
|
|
25
|
-
'Anatomy: header (h1 title), close button, body, footer with primary + cancel/close',
|
|
26
|
-
'Footer: primary button on the right of secondary; multiple dismissal methods (close button, Esc, blanket click, Cancel/Close)',
|
|
27
|
-
'Use Popup for smaller info + controls; Spotlight for onboarding; Inline message for alert/action',
|
|
28
|
-
],
|
|
29
|
-
contentGuidelines: [
|
|
30
|
-
'Use clear, descriptive titles',
|
|
31
|
-
'Primary button label should reflect the modal title',
|
|
32
|
-
'Use action verbs in button labels',
|
|
33
|
-
'Keep content focused on a single task or message',
|
|
34
|
-
'Use sentence case for all text',
|
|
35
|
-
],
|
|
36
|
-
accessibilityGuidelines: [
|
|
37
|
-
'Modal must have a title: use title component, or titleId from useModal, or label (avoid if no visual title)',
|
|
38
|
-
'Close button is required (except rare cases—consult a11y team)',
|
|
39
|
-
'Do not rely on color alone for severity; provide accessible label for icons',
|
|
40
|
-
'Focus order: 1) close (or title or container), 2) first focusable, 3) secondary button, 4) primary, 5) return focus to trigger on close',
|
|
41
|
-
'Dismiss via: close button, Esc, click blanket, Cancel/Close in footer',
|
|
42
|
-
'Ensure modal content is announced by screen readers',
|
|
43
|
-
'Ensure keyboard navigation and escape key support',
|
|
44
|
-
'Maintain focus within modal when open',
|
|
45
|
-
],
|
|
46
|
-
examples: [
|
|
47
|
-
{
|
|
48
|
-
name: 'Default',
|
|
49
|
-
description: 'The default form of a modal dialog.',
|
|
50
|
-
source: path.resolve(__dirname, './examples/constellation/modal-default.tsx'),
|
|
51
|
-
},
|
|
52
|
-
],
|
|
53
|
-
keywords: ['modal', 'dialog', 'popup', 'overlay', 'focused', 'interaction', 'layer'],
|
|
54
|
-
categories: ['overlay'],
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
name: 'ModalHeader',
|
|
58
|
-
description:
|
|
59
|
-
'The header section of a modal dialog, typically containing the title and optional close button.',
|
|
60
|
-
status: 'general-availability',
|
|
61
|
-
import: {
|
|
62
|
-
name: 'ModalHeader',
|
|
63
|
-
package: '@atlaskit/modal-dialog/modal-header',
|
|
64
|
-
type: 'default',
|
|
65
|
-
packagePath: path.resolve(__dirname),
|
|
66
|
-
packageJson: require('./package.json'),
|
|
67
|
-
},
|
|
68
|
-
usageGuidelines: [
|
|
69
|
-
'Use as the first child of Modal',
|
|
70
|
-
'Use hasCloseButton prop for standard close affordance',
|
|
71
|
-
'Ensure header contains ModalTitle or equivalent for accessibility',
|
|
72
|
-
'For custom headers, use CloseButton as first element in DOM',
|
|
73
|
-
],
|
|
74
|
-
contentGuidelines: ['Use clear, descriptive titles', 'Keep header content focused'],
|
|
75
|
-
examples: [
|
|
76
|
-
{
|
|
77
|
-
name: 'Modal Header',
|
|
78
|
-
description: 'ModalHeader example',
|
|
79
|
-
source: path.resolve(__dirname, './examples/constellation/modal-header-default.tsx'),
|
|
80
|
-
},
|
|
81
|
-
],
|
|
82
|
-
keywords: ['modal', 'header', 'title', 'close'],
|
|
83
|
-
categories: ['overlay'],
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
name: 'ModalTitle',
|
|
87
|
-
description: 'The title element for a modal dialog. Renders as h1 for accessibility.',
|
|
88
|
-
status: 'general-availability',
|
|
89
|
-
import: {
|
|
90
|
-
name: 'ModalTitle',
|
|
91
|
-
package: '@atlaskit/modal-dialog/modal-title',
|
|
92
|
-
type: 'default',
|
|
93
|
-
packagePath: path.resolve(__dirname),
|
|
94
|
-
packageJson: require('./package.json'),
|
|
95
|
-
},
|
|
96
|
-
usageGuidelines: [
|
|
97
|
-
'Use inside ModalHeader for the modal title',
|
|
98
|
-
'Modal must have a title for accessibility',
|
|
99
|
-
'Supports any valid React element as children',
|
|
100
|
-
],
|
|
101
|
-
contentGuidelines: ['Use clear, descriptive titles', 'Use sentence case'],
|
|
102
|
-
examples: [
|
|
103
|
-
{
|
|
104
|
-
name: 'Modal Title',
|
|
105
|
-
description: 'ModalTitle example',
|
|
106
|
-
source: path.resolve(__dirname, './examples/constellation/modal-default.tsx'),
|
|
107
|
-
},
|
|
108
|
-
],
|
|
109
|
-
keywords: ['modal', 'title', 'heading'],
|
|
110
|
-
categories: ['overlay'],
|
|
111
|
-
},
|
|
112
|
-
{
|
|
113
|
-
name: 'ModalBody',
|
|
114
|
-
description: 'The main content area of a modal dialog.',
|
|
115
|
-
status: 'general-availability',
|
|
116
|
-
import: {
|
|
117
|
-
name: 'ModalBody',
|
|
118
|
-
package: '@atlaskit/modal-dialog/modal-body',
|
|
119
|
-
type: 'default',
|
|
120
|
-
packagePath: path.resolve(__dirname),
|
|
121
|
-
packageJson: require('./package.json'),
|
|
122
|
-
},
|
|
123
|
-
usageGuidelines: [
|
|
124
|
-
'Use for the primary content between header and footer',
|
|
125
|
-
'Accepts any valid React element as children',
|
|
126
|
-
'Handles overflow and scrolling when content exceeds viewport',
|
|
127
|
-
],
|
|
128
|
-
contentGuidelines: ['Keep content focused on a single task', 'Use clear, readable content'],
|
|
129
|
-
examples: [
|
|
130
|
-
{
|
|
131
|
-
name: 'Modal Body',
|
|
132
|
-
description: 'ModalBody example',
|
|
133
|
-
source: path.resolve(__dirname, './examples/constellation/modal-default.tsx'),
|
|
134
|
-
},
|
|
135
|
-
],
|
|
136
|
-
keywords: ['modal', 'body', 'content'],
|
|
137
|
-
categories: ['overlay'],
|
|
138
|
-
},
|
|
139
|
-
{
|
|
140
|
-
name: 'ModalFooter',
|
|
141
|
-
description: 'The footer section of a modal dialog, typically containing action buttons.',
|
|
142
|
-
status: 'general-availability',
|
|
143
|
-
import: {
|
|
144
|
-
name: 'ModalFooter',
|
|
145
|
-
package: '@atlaskit/modal-dialog/modal-footer',
|
|
146
|
-
type: 'default',
|
|
147
|
-
packagePath: path.resolve(__dirname),
|
|
148
|
-
packageJson: require('./package.json'),
|
|
149
|
-
},
|
|
150
|
-
usageGuidelines: [
|
|
151
|
-
'Use for primary and secondary actions',
|
|
152
|
-
'Primary button on the right of secondary',
|
|
153
|
-
'Include Cancel/Close for dismissal',
|
|
154
|
-
'Accepts any valid React element for custom layouts',
|
|
155
|
-
],
|
|
156
|
-
contentGuidelines: [
|
|
157
|
-
'Primary button label should reflect the modal title',
|
|
158
|
-
'Use action verbs in button labels',
|
|
159
|
-
],
|
|
160
|
-
examples: [
|
|
161
|
-
{
|
|
162
|
-
name: 'Modal Footer',
|
|
163
|
-
description: 'ModalFooter example',
|
|
164
|
-
source: path.resolve(__dirname, './examples/constellation/modal-footer-default.tsx'),
|
|
165
|
-
},
|
|
166
|
-
],
|
|
167
|
-
keywords: ['modal', 'footer', 'actions', 'buttons'],
|
|
168
|
-
categories: ['overlay'],
|
|
169
|
-
},
|
|
170
|
-
{
|
|
171
|
-
name: 'ModalTransition',
|
|
172
|
-
description: 'A wrapper that provides enter/exit transitions for modal content.',
|
|
173
|
-
status: 'general-availability',
|
|
174
|
-
import: {
|
|
175
|
-
name: 'ModalTransition',
|
|
176
|
-
package: '@atlaskit/modal-dialog/modal-transition',
|
|
177
|
-
type: 'default',
|
|
178
|
-
packagePath: path.resolve(__dirname),
|
|
179
|
-
packageJson: require('./package.json'),
|
|
180
|
-
},
|
|
181
|
-
usageGuidelines: [
|
|
182
|
-
'Wrap Modal with ModalTransition for animated open/close',
|
|
183
|
-
'Use when modal visibility is controlled by state',
|
|
184
|
-
'Children mount when visible and unmount when closed',
|
|
185
|
-
],
|
|
186
|
-
examples: [
|
|
187
|
-
{
|
|
188
|
-
name: 'Modal Transition',
|
|
189
|
-
description: 'ModalTransition example',
|
|
190
|
-
source: path.resolve(__dirname, './examples/constellation/modal-default.tsx'),
|
|
191
|
-
},
|
|
192
|
-
],
|
|
193
|
-
keywords: ['modal', 'transition', 'animation'],
|
|
194
|
-
categories: ['overlay'],
|
|
195
|
-
},
|
|
196
|
-
{
|
|
197
|
-
name: 'CloseButton',
|
|
198
|
-
description:
|
|
199
|
-
'An accessible close button for use in custom modal headers. Ensures users have an obvious way to close the modal.',
|
|
200
|
-
status: 'general-availability',
|
|
201
|
-
import: {
|
|
202
|
-
name: 'CloseButton',
|
|
203
|
-
package: '@atlaskit/modal-dialog/close-button',
|
|
204
|
-
type: 'named',
|
|
205
|
-
packagePath: path.resolve(__dirname),
|
|
206
|
-
packageJson: require('./package.json'),
|
|
207
|
-
},
|
|
208
|
-
usageGuidelines: [
|
|
209
|
-
'Use when customizing ModalHeader without hasCloseButton',
|
|
210
|
-
'Render CloseButton first in DOM for proper focus order',
|
|
211
|
-
'Use Flex with row-reverse if close should appear on the right',
|
|
212
|
-
'Provide label prop for custom accessible name',
|
|
213
|
-
],
|
|
214
|
-
accessibilityGuidelines: [
|
|
215
|
-
'Close button is required for modals (consult a11y team for rare exceptions)',
|
|
216
|
-
'Ensure close button is keyboard accessible',
|
|
217
|
-
],
|
|
218
|
-
examples: [
|
|
219
|
-
{
|
|
220
|
-
name: 'Close Button',
|
|
221
|
-
description: 'CloseButton example',
|
|
222
|
-
source: path.resolve(__dirname, './examples/constellation/modal-header-custom.tsx'),
|
|
223
|
-
},
|
|
224
|
-
],
|
|
225
|
-
keywords: ['modal', 'close', 'button', 'dismiss'],
|
|
226
|
-
categories: ['overlay'],
|
|
227
|
-
},
|
|
228
|
-
],
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
export default documentation;
|