@khanacademy/wonder-blocks-form 2.4.2 → 2.4.5

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 CHANGED
@@ -1,5 +1,34 @@
1
1
  # @khanacademy/wonder-blocks-form
2
2
 
3
+ ## 2.4.5
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [83486dba]
8
+ - @khanacademy/wonder-blocks-icon@1.2.29
9
+
10
+ ## 2.4.4
11
+
12
+ ### Patch Changes
13
+
14
+ - Updated dependencies [5f4a4297]
15
+ - Updated dependencies [2b96fd59]
16
+ - @khanacademy/wonder-blocks-core@4.3.2
17
+ - @khanacademy/wonder-blocks-clickable@2.2.7
18
+ - @khanacademy/wonder-blocks-icon@1.2.28
19
+ - @khanacademy/wonder-blocks-layout@1.4.10
20
+ - @khanacademy/wonder-blocks-typography@1.1.32
21
+
22
+ ## 2.4.3
23
+
24
+ ### Patch Changes
25
+
26
+ - @khanacademy/wonder-blocks-clickable@2.2.6
27
+ - @khanacademy/wonder-blocks-core@4.3.1
28
+ - @khanacademy/wonder-blocks-icon@1.2.27
29
+ - @khanacademy/wonder-blocks-layout@1.4.9
30
+ - @khanacademy/wonder-blocks-typography@1.1.31
31
+
3
32
  ## 2.4.2
4
33
 
5
34
  ### Patch Changes
package/dist/es/index.js CHANGED
@@ -24,16 +24,11 @@ const StyledInput$1 = addStyle("input");
24
24
  const checkboxCheck = {
25
25
  small: "M11.263 4.324a1 1 0 1 1 1.474 1.352l-5.5 6a1 1 0 0 1-1.505-.036l-2.5-3a1 1 0 1 1 1.536-1.28L6.536 9.48l4.727-5.157z"
26
26
  };
27
- /**
28
- * The internal stateless ☑️ Checkbox
29
- */
30
-
31
27
  class CheckboxCore extends React.Component {
32
28
  constructor(...args) {
33
29
  super(...args);
34
30
 
35
31
  this.handleChange = () => {
36
- // Empty because change is handled by ClickableBehavior
37
32
  return;
38
33
  };
39
34
  }
@@ -59,18 +54,16 @@ class CheckboxCore extends React.Component {
59
54
  const props = {
60
55
  "data-test-id": testId
61
56
  };
62
- return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(StyledInput$1, _extends({}, sharedProps, {
57
+ return React.createElement(React.Fragment, null, React.createElement(StyledInput$1, _extends({}, sharedProps, {
63
58
  type: "checkbox",
64
59
  "aria-invalid": error,
65
60
  checked: checked,
66
61
  disabled: disabled,
67
62
  id: id,
68
- name: groupName // Need to specify because this is a controlled React form
69
- // component, but we handle the click via ClickableBehavior
70
- ,
63
+ name: groupName,
71
64
  onChange: this.handleChange,
72
65
  style: defaultStyle
73
- }, props)), checked && /*#__PURE__*/React.createElement(Icon, {
66
+ }, props)), checked && React.createElement(Icon, {
74
67
  color: disabled ? offBlack32$1 : white$1,
75
68
  icon: checkboxCheck,
76
69
  size: "small",
@@ -81,7 +74,6 @@ class CheckboxCore extends React.Component {
81
74
  }
82
75
  const size$1 = 16;
83
76
  const sharedStyles$1 = StyleSheet.create({
84
- // Reset the default styled input element
85
77
  inputReset: {
86
78
  appearance: "none",
87
79
  WebkitAppearance: "none",
@@ -129,7 +121,6 @@ const colors$1 = {
129
121
  const styles$5 = {};
130
122
 
131
123
  const _generateStyles$1 = (checked, error) => {
132
- // "hash" the parameters
133
124
  const styleKey = `${String(checked)}-${String(error)}`;
134
125
 
135
126
  if (styles$5[styleKey]) {
@@ -187,16 +178,11 @@ const {
187
178
  offBlack50
188
179
  } = Color;
189
180
  const StyledInput = addStyle("input");
190
- /**
191
- * The internal stateless 🔘 Radio button
192
- */
193
-
194
181
  class RadioCore extends React.Component {
195
182
  constructor(...args) {
196
183
  super(...args);
197
184
 
198
185
  this.handleChange = () => {
199
- // Empty because change is handled by ClickableBehavior
200
186
  return;
201
187
  };
202
188
  }
@@ -222,25 +208,22 @@ class RadioCore extends React.Component {
222
208
  const props = {
223
209
  "data-test-id": testId
224
210
  };
225
- return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(StyledInput, _extends({}, sharedProps, {
211
+ return React.createElement(React.Fragment, null, React.createElement(StyledInput, _extends({}, sharedProps, {
226
212
  type: "radio",
227
213
  "aria-invalid": error,
228
214
  checked: checked,
229
215
  disabled: disabled,
230
216
  id: id,
231
- name: groupName // Need to specify because this is a controlled React form
232
- // component, but we handle the click via ClickableBehavior
233
- ,
217
+ name: groupName,
234
218
  onChange: this.handleChange,
235
219
  style: defaultStyle
236
- }, props)), disabled && checked && /*#__PURE__*/React.createElement("span", {
220
+ }, props)), disabled && checked && React.createElement("span", {
237
221
  style: disabledChecked
238
222
  }));
239
223
  }
240
224
 
241
225
  }
242
- const size = 16; // circle with a different color. Here, we add that center circle. // If the checkbox is disabled and selected, it has a border but also an inner
243
-
226
+ const size = 16;
244
227
  const disabledChecked = {
245
228
  position: "absolute",
246
229
  top: size / 4,
@@ -251,7 +234,6 @@ const disabledChecked = {
251
234
  backgroundColor: offBlack32
252
235
  };
253
236
  const sharedStyles = StyleSheet.create({
254
- // Reset the default styled input element
255
237
  inputReset: {
256
238
  appearance: "none",
257
239
  WebkitAppearance: "none",
@@ -295,7 +277,6 @@ const colors = {
295
277
  const styles$4 = {};
296
278
 
297
279
  const _generateStyles = (checked, error) => {
298
- // "hash" the parameters
299
280
  const styleKey = `${String(checked)}-${String(error)}`;
300
281
 
301
282
  if (styles$4[styleKey]) {
@@ -344,22 +325,11 @@ const _generateStyles = (checked, error) => {
344
325
  };
345
326
 
346
327
  const _excluded$2 = ["label", "description", "onChange", "style", "className", "variant"];
347
-
348
- /**
349
- * This is a potentially labeled 🔘 or ☑️ item. This is an internal component
350
- * that's wrapped by Checkbox and Radio. Choice is a wrapper for Checkbox and
351
- * Radio with many of its props auto-populated, to be used with CheckboxGroup
352
- * and RadioGroup. This design allows for more explicit prop typing. For
353
- * example, we can make onChange a required prop on Checkbox but not on Choice
354
- * (because for Choice, that prop would be auto-populated by CheckboxGroup).
355
- */
356
328
  class ChoiceInternal extends React.Component {
357
329
  constructor(...args) {
358
330
  super(...args);
359
331
 
360
332
  this.handleLabelClick = event => {
361
- // Browsers automatically use the for attribute to select the input,
362
- // but we use ClickableBehavior to handle this.
363
333
  event.preventDefault();
364
334
  };
365
335
 
@@ -368,7 +338,7 @@ class ChoiceInternal extends React.Component {
368
338
  checked,
369
339
  onChange,
370
340
  variant
371
- } = this.props; // Radio buttons cannot be unchecked
341
+ } = this.props;
372
342
 
373
343
  if (variant === "radio" && checked) {
374
344
  return;
@@ -392,9 +362,9 @@ class ChoiceInternal extends React.Component {
392
362
  id,
393
363
  label
394
364
  } = this.props;
395
- return /*#__PURE__*/React.createElement(LabelMedium, {
365
+ return React.createElement(LabelMedium, {
396
366
  style: [styles$3.label, disabled && styles$3.disabledLabel]
397
- }, /*#__PURE__*/React.createElement("label", {
367
+ }, React.createElement("label", {
398
368
  htmlFor: id,
399
369
  onClick: this.handleLabelClick
400
370
  }, label));
@@ -404,7 +374,7 @@ class ChoiceInternal extends React.Component {
404
374
  const {
405
375
  description
406
376
  } = this.props;
407
- return /*#__PURE__*/React.createElement(LabelSmall, {
377
+ return React.createElement(LabelSmall, {
408
378
  style: styles$3.description,
409
379
  id: id
410
380
  }, description);
@@ -423,29 +393,26 @@ class ChoiceInternal extends React.Component {
423
393
 
424
394
  const ChoiceCore = this.getChoiceCoreComponent();
425
395
  const ClickableBehavior = getClickableBehavior();
426
- return /*#__PURE__*/React.createElement(UniqueIDProvider, {
396
+ return React.createElement(UniqueIDProvider, {
427
397
  mockOnFirstRender: true,
428
398
  scope: "choice"
429
399
  }, ids => {
430
400
  const descriptionId = description && ids.get("description");
431
- return /*#__PURE__*/React.createElement(View, {
401
+ return React.createElement(View, {
432
402
  style: style,
433
403
  className: className
434
- }, /*#__PURE__*/React.createElement(ClickableBehavior, {
404
+ }, React.createElement(ClickableBehavior, {
435
405
  disabled: coreProps.disabled,
436
406
  onClick: this.handleClick,
437
407
  role: variant
438
408
  }, (state, childrenProps) => {
439
- return /*#__PURE__*/React.createElement(View, _extends({
409
+ return React.createElement(View, _extends({
440
410
  style: styles$3.wrapper
441
411
  }, childrenProps, {
442
- // We are resetting the tabIndex=0 from handlers
443
- // because the ChoiceCore component will receive
444
- // focus on basis of it being an input element.
445
412
  tabIndex: -1
446
- }), /*#__PURE__*/React.createElement(ChoiceCore, _extends({}, coreProps, state, {
413
+ }), React.createElement(ChoiceCore, _extends({}, coreProps, state, {
447
414
  "aria-describedby": descriptionId
448
- })), /*#__PURE__*/React.createElement(Strut, {
415
+ })), React.createElement(Strut, {
449
416
  size: Spacing.xSmall_8
450
417
  }), label && this.getLabel());
451
418
  }), description && this.getDescription(descriptionId));
@@ -466,33 +433,21 @@ const styles$3 = StyleSheet.create({
466
433
  },
467
434
  label: {
468
435
  userSelect: "none",
469
- // NOTE: The checkbox/radio button (height 16px) should be center
470
- // aligned with the first line of the label. However, LabelMedium has a
471
- // declared line height of 20px, so we need to adjust the top to get the
472
- // desired alignment.
473
436
  marginTop: -2
474
437
  },
475
438
  disabledLabel: {
476
439
  color: Color.offBlack32
477
440
  },
478
441
  description: {
479
- // 16 for icon + 8 for spacing strut
480
442
  marginLeft: Spacing.medium_16 + Spacing.xSmall_8,
481
443
  marginTop: Spacing.xxxSmall_4,
482
444
  color: Color.offBlack64
483
445
  }
484
446
  });
485
447
 
486
- /**
487
- * ☑️ A nicely styled checkbox for all your checking needs. Can optionally take
488
- * label and description props.
489
- *
490
- * If you want a whole group of Checkbox[es] that are related, see the Choice
491
- * and CheckboxGroup components.
492
- */
493
448
  class Checkbox extends React.Component {
494
449
  render() {
495
- return /*#__PURE__*/React.createElement(ChoiceInternal, _extends({
450
+ return React.createElement(ChoiceInternal, _extends({
496
451
  variant: "checkbox"
497
452
  }, this.props));
498
453
  }
@@ -503,16 +458,9 @@ Checkbox.defaultProps = {
503
458
  error: false
504
459
  };
505
460
 
506
- /**
507
- * 🔘 A nicely styled radio button for all your non-AMFM radio button needs. Can
508
- * optionally take label and description props.
509
- *
510
- * This component should not really be used by itself because radio buttons are
511
- * often grouped together. See RadioGroup.
512
- */
513
461
  class Radio extends React.Component {
514
462
  render() {
515
- return /*#__PURE__*/React.createElement(ChoiceInternal, _extends({
463
+ return React.createElement(ChoiceInternal, _extends({
516
464
  variant: "radio"
517
465
  }, this.props));
518
466
  }
@@ -524,16 +472,6 @@ Radio.defaultProps = {
524
472
  };
525
473
 
526
474
  const _excluded$1 = ["value", "variant"];
527
-
528
- /**
529
- * This is a labeled 🔘 or ☑️ item. Choice is meant to be used as children of
530
- * CheckboxGroup and RadioGroup because many of its props are auto-populated
531
- * and not shown in the documentation here. See those components for usage
532
- * examples.
533
- *
534
- * If you wish to use just a single field, use Checkbox or Radio with the
535
- * optional label and description props.
536
- */
537
475
  class Choice extends React.Component {
538
476
  getChoiceComponent(variant) {
539
477
  if (variant === "checkbox") {
@@ -544,8 +482,6 @@ class Choice extends React.Component {
544
482
  }
545
483
 
546
484
  render() {
547
- // we don't need this going into the ChoiceComponent
548
- // eslint-disable-next-line no-unused-vars
549
485
  const _this$props = this.props,
550
486
  {
551
487
  variant
@@ -553,7 +489,7 @@ class Choice extends React.Component {
553
489
  remainingProps = _objectWithoutPropertiesLoose(_this$props, _excluded$1);
554
490
 
555
491
  const ChoiceComponent = this.getChoiceComponent(variant);
556
- return /*#__PURE__*/React.createElement(ChoiceComponent, remainingProps);
492
+ return React.createElement(ChoiceComponent, remainingProps);
557
493
  }
558
494
 
559
495
  }
@@ -587,13 +523,6 @@ const styles$2 = StyleSheet.create({
587
523
 
588
524
  const StyledFieldset$1 = addStyle("fieldset");
589
525
  const StyledLegend$1 = addStyle("legend");
590
- /**
591
- * A checkbox group allows multiple selection. This component auto-populates
592
- * many props for its children Choice components. The Choice component is
593
- * exposed for the user to apply custom styles or to indicate which choices are
594
- * disabled.
595
- */
596
-
597
526
  class CheckboxGroup extends React.Component {
598
527
  handleChange(changedValue, originalCheckedState) {
599
528
  const {
@@ -621,18 +550,18 @@ class CheckboxGroup extends React.Component {
621
550
  style,
622
551
  testId
623
552
  } = this.props;
624
- return /*#__PURE__*/React.createElement(StyledFieldset$1, {
553
+ return React.createElement(StyledFieldset$1, {
625
554
  "data-test-id": testId,
626
555
  style: styles$2.fieldset
627
- }, /*#__PURE__*/React.createElement(View, {
556
+ }, React.createElement(View, {
628
557
  style: style
629
- }, typeof label === "string" ? /*#__PURE__*/React.createElement(StyledLegend$1, {
558
+ }, typeof label === "string" ? React.createElement(StyledLegend$1, {
630
559
  style: styles$2.legend
631
- }, /*#__PURE__*/React.createElement(LabelMedium, null, label)) : label && label, typeof description === "string" ? /*#__PURE__*/React.createElement(LabelSmall, {
560
+ }, React.createElement(LabelMedium, null, label)) : label && label, typeof description === "string" ? React.createElement(LabelSmall, {
632
561
  style: styles$2.description
633
- }, description) : description && description, errorMessage && /*#__PURE__*/React.createElement(LabelSmall, {
562
+ }, description) : description && description, errorMessage && React.createElement(LabelSmall, {
634
563
  style: styles$2.error
635
- }, errorMessage), (label || description || errorMessage) && /*#__PURE__*/React.createElement(Strut, {
564
+ }, errorMessage), (label || description || errorMessage) && React.createElement(Strut, {
636
565
  size: Spacing.small_12
637
566
  }), React.Children.map(children, (child, index) => {
638
567
  const {
@@ -640,7 +569,7 @@ class CheckboxGroup extends React.Component {
640
569
  value
641
570
  } = child.props;
642
571
  const checked = selectedValues.includes(value);
643
- return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.cloneElement(child, {
572
+ return React.createElement(React.Fragment, null, React.cloneElement(child, {
644
573
  checked: checked,
645
574
  error: !!errorMessage,
646
575
  groupName: groupName,
@@ -657,15 +586,6 @@ class CheckboxGroup extends React.Component {
657
586
 
658
587
  const StyledFieldset = addStyle("fieldset");
659
588
  const StyledLegend = addStyle("legend");
660
- /**
661
- * A radio group allows only single selection. Like CheckboxGroup, this
662
- * component auto-populates many props for its children Choice components. The
663
- * Choice component is exposed for the user to apply custom styles or to
664
- * indicate which choices are disabled. The use of the groupName prop is
665
- * important to maintain expected keyboard navigation behavior for
666
- * accessibility.
667
- */
668
-
669
589
  class RadioGroup extends React.Component {
670
590
  handleChange(changedValue) {
671
591
  this.props.onChange(changedValue);
@@ -682,18 +602,18 @@ class RadioGroup extends React.Component {
682
602
  style,
683
603
  testId
684
604
  } = this.props;
685
- return /*#__PURE__*/React.createElement(StyledFieldset, {
605
+ return React.createElement(StyledFieldset, {
686
606
  "data-test-id": testId,
687
607
  style: styles$2.fieldset
688
- }, /*#__PURE__*/React.createElement(View, {
608
+ }, React.createElement(View, {
689
609
  style: style
690
- }, label && /*#__PURE__*/React.createElement(StyledLegend, {
610
+ }, label && React.createElement(StyledLegend, {
691
611
  style: styles$2.legend
692
- }, /*#__PURE__*/React.createElement(LabelMedium, null, label)), description && /*#__PURE__*/React.createElement(LabelSmall, {
612
+ }, React.createElement(LabelMedium, null, label)), description && React.createElement(LabelSmall, {
693
613
  style: styles$2.description
694
- }, description), errorMessage && /*#__PURE__*/React.createElement(LabelSmall, {
614
+ }, description), errorMessage && React.createElement(LabelSmall, {
695
615
  style: styles$2.error
696
- }, errorMessage), (label || description || errorMessage) && /*#__PURE__*/React.createElement(Strut, {
616
+ }, errorMessage), (label || description || errorMessage) && React.createElement(Strut, {
697
617
  size: Spacing.small_12
698
618
  }), React.Children.map(children, (child, index) => {
699
619
  const {
@@ -701,7 +621,7 @@ class RadioGroup extends React.Component {
701
621
  value
702
622
  } = child.props;
703
623
  const checked = selectedValue === value;
704
- return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.cloneElement(child, {
624
+ return React.createElement(React.Fragment, null, React.cloneElement(child, {
705
625
  checked: checked,
706
626
  error: !!errorMessage,
707
627
  groupName: groupName,
@@ -719,11 +639,6 @@ class RadioGroup extends React.Component {
719
639
  const _excluded = ["id", "type", "value", "disabled", "onKeyDown", "placeholder", "light", "style", "testId", "readOnly", "autoComplete", "forwardedRef", "onFocus", "onBlur", "onValidate", "validate", "onChange", "required"];
720
640
  const defaultErrorMessage = "This field is required.";
721
641
 
722
- // TODO(WB-1081): Change class name back to TextField after Styleguidist is gone.
723
-
724
- /**
725
- * A TextField is an element used to accept a single line of text from the user.
726
- */
727
642
  class TextFieldInternal extends React.Component {
728
643
  constructor(props) {
729
644
  super(props);
@@ -797,7 +712,6 @@ class TextFieldInternal extends React.Component {
797
712
  };
798
713
 
799
714
  if (props.validate && props.value !== "") {
800
- // Ensures error is updated on unmounted server-side renders
801
715
  this.state.error = props.validate(props.value) || null;
802
716
  }
803
717
  }
@@ -826,9 +740,8 @@ class TextFieldInternal extends React.Component {
826
740
  } = _this$props,
827
741
  otherProps = _objectWithoutPropertiesLoose(_this$props, _excluded);
828
742
 
829
- return /*#__PURE__*/React.createElement("input", _extends({
830
- className: css([styles$1.input, styles$6.LabelMedium, styles$1.default, // Prioritizes disabled, then focused, then error (if any)
831
- disabled ? styles$1.disabled : this.state.focused ? [styles$1.focused, light && styles$1.defaultLight] : this.state.error && [styles$1.error, light && styles$1.errorLight], style && style]),
743
+ return React.createElement("input", _extends({
744
+ className: css([styles$1.input, styles$6.LabelMedium, styles$1.default, disabled ? styles$1.disabled : this.state.focused ? [styles$1.focused, light && styles$1.defaultLight] : this.state.error && [styles$1.error, light && styles$1.errorLight], style && style]),
832
745
  id: id,
833
746
  type: type,
834
747
  placeholder: placeholder,
@@ -902,16 +815,11 @@ const styles$1 = StyleSheet.create({
902
815
  boxShadow: `0px 0px 0px 1px ${Color.red}, 0px 0px 0px 2px ${Color.white}`
903
816
  }
904
817
  });
905
- const TextField = /*#__PURE__*/React.forwardRef((props, ref) => /*#__PURE__*/React.createElement(TextFieldInternal, _extends({}, props, {
818
+ const TextField = React.forwardRef((props, ref) => React.createElement(TextFieldInternal, _extends({}, props, {
906
819
  forwardedRef: ref
907
820
  })));
908
821
 
909
822
  const StyledSpan = addStyle("span");
910
- /**
911
- * A FieldHeading is an element that provides a label, description, and error element
912
- * to present better context and hints to any type of form field component.
913
- */
914
-
915
823
  class FieldHeading extends React.Component {
916
824
  renderLabel() {
917
825
  const {
@@ -920,16 +828,16 @@ class FieldHeading extends React.Component {
920
828
  required,
921
829
  testId
922
830
  } = this.props;
923
- const requiredIcon = /*#__PURE__*/React.createElement(StyledSpan, {
831
+ const requiredIcon = React.createElement(StyledSpan, {
924
832
  style: styles.required,
925
833
  "aria-hidden": true
926
834
  }, " ", "*");
927
- return /*#__PURE__*/React.createElement(React.Fragment, null, typeof label === "string" ? /*#__PURE__*/React.createElement(LabelMedium, {
835
+ return React.createElement(React.Fragment, null, typeof label === "string" ? React.createElement(LabelMedium, {
928
836
  style: styles.label,
929
837
  tag: "label",
930
838
  htmlFor: id && `${id}-field`,
931
839
  testId: testId && `${testId}-label`
932
- }, label, required && requiredIcon) : label, /*#__PURE__*/React.createElement(Strut, {
840
+ }, label, required && requiredIcon) : label, React.createElement(Strut, {
933
841
  size: Spacing.xxxSmall_4
934
842
  }));
935
843
  }
@@ -944,10 +852,10 @@ class FieldHeading extends React.Component {
944
852
  return null;
945
853
  }
946
854
 
947
- return /*#__PURE__*/React.createElement(React.Fragment, null, typeof description === "string" ? /*#__PURE__*/React.createElement(LabelSmall, {
855
+ return React.createElement(React.Fragment, null, typeof description === "string" ? React.createElement(LabelSmall, {
948
856
  style: styles.description,
949
857
  testId: testId && `${testId}-description`
950
- }, description) : description, /*#__PURE__*/React.createElement(Strut, {
858
+ }, description) : description, React.createElement(Strut, {
951
859
  size: Spacing.xxxSmall_4
952
860
  }));
953
861
  }
@@ -963,9 +871,9 @@ class FieldHeading extends React.Component {
963
871
  return null;
964
872
  }
965
873
 
966
- return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Strut, {
874
+ return React.createElement(React.Fragment, null, React.createElement(Strut, {
967
875
  size: Spacing.small_12
968
- }), typeof error === "string" ? /*#__PURE__*/React.createElement(LabelSmall, {
876
+ }), typeof error === "string" ? React.createElement(LabelSmall, {
969
877
  style: styles.error,
970
878
  role: "alert",
971
879
  id: id && `${id}-error`,
@@ -978,9 +886,9 @@ class FieldHeading extends React.Component {
978
886
  field,
979
887
  style
980
888
  } = this.props;
981
- return /*#__PURE__*/React.createElement(View, {
889
+ return React.createElement(View, {
982
890
  style: style
983
- }, this.renderLabel(), this.maybeRenderDescription(), /*#__PURE__*/React.createElement(Strut, {
891
+ }, this.renderLabel(), this.maybeRenderDescription(), React.createElement(Strut, {
984
892
  size: Spacing.xSmall_8
985
893
  }), field, this.maybeRenderError());
986
894
  }
@@ -1001,12 +909,6 @@ const styles = StyleSheet.create({
1001
909
  }
1002
910
  });
1003
911
 
1004
- // TODO(WB-1081): Change class name back to LabeledTextField after Styleguidist is gone.
1005
-
1006
- /**
1007
- * A LabeledTextField is an element used to accept a single line of text
1008
- * from the user paired with a label, description, and error field elements.
1009
- */
1010
912
  class LabeledTextFieldInternal extends React.Component {
1011
913
  constructor(props) {
1012
914
  super(props);
@@ -1077,14 +979,14 @@ class LabeledTextFieldInternal extends React.Component {
1077
979
  forwardedRef,
1078
980
  ariaDescribedby
1079
981
  } = this.props;
1080
- return /*#__PURE__*/React.createElement(IDProvider, {
982
+ return React.createElement(IDProvider, {
1081
983
  id: id,
1082
984
  scope: "labeled-text-field"
1083
- }, uniqueId => /*#__PURE__*/React.createElement(FieldHeading, {
985
+ }, uniqueId => React.createElement(FieldHeading, {
1084
986
  id: uniqueId,
1085
987
  testId: testId,
1086
988
  style: style,
1087
- field: /*#__PURE__*/React.createElement(TextField, {
989
+ field: React.createElement(TextField, {
1088
990
  id: `${uniqueId}-field`,
1089
991
  "aria-describedby": ariaDescribedby ? ariaDescribedby : `${uniqueId}-error`,
1090
992
  "aria-invalid": this.state.error ? "true" : "false",
@@ -1120,7 +1022,7 @@ LabeledTextFieldInternal.defaultProps = {
1120
1022
  disabled: false,
1121
1023
  light: false
1122
1024
  };
1123
- const LabeledTextField = /*#__PURE__*/React.forwardRef((props, ref) => /*#__PURE__*/React.createElement(LabeledTextFieldInternal, _extends({}, props, {
1025
+ const LabeledTextField = React.forwardRef((props, ref) => React.createElement(LabeledTextFieldInternal, _extends({}, props, {
1124
1026
  forwardedRef: ref
1125
1027
  })));
1126
1028
 
package/dist/index.js CHANGED
@@ -182,8 +182,22 @@ function _extends() { _extends = Object.assign || function (target) { for (var i
182
182
  * ☑️ A nicely styled checkbox for all your checking needs. Can optionally take
183
183
  * label and description props.
184
184
  *
185
+ * If used by itself, a checkbox provides two options - checked and unchecked.
186
+ * A group of checkboxes can be used to allow a user to select multiple values
187
+ * from a list of options.
188
+ *
185
189
  * If you want a whole group of Checkbox[es] that are related, see the Choice
186
190
  * and CheckboxGroup components.
191
+ *
192
+ * ### Usage
193
+ *
194
+ * ```jsx
195
+ * import {Checkbox} from "@khanacademy/wonder-blocks-form";
196
+ *
197
+ * const [checked, setChecked] = React.useState(false);
198
+ *
199
+ * <Checkbox checked={checked} onChange={setChecked} />
200
+ * ```
187
201
  */
188
202
  class Checkbox extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
189
203
  render() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khanacademy/wonder-blocks-form",
3
- "version": "2.4.2",
3
+ "version": "2.4.5",
4
4
  "design": "v1",
5
5
  "description": "Form components for Wonder Blocks.",
6
6
  "main": "dist/index.js",
@@ -16,19 +16,19 @@
16
16
  },
17
17
  "dependencies": {
18
18
  "@babel/runtime": "^7.16.3",
19
- "@khanacademy/wonder-blocks-clickable": "^2.2.5",
19
+ "@khanacademy/wonder-blocks-clickable": "^2.2.7",
20
20
  "@khanacademy/wonder-blocks-color": "^1.1.20",
21
- "@khanacademy/wonder-blocks-core": "^4.3.0",
22
- "@khanacademy/wonder-blocks-icon": "^1.2.26",
23
- "@khanacademy/wonder-blocks-layout": "^1.4.8",
21
+ "@khanacademy/wonder-blocks-core": "^4.3.2",
22
+ "@khanacademy/wonder-blocks-icon": "^1.2.29",
23
+ "@khanacademy/wonder-blocks-layout": "^1.4.10",
24
24
  "@khanacademy/wonder-blocks-spacing": "^3.0.5",
25
- "@khanacademy/wonder-blocks-typography": "^1.1.30"
25
+ "@khanacademy/wonder-blocks-typography": "^1.1.32"
26
26
  },
27
27
  "peerDependencies": {
28
28
  "aphrodite": "^1.2.5",
29
29
  "react": "16.14.0"
30
30
  },
31
31
  "devDependencies": {
32
- "wb-dev-build-settings": "^0.3.0"
32
+ "wb-dev-build-settings": "^0.4.0"
33
33
  }
34
34
  }
@@ -0,0 +1,147 @@
1
+ import {Meta, Story, Canvas} from "@storybook/addon-docs";
2
+ import {StyleSheet} from "aphrodite";
3
+
4
+ import Color from "@khanacademy/wonder-blocks-color";
5
+ import {Checkbox} from "@khanacademy/wonder-blocks-form";
6
+ import {LabelSmall} from "@khanacademy/wonder-blocks-typography";
7
+
8
+ <Meta
9
+ title="Form/Checkbox/Accessibility"
10
+ component={Checkbox}
11
+ parameters={{
12
+ previewTabs: {
13
+ canvas: {hidden: true},
14
+ },
15
+ viewMode: "docs",
16
+ chromatic: {
17
+ // Disables chromatic testing for these stories.
18
+ disableSnapshot: true,
19
+ },
20
+ }}
21
+ />
22
+
23
+ export const ErrorTemplate = (args) => {
24
+ const [checked, setChecked] = React.useState(false);
25
+ const errorState = !checked;
26
+ return (
27
+ <View>
28
+ <Checkbox
29
+ checked={checked}
30
+ onChange={setChecked}
31
+ error={errorState}
32
+ aria-describedby={errorState && "error-message"}
33
+ aria-required={true}
34
+ {...args}
35
+ />
36
+ {errorState && (
37
+ <LabelSmall style={styles.error} id="error-message">
38
+ You must agree to the terms to continue
39
+ </LabelSmall>
40
+ )}
41
+ </View>
42
+ );
43
+ };
44
+
45
+ export const DisabledTemplate = (args) => {
46
+ const [checked, setChecked] = React.useState(false);
47
+ const errorState = !checked;
48
+ return (
49
+ <Checkbox
50
+ checked={checked}
51
+ onChange={setChecked}
52
+ label="Some setting"
53
+ description="You do not have permission to change this setting"
54
+ disabled={true}
55
+ />
56
+ );
57
+ };
58
+
59
+ ## Accessibility
60
+
61
+ ### ARIA
62
+
63
+ `Checkbox` can take in all ARIA props defined in Wonder Blocks Core types.
64
+
65
+ Elements with role `"checkbox"` can have an `aria-checked` property that
66
+ exposes the checked state to assistive technology. The dev does not have
67
+ to worry about this because the Wonder Blocks Checkbox component is an
68
+ `input` element with type `"checkbox"`, as this has built-in semantics and
69
+ does not require ARIA.
70
+
71
+ The current implementation of `Checkbox` uses `aria-describedby` with the
72
+ label and description that may be passed in as props.
73
+
74
+ See the Error section for information about `aria-invalid` and
75
+ `aria-required`.
76
+
77
+ ### Error state
78
+
79
+ The Wonder Blocks `Checkbox` component takes an `error` boolean prop. Setting
80
+ this prop to true will set `aria-invalid` to true, and the color of the
81
+ checkbox to red.
82
+
83
+ When a form input is invalid, the user should provide a reason for why
84
+ this is.
85
+
86
+ Generally, it is also suggested this is the validation error message is
87
+ passed to the checkbox's `aria-describedby` prop so assistive tech can
88
+ read it. However, this is not possible with the current implementation of
89
+ the Wonder Blocks Form Checkbox component.
90
+
91
+ The error state can be used to signal that a required checkbox has not been
92
+ checked. In cases where a checkbox is required, the checkbox component should
93
+ set the `aria-required` prop to true for assistive tech.
94
+ There should also be some sort of visual indication that checking
95
+ the box is required, such as a "Required" label or an asterisk.
96
+
97
+ <Canvas>
98
+ <Story
99
+ name="Error state"
100
+ args={{
101
+ label: "I accept the terms and conditions",
102
+ }}
103
+ >
104
+ {ErrorTemplate.bind({})}
105
+ </Story>
106
+ </Canvas>
107
+
108
+ ### Disabled state
109
+
110
+ The Wonder Blocks `Checkbox` compoenent takes a `disabled` boolean prop.
111
+ This sets the underlying `input` element's `disabled` prop to `true`.
112
+ This makes is so that the checkbox is not interactable. Also, assistive
113
+ tech will indicated that the checkbox is dimmed.
114
+
115
+ A user will not be able to navigate to the checkbox with a keyboard.
116
+ Screen reader users will be able to navigate to the checkbox with
117
+ screen reader controls.
118
+
119
+ It is suggested that if an element is disabled, an explanation as to why
120
+ should to provided somewhere.
121
+
122
+ <Canvas>
123
+ <Story name="Disabled state">{DisabledTemplate.bind({})}</Story>
124
+ </Canvas>
125
+
126
+ ### Keyboard Interaction
127
+
128
+ If a checkbox is not disabled, a user can tab to it using standard
129
+ keyboard navigation. The Space key toggles the checked state of the checkbox.
130
+
131
+ Note the the Space key triggers the `onChange` function of the
132
+ Wonder Blocks Checkbox component. If the user does not specify an `onChange`
133
+ funciton prop that in turn updates the value of `checked`, neither clicking
134
+ nor the Space key will toggle the Checkbox.
135
+
136
+ ### References
137
+
138
+ - [Accessible validation of checkbox and radiobutton groups](https://blog.tenon.io/accessible-validation-of-checkbox-and-radiobutton-groups/)
139
+ - [HTML: Validating a checkbox with HTML5](https://www.the-art-of-web.com/html/html5-checkbox-required/#example1)
140
+ - [aria-checked MDN Docs](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-checked)
141
+ - [ARIA: checkbox role MDN Docs](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/checkbox_role)
142
+
143
+ export const styles = StyleSheet.create({
144
+ error: {
145
+ color: Color.red,
146
+ },
147
+ });
@@ -0,0 +1,167 @@
1
+ // @flow
2
+ import * as React from "react";
3
+ import {StyleSheet} from "aphrodite";
4
+
5
+ import {View} from "@khanacademy/wonder-blocks-core";
6
+ import {LabelMedium, LabelSmall} from "@khanacademy/wonder-blocks-typography";
7
+ import type {StoryComponentType} from "@storybook/react";
8
+
9
+ import Checkbox from "../checkbox.js";
10
+
11
+ import ComponentInfo from "../../../../../.storybook/components/component-info.js";
12
+ import {name, version} from "../../../package.json";
13
+
14
+ export default {
15
+ title: "Form / Checkbox",
16
+ component: Checkbox,
17
+ parameters: {
18
+ componentSubtitle: ((
19
+ <ComponentInfo name={name} version={version} />
20
+ ): any),
21
+ },
22
+ };
23
+
24
+ export const Default: StoryComponentType = (args) => <Checkbox {...args} />;
25
+
26
+ Default.args = {
27
+ checked: false,
28
+ onChange: () => {},
29
+ };
30
+
31
+ Default.parameters = {
32
+ chromatic: {
33
+ // We already have screenshots of another story that covers
34
+ // this and more cases.
35
+ disableSnapshot: true,
36
+ },
37
+ };
38
+
39
+ export const Controlled: StoryComponentType = () => {
40
+ const [checked, setChecked] = React.useState(false);
41
+ return <Checkbox checked={checked} onChange={setChecked} />;
42
+ };
43
+
44
+ Controlled.parameters = {
45
+ chromatic: {
46
+ // Disabling because this doesn't test visuals, its for testing
47
+ // that `state` works as expected.
48
+ disableSnapshot: true,
49
+ },
50
+ docs: {
51
+ storyDescription:
52
+ "Use state to keep track of whether the checkbox is checked or not",
53
+ },
54
+ };
55
+
56
+ export const Variants: StoryComponentType = () => (
57
+ <View style={styles.row}>
58
+ <Checkbox
59
+ error={false}
60
+ checked={false}
61
+ style={styles.marginRight}
62
+ onChange={() => {}}
63
+ />
64
+ <Checkbox
65
+ error={false}
66
+ checked={true}
67
+ style={styles.marginRight}
68
+ onChange={() => {}}
69
+ />
70
+ <Checkbox
71
+ error={true}
72
+ checked={false}
73
+ style={styles.marginRight}
74
+ onChange={() => {}}
75
+ />
76
+ <Checkbox
77
+ error={true}
78
+ checked={true}
79
+ style={styles.marginRight}
80
+ onChange={() => {}}
81
+ />
82
+ <Checkbox
83
+ disabled={true}
84
+ checked={false}
85
+ style={styles.marginRight}
86
+ onChange={() => {}}
87
+ />
88
+ <Checkbox
89
+ disabled={true}
90
+ checked={true}
91
+ style={styles.marginRight}
92
+ onChange={() => {}}
93
+ />
94
+ </View>
95
+ );
96
+
97
+ Variants.parameters = {
98
+ docs: {
99
+ storyDescription:
100
+ "The checkbox has various styles for clickable states. Here are sets of default checkboxes, checkboxes in an error state, and disabled checkboxes.",
101
+ },
102
+ };
103
+
104
+ export const WithLabel: StoryComponentType = () => {
105
+ const [checked, setChecked] = React.useState(false);
106
+
107
+ return (
108
+ <Checkbox
109
+ label="Receive assignment reminders for Algebra"
110
+ description="You will receive a reminder 24 hours before each deadline"
111
+ checked={checked}
112
+ onChange={setChecked}
113
+ />
114
+ );
115
+ };
116
+
117
+ WithLabel.parameters = {
118
+ docs: {
119
+ storyDescription:
120
+ "The checkbox can have a optional label and description. This allows it to be used as a settings-like item. The user of this component is responsible for keeping track of checked state and providing an onChange callback.",
121
+ },
122
+ };
123
+
124
+ export const AdditionalClickTarget: StoryComponentType = () => {
125
+ const [checked, setChecked] = React.useState(false);
126
+ const headingText = "Functions";
127
+ const descriptionText = `A great cook knows how to take basic
128
+ ingredients and prepare a delicious meal. In this topic, you will
129
+ become function-chefs! You will learn how to combine functions
130
+ with arithmetic operations and how to compose functions.`;
131
+
132
+ return (
133
+ <View style={styles.wrapper}>
134
+ <View style={styles.topic}>
135
+ <label htmlFor="topic-123">
136
+ <LabelMedium>{headingText}</LabelMedium>
137
+ </label>
138
+ <LabelSmall>{descriptionText}</LabelSmall>
139
+ </View>
140
+ <Checkbox checked={checked} id="topic-123" onChange={setChecked} />
141
+ </View>
142
+ );
143
+ };
144
+
145
+ AdditionalClickTarget.parameters = {
146
+ docs: {
147
+ storyDescription:
148
+ "Sometimes one may wish to use a checkbox in a different context (label may not be right next to the checkbox), like in this example content item. Use a `<label htmlFor={id}>` element where the id matches the `id` prop of the Checkbox. This is for accessibility purposes, and doing this also automatically makes the label a click target for the checkbox.",
149
+ },
150
+ };
151
+
152
+ const styles = StyleSheet.create({
153
+ row: {
154
+ flexDirection: "row",
155
+ },
156
+ marginRight: {
157
+ marginRight: 16,
158
+ },
159
+ wrapper: {
160
+ flexDirection: "row",
161
+ alignItems: "center",
162
+ justifyContent: "space-evenly",
163
+ },
164
+ topic: {
165
+ maxWidth: 600,
166
+ },
167
+ });
@@ -79,8 +79,22 @@ type DefaultProps = {|
79
79
  * ☑️ A nicely styled checkbox for all your checking needs. Can optionally take
80
80
  * label and description props.
81
81
  *
82
+ * If used by itself, a checkbox provides two options - checked and unchecked.
83
+ * A group of checkboxes can be used to allow a user to select multiple values
84
+ * from a list of options.
85
+ *
82
86
  * If you want a whole group of Checkbox[es] that are related, see the Choice
83
87
  * and CheckboxGroup components.
88
+ *
89
+ * ### Usage
90
+ *
91
+ * ```jsx
92
+ * import {Checkbox} from "@khanacademy/wonder-blocks-form";
93
+ *
94
+ * const [checked, setChecked] = React.useState(false);
95
+ *
96
+ * <Checkbox checked={checked} onChange={setChecked} />
97
+ * ```
84
98
  */
85
99
  export default class Checkbox extends React.Component<ChoiceComponentProps> {
86
100
  static defaultProps: DefaultProps = {