@khanacademy/wonder-blocks-button 2.9.13 → 2.10.2
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/dist/es/index.js +17 -15
- package/dist/index.js +188 -249
- package/package.json +14 -14
- package/src/__tests__/__snapshots__/generated-snapshot.test.js.snap +240 -11
- package/src/__tests__/generated-snapshot.test.js +74 -12
- package/src/components/__docs__/accessibility.stories.mdx +92 -0
- package/src/components/__docs__/best-practices.stories.mdx +107 -0
- package/src/components/__docs__/button.argtypes.js +231 -0
- package/src/components/__docs__/navigation-callbacks.stories.mdx +68 -0
- package/src/components/__tests__/button.test.js +11 -0
- package/src/components/button-core.js +10 -9
- package/src/components/button.js +20 -16
- package/src/components/button.md +134 -23
- package/src/components/button.stories.js +413 -104
package/src/components/button.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
### Example: kind
|
|
2
2
|
|
|
3
3
|
There are three `kind`s of buttons: `"primary"` (default), `"secondary"`, and
|
|
4
4
|
`"tertiary"`:
|
|
@@ -40,7 +40,7 @@ const styles = StyleSheet.create({
|
|
|
40
40
|
</View>
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
### Example: color
|
|
44
44
|
|
|
45
45
|
Buttons have a `color` that is either `"default"` (the default, as shown above) or `"destructive"` (as can seen below):
|
|
46
46
|
|
|
@@ -85,7 +85,7 @@ const styles = StyleSheet.create({
|
|
|
85
85
|
</View>
|
|
86
86
|
```
|
|
87
87
|
|
|
88
|
-
|
|
88
|
+
### Example: disabled
|
|
89
89
|
|
|
90
90
|
Buttons can be `disabled`:
|
|
91
91
|
|
|
@@ -139,7 +139,7 @@ const styles = StyleSheet.create({
|
|
|
139
139
|
</View>
|
|
140
140
|
```
|
|
141
141
|
|
|
142
|
-
|
|
142
|
+
### Example: dark
|
|
143
143
|
|
|
144
144
|
Buttons on a `darkBlue` background should set `light` to `true`.
|
|
145
145
|
```jsx
|
|
@@ -212,7 +212,7 @@ const styles = StyleSheet.create({
|
|
|
212
212
|
</View>
|
|
213
213
|
```
|
|
214
214
|
|
|
215
|
-
|
|
215
|
+
### Example: size
|
|
216
216
|
|
|
217
217
|
Buttons have a `size` that's either `"medium"` (default), `"small"`, or `"xlarge"`.
|
|
218
218
|
```js
|
|
@@ -309,7 +309,7 @@ const styles = StyleSheet.create({
|
|
|
309
309
|
</View>
|
|
310
310
|
```
|
|
311
311
|
|
|
312
|
-
|
|
312
|
+
### Example: spinner
|
|
313
313
|
|
|
314
314
|
Buttons can show a `spinner`. This is useful when indicating to a user that
|
|
315
315
|
their input has been recognized but that the operation will take some time.
|
|
@@ -343,7 +343,7 @@ const styles = StyleSheet.create({
|
|
|
343
343
|
</View>
|
|
344
344
|
```
|
|
345
345
|
|
|
346
|
-
|
|
346
|
+
### Example: Navigation
|
|
347
347
|
|
|
348
348
|
```jsx
|
|
349
349
|
import Button from "@khanacademy/wonder-blocks-button";
|
|
@@ -384,7 +384,7 @@ const styles = StyleSheet.create({
|
|
|
384
384
|
</View>
|
|
385
385
|
```
|
|
386
386
|
|
|
387
|
-
|
|
387
|
+
### Example: Navigation with React Router
|
|
388
388
|
|
|
389
389
|
Buttons do client-side navigation by default, if React Router exists:
|
|
390
390
|
```jsx
|
|
@@ -420,12 +420,59 @@ const styles = StyleSheet.create({
|
|
|
420
420
|
</View>
|
|
421
421
|
</MemoryRouter>
|
|
422
422
|
```
|
|
423
|
+
### Running callbacks on navigation
|
|
424
|
+
|
|
425
|
+
Sometimes you may need to run some code and also navigate when the user
|
|
426
|
+
clicks the button. For example, you might want to send a request to the
|
|
427
|
+
server and also send the user to a different page. You can do this by
|
|
428
|
+
passing in a URL to the `href` prop and also passing in a callback
|
|
429
|
+
function to either the `onClick`, `beforeNav`, or `safeWithNav` prop.
|
|
430
|
+
Which prop you choose depends on your use case.
|
|
431
|
+
|
|
432
|
+
- `onClick` is guaranteed to run to completion before navigation starts,
|
|
433
|
+
but it is not async aware, so it should only be used if all of the code
|
|
434
|
+
in your callback function executes synchronously.
|
|
435
|
+
|
|
436
|
+
- `beforeNav` is guaranteed to run async operations before navigation
|
|
437
|
+
starts. You must return a promise from the callback function passed in
|
|
438
|
+
to this prop, and the navigation will happen after the promise
|
|
439
|
+
resolves. If the promise rejects, the navigation will not occur.
|
|
440
|
+
This prop should be used if it's important that the async code
|
|
441
|
+
completely finishes before the next URL starts loading.
|
|
442
|
+
|
|
443
|
+
- `safeWithNav` runs async code concurrently with navigation when safe,
|
|
444
|
+
but delays navigation until the async code is finished when
|
|
445
|
+
concurrent execution is not safe. You must return a promise from the
|
|
446
|
+
callback function passed in to this prop, and Wonder Blocks will run
|
|
447
|
+
the async code in parallel with client-side navigation or while opening
|
|
448
|
+
a new tab, but will wait until the async code finishes to start a
|
|
449
|
+
server-side navigation. If the promise rejects the navigation will
|
|
450
|
+
happen anyway. This prop should be used when it's okay to load
|
|
451
|
+
the next URL while the async callback code is running.
|
|
452
|
+
|
|
453
|
+
This table gives an overview of the options:
|
|
454
|
+
|
|
455
|
+
| Prop | Async safe? | Completes before navigation? |
|
|
456
|
+
|-------------|-------------|------------------------------|
|
|
457
|
+
| onClick | no | yes |
|
|
458
|
+
| beforeNav | yes | yes |
|
|
459
|
+
| safeWithNav | yes | no |
|
|
460
|
+
|
|
461
|
+
It is possible to use more than one of these props on the same element.
|
|
462
|
+
If multiple props are used, they will run in this order: first `onClick`,
|
|
463
|
+
then `beforeNav`, then `safeWithNav`. If both `beforeNav` and `safeWithNav`
|
|
464
|
+
are used, the `safeWithNav` callback will not be called until the
|
|
465
|
+
`beforeNav` promise resolves successfully. If the `beforeNav` promise
|
|
466
|
+
rejects, `safeWithNav` will not be run.
|
|
467
|
+
|
|
468
|
+
If the `onClick` handler calls `preventDefault()`, then `beforeNav`
|
|
469
|
+
and `safeWithNav` will still run, but navigation will not occur.
|
|
470
|
+
|
|
471
|
+
### Example: beforeNav callbacks
|
|
472
|
+
|
|
473
|
+
These buttons always wait until the async callback code completes before
|
|
474
|
+
starting navigation.
|
|
423
475
|
|
|
424
|
-
#### Example: Navigation with async action
|
|
425
|
-
|
|
426
|
-
Sometimes you may need to perform an async action either before or during
|
|
427
|
-
navigation. This can be accomplished with `beforeNav` and `safeWithNav`
|
|
428
|
-
respectively.
|
|
429
476
|
```jsx
|
|
430
477
|
import Button from "@khanacademy/wonder-blocks-button";
|
|
431
478
|
import {View} from "@khanacademy/wonder-blocks-core";
|
|
@@ -452,7 +499,7 @@ const styles = StyleSheet.create({
|
|
|
452
499
|
setTimeout(resolve, 1000);
|
|
453
500
|
})}
|
|
454
501
|
>
|
|
455
|
-
|
|
502
|
+
beforeNav, client-side nav
|
|
456
503
|
</Button>
|
|
457
504
|
<Button
|
|
458
505
|
href="/foo"
|
|
@@ -462,7 +509,7 @@ const styles = StyleSheet.create({
|
|
|
462
509
|
setTimeout(resolve, 1000);
|
|
463
510
|
})}
|
|
464
511
|
>
|
|
465
|
-
|
|
512
|
+
beforeNav, server-side nav
|
|
466
513
|
</Button>
|
|
467
514
|
<Button
|
|
468
515
|
href="https://google.com"
|
|
@@ -473,7 +520,71 @@ const styles = StyleSheet.create({
|
|
|
473
520
|
setTimeout(resolve, 1000);
|
|
474
521
|
})}
|
|
475
522
|
>
|
|
476
|
-
|
|
523
|
+
beforeNav, open URL in new tab
|
|
524
|
+
</Button>
|
|
525
|
+
<Switch>
|
|
526
|
+
<Route path="/foo">
|
|
527
|
+
<View id="foo">Hello, world!</View>
|
|
528
|
+
</Route>
|
|
529
|
+
</Switch>
|
|
530
|
+
</View>
|
|
531
|
+
</MemoryRouter>
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
### Example: safeWithNav callbacks
|
|
535
|
+
|
|
536
|
+
These buttons navigate immediately when doing client-side navigation
|
|
537
|
+
or when opening a new tab, but wait until the async callback code
|
|
538
|
+
completes before starting server-side navigation.
|
|
539
|
+
|
|
540
|
+
```jsx
|
|
541
|
+
import Button from "@khanacademy/wonder-blocks-button";
|
|
542
|
+
import {View} from "@khanacademy/wonder-blocks-core";
|
|
543
|
+
import {StyleSheet} from "aphrodite";
|
|
544
|
+
import {MemoryRouter, Route, Switch} from "react-router-dom";
|
|
545
|
+
|
|
546
|
+
const styles = StyleSheet.create({
|
|
547
|
+
row: {
|
|
548
|
+
flexDirection: "row",
|
|
549
|
+
alignItems: "center",
|
|
550
|
+
},
|
|
551
|
+
button: {
|
|
552
|
+
marginRight: 10,
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
// NOTE: In actual code you would use BrowserRouter instead
|
|
557
|
+
<MemoryRouter>
|
|
558
|
+
<View style={styles.row}>
|
|
559
|
+
<Button
|
|
560
|
+
href="/foo"
|
|
561
|
+
style={styles.button}
|
|
562
|
+
safeWithNav={() => new Promise((resolve, reject) => {
|
|
563
|
+
setTimeout(resolve, 1000);
|
|
564
|
+
})}
|
|
565
|
+
>
|
|
566
|
+
safeWithNav, client-side nav
|
|
567
|
+
</Button>
|
|
568
|
+
<Button
|
|
569
|
+
href="/foo"
|
|
570
|
+
style={styles.button}
|
|
571
|
+
skipClientNav={true}
|
|
572
|
+
safeWithNav={() => new Promise((resolve, reject) => {
|
|
573
|
+
setTimeout(resolve, 1000);
|
|
574
|
+
})}
|
|
575
|
+
>
|
|
576
|
+
safeWithNav, server-side nav
|
|
577
|
+
</Button>
|
|
578
|
+
<Button
|
|
579
|
+
href="https://google.com"
|
|
580
|
+
target="_blank"
|
|
581
|
+
style={styles.button}
|
|
582
|
+
skipClientNav={true}
|
|
583
|
+
safeWithNav={() => new Promise((resolve, reject) => {
|
|
584
|
+
setTimeout(resolve, 1000);
|
|
585
|
+
})}
|
|
586
|
+
>
|
|
587
|
+
safeWithNav, open URL in new tab
|
|
477
588
|
</Button>
|
|
478
589
|
<Switch>
|
|
479
590
|
<Route path="/foo">
|
|
@@ -484,11 +595,11 @@ const styles = StyleSheet.create({
|
|
|
484
595
|
</MemoryRouter>
|
|
485
596
|
```
|
|
486
597
|
|
|
487
|
-
|
|
598
|
+
### Example: Prevent navigation by calling e.preventDefault()
|
|
599
|
+
|
|
600
|
+
If the `onClick` callback calls `preventDefault()`, then navigation
|
|
601
|
+
will not occur.
|
|
488
602
|
|
|
489
|
-
Sometimes you may need to perform an async action either before or during
|
|
490
|
-
navigation. This can be accomplished with `beforeNav` and `safeWithNav`
|
|
491
|
-
respectively.
|
|
492
603
|
```jsx
|
|
493
604
|
import Button from "@khanacademy/wonder-blocks-button";
|
|
494
605
|
import {View} from "@khanacademy/wonder-blocks-core";
|
|
@@ -513,7 +624,7 @@ const styles = StyleSheet.create({
|
|
|
513
624
|
style={styles.button}
|
|
514
625
|
onClick={e => e.preventDefault()}
|
|
515
626
|
>
|
|
516
|
-
This button
|
|
627
|
+
This button prevents navigation.
|
|
517
628
|
</Button>
|
|
518
629
|
<Switch>
|
|
519
630
|
<Route path="/foo">
|
|
@@ -524,7 +635,7 @@ const styles = StyleSheet.create({
|
|
|
524
635
|
</MemoryRouter>
|
|
525
636
|
```
|
|
526
637
|
|
|
527
|
-
|
|
638
|
+
### Example: style
|
|
528
639
|
|
|
529
640
|
Buttons can have a `style` props which supports width, position, margin,
|
|
530
641
|
and flex styles.
|
|
@@ -582,7 +693,7 @@ const kinds = ["primary", "secondary", "tertiary"];
|
|
|
582
693
|
</View>
|
|
583
694
|
```
|
|
584
695
|
|
|
585
|
-
|
|
696
|
+
### Example: "submit" buttons in forms
|
|
586
697
|
|
|
587
698
|
```jsx
|
|
588
699
|
import Button from "@khanacademy/wonder-blocks-button";
|