@khanacademy/wonder-blocks-button 3.0.13 → 3.0.15

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.
@@ -1,508 +0,0 @@
1
- // @flow
2
- import * as React from "react";
3
- import {StyleSheet} from "aphrodite";
4
- import {withDesign} from "storybook-addon-designs";
5
- import {action} from "@storybook/addon-actions";
6
-
7
- import {MemoryRouter, Route, Switch} from "react-router-dom";
8
-
9
- import Color from "@khanacademy/wonder-blocks-color";
10
- import {View} from "@khanacademy/wonder-blocks-core";
11
- import {icons} from "@khanacademy/wonder-blocks-icon";
12
- import {Strut} from "@khanacademy/wonder-blocks-layout";
13
- import Spacing from "@khanacademy/wonder-blocks-spacing";
14
- import {LabelMedium} from "@khanacademy/wonder-blocks-typography";
15
- import type {StoryComponentType} from "@storybook/react";
16
- import type {StyleDeclaration} from "aphrodite";
17
-
18
- import Button from "../button.js";
19
-
20
- import ComponentInfo from "../../../../../.storybook/components/component-info.js";
21
- import ButtonArgTypes from "./button.argtypes.js";
22
- import {name, version} from "../../../package.json";
23
-
24
- export default {
25
- title: "Button",
26
- component: Button,
27
- parameters: {
28
- componentSubtitle: ((
29
- <ComponentInfo name={name} version={version} />
30
- ): any),
31
- },
32
- decorators: [withDesign],
33
- argTypes: ButtonArgTypes,
34
- excludeStories: ["styles"],
35
- };
36
-
37
- const Template = (args) => <Button {...args} />;
38
-
39
- export const Default: StoryComponentType = Template.bind({});
40
-
41
- Default.args = {
42
- children: "Hello, world!",
43
- kind: "primary",
44
- color: "default",
45
- size: "medium",
46
- light: false,
47
- disabled: false,
48
- style: {maxWidth: 200},
49
- onClick: () => {
50
- // eslint-disable-next-line no-alert
51
- alert("Click!");
52
- },
53
- };
54
-
55
- Default.parameters = {
56
- design: {
57
- type: "figma",
58
- url: "https://www.figma.com/file/VbVu3h2BpBhH80niq101MHHE/Wonder-Blocks-(Web)?node-id=401%3A307",
59
- },
60
- chromatic: {
61
- // We already have screenshots of other stories that cover more of the button states
62
- disableSnapshot: true,
63
- },
64
- };
65
-
66
- export const styles: StyleDeclaration = StyleSheet.create({
67
- row: {
68
- flexDirection: "row",
69
- alignItems: "center",
70
- marginBottom: Spacing.xSmall_8,
71
- },
72
- button: {
73
- marginRight: Spacing.xSmall_8,
74
- },
75
- fillSpace: {
76
- minWidth: 140,
77
- },
78
- example: {
79
- background: Color.offWhite,
80
- padding: Spacing.medium_16,
81
- },
82
- });
83
-
84
- export const Variants: StoryComponentType = () => (
85
- <View>
86
- <View style={{flexDirection: "row"}}>
87
- <Button onClick={() => {}}>Hello, world!</Button>
88
- <Strut size={16} />
89
- <Button onClick={() => {}} kind="secondary">
90
- Hello, world!
91
- </Button>
92
- <Strut size={16} />
93
- <Button onClick={() => {}} kind="tertiary">
94
- Hello, world!
95
- </Button>
96
- </View>
97
- <Strut size={16} />
98
- <View style={{flexDirection: "row"}}>
99
- <Button onClick={() => {}} disabled={true}>
100
- Hello, world!
101
- </Button>
102
- <Strut size={16} />
103
- <Button onClick={() => {}} disabled={true} kind="secondary">
104
- Hello, world!
105
- </Button>
106
- <Strut size={16} />
107
- <Button onClick={() => {}} disabled={true} kind="tertiary">
108
- Hello, world!
109
- </Button>
110
- </View>
111
- <Strut size={16} />
112
- <View style={{flexDirection: "row"}}>
113
- <Button onClick={() => {}} color="destructive">
114
- Hello, world!
115
- </Button>
116
- <Strut size={16} />
117
- <Button onClick={() => {}} kind="secondary" color="destructive">
118
- Hello, world!
119
- </Button>
120
- <Strut size={16} />
121
- <Button onClick={() => {}} kind="tertiary" color="destructive">
122
- Hello, world!
123
- </Button>
124
- </View>
125
- </View>
126
- );
127
-
128
- Variants.parameters = {
129
- docs: {
130
- storyDescription:
131
- "There are three kinds of buttons: `primary` (default), `secondary`, and `tertiary`.",
132
- },
133
- };
134
-
135
- export const WithColor: StoryComponentType = () => (
136
- <View style={styles.row}>
137
- <Button style={styles.button} onClick={() => {}} color="destructive">
138
- Primary
139
- </Button>
140
- <Button
141
- style={styles.button}
142
- onClick={() => {}}
143
- kind="secondary"
144
- color="destructive"
145
- >
146
- Secondary
147
- </Button>
148
- <Button
149
- style={styles.button}
150
- onClick={() => {}}
151
- kind="tertiary"
152
- color="destructive"
153
- >
154
- Tertiary
155
- </Button>
156
- </View>
157
- );
158
-
159
- WithColor.storyName = "Color";
160
-
161
- WithColor.parameters = {
162
- docs: {
163
- storyDescription:
164
- "Buttons have a `color` that is either `default` (the default, as shown above) or `destructive` (as can seen below):",
165
- },
166
- };
167
-
168
- export const Dark: StoryComponentType = () => (
169
- <View style={{backgroundColor: Color.darkBlue}}>
170
- <View style={{flexDirection: "row"}}>
171
- <Button onClick={() => {}} light={true}>
172
- Hello, world!
173
- </Button>
174
- <Strut size={16} />
175
- <Button onClick={() => {}} light={true} kind="secondary">
176
- Hello, world!
177
- </Button>
178
- <Strut size={16} />
179
- <Button onClick={() => {}} light={true} kind="tertiary">
180
- Hello, world!
181
- </Button>
182
- </View>
183
- <Strut size={16} />
184
- <View style={{flexDirection: "row"}}>
185
- <Button onClick={() => {}} light={true} disabled={true}>
186
- Hello, world!
187
- </Button>
188
- <Strut size={16} />
189
- <Button
190
- onClick={() => {}}
191
- light={true}
192
- disabled={true}
193
- kind="secondary"
194
- >
195
- Hello, world!
196
- </Button>
197
- <Strut size={16} />
198
- <Button
199
- onClick={() => {}}
200
- light={true}
201
- disabled={true}
202
- kind="tertiary"
203
- >
204
- Hello, world!
205
- </Button>
206
- </View>
207
- <Strut size={16} />
208
- <View style={{flexDirection: "row"}}>
209
- <Button onClick={() => {}} light={true} color="destructive">
210
- Hello, world!
211
- </Button>
212
- <Strut size={16} />
213
- <Button
214
- onClick={() => {}}
215
- light={true}
216
- kind="secondary"
217
- color="destructive"
218
- >
219
- Hello, world!
220
- </Button>
221
- <Strut size={16} />
222
- <Button
223
- onClick={() => {}}
224
- light={true}
225
- kind="tertiary"
226
- color="destructive"
227
- >
228
- Hello, world!
229
- </Button>
230
- </View>
231
- </View>
232
- );
233
-
234
- Dark.parameters = {
235
- backgrounds: {
236
- default: "darkBlue",
237
- },
238
- docs: {
239
- storyDescription:
240
- "Buttons on a `darkBlue` background should set `light` to `true`.",
241
- },
242
- };
243
-
244
- const kinds = ["primary", "secondary", "tertiary"];
245
-
246
- export const Icon: StoryComponentType = () => (
247
- <View>
248
- <View style={styles.row}>
249
- {kinds.map((kind, idx) => (
250
- <Button
251
- kind={kind}
252
- icon={icons.contentExercise}
253
- style={styles.button}
254
- key={idx}
255
- >
256
- {kind}
257
- </Button>
258
- ))}
259
- </View>
260
- <View style={styles.row}>
261
- {kinds.map((kind, idx) => (
262
- <Button
263
- kind={kind}
264
- icon={icons.contentExercise}
265
- style={styles.button}
266
- key={idx}
267
- size="small"
268
- >
269
- {`${kind} small`}
270
- </Button>
271
- ))}
272
- </View>
273
- </View>
274
- );
275
-
276
- Icon.parameters = {
277
- docs: {
278
- storyDescription: "Buttons can have an icon on it's left side.",
279
- },
280
- };
281
-
282
- export const Size: StoryComponentType = () => (
283
- <View>
284
- <View style={styles.row}>
285
- <LabelMedium style={styles.fillSpace}>small</LabelMedium>
286
- <View style={[styles.row, styles.example]}>
287
- <Button style={styles.button} onClick={() => {}} size="small">
288
- Label
289
- </Button>
290
- <Button
291
- style={styles.button}
292
- onClick={() => {}}
293
- kind="secondary"
294
- size="small"
295
- >
296
- Label
297
- </Button>
298
- <Button
299
- style={styles.button}
300
- onClick={() => {}}
301
- kind="tertiary"
302
- size="small"
303
- >
304
- Label
305
- </Button>
306
- </View>
307
- </View>
308
- <View style={styles.row}>
309
- <LabelMedium style={styles.fillSpace}>medium (default)</LabelMedium>
310
-
311
- <View style={[styles.row, styles.example]}>
312
- <Button style={styles.button} onClick={() => {}} size="medium">
313
- Label
314
- </Button>
315
- <Button
316
- style={styles.button}
317
- onClick={() => {}}
318
- kind="secondary"
319
- size="medium"
320
- >
321
- Label
322
- </Button>
323
- <Button
324
- style={styles.button}
325
- onClick={() => {}}
326
- kind="tertiary"
327
- size="medium"
328
- >
329
- Label
330
- </Button>
331
- </View>
332
- </View>
333
- <View style={styles.row}>
334
- <LabelMedium style={styles.fillSpace}>large</LabelMedium>
335
- <View style={[styles.row, styles.example]}>
336
- <Button style={styles.button} onClick={() => {}} size="large">
337
- Label
338
- </Button>
339
- <Button
340
- style={styles.button}
341
- onClick={() => {}}
342
- kind="secondary"
343
- size="large"
344
- >
345
- Label
346
- </Button>
347
- <Button
348
- style={styles.button}
349
- onClick={() => {}}
350
- kind="tertiary"
351
- size="large"
352
- >
353
- Label
354
- </Button>
355
- </View>
356
- </View>
357
- </View>
358
- );
359
-
360
- Size.parameters = {
361
- docs: {
362
- storyDescription:
363
- "Buttons have a size that's either `medium` (default), `small`, or `large`.",
364
- },
365
- };
366
-
367
- export const Spinner: StoryComponentType = () => (
368
- <View style={{flexDirection: "row"}}>
369
- <Button
370
- onClick={() => {}}
371
- spinner={true}
372
- size="large"
373
- aria-label={"waiting"}
374
- >
375
- Hello, world
376
- </Button>
377
- <Strut size={16} />
378
- <Button onClick={() => {}} spinner={true} aria-label={"waiting"}>
379
- Hello, world
380
- </Button>
381
- <Strut size={16} />
382
- <Button
383
- onClick={() => {}}
384
- spinner={true}
385
- size="small"
386
- aria-label={"waiting"}
387
- >
388
- Hello, world
389
- </Button>
390
- </View>
391
- );
392
-
393
- Spinner.parameters = {
394
- docs: {
395
- storyDescription:
396
- "Buttons can show a spinner. This is useful when indicating to a user that their input has been recognized but that the operation will take some time. While the spinner property is set to true the button is disabled.",
397
- },
398
- };
399
-
400
- export const TruncatingLabels: StoryComponentType = () => (
401
- <Button onClick={() => {}} style={{maxWidth: 200}}>
402
- label too long for the parent container
403
- </Button>
404
- );
405
-
406
- TruncatingLabels.parameters = {
407
- docs: {
408
- storyDescription:
409
- "If the label is too long for the button width, the text will be truncated.",
410
- },
411
- };
412
-
413
- TruncatingLabels.storyName = "Truncating labels";
414
-
415
- export const SubmittingForms: StoryComponentType = () => (
416
- <form
417
- onSubmit={(e) => {
418
- e.preventDefault();
419
- window.alert("form submitted"); // eslint-disable-line no-alert
420
- }}
421
- >
422
- <View>
423
- Foo: <input id="foo" value="bar" />
424
- <Button type="submit">Submit</Button>
425
- </View>
426
- </form>
427
- );
428
-
429
- SubmittingForms.parameters = {
430
- docs: {
431
- storyDescription:
432
- 'If the button is inside a form, you can use the `type="submit"` variant, so the form will be submitted on click.',
433
- },
434
- options: {
435
- showAddonPanel: true,
436
- },
437
- chromatic: {
438
- // We already have screenshots of other stories that cover more of the
439
- // button states.
440
- disableSnapshot: true,
441
- },
442
- };
443
-
444
- SubmittingForms.storyName = "Submitting forms";
445
-
446
- export const PreventNavigation: StoryComponentType = () => (
447
- <MemoryRouter>
448
- <View style={styles.row}>
449
- <Button
450
- href="/foo"
451
- style={styles.button}
452
- onClick={(e) => {
453
- action("clicked")(e);
454
- e.preventDefault();
455
- }}
456
- >
457
- This button prevents navigation.
458
- </Button>
459
- <Switch>
460
- <Route path="/foo">
461
- <View id="foo">Hello, world!</View>
462
- </Route>
463
- </Switch>
464
- </View>
465
- </MemoryRouter>
466
- );
467
-
468
- PreventNavigation.storyName = "Preventing navigation";
469
-
470
- PreventNavigation.parameters = {
471
- docs: {
472
- storyDescription:
473
- "Sometimes you may need to perform an async action either before or during navigation. This can be accomplished with `beforeNav` and `safeWithNav` respectively.",
474
- },
475
- chromatic: {
476
- disableSnapshot: true,
477
- },
478
- };
479
-
480
- export const WithRouter: StoryComponentType = () => (
481
- <MemoryRouter>
482
- <View style={styles.row}>
483
- <Button href="/foo" style={styles.button}>
484
- Uses Client-side Nav
485
- </Button>
486
- <Button href="/foo" style={styles.button} skipClientNav>
487
- Avoids Client-side Nav
488
- </Button>
489
- <Switch>
490
- <Route path="/foo">
491
- <View id="foo">Hello, world!</View>
492
- </Route>
493
- </Switch>
494
- </View>
495
- </MemoryRouter>
496
- );
497
-
498
- WithRouter.storyName = "Navigation with React Router";
499
-
500
- WithRouter.parameters = {
501
- docs: {
502
- storyDescription:
503
- "Buttons do client-side navigation by default, if React Router exists:",
504
- },
505
- chromatic: {
506
- disableSnapshot: true,
507
- },
508
- };
@@ -1,187 +0,0 @@
1
- import {Meta, Story, Canvas} from "@storybook/addon-docs";
2
- import {StyleSheet} from "aphrodite";
3
-
4
- import Button from "@khanacademy/wonder-blocks-button";
5
- import {View} from "@khanacademy/wonder-blocks-core";
6
-
7
- import {styles} from "./button.stories.js";
8
-
9
- <Meta
10
- title="Button / Navigation Callbacks"
11
- component={Button}
12
- parameters={{
13
- previewTabs: {
14
- canvas: {hidden: true},
15
- },
16
- viewMode: "docs",
17
- chromatic: {
18
- // Disables chromatic testing for these stories.
19
- disableSnapshot: true,
20
- },
21
- }}
22
- />
23
-
24
- # Running Callbacks on Navigation
25
-
26
- Sometimes you may need to run some code and also navigate when the user
27
- clicks the button. For example, you might want to send a request to the
28
- server and also send the user to a different page. You can do this by
29
- passing in a URL to the `href` prop and also passing in a callback
30
- function to either the `onClick`, `beforeNav`, or `safeWithNav` prop.
31
- Which prop you choose depends on your use case.
32
-
33
- - `onClick` is guaranteed to run to completion before navigation starts,
34
- but it is not async aware, so it should only be used if all of the code
35
- in your callback function executes synchronously.
36
-
37
- - `beforeNav` is guaranteed to run async operations before navigation
38
- starts. You must return a promise from the callback function passed in
39
- to this prop, and the navigation will happen after the promise
40
- resolves. If the promise rejects, the navigation will not occur.
41
- This prop should be used if it's important that the async code
42
- completely finishes before the next URL starts loading.
43
-
44
- - `safeWithNav` runs async code concurrently with navigation when safe,
45
- but delays navigation until the async code is finished when
46
- concurrent execution is not safe. You must return a promise from the
47
- callback function passed in to this prop, and Wonder Blocks will run
48
- the async code in parallel with client-side navigation or while opening
49
- a new tab, but will wait until the async code finishes to start a
50
- server-side navigation. If the promise rejects the navigation will
51
- happen anyway. This prop should be used when it's okay to load
52
- the next URL while the async callback code is running.
53
-
54
- This table gives an overview of the options:
55
-
56
- | Prop | Async safe? | Completes before navigation? |
57
- |-------------|-------------|------------------------------|
58
- | onClick | no | yes |
59
- | beforeNav | yes | yes |
60
- | safeWithNav | yes | no |
61
-
62
- It is possible to use more than one of these props on the same element.
63
- If multiple props are used, they will run in this order: first `onClick`,
64
- then `beforeNav`, then `safeWithNav`. If both `beforeNav` and `safeWithNav`
65
- are used, the `safeWithNav` callback will not be called until the
66
- `beforeNav` promise resolves successfully. If the `beforeNav` promise
67
- rejects, `safeWithNav` will not be run.
68
-
69
- If the `onClick` handler calls `preventDefault()`, then `beforeNav`
70
- and `safeWithNav` will still run, but navigation will not occur.
71
-
72
- export const BeforeNavCallbacks = () => (
73
- <MemoryRouter>
74
- <View style={styles.row}>
75
- <Button
76
- href="/foo"
77
- style={styles.button}
78
- beforeNav={() =>
79
- new Promise((resolve, reject) => {
80
- setTimeout(resolve, 1000);
81
- })
82
- }
83
- >
84
- beforeNav, client-side nav
85
- </Button>
86
- <Button
87
- href="/foo"
88
- style={styles.button}
89
- skipClientNav={true}
90
- beforeNav={() =>
91
- new Promise((resolve, reject) => {
92
- setTimeout(resolve, 1000);
93
- })
94
- }
95
- >
96
- beforeNav, server-side nav
97
- </Button>
98
- <Button
99
- href="https://google.com"
100
- style={styles.button}
101
- skipClientNav={true}
102
- beforeNav={() =>
103
- new Promise((resolve, reject) => {
104
- setTimeout(resolve, 1000);
105
- })
106
- }
107
- >
108
- beforeNav, open URL in new tab
109
- </Button>
110
- <Switch>
111
- <Route path="/foo">
112
- <View id="foo">Hello, world!</View>
113
- </Route>
114
- </Switch>
115
- </View>
116
- </MemoryRouter>
117
- );
118
-
119
- export const SafeWithNavCallbacks = () => (
120
- <MemoryRouter>
121
- <View style={styles.row}>
122
- <Button
123
- href="/foo"
124
- style={styles.button}
125
- safeWithNav={() =>
126
- new Promise((resolve, reject) => {
127
- setTimeout(resolve, 1000);
128
- })
129
- }
130
- >
131
- safeWithNav, client-side nav
132
- </Button>
133
- <Button
134
- href="/foo"
135
- style={styles.button}
136
- skipClientNav={true}
137
- safeWithNav={() =>
138
- new Promise((resolve, reject) => {
139
- setTimeout(resolve, 1000);
140
- })
141
- }
142
- >
143
- safeWithNav, server-side nav
144
- </Button>
145
- <Button
146
- href="https://google.com"
147
- style={styles.button}
148
- skipClientNav={true}
149
- safeWithNav={() =>
150
- new Promise((resolve, reject) => {
151
- setTimeout(resolve, 1000);
152
- })
153
- }
154
- >
155
- safeWithNav, open URL in new tab
156
- </Button>
157
- <Switch>
158
- <Route path="/foo">
159
- <View id="foo">Hello, world!</View>
160
- </Route>
161
- </Switch>
162
- </View>
163
- </MemoryRouter>
164
- );
165
-
166
- ## Stories
167
-
168
- ### beforeNav Callbacks
169
-
170
- These buttons always wait until the async callback code completes before
171
- starting navigation.
172
-
173
- <Canvas>
174
- <Story name="beforeNav Callbacks">
175
- {BeforeNavCallbacks.bind({})}
176
- </Story>
177
- </Canvas>
178
-
179
- ### safeWithNav Callbacks
180
-
181
- If the `onClick` callback calls `preventDefault()`, then navigation will not occur.
182
-
183
- <Canvas>
184
- <Story name="safeWithNav Callbacks">
185
- {SafeWithNavCallbacks.bind({})}
186
- </Story>
187
- </Canvas>