@khanacademy/wonder-blocks-modal 4.0.39 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/components/modal-dialog.d.ts +3 -19
- package/dist/es/index.js +113 -96
- package/dist/index.js +128 -111
- package/dist/themes/default.d.ts +11 -0
- package/dist/themes/themed-modal-dialog.d.ts +26 -0
- package/package.json +2 -1
- package/src/components/__tests__/modal-dialog.test.tsx +78 -0
- package/src/components/modal-dialog.tsx +103 -103
- package/src/themes/default.ts +14 -0
- package/src/themes/themed-modal-dialog.tsx +43 -0
- package/tsconfig-build.json +1 -0
- package/tsconfig-build.tsbuildinfo +1 -1
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import {StyleSheet} from "aphrodite";
|
|
3
|
-
import {
|
|
4
|
-
MediaLayout,
|
|
5
|
-
MediaLayoutContext,
|
|
6
|
-
MEDIA_MODAL_SPEC,
|
|
7
|
-
} from "@khanacademy/wonder-blocks-layout";
|
|
8
2
|
import {View} from "@khanacademy/wonder-blocks-core";
|
|
9
3
|
import type {StyleType} from "@khanacademy/wonder-blocks-core";
|
|
10
|
-
|
|
11
|
-
import
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
ThemedStylesFn,
|
|
7
|
+
useScopedTheme,
|
|
8
|
+
useStyles,
|
|
9
|
+
} from "@khanacademy/wonder-blocks-theming";
|
|
10
|
+
import ThemeModalDialog, {
|
|
11
|
+
ModalDialogThemeContext,
|
|
12
|
+
ModalDialogThemeContract,
|
|
13
|
+
} from "../themes/themed-modal-dialog";
|
|
12
14
|
|
|
13
15
|
type Props = {
|
|
14
16
|
/**
|
|
@@ -42,7 +44,7 @@ type Props = {
|
|
|
42
44
|
*/
|
|
43
45
|
testId?: string;
|
|
44
46
|
/**
|
|
45
|
-
* The ID of the
|
|
47
|
+
* The ID of the title labelling this dialog, if applicable.
|
|
46
48
|
*/
|
|
47
49
|
"aria-labelledby"?: string;
|
|
48
50
|
/**
|
|
@@ -51,10 +53,6 @@ type Props = {
|
|
|
51
53
|
"aria-describedby"?: string;
|
|
52
54
|
};
|
|
53
55
|
|
|
54
|
-
type DefaultProps = {
|
|
55
|
-
role: Props["role"];
|
|
56
|
-
};
|
|
57
|
-
|
|
58
56
|
/**
|
|
59
57
|
* `ModalDialog` is a component that contains these elements:
|
|
60
58
|
* - The visual dialog element itself (`<div role="dialog"/>`)
|
|
@@ -65,102 +63,104 @@ type DefaultProps = {
|
|
|
65
63
|
* - If there is a custom Dialog implementation (e.g. `TwoPaneDialog`), the dialog element doesn’t have to have
|
|
66
64
|
* the `aria-labelledby` attribute however this is recommended. It should match the `id` of the dialog title.
|
|
67
65
|
*/
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
66
|
+
const ModalDialogCore = React.forwardRef(function ModalDialogCore(
|
|
67
|
+
props: Props,
|
|
68
|
+
ref: React.ForwardedRef<HTMLDivElement>,
|
|
69
|
+
) {
|
|
70
|
+
const {
|
|
71
|
+
above,
|
|
72
|
+
below,
|
|
73
|
+
role = "dialog",
|
|
74
|
+
style,
|
|
75
|
+
children,
|
|
76
|
+
testId,
|
|
77
|
+
/* eslint-disable react/prop-types */
|
|
78
|
+
// the react/prop-types plugin does not like these
|
|
79
|
+
"aria-labelledby": ariaLabelledBy,
|
|
80
|
+
"aria-describedby": ariaDescribedBy,
|
|
81
|
+
/* eslint-enable react/prop-types */
|
|
82
|
+
} = props;
|
|
72
83
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
above,
|
|
76
|
-
below,
|
|
77
|
-
role,
|
|
78
|
-
style,
|
|
79
|
-
children,
|
|
80
|
-
testId,
|
|
81
|
-
/* eslint-disable react/prop-types */
|
|
82
|
-
// the react/prop-types plugin does not like these
|
|
83
|
-
"aria-labelledby": ariaLabelledBy,
|
|
84
|
-
"aria-describedby": ariaDescribedBy,
|
|
85
|
-
/* eslint-enable react/prop-types */
|
|
86
|
-
} = this.props;
|
|
84
|
+
const {theme} = useScopedTheme(ModalDialogThemeContext);
|
|
85
|
+
const styles = useStyles(themedStylesFn, theme);
|
|
87
86
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
87
|
+
return (
|
|
88
|
+
<View style={[styles.wrapper, style]}>
|
|
89
|
+
{below && <View style={styles.below}>{below}</View>}
|
|
90
|
+
<View
|
|
91
|
+
role={role}
|
|
92
|
+
aria-modal="true"
|
|
93
|
+
aria-labelledby={ariaLabelledBy}
|
|
94
|
+
aria-describedby={ariaDescribedBy}
|
|
95
|
+
ref={ref}
|
|
96
|
+
style={styles.dialog}
|
|
97
|
+
testId={testId}
|
|
98
|
+
>
|
|
99
|
+
{children}
|
|
100
|
+
</View>
|
|
101
|
+
{above && <View style={styles.above}>{above}</View>}
|
|
102
|
+
</View>
|
|
103
|
+
);
|
|
104
|
+
});
|
|
92
105
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
aria-describedby={ariaDescribedBy}
|
|
104
|
-
style={styles.dialog}
|
|
105
|
-
testId={testId}
|
|
106
|
-
>
|
|
107
|
-
{children}
|
|
108
|
-
</View>
|
|
109
|
-
{above && <View style={styles.above}>{above}</View>}
|
|
110
|
-
</View>
|
|
111
|
-
)}
|
|
112
|
-
</MediaLayout>
|
|
113
|
-
</MediaLayoutContext.Provider>
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
106
|
+
const ModalDialog = React.forwardRef(function ModalDialog(
|
|
107
|
+
props: Props,
|
|
108
|
+
ref: React.ForwardedRef<HTMLDivElement>,
|
|
109
|
+
) {
|
|
110
|
+
return (
|
|
111
|
+
<ThemeModalDialog>
|
|
112
|
+
<ModalDialogCore {...props} ref={ref} />
|
|
113
|
+
</ThemeModalDialog>
|
|
114
|
+
);
|
|
115
|
+
});
|
|
117
116
|
|
|
118
|
-
const
|
|
119
|
-
all: StyleSheet.create({
|
|
120
|
-
wrapper: {
|
|
121
|
-
display: "flex",
|
|
122
|
-
flexDirection: "row",
|
|
123
|
-
alignItems: "stretch",
|
|
124
|
-
width: "100%",
|
|
125
|
-
height: "100%",
|
|
126
|
-
position: "relative",
|
|
127
|
-
},
|
|
117
|
+
const small = "@media (max-width: 767px)";
|
|
128
118
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
119
|
+
const themedStylesFn: ThemedStylesFn<ModalDialogThemeContract> = (theme) => ({
|
|
120
|
+
wrapper: {
|
|
121
|
+
display: "flex",
|
|
122
|
+
flexDirection: "row",
|
|
123
|
+
alignItems: "stretch",
|
|
124
|
+
width: "100%",
|
|
125
|
+
height: "100%",
|
|
126
|
+
position: "relative",
|
|
127
|
+
[small]: {
|
|
128
|
+
padding: theme.spacing.dialog.small,
|
|
129
|
+
flexDirection: "column",
|
|
137
130
|
},
|
|
131
|
+
},
|
|
138
132
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
133
|
+
/**
|
|
134
|
+
* Ensures the dialog container uses the container size
|
|
135
|
+
*/
|
|
136
|
+
dialog: {
|
|
137
|
+
width: "100%",
|
|
138
|
+
height: "100%",
|
|
139
|
+
borderRadius: theme.border.radius,
|
|
140
|
+
overflow: "hidden",
|
|
141
|
+
},
|
|
148
142
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}),
|
|
143
|
+
above: {
|
|
144
|
+
pointerEvents: "none",
|
|
145
|
+
position: "absolute",
|
|
146
|
+
top: 0,
|
|
147
|
+
left: 0,
|
|
148
|
+
bottom: 0,
|
|
149
|
+
right: 0,
|
|
150
|
+
zIndex: 1,
|
|
151
|
+
},
|
|
159
152
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
153
|
+
below: {
|
|
154
|
+
pointerEvents: "none",
|
|
155
|
+
position: "absolute",
|
|
156
|
+
top: 0,
|
|
157
|
+
left: 0,
|
|
158
|
+
bottom: 0,
|
|
159
|
+
right: 0,
|
|
160
|
+
zIndex: -1,
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
ModalDialog.displayName = "ModalDialog";
|
|
165
|
+
|
|
166
|
+
export default ModalDialog;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import {
|
|
3
|
+
createThemeContext,
|
|
4
|
+
Themes,
|
|
5
|
+
ThemeSwitcherContext,
|
|
6
|
+
} from "@khanacademy/wonder-blocks-theming";
|
|
7
|
+
|
|
8
|
+
import defaultTheme from "./default";
|
|
9
|
+
|
|
10
|
+
type Props = {
|
|
11
|
+
children: React.ReactNode;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type ModalDialogThemeContract = typeof defaultTheme;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The themes available to the ModalDialog component.
|
|
18
|
+
*/
|
|
19
|
+
const themes: Themes<ModalDialogThemeContract> = {
|
|
20
|
+
default: defaultTheme,
|
|
21
|
+
// khanmigo: khanmigoTheme,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The context that provides the theme to the ModalDialog component.
|
|
26
|
+
* This is generally consumed via the `useScopedTheme` hook.
|
|
27
|
+
*/
|
|
28
|
+
export const ModalDialogThemeContext = createThemeContext(defaultTheme);
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* ThemeModalDialog is a component that provides a theme to the <ModalDialog/>
|
|
32
|
+
* component.
|
|
33
|
+
*/
|
|
34
|
+
export default function ThemeModalDialog(props: Props) {
|
|
35
|
+
const currentTheme = React.useContext(ThemeSwitcherContext);
|
|
36
|
+
|
|
37
|
+
const theme = themes[currentTheme] || defaultTheme;
|
|
38
|
+
return (
|
|
39
|
+
<ModalDialogThemeContext.Provider value={theme}>
|
|
40
|
+
{props.children}
|
|
41
|
+
</ModalDialogThemeContext.Provider>
|
|
42
|
+
);
|
|
43
|
+
}
|
package/tsconfig-build.json
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
{"path": "../wonder-blocks-icon-button/tsconfig-build.json"},
|
|
15
15
|
{"path": "../wonder-blocks-layout/tsconfig-build.json"},
|
|
16
16
|
{"path": "../wonder-blocks-spacing/tsconfig-build.json"},
|
|
17
|
+
{"path": "../wonder-blocks-theming/tsconfig-build.json"},
|
|
17
18
|
{"path": "../wonder-blocks-timing/tsconfig-build.json"},
|
|
18
19
|
{"path": "../wonder-blocks-typography/tsconfig-build.json"},
|
|
19
20
|
]
|