@khanacademy/wonder-blocks-form 4.9.1 → 4.9.3

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/CHANGELOG.md +19 -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/radio-core.d.ts +2 -2
  7. package/dist/components/radio.d.ts +2 -2
  8. package/dist/components/text-area.d.ts +2 -2
  9. package/dist/components/text-field.d.ts +4 -1
  10. package/dist/es/index.js +31 -78
  11. package/dist/index.js +31 -78
  12. package/package.json +7 -7
  13. package/src/__tests__/__snapshots__/custom-snapshot.test.tsx.snap +0 -247
  14. package/src/__tests__/custom-snapshot.test.tsx +0 -48
  15. package/src/components/__tests__/checkbox-group.test.tsx +0 -162
  16. package/src/components/__tests__/checkbox.test.tsx +0 -138
  17. package/src/components/__tests__/field-heading.test.tsx +0 -225
  18. package/src/components/__tests__/labeled-text-field.test.tsx +0 -727
  19. package/src/components/__tests__/radio-group.test.tsx +0 -182
  20. package/src/components/__tests__/text-area.test.tsx +0 -1286
  21. package/src/components/__tests__/text-field.test.tsx +0 -562
  22. package/src/components/checkbox-core.tsx +0 -239
  23. package/src/components/checkbox-group.tsx +0 -174
  24. package/src/components/checkbox.tsx +0 -99
  25. package/src/components/choice-internal.tsx +0 -184
  26. package/src/components/choice.tsx +0 -157
  27. package/src/components/field-heading.tsx +0 -169
  28. package/src/components/group-styles.ts +0 -33
  29. package/src/components/labeled-text-field.tsx +0 -317
  30. package/src/components/radio-core.tsx +0 -171
  31. package/src/components/radio-group.tsx +0 -159
  32. package/src/components/radio.tsx +0 -82
  33. package/src/components/text-area.tsx +0 -430
  34. package/src/components/text-field.tsx +0 -437
  35. package/src/index.ts +0 -17
  36. package/src/util/types.ts +0 -85
  37. package/tsconfig-build.json +0 -19
  38. package/tsconfig-build.tsbuildinfo +0 -1
@@ -1,727 +0,0 @@
1
- import * as React from "react";
2
- import {render, screen, fireEvent} from "@testing-library/react";
3
- import {userEvent} from "@testing-library/user-event";
4
-
5
- import {StyleSheet} from "aphrodite";
6
- import LabeledTextField from "../labeled-text-field";
7
-
8
- describe("LabeledTextField", () => {
9
- it("labeledtextfield becomes focused", async () => {
10
- // Arrange
11
- render(<LabeledTextField label="Label" value="" onChange={() => {}} />);
12
- const field = await screen.findByRole("textbox");
13
-
14
- // Act
15
- await userEvent.tab();
16
-
17
- // Assert
18
- expect(field).toHaveFocus();
19
- });
20
-
21
- it("labeledtextfield becomes blurred", async () => {
22
- // Arrange
23
- render(<LabeledTextField label="Label" value="" onChange={() => {}} />);
24
-
25
- // focus
26
- await userEvent.tab();
27
-
28
- // Act
29
- // blur
30
- await userEvent.tab();
31
-
32
- // Assert
33
- expect(await screen.findByRole("textbox")).not.toHaveFocus();
34
- });
35
-
36
- it("id prop is passed to input", async () => {
37
- // Arrange
38
- const id = "exampleid";
39
-
40
- // Act
41
- render(
42
- <LabeledTextField
43
- id={id}
44
- label="Label"
45
- value=""
46
- onChange={() => {}}
47
- disabled={true}
48
- />,
49
- );
50
-
51
- // Assert
52
- const input = await screen.findByRole("textbox");
53
- expect(input).toHaveAttribute("id", `${id}-field`);
54
- });
55
-
56
- it("auto-generated id is passed to input when id prop is not set", async () => {
57
- // Arrange
58
-
59
- // Act
60
- render(<LabeledTextField label="Label" value="" onChange={() => {}} />);
61
-
62
- // Assert
63
- // Since the generated id is unique, we cannot know what it will be. We
64
- // only test if the id attribute starts with "uid-", then followed by
65
- // "text-field-" as the scope assigned to IDProvider.
66
- const input = await screen.findByRole("textbox");
67
- expect(input.getAttribute("id")).toMatch(
68
- /uid-labeled-text-field.*-field/,
69
- );
70
- });
71
-
72
- it("type prop is passed to input", async () => {
73
- // Arrange
74
- const type = "email";
75
-
76
- // Act
77
- render(
78
- <LabeledTextField
79
- type={type}
80
- label="Label"
81
- value=""
82
- onChange={() => {}}
83
- />,
84
- );
85
-
86
- // Assert
87
- const input = await screen.findByRole("textbox");
88
- expect(input).toHaveAttribute("type", type);
89
- });
90
-
91
- it("label prop is rendered", async () => {
92
- // Arrange
93
- const label = "Label";
94
-
95
- // Act
96
- render(<LabeledTextField label={label} value="" onChange={() => {}} />);
97
-
98
- // Assert
99
- expect(await screen.findByText(label)).toBeInTheDocument();
100
- });
101
-
102
- it("description prop is rendered", async () => {
103
- // Arrange
104
- const description = "Description";
105
-
106
- // Act
107
- render(
108
- <LabeledTextField
109
- label="Label"
110
- description={description}
111
- value=""
112
- onChange={() => {}}
113
- />,
114
- );
115
-
116
- // Assert
117
- expect(await screen.findByText(description)).toBeInTheDocument();
118
- });
119
-
120
- it("value prop is set on mount", async () => {
121
- // Arrange
122
- const value = "Value";
123
-
124
- // Act
125
- render(
126
- <LabeledTextField
127
- label="Label"
128
- value={value}
129
- onChange={() => {}}
130
- />,
131
- );
132
-
133
- // Assert
134
- const input = await screen.findByRole("textbox");
135
- expect(input).toHaveAttribute("value", value);
136
- });
137
-
138
- it("value prop change from parent reflects on input value", async () => {
139
- // Arrange
140
- const handleChange = jest.fn((newValue: string) => {});
141
-
142
- const {rerender} = render(
143
- <LabeledTextField label="Label" value="" onChange={handleChange} />,
144
- );
145
-
146
- // Act
147
- const newValue = "new value";
148
- rerender(
149
- <LabeledTextField
150
- label="Label"
151
- value={newValue}
152
- onChange={handleChange}
153
- />,
154
- );
155
-
156
- // Assert
157
- const input = await screen.findByRole("textbox");
158
- expect(input).toHaveAttribute("value", newValue);
159
- });
160
-
161
- it("disabled prop disables the input", async () => {
162
- // Arrange
163
-
164
- // Act
165
- render(
166
- <LabeledTextField
167
- label="Label"
168
- value=""
169
- onChange={() => {}}
170
- disabled={true}
171
- />,
172
- );
173
-
174
- // Assert
175
- const input = await screen.findByRole("textbox");
176
- expect(input).toBeDisabled();
177
- });
178
-
179
- it("ariaDescribedby prop sets aria-describedby", async () => {
180
- // Arrange
181
- const ariaDescription = "aria description";
182
-
183
- // Act
184
- render(
185
- <LabeledTextField
186
- label="Label"
187
- value=""
188
- onChange={() => {}}
189
- ariaDescribedby={ariaDescription}
190
- />,
191
- );
192
-
193
- // Assert
194
- const input = await screen.findByRole("textbox");
195
- expect(input.getAttribute("aria-describedby")).toEqual(ariaDescription);
196
- });
197
-
198
- it("auto-generates a unique error when ariaDescribedby is not passed in", async () => {
199
- // Arrange
200
-
201
- // Act
202
- render(
203
- <LabeledTextField
204
- label="Label"
205
- value=""
206
- onChange={() => {}}
207
- // ariaDescribedby is not passed in
208
- />,
209
- );
210
-
211
- // Assert
212
- // Since the generated aria-describedby is unique,
213
- // we cannot know what it will be.
214
- // We only test if the aria-describedby attribute starts with
215
- // "uid-" and ends with "-error".
216
- const input = await screen.findByRole("textbox");
217
- expect(input.getAttribute("aria-describedby")).toMatch(
218
- /^uid-.*-error$/,
219
- );
220
- });
221
-
222
- it("validate prop is called when input changes", async () => {
223
- // Arrange
224
- const validate = jest.fn((value: string): any => {});
225
- render(
226
- <LabeledTextField
227
- label="Label"
228
- value=""
229
- onChange={() => {}}
230
- validate={validate}
231
- />,
232
- );
233
-
234
- // Act
235
- const newValue = "New Value";
236
- const input = await screen.findByRole("textbox");
237
- // @see https://testing-library.com/docs/react-testing-library/faq
238
- // How do I test input onChange handlers?
239
- // eslint-disable-next-line testing-library/prefer-user-event
240
- fireEvent.change(input, {target: {value: newValue}});
241
-
242
- // Assert
243
- expect(validate).toHaveBeenCalledWith(newValue);
244
- });
245
-
246
- it("onValidate prop is called on new validated input", async () => {
247
- // Arrange
248
- const handleValidate = jest.fn((errorMessage?: string | null) => {});
249
- const errorMessage = "Password must be at least 8 characters long";
250
-
251
- const validate = (value: string): string | null | undefined => {
252
- if (value.length < 8) {
253
- return errorMessage;
254
- }
255
- };
256
-
257
- render(
258
- <LabeledTextField
259
- label="Label"
260
- value="LongerThan8Chars"
261
- onChange={() => {}}
262
- validate={validate}
263
- onValidate={handleValidate}
264
- />,
265
- );
266
-
267
- // Act
268
- // Select all text and replace it with the new value.
269
- const textbox = await screen.findByRole("textbox");
270
- await userEvent.click(textbox);
271
- await userEvent.clear(textbox);
272
- await userEvent.paste("Short");
273
-
274
- // Assert
275
- expect(handleValidate).toHaveBeenCalledWith(errorMessage);
276
- });
277
-
278
- it("onChange prop is called on input change", async () => {
279
- // Arrange
280
- const handleChange = jest.fn((newValue: string) => {});
281
-
282
- render(
283
- <LabeledTextField label="Label" value="" onChange={handleChange} />,
284
- );
285
-
286
- // Act
287
- const newValue = "new value";
288
- const input = await screen.findByRole("textbox");
289
- // @see https://testing-library.com/docs/react-testing-library/faq
290
- // How do I test input onChange handlers?
291
- // eslint-disable-next-line testing-library/prefer-user-event
292
- fireEvent.change(input, {target: {value: newValue}});
293
-
294
- // Assert
295
- expect(handleChange).toHaveBeenCalledWith(newValue);
296
- });
297
-
298
- it("onKeyDown prop is called on keyboard keypress", async () => {
299
- // Arrange
300
- const handleKeyDown = jest.fn(
301
- (event: React.KeyboardEvent<HTMLInputElement>) => {
302
- return event.key;
303
- },
304
- );
305
-
306
- render(
307
- <LabeledTextField
308
- label="Label"
309
- value=""
310
- onChange={() => {}}
311
- onKeyDown={handleKeyDown}
312
- />,
313
- );
314
-
315
- // Act
316
- await userEvent.type(await screen.findByRole("textbox"), "{enter}");
317
-
318
- // Assert
319
- expect(handleKeyDown).toHaveReturnedWith("Enter");
320
- });
321
-
322
- it("onFocus prop is called when field is focused", async () => {
323
- // Arrange
324
- const handleFocus = jest.fn(() => {});
325
- render(
326
- <LabeledTextField
327
- label="Label"
328
- value=""
329
- onChange={() => {}}
330
- onFocus={handleFocus}
331
- />,
332
- );
333
-
334
- // Act
335
- const field = await screen.findByRole("textbox");
336
- field.focus();
337
-
338
- // Assert
339
- expect(handleFocus).toHaveBeenCalled();
340
- });
341
-
342
- it("onBlur prop is called when field is blurred", async () => {
343
- // Arrange
344
- const handleBlur = jest.fn(() => {});
345
- render(
346
- <LabeledTextField
347
- label="Label"
348
- value=""
349
- onChange={() => {}}
350
- onBlur={handleBlur}
351
- />,
352
- );
353
-
354
- // focus
355
- await userEvent.tab();
356
-
357
- // Act
358
- // blur
359
- await userEvent.tab();
360
-
361
- // Assert
362
- expect(handleBlur).toHaveBeenCalled();
363
- });
364
-
365
- it("placeholder prop is passed to input", async () => {
366
- // Arrange
367
- const placeholder = "Placeholder";
368
-
369
- // Act
370
- render(
371
- <LabeledTextField
372
- label="Label"
373
- value=""
374
- onChange={() => {}}
375
- placeholder={placeholder}
376
- />,
377
- );
378
-
379
- // Assert
380
- const input = await screen.findByPlaceholderText(placeholder);
381
- expect(input).toBeInTheDocument();
382
- });
383
-
384
- it("style prop is passed to fieldheading", async () => {
385
- // Arrange
386
- const styles = StyleSheet.create({
387
- style1: {
388
- minWidth: 250,
389
- background: "blue",
390
- },
391
- });
392
-
393
- // Act
394
- const {container} = render(
395
- <LabeledTextField
396
- label="Label"
397
- value=""
398
- onChange={() => {}}
399
- style={styles.style1}
400
- />,
401
- );
402
-
403
- // Assert
404
- const fieldHeading = container.childNodes[0];
405
- expect(fieldHeading).toHaveStyle("min-width: 250px");
406
- });
407
-
408
- it("testId prop is passed to textfield", async () => {
409
- // Arrange
410
- const testId = "example-testid";
411
-
412
- // Act
413
- render(
414
- <LabeledTextField
415
- label="Label"
416
- value=""
417
- onChange={() => {}}
418
- testId={testId}
419
- />,
420
- );
421
-
422
- // Assert
423
- const input = await screen.findByRole("textbox");
424
- expect(input).toHaveAttribute("data-testid", `${testId}-field`);
425
- });
426
-
427
- it("readOnly prop is passed to textfield", async () => {
428
- // Arrange
429
-
430
- // Act
431
- render(
432
- <LabeledTextField
433
- label="Label"
434
- value=""
435
- onChange={() => {}}
436
- readOnly={true}
437
- />,
438
- );
439
-
440
- // Assert
441
- const input = await screen.findByRole("textbox");
442
- expect(input).toHaveAttribute("readOnly");
443
- });
444
-
445
- it("autoComplete prop is passed to textfield", async () => {
446
- // Arrange
447
- const autoComplete = "name";
448
-
449
- // Act
450
- render(
451
- <LabeledTextField
452
- label="Label"
453
- value=""
454
- onChange={() => {}}
455
- autoComplete={autoComplete}
456
- />,
457
- );
458
-
459
- // Assert
460
- const input = await screen.findByRole("textbox");
461
- expect(input).toHaveAttribute("autoComplete", autoComplete);
462
- });
463
-
464
- it("aria-invalid is set true if given an invalid input", async () => {
465
- // Arrange
466
- const handleValidate = jest.fn((errorMessage?: string | null) => {});
467
-
468
- const validate = (value: string): string | null | undefined => {
469
- if (value.length < 8) {
470
- return "Password must be at least 8 characters long";
471
- }
472
- };
473
-
474
- const TextFieldWrapper = () => {
475
- const [value, setValue] = React.useState("LongerThan8Chars");
476
-
477
- return (
478
- <LabeledTextField
479
- label="Label"
480
- value={value}
481
- onChange={setValue}
482
- validate={validate}
483
- onValidate={handleValidate}
484
- />
485
- );
486
- };
487
-
488
- render(<TextFieldWrapper />);
489
-
490
- // Act
491
- // Select all text and replace it with the new value.
492
- const textbox = await screen.findByRole("textbox");
493
- await userEvent.click(textbox);
494
- await userEvent.clear(textbox);
495
- await userEvent.paste("Short");
496
-
497
- // Assert
498
- expect(textbox).toHaveAttribute("aria-invalid", "true");
499
- });
500
-
501
- it("aria-invalid is set false if given a valid input", async () => {
502
- // Arrange
503
- const handleValidate = jest.fn((errorMessage?: string | null) => {});
504
-
505
- const validate = (value: string): string | null | undefined => {
506
- if (value.length < 8) {
507
- return "Password must be at least 8 characters long";
508
- }
509
- };
510
-
511
- const TextFieldWrapper = () => {
512
- const [value, setValue] = React.useState("Short");
513
-
514
- return (
515
- <LabeledTextField
516
- label="Label"
517
- value={value}
518
- onChange={setValue}
519
- validate={validate}
520
- onValidate={handleValidate}
521
- />
522
- );
523
- };
524
-
525
- render(<TextFieldWrapper />);
526
-
527
- // Act
528
- // Select all text and replace it with the new value.
529
- const textbox = await screen.findByRole("textbox");
530
- await userEvent.click(textbox);
531
- await userEvent.clear(textbox);
532
- await userEvent.paste("LongerThan8Chars");
533
- const shortTextbox = await screen.findByRole("textbox");
534
-
535
- // Assert
536
- expect(shortTextbox).toHaveAttribute("aria-invalid", "false");
537
- });
538
- });
539
-
540
- describe("Required LabeledTextField", () => {
541
- test("has * when `required` prop is true", async () => {
542
- // Arrange
543
-
544
- // Act
545
- render(
546
- <LabeledTextField
547
- label="Label"
548
- value=""
549
- onChange={() => {}}
550
- required={true}
551
- />,
552
- );
553
-
554
- // Assert
555
- expect(await screen.findByText("*")).toBeInTheDocument();
556
- });
557
-
558
- test("does not have * when `required` prop is false", async () => {
559
- // Arrange
560
-
561
- // Act
562
- render(
563
- <LabeledTextField
564
- label="Label"
565
- value=""
566
- onChange={() => {}}
567
- required={false}
568
- />,
569
- );
570
-
571
- // Assert
572
- expect(screen.queryByText("*")).not.toBeInTheDocument();
573
- });
574
-
575
- test("aria-required is true when `required` prop is true", async () => {
576
- // Arrange
577
-
578
- // Act
579
- render(
580
- <LabeledTextField
581
- label="Label"
582
- value=""
583
- onChange={() => {}}
584
- testId="foo-labeled-text-field"
585
- required={true}
586
- />,
587
- );
588
-
589
- const textField = await screen.findByTestId(
590
- "foo-labeled-text-field-field",
591
- );
592
-
593
- // Assert
594
- expect(textField).toHaveAttribute("aria-required", "true");
595
- });
596
-
597
- test("aria-required is false when `required` prop is false", async () => {
598
- // Arrange
599
-
600
- // Act
601
- render(
602
- <LabeledTextField
603
- label="Label"
604
- value=""
605
- onChange={() => {}}
606
- testId="foo-labeled-text-field"
607
- required={false}
608
- />,
609
- );
610
-
611
- const textField = await screen.findByTestId(
612
- "foo-labeled-text-field-field",
613
- );
614
-
615
- // Assert
616
- expect(textField).toHaveAttribute("aria-required", "false");
617
- });
618
-
619
- test("displays the default message when the `required` prop is `true`", async () => {
620
- // Arrange
621
- const TextFieldWrapper = () => {
622
- const [value, setValue] = React.useState("");
623
- return (
624
- <LabeledTextField
625
- label="Label"
626
- value={value}
627
- onChange={setValue}
628
- required={true}
629
- testId="test-labeled-text-field"
630
- />
631
- );
632
- };
633
-
634
- render(<TextFieldWrapper />);
635
-
636
- const textField = await screen.findByTestId(
637
- "test-labeled-text-field-field",
638
- );
639
- textField.focus();
640
- await userEvent.type(textField, "a");
641
- await userEvent.clear(textField);
642
-
643
- // Act
644
- textField.blur();
645
-
646
- // Assert
647
- expect(await screen.findByRole("alert")).toHaveTextContent(
648
- "This field is required.",
649
- );
650
- });
651
-
652
- test("displays the string passed into `required`", async () => {
653
- // Arrange
654
- const errorMessage = "This is an example error message.";
655
-
656
- const TextFieldWrapper = () => {
657
- const [value, setValue] = React.useState("");
658
- return (
659
- <LabeledTextField
660
- label="Label"
661
- value={value}
662
- onChange={setValue}
663
- required={errorMessage}
664
- testId="test-labeled-text-field"
665
- />
666
- );
667
- };
668
-
669
- render(<TextFieldWrapper />);
670
-
671
- const textField = await screen.findByTestId(
672
- "test-labeled-text-field-field",
673
- );
674
- textField.focus();
675
- await userEvent.type(textField, "a");
676
- await userEvent.clear(textField);
677
-
678
- // Act
679
- textField.blur();
680
-
681
- // Assert
682
- expect(await screen.findByRole("alert")).toHaveTextContent(
683
- errorMessage,
684
- );
685
- });
686
-
687
- test("displays an error even when onValidate is set", async () => {
688
- // Arrange
689
- const errorMessage = "Empty string!";
690
-
691
- const validate = (value: string): string | null | undefined => {
692
- if (value === "") {
693
- return errorMessage;
694
- }
695
- };
696
-
697
- const TextFieldWrapper = () => {
698
- const [value, setValue] = React.useState("initial");
699
- return (
700
- <LabeledTextField
701
- label="Label"
702
- value={value}
703
- onChange={setValue}
704
- validate={validate}
705
- onValidate={jest.fn()}
706
- testId="test-labeled-text-field"
707
- />
708
- );
709
- };
710
-
711
- render(<TextFieldWrapper />);
712
-
713
- const textField = await screen.findByTestId(
714
- "test-labeled-text-field-field",
715
- );
716
- textField.focus();
717
- await userEvent.clear(textField);
718
-
719
- // Act
720
- textField.blur();
721
-
722
- // Assert
723
- expect(await screen.findByRole("alert")).toHaveTextContent(
724
- errorMessage,
725
- );
726
- });
727
- });