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