@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.
@@ -1,4 +1,4 @@
1
- #### Example: kind
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
- #### Example: color
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
- #### Example: disabled
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
- #### Example: dark
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
- #### Example: size
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
- #### Example: spinner
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
- #### Example: Navigation
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
- #### Example: Navigation with React Router
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
- Async action, client-side nav
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
- Async action, server-side nav
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
- Async action, open URL in new tab
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
- #### Example: Prevent navigation by calling e.preventDefault()
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 prevent navigation.
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
- #### Example: style
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
- #### Example: "submit" buttons in forms
696
+ ### Example: "submit" buttons in forms
586
697
 
587
698
  ```jsx
588
699
  import Button from "@khanacademy/wonder-blocks-button";