@khanacademy/wonder-blocks-modal 5.1.11 → 5.1.13

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 (33) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/package.json +10 -10
  3. package/src/components/__tests__/close-button.test.tsx +0 -37
  4. package/src/components/__tests__/focus-trap.test.tsx +0 -100
  5. package/src/components/__tests__/modal-backdrop.test.tsx +0 -241
  6. package/src/components/__tests__/modal-dialog.test.tsx +0 -87
  7. package/src/components/__tests__/modal-header.test.tsx +0 -97
  8. package/src/components/__tests__/modal-launcher.test.tsx +0 -436
  9. package/src/components/__tests__/modal-panel.test.tsx +0 -42
  10. package/src/components/__tests__/one-pane-dialog.test.tsx +0 -87
  11. package/src/components/close-button.tsx +0 -64
  12. package/src/components/focus-trap.tsx +0 -148
  13. package/src/components/modal-backdrop.tsx +0 -172
  14. package/src/components/modal-content.tsx +0 -81
  15. package/src/components/modal-context.ts +0 -16
  16. package/src/components/modal-dialog.tsx +0 -164
  17. package/src/components/modal-footer.tsx +0 -54
  18. package/src/components/modal-header.tsx +0 -194
  19. package/src/components/modal-launcher.tsx +0 -297
  20. package/src/components/modal-panel.tsx +0 -188
  21. package/src/components/one-pane-dialog.tsx +0 -244
  22. package/src/components/scroll-disabler.ts +0 -95
  23. package/src/index.ts +0 -17
  24. package/src/themes/default.ts +0 -36
  25. package/src/themes/khanmigo.ts +0 -16
  26. package/src/themes/themed-modal-dialog.tsx +0 -44
  27. package/src/util/constants.ts +0 -6
  28. package/src/util/find-focusable-nodes.ts +0 -12
  29. package/src/util/maybe-get-portal-mounted-modal-host-element.test.tsx +0 -133
  30. package/src/util/maybe-get-portal-mounted-modal-host-element.ts +0 -35
  31. package/src/util/types.ts +0 -13
  32. package/tsconfig-build.json +0 -20
  33. package/tsconfig-build.tsbuildinfo +0 -1
@@ -1,194 +0,0 @@
1
- import * as React from "react";
2
- import {Breadcrumbs} from "@khanacademy/wonder-blocks-breadcrumbs";
3
- import {View} from "@khanacademy/wonder-blocks-core";
4
- import {HeadingMedium, LabelSmall} from "@khanacademy/wonder-blocks-typography";
5
- import {
6
- ThemedStylesFn,
7
- useScopedTheme,
8
- useStyles,
9
- } from "@khanacademy/wonder-blocks-theming";
10
- import {
11
- ModalDialogThemeContext,
12
- ModalDialogThemeContract,
13
- } from "../themes/themed-modal-dialog";
14
-
15
- type Common = {
16
- /**
17
- * The main title rendered in larger bold text.
18
- */
19
- title: string;
20
- /**
21
- * Whether to display the "light" version of this component instead, for
22
- * use when the item is used on a dark background.
23
- */
24
- light: boolean;
25
- /**
26
- * An id to provide a selector for the title element.
27
- */
28
- titleId: string;
29
- /**
30
- * Test ID used for e2e testing.
31
- *
32
- * In this case, this component is internal, so `testId` is composed with
33
- * the `testId` passed down from the Dialog variant + a suffix to scope it
34
- * to this component.
35
- *
36
- * @example
37
- * For testId="some-random-id"
38
- * The result will be: `some-random-id-modal-header`
39
- */
40
- testId?: string;
41
- };
42
-
43
- type WithSubtitle = Common & {
44
- /**
45
- * The dialog subtitle.
46
- */
47
- subtitle: string;
48
- };
49
-
50
- type WithBreadcrumbs = Common & {
51
- /**
52
- * Adds a breadcrumb-trail, appearing in the ModalHeader, above the title.
53
- */
54
- breadcrumbs: React.ReactElement<React.ComponentProps<typeof Breadcrumbs>>;
55
- };
56
-
57
- type Props = Common | WithSubtitle | WithBreadcrumbs;
58
-
59
- /**
60
- * This is a helper component that is never rendered by itself. It is always
61
- * pinned to the top of the dialog, is responsive using the same behavior as its
62
- * parent dialog, and has the following properties:
63
- * - title
64
- * - breadcrumb OR subtitle, but not both.
65
- *
66
- * **Accessibility notes:**
67
- *
68
- * - By default (e.g. using [OnePaneDialog](/#onepanedialog)), `titleId` is
69
- * populated automatically by the parent container.
70
- * - If there is a custom Dialog implementation (e.g. `TwoPaneDialog`), the
71
- * ModalHeader doesn’t have to have the `titleId` prop however this is
72
- * recommended. It should match the `aria-labelledby` prop of the
73
- * [ModalDialog](/#modaldialog) component. If you want to see an example of
74
- * how to generate this ID, check [IDProvider](/#idprovider).
75
- *
76
- * **Implementation notes:**
77
- *
78
- * If you are creating a custom Dialog, make sure to follow these guidelines:
79
- * - Make sure to include it as part of [ModalPanel](/#modalpanel) by using the
80
- * `header` prop.
81
- * - Add a title (required).
82
- * - Optionally add a subtitle or breadcrumbs.
83
- * - We encourage you to add `titleId` (see Accessibility notes).
84
- * - If the `ModalPanel` has a dark background, make sure to set `light` to
85
- * `false`.
86
- * - If you need to create e2e tests, make sure to pass a `testId` prop and
87
- * add a sufix to scope the testId to this component: e.g.
88
- * `some-random-id-ModalHeader`. This scope will also be passed to the title
89
- * and subtitle elements: e.g. `some-random-id-ModalHeader-title`.
90
- *
91
- * Example:
92
- *
93
- * ```js
94
- * <ModalHeader
95
- * title="Sidebar using ModalHeader"
96
- * subtitle="subtitle"
97
- * titleId="uniqueTitleId"
98
- * light={false}
99
- * />
100
- * ```
101
- */
102
- export default function ModalHeader(props: Props) {
103
- const {
104
- // @ts-expect-error [FEI-5019] - TS2339 - Property 'breadcrumbs' does not exist on type 'Readonly<Props> & Readonly<{ children?: ReactNode; }>'.
105
- breadcrumbs = undefined,
106
- light,
107
- // @ts-expect-error [FEI-5019] - TS2339 - Property 'subtitle' does not exist on type 'Readonly<Props> & Readonly<{ children?: ReactNode; }>'.
108
- subtitle = undefined,
109
- testId,
110
- title,
111
- titleId,
112
- } = props;
113
-
114
- if (subtitle && breadcrumbs) {
115
- throw new Error("'subtitle' and 'breadcrumbs' can't be used together");
116
- }
117
-
118
- const {theme} = useScopedTheme(ModalDialogThemeContext);
119
- const styles = useStyles(themedStylesFn, theme);
120
-
121
- return (
122
- <View style={[styles.header, !light && styles.dark]} testId={testId}>
123
- {breadcrumbs && (
124
- <View style={styles.breadcrumbs}>{breadcrumbs}</View>
125
- )}
126
- <HeadingMedium
127
- style={styles.title}
128
- id={titleId}
129
- testId={testId && `${testId}-title`}
130
- >
131
- {title}
132
- </HeadingMedium>
133
- {subtitle && (
134
- <LabelSmall
135
- style={light && styles.subtitle}
136
- testId={testId && `${testId}-subtitle`}
137
- >
138
- {subtitle}
139
- </LabelSmall>
140
- )}
141
- </View>
142
- );
143
- }
144
-
145
- /**
146
- * Media query for small screens.
147
- * TODO(WB-1655): Change this to use the theme instead (inside themedStylesFn).
148
- * e.g. `[theme.breakpoints.small]: {...}`
149
- */
150
- const small = "@media (max-width: 767px)";
151
-
152
- const themedStylesFn: ThemedStylesFn<ModalDialogThemeContract> = (theme) => ({
153
- header: {
154
- boxShadow: `0px 1px 0px ${theme.color.shadow.default}`,
155
- display: "flex",
156
- flexDirection: "column",
157
- minHeight: 66,
158
- padding: `${theme.spacing.header.medium}px ${theme.spacing.header.large}px`,
159
- position: "relative",
160
- width: "100%",
161
-
162
- [small]: {
163
- paddingLeft: theme.spacing.header.small,
164
- paddingRight: theme.spacing.header.small,
165
- },
166
- },
167
-
168
- dark: {
169
- background: theme.color.bg.inverse,
170
- color: theme.color.text.inverse,
171
- },
172
-
173
- breadcrumbs: {
174
- color: theme.color.text.secondary,
175
- marginBottom: theme.spacing.header.xsmall,
176
- },
177
-
178
- title: {
179
- // Prevent title from overlapping the close button
180
- paddingRight: theme.spacing.header.small,
181
- [small]: {
182
- paddingRight: theme.spacing.header.large,
183
- },
184
- },
185
-
186
- subtitle: {
187
- color: theme.color.text.secondary,
188
- marginTop: theme.spacing.header.xsmall,
189
- },
190
- });
191
-
192
- ModalHeader.defaultProps = {
193
- light: true,
194
- };
@@ -1,297 +0,0 @@
1
- import * as React from "react";
2
- import * as ReactDOM from "react-dom";
3
- import {StyleSheet} from "aphrodite";
4
-
5
- import {withActionScheduler} from "@khanacademy/wonder-blocks-timing";
6
- import type {WithActionSchedulerProps} from "@khanacademy/wonder-blocks-timing";
7
-
8
- import FocusTrap from "./focus-trap";
9
- import ModalBackdrop from "./modal-backdrop";
10
- import ScrollDisabler from "./scroll-disabler";
11
- import type {ModalElement} from "../util/types";
12
- import ModalContext from "./modal-context";
13
-
14
- type Props = Readonly<{
15
- /**
16
- * The modal to render.
17
- *
18
- * The modal will be rendered inside of a container whose parent is
19
- * document.body. This allows us to use ModalLauncher within menus and
20
- * other components that clip their content. If the modal needs to close
21
- * itself by some other means than tapping the backdrop or the default
22
- * close button a render callback can be passed. The closeModal function
23
- * provided to this callback can be called to close the modal.
24
- *
25
- * Note: Don't call `closeModal` while rendering! It should be used to
26
- * respond to user intearction, like `onClick`.
27
- */
28
- modal: ModalElement | ((props: {closeModal: () => void}) => ModalElement);
29
- /**
30
- * Enables the backdrop to dismiss the modal on click/tap
31
- */
32
- backdropDismissEnabled?: boolean;
33
- /**
34
- * The selector for the element that will be focused when the dialog shows.
35
- * When not set, the first tabbable element within the dialog will be used.
36
- */
37
- initialFocusId?: string;
38
- /**
39
- * The selector for the element that will be focused after the dialog
40
- * closes. When not set, the last element focused outside the modal will
41
- * be used if it exists.
42
- */
43
- closedFocusId?: string;
44
- /**
45
- * Test ID used for e2e testing. It's set on the ModalBackdrop
46
- */
47
- testId?: string;
48
-
49
- /**
50
- * Renders the modal when true, renders nothing when false.
51
- *
52
- * Using this prop makes the component behave as a controlled component.
53
- * The parent is responsible for managing the opening/closing of the modal
54
- * when using this prop. `onClose` should always be used and `children`
55
- * should never be used with this prop. Not doing so will result in an
56
- * error being thrown.
57
- */
58
- opened?: boolean;
59
-
60
- /**
61
- * If the parent needs to be notified when the modal is closed, use this
62
- * prop. You probably want to use this instead of `onClose` on the modals
63
- * themselves, since this will capture a more complete set of close events.
64
- *
65
- * Called when the modal needs to notify the parent component that it should
66
- * be closed.
67
- *
68
- * This prop must be used when the component is being used as a controlled
69
- * component.
70
- */
71
- onClose?: () => unknown;
72
-
73
- /**
74
- * WARNING: This props should only be used when using the component as a
75
- * controlled component.
76
- */
77
- children?: (arg1: {openModal: () => unknown}) => React.ReactNode;
78
- }> &
79
- WithActionSchedulerProps;
80
-
81
- type DefaultProps = Readonly<{
82
- backdropDismissEnabled: Props["backdropDismissEnabled"];
83
- }>;
84
-
85
- type State = Readonly<{
86
- /** Whether the modal should currently be open. */
87
- opened: boolean;
88
- }>;
89
-
90
- /**
91
- * This component enables you to launch a modal, covering the screen.
92
- *
93
- * Children have access to `openModal` function via the function-as-children
94
- * pattern, so one common use case is for this component to wrap a button:
95
- *
96
- * ```js
97
- * <ModalLauncher modal={<TwoColumnModal ... />}>
98
- * {({openModal}) => <button onClick={openModal}>Learn more</button>}
99
- * </ModalLauncher>
100
- * ```
101
- *
102
- * The actual modal itself is constructed separately, using a layout component
103
- * like OnePaneDialog and is provided via
104
- * the `modal` prop.
105
- */
106
- class ModalLauncher extends React.Component<Props, State> {
107
- /**
108
- * The most recent element _outside this component_ that received focus.
109
- * Be default, it captures the element that triggered the modal opening
110
- */
111
- lastElementFocusedOutsideModal: HTMLElement | null | undefined;
112
-
113
- static defaultProps: DefaultProps = {
114
- backdropDismissEnabled: true,
115
- };
116
-
117
- static getDerivedStateFromProps(
118
- props: Props,
119
- state: State,
120
- ): Partial<State> {
121
- if (typeof props.opened === "boolean" && props.children) {
122
- // eslint-disable-next-line no-console
123
- console.warn("'children' and 'opened' can't be used together");
124
- }
125
- if (typeof props.opened === "boolean" && !props.onClose) {
126
- // eslint-disable-next-line no-console
127
- console.warn("'onClose' should be used with 'opened'");
128
- }
129
- if (typeof props.opened !== "boolean" && !props.children) {
130
- // eslint-disable-next-line no-console
131
- console.warn("either 'children' or 'opened' must be set");
132
- }
133
- return {
134
- opened:
135
- typeof props.opened === "boolean" ? props.opened : state.opened,
136
- };
137
- }
138
-
139
- state: State = {opened: false};
140
-
141
- componentDidUpdate(prevProps: Props) {
142
- // ensures the element is stored only when the modal is opened
143
- if (!prevProps.opened && this.props.opened) {
144
- this._saveLastElementFocused();
145
- }
146
- }
147
-
148
- _saveLastElementFocused: () => void = () => {
149
- // keep a reference of the element that triggers the modal
150
- // @ts-expect-error [FEI-5019] - TS2322 - Type 'Element | null' is not assignable to type 'HTMLElement | null | undefined'.
151
- this.lastElementFocusedOutsideModal = document.activeElement;
152
- };
153
-
154
- _openModal: () => void = () => {
155
- this._saveLastElementFocused();
156
- this.setState({opened: true});
157
- };
158
-
159
- _returnFocus: () => void = () => {
160
- const {closedFocusId, schedule} = this.props;
161
- const lastElement = this.lastElementFocusedOutsideModal;
162
-
163
- // Focus on the specified element after closing the modal.
164
- if (closedFocusId) {
165
- const focusElement = ReactDOM.findDOMNode(
166
- document.getElementById(closedFocusId),
167
- ) as any;
168
-
169
- if (focusElement) {
170
- // Wait for the modal to leave the DOM before trying
171
- // to focus on the specified element.
172
- schedule.animationFrame(() => {
173
- focusElement.focus();
174
- });
175
- return;
176
- }
177
- }
178
-
179
- if (lastElement != null) {
180
- // Wait for the modal to leave the DOM before trying to
181
- // return focus to the element that triggered the modal.
182
- schedule.animationFrame(() => {
183
- lastElement.focus();
184
- });
185
- }
186
- };
187
-
188
- handleCloseModal: () => void = () => {
189
- this.setState({opened: false}, () => {
190
- const {onClose} = this.props;
191
-
192
- onClose?.();
193
- this._returnFocus();
194
- });
195
- };
196
-
197
- _renderModal(): ModalElement {
198
- if (typeof this.props.modal === "function") {
199
- return this.props.modal({
200
- closeModal: this.handleCloseModal,
201
- });
202
- } else {
203
- return this.props.modal;
204
- }
205
- }
206
-
207
- render(): React.ReactElement | null {
208
- const renderedChildren = this.props.children
209
- ? this.props.children({
210
- openModal: this._openModal,
211
- })
212
- : null;
213
-
214
- const {body} = document;
215
- if (!body) {
216
- return null;
217
- }
218
-
219
- return (
220
- <ModalContext.Provider value={{closeModal: this.handleCloseModal}}>
221
- {renderedChildren}
222
- {this.state.opened &&
223
- ReactDOM.createPortal(
224
- /* We need the container View that FocusTrap creates to be at the
225
- correct z-index so that it'll be above the global nav in webapp. */
226
- <FocusTrap style={styles.container}>
227
- <ModalBackdrop
228
- initialFocusId={this.props.initialFocusId}
229
- testId={this.props.testId}
230
- onCloseModal={
231
- this.props.backdropDismissEnabled
232
- ? this.handleCloseModal
233
- : () => {}
234
- }
235
- >
236
- {this._renderModal()}
237
- </ModalBackdrop>
238
- </FocusTrap>,
239
- body,
240
- )}
241
- {this.state.opened && (
242
- <ModalLauncherKeypressListener
243
- onClose={this.handleCloseModal}
244
- />
245
- )}
246
- {this.state.opened && <ScrollDisabler />}
247
- </ModalContext.Provider>
248
- );
249
- }
250
- }
251
-
252
- /** A component that, when mounted, calls `onClose` when Escape is pressed. */
253
- class ModalLauncherKeypressListener extends React.Component<{
254
- onClose: () => unknown;
255
- }> {
256
- componentDidMount() {
257
- window.addEventListener("keyup", this._handleKeyup);
258
- }
259
-
260
- componentWillUnmount() {
261
- window.removeEventListener("keyup", this._handleKeyup);
262
- }
263
-
264
- _handleKeyup = (e: KeyboardEvent) => {
265
- // We check the key as that's keyboard layout agnostic and also avoids
266
- // the minefield of deprecated number type properties like keyCode and
267
- // which, with the replacement code, which uses a string instead.
268
- if (e.key === "Escape") {
269
- // Stop the event going any further.
270
- // For cancellation events, like the Escape key, we generally should
271
- // air on the side of caution and only allow it to cancel one thing.
272
- // So, it's polite for us to stop propagation of the event.
273
- // Otherwise, we end up with UX where one Escape key press
274
- // unexpectedly cancels multiple things.
275
- e.preventDefault();
276
- e.stopPropagation();
277
- this.props.onClose();
278
- }
279
- };
280
-
281
- render(): React.ReactElement | null {
282
- return null;
283
- }
284
- }
285
-
286
- const styles = StyleSheet.create({
287
- container: {
288
- // This z-index is copied from the Khan Academy webapp.
289
- //
290
- // TODO(mdr): Should we keep this in a constants file somewhere? Or
291
- // not hardcode it at all, and provide it to Wonder Blocks via
292
- // configuration?
293
- zIndex: 1080,
294
- },
295
- });
296
-
297
- export default withActionScheduler(ModalLauncher);
@@ -1,188 +0,0 @@
1
- import * as React from "react";
2
- import {PropsFor, View} from "@khanacademy/wonder-blocks-core";
3
- import type {StyleType} from "@khanacademy/wonder-blocks-core";
4
-
5
- import {
6
- ThemedStylesFn,
7
- useScopedTheme,
8
- useStyles,
9
- } from "@khanacademy/wonder-blocks-theming";
10
- import ModalContent from "./modal-content";
11
- import ModalHeader from "./modal-header";
12
- import ModalFooter from "./modal-footer";
13
- import CloseButton from "./close-button";
14
- import {
15
- ModalDialogThemeContext,
16
- ModalDialogThemeContract,
17
- } from "../themes/themed-modal-dialog";
18
-
19
- type Props = {
20
- /**
21
- * The main contents of the ModalPanel. All other parts of the panel
22
- * are positioned around it.
23
- */
24
- content:
25
- | React.ReactElement<PropsFor<typeof ModalContent>>
26
- | React.ReactNode;
27
- /**
28
- * The modal header to show at the top of the panel.
29
- */
30
- header?: React.ReactElement<PropsFor<typeof ModalHeader>> | React.ReactNode;
31
- /**
32
- * A footer to show beneath the contents.
33
- */
34
- footer?: React.ReactElement<PropsFor<typeof ModalFooter>> | React.ReactNode;
35
- /**
36
- * When true, the close button is shown; otherwise, the close button is not shown.
37
- */
38
- closeButtonVisible: boolean;
39
- /**
40
- * Should the contents of the panel become scrollable should they
41
- * become too tall?
42
- */
43
- scrollOverflow: boolean;
44
- /**
45
- * Whether to display the "light" version of this component instead, for
46
- * use when the item is used on a dark background.
47
- */
48
- light: boolean;
49
- /**
50
- * Any optional styling to apply to the panel.
51
- */
52
- style?: StyleType;
53
- /**
54
- * Called when the close button is clicked.
55
- *
56
- * If you're using `ModalLauncher`, you should not use this prop!
57
- * Instead, to listen for when the modal closes, add an `onClose` handler
58
- * to the `ModalLauncher`. Doing so will throw an error.
59
- */
60
- onClose?: () => unknown;
61
- /**
62
- * Test ID used for e2e testing.
63
- *
64
- * In this case, this `testId` comes from the `testId` prop defined in the
65
- * Dialog variant (e.g. OnePaneDialog).
66
- */
67
- testId?: string;
68
- };
69
-
70
- /**
71
- * ModalPanel is the content container.
72
- *
73
- * **Implementation notes:**
74
- *
75
- * If you are creating a custom Dialog, make sure to follow these guidelines:
76
- * - Make sure to add this component inside the [ModalDialog](/#modaldialog).
77
- * - If needed, you can also add a [ModalHeader](/#modalheader) using the
78
- * `header` prop. Same goes for [ModalFooter](/#modalfooter).
79
- * - If you need to create e2e tests, make sure to pass a `testId` prop. This
80
- * will be passed down to this component using a sufix: e.g.
81
- * `some-random-id-ModalPanel`. This scope will be propagated to the
82
- * CloseButton element as well: e.g. `some-random-id-CloseButton`.
83
- *
84
- * ```js
85
- * <ModalDialog>
86
- * <ModalPanel content={"custom content goes here"} />
87
- * </ModalDialog>
88
- * ```
89
- */
90
- export default function ModalPanel({
91
- closeButtonVisible = true,
92
- scrollOverflow = true,
93
- light = true,
94
- content,
95
- footer,
96
- header,
97
- onClose,
98
- style,
99
- testId,
100
- }: Props) {
101
- const {theme} = useScopedTheme(ModalDialogThemeContext);
102
- const styles = useStyles(themedStylesFn, theme);
103
-
104
- const renderMainContent = React.useCallback((): React.ReactNode => {
105
- const mainContent = ModalContent.isComponentOf(content) ? (
106
- (content as React.ReactElement<PropsFor<typeof ModalContent>>)
107
- ) : (
108
- <ModalContent>{content}</ModalContent>
109
- );
110
-
111
- if (!mainContent) {
112
- return mainContent;
113
- }
114
-
115
- return React.cloneElement(mainContent, {
116
- // Pass the scrollOverflow and header in to the main content
117
- scrollOverflow,
118
- // We override the styling of the main content to help position
119
- // it if there is a footer or close button being
120
- // shown. We have to do this here as the ModalContent doesn't
121
- // know about things being positioned around it.
122
- style: [!!footer && styles.hasFooter, mainContent.props.style],
123
- });
124
- }, [content, footer, scrollOverflow, styles.hasFooter]);
125
-
126
- const mainContent = renderMainContent();
127
-
128
- return (
129
- <View
130
- style={[styles.wrapper, !light && styles.dark, style]}
131
- testId={testId && `${testId}-panel`}
132
- >
133
- {closeButtonVisible && (
134
- <CloseButton
135
- light={!light}
136
- onClick={onClose}
137
- style={styles.closeButton}
138
- testId={testId && `${testId}-close`}
139
- />
140
- )}
141
- {header}
142
- {mainContent}
143
- {!footer || ModalFooter.isComponentOf(footer) ? (
144
- footer
145
- ) : (
146
- <ModalFooter>{footer}</ModalFooter>
147
- )}
148
- </View>
149
- );
150
- }
151
-
152
- ModalPanel.defaultProps = {
153
- closeButtonVisible: true,
154
- scrollOverflow: true,
155
- light: true,
156
- };
157
-
158
- const themedStylesFn: ThemedStylesFn<ModalDialogThemeContract> = (theme) => ({
159
- wrapper: {
160
- flex: "1 1 auto",
161
- position: "relative",
162
- display: "flex",
163
- flexDirection: "column",
164
- background: "white",
165
- boxSizing: "border-box",
166
- overflow: "hidden",
167
- height: "100%",
168
- width: "100%",
169
- },
170
-
171
- closeButton: {
172
- position: "absolute",
173
- right: theme.spacing.panel.closeButton,
174
- top: theme.spacing.panel.closeButton,
175
- // This is to allow the button to be tab-ordered before the modal
176
- // content but still be above the header and content.
177
- zIndex: 1,
178
- },
179
-
180
- dark: {
181
- background: theme.color.bg.inverse,
182
- color: theme.color.text.inverse,
183
- },
184
-
185
- hasFooter: {
186
- paddingBottom: theme.spacing.panel.footer,
187
- },
188
- });