@khanacademy/wonder-blocks-modal 2.3.5 → 2.3.7

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.
@@ -0,0 +1,76 @@
1
+ // @flow
2
+ import * as React from "react";
3
+ import {
4
+ Breadcrumbs,
5
+ BreadcrumbsItem,
6
+ } from "@khanacademy/wonder-blocks-breadcrumbs";
7
+ import Link from "@khanacademy/wonder-blocks-link";
8
+
9
+ const BreadcrumbsMappings: {[key: string]: React.Node} = {
10
+ none: null,
11
+ "lesson path": (
12
+ <Breadcrumbs>
13
+ <BreadcrumbsItem>
14
+ <Link href="">Course</Link>
15
+ </BreadcrumbsItem>
16
+ <BreadcrumbsItem>
17
+ <Link href="">Unit</Link>
18
+ </BreadcrumbsItem>
19
+ <BreadcrumbsItem>Lesson</BreadcrumbsItem>
20
+ </Breadcrumbs>
21
+ ),
22
+ };
23
+
24
+ export default {
25
+ title: {
26
+ control: {type: "text"},
27
+ description: "The main title rendered in larger bold text.",
28
+ table: {type: {summary: "string"}},
29
+ },
30
+ light: {
31
+ control: {type: "boolean"},
32
+ defaultValue: "true",
33
+
34
+ description: `Whether to display the "light" version of this
35
+ component instead, for use when the item is used on a dark
36
+ background.`,
37
+ table: {
38
+ defaultValue: {summary: "true"},
39
+ type: {summary: "boolean"},
40
+ },
41
+ },
42
+ titleId: {
43
+ control: {type: "text"},
44
+ description: `An id to provide a selector for the title element.
45
+ Use this as the \`aria-labelledby\` value on the encompassing
46
+ \`<ModalDialog>\`.`,
47
+ table: {
48
+ type: {summary: "string"},
49
+ },
50
+ },
51
+ testId: {
52
+ control: {type: "text"},
53
+ description: `Test ID used for e2e testing.\n\nIn this case, this
54
+ component is internal, so \`testId\` is composed with the
55
+ \`testId\` passed down from the Dialog variant + a suffix to scope
56
+ it to this component. If the dialog \`testId\` is
57
+ \`"some-random-id"\` then the header will have the \`testId\`
58
+ \`"some-random-id-modal-header"\`.`,
59
+ table: {
60
+ type: {summary: "string"},
61
+ },
62
+ },
63
+ subtitle: {
64
+ control: {type: "text"},
65
+ description: "The dialog subtitle.",
66
+ table: {type: {summary: "string"}},
67
+ },
68
+ breadcrumbs: {
69
+ control: {type: "select"},
70
+ description: `Adds a breadcrumb-trail, appearing in the ModalHeader,
71
+ above the title.`,
72
+ options: (Object.keys(BreadcrumbsMappings): Array<React.Node>),
73
+ mapping: BreadcrumbsMappings,
74
+ table: {type: {summary: "React.Element<Breadcrumbs>"}},
75
+ },
76
+ };
@@ -0,0 +1,294 @@
1
+ // @flow
2
+ import * as React from "react";
3
+ import {StyleSheet} from "aphrodite";
4
+
5
+ import {
6
+ Breadcrumbs,
7
+ BreadcrumbsItem,
8
+ } from "@khanacademy/wonder-blocks-breadcrumbs";
9
+ import {View} from "@khanacademy/wonder-blocks-core";
10
+ import Link from "@khanacademy/wonder-blocks-link";
11
+ import {
12
+ ModalDialog,
13
+ ModalPanel,
14
+ ModalHeader,
15
+ } from "@khanacademy/wonder-blocks-modal";
16
+ import {Body} from "@khanacademy/wonder-blocks-typography";
17
+
18
+ import type {StoryComponentType} from "@storybook/react";
19
+
20
+ import ComponentInfo from "../../../../../.storybook/components/component-info.js";
21
+ import {name, version} from "../../../package.json";
22
+ import ModalHeaderArgtypes from "./modal-header.argtypes.js";
23
+
24
+ const customViewports = {
25
+ phone: {
26
+ name: "phone",
27
+ styles: {
28
+ width: "320px",
29
+ height: "568px",
30
+ },
31
+ },
32
+ tablet: {
33
+ name: "tablet",
34
+ styles: {
35
+ width: "640px",
36
+ height: "960px",
37
+ },
38
+ },
39
+ desktop: {
40
+ name: "desktop",
41
+ styles: {
42
+ width: "1024px",
43
+ height: "768px",
44
+ },
45
+ },
46
+ };
47
+
48
+ const longBody = (
49
+ <>
50
+ <Body>
51
+ {`Let's make this body content long in order
52
+ to test scroll overflow.`}
53
+ </Body>
54
+ <br />
55
+ <Body>
56
+ {`Lorem ipsum dolor sit amet, consectetur
57
+ adipiscing elit, sed do eiusmod tempor incididunt
58
+ ut labore et dolore magna aliqua. Ut enim ad minim
59
+ veniam, quis nostrud exercitation ullamco laboris
60
+ nisi ut aliquip ex ea commodo consequat. Duis aute
61
+ irure dolor in reprehenderit in voluptate velit
62
+ esse cillum dolore eu fugiat nulla pariatur.
63
+ Excepteur sint occaecat cupidatat non proident,
64
+ sunt in culpa qui officia deserunt mollit anim id
65
+ est.`}
66
+ </Body>
67
+ <br />
68
+ <Body>
69
+ {`Lorem ipsum dolor sit amet, consectetur
70
+ adipiscing elit, sed do eiusmod tempor incididunt
71
+ ut labore et dolore magna aliqua. Ut enim ad minim
72
+ veniam, quis nostrud exercitation ullamco laboris
73
+ nisi ut aliquip ex ea commodo consequat. Duis aute
74
+ irure dolor in reprehenderit in voluptate velit
75
+ esse cillum dolore eu fugiat nulla pariatur.
76
+ Excepteur sint occaecat cupidatat non proident,
77
+ sunt in culpa qui officia deserunt mollit anim id
78
+ est.`}
79
+ </Body>
80
+ <br />
81
+ <Body>
82
+ {`Lorem ipsum dolor sit amet, consectetur
83
+ adipiscing elit, sed do eiusmod tempor incididunt
84
+ ut labore et dolore magna aliqua. Ut enim ad minim
85
+ veniam, quis nostrud exercitation ullamco laboris
86
+ nisi ut aliquip ex ea commodo consequat. Duis aute
87
+ irure dolor in reprehenderit in voluptate velit
88
+ esse cillum dolore eu fugiat nulla pariatur.
89
+ Excepteur sint occaecat cupidatat non proident,
90
+ sunt in culpa qui officia deserunt mollit anim id
91
+ est.`}
92
+ </Body>
93
+ </>
94
+ );
95
+
96
+ export default {
97
+ title: "Modal/Building Blocks/ModalHeader",
98
+ component: ModalHeader,
99
+ decorators: [
100
+ (Story: StoryComponentType): React.Element<typeof View> => (
101
+ <View style={styles.previewSizer}>
102
+ <View style={styles.modalPositioner}>
103
+ <Story />
104
+ </View>
105
+ </View>
106
+ ),
107
+ ],
108
+ parameters: {
109
+ componentSubtitle: ((
110
+ <ComponentInfo name={name} version={version} />
111
+ ): any),
112
+ docs: {
113
+ description: {
114
+ component: null,
115
+ },
116
+ source: {
117
+ // See https://github.com/storybookjs/storybook/issues/12596
118
+ excludeDecorators: true,
119
+ },
120
+ },
121
+ viewport: {
122
+ viewports: customViewports,
123
+ defaultViewport: "desktop",
124
+ },
125
+ chromatic: {
126
+ viewports: [320, 640, 1024],
127
+ },
128
+ },
129
+ argTypes: ModalHeaderArgtypes,
130
+ };
131
+
132
+ export const Default: StoryComponentType = (args) => (
133
+ <ModalDialog aria-labelledby={args.titleId} style={styles.dialog}>
134
+ <ModalPanel header={<ModalHeader {...args} />} content={longBody} />
135
+ </ModalDialog>
136
+ );
137
+
138
+ Default.args = {
139
+ title: "This is a modal title.",
140
+ titleId: "modal-title-id-default-example",
141
+ };
142
+
143
+ export const Simple: StoryComponentType = () => (
144
+ <ModalDialog aria-labelledby="modal-title-1" style={styles.dialog}>
145
+ <ModalPanel
146
+ header={<ModalHeader title="Modal Title" titleId="modal-title-1" />}
147
+ content={longBody}
148
+ />
149
+ </ModalDialog>
150
+ );
151
+
152
+ Simple.parameters = {
153
+ docs: {
154
+ storyDescription: `This is a basic \`<ModalHeader>\`. It just has a
155
+ \`content\` prop that contains a title and a body.`,
156
+ },
157
+ };
158
+
159
+ export const Dark: StoryComponentType = () => (
160
+ <ModalDialog aria-labelledby="modal-title-2" style={styles.dialog}>
161
+ <ModalPanel
162
+ header={
163
+ <ModalHeader
164
+ title="Modal Title"
165
+ titleId="modal-title-2"
166
+ light={false}
167
+ />
168
+ }
169
+ content={longBody}
170
+ light={false}
171
+ />
172
+ </ModalDialog>
173
+ );
174
+
175
+ Dark.parameters = {
176
+ docs: {
177
+ storyDescription: `This is \`<ModalHeader>\` when \`light\` is
178
+ set to false. This should only be false if the \`light\` prop
179
+ on the encompassing \`<ModalPanel>\` is also false . Note that
180
+ the close button is not visible on the header if the panel is
181
+ light.`,
182
+ },
183
+ };
184
+
185
+ export const WithSubtitle: StoryComponentType = () => (
186
+ <ModalDialog aria-labelledby="modal-title-3" style={styles.dialog}>
187
+ <ModalPanel
188
+ header={
189
+ <ModalHeader
190
+ title="Modal Title"
191
+ titleId="modal-title-3"
192
+ subtitle="This is what a subtitle looks like."
193
+ />
194
+ }
195
+ content={longBody}
196
+ />
197
+ </ModalDialog>
198
+ );
199
+
200
+ WithSubtitle.parameters = {
201
+ docs: {
202
+ storyDescription: `This is \`<ModalHeader>\` with a subtitle, which
203
+ can be done by passing a string into the \`subtitle\` prop.`,
204
+ },
205
+ };
206
+
207
+ export const WithSubtitleDark: StoryComponentType = () => (
208
+ <ModalDialog aria-labelledby="modal-title-4" style={styles.dialog}>
209
+ <ModalPanel
210
+ header={
211
+ <ModalHeader
212
+ title="Modal Title"
213
+ titleId="modal-title-4"
214
+ subtitle="This is what a subtitle looks like."
215
+ light={false}
216
+ />
217
+ }
218
+ content={longBody}
219
+ light={false}
220
+ />
221
+ </ModalDialog>
222
+ );
223
+
224
+ WithSubtitleDark.parameters = {
225
+ docs: {
226
+ storyDescription: `This is \`<ModalHeader>\` with a subtitle
227
+ when it also has \`light\` set to false.`,
228
+ },
229
+ };
230
+
231
+ export const WithBreadcrumbs: StoryComponentType = () => (
232
+ <ModalDialog aria-labelledby="modal-title-5" style={styles.dialog}>
233
+ <ModalPanel
234
+ header={
235
+ <ModalHeader
236
+ title="Modal Title"
237
+ titleId="modal-title-5"
238
+ breadcrumbs={
239
+ <Breadcrumbs>
240
+ <BreadcrumbsItem>
241
+ <Link href="">Course</Link>
242
+ </BreadcrumbsItem>
243
+ <BreadcrumbsItem>
244
+ <Link href="">Unit</Link>
245
+ </BreadcrumbsItem>
246
+ <BreadcrumbsItem>Lesson</BreadcrumbsItem>
247
+ </Breadcrumbs>
248
+ }
249
+ />
250
+ }
251
+ content={longBody}
252
+ />
253
+ </ModalDialog>
254
+ );
255
+
256
+ WithBreadcrumbs.parameters = {
257
+ docs: {
258
+ storyDescription: `This is \`<ModalHeader>\` with breadcrumbs, which
259
+ can be done by passing a Wonder Blocks \`<Breadcrumbs>\`
260
+ element into the \`breadcrumbs\` prop. Note that \`breadcrumbs\`
261
+ currently do not work when \`light\` is false.`,
262
+ },
263
+ };
264
+
265
+ const styles = StyleSheet.create({
266
+ dialog: {
267
+ maxWidth: 600,
268
+ maxHeight: 500,
269
+ },
270
+ modalPositioner: {
271
+ // Checkerboard background
272
+ backgroundImage:
273
+ "linear-gradient(45deg, #ccc 25%, transparent 25%), linear-gradient(-45deg, #ccc 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #ccc 75%), linear-gradient(-45deg, transparent 75%, #ccc 75%)",
274
+ backgroundSize: "20px 20px",
275
+ backgroundPosition: "0 0, 0 10px, 10px -10px, -10px 0px",
276
+
277
+ flexDirection: "row",
278
+ alignItems: "center",
279
+ justifyContent: "center",
280
+
281
+ position: "absolute",
282
+ left: 0,
283
+ right: 0,
284
+ top: 0,
285
+ bottom: 0,
286
+ },
287
+ previewSizer: {
288
+ height: 600,
289
+ },
290
+ example: {
291
+ alignItems: "center",
292
+ justifyContent: "center",
293
+ },
294
+ });
@@ -0,0 +1,78 @@
1
+ // @flow
2
+
3
+ export default {
4
+ modal: {
5
+ control: {type: null},
6
+ description: `The modal to render. The modal will be rendered inside
7
+ of a container whose parent is document.body. This allows us to
8
+ use ModalLauncher within menus and other components that clip
9
+ their content. If the modal needs to close itself by some other
10
+ means than tapping the backdrop or the default close button a
11
+ render callback can be passed. The closeModal function provided
12
+ to this callback can be called to close the modal.
13
+ **Note**: Don't call \`closeModal\` while rendering! It should
14
+ be used to respond to user intearction, like \`onClick\`.`,
15
+ table: {
16
+ type: {
17
+ summary:
18
+ "ModalElement | (({|closeModal: () => void|}) => ModalElement)",
19
+ },
20
+ },
21
+ type: {required: true},
22
+ },
23
+ backdropDismissEnabled: {
24
+ control: {type: "boolean"},
25
+ defaultValue: "true",
26
+ description: "Enables the backdrop to dismiss the modal on click/tap.",
27
+ table: {
28
+ defaultValue: {summary: "true"},
29
+ type: {summary: "boolean"},
30
+ },
31
+ },
32
+ initialFocusId: {
33
+ control: {type: "text"},
34
+ description: `The selector for the element that will be focused
35
+ when the dialog shows. When not set, the first tabbable element
36
+ within the dialog will be used.`,
37
+ table: {
38
+ type: {summary: "string"},
39
+ },
40
+ },
41
+ closedFocusId: {
42
+ control: {type: "text"},
43
+ description: `The selector for the element that will be focused
44
+ after the dialog closes. When not set, the last element focused
45
+ outside the modal will be used if it exists.`,
46
+ table: {type: {summary: "string"}},
47
+ },
48
+ testId: {
49
+ control: {type: "text"},
50
+ description:
51
+ "Test ID used for e2e testing. It's set on the ModalBackdrop",
52
+ table: {type: {summary: "string"}},
53
+ },
54
+ opened: {
55
+ control: {type: "boolean"},
56
+ description: `Renders the modal when true, renders nothing when false.
57
+ Using this prop makes the component behave as a controlled
58
+ component. The parent is responsible for managing the
59
+ opening/closing of the modal when using this prop. \`onClose\`
60
+ should always be used and \`children\` should never be used with
61
+ this prop. Not doing so will result in an error being thrown.`,
62
+ table: {
63
+ category: "Controlled",
64
+ type: {summary: "boolean"},
65
+ },
66
+ },
67
+ onClose: {
68
+ description: `If the parent needs to be notified when the modal is
69
+ closed, use this prop. You probably want to use this instead of
70
+ \`onClose\` on the modals themselves, since this will capture a
71
+ more complete set of close events. \`onClose\` is required when
72
+ the component is being used as a controlled component.`,
73
+ table: {
74
+ category: "Controlled",
75
+ type: {summary: "() => mixed"},
76
+ },
77
+ },
78
+ };