@khanacademy/wonder-blocks-form 2.2.1

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 (38) hide show
  1. package/LICENSE +21 -0
  2. package/dist/es/index.js +1100 -0
  3. package/dist/index.js +1419 -0
  4. package/dist/index.js.flow +2 -0
  5. package/docs.md +1 -0
  6. package/package.json +35 -0
  7. package/src/__tests__/__snapshots__/custom-snapshot.test.js.snap +1349 -0
  8. package/src/__tests__/__snapshots__/generated-snapshot.test.js.snap +6126 -0
  9. package/src/__tests__/custom-snapshot.test.js +66 -0
  10. package/src/__tests__/generated-snapshot.test.js +654 -0
  11. package/src/components/__tests__/checkbox-group.test.js +84 -0
  12. package/src/components/__tests__/field-heading.test.js +182 -0
  13. package/src/components/__tests__/labeled-text-field.test.js +442 -0
  14. package/src/components/__tests__/radio-group.test.js +84 -0
  15. package/src/components/__tests__/text-field.test.js +424 -0
  16. package/src/components/checkbox-core.js +201 -0
  17. package/src/components/checkbox-group.js +161 -0
  18. package/src/components/checkbox-group.md +200 -0
  19. package/src/components/checkbox.js +94 -0
  20. package/src/components/checkbox.md +134 -0
  21. package/src/components/choice-internal.js +206 -0
  22. package/src/components/choice.js +104 -0
  23. package/src/components/field-heading.js +157 -0
  24. package/src/components/field-heading.md +43 -0
  25. package/src/components/group-styles.js +35 -0
  26. package/src/components/labeled-text-field.js +265 -0
  27. package/src/components/labeled-text-field.md +535 -0
  28. package/src/components/labeled-text-field.stories.js +359 -0
  29. package/src/components/radio-core.js +176 -0
  30. package/src/components/radio-group.js +142 -0
  31. package/src/components/radio-group.md +129 -0
  32. package/src/components/radio.js +93 -0
  33. package/src/components/radio.md +26 -0
  34. package/src/components/text-field.js +326 -0
  35. package/src/components/text-field.md +770 -0
  36. package/src/components/text-field.stories.js +513 -0
  37. package/src/index.js +18 -0
  38. package/src/util/types.js +77 -0
@@ -0,0 +1,265 @@
1
+ // @flow
2
+ import * as React from "react";
3
+
4
+ import {IDProvider, type StyleType} from "@khanacademy/wonder-blocks-core";
5
+ import {type Typography} from "@khanacademy/wonder-blocks-typography";
6
+
7
+ import FieldHeading from "./field-heading.js";
8
+ import TextField, {type TextFieldType} from "./text-field.js";
9
+
10
+ type WithForwardRef = {|forwardedRef: React.Ref<"input">|};
11
+
12
+ type Props = {|
13
+ /**
14
+ * An optional unique identifier for the TextField.
15
+ * If no id is specified, a unique id will be auto-generated.
16
+ */
17
+ id?: string,
18
+
19
+ /**
20
+ * Determines the type of input. Defaults to text.
21
+ */
22
+ type: TextFieldType,
23
+
24
+ /**
25
+ * Provide a label for the TextField.
26
+ */
27
+ label: string | React.Element<Typography>,
28
+
29
+ /**
30
+ * Provide a description for the TextField.
31
+ */
32
+ description?: string | React.Element<Typography>,
33
+
34
+ /**
35
+ * The input value.
36
+ */
37
+ value: string,
38
+
39
+ /**
40
+ * Makes a read-only input field that cannot be focused. Defaults to false.
41
+ */
42
+ disabled: boolean,
43
+
44
+ /**
45
+ * Provide a validation for the input value.
46
+ * Return a string error message or null | void for a valid input.
47
+ */
48
+ validate?: (value: string) => ?string,
49
+
50
+ /**
51
+ * Called when the TextField input is validated.
52
+ */
53
+ onValidate?: (errorMessage: ?string) => mixed,
54
+
55
+ /**
56
+ * Called when the value has changed.
57
+ */
58
+ onChange: (newValue: string) => mixed,
59
+
60
+ /**
61
+ * Called when a key is pressed.
62
+ */
63
+ onKeyDown?: (event: SyntheticKeyboardEvent<HTMLInputElement>) => mixed,
64
+
65
+ /**
66
+ * Called when the element has been focused.
67
+ */
68
+ onFocus?: (event: SyntheticFocusEvent<HTMLInputElement>) => mixed,
69
+
70
+ /**
71
+ * Called when the element has been blurred.
72
+ */
73
+ onBlur?: (event: SyntheticFocusEvent<HTMLInputElement>) => mixed,
74
+
75
+ /**
76
+ * Provide hints or examples of what to enter.
77
+ */
78
+ placeholder?: string,
79
+
80
+ /**
81
+ * Change the field’s sub-components to fit a dark background.
82
+ */
83
+ light: boolean,
84
+
85
+ /**
86
+ * Custom styles for the container.
87
+ *
88
+ * Note: This style is passed to the field heading container
89
+ * due to scenarios where we would like to set a specific
90
+ * width for the text field. If we apply the style directly
91
+ * to the text field, the container would not be affected.
92
+ * For example, setting text field to "width: 50%" would not
93
+ * affect the container since text field is a child of the container.
94
+ * In this case, the container would maintain its width ocuppying
95
+ * unnecessary space when the text field is smaller.
96
+ */
97
+ style?: StyleType,
98
+
99
+ /**
100
+ * Optional test ID for e2e testing.
101
+ */
102
+ testId?: string,
103
+
104
+ /**
105
+ * Specifies if the TextField is read-only.
106
+ */
107
+ readOnly?: boolean,
108
+
109
+ /**
110
+ * Specifies if the TextField allows autocomplete.
111
+ */
112
+ autoComplete?: string,
113
+ |};
114
+
115
+ type PropsWithForwardRef = {|
116
+ ...Props,
117
+ ...WithForwardRef,
118
+ |};
119
+
120
+ type DefaultProps = {|
121
+ type: $PropertyType<PropsWithForwardRef, "type">,
122
+ disabled: $PropertyType<PropsWithForwardRef, "disabled">,
123
+ light: $PropertyType<PropsWithForwardRef, "light">,
124
+ |};
125
+
126
+ type State = {|
127
+ /**
128
+ * Displayed when the validation fails.
129
+ */
130
+ error: ?string,
131
+
132
+ /**
133
+ * The user focuses on the textfield.
134
+ */
135
+ focused: boolean,
136
+ |};
137
+
138
+ // TODO(WB-1081): Change class name back to LabeledTextField after Styleguidist is gone.
139
+ /**
140
+ * A LabeledTextField is an element used to accept a single line of text
141
+ * from the user paired with a label, description, and error field elements.
142
+ */
143
+ class LabeledTextFieldInternal extends React.Component<
144
+ PropsWithForwardRef,
145
+ State,
146
+ > {
147
+ static defaultProps: DefaultProps = {
148
+ type: "text",
149
+ disabled: false,
150
+ light: false,
151
+ };
152
+
153
+ constructor(props: PropsWithForwardRef) {
154
+ super(props);
155
+ this.state = {
156
+ error: null,
157
+ focused: false,
158
+ };
159
+ }
160
+
161
+ handleValidate: (errorMessage: ?string) => mixed = (errorMessage) => {
162
+ const {onValidate} = this.props;
163
+ this.setState({error: errorMessage}, () => {
164
+ if (onValidate) {
165
+ onValidate(errorMessage);
166
+ }
167
+ });
168
+ };
169
+
170
+ handleFocus: (event: SyntheticFocusEvent<HTMLInputElement>) => mixed = (
171
+ event,
172
+ ) => {
173
+ const {onFocus} = this.props;
174
+ this.setState({focused: true}, () => {
175
+ if (onFocus) {
176
+ onFocus(event);
177
+ }
178
+ });
179
+ };
180
+
181
+ handleBlur: (event: SyntheticFocusEvent<HTMLInputElement>) => mixed = (
182
+ event,
183
+ ) => {
184
+ const {onBlur} = this.props;
185
+ this.setState({focused: false}, () => {
186
+ if (onBlur) {
187
+ onBlur(event);
188
+ }
189
+ });
190
+ };
191
+
192
+ render(): React.Node {
193
+ const {
194
+ id,
195
+ type,
196
+ label,
197
+ description,
198
+ value,
199
+ disabled,
200
+ validate,
201
+ onChange,
202
+ onKeyDown,
203
+ placeholder,
204
+ light,
205
+ style,
206
+ testId,
207
+ readOnly,
208
+ autoComplete,
209
+ forwardedRef,
210
+ } = this.props;
211
+
212
+ return (
213
+ <IDProvider id={id} scope="labeled-text-field">
214
+ {(uniqueId) => (
215
+ <FieldHeading
216
+ id={uniqueId}
217
+ testId={testId}
218
+ style={style}
219
+ field={
220
+ <TextField
221
+ id={`${uniqueId}-field`}
222
+ aria-describedby={`${uniqueId}-error`}
223
+ aria-invalid={
224
+ this.state.error ? "true" : "false"
225
+ }
226
+ testId={testId && `${testId}-field`}
227
+ type={type}
228
+ value={value}
229
+ placeholder={placeholder}
230
+ disabled={disabled}
231
+ validate={validate}
232
+ onValidate={this.handleValidate}
233
+ onChange={onChange}
234
+ onKeyDown={onKeyDown}
235
+ onFocus={this.handleFocus}
236
+ onBlur={this.handleBlur}
237
+ light={light}
238
+ readOnly={readOnly}
239
+ autoComplete={autoComplete}
240
+ ref={forwardedRef}
241
+ />
242
+ }
243
+ label={label}
244
+ description={description}
245
+ error={(!this.state.focused && this.state.error) || ""}
246
+ />
247
+ )}
248
+ </IDProvider>
249
+ );
250
+ }
251
+ }
252
+
253
+ type ExportProps = $Diff<
254
+ React.ElementConfig<typeof LabeledTextFieldInternal>,
255
+ WithForwardRef,
256
+ >;
257
+
258
+ const LabeledTextField: React.AbstractComponent<
259
+ ExportProps,
260
+ HTMLInputElement,
261
+ > = React.forwardRef<ExportProps, HTMLInputElement>((props, ref) => (
262
+ <LabeledTextFieldInternal {...props} forwardedRef={ref} />
263
+ ));
264
+
265
+ export default LabeledTextField;