@khanacademy/wonder-blocks-button 2.9.13
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/LICENSE +21 -0
- package/dist/es/index.js +402 -0
- package/dist/index.js +668 -0
- package/dist/index.js.flow +2 -0
- package/docs.md +0 -0
- package/package.json +37 -0
- package/src/__tests__/__snapshots__/custom-snapshot.test.js.snap +8710 -0
- package/src/__tests__/__snapshots__/generated-snapshot.test.js.snap +4774 -0
- package/src/__tests__/custom-snapshot.test.js +117 -0
- package/src/__tests__/generated-snapshot.test.js +727 -0
- package/src/components/__tests__/button.flowtest.js +53 -0
- package/src/components/__tests__/button.test.js +826 -0
- package/src/components/button-core.js +375 -0
- package/src/components/button.js +347 -0
- package/src/components/button.md +810 -0
- package/src/components/button.stories.js +276 -0
- package/src/index.js +6 -0
|
@@ -0,0 +1,826 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import {MemoryRouter, Route, Switch} from "react-router-dom";
|
|
4
|
+
import {mount} from "enzyme";
|
|
5
|
+
|
|
6
|
+
import Button from "../button.js";
|
|
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
|
+
const keyCodes = {
|
|
15
|
+
tab: 9,
|
|
16
|
+
enter: 13,
|
|
17
|
+
space: 32,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
describe("Button", () => {
|
|
21
|
+
test("client-side navigation", () => {
|
|
22
|
+
// Arrange
|
|
23
|
+
const wrapper = mount(
|
|
24
|
+
<MemoryRouter>
|
|
25
|
+
<div>
|
|
26
|
+
<Button href="/foo">Click me!</Button>
|
|
27
|
+
<Switch>
|
|
28
|
+
<Route path="/foo">
|
|
29
|
+
<div id="foo">Hello, world!</div>
|
|
30
|
+
</Route>
|
|
31
|
+
</Switch>
|
|
32
|
+
</div>
|
|
33
|
+
</MemoryRouter>,
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
// Act
|
|
37
|
+
const buttonWrapper = wrapper.find("Button");
|
|
38
|
+
buttonWrapper.simulate("click", {button: 0});
|
|
39
|
+
|
|
40
|
+
// Assert
|
|
41
|
+
expect(wrapper.find("#foo")).toExist();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("beforeNav rejection blocks client-side navigation", async () => {
|
|
45
|
+
// Arrange
|
|
46
|
+
const wrapper = mount(
|
|
47
|
+
<MemoryRouter>
|
|
48
|
+
<div>
|
|
49
|
+
<Button href="/foo" beforeNav={(e) => Promise.reject()}>
|
|
50
|
+
Click me!
|
|
51
|
+
</Button>
|
|
52
|
+
<Switch>
|
|
53
|
+
<Route path="/foo">
|
|
54
|
+
<div id="foo">Hello, world!</div>
|
|
55
|
+
</Route>
|
|
56
|
+
</Switch>
|
|
57
|
+
</div>
|
|
58
|
+
</MemoryRouter>,
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
// Act
|
|
62
|
+
const buttonWrapper = wrapper.find("Button");
|
|
63
|
+
buttonWrapper.simulate("click", {button: 0});
|
|
64
|
+
await wait(0);
|
|
65
|
+
buttonWrapper.update();
|
|
66
|
+
|
|
67
|
+
// Assert
|
|
68
|
+
expect(wrapper.find("#foo")).not.toExist();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("beforeNav rejection blocks calling safeWithNav", async () => {
|
|
72
|
+
// Arrange
|
|
73
|
+
const safeWithNavMock = jest.fn();
|
|
74
|
+
const wrapper = mount(
|
|
75
|
+
<MemoryRouter>
|
|
76
|
+
<div>
|
|
77
|
+
<Button
|
|
78
|
+
href="/foo"
|
|
79
|
+
beforeNav={(e) => Promise.reject()}
|
|
80
|
+
safeWithNav={safeWithNavMock}
|
|
81
|
+
>
|
|
82
|
+
Click me!
|
|
83
|
+
</Button>
|
|
84
|
+
<Switch>
|
|
85
|
+
<Route path="/foo">
|
|
86
|
+
<div id="foo">Hello, world!</div>
|
|
87
|
+
</Route>
|
|
88
|
+
</Switch>
|
|
89
|
+
</div>
|
|
90
|
+
</MemoryRouter>,
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
// Act
|
|
94
|
+
const buttonWrapper = wrapper.find("Button");
|
|
95
|
+
buttonWrapper.simulate("click", {button: 0});
|
|
96
|
+
await wait(0);
|
|
97
|
+
buttonWrapper.update();
|
|
98
|
+
|
|
99
|
+
// Assert
|
|
100
|
+
expect(safeWithNavMock).not.toHaveBeenCalled();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("beforeNav resolution results in client-side navigation", async () => {
|
|
104
|
+
// Arrange
|
|
105
|
+
const wrapper = mount(
|
|
106
|
+
<MemoryRouter>
|
|
107
|
+
<div>
|
|
108
|
+
<Button href="/foo" beforeNav={(e) => Promise.resolve()}>
|
|
109
|
+
Click me!
|
|
110
|
+
</Button>
|
|
111
|
+
<Switch>
|
|
112
|
+
<Route path="/foo">
|
|
113
|
+
<div id="foo">Hello, world!</div>
|
|
114
|
+
</Route>
|
|
115
|
+
</Switch>
|
|
116
|
+
</div>
|
|
117
|
+
</MemoryRouter>,
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
// Act
|
|
121
|
+
const buttonWrapper = wrapper.find("Button");
|
|
122
|
+
buttonWrapper.simulate("click", {button: 0});
|
|
123
|
+
await wait(0);
|
|
124
|
+
buttonWrapper.update();
|
|
125
|
+
|
|
126
|
+
// Assert
|
|
127
|
+
expect(wrapper.find("#foo")).toExist();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("beforeNav resolution results in safeWithNav being called", async () => {
|
|
131
|
+
// Arrange
|
|
132
|
+
const safeWithNavMock = jest.fn();
|
|
133
|
+
const wrapper = mount(
|
|
134
|
+
<MemoryRouter>
|
|
135
|
+
<div>
|
|
136
|
+
<Button
|
|
137
|
+
href="/foo"
|
|
138
|
+
beforeNav={(e) => Promise.resolve()}
|
|
139
|
+
safeWithNav={safeWithNavMock}
|
|
140
|
+
>
|
|
141
|
+
Click me!
|
|
142
|
+
</Button>
|
|
143
|
+
<Switch>
|
|
144
|
+
<Route path="/foo">
|
|
145
|
+
<div id="foo">Hello, world!</div>
|
|
146
|
+
</Route>
|
|
147
|
+
</Switch>
|
|
148
|
+
</div>
|
|
149
|
+
</MemoryRouter>,
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
// Act
|
|
153
|
+
const buttonWrapper = wrapper.find("Button");
|
|
154
|
+
buttonWrapper.simulate("click", {button: 0});
|
|
155
|
+
await wait(0);
|
|
156
|
+
buttonWrapper.update();
|
|
157
|
+
|
|
158
|
+
// Assert
|
|
159
|
+
expect(safeWithNavMock).toHaveBeenCalled();
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test("show circular spinner before beforeNav resolves", async () => {
|
|
163
|
+
// Arrange
|
|
164
|
+
const wrapper = mount(
|
|
165
|
+
<MemoryRouter>
|
|
166
|
+
<div>
|
|
167
|
+
<Button href="/foo" beforeNav={(e) => Promise.resolve()}>
|
|
168
|
+
Click me!
|
|
169
|
+
</Button>
|
|
170
|
+
<Switch>
|
|
171
|
+
<Route path="/foo">
|
|
172
|
+
<div id="foo">Hello, world!</div>
|
|
173
|
+
</Route>
|
|
174
|
+
</Switch>
|
|
175
|
+
</div>
|
|
176
|
+
</MemoryRouter>,
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
// Act
|
|
180
|
+
const buttonWrapper = wrapper.find("Button");
|
|
181
|
+
buttonWrapper.simulate("click", {button: 0});
|
|
182
|
+
|
|
183
|
+
// Assert
|
|
184
|
+
// We want the button to look exactly the same as if someone had passed
|
|
185
|
+
// `spinner={true}` as a prop.
|
|
186
|
+
expect(wrapper.find("ButtonCore")).toHaveProp({spinner: true});
|
|
187
|
+
expect(wrapper.find("CircularSpinner")).toExist();
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
test("safeWithNav with skipClientNav=true waits for promise resolution", async () => {
|
|
191
|
+
// Arrange
|
|
192
|
+
jest.spyOn(window.location, "assign");
|
|
193
|
+
const wrapper = mount(
|
|
194
|
+
<MemoryRouter>
|
|
195
|
+
<div>
|
|
196
|
+
<Button
|
|
197
|
+
href="/foo"
|
|
198
|
+
safeWithNav={(e) => Promise.resolve()}
|
|
199
|
+
skipClientNav={true}
|
|
200
|
+
>
|
|
201
|
+
Click me!
|
|
202
|
+
</Button>
|
|
203
|
+
<Switch>
|
|
204
|
+
<Route path="/foo">
|
|
205
|
+
<div id="foo">Hello, world!</div>
|
|
206
|
+
</Route>
|
|
207
|
+
</Switch>
|
|
208
|
+
</div>
|
|
209
|
+
</MemoryRouter>,
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
// Act
|
|
213
|
+
const buttonWrapper = wrapper.find("Button");
|
|
214
|
+
buttonWrapper.simulate("click", {button: 0});
|
|
215
|
+
await wait(0);
|
|
216
|
+
buttonWrapper.update();
|
|
217
|
+
|
|
218
|
+
// Assert
|
|
219
|
+
expect(window.location.assign).toHaveBeenCalledWith("/foo");
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
test("safeWithNav with skipClientNav=true shows spinner", async () => {
|
|
223
|
+
// Arrange
|
|
224
|
+
jest.spyOn(window.location, "assign");
|
|
225
|
+
const wrapper = mount(
|
|
226
|
+
<MemoryRouter>
|
|
227
|
+
<div>
|
|
228
|
+
<Button
|
|
229
|
+
href="/foo"
|
|
230
|
+
safeWithNav={(e) => Promise.resolve()}
|
|
231
|
+
skipClientNav={true}
|
|
232
|
+
>
|
|
233
|
+
Click me!
|
|
234
|
+
</Button>
|
|
235
|
+
<Switch>
|
|
236
|
+
<Route path="/foo">
|
|
237
|
+
<div id="foo">Hello, world!</div>
|
|
238
|
+
</Route>
|
|
239
|
+
</Switch>
|
|
240
|
+
</div>
|
|
241
|
+
</MemoryRouter>,
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
// Act
|
|
245
|
+
const buttonWrapper = wrapper.find("Button");
|
|
246
|
+
buttonWrapper.simulate("click", {button: 0});
|
|
247
|
+
|
|
248
|
+
// Assert
|
|
249
|
+
expect(wrapper.find("CircularSpinner")).toExist();
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
test("beforeNav resolution and safeWithNav with skipClientNav=true waits for promise resolution", async () => {
|
|
253
|
+
// Arrange
|
|
254
|
+
jest.spyOn(window.location, "assign");
|
|
255
|
+
const wrapper = mount(
|
|
256
|
+
<MemoryRouter>
|
|
257
|
+
<div>
|
|
258
|
+
<Button
|
|
259
|
+
href="/foo"
|
|
260
|
+
beforeNav={(e) => Promise.resolve()}
|
|
261
|
+
safeWithNav={(e) => Promise.resolve()}
|
|
262
|
+
skipClientNav={true}
|
|
263
|
+
>
|
|
264
|
+
Click me!
|
|
265
|
+
</Button>
|
|
266
|
+
<Switch>
|
|
267
|
+
<Route path="/foo">
|
|
268
|
+
<div id="foo">Hello, world!</div>
|
|
269
|
+
</Route>
|
|
270
|
+
</Switch>
|
|
271
|
+
</div>
|
|
272
|
+
</MemoryRouter>,
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
// Act
|
|
276
|
+
const buttonWrapper = wrapper.find("Button");
|
|
277
|
+
buttonWrapper.simulate("click", {button: 0});
|
|
278
|
+
await wait(0);
|
|
279
|
+
buttonWrapper.update();
|
|
280
|
+
await wait(0);
|
|
281
|
+
buttonWrapper.update();
|
|
282
|
+
|
|
283
|
+
// Assert
|
|
284
|
+
expect(window.location.assign).toHaveBeenCalledWith("/foo");
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
test("safeWithNav with skipClientNav=true waits for promise rejection", async () => {
|
|
288
|
+
// Arrange
|
|
289
|
+
jest.spyOn(window.location, "assign");
|
|
290
|
+
const wrapper = mount(
|
|
291
|
+
<MemoryRouter>
|
|
292
|
+
<div>
|
|
293
|
+
<Button
|
|
294
|
+
href="/foo"
|
|
295
|
+
safeWithNav={(e) => Promise.reject()}
|
|
296
|
+
skipClientNav={true}
|
|
297
|
+
>
|
|
298
|
+
Click me!
|
|
299
|
+
</Button>
|
|
300
|
+
<Switch>
|
|
301
|
+
<Route path="/foo">
|
|
302
|
+
<div id="foo">Hello, world!</div>
|
|
303
|
+
</Route>
|
|
304
|
+
</Switch>
|
|
305
|
+
</div>
|
|
306
|
+
</MemoryRouter>,
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
// Act
|
|
310
|
+
const buttonWrapper = wrapper.find("Button");
|
|
311
|
+
buttonWrapper.simulate("click", {button: 0});
|
|
312
|
+
await wait(0);
|
|
313
|
+
buttonWrapper.update();
|
|
314
|
+
|
|
315
|
+
// Assert
|
|
316
|
+
expect(window.location.assign).toHaveBeenCalledWith("/foo");
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
test("safeWithNav with skipClientNav=false calls safeWithNav but doesn't wait to navigate", async () => {
|
|
320
|
+
// Arrange
|
|
321
|
+
jest.spyOn(window.location, "assign");
|
|
322
|
+
const safeWithNavMock = jest.fn();
|
|
323
|
+
const wrapper = mount(
|
|
324
|
+
<MemoryRouter>
|
|
325
|
+
<div>
|
|
326
|
+
<Button
|
|
327
|
+
href="/foo"
|
|
328
|
+
safeWithNav={safeWithNavMock}
|
|
329
|
+
skipClientNav={false}
|
|
330
|
+
>
|
|
331
|
+
Click me!
|
|
332
|
+
</Button>
|
|
333
|
+
<Switch>
|
|
334
|
+
<Route path="/foo">
|
|
335
|
+
<div id="foo">Hello, world!</div>
|
|
336
|
+
</Route>
|
|
337
|
+
</Switch>
|
|
338
|
+
</div>
|
|
339
|
+
</MemoryRouter>,
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
// Act
|
|
343
|
+
const buttonWrapper = wrapper.find("Button");
|
|
344
|
+
buttonWrapper.simulate("click", {button: 0});
|
|
345
|
+
|
|
346
|
+
// Assert
|
|
347
|
+
expect(safeWithNavMock).toHaveBeenCalled();
|
|
348
|
+
expect(window.location.assign).toHaveBeenCalledWith("/foo");
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
test("safeWithNav with beforeNav resolution and skipClientNav=false calls safeWithNav but doesn't wait to navigate", async () => {
|
|
352
|
+
// Arrange
|
|
353
|
+
jest.spyOn(window.location, "assign");
|
|
354
|
+
const safeWithNavMock = jest.fn();
|
|
355
|
+
const wrapper = mount(
|
|
356
|
+
<MemoryRouter>
|
|
357
|
+
<div>
|
|
358
|
+
<Button
|
|
359
|
+
href="/foo"
|
|
360
|
+
beforeNav={() => Promise.resolve()}
|
|
361
|
+
safeWithNav={safeWithNavMock}
|
|
362
|
+
skipClientNav={false}
|
|
363
|
+
>
|
|
364
|
+
Click me!
|
|
365
|
+
</Button>
|
|
366
|
+
<Switch>
|
|
367
|
+
<Route path="/foo">
|
|
368
|
+
<div id="foo">Hello, world!</div>
|
|
369
|
+
</Route>
|
|
370
|
+
</Switch>
|
|
371
|
+
</div>
|
|
372
|
+
</MemoryRouter>,
|
|
373
|
+
);
|
|
374
|
+
|
|
375
|
+
// Act
|
|
376
|
+
const buttonWrapper = wrapper.find("Button");
|
|
377
|
+
buttonWrapper.simulate("click", {button: 0});
|
|
378
|
+
await wait(0);
|
|
379
|
+
buttonWrapper.update();
|
|
380
|
+
|
|
381
|
+
// Assert
|
|
382
|
+
expect(safeWithNavMock).toHaveBeenCalled();
|
|
383
|
+
expect(window.location.assign).toHaveBeenCalledWith("/foo");
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
test("client-side navigation with unknown URL fails", () => {
|
|
387
|
+
// Arrange
|
|
388
|
+
const wrapper = mount(
|
|
389
|
+
<MemoryRouter>
|
|
390
|
+
<div>
|
|
391
|
+
<Button href="/unknown">Click me!</Button>
|
|
392
|
+
<Switch>
|
|
393
|
+
<Route path="/foo">
|
|
394
|
+
<div id="foo">Hello, world!</div>
|
|
395
|
+
</Route>
|
|
396
|
+
</Switch>
|
|
397
|
+
</div>
|
|
398
|
+
</MemoryRouter>,
|
|
399
|
+
);
|
|
400
|
+
|
|
401
|
+
// Act
|
|
402
|
+
const buttonWrapper = wrapper.find("Button");
|
|
403
|
+
buttonWrapper.simulate("click", {button: 0});
|
|
404
|
+
|
|
405
|
+
// Assert
|
|
406
|
+
expect(wrapper.find("#foo")).not.toExist();
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
test("client-side navigation with `skipClientNav` set to `true` fails", () => {
|
|
410
|
+
// Arrange
|
|
411
|
+
const wrapper = mount(
|
|
412
|
+
<MemoryRouter>
|
|
413
|
+
<div>
|
|
414
|
+
<Button href="/foo" skipClientNav>
|
|
415
|
+
Click me!
|
|
416
|
+
</Button>
|
|
417
|
+
<Switch>
|
|
418
|
+
<Route path="/foo">
|
|
419
|
+
<div id="foo">Hello, world!</div>
|
|
420
|
+
</Route>
|
|
421
|
+
</Switch>
|
|
422
|
+
</div>
|
|
423
|
+
</MemoryRouter>,
|
|
424
|
+
);
|
|
425
|
+
|
|
426
|
+
// Act
|
|
427
|
+
const buttonWrapper = wrapper.find("Button");
|
|
428
|
+
buttonWrapper.simulate("click", {button: 0});
|
|
429
|
+
|
|
430
|
+
// Assert
|
|
431
|
+
expect(wrapper.find("#foo")).not.toExist();
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
test("disallow navigation when href and disabled are both set", () => {
|
|
435
|
+
// Arrange
|
|
436
|
+
const wrapper = mount(
|
|
437
|
+
<MemoryRouter>
|
|
438
|
+
<div>
|
|
439
|
+
<Button href="/foo" disabled={true}>
|
|
440
|
+
Click me!
|
|
441
|
+
</Button>
|
|
442
|
+
<Switch>
|
|
443
|
+
<Route path="/foo">
|
|
444
|
+
<div id="foo">Hello, world!</div>
|
|
445
|
+
</Route>
|
|
446
|
+
</Switch>
|
|
447
|
+
</div>
|
|
448
|
+
</MemoryRouter>,
|
|
449
|
+
);
|
|
450
|
+
|
|
451
|
+
// Act
|
|
452
|
+
const buttonWrapper = wrapper.find("Button");
|
|
453
|
+
buttonWrapper.simulate("click", {button: 0});
|
|
454
|
+
|
|
455
|
+
// Assert
|
|
456
|
+
expect(wrapper.find("#foo")).not.toExist();
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
test("don't call beforeNav when href and disabled are both set", () => {
|
|
460
|
+
// Arrange
|
|
461
|
+
const beforeNavMock = jest.fn();
|
|
462
|
+
const wrapper = mount(
|
|
463
|
+
<MemoryRouter>
|
|
464
|
+
<div>
|
|
465
|
+
<Button
|
|
466
|
+
href="/foo"
|
|
467
|
+
disabled={true}
|
|
468
|
+
beforeNav={beforeNavMock}
|
|
469
|
+
>
|
|
470
|
+
Click me!
|
|
471
|
+
</Button>
|
|
472
|
+
<Switch>
|
|
473
|
+
<Route path="/foo">
|
|
474
|
+
<div id="foo">Hello, world!</div>
|
|
475
|
+
</Route>
|
|
476
|
+
</Switch>
|
|
477
|
+
</div>
|
|
478
|
+
</MemoryRouter>,
|
|
479
|
+
);
|
|
480
|
+
|
|
481
|
+
// Act
|
|
482
|
+
const buttonWrapper = wrapper.find("Button");
|
|
483
|
+
buttonWrapper.simulate("click", {button: 0});
|
|
484
|
+
|
|
485
|
+
// Assert
|
|
486
|
+
expect(beforeNavMock).not.toHaveBeenCalled();
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
test("don't call safeWithNav when href and disabled are both set", () => {
|
|
490
|
+
// Arrange
|
|
491
|
+
const safeWithNavMock = jest.fn();
|
|
492
|
+
const wrapper = mount(
|
|
493
|
+
<MemoryRouter>
|
|
494
|
+
<div>
|
|
495
|
+
<Button
|
|
496
|
+
href="/foo"
|
|
497
|
+
disabled={true}
|
|
498
|
+
safeWithNav={safeWithNavMock}
|
|
499
|
+
>
|
|
500
|
+
Click me!
|
|
501
|
+
</Button>
|
|
502
|
+
<Switch>
|
|
503
|
+
<Route path="/foo">
|
|
504
|
+
<div id="foo">Hello, world!</div>
|
|
505
|
+
</Route>
|
|
506
|
+
</Switch>
|
|
507
|
+
</div>
|
|
508
|
+
</MemoryRouter>,
|
|
509
|
+
);
|
|
510
|
+
|
|
511
|
+
// Act
|
|
512
|
+
const buttonWrapper = wrapper.find("Button");
|
|
513
|
+
buttonWrapper.simulate("click", {button: 0});
|
|
514
|
+
|
|
515
|
+
// Assert
|
|
516
|
+
expect(safeWithNavMock).not.toHaveBeenCalled();
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
it("should set label on the underlying button", () => {
|
|
520
|
+
// Arrange
|
|
521
|
+
const wrapper = mount(
|
|
522
|
+
<Button id="foo" onClick={() => {}}>
|
|
523
|
+
Click me!
|
|
524
|
+
</Button>,
|
|
525
|
+
);
|
|
526
|
+
|
|
527
|
+
expect(wrapper.find("button")).toHaveProp({id: "foo"});
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
it("should set label on the underlying link", () => {
|
|
531
|
+
// Arrange
|
|
532
|
+
const wrapper = mount(
|
|
533
|
+
<Button id="foo" href="/bar">
|
|
534
|
+
Click me!
|
|
535
|
+
</Button>,
|
|
536
|
+
);
|
|
537
|
+
|
|
538
|
+
expect(wrapper.find("a")).toHaveProp({id: "foo"});
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
describe("client-side navigation with keyboard", () => {
|
|
542
|
+
it("should navigate on pressing the space key", () => {
|
|
543
|
+
// Arrange
|
|
544
|
+
const wrapper = mount(
|
|
545
|
+
<MemoryRouter>
|
|
546
|
+
<div>
|
|
547
|
+
<Button href="/foo">Click me!</Button>
|
|
548
|
+
<Switch>
|
|
549
|
+
<Route path="/foo">
|
|
550
|
+
<div id="foo">Hello, world!</div>
|
|
551
|
+
</Route>
|
|
552
|
+
</Switch>
|
|
553
|
+
</div>
|
|
554
|
+
</MemoryRouter>,
|
|
555
|
+
);
|
|
556
|
+
|
|
557
|
+
// Act
|
|
558
|
+
const buttonWrapper = wrapper.find("Button");
|
|
559
|
+
buttonWrapper.simulate("keydown", {
|
|
560
|
+
keyCode: keyCodes.space,
|
|
561
|
+
});
|
|
562
|
+
buttonWrapper.simulate("keyup", {
|
|
563
|
+
keyCode: keyCodes.space,
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
// Assert
|
|
567
|
+
expect(wrapper.find("#foo")).toExist();
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
it("should navigate on pressing the enter key", () => {
|
|
571
|
+
// Arrange
|
|
572
|
+
const wrapper = mount(
|
|
573
|
+
<MemoryRouter>
|
|
574
|
+
<div>
|
|
575
|
+
<Button href="/foo">Click me!</Button>
|
|
576
|
+
<Switch>
|
|
577
|
+
<Route path="/foo">
|
|
578
|
+
<div id="foo">Hello, world!</div>
|
|
579
|
+
</Route>
|
|
580
|
+
</Switch>
|
|
581
|
+
</div>
|
|
582
|
+
</MemoryRouter>,
|
|
583
|
+
);
|
|
584
|
+
|
|
585
|
+
// Act
|
|
586
|
+
const buttonWrapper = wrapper.find("Button");
|
|
587
|
+
buttonWrapper.simulate("keydown", {
|
|
588
|
+
keyCode: keyCodes.enter,
|
|
589
|
+
});
|
|
590
|
+
buttonWrapper.simulate("keyup", {
|
|
591
|
+
keyCode: keyCodes.enter,
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
// Assert
|
|
595
|
+
expect(wrapper.find("#foo")).toExist();
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
test("beforeNav rejection blocks client-side navigation ", async () => {
|
|
599
|
+
// Arrange
|
|
600
|
+
const wrapper = mount(
|
|
601
|
+
<MemoryRouter>
|
|
602
|
+
<div>
|
|
603
|
+
<Button href="/foo" beforeNav={(e) => Promise.reject()}>
|
|
604
|
+
Click me!
|
|
605
|
+
</Button>
|
|
606
|
+
<Switch>
|
|
607
|
+
<Route path="/foo">
|
|
608
|
+
<div id="foo">Hello, world!</div>
|
|
609
|
+
</Route>
|
|
610
|
+
</Switch>
|
|
611
|
+
</div>
|
|
612
|
+
</MemoryRouter>,
|
|
613
|
+
);
|
|
614
|
+
|
|
615
|
+
// Act
|
|
616
|
+
const buttonWrapper = wrapper.find("Button");
|
|
617
|
+
buttonWrapper.simulate("keydown", {
|
|
618
|
+
keyCode: keyCodes.enter,
|
|
619
|
+
});
|
|
620
|
+
buttonWrapper.simulate("keyup", {
|
|
621
|
+
keyCode: keyCodes.enter,
|
|
622
|
+
});
|
|
623
|
+
await wait(0);
|
|
624
|
+
buttonWrapper.update();
|
|
625
|
+
|
|
626
|
+
// Assert
|
|
627
|
+
expect(wrapper.find("#foo")).not.toExist();
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
test("beforeNav resolution results in client-side navigation", async () => {
|
|
631
|
+
// Arrange
|
|
632
|
+
const wrapper = mount(
|
|
633
|
+
<MemoryRouter>
|
|
634
|
+
<div>
|
|
635
|
+
<Button
|
|
636
|
+
href="/foo"
|
|
637
|
+
beforeNav={(e) => Promise.resolve()}
|
|
638
|
+
>
|
|
639
|
+
Click me!
|
|
640
|
+
</Button>
|
|
641
|
+
<Switch>
|
|
642
|
+
<Route path="/foo">
|
|
643
|
+
<div id="foo">Hello, world!</div>
|
|
644
|
+
</Route>
|
|
645
|
+
</Switch>
|
|
646
|
+
</div>
|
|
647
|
+
</MemoryRouter>,
|
|
648
|
+
);
|
|
649
|
+
|
|
650
|
+
// Act
|
|
651
|
+
const buttonWrapper = wrapper.find("Button");
|
|
652
|
+
buttonWrapper.simulate("keydown", {
|
|
653
|
+
keyCode: keyCodes.enter,
|
|
654
|
+
});
|
|
655
|
+
buttonWrapper.simulate("keyup", {
|
|
656
|
+
keyCode: keyCodes.enter,
|
|
657
|
+
});
|
|
658
|
+
await wait(0);
|
|
659
|
+
buttonWrapper.update();
|
|
660
|
+
|
|
661
|
+
// Assert
|
|
662
|
+
expect(wrapper.find("#foo")).toExist();
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
test("safeWithNav with skipClientNav=true waits for promise resolution", async () => {
|
|
666
|
+
// Arrange
|
|
667
|
+
jest.spyOn(window.location, "assign");
|
|
668
|
+
const wrapper = mount(
|
|
669
|
+
<MemoryRouter>
|
|
670
|
+
<div>
|
|
671
|
+
<Button
|
|
672
|
+
href="/foo"
|
|
673
|
+
safeWithNav={(e) => Promise.resolve()}
|
|
674
|
+
skipClientNav={true}
|
|
675
|
+
>
|
|
676
|
+
Click me!
|
|
677
|
+
</Button>
|
|
678
|
+
<Switch>
|
|
679
|
+
<Route path="/foo">
|
|
680
|
+
<div id="foo">Hello, world!</div>
|
|
681
|
+
</Route>
|
|
682
|
+
</Switch>
|
|
683
|
+
</div>
|
|
684
|
+
</MemoryRouter>,
|
|
685
|
+
);
|
|
686
|
+
|
|
687
|
+
// Act
|
|
688
|
+
const buttonWrapper = wrapper.find("Button");
|
|
689
|
+
buttonWrapper.simulate("keydown", {
|
|
690
|
+
keyCode: keyCodes.enter,
|
|
691
|
+
});
|
|
692
|
+
buttonWrapper.simulate("keyup", {
|
|
693
|
+
keyCode: keyCodes.enter,
|
|
694
|
+
});
|
|
695
|
+
await wait(0);
|
|
696
|
+
buttonWrapper.update();
|
|
697
|
+
|
|
698
|
+
// Assert
|
|
699
|
+
expect(window.location.assign).toHaveBeenCalledWith("/foo");
|
|
700
|
+
});
|
|
701
|
+
|
|
702
|
+
test("safeWithNav with skipClientNav=true waits for promise rejection", async () => {
|
|
703
|
+
// Arrange
|
|
704
|
+
jest.spyOn(window.location, "assign");
|
|
705
|
+
const wrapper = mount(
|
|
706
|
+
<MemoryRouter>
|
|
707
|
+
<div>
|
|
708
|
+
<Button
|
|
709
|
+
href="/foo"
|
|
710
|
+
safeWithNav={(e) => Promise.reject()}
|
|
711
|
+
skipClientNav={true}
|
|
712
|
+
>
|
|
713
|
+
Click me!
|
|
714
|
+
</Button>
|
|
715
|
+
<Switch>
|
|
716
|
+
<Route path="/foo">
|
|
717
|
+
<div id="foo">Hello, world!</div>
|
|
718
|
+
</Route>
|
|
719
|
+
</Switch>
|
|
720
|
+
</div>
|
|
721
|
+
</MemoryRouter>,
|
|
722
|
+
);
|
|
723
|
+
|
|
724
|
+
// Act
|
|
725
|
+
const buttonWrapper = wrapper.find("Button");
|
|
726
|
+
buttonWrapper.simulate("keydown", {
|
|
727
|
+
keyCode: keyCodes.enter,
|
|
728
|
+
});
|
|
729
|
+
buttonWrapper.simulate("keyup", {
|
|
730
|
+
keyCode: keyCodes.enter,
|
|
731
|
+
});
|
|
732
|
+
await wait(0);
|
|
733
|
+
buttonWrapper.update();
|
|
734
|
+
|
|
735
|
+
// Assert
|
|
736
|
+
expect(window.location.assign).toHaveBeenCalledWith("/foo");
|
|
737
|
+
});
|
|
738
|
+
|
|
739
|
+
test("safeWithNav with skipClientNav=false calls safeWithNav but doesn't wait to navigate", async () => {
|
|
740
|
+
// Arrange
|
|
741
|
+
jest.spyOn(window.location, "assign");
|
|
742
|
+
const safeWithNavMock = jest.fn();
|
|
743
|
+
const wrapper = mount(
|
|
744
|
+
<MemoryRouter>
|
|
745
|
+
<div>
|
|
746
|
+
<Button
|
|
747
|
+
href="/foo"
|
|
748
|
+
safeWithNav={safeWithNavMock}
|
|
749
|
+
skipClientNav={false}
|
|
750
|
+
>
|
|
751
|
+
Click me!
|
|
752
|
+
</Button>
|
|
753
|
+
<Switch>
|
|
754
|
+
<Route path="/foo">
|
|
755
|
+
<div id="foo">Hello, world!</div>
|
|
756
|
+
</Route>
|
|
757
|
+
</Switch>
|
|
758
|
+
</div>
|
|
759
|
+
</MemoryRouter>,
|
|
760
|
+
);
|
|
761
|
+
|
|
762
|
+
// Act
|
|
763
|
+
const buttonWrapper = wrapper.find("Button");
|
|
764
|
+
buttonWrapper.simulate("keydown", {
|
|
765
|
+
keyCode: keyCodes.enter,
|
|
766
|
+
});
|
|
767
|
+
buttonWrapper.simulate("keyup", {
|
|
768
|
+
keyCode: keyCodes.enter,
|
|
769
|
+
});
|
|
770
|
+
|
|
771
|
+
// Assert
|
|
772
|
+
expect(safeWithNavMock).toHaveBeenCalled();
|
|
773
|
+
expect(window.location.assign).toHaveBeenCalledWith("/foo");
|
|
774
|
+
});
|
|
775
|
+
});
|
|
776
|
+
|
|
777
|
+
describe("type='submit'", () => {
|
|
778
|
+
test("submit button within form via click", () => {
|
|
779
|
+
// Arrange
|
|
780
|
+
const submitFnMock = jest.fn();
|
|
781
|
+
const wrapper = mount(
|
|
782
|
+
<form onSubmit={submitFnMock}>
|
|
783
|
+
<Button type="submit">Click me!</Button>
|
|
784
|
+
</form>,
|
|
785
|
+
);
|
|
786
|
+
|
|
787
|
+
// Act
|
|
788
|
+
wrapper.find("button").simulate("click");
|
|
789
|
+
|
|
790
|
+
// Assert
|
|
791
|
+
expect(submitFnMock).toHaveBeenCalled();
|
|
792
|
+
});
|
|
793
|
+
|
|
794
|
+
test("submit button within form via keyboard", () => {
|
|
795
|
+
// Arrange
|
|
796
|
+
const submitFnMock = jest.fn();
|
|
797
|
+
const wrapper = mount(
|
|
798
|
+
<form onSubmit={submitFnMock}>
|
|
799
|
+
<Button type="submit">Click me!</Button>
|
|
800
|
+
</form>,
|
|
801
|
+
);
|
|
802
|
+
|
|
803
|
+
// Act
|
|
804
|
+
wrapper.find("button").simulate("keydown", {
|
|
805
|
+
keyCode: keyCodes.enter,
|
|
806
|
+
});
|
|
807
|
+
wrapper.find("button").simulate("keyup", {
|
|
808
|
+
keyCode: keyCodes.enter,
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
// Assert
|
|
812
|
+
expect(submitFnMock).toHaveBeenCalled();
|
|
813
|
+
});
|
|
814
|
+
|
|
815
|
+
test("submit button doesn't break if it's not in a form", () => {
|
|
816
|
+
// Arrange
|
|
817
|
+
const wrapper = mount(<Button type="submit">Click me!</Button>);
|
|
818
|
+
|
|
819
|
+
// Act
|
|
820
|
+
expect(() => {
|
|
821
|
+
// Assert
|
|
822
|
+
wrapper.find("button").simulate("click");
|
|
823
|
+
}).not.toThrow();
|
|
824
|
+
});
|
|
825
|
+
});
|
|
826
|
+
});
|