@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.
- package/CHANGELOG.md +21 -0
- package/dist/components/checkbox-core.d.ts +2 -2
- package/dist/components/checkbox.d.ts +2 -2
- package/dist/components/choice-internal.d.ts +2 -2
- package/dist/components/choice.d.ts +2 -2
- package/dist/components/labeled-text-field.d.ts +9 -1
- package/dist/components/radio-core.d.ts +2 -2
- package/dist/components/radio.d.ts +2 -2
- package/dist/components/text-area.d.ts +2 -2
- package/dist/components/text-field.d.ts +10 -5
- package/dist/es/index.js +93 -48
- package/dist/index.js +93 -48
- package/package.json +7 -7
- package/src/__tests__/__snapshots__/custom-snapshot.test.tsx.snap +0 -247
- package/src/__tests__/custom-snapshot.test.tsx +0 -48
- package/src/components/__tests__/checkbox-group.test.tsx +0 -162
- package/src/components/__tests__/checkbox.test.tsx +0 -138
- package/src/components/__tests__/field-heading.test.tsx +0 -225
- package/src/components/__tests__/labeled-text-field.test.tsx +0 -750
- package/src/components/__tests__/radio-group.test.tsx +0 -182
- package/src/components/__tests__/text-area.test.tsx +0 -1286
- package/src/components/__tests__/text-field.test.tsx +0 -562
- package/src/components/checkbox-core.tsx +0 -239
- package/src/components/checkbox-group.tsx +0 -174
- package/src/components/checkbox.tsx +0 -99
- package/src/components/choice-internal.tsx +0 -184
- package/src/components/choice.tsx +0 -157
- package/src/components/field-heading.tsx +0 -169
- package/src/components/group-styles.ts +0 -33
- package/src/components/labeled-text-field.tsx +0 -317
- package/src/components/radio-core.tsx +0 -171
- package/src/components/radio-group.tsx +0 -159
- package/src/components/radio.tsx +0 -82
- package/src/components/text-area.tsx +0 -430
- package/src/components/text-field.tsx +0 -399
- package/src/index.ts +0 -17
- package/src/util/types.ts +0 -85
- package/tsconfig-build.json +0 -19
- 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
|
-
});
|