@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.
@@ -1,35 +1,27 @@
1
1
  // @flow
2
2
  import * as React from "react";
3
- import {mount} from "enzyme";
4
- import "jest-enzyme";
3
+ import {fireEvent, render, screen} from "@testing-library/react";
4
+ import userEvent from "@testing-library/user-event";
5
5
 
6
6
  import TextField from "../text-field.js";
7
7
 
8
- const wait = (delay: number = 0) =>
9
- new Promise((resolve, reject) => {
10
- // eslint-disable-next-line no-restricted-syntax
11
- return setTimeout(resolve, delay);
12
- });
13
-
14
8
  describe("TextField", () => {
15
9
  it("textfield is focused", () => {
16
10
  // Arrange
17
- const wrapper = mount(
18
- <TextField id="tf-1" value="" onChange={() => {}} />,
19
- );
11
+ render(<TextField id="tf-1" value="" onChange={() => {}} />);
20
12
 
21
13
  // Act
22
- wrapper.simulate("focus");
14
+ userEvent.tab();
23
15
 
24
16
  // Assert
25
- expect(wrapper.find("TextFieldInternal")).toHaveState("focused", true);
17
+ expect(screen.getByRole("textbox")).toHaveFocus();
26
18
  });
27
19
 
28
20
  it("onFocus is called after textfield is focused", () => {
29
21
  // Arrange
30
22
  const handleOnFocus = jest.fn(() => {});
31
23
 
32
- const wrapper = mount(
24
+ render(
33
25
  <TextField
34
26
  id={"tf-1"}
35
27
  value="TextIsLongerThan8"
@@ -39,7 +31,7 @@ describe("TextField", () => {
39
31
  );
40
32
 
41
33
  // Act
42
- wrapper.simulate("focus");
34
+ userEvent.tab();
43
35
 
44
36
  // Assert
45
37
  expect(handleOnFocus).toHaveBeenCalled();
@@ -47,24 +39,24 @@ describe("TextField", () => {
47
39
 
48
40
  it("textfield is blurred", async () => {
49
41
  // Arrange
50
- const wrapper = mount(
51
- <TextField id="tf-1" value="" onChange={() => {}} />,
52
- );
42
+ render(<TextField id="tf-1" value="" onChange={() => {}} />);
43
+
44
+ // focus
45
+ userEvent.tab();
53
46
 
54
47
  // Act
55
- wrapper.simulate("focus");
56
- await wait(0);
57
- wrapper.simulate("blur");
48
+ // blur
49
+ userEvent.tab();
58
50
 
59
51
  // Assert
60
- expect(wrapper.find("TextFieldInternal")).toHaveState("focused", false);
52
+ expect(screen.getByRole("textbox")).not.toHaveFocus();
61
53
  });
62
54
 
63
55
  it("onBlur is called after textfield is blurred", async () => {
64
56
  // Arrange
65
57
  const handleOnBlur = jest.fn(() => {});
66
58
 
67
- const wrapper = mount(
59
+ render(
68
60
  <TextField
69
61
  id={"tf-1"}
70
62
  value="TextIsLongerThan8"
@@ -73,10 +65,12 @@ describe("TextField", () => {
73
65
  />,
74
66
  );
75
67
 
68
+ // focus
69
+ userEvent.tab();
70
+
76
71
  // Act
77
- wrapper.simulate("focus");
78
- await wait(0);
79
- wrapper.simulate("blur");
72
+ // blur
73
+ userEvent.tab();
80
74
 
81
75
  // Assert
82
76
  expect(handleOnBlur).toHaveBeenCalled();
@@ -87,13 +81,11 @@ describe("TextField", () => {
87
81
  const id: string = "tf-1";
88
82
 
89
83
  // Act
90
- const wrapper = mount(
91
- <TextField id={id} value="" onChange={() => {}} />,
92
- );
84
+ render(<TextField id={id} value="" onChange={() => {}} />);
93
85
 
94
86
  // Assert
95
- const input = wrapper.find("input");
96
- expect(input).toContainMatchingElement(`[id="${id}"]`);
87
+ const input = screen.getByRole("textbox");
88
+ expect(input).toHaveAttribute("id", id);
97
89
  });
98
90
 
99
91
  it("type prop is passed to the input element", () => {
@@ -101,13 +93,14 @@ describe("TextField", () => {
101
93
  const type = "number";
102
94
 
103
95
  // Act
104
- const wrapper = mount(
96
+ render(
105
97
  <TextField id={"tf-1"} type={type} value="" onChange={() => {}} />,
106
98
  );
107
99
 
108
100
  // Assert
109
- const input = wrapper.find("input");
110
- expect(input).toContainMatchingElement(`[type="${type}"]`);
101
+ // NOTE: The implicit role for input[type=number] is "spinbutton".
102
+ const input = screen.getByRole("spinbutton");
103
+ expect(input).toHaveAttribute("type", type);
111
104
  });
112
105
 
113
106
  it("value prop is passed to the input element", () => {
@@ -115,18 +108,16 @@ describe("TextField", () => {
115
108
  const value = "Text";
116
109
 
117
110
  // Act
118
- const wrapper = mount(
119
- <TextField id={"tf-1"} value={value} onChange={() => {}} />,
120
- );
111
+ render(<TextField id={"tf-1"} value={value} onChange={() => {}} />);
121
112
 
122
113
  // Assert
123
- const input = wrapper.find("input");
124
- expect(input).toContainMatchingElement(`[value="${value}"]`);
114
+ const input = screen.getByDisplayValue(value);
115
+ expect(input).toBeInTheDocument();
125
116
  });
126
117
 
127
118
  it("disabled prop disables the input element", () => {
128
119
  // Arrange
129
- const wrapper = mount(
120
+ render(
130
121
  <TextField
131
122
  id="tf-1"
132
123
  value=""
@@ -134,7 +125,7 @@ describe("TextField", () => {
134
125
  disabled={true}
135
126
  />,
136
127
  );
137
- const input = wrapper.find("input");
128
+ const input = screen.getByRole("textbox");
138
129
 
139
130
  // Act
140
131
 
@@ -146,13 +137,17 @@ describe("TextField", () => {
146
137
  // Arrange
147
138
  const handleOnChange = jest.fn();
148
139
 
149
- const wrapper = mount(
140
+ render(
150
141
  <TextField id={"tf-1"} value="Text" onChange={handleOnChange} />,
151
142
  );
152
143
 
153
144
  // Act
154
145
  const newValue = "Test2";
155
- wrapper.simulate("change", {target: {value: newValue}});
146
+ const input = screen.getByRole("textbox");
147
+ // @see https://testing-library.com/docs/react-testing-library/faq
148
+ // How do I test input onChange handlers?
149
+ // eslint-disable-next-line testing-library/prefer-user-event
150
+ fireEvent.change(input, {target: {value: newValue}});
156
151
 
157
152
  // Assert
158
153
  expect(handleOnChange).toHaveBeenCalledWith(newValue);
@@ -162,7 +157,7 @@ describe("TextField", () => {
162
157
  // Arrange
163
158
  const handleValidate = jest.fn((value: string): ?string => {});
164
159
 
165
- const wrapper = mount(
160
+ render(
166
161
  <TextField
167
162
  id={"tf-1"}
168
163
  value="Text"
@@ -173,7 +168,8 @@ describe("TextField", () => {
173
168
 
174
169
  // Act
175
170
  const newValue = "Text2";
176
- wrapper.simulate("change", {target: {value: newValue}});
171
+ // Select all text and replace it with the new value.
172
+ userEvent.type(screen.getByRole("textbox"), `{selectall}${newValue}`);
177
173
 
178
174
  // Assert
179
175
  expect(handleValidate).toHaveBeenCalledWith(newValue);
@@ -187,7 +183,7 @@ describe("TextField", () => {
187
183
  }
188
184
  });
189
185
 
190
- const wrapper = mount(
186
+ render(
191
187
  <TextField
192
188
  id={"tf-1"}
193
189
  value="TextIsLong"
@@ -198,7 +194,8 @@ describe("TextField", () => {
198
194
 
199
195
  // Act
200
196
  const newValue = "TextIsLongerThan8";
201
- wrapper.simulate("change", {target: {value: newValue}});
197
+ // Select all text and replace it with the new value.
198
+ userEvent.type(screen.getByRole("textbox"), `{selectall}${newValue}`);
202
199
 
203
200
  // Assert
204
201
  expect(handleValidate).toHaveReturnedWith(undefined);
@@ -213,7 +210,7 @@ describe("TextField", () => {
213
210
  }
214
211
  });
215
212
 
216
- const wrapper = mount(
213
+ render(
217
214
  <TextField
218
215
  id={"tf-1"}
219
216
  value="TextIsLongerThan8"
@@ -224,7 +221,8 @@ describe("TextField", () => {
224
221
 
225
222
  // Act
226
223
  const newValue = "Text";
227
- wrapper.simulate("change", {target: {value: newValue}});
224
+ // Select all text and replace it with the new value.
225
+ userEvent.type(screen.getByRole("textbox"), `{selectall}${newValue}`);
228
226
 
229
227
  // Assert
230
228
  expect(handleValidate).toHaveReturnedWith(errorMessage);
@@ -240,7 +238,7 @@ describe("TextField", () => {
240
238
  }
241
239
  });
242
240
 
243
- const wrapper = mount(
241
+ render(
244
242
  <TextField
245
243
  id={"tf-1"}
246
244
  value="TextIsLongerThan8"
@@ -252,7 +250,8 @@ describe("TextField", () => {
252
250
 
253
251
  // Act
254
252
  const newValue = "Text";
255
- wrapper.simulate("change", {target: {value: newValue}});
253
+ // Select all text and replace it with the new value.
254
+ userEvent.type(screen.getByRole("textbox"), `{selectall}${newValue}`);
256
255
 
257
256
  // Assert
258
257
  expect(handleValidate).toHaveBeenCalledWith(errorMessage);
@@ -269,7 +268,7 @@ describe("TextField", () => {
269
268
  });
270
269
 
271
270
  // Act
272
- mount(
271
+ render(
273
272
  <TextField
274
273
  id={"tf-1"}
275
274
  value="Short"
@@ -291,7 +290,7 @@ describe("TextField", () => {
291
290
  },
292
291
  );
293
292
 
294
- const wrapper = mount(
293
+ render(
295
294
  <TextField
296
295
  id={"tf-1"}
297
296
  value="TextIsLongerThan8"
@@ -301,12 +300,10 @@ describe("TextField", () => {
301
300
  );
302
301
 
303
302
  // Act
304
- const key = "Enter";
305
- const input = wrapper.find("input");
306
- input.simulate("keyDown", {key: key});
303
+ userEvent.type(screen.getByRole("textbox"), "{enter}");
307
304
 
308
305
  // Assert
309
- expect(handleOnKeyDown).toHaveReturnedWith(key);
306
+ expect(handleOnKeyDown).toHaveReturnedWith("Enter");
310
307
  });
311
308
 
312
309
  it("placeholder prop is passed to the input element", () => {
@@ -314,7 +311,7 @@ describe("TextField", () => {
314
311
  const placeholder = "Placeholder";
315
312
 
316
313
  // Act
317
- const wrapper = mount(
314
+ render(
318
315
  <TextField
319
316
  id={"tf-1"}
320
317
  value="Text"
@@ -324,16 +321,14 @@ describe("TextField", () => {
324
321
  );
325
322
 
326
323
  // Assert
327
- const input = wrapper.find("input");
328
- expect(input).toContainMatchingElement(
329
- `[placeholder="${placeholder}"]`,
330
- );
324
+ const input = screen.getByPlaceholderText(placeholder);
325
+ expect(input).toBeInTheDocument();
331
326
  });
332
327
 
333
328
  it("testId is passed to the input element", () => {
334
329
  // Arrange
335
330
  const testId = "some-test-id";
336
- const wrapper = mount(
331
+ render(
337
332
  <TextField
338
333
  id={"tf-1"}
339
334
  value="Text"
@@ -345,14 +340,14 @@ describe("TextField", () => {
345
340
  // Act
346
341
 
347
342
  // Assert
348
- const input = wrapper.find("input");
349
- expect(input).toContainMatchingElement(`[data-test-id="${testId}"]`);
343
+ const input = screen.getByRole("textbox");
344
+ expect(input).toHaveAttribute("data-test-id", testId);
350
345
  });
351
346
 
352
347
  it("aria props are passed to the input element", () => {
353
348
  // Arrange
354
349
  const ariaLabel = "example-text-field";
355
- const wrapper = mount(
350
+ render(
356
351
  <TextField
357
352
  id={"tf-1"}
358
353
  value="Text"
@@ -364,15 +359,15 @@ describe("TextField", () => {
364
359
  // Act
365
360
 
366
361
  // Assert
367
- const input = wrapper.find("input");
368
- expect(input).toContainMatchingElement(`[aria-label="${ariaLabel}"]`);
362
+ const input = screen.getByRole("textbox");
363
+ expect(input).toHaveAttribute("aria-label", ariaLabel);
369
364
  });
370
365
 
371
366
  it("readOnly prop is passed to the input element", async () => {
372
367
  // Arrange
373
368
 
374
369
  // Act
375
- const wrapper = mount(
370
+ render(
376
371
  <TextField
377
372
  id={"tf-1"}
378
373
  value={"Text"}
@@ -382,8 +377,8 @@ describe("TextField", () => {
382
377
  );
383
378
 
384
379
  // Assert
385
- const input = wrapper.find("input");
386
- expect(input).toHaveProp("readOnly");
380
+ const input = screen.getByRole("textbox");
381
+ expect(input).toHaveAttribute("readOnly");
387
382
  });
388
383
 
389
384
  it("autoComplete prop is passed to the input element", async () => {
@@ -391,7 +386,7 @@ describe("TextField", () => {
391
386
  const autoComplete = "name";
392
387
 
393
388
  // Act
394
- const wrapper = mount(
389
+ render(
395
390
  <TextField
396
391
  id={"tf-1"}
397
392
  value={"Text"}
@@ -401,7 +396,7 @@ describe("TextField", () => {
401
396
  );
402
397
 
403
398
  // Assert
404
- const input = wrapper.find("input");
405
- expect(input).toHaveProp("autoComplete", autoComplete);
399
+ const input = screen.getByRole("textbox");
400
+ expect(input).toHaveAttribute("autoComplete", autoComplete);
406
401
  });
407
402
  });
@@ -163,15 +163,11 @@ type State = {|
163
163
  focused: boolean,
164
164
  |};
165
165
 
166
- // TODO(WB-1081): Change class name back to LabeledTextField after Styleguidist is gone.
167
166
  /**
168
167
  * A LabeledTextField is an element used to accept a single line of text
169
168
  * from the user paired with a label, description, and error field elements.
170
169
  */
171
- class LabeledTextFieldInternal extends React.Component<
172
- PropsWithForwardRef,
173
- State,
174
- > {
170
+ class LabeledTextField extends React.Component<PropsWithForwardRef, State> {
175
171
  static defaultProps: DefaultProps = {
176
172
  type: "text",
177
173
  disabled: false,
@@ -288,7 +284,7 @@ class LabeledTextFieldInternal extends React.Component<
288
284
  }
289
285
 
290
286
  type ExportProps = $Diff<
291
- React.ElementConfig<typeof LabeledTextFieldInternal>,
287
+ React.ElementConfig<typeof LabeledTextField>,
292
288
  WithForwardRef,
293
289
  >;
294
290
 
@@ -312,9 +308,7 @@ type ExportProps = $Diff<
312
308
  * />
313
309
  * ```
314
310
  */
315
- const LabeledTextField: React.AbstractComponent<ExportProps, HTMLInputElement> =
316
- React.forwardRef<ExportProps, HTMLInputElement>((props, ref) => (
317
- <LabeledTextFieldInternal {...props} forwardedRef={ref} />
318
- ));
319
311
 
320
- export default LabeledTextField;
312
+ export default (React.forwardRef<ExportProps, HTMLInputElement>(
313
+ (props, ref) => <LabeledTextField {...props} forwardedRef={ref} />,
314
+ ): React.AbstractComponent<ExportProps, HTMLInputElement>);
@@ -146,11 +146,10 @@ type State = {|
146
146
  focused: boolean,
147
147
  |};
148
148
 
149
- // TODO(WB-1081): Change class name back to TextField after Styleguidist is gone.
150
149
  /**
151
150
  * A TextField is an element used to accept a single line of text from the user.
152
151
  */
153
- class TextFieldInternal extends React.Component<PropsWithForwardRef, State> {
152
+ class TextField extends React.Component<PropsWithForwardRef, State> {
154
153
  static defaultProps: DefaultProps = {
155
154
  type: "text",
156
155
  disabled: false,
@@ -343,10 +342,7 @@ const styles = StyleSheet.create({
343
342
  },
344
343
  });
345
344
 
346
- type ExportProps = $Diff<
347
- React.ElementConfig<typeof TextFieldInternal>,
348
- WithForwardRef,
349
- >;
345
+ type ExportProps = $Diff<React.ElementConfig<typeof TextField>, WithForwardRef>;
350
346
 
351
347
  /**
352
348
  * A TextField is an element used to accept a single line of text from the user.
@@ -365,9 +361,6 @@ type ExportProps = $Diff<
365
361
  * />
366
362
  * ```
367
363
  */
368
- const TextField: React.AbstractComponent<ExportProps, HTMLInputElement> =
369
- React.forwardRef<ExportProps, HTMLInputElement>((props, ref) => (
370
- <TextFieldInternal {...props} forwardedRef={ref} />
371
- ));
372
-
373
- export default TextField;
364
+ export default (React.forwardRef<ExportProps, HTMLInputElement>(
365
+ (props, ref) => <TextField {...props} forwardedRef={ref} />,
366
+ ): React.AbstractComponent<ExportProps, HTMLInputElement>);