@khanacademy/wonder-blocks-modal 3.0.6 → 3.0.8
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 +50 -0
- package/dist/components/close-button.d.ts +31 -0
- package/dist/components/close-button.js.flow +43 -0
- package/dist/components/focus-trap.d.ts +57 -0
- package/dist/components/focus-trap.js.flow +69 -0
- package/dist/components/modal-backdrop.d.ts +50 -0
- package/dist/components/modal-backdrop.js.flow +63 -0
- package/dist/components/modal-content.d.ts +23 -0
- package/dist/components/modal-content.js.flow +39 -0
- package/dist/components/modal-context.d.ts +6 -0
- package/dist/components/modal-context.js.flow +14 -0
- package/dist/components/modal-dialog.d.ts +60 -0
- package/dist/components/modal-dialog.js.flow +75 -0
- package/dist/components/modal-footer.d.ts +27 -0
- package/dist/components/modal-footer.js.flow +34 -0
- package/dist/components/modal-header.d.ts +93 -0
- package/dist/components/modal-header.js.flow +110 -0
- package/dist/components/modal-launcher.d.ts +17 -0
- package/dist/components/modal-launcher.js.flow +34 -0
- package/dist/components/modal-panel.d.ts +84 -0
- package/dist/components/modal-panel.js.flow +102 -0
- package/dist/components/one-pane-dialog.d.ts +124 -0
- package/dist/components/one-pane-dialog.js.flow +153 -0
- package/dist/components/scroll-disabler.d.ts +24 -0
- package/dist/components/scroll-disabler.js.flow +21 -0
- package/dist/es/index.js +222 -210
- package/dist/index.d.ts +8 -0
- package/dist/index.js +237 -226
- package/dist/index.js.flow +23 -2
- package/dist/util/constants.d.ts +5 -0
- package/dist/util/constants.js.flow +12 -0
- package/dist/util/find-focusable-nodes.d.ts +1 -0
- package/dist/util/find-focusable-nodes.js.flow +10 -0
- package/dist/util/maybe-get-portal-mounted-modal-host-element.d.ts +9 -0
- package/dist/util/maybe-get-portal-mounted-modal-host-element.js.flow +18 -0
- package/dist/util/types.d.ts +12 -0
- package/dist/util/types.js.flow +20 -0
- package/package.json +13 -13
- package/src/components/__tests__/{close-button.test.js → close-button.test.tsx} +3 -4
- package/src/components/__tests__/{focus-trap.test.js → focus-trap.test.tsx} +1 -2
- package/src/components/__tests__/{modal-backdrop.test.js → modal-backdrop.test.tsx} +2 -3
- package/src/components/__tests__/{modal-header.test.js → modal-header.test.tsx} +4 -3
- package/src/components/__tests__/{modal-launcher.test.js → modal-launcher.test.tsx} +13 -17
- package/src/components/__tests__/{modal-panel.test.js → modal-panel.test.tsx} +3 -4
- package/src/components/__tests__/{one-pane-dialog.test.js → one-pane-dialog.test.tsx} +1 -2
- package/src/components/{close-button.js → close-button.tsx} +8 -12
- package/src/components/{focus-trap.js → focus-trap.tsx} +12 -12
- package/src/components/{modal-backdrop.js → modal-backdrop.tsx} +23 -21
- package/src/components/{modal-content.js → modal-content.tsx} +11 -12
- package/src/components/modal-context.ts +13 -0
- package/src/components/{modal-dialog.js → modal-dialog.tsx} +15 -23
- package/src/components/{modal-footer.js → modal-footer.tsx} +5 -6
- package/src/components/{modal-header.js → modal-header.tsx} +20 -26
- package/src/components/{modal-launcher.js → modal-launcher.tsx} +43 -70
- package/src/components/{modal-panel.js → modal-panel.tsx} +31 -32
- package/src/components/{one-pane-dialog.js → one-pane-dialog.tsx} +40 -48
- package/src/components/{scroll-disabler.js → scroll-disabler.ts} +3 -4
- package/src/index.ts +17 -0
- package/src/util/{constants.js → constants.ts} +0 -2
- package/src/util/{find-focusable-nodes.js → find-focusable-nodes.ts} +0 -2
- package/src/util/{maybe-get-portal-mounted-modal-host-element.test.js → maybe-get-portal-mounted-modal-host-element.test.tsx} +8 -9
- package/src/util/{maybe-get-portal-mounted-modal-host-element.js → maybe-get-portal-mounted-modal-host-element.ts} +6 -5
- package/src/util/{types.js → types.ts} +3 -2
- package/tsconfig.json +20 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/src/components/__docs__/modal-dialog.stories.js +0 -308
- package/src/components/__docs__/modal-footer.stories.js +0 -337
- package/src/components/__docs__/modal-header.argtypes.js +0 -76
- package/src/components/__docs__/modal-header.stories.js +0 -294
- package/src/components/__docs__/modal-launcher.argtypes.js +0 -78
- package/src/components/__docs__/modal-launcher.stories.js +0 -513
- package/src/components/__docs__/modal-panel.stories.js +0 -414
- package/src/components/__docs__/one-pane-dialog.argtypes.js +0 -108
- package/src/components/__docs__/one-pane-dialog.stories.js +0 -582
- package/src/components/modal-context.js +0 -14
- package/src/index.js +0 -18
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// @flow
|
|
2
1
|
import * as React from "react";
|
|
3
2
|
import {StyleSheet} from "aphrodite";
|
|
4
3
|
import {View} from "@khanacademy/wonder-blocks-core";
|
|
@@ -7,18 +6,18 @@ import Spacing from "@khanacademy/wonder-blocks-spacing";
|
|
|
7
6
|
|
|
8
7
|
import type {StyleType} from "@khanacademy/wonder-blocks-core";
|
|
9
8
|
|
|
10
|
-
type Props = {
|
|
9
|
+
type Props = {
|
|
11
10
|
/** Should the content scroll on overflow, or just expand. */
|
|
12
|
-
scrollOverflow: boolean
|
|
11
|
+
scrollOverflow: boolean;
|
|
13
12
|
/** The contents of the ModalContent */
|
|
14
|
-
children: React.
|
|
13
|
+
children: React.ReactNode;
|
|
15
14
|
/** Optional styling to apply to the contents. */
|
|
16
|
-
style?: StyleType
|
|
17
|
-
|
|
15
|
+
style?: StyleType;
|
|
16
|
+
};
|
|
18
17
|
|
|
19
|
-
type DefaultProps = {
|
|
20
|
-
scrollOverflow:
|
|
21
|
-
|
|
18
|
+
type DefaultProps = {
|
|
19
|
+
scrollOverflow: Props["scrollOverflow"];
|
|
20
|
+
};
|
|
22
21
|
|
|
23
22
|
/**
|
|
24
23
|
* The Modal content included after the header
|
|
@@ -31,9 +30,9 @@ export default class ModalContent extends React.Component<Props> {
|
|
|
31
30
|
scrollOverflow: true,
|
|
32
31
|
};
|
|
33
32
|
|
|
34
|
-
static __IS_MODAL_CONTENT__
|
|
33
|
+
static __IS_MODAL_CONTENT__ = true;
|
|
35
34
|
|
|
36
|
-
render(): React.
|
|
35
|
+
render(): React.ReactElement {
|
|
37
36
|
const {scrollOverflow, style, children} = this.props;
|
|
38
37
|
|
|
39
38
|
return (
|
|
@@ -80,4 +79,4 @@ const styleSheets = {
|
|
|
80
79
|
padding: `${Spacing.xLarge_32}px ${Spacing.medium_16}px`,
|
|
81
80
|
},
|
|
82
81
|
}),
|
|
83
|
-
};
|
|
82
|
+
} as const;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
type ContextType = {
|
|
4
|
+
closeModal?: () => unknown;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
const defaultContext: ContextType = {
|
|
8
|
+
closeModal: undefined,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export default React.createContext<ContextType>(
|
|
12
|
+
defaultContext,
|
|
13
|
+
) as React.Context<ContextType>;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// @flow
|
|
2
1
|
import * as React from "react";
|
|
3
2
|
import {StyleSheet} from "aphrodite";
|
|
4
3
|
import {
|
|
@@ -11,57 +10,50 @@ import type {StyleType} from "@khanacademy/wonder-blocks-core";
|
|
|
11
10
|
import type {MediaLayoutContextValue} from "@khanacademy/wonder-blocks-layout";
|
|
12
11
|
import Spacing from "@khanacademy/wonder-blocks-spacing";
|
|
13
12
|
|
|
14
|
-
type Props = {
|
|
13
|
+
type Props = {
|
|
15
14
|
/**
|
|
16
15
|
* The dialog content
|
|
17
16
|
*/
|
|
18
|
-
children: React.
|
|
19
|
-
|
|
17
|
+
children: React.ReactNode;
|
|
20
18
|
/**
|
|
21
19
|
* When set, provides a component that can render content above the top of the modal;
|
|
22
20
|
* when not set, no additional content is shown above the modal.
|
|
23
21
|
* This prop is passed down to the ModalDialog.
|
|
24
22
|
*/
|
|
25
|
-
above?: React.
|
|
26
|
-
|
|
23
|
+
above?: React.ReactNode;
|
|
27
24
|
/**
|
|
28
25
|
* When set, provides a component that will render content below the bottom of the modal;
|
|
29
26
|
* when not set, no additional content is shown below the modal.
|
|
30
27
|
* This prop is passed down to the ModalDialog.
|
|
31
28
|
*/
|
|
32
|
-
below?: React.
|
|
33
|
-
|
|
29
|
+
below?: React.ReactNode;
|
|
34
30
|
/**
|
|
35
31
|
* When set, overrides the default role value. Default role is "dialog"
|
|
36
32
|
* Roles other than dialog and alertdialog aren't appropriate for this
|
|
37
33
|
* component
|
|
38
34
|
*/
|
|
39
|
-
role?: "dialog" | "alertdialog"
|
|
40
|
-
|
|
35
|
+
role?: "dialog" | "alertdialog";
|
|
41
36
|
/**
|
|
42
37
|
* Custom styles
|
|
43
38
|
*/
|
|
44
|
-
style?: StyleType
|
|
45
|
-
|
|
39
|
+
style?: StyleType;
|
|
46
40
|
/**
|
|
47
41
|
* Test ID used for e2e testing.
|
|
48
42
|
*/
|
|
49
|
-
testId?: string
|
|
50
|
-
|
|
43
|
+
testId?: string;
|
|
51
44
|
/**
|
|
52
45
|
* The ID of the content labelling this dialog, if applicable.
|
|
53
46
|
*/
|
|
54
|
-
"aria-labelledby"?: string
|
|
55
|
-
|
|
47
|
+
["aria-labelledby"]?: string;
|
|
56
48
|
/**
|
|
57
49
|
* The ID of the content describing this dialog, if applicable.
|
|
58
50
|
*/
|
|
59
|
-
"aria-describedby"?: string
|
|
60
|
-
|
|
51
|
+
["aria-describedby"]?: string;
|
|
52
|
+
};
|
|
61
53
|
|
|
62
|
-
type DefaultProps = {
|
|
63
|
-
role:
|
|
64
|
-
|
|
54
|
+
type DefaultProps = {
|
|
55
|
+
role: Props["role"];
|
|
56
|
+
};
|
|
65
57
|
|
|
66
58
|
/**
|
|
67
59
|
* `ModalDialog` is a component that contains these elements:
|
|
@@ -78,7 +70,7 @@ export default class ModalDialog extends React.Component<Props> {
|
|
|
78
70
|
role: "dialog",
|
|
79
71
|
};
|
|
80
72
|
|
|
81
|
-
render(): React.
|
|
73
|
+
render(): React.ReactElement {
|
|
82
74
|
const {
|
|
83
75
|
above,
|
|
84
76
|
below,
|
|
@@ -171,4 +163,4 @@ const styleSheets = {
|
|
|
171
163
|
flexDirection: "column",
|
|
172
164
|
},
|
|
173
165
|
}),
|
|
174
|
-
};
|
|
166
|
+
} as const;
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
// @flow
|
|
2
1
|
import * as React from "react";
|
|
3
2
|
import {StyleSheet} from "aphrodite";
|
|
4
3
|
import Color from "@khanacademy/wonder-blocks-color";
|
|
5
4
|
import {View} from "@khanacademy/wonder-blocks-core";
|
|
6
5
|
import Spacing from "@khanacademy/wonder-blocks-spacing";
|
|
7
6
|
|
|
8
|
-
type Props = {
|
|
9
|
-
children: React.
|
|
10
|
-
|
|
7
|
+
type Props = {
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
};
|
|
11
10
|
|
|
12
11
|
/**
|
|
13
12
|
* Modal footer included after the content.
|
|
@@ -30,9 +29,9 @@ export default class ModalFooter extends React.Component<Props> {
|
|
|
30
29
|
static isClassOf(instance: any): boolean {
|
|
31
30
|
return instance && instance.type && instance.type.__IS_MODAL_FOOTER__;
|
|
32
31
|
}
|
|
33
|
-
static __IS_MODAL_FOOTER__
|
|
32
|
+
static __IS_MODAL_FOOTER__ = true;
|
|
34
33
|
|
|
35
|
-
render(): React.
|
|
34
|
+
render(): React.ReactElement {
|
|
36
35
|
const {children} = this.props;
|
|
37
36
|
return <View style={styles.footer}>{children}</View>;
|
|
38
37
|
}
|
|
@@ -1,30 +1,26 @@
|
|
|
1
|
-
// @flow
|
|
2
1
|
import * as React from "react";
|
|
3
2
|
import {StyleSheet} from "aphrodite";
|
|
4
|
-
import {
|
|
3
|
+
import {Breadcrumbs} from "@khanacademy/wonder-blocks-breadcrumbs";
|
|
5
4
|
import Color from "@khanacademy/wonder-blocks-color";
|
|
6
5
|
import {View} from "@khanacademy/wonder-blocks-core";
|
|
7
6
|
import {MediaLayout} from "@khanacademy/wonder-blocks-layout";
|
|
8
7
|
import Spacing from "@khanacademy/wonder-blocks-spacing";
|
|
9
8
|
import {HeadingMedium, LabelSmall} from "@khanacademy/wonder-blocks-typography";
|
|
10
9
|
|
|
11
|
-
type Common = {
|
|
10
|
+
type Common = {
|
|
12
11
|
/**
|
|
13
12
|
* The main title rendered in larger bold text.
|
|
14
13
|
*/
|
|
15
|
-
title: string
|
|
16
|
-
|
|
14
|
+
title: string;
|
|
17
15
|
/**
|
|
18
16
|
* Whether to display the "light" version of this component instead, for
|
|
19
17
|
* use when the item is used on a dark background.
|
|
20
18
|
*/
|
|
21
|
-
light: boolean
|
|
22
|
-
|
|
19
|
+
light: boolean;
|
|
23
20
|
/**
|
|
24
21
|
* An id to provide a selector for the title element.
|
|
25
22
|
*/
|
|
26
|
-
titleId: string
|
|
27
|
-
|
|
23
|
+
titleId: string;
|
|
28
24
|
/**
|
|
29
25
|
* Test ID used for e2e testing.
|
|
30
26
|
*
|
|
@@ -36,32 +32,28 @@ type Common = {|
|
|
|
36
32
|
* For testId="some-random-id"
|
|
37
33
|
* The result will be: `some-random-id-modal-header`
|
|
38
34
|
*/
|
|
39
|
-
testId?: string
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
type WithSubtitle = {|
|
|
43
|
-
...Common,
|
|
35
|
+
testId?: string;
|
|
36
|
+
};
|
|
44
37
|
|
|
38
|
+
type WithSubtitle = Common & {
|
|
45
39
|
/**
|
|
46
40
|
* The dialog subtitle.
|
|
47
41
|
*/
|
|
48
|
-
subtitle: string
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
type WithBreadcrumbs = {|
|
|
52
|
-
...Common,
|
|
42
|
+
subtitle: string;
|
|
43
|
+
};
|
|
53
44
|
|
|
45
|
+
type WithBreadcrumbs = Common & {
|
|
54
46
|
/**
|
|
55
47
|
* Adds a breadcrumb-trail, appearing in the ModalHeader, above the title.
|
|
56
48
|
*/
|
|
57
|
-
breadcrumbs: React.
|
|
58
|
-
|
|
49
|
+
breadcrumbs: React.ReactElement<React.ComponentProps<typeof Breadcrumbs>>;
|
|
50
|
+
};
|
|
59
51
|
|
|
60
52
|
type Props = Common | WithSubtitle | WithBreadcrumbs;
|
|
61
53
|
|
|
62
|
-
type DefaultProps = {
|
|
63
|
-
light:
|
|
64
|
-
|
|
54
|
+
type DefaultProps = {
|
|
55
|
+
light: Props["light"];
|
|
56
|
+
};
|
|
65
57
|
|
|
66
58
|
/**
|
|
67
59
|
* This is a helper component that is never rendered by itself. It is always
|
|
@@ -111,10 +103,12 @@ export default class ModalHeader extends React.Component<Props> {
|
|
|
111
103
|
light: true,
|
|
112
104
|
};
|
|
113
105
|
|
|
114
|
-
render(): React.
|
|
106
|
+
render(): React.ReactElement {
|
|
115
107
|
const {
|
|
108
|
+
// @ts-expect-error [FEI-5019] - TS2339 - Property 'breadcrumbs' does not exist on type 'Readonly<Props> & Readonly<{ children?: ReactNode; }>'.
|
|
116
109
|
breadcrumbs = undefined,
|
|
117
110
|
light,
|
|
111
|
+
// @ts-expect-error [FEI-5019] - TS2339 - Property 'subtitle' does not exist on type 'Readonly<Props> & Readonly<{ children?: ReactNode; }>'.
|
|
118
112
|
subtitle = undefined,
|
|
119
113
|
testId,
|
|
120
114
|
title,
|
|
@@ -204,4 +198,4 @@ const styleSheets = {
|
|
|
204
198
|
paddingRight: Spacing.xLarge_32,
|
|
205
199
|
},
|
|
206
200
|
}),
|
|
207
|
-
};
|
|
201
|
+
} as const;
|
|
@@ -1,21 +1,18 @@
|
|
|
1
|
-
// @flow
|
|
2
1
|
import * as React from "react";
|
|
3
2
|
import * as ReactDOM from "react-dom";
|
|
4
3
|
import {StyleSheet} from "aphrodite";
|
|
5
4
|
|
|
6
5
|
import {withActionScheduler} from "@khanacademy/wonder-blocks-timing";
|
|
7
|
-
import type {
|
|
8
|
-
WithActionSchedulerProps,
|
|
9
|
-
WithoutActionScheduler,
|
|
10
|
-
} from "@khanacademy/wonder-blocks-timing";
|
|
6
|
+
import type {WithActionSchedulerProps} from "@khanacademy/wonder-blocks-timing";
|
|
11
7
|
|
|
12
|
-
import FocusTrap from "./focus-trap
|
|
13
|
-
import ModalBackdrop from "./modal-backdrop
|
|
14
|
-
import ScrollDisabler from "./scroll-disabler
|
|
15
|
-
import type {ModalElement} from "../util/types
|
|
16
|
-
import ModalContext from "./modal-context
|
|
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";
|
|
17
13
|
|
|
18
|
-
|
|
14
|
+
// TODO(FEI-5000): Convert back to conditional props after TS migration is complete.
|
|
15
|
+
type Props = {
|
|
19
16
|
/**
|
|
20
17
|
* The modal to render.
|
|
21
18
|
*
|
|
@@ -29,43 +26,26 @@ type CommonProps = {|
|
|
|
29
26
|
* Note: Don't call `closeModal` while rendering! It should be used to
|
|
30
27
|
* respond to user intearction, like `onClick`.
|
|
31
28
|
*/
|
|
32
|
-
modal: ModalElement |
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* If the parent needs to be notified when the modal is closed, use this
|
|
36
|
-
* prop. You probably want to use this instead of `onClose` on the modals
|
|
37
|
-
* themselves, since this will capture a more complete set of close events.
|
|
38
|
-
*/
|
|
39
|
-
onClose?: () => mixed,
|
|
40
|
-
|
|
29
|
+
modal: ModalElement | React.FC<{closeModal: () => void}>;
|
|
41
30
|
/**
|
|
42
31
|
* Enables the backdrop to dismiss the modal on click/tap
|
|
43
32
|
*/
|
|
44
|
-
backdropDismissEnabled?: boolean
|
|
45
|
-
|
|
33
|
+
backdropDismissEnabled?: boolean;
|
|
46
34
|
/**
|
|
47
35
|
* The selector for the element that will be focused when the dialog shows.
|
|
48
36
|
* When not set, the first tabbable element within the dialog will be used.
|
|
49
37
|
*/
|
|
50
|
-
initialFocusId?: string
|
|
51
|
-
|
|
38
|
+
initialFocusId?: string;
|
|
52
39
|
/**
|
|
53
40
|
* The selector for the element that will be focused after the dialog
|
|
54
41
|
* closes. When not set, the last element focused outside the modal will
|
|
55
42
|
* be used if it exists.
|
|
56
43
|
*/
|
|
57
|
-
closedFocusId?: string
|
|
58
|
-
|
|
44
|
+
closedFocusId?: string;
|
|
59
45
|
/**
|
|
60
46
|
* Test ID used for e2e testing. It's set on the ModalBackdrop
|
|
61
47
|
*/
|
|
62
|
-
testId?: string
|
|
63
|
-
|
|
64
|
-
...WithActionSchedulerProps,
|
|
65
|
-
|};
|
|
66
|
-
|
|
67
|
-
type ControlledProps = {|
|
|
68
|
-
...CommonProps,
|
|
48
|
+
testId?: string;
|
|
69
49
|
/**
|
|
70
50
|
* Renders the modal when true, renders nothing when false.
|
|
71
51
|
*
|
|
@@ -75,33 +55,34 @@ type ControlledProps = {|
|
|
|
75
55
|
* should never be used with this prop. Not doing so will result in an
|
|
76
56
|
* error being thrown.
|
|
77
57
|
*/
|
|
78
|
-
opened
|
|
79
|
-
|
|
58
|
+
opened?: boolean;
|
|
80
59
|
/**
|
|
60
|
+
* If the parent needs to be notified when the modal is closed, use this
|
|
61
|
+
* prop. You probably want to use this instead of `onClose` on the modals
|
|
62
|
+
* themselves, since this will capture a more complete set of close events.
|
|
63
|
+
*
|
|
81
64
|
* Called when the modal needs to notify the parent component that it should
|
|
82
65
|
* be closed.
|
|
83
66
|
*
|
|
84
67
|
* This prop must be used when the component is being used as a controlled
|
|
85
68
|
* component.
|
|
86
69
|
*/
|
|
87
|
-
onClose
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
type UncontrolledProps = {|
|
|
95
|
-
...CommonProps,
|
|
96
|
-
children: ({|openModal: () => mixed|}) => React.Node,
|
|
97
|
-
|};
|
|
70
|
+
onClose?: () => unknown;
|
|
71
|
+
/**
|
|
72
|
+
* WARNING: This props should only be used when using the component as a
|
|
73
|
+
* controlled component.
|
|
74
|
+
*/
|
|
75
|
+
children?: (arg1: {openModal: () => unknown}) => React.ReactNode;
|
|
76
|
+
} & WithActionSchedulerProps;
|
|
98
77
|
|
|
99
|
-
type
|
|
78
|
+
type DefaultProps = {
|
|
79
|
+
backdropDismissEnabled: Props["backdropDismissEnabled"];
|
|
80
|
+
};
|
|
100
81
|
|
|
101
|
-
type State = {
|
|
82
|
+
type State = {
|
|
102
83
|
/** Whether the modal should currently be open. */
|
|
103
|
-
opened: boolean
|
|
104
|
-
|
|
84
|
+
opened: boolean;
|
|
85
|
+
};
|
|
105
86
|
|
|
106
87
|
/**
|
|
107
88
|
* This component enables you to launch a modal, covering the screen.
|
|
@@ -124,7 +105,7 @@ class ModalLauncher extends React.Component<Props, State> {
|
|
|
124
105
|
* The most recent element _outside this component_ that received focus.
|
|
125
106
|
* Be default, it captures the element that triggered the modal opening
|
|
126
107
|
*/
|
|
127
|
-
lastElementFocusedOutsideModal:
|
|
108
|
+
lastElementFocusedOutsideModal: HTMLElement | null | undefined;
|
|
128
109
|
|
|
129
110
|
static defaultProps: DefaultProps = {
|
|
130
111
|
backdropDismissEnabled: true,
|
|
@@ -133,7 +114,7 @@ class ModalLauncher extends React.Component<Props, State> {
|
|
|
133
114
|
static getDerivedStateFromProps(
|
|
134
115
|
props: Props,
|
|
135
116
|
state: State,
|
|
136
|
-
):
|
|
117
|
+
): Partial<State> {
|
|
137
118
|
if (typeof props.opened === "boolean" && props.children) {
|
|
138
119
|
// eslint-disable-next-line no-console
|
|
139
120
|
console.warn("'children' and 'opened' can't be used together");
|
|
@@ -163,6 +144,7 @@ class ModalLauncher extends React.Component<Props, State> {
|
|
|
163
144
|
|
|
164
145
|
_saveLastElementFocused: () => void = () => {
|
|
165
146
|
// keep a reference of the element that triggers the modal
|
|
147
|
+
// @ts-expect-error [FEI-5019] - TS2322 - Type 'Element | null' is not assignable to type 'HTMLElement | null | undefined'.
|
|
166
148
|
this.lastElementFocusedOutsideModal = document.activeElement;
|
|
167
149
|
};
|
|
168
150
|
|
|
@@ -177,9 +159,9 @@ class ModalLauncher extends React.Component<Props, State> {
|
|
|
177
159
|
|
|
178
160
|
// Focus on the specified element after closing the modal.
|
|
179
161
|
if (closedFocusId) {
|
|
180
|
-
const focusElement =
|
|
162
|
+
const focusElement = ReactDOM.findDOMNode(
|
|
181
163
|
document.getElementById(closedFocusId),
|
|
182
|
-
)
|
|
164
|
+
) as any;
|
|
183
165
|
|
|
184
166
|
if (focusElement) {
|
|
185
167
|
// Wait for the modal to leave the DOM before trying
|
|
@@ -204,7 +186,7 @@ class ModalLauncher extends React.Component<Props, State> {
|
|
|
204
186
|
this.setState({opened: false}, () => {
|
|
205
187
|
const {onClose} = this.props;
|
|
206
188
|
|
|
207
|
-
onClose
|
|
189
|
+
onClose?.();
|
|
208
190
|
this._returnFocus();
|
|
209
191
|
});
|
|
210
192
|
};
|
|
@@ -219,7 +201,7 @@ class ModalLauncher extends React.Component<Props, State> {
|
|
|
219
201
|
}
|
|
220
202
|
}
|
|
221
203
|
|
|
222
|
-
render(): React.
|
|
204
|
+
render(): React.ReactElement | null {
|
|
223
205
|
const renderedChildren = this.props.children
|
|
224
206
|
? this.props.children({
|
|
225
207
|
openModal: this._openModal,
|
|
@@ -232,9 +214,6 @@ class ModalLauncher extends React.Component<Props, State> {
|
|
|
232
214
|
}
|
|
233
215
|
|
|
234
216
|
return (
|
|
235
|
-
// This flow check is valid, it's the babel plugin which is broken,
|
|
236
|
-
// see modal-context.js for details.
|
|
237
|
-
// $FlowFixMe
|
|
238
217
|
<ModalContext.Provider value={{closeModal: this.handleCloseModal}}>
|
|
239
218
|
{renderedChildren}
|
|
240
219
|
{this.state.opened &&
|
|
@@ -268,9 +247,9 @@ class ModalLauncher extends React.Component<Props, State> {
|
|
|
268
247
|
}
|
|
269
248
|
|
|
270
249
|
/** A component that, when mounted, calls `onClose` when Escape is pressed. */
|
|
271
|
-
class ModalLauncherKeypressListener extends React.Component<{
|
|
272
|
-
onClose: () =>
|
|
273
|
-
|
|
250
|
+
class ModalLauncherKeypressListener extends React.Component<{
|
|
251
|
+
onClose: () => unknown;
|
|
252
|
+
}> {
|
|
274
253
|
componentDidMount() {
|
|
275
254
|
window.addEventListener("keyup", this._handleKeyup);
|
|
276
255
|
}
|
|
@@ -296,7 +275,7 @@ class ModalLauncherKeypressListener extends React.Component<{|
|
|
|
296
275
|
}
|
|
297
276
|
};
|
|
298
277
|
|
|
299
|
-
render(): React.
|
|
278
|
+
render(): React.ReactElement | null {
|
|
300
279
|
return null;
|
|
301
280
|
}
|
|
302
281
|
}
|
|
@@ -312,10 +291,4 @@ const styles = StyleSheet.create({
|
|
|
312
291
|
},
|
|
313
292
|
});
|
|
314
293
|
|
|
315
|
-
|
|
316
|
-
React.ElementConfig<typeof ModalLauncher>,
|
|
317
|
-
>;
|
|
318
|
-
|
|
319
|
-
export default (withActionScheduler(
|
|
320
|
-
ModalLauncher,
|
|
321
|
-
): React.ComponentType<ExportProps>);
|
|
294
|
+
export default withActionScheduler(ModalLauncher);
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// @flow
|
|
2
1
|
import * as React from "react";
|
|
3
2
|
import {StyleSheet} from "aphrodite";
|
|
4
3
|
import Color from "@khanacademy/wonder-blocks-color";
|
|
@@ -6,50 +5,49 @@ import {View} from "@khanacademy/wonder-blocks-core";
|
|
|
6
5
|
import Spacing from "@khanacademy/wonder-blocks-spacing";
|
|
7
6
|
import type {StyleType} from "@khanacademy/wonder-blocks-core";
|
|
8
7
|
|
|
9
|
-
import ModalContent from "./modal-content
|
|
10
|
-
import ModalHeader from "./modal-header
|
|
11
|
-
import ModalFooter from "./modal-footer
|
|
12
|
-
import CloseButton from "./close-button
|
|
8
|
+
import ModalContent from "./modal-content";
|
|
9
|
+
import ModalHeader from "./modal-header";
|
|
10
|
+
import ModalFooter from "./modal-footer";
|
|
11
|
+
import CloseButton from "./close-button";
|
|
13
12
|
|
|
14
|
-
type Props = {
|
|
13
|
+
type Props = {
|
|
15
14
|
/**
|
|
16
15
|
* The main contents of the ModalPanel. All other parts of the panel
|
|
17
16
|
* are positioned around it.
|
|
18
17
|
*/
|
|
19
|
-
content:
|
|
20
|
-
|
|
18
|
+
content:
|
|
19
|
+
| React.ReactElement<React.ComponentProps<typeof ModalContent>>
|
|
20
|
+
| React.ReactNode;
|
|
21
21
|
/**
|
|
22
22
|
* The modal header to show at the top of the panel.
|
|
23
23
|
*/
|
|
24
|
-
header?:
|
|
25
|
-
|
|
24
|
+
header?:
|
|
25
|
+
| React.ReactElement<React.ComponentProps<typeof ModalHeader>>
|
|
26
|
+
| React.ReactNode;
|
|
26
27
|
/**
|
|
27
28
|
* A footer to show beneath the contents.
|
|
28
29
|
*/
|
|
29
|
-
footer?:
|
|
30
|
-
|
|
30
|
+
footer?:
|
|
31
|
+
| React.ReactElement<React.ComponentProps<typeof ModalFooter>>
|
|
32
|
+
| React.ReactNode;
|
|
31
33
|
/**
|
|
32
34
|
* When true, the close button is shown; otherwise, the close button is not shown.
|
|
33
35
|
*/
|
|
34
|
-
closeButtonVisible: boolean
|
|
35
|
-
|
|
36
|
+
closeButtonVisible: boolean;
|
|
36
37
|
/**
|
|
37
38
|
* Should the contents of the panel become scrollable should they
|
|
38
39
|
* become too tall?
|
|
39
40
|
*/
|
|
40
|
-
scrollOverflow: boolean
|
|
41
|
-
|
|
41
|
+
scrollOverflow: boolean;
|
|
42
42
|
/**
|
|
43
43
|
* Whether to display the "light" version of this component instead, for
|
|
44
44
|
* use when the item is used on a dark background.
|
|
45
45
|
*/
|
|
46
|
-
light: boolean
|
|
47
|
-
|
|
46
|
+
light: boolean;
|
|
48
47
|
/**
|
|
49
48
|
* Any optional styling to apply to the panel.
|
|
50
49
|
*/
|
|
51
|
-
style?: StyleType
|
|
52
|
-
|
|
50
|
+
style?: StyleType;
|
|
53
51
|
/**
|
|
54
52
|
* Called when the close button is clicked.
|
|
55
53
|
*
|
|
@@ -57,22 +55,21 @@ type Props = {|
|
|
|
57
55
|
* Instead, to listen for when the modal closes, add an `onClose` handler
|
|
58
56
|
* to the `ModalLauncher`. Doing so will throw an error.
|
|
59
57
|
*/
|
|
60
|
-
onClose?: () =>
|
|
61
|
-
|
|
58
|
+
onClose?: () => unknown;
|
|
62
59
|
/**
|
|
63
60
|
* Test ID used for e2e testing.
|
|
64
61
|
*
|
|
65
62
|
* In this case, this `testId` comes from the `testId` prop defined in the
|
|
66
63
|
* Dialog variant (e.g. OnePaneDialog).
|
|
67
64
|
*/
|
|
68
|
-
testId?: string
|
|
69
|
-
|
|
65
|
+
testId?: string;
|
|
66
|
+
};
|
|
70
67
|
|
|
71
|
-
type DefaultProps = {
|
|
72
|
-
closeButtonVisible:
|
|
73
|
-
scrollOverflow:
|
|
74
|
-
light:
|
|
75
|
-
|
|
68
|
+
type DefaultProps = {
|
|
69
|
+
closeButtonVisible: Props["closeButtonVisible"];
|
|
70
|
+
scrollOverflow: Props["scrollOverflow"];
|
|
71
|
+
light: Props["light"];
|
|
72
|
+
};
|
|
76
73
|
|
|
77
74
|
/**
|
|
78
75
|
* ModalPanel is the content container.
|
|
@@ -101,11 +98,13 @@ export default class ModalPanel extends React.Component<Props> {
|
|
|
101
98
|
light: true,
|
|
102
99
|
};
|
|
103
100
|
|
|
104
|
-
renderMainContent(): React.
|
|
101
|
+
renderMainContent(): React.ReactNode {
|
|
105
102
|
const {content, footer, scrollOverflow} = this.props;
|
|
106
103
|
|
|
107
104
|
const mainContent = ModalContent.isClassOf(content) ? (
|
|
108
|
-
(
|
|
105
|
+
(content as React.ReactElement<
|
|
106
|
+
React.ComponentProps<typeof ModalContent>
|
|
107
|
+
>)
|
|
109
108
|
) : (
|
|
110
109
|
<ModalContent>{content}</ModalContent>
|
|
111
110
|
);
|
|
@@ -125,7 +124,7 @@ export default class ModalPanel extends React.Component<Props> {
|
|
|
125
124
|
});
|
|
126
125
|
}
|
|
127
126
|
|
|
128
|
-
render(): React.
|
|
127
|
+
render(): React.ReactElement {
|
|
129
128
|
const {
|
|
130
129
|
closeButtonVisible,
|
|
131
130
|
footer,
|