@khanacademy/wonder-blocks-form 4.9.2 → 4.9.4

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.
Files changed (39) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/components/checkbox-core.d.ts +2 -2
  3. package/dist/components/checkbox.d.ts +2 -2
  4. package/dist/components/choice-internal.d.ts +2 -2
  5. package/dist/components/choice.d.ts +2 -2
  6. package/dist/components/labeled-text-field.d.ts +9 -1
  7. package/dist/components/radio-core.d.ts +2 -2
  8. package/dist/components/radio.d.ts +2 -2
  9. package/dist/components/text-area.d.ts +2 -2
  10. package/dist/components/text-field.d.ts +10 -5
  11. package/dist/es/index.js +93 -48
  12. package/dist/index.js +93 -48
  13. package/package.json +7 -7
  14. package/src/__tests__/__snapshots__/custom-snapshot.test.tsx.snap +0 -247
  15. package/src/__tests__/custom-snapshot.test.tsx +0 -48
  16. package/src/components/__tests__/checkbox-group.test.tsx +0 -162
  17. package/src/components/__tests__/checkbox.test.tsx +0 -138
  18. package/src/components/__tests__/field-heading.test.tsx +0 -225
  19. package/src/components/__tests__/labeled-text-field.test.tsx +0 -750
  20. package/src/components/__tests__/radio-group.test.tsx +0 -182
  21. package/src/components/__tests__/text-area.test.tsx +0 -1286
  22. package/src/components/__tests__/text-field.test.tsx +0 -562
  23. package/src/components/checkbox-core.tsx +0 -239
  24. package/src/components/checkbox-group.tsx +0 -174
  25. package/src/components/checkbox.tsx +0 -99
  26. package/src/components/choice-internal.tsx +0 -184
  27. package/src/components/choice.tsx +0 -157
  28. package/src/components/field-heading.tsx +0 -169
  29. package/src/components/group-styles.ts +0 -33
  30. package/src/components/labeled-text-field.tsx +0 -317
  31. package/src/components/radio-core.tsx +0 -171
  32. package/src/components/radio-group.tsx +0 -159
  33. package/src/components/radio.tsx +0 -82
  34. package/src/components/text-area.tsx +0 -430
  35. package/src/components/text-field.tsx +0 -399
  36. package/src/index.ts +0 -17
  37. package/src/util/types.ts +0 -85
  38. package/tsconfig-build.json +0 -19
  39. package/tsconfig-build.tsbuildinfo +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # @khanacademy/wonder-blocks-form
2
2
 
3
+ ## 4.9.4
4
+
5
+ ### Patch Changes
6
+
7
+ - 61dc4448: Allow `TextField` to be focusable when disabled. It now sets `aria-disabled` instead of the `disabled` attribute based on the `disabled` prop. This makes it so screenreaders will continue to communicate that the component is disabled, while allowing focus on the disabled component. Focus styling is also added to the disabled state.
8
+ - 2dfd5eb6: - Update `TextField` state styling so that it is consistent with other components like `TextArea`, `SingleSelect`, `MultiSelect` (especially the focus styling). The styling also now uses CSS pseudo-classes for easier testing in Chromatic and debugging in browsers.
9
+ - `TextField` and `TextArea` state styling has also been updated so that any outline styles outside of the component are now applied within the component to prevent cropped focus outlines in places where an ancestor element has `overflow: hidden`.
10
+
11
+ ## 4.9.3
12
+
13
+ ### Patch Changes
14
+
15
+ - 02a1b298: Make sure we don't package tsconfig and tsbuildinfo files
16
+ - Updated dependencies [02a1b298]
17
+ - @khanacademy/wonder-blocks-clickable@4.2.8
18
+ - @khanacademy/wonder-blocks-core@7.0.1
19
+ - @khanacademy/wonder-blocks-icon@4.1.5
20
+ - @khanacademy/wonder-blocks-layout@2.2.1
21
+ - @khanacademy/wonder-blocks-tokens@2.0.1
22
+ - @khanacademy/wonder-blocks-typography@2.1.16
23
+
3
24
  ## 4.9.2
4
25
 
5
26
  ### Patch Changes
@@ -3,8 +3,8 @@ import type { Checked } from "../util/types";
3
3
  /**
4
4
  * The internal stateless ☑️ Checkbox
5
5
  */
6
- declare const CheckboxCore: React.ForwardRefExoticComponent<Readonly<import("../../../wonder-blocks-core/src/util/aria-types").AriaAttributes> & Readonly<{
7
- role?: import("../../../wonder-blocks-core/src/util/aria-types").AriaRole | undefined;
6
+ declare const CheckboxCore: React.ForwardRefExoticComponent<Readonly<import("@khanacademy/wonder-blocks-core").AriaAttributes> & Readonly<{
7
+ role?: import("@khanacademy/wonder-blocks-core").AriaRole | undefined;
8
8
  }> & {
9
9
  checked: Checked;
10
10
  disabled: boolean;
@@ -22,8 +22,8 @@ import type { Checked } from "../util/types";
22
22
  * <Checkbox checked={checked} onChange={setChecked} />
23
23
  * ```
24
24
  */
25
- declare const Checkbox: React.ForwardRefExoticComponent<Readonly<import("../../../wonder-blocks-core/src/util/aria-types").AriaAttributes> & Readonly<{
26
- role?: import("../../../wonder-blocks-core/src/util/aria-types").AriaRole | undefined;
25
+ declare const Checkbox: React.ForwardRefExoticComponent<Readonly<import("@khanacademy/wonder-blocks-core").AriaAttributes> & Readonly<{
26
+ role?: import("@khanacademy/wonder-blocks-core").AriaRole | undefined;
27
27
  }> & {
28
28
  /**
29
29
  * Whether this component is checked or indeterminate
@@ -7,8 +7,8 @@ import type { StyleType } from "@khanacademy/wonder-blocks-core";
7
7
  * and RadioGroup. This design allows for more explicit prop typing. For
8
8
  * example, we can make onChange a required prop on Checkbox but not on Choice
9
9
  * (because for Choice, that prop would be auto-populated by CheckboxGroup).
10
- */ declare const ChoiceInternal: React.ForwardRefExoticComponent<Readonly<import("../../../wonder-blocks-core/src/util/aria-types").AriaAttributes> & Readonly<{
11
- role?: import("../../../wonder-blocks-core/src/util/aria-types").AriaRole | undefined;
10
+ */ declare const ChoiceInternal: React.ForwardRefExoticComponent<Readonly<import("@khanacademy/wonder-blocks-core").AriaAttributes> & Readonly<{
11
+ role?: import("@khanacademy/wonder-blocks-core").AriaRole | undefined;
12
12
  }> & {
13
13
  /** Whether this choice is checked. */
14
14
  checked: boolean | null | undefined;
@@ -66,8 +66,8 @@ import type { StyleType } from "@khanacademy/wonder-blocks-core";
66
66
  * </RadioGroup>
67
67
  * ```
68
68
  */
69
- declare const Choice: React.ForwardRefExoticComponent<Readonly<import("../../../wonder-blocks-core/src/util/aria-types").AriaAttributes> & Readonly<{
70
- role?: import("../../../wonder-blocks-core/src/util/aria-types").AriaRole | undefined;
69
+ declare const Choice: React.ForwardRefExoticComponent<Readonly<import("@khanacademy/wonder-blocks-core").AriaAttributes> & Readonly<{
70
+ role?: import("@khanacademy/wonder-blocks-core").AriaRole | undefined;
71
71
  }> & {
72
72
  /** User-defined. Label for the field. */
73
73
  label: React.ReactNode;
@@ -24,7 +24,15 @@ type CommonProps = {
24
24
  */
25
25
  value: string;
26
26
  /**
27
- * Makes a read-only input field that cannot be focused. Defaults to false.
27
+ * Whether the input should be disabled. Defaults to false.
28
+ * If the disabled prop is set to `true`, LabeledTextField will have disabled
29
+ * styling and will not be interactable.
30
+ *
31
+ * Note: The `disabled` prop sets the `aria-disabled` attribute to `true`
32
+ * instead of setting the `disabled` attribute. This is so that the component
33
+ * remains focusable while communicating to screen readers that it is disabled.
34
+ * This `disabled` prop will also set the `readonly` attribute to prevent
35
+ * typing in the field.
28
36
  */
29
37
  disabled: boolean;
30
38
  /**
@@ -2,8 +2,8 @@ import * as React from "react";
2
2
  import type { Checked } from "../util/types";
3
3
  /**
4
4
  * The internal stateless 🔘 Radio button
5
- */ declare const RadioCore: React.ForwardRefExoticComponent<Readonly<import("../../../wonder-blocks-core/src/util/aria-types").AriaAttributes> & Readonly<{
6
- role?: import("../../../wonder-blocks-core/src/util/aria-types").AriaRole | undefined;
5
+ */ declare const RadioCore: React.ForwardRefExoticComponent<Readonly<import("@khanacademy/wonder-blocks-core").AriaAttributes> & Readonly<{
6
+ role?: import("@khanacademy/wonder-blocks-core").AriaRole | undefined;
7
7
  }> & {
8
8
  checked: Checked;
9
9
  disabled: boolean;
@@ -6,8 +6,8 @@ import type { StyleType } from "@khanacademy/wonder-blocks-core";
6
6
  *
7
7
  * This component should not really be used by itself because radio buttons are
8
8
  * often grouped together. See RadioGroup.
9
- */ declare const Radio: React.ForwardRefExoticComponent<Readonly<import("../../../wonder-blocks-core/src/util/aria-types").AriaAttributes> & Readonly<{
10
- role?: import("../../../wonder-blocks-core/src/util/aria-types").AriaRole | undefined;
9
+ */ declare const Radio: React.ForwardRefExoticComponent<Readonly<import("@khanacademy/wonder-blocks-core").AriaAttributes> & Readonly<{
10
+ role?: import("@khanacademy/wonder-blocks-core").AriaRole | undefined;
11
11
  }> & {
12
12
  /**
13
13
  * Whether this component is checked
@@ -1,7 +1,7 @@
1
1
  import * as React from "react";
2
2
  import { StyleType } from "@khanacademy/wonder-blocks-core";
3
- declare const TextArea: React.ForwardRefExoticComponent<Readonly<import("../../../wonder-blocks-core/src/util/aria-types").AriaAttributes> & Readonly<{
4
- role?: import("../../../wonder-blocks-core/src/util/aria-types").AriaRole | undefined;
3
+ declare const TextArea: React.ForwardRefExoticComponent<Readonly<import("@khanacademy/wonder-blocks-core").AriaAttributes> & Readonly<{
4
+ role?: import("@khanacademy/wonder-blocks-core").AriaRole | undefined;
5
5
  }> & {
6
6
  /**
7
7
  * The text area value.
@@ -21,7 +21,15 @@ type CommonProps = AriaProps & {
21
21
  */
22
22
  name?: string;
23
23
  /**
24
- * Makes a read-only input field that cannot be focused. Defaults to false.
24
+ * Whether the input should be disabled. Defaults to false.
25
+ * If the disabled prop is set to `true`, TextField will have disabled
26
+ * styling and will not be interactable.
27
+ *
28
+ * Note: The `disabled` prop sets the `aria-disabled` attribute to `true`
29
+ * instead of setting the `disabled` attribute. This is so that the component
30
+ * remains focusable while communicating to screen readers that it is disabled.
31
+ * This `disabled` prop will also set the `readonly` attribute to prevent
32
+ * typing in the field.
25
33
  */
26
34
  disabled: boolean;
27
35
  /**
@@ -133,10 +141,6 @@ type State = {
133
141
  * Displayed when the validation fails.
134
142
  */
135
143
  error: string | null | undefined;
136
- /**
137
- * The user focuses on this field.
138
- */
139
- focused: boolean;
140
144
  };
141
145
  /**
142
146
  * A TextField is an element used to accept a single line of text from the user.
@@ -150,6 +154,7 @@ declare class TextField extends React.Component<PropsWithForwardRef, State> {
150
154
  handleChange: (event: React.ChangeEvent<HTMLInputElement>) => unknown;
151
155
  handleFocus: (event: React.FocusEvent<HTMLInputElement>) => unknown;
152
156
  handleBlur: (event: React.FocusEvent<HTMLInputElement>) => unknown;
157
+ getStyles: () => StyleType;
153
158
  render(): React.ReactNode;
154
159
  }
155
160
  type ExportProps = OmitConstrained<JSX.LibraryManagedAttributes<typeof TextField, React.ComponentProps<typeof TextField>>, "forwardedRef">;
package/dist/es/index.js CHANGED
@@ -592,15 +592,14 @@ const RadioGroup = React.forwardRef(function RadioGroup(props, ref) {
592
592
  })));
593
593
  });
594
594
 
595
- const _excluded$2 = ["id", "type", "value", "name", "disabled", "onKeyDown", "placeholder", "light", "style", "testId", "readOnly", "autoFocus", "autoComplete", "forwardedRef", "onFocus", "onBlur", "onValidate", "validate", "onChange", "required"];
595
+ const _excluded$2 = ["id", "type", "value", "name", "disabled", "onKeyDown", "placeholder", "style", "testId", "readOnly", "autoFocus", "autoComplete", "forwardedRef", "light", "onFocus", "onBlur", "onValidate", "validate", "onChange", "required"];
596
596
  const defaultErrorMessage$1 = "This field is required.";
597
597
  const StyledInput = addStyle("input");
598
598
  class TextField extends React.Component {
599
599
  constructor(props) {
600
600
  super(props);
601
601
  this.state = {
602
- error: null,
603
- focused: false
602
+ error: null
604
603
  };
605
604
  this.maybeValidate = newValue => {
606
605
  const {
@@ -641,25 +640,30 @@ class TextField extends React.Component {
641
640
  const {
642
641
  onFocus
643
642
  } = this.props;
644
- this.setState({
645
- focused: true
646
- }, () => {
647
- if (onFocus) {
648
- onFocus(event);
649
- }
650
- });
643
+ if (onFocus) {
644
+ onFocus(event);
645
+ }
651
646
  };
652
647
  this.handleBlur = event => {
653
648
  const {
654
649
  onBlur
655
650
  } = this.props;
656
- this.setState({
657
- focused: false
658
- }, () => {
659
- if (onBlur) {
660
- onBlur(event);
661
- }
662
- });
651
+ if (onBlur) {
652
+ onBlur(event);
653
+ }
654
+ };
655
+ this.getStyles = () => {
656
+ const {
657
+ disabled,
658
+ light
659
+ } = this.props;
660
+ const {
661
+ error
662
+ } = this.state;
663
+ const baseStyles = [styles$2.input, styles$7.LabelMedium];
664
+ const defaultStyles = [styles$2.default, !disabled && styles$2.defaultFocus, disabled && styles$2.disabled, !!error && styles$2.error];
665
+ const lightStyles = [styles$2.light, !disabled && styles$2.lightFocus, disabled && styles$2.lightDisabled, !!error && styles$2.lightError];
666
+ return [...baseStyles, ...(light ? lightStyles : defaultStyles)];
663
667
  };
664
668
  if (props.validate && props.value !== "") {
665
669
  this.state.error = props.validate(props.value) || null;
@@ -680,7 +684,6 @@ class TextField extends React.Component {
680
684
  disabled,
681
685
  onKeyDown,
682
686
  placeholder,
683
- light,
684
687
  style,
685
688
  testId,
686
689
  readOnly,
@@ -693,19 +696,19 @@ class TextField extends React.Component {
693
696
  id: id,
694
697
  scope: "text-field"
695
698
  }, uniqueId => React.createElement(StyledInput, _extends({
696
- style: [styles$2.input, styles$7.LabelMedium, styles$2.default, disabled ? styles$2.disabled : this.state.focused ? [styles$2.focused, light && styles$2.defaultLight] : !!this.state.error && [styles$2.error, light && styles$2.errorLight], !!this.state.error && styles$2.error, style && style],
699
+ style: [this.getStyles(), style],
697
700
  id: uniqueId,
698
701
  type: type,
699
702
  placeholder: placeholder,
700
703
  value: value,
701
704
  name: name,
702
- disabled: disabled,
705
+ "aria-disabled": disabled,
703
706
  onChange: this.handleChange,
704
- onKeyDown: onKeyDown,
707
+ onKeyDown: disabled ? undefined : onKeyDown,
705
708
  onFocus: this.handleFocus,
706
709
  onBlur: this.handleBlur,
707
710
  "data-testid": testId,
708
- readOnly: readOnly,
711
+ readOnly: readOnly || disabled,
709
712
  autoFocus: autoFocus,
710
713
  autoComplete: autoComplete,
711
714
  ref: forwardedRef
@@ -723,12 +726,10 @@ const styles$2 = StyleSheet.create({
723
726
  input: {
724
727
  width: "100%",
725
728
  height: 40,
726
- borderRadius: 4,
729
+ borderRadius: border.radius.medium_4,
727
730
  boxSizing: "border-box",
728
731
  paddingLeft: spacing.medium_16,
729
- margin: 0,
730
- outline: "none",
731
- boxShadow: "none"
732
+ margin: 0
732
733
  },
733
734
  default: {
734
735
  background: color.white,
@@ -738,12 +739,23 @@ const styles$2 = StyleSheet.create({
738
739
  color: color.offBlack64
739
740
  }
740
741
  },
742
+ defaultFocus: {
743
+ ":focus-visible": {
744
+ borderColor: color.blue,
745
+ outline: `1px solid ${color.blue}`,
746
+ outlineOffset: "-2px"
747
+ }
748
+ },
741
749
  error: {
742
750
  background: color.fadedRed8,
743
751
  border: `1px solid ${color.red}`,
744
752
  color: color.offBlack,
745
753
  "::placeholder": {
746
754
  color: color.offBlack64
755
+ },
756
+ ":focus-visible": {
757
+ outlineColor: color.red,
758
+ borderColor: color.red
747
759
  }
748
760
  },
749
761
  disabled: {
@@ -751,22 +763,56 @@ const styles$2 = StyleSheet.create({
751
763
  border: `1px solid ${color.offBlack16}`,
752
764
  color: color.offBlack64,
753
765
  "::placeholder": {
754
- color: color.offBlack32
766
+ color: color.offBlack64
767
+ },
768
+ cursor: "not-allowed",
769
+ ":focus-visible": {
770
+ outline: `2px solid ${color.offBlack32}`,
771
+ outlineOffset: "-3px"
755
772
  }
756
773
  },
757
- focused: {
774
+ light: {
758
775
  background: color.white,
759
- border: `1px solid ${color.blue}`,
776
+ border: `1px solid ${color.offBlack16}`,
760
777
  color: color.offBlack,
761
778
  "::placeholder": {
762
779
  color: color.offBlack64
763
780
  }
764
781
  },
765
- defaultLight: {
766
- boxShadow: `0px 0px 0px 1px ${color.blue}, 0px 0px 0px 2px ${color.white}`
782
+ lightFocus: {
783
+ ":focus-visible": {
784
+ outline: `3px solid ${color.blue}`,
785
+ outlineOffset: "-4px",
786
+ borderColor: color.white
787
+ }
788
+ },
789
+ lightDisabled: {
790
+ backgroundColor: "transparent",
791
+ border: `1px solid ${color.white32}`,
792
+ color: color.white64,
793
+ "::placeholder": {
794
+ color: color.white64
795
+ },
796
+ cursor: "not-allowed",
797
+ ":focus-visible": {
798
+ borderColor: mix(color.white32, color.blue),
799
+ outline: `3px solid ${color.fadedBlue}`,
800
+ outlineOffset: "-4px"
801
+ }
767
802
  },
768
- errorLight: {
769
- boxShadow: `0px 0px 0px 1px ${color.red}, 0px 0px 0px 2px ${color.white}`
803
+ lightError: {
804
+ background: color.fadedRed8,
805
+ border: `1px solid ${color.white}`,
806
+ outline: `2px solid ${color.red}`,
807
+ outlineOffset: "-3px",
808
+ color: color.offBlack,
809
+ "::placeholder": {
810
+ color: color.offBlack64
811
+ },
812
+ ":focus-visible": {
813
+ outline: `3px solid ${color.red}`,
814
+ outlineOffset: "-4px"
815
+ }
770
816
  }
771
817
  });
772
818
  var TextField$1 = React.forwardRef((props, ref) => React.createElement(TextField, _extends({}, props, {
@@ -1062,7 +1108,7 @@ const TextArea = React.forwardRef(function TextArea(props, ref) {
1062
1108
  "data-testid": testId,
1063
1109
  ref: ref,
1064
1110
  className: className,
1065
- style: [...getStyles(), style],
1111
+ style: [getStyles(), style],
1066
1112
  value: value,
1067
1113
  onChange: handleChange,
1068
1114
  placeholder: placeholder,
@@ -1106,7 +1152,7 @@ const styles = StyleSheet.create({
1106
1152
  ":focus-visible": {
1107
1153
  borderColor: color.blue,
1108
1154
  outline: `1px solid ${color.blue}`,
1109
- outlineOffset: 0
1155
+ outlineOffset: "-2px"
1110
1156
  }
1111
1157
  },
1112
1158
  disabled: {
@@ -1118,8 +1164,8 @@ const styles = StyleSheet.create({
1118
1164
  },
1119
1165
  cursor: "not-allowed",
1120
1166
  ":focus-visible": {
1121
- outline: "none",
1122
- boxShadow: `0 0 0 1px ${color.white}, 0 0 0 3px ${color.offBlack32}`
1167
+ outline: `2px solid ${color.offBlack32}`,
1168
+ outlineOffset: "-3px"
1123
1169
  }
1124
1170
  },
1125
1171
  error: {
@@ -1144,10 +1190,9 @@ const styles = StyleSheet.create({
1144
1190
  },
1145
1191
  lightFocus: {
1146
1192
  ":focus-visible": {
1147
- outline: `1px solid ${color.blue}`,
1148
- outlineOffset: 0,
1149
- borderColor: color.blue,
1150
- boxShadow: `0px 0px 0px 2px ${color.blue}, 0px 0px 0px 3px ${color.white}`
1193
+ outline: `3px solid ${color.blue}`,
1194
+ outlineOffset: "-4px",
1195
+ borderColor: color.white
1151
1196
  }
1152
1197
  },
1153
1198
  lightDisabled: {
@@ -1160,22 +1205,22 @@ const styles = StyleSheet.create({
1160
1205
  cursor: "not-allowed",
1161
1206
  ":focus-visible": {
1162
1207
  borderColor: mix(color.white32, color.blue),
1163
- outline: "none",
1164
- boxShadow: `0 0 0 1px ${color.offBlack32}, 0 0 0 3px ${color.fadedBlue}`
1208
+ outline: `3px solid ${color.fadedBlue}`,
1209
+ outlineOffset: "-4px"
1165
1210
  }
1166
1211
  },
1167
1212
  lightError: {
1168
1213
  background: color.fadedRed8,
1169
- border: `1px solid ${color.red}`,
1170
- boxShadow: `0px 0px 0px 1px ${color.red}, 0px 0px 0px 2px ${color.white}`,
1214
+ border: `1px solid ${color.white}`,
1215
+ outline: `2px solid ${color.red}`,
1216
+ outlineOffset: "-3px",
1171
1217
  color: color.offBlack,
1172
1218
  "::placeholder": {
1173
1219
  color: color.offBlack64
1174
1220
  },
1175
1221
  ":focus-visible": {
1176
- outlineColor: color.red,
1177
- borderColor: color.red,
1178
- boxShadow: `0px 0px 0px 2px ${color.red}, 0px 0px 0px 3px ${color.white}`
1222
+ outline: `3px solid ${color.red}`,
1223
+ outlineOffset: "-4px"
1179
1224
  }
1180
1225
  }
1181
1226
  });