@khanacademy/wonder-blocks-form 3.1.5 → 3.1.7
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 +21 -0
- package/dist/es/index.js +8 -8
- package/package.json +7 -7
- package/src/components/__tests__/field-heading.test.js +35 -40
- package/src/components/__tests__/labeled-text-field.test.js +87 -97
- package/src/components/__tests__/text-field.test.js +69 -74
- package/src/components/labeled-text-field.js +5 -11
- package/src/components/text-field.js +5 -12
- package/dist/index.js +0 -1737
- package/dist/index.js.flow +0 -2
- package/docs.md +0 -5
|
@@ -1,54 +1,37 @@
|
|
|
1
1
|
//@flow
|
|
2
2
|
import * as React from "react";
|
|
3
|
-
import {
|
|
4
|
-
import "jest-enzyme";
|
|
5
|
-
import {render, screen} from "@testing-library/react";
|
|
3
|
+
import {render, screen, fireEvent} from "@testing-library/react";
|
|
6
4
|
import userEvent from "@testing-library/user-event";
|
|
7
5
|
|
|
8
6
|
import {StyleSheet} from "aphrodite";
|
|
9
7
|
import LabeledTextField from "../labeled-text-field.js";
|
|
10
8
|
|
|
11
|
-
const wait = (delay: number = 0) =>
|
|
12
|
-
new Promise((resolve, reject) => {
|
|
13
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
14
|
-
return setTimeout(resolve, delay);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
9
|
describe("LabeledTextField", () => {
|
|
18
10
|
it("labeledtextfield becomes focused", () => {
|
|
19
11
|
// Arrange
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
);
|
|
23
|
-
const field = wrapper.find("TextFieldInternal");
|
|
12
|
+
render(<LabeledTextField label="Label" value="" onChange={() => {}} />);
|
|
13
|
+
const field = screen.getByRole("textbox");
|
|
24
14
|
|
|
25
15
|
// Act
|
|
26
|
-
|
|
16
|
+
userEvent.tab();
|
|
27
17
|
|
|
28
18
|
// Assert
|
|
29
|
-
expect(
|
|
30
|
-
"focused",
|
|
31
|
-
true,
|
|
32
|
-
);
|
|
19
|
+
expect(field).toHaveFocus();
|
|
33
20
|
});
|
|
34
21
|
|
|
35
22
|
it("labeledtextfield becomes blurred", async () => {
|
|
36
23
|
// Arrange
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
24
|
+
render(<LabeledTextField label="Label" value="" onChange={() => {}} />);
|
|
25
|
+
|
|
26
|
+
// focus
|
|
27
|
+
userEvent.tab();
|
|
41
28
|
|
|
42
29
|
// Act
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
field.simulate("blur");
|
|
30
|
+
// blur
|
|
31
|
+
userEvent.tab();
|
|
46
32
|
|
|
47
33
|
// Assert
|
|
48
|
-
expect(
|
|
49
|
-
"focused",
|
|
50
|
-
false,
|
|
51
|
-
);
|
|
34
|
+
expect(screen.getByRole("textbox")).not.toHaveFocus();
|
|
52
35
|
});
|
|
53
36
|
|
|
54
37
|
it("id prop is passed to input", () => {
|
|
@@ -56,7 +39,7 @@ describe("LabeledTextField", () => {
|
|
|
56
39
|
const id = "exampleid";
|
|
57
40
|
|
|
58
41
|
// Act
|
|
59
|
-
|
|
42
|
+
render(
|
|
60
43
|
<LabeledTextField
|
|
61
44
|
id={id}
|
|
62
45
|
label="Label"
|
|
@@ -67,23 +50,21 @@ describe("LabeledTextField", () => {
|
|
|
67
50
|
);
|
|
68
51
|
|
|
69
52
|
// Assert
|
|
70
|
-
const input =
|
|
71
|
-
expect(input).
|
|
53
|
+
const input = screen.getByRole("textbox");
|
|
54
|
+
expect(input).toHaveAttribute("id", `${id}-field`);
|
|
72
55
|
});
|
|
73
56
|
|
|
74
57
|
it("auto-generated id is passed to input when id prop is not set", () => {
|
|
75
58
|
// Arrange
|
|
76
59
|
|
|
77
60
|
// Act
|
|
78
|
-
|
|
79
|
-
<LabeledTextField label="Label" value="" onChange={() => {}} />,
|
|
80
|
-
);
|
|
61
|
+
render(<LabeledTextField label="Label" value="" onChange={() => {}} />);
|
|
81
62
|
|
|
82
63
|
// Assert
|
|
83
64
|
// Since the generated id is unique, we cannot know what it will be.
|
|
84
65
|
// We only test if the id attribute starts with "uid-" and ends with "-field".
|
|
85
|
-
const input =
|
|
86
|
-
expect(input.
|
|
66
|
+
const input = screen.getByRole("textbox");
|
|
67
|
+
expect(input.getAttribute("id")).toMatch(/uid-.*-field/);
|
|
87
68
|
});
|
|
88
69
|
|
|
89
70
|
it("type prop is passed to input", () => {
|
|
@@ -91,7 +72,7 @@ describe("LabeledTextField", () => {
|
|
|
91
72
|
const type = "email";
|
|
92
73
|
|
|
93
74
|
// Act
|
|
94
|
-
|
|
75
|
+
render(
|
|
95
76
|
<LabeledTextField
|
|
96
77
|
type={type}
|
|
97
78
|
label="Label"
|
|
@@ -101,8 +82,8 @@ describe("LabeledTextField", () => {
|
|
|
101
82
|
);
|
|
102
83
|
|
|
103
84
|
// Assert
|
|
104
|
-
const input =
|
|
105
|
-
expect(input).
|
|
85
|
+
const input = screen.getByRole("textbox");
|
|
86
|
+
expect(input).toHaveAttribute("type", type);
|
|
106
87
|
});
|
|
107
88
|
|
|
108
89
|
it("label prop is rendered", () => {
|
|
@@ -110,12 +91,10 @@ describe("LabeledTextField", () => {
|
|
|
110
91
|
const label = "Label";
|
|
111
92
|
|
|
112
93
|
// Act
|
|
113
|
-
|
|
114
|
-
<LabeledTextField label={label} value="" onChange={() => {}} />,
|
|
115
|
-
);
|
|
94
|
+
render(<LabeledTextField label={label} value="" onChange={() => {}} />);
|
|
116
95
|
|
|
117
96
|
// Assert
|
|
118
|
-
expect(
|
|
97
|
+
expect(screen.getByText(label)).toBeInTheDocument();
|
|
119
98
|
});
|
|
120
99
|
|
|
121
100
|
it("description prop is rendered", () => {
|
|
@@ -123,7 +102,7 @@ describe("LabeledTextField", () => {
|
|
|
123
102
|
const description = "Description";
|
|
124
103
|
|
|
125
104
|
// Act
|
|
126
|
-
|
|
105
|
+
render(
|
|
127
106
|
<LabeledTextField
|
|
128
107
|
label="Label"
|
|
129
108
|
description={description}
|
|
@@ -133,7 +112,7 @@ describe("LabeledTextField", () => {
|
|
|
133
112
|
);
|
|
134
113
|
|
|
135
114
|
// Assert
|
|
136
|
-
expect(
|
|
115
|
+
expect(screen.getByText(description)).toBeInTheDocument();
|
|
137
116
|
});
|
|
138
117
|
|
|
139
118
|
it("value prop is set on mount", () => {
|
|
@@ -141,7 +120,7 @@ describe("LabeledTextField", () => {
|
|
|
141
120
|
const value = "Value";
|
|
142
121
|
|
|
143
122
|
// Act
|
|
144
|
-
|
|
123
|
+
render(
|
|
145
124
|
<LabeledTextField
|
|
146
125
|
label="Label"
|
|
147
126
|
value={value}
|
|
@@ -150,32 +129,38 @@ describe("LabeledTextField", () => {
|
|
|
150
129
|
);
|
|
151
130
|
|
|
152
131
|
// Assert
|
|
153
|
-
const input =
|
|
154
|
-
expect(input).
|
|
132
|
+
const input = screen.getByRole("textbox");
|
|
133
|
+
expect(input).toHaveAttribute("value", value);
|
|
155
134
|
});
|
|
156
135
|
|
|
157
136
|
it("value prop change from parent reflects on input value", async () => {
|
|
158
137
|
// Arrange
|
|
159
138
|
const handleChange = jest.fn((newValue: string) => {});
|
|
160
139
|
|
|
161
|
-
const
|
|
140
|
+
const {rerender} = render(
|
|
162
141
|
<LabeledTextField label="Label" value="" onChange={handleChange} />,
|
|
163
142
|
);
|
|
164
143
|
|
|
165
144
|
// Act
|
|
166
145
|
const newValue = "new value";
|
|
167
|
-
|
|
146
|
+
rerender(
|
|
147
|
+
<LabeledTextField
|
|
148
|
+
label="Label"
|
|
149
|
+
value={newValue}
|
|
150
|
+
onChange={handleChange}
|
|
151
|
+
/>,
|
|
152
|
+
);
|
|
168
153
|
|
|
169
154
|
// Assert
|
|
170
|
-
const input =
|
|
171
|
-
expect(input).
|
|
155
|
+
const input = screen.getByRole("textbox");
|
|
156
|
+
expect(input).toHaveAttribute("value", newValue);
|
|
172
157
|
});
|
|
173
158
|
|
|
174
159
|
it("disabled prop disables the input", () => {
|
|
175
160
|
// Arrange
|
|
176
161
|
|
|
177
162
|
// Act
|
|
178
|
-
|
|
163
|
+
render(
|
|
179
164
|
<LabeledTextField
|
|
180
165
|
label="Label"
|
|
181
166
|
value=""
|
|
@@ -185,7 +170,7 @@ describe("LabeledTextField", () => {
|
|
|
185
170
|
);
|
|
186
171
|
|
|
187
172
|
// Assert
|
|
188
|
-
const input =
|
|
173
|
+
const input = screen.getByRole("textbox");
|
|
189
174
|
expect(input).toBeDisabled();
|
|
190
175
|
});
|
|
191
176
|
|
|
@@ -235,7 +220,7 @@ describe("LabeledTextField", () => {
|
|
|
235
220
|
it("validate prop is called when input changes", () => {
|
|
236
221
|
// Arrange
|
|
237
222
|
const validate = jest.fn((value: string): ?string => {});
|
|
238
|
-
|
|
223
|
+
render(
|
|
239
224
|
<LabeledTextField
|
|
240
225
|
label="Label"
|
|
241
226
|
value=""
|
|
@@ -246,8 +231,11 @@ describe("LabeledTextField", () => {
|
|
|
246
231
|
|
|
247
232
|
// Act
|
|
248
233
|
const newValue = "New Value";
|
|
249
|
-
const input =
|
|
250
|
-
|
|
234
|
+
const input = screen.getByRole("textbox");
|
|
235
|
+
// @see https://testing-library.com/docs/react-testing-library/faq
|
|
236
|
+
// How do I test input onChange handlers?
|
|
237
|
+
// eslint-disable-next-line testing-library/prefer-user-event
|
|
238
|
+
fireEvent.change(input, {target: {value: newValue}});
|
|
251
239
|
|
|
252
240
|
// Assert
|
|
253
241
|
expect(validate).toHaveBeenCalledWith(newValue);
|
|
@@ -264,7 +252,7 @@ describe("LabeledTextField", () => {
|
|
|
264
252
|
}
|
|
265
253
|
};
|
|
266
254
|
|
|
267
|
-
|
|
255
|
+
render(
|
|
268
256
|
<LabeledTextField
|
|
269
257
|
label="Label"
|
|
270
258
|
value="LongerThan8Chars"
|
|
@@ -275,8 +263,8 @@ describe("LabeledTextField", () => {
|
|
|
275
263
|
);
|
|
276
264
|
|
|
277
265
|
// Act
|
|
278
|
-
|
|
279
|
-
|
|
266
|
+
// Select all text and replace it with the new value.
|
|
267
|
+
userEvent.type(screen.getByRole("textbox"), `{selectall}Short`);
|
|
280
268
|
|
|
281
269
|
// Assert
|
|
282
270
|
expect(handleValidate).toHaveBeenCalledWith(errorMessage);
|
|
@@ -286,14 +274,17 @@ describe("LabeledTextField", () => {
|
|
|
286
274
|
// Arrange
|
|
287
275
|
const handleChange = jest.fn((newValue: string) => {});
|
|
288
276
|
|
|
289
|
-
|
|
277
|
+
render(
|
|
290
278
|
<LabeledTextField label="Label" value="" onChange={handleChange} />,
|
|
291
279
|
);
|
|
292
280
|
|
|
293
281
|
// Act
|
|
294
282
|
const newValue = "new value";
|
|
295
|
-
const input =
|
|
296
|
-
|
|
283
|
+
const input = screen.getByRole("textbox");
|
|
284
|
+
// @see https://testing-library.com/docs/react-testing-library/faq
|
|
285
|
+
// How do I test input onChange handlers?
|
|
286
|
+
// eslint-disable-next-line testing-library/prefer-user-event
|
|
287
|
+
fireEvent.change(input, {target: {value: newValue}});
|
|
297
288
|
|
|
298
289
|
// Assert
|
|
299
290
|
expect(handleChange).toHaveBeenCalledWith(newValue);
|
|
@@ -307,7 +298,7 @@ describe("LabeledTextField", () => {
|
|
|
307
298
|
},
|
|
308
299
|
);
|
|
309
300
|
|
|
310
|
-
|
|
301
|
+
render(
|
|
311
302
|
<LabeledTextField
|
|
312
303
|
label="Label"
|
|
313
304
|
value=""
|
|
@@ -317,18 +308,16 @@ describe("LabeledTextField", () => {
|
|
|
317
308
|
);
|
|
318
309
|
|
|
319
310
|
// Act
|
|
320
|
-
|
|
321
|
-
const input = wrapper.find("input");
|
|
322
|
-
input.simulate("keyDown", {key: key});
|
|
311
|
+
userEvent.type(screen.getByRole("textbox"), "{enter}");
|
|
323
312
|
|
|
324
313
|
// Assert
|
|
325
|
-
expect(handleKeyDown).toHaveReturnedWith(
|
|
314
|
+
expect(handleKeyDown).toHaveReturnedWith("Enter");
|
|
326
315
|
});
|
|
327
316
|
|
|
328
317
|
it("onFocus prop is called when field is focused", () => {
|
|
329
318
|
// Arrange
|
|
330
319
|
const handleFocus = jest.fn(() => {});
|
|
331
|
-
|
|
320
|
+
render(
|
|
332
321
|
<LabeledTextField
|
|
333
322
|
label="Label"
|
|
334
323
|
value=""
|
|
@@ -338,8 +327,8 @@ describe("LabeledTextField", () => {
|
|
|
338
327
|
);
|
|
339
328
|
|
|
340
329
|
// Act
|
|
341
|
-
const field =
|
|
342
|
-
field.
|
|
330
|
+
const field = screen.getByRole("textbox");
|
|
331
|
+
field.focus();
|
|
343
332
|
|
|
344
333
|
// Assert
|
|
345
334
|
expect(handleFocus).toHaveBeenCalled();
|
|
@@ -348,7 +337,7 @@ describe("LabeledTextField", () => {
|
|
|
348
337
|
it("onBlur prop is called when field is blurred", async () => {
|
|
349
338
|
// Arrange
|
|
350
339
|
const handleBlur = jest.fn(() => {});
|
|
351
|
-
|
|
340
|
+
render(
|
|
352
341
|
<LabeledTextField
|
|
353
342
|
label="Label"
|
|
354
343
|
value=""
|
|
@@ -357,11 +346,12 @@ describe("LabeledTextField", () => {
|
|
|
357
346
|
/>,
|
|
358
347
|
);
|
|
359
348
|
|
|
349
|
+
// focus
|
|
350
|
+
userEvent.tab();
|
|
351
|
+
|
|
360
352
|
// Act
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
await wait(0);
|
|
364
|
-
field.simulate("blur");
|
|
353
|
+
// blur
|
|
354
|
+
userEvent.tab();
|
|
365
355
|
|
|
366
356
|
// Assert
|
|
367
357
|
expect(handleBlur).toHaveBeenCalled();
|
|
@@ -372,7 +362,7 @@ describe("LabeledTextField", () => {
|
|
|
372
362
|
const placeholder = "Placeholder";
|
|
373
363
|
|
|
374
364
|
// Act
|
|
375
|
-
|
|
365
|
+
render(
|
|
376
366
|
<LabeledTextField
|
|
377
367
|
label="Label"
|
|
378
368
|
value=""
|
|
@@ -382,17 +372,15 @@ describe("LabeledTextField", () => {
|
|
|
382
372
|
);
|
|
383
373
|
|
|
384
374
|
// Assert
|
|
385
|
-
const input =
|
|
386
|
-
expect(input).
|
|
387
|
-
`[placeholder="${placeholder}"]`,
|
|
388
|
-
);
|
|
375
|
+
const input = screen.getByPlaceholderText(placeholder);
|
|
376
|
+
expect(input).toBeInTheDocument();
|
|
389
377
|
});
|
|
390
378
|
|
|
391
379
|
it("light prop is passed to textfield", async () => {
|
|
392
380
|
// Arrange
|
|
393
381
|
|
|
394
382
|
// Act
|
|
395
|
-
|
|
383
|
+
render(
|
|
396
384
|
<LabeledTextField
|
|
397
385
|
label="Label"
|
|
398
386
|
value=""
|
|
@@ -401,9 +389,11 @@ describe("LabeledTextField", () => {
|
|
|
401
389
|
/>,
|
|
402
390
|
);
|
|
403
391
|
|
|
392
|
+
const textField = screen.getByRole("textbox");
|
|
393
|
+
textField.focus();
|
|
394
|
+
|
|
404
395
|
// Assert
|
|
405
|
-
|
|
406
|
-
expect(textField).toHaveProp("light", true);
|
|
396
|
+
expect(textField.getAttribute("class")).toMatch(/light/i);
|
|
407
397
|
});
|
|
408
398
|
|
|
409
399
|
it("style prop is passed to fieldheading", async () => {
|
|
@@ -416,7 +406,7 @@ describe("LabeledTextField", () => {
|
|
|
416
406
|
});
|
|
417
407
|
|
|
418
408
|
// Act
|
|
419
|
-
const
|
|
409
|
+
const {container} = render(
|
|
420
410
|
<LabeledTextField
|
|
421
411
|
label="Label"
|
|
422
412
|
value=""
|
|
@@ -426,8 +416,8 @@ describe("LabeledTextField", () => {
|
|
|
426
416
|
);
|
|
427
417
|
|
|
428
418
|
// Assert
|
|
429
|
-
const fieldHeading =
|
|
430
|
-
expect(fieldHeading).toHaveStyle(
|
|
419
|
+
const fieldHeading = container.childNodes[0];
|
|
420
|
+
expect(fieldHeading).toHaveStyle("min-width: 250px");
|
|
431
421
|
});
|
|
432
422
|
|
|
433
423
|
it("testId prop is passed to textfield", async () => {
|
|
@@ -435,7 +425,7 @@ describe("LabeledTextField", () => {
|
|
|
435
425
|
const testId = "example-testid";
|
|
436
426
|
|
|
437
427
|
// Act
|
|
438
|
-
|
|
428
|
+
render(
|
|
439
429
|
<LabeledTextField
|
|
440
430
|
label="Label"
|
|
441
431
|
value=""
|
|
@@ -445,15 +435,15 @@ describe("LabeledTextField", () => {
|
|
|
445
435
|
);
|
|
446
436
|
|
|
447
437
|
// Assert
|
|
448
|
-
const
|
|
449
|
-
expect(
|
|
438
|
+
const input = screen.getByRole("textbox");
|
|
439
|
+
expect(input).toHaveAttribute("data-test-id", `${testId}-field`);
|
|
450
440
|
});
|
|
451
441
|
|
|
452
442
|
it("readOnly prop is passed to textfield", async () => {
|
|
453
443
|
// Arrange
|
|
454
444
|
|
|
455
445
|
// Act
|
|
456
|
-
|
|
446
|
+
render(
|
|
457
447
|
<LabeledTextField
|
|
458
448
|
label="Label"
|
|
459
449
|
value=""
|
|
@@ -463,8 +453,8 @@ describe("LabeledTextField", () => {
|
|
|
463
453
|
);
|
|
464
454
|
|
|
465
455
|
// Assert
|
|
466
|
-
const
|
|
467
|
-
expect(
|
|
456
|
+
const input = screen.getByRole("textbox");
|
|
457
|
+
expect(input).toHaveAttribute("readOnly");
|
|
468
458
|
});
|
|
469
459
|
|
|
470
460
|
it("autoComplete prop is passed to textfield", async () => {
|
|
@@ -472,7 +462,7 @@ describe("LabeledTextField", () => {
|
|
|
472
462
|
const autoComplete = "name";
|
|
473
463
|
|
|
474
464
|
// Act
|
|
475
|
-
|
|
465
|
+
render(
|
|
476
466
|
<LabeledTextField
|
|
477
467
|
label="Label"
|
|
478
468
|
value=""
|
|
@@ -482,8 +472,8 @@ describe("LabeledTextField", () => {
|
|
|
482
472
|
);
|
|
483
473
|
|
|
484
474
|
// Assert
|
|
485
|
-
const
|
|
486
|
-
expect(
|
|
475
|
+
const input = screen.getByRole("textbox");
|
|
476
|
+
expect(input).toHaveAttribute("autoComplete", autoComplete);
|
|
487
477
|
});
|
|
488
478
|
});
|
|
489
479
|
|