@khanacademy/wonder-blocks-clickable 4.2.6 → 4.2.8
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 +16 -0
- package/package.json +3 -3
- package/src/components/__tests__/clickable-behavior.test.tsx +0 -1438
- package/src/components/__tests__/clickable-behavior.typestest.tsx +0 -20
- package/src/components/__tests__/clickable.test.tsx +0 -661
- package/src/components/__tests__/clickable.typestest.tsx +0 -64
- package/src/components/clickable-behavior.ts +0 -659
- package/src/components/clickable.tsx +0 -429
- package/src/index.ts +0 -14
- package/src/util/__tests__/get-clickable-behavior.test.tsx +0 -104
- package/src/util/__tests__/is-client-side-url.js.test.ts +0 -49
- package/src/util/get-clickable-behavior.ts +0 -46
- package/src/util/is-client-side-url.ts +0 -15
- package/tsconfig-build.json +0 -12
- package/tsconfig-build.tsbuildinfo +0 -1
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
|
|
3
|
-
import ClickableBehavior from "../clickable-behavior";
|
|
4
|
-
|
|
5
|
-
<ClickableBehavior>
|
|
6
|
-
{(_, childrenProps) => <div {...childrenProps} />}
|
|
7
|
-
</ClickableBehavior>;
|
|
8
|
-
|
|
9
|
-
<ClickableBehavior target="_blank">
|
|
10
|
-
{(_, childrenProps) => <div {...childrenProps} />}
|
|
11
|
-
</ClickableBehavior>;
|
|
12
|
-
|
|
13
|
-
<ClickableBehavior beforeNav={() => Promise.resolve()}>
|
|
14
|
-
{(_, childrenProps) => <div {...childrenProps} />}
|
|
15
|
-
</ClickableBehavior>;
|
|
16
|
-
|
|
17
|
-
// @ts-expect-error `target` and `beforeNav` can't be used together.
|
|
18
|
-
<ClickableBehavior target="_blank" beforeNav={() => Promise.resolve()}>
|
|
19
|
-
{(_, childrenProps) => <div {...childrenProps} />}
|
|
20
|
-
</ClickableBehavior>;
|
|
@@ -1,661 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import {MemoryRouter, Route, Switch} from "react-router-dom";
|
|
3
|
-
import {render, screen, fireEvent, waitFor} from "@testing-library/react";
|
|
4
|
-
import {userEvent} from "@testing-library/user-event";
|
|
5
|
-
|
|
6
|
-
import {View} from "@khanacademy/wonder-blocks-core";
|
|
7
|
-
import Clickable from "../clickable";
|
|
8
|
-
|
|
9
|
-
describe("Clickable", () => {
|
|
10
|
-
beforeEach(() => {
|
|
11
|
-
// @ts-expect-error [FEI-5019] - TS2790 - The operand of a 'delete' operator must be optional.
|
|
12
|
-
delete window.location;
|
|
13
|
-
// @ts-expect-error [FEI-5019] - TS2740 - Type '{ assign: Mock<any, any, any>; }' is missing the following properties from type 'Location': ancestorOrigins, hash, host, hostname, and 8 more.
|
|
14
|
-
window.location = {assign: jest.fn()};
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
test("client-side navigation", async () => {
|
|
18
|
-
// Arrange
|
|
19
|
-
render(
|
|
20
|
-
<MemoryRouter>
|
|
21
|
-
<View>
|
|
22
|
-
<Clickable testId="button" href="/foo">
|
|
23
|
-
{(eventState: any) => <h1>Click Me!</h1>}
|
|
24
|
-
</Clickable>
|
|
25
|
-
<Switch>
|
|
26
|
-
<Route path="/foo">
|
|
27
|
-
<View>Hello, world!</View>
|
|
28
|
-
</Route>
|
|
29
|
-
</Switch>
|
|
30
|
-
</View>
|
|
31
|
-
</MemoryRouter>,
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
// Act
|
|
35
|
-
await userEvent.click(await screen.findByTestId("button"));
|
|
36
|
-
|
|
37
|
-
// Assert
|
|
38
|
-
expect(await screen.findByText("Hello, world!")).toBeInTheDocument();
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
test("client-side navigation with unknown URL fails", async () => {
|
|
42
|
-
// Arrange
|
|
43
|
-
render(
|
|
44
|
-
<MemoryRouter>
|
|
45
|
-
<View>
|
|
46
|
-
<Clickable testId="button" href="/unknown">
|
|
47
|
-
{(eventState: any) => <h1>Click Me!</h1>}
|
|
48
|
-
</Clickable>
|
|
49
|
-
<Switch>
|
|
50
|
-
<Route path="/foo">
|
|
51
|
-
<View>Hello, world!</View>
|
|
52
|
-
</Route>
|
|
53
|
-
</Switch>
|
|
54
|
-
</View>
|
|
55
|
-
</MemoryRouter>,
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
// Act
|
|
59
|
-
await userEvent.click(await screen.findByTestId("button"));
|
|
60
|
-
|
|
61
|
-
// Assert
|
|
62
|
-
expect(screen.queryByText("Hello, world!")).not.toBeInTheDocument();
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
test("client-side navigation with `skipClientNav` set to `true` fails", async () => {
|
|
66
|
-
// Arrange
|
|
67
|
-
render(
|
|
68
|
-
<MemoryRouter>
|
|
69
|
-
<View>
|
|
70
|
-
<Clickable testId="button" href="/foo" skipClientNav>
|
|
71
|
-
{(eventState: any) => <h1>Click Me!</h1>}
|
|
72
|
-
</Clickable>
|
|
73
|
-
<Switch>
|
|
74
|
-
<Route path="/foo">
|
|
75
|
-
<View>Hello, world!</View>
|
|
76
|
-
</Route>
|
|
77
|
-
</Switch>
|
|
78
|
-
</View>
|
|
79
|
-
</MemoryRouter>,
|
|
80
|
-
);
|
|
81
|
-
|
|
82
|
-
// Act
|
|
83
|
-
await userEvent.click(await screen.findByTestId("button"));
|
|
84
|
-
|
|
85
|
-
// Assert
|
|
86
|
-
expect(screen.queryByText("Hello, world!")).not.toBeInTheDocument();
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
test("disallow navigation when href and disabled are both set", async () => {
|
|
90
|
-
// Arrange
|
|
91
|
-
render(
|
|
92
|
-
<MemoryRouter>
|
|
93
|
-
<View>
|
|
94
|
-
<Clickable testId="button" href="/foo" disabled={true}>
|
|
95
|
-
{(eventState: any) => <h1>Click Me!</h1>}
|
|
96
|
-
</Clickable>
|
|
97
|
-
<Switch>
|
|
98
|
-
<Route path="/foo">
|
|
99
|
-
<View>Hello, world!</View>
|
|
100
|
-
</Route>
|
|
101
|
-
</Switch>
|
|
102
|
-
</View>
|
|
103
|
-
</MemoryRouter>,
|
|
104
|
-
);
|
|
105
|
-
|
|
106
|
-
// Act
|
|
107
|
-
await userEvent.click(await screen.findByTestId("button"));
|
|
108
|
-
|
|
109
|
-
// Assert
|
|
110
|
-
expect(screen.queryByText("Hello, world!")).not.toBeInTheDocument();
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
test("a link is rendered with the given href", async () => {
|
|
114
|
-
// Arrange, Act
|
|
115
|
-
render(
|
|
116
|
-
<Clickable testId="button" href="/foo" skipClientNav={true}>
|
|
117
|
-
{(eventState: any) => <h1>Click Me!</h1>}
|
|
118
|
-
</Clickable>,
|
|
119
|
-
);
|
|
120
|
-
|
|
121
|
-
// Assert
|
|
122
|
-
const link = await screen.findByRole("link");
|
|
123
|
-
expect(link).toBeInTheDocument();
|
|
124
|
-
expect(link).toHaveAttribute("href", "/foo");
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
test("should navigate to a specific link using the keyboard", async () => {
|
|
128
|
-
// Arrange
|
|
129
|
-
render(
|
|
130
|
-
<Clickable testId="button" href="/foo" skipClientNav={true}>
|
|
131
|
-
{(eventState: any) => <h1>Click Me!</h1>}
|
|
132
|
-
</Clickable>,
|
|
133
|
-
);
|
|
134
|
-
|
|
135
|
-
// Act
|
|
136
|
-
const button = await screen.findByTestId("button");
|
|
137
|
-
// simulate Enter
|
|
138
|
-
// eslint-disable-next-line testing-library/prefer-user-event
|
|
139
|
-
fireEvent.keyUp(button, {keyCode: 13});
|
|
140
|
-
|
|
141
|
-
// Assert
|
|
142
|
-
expect(window.location.assign).toHaveBeenCalledWith("/foo");
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
test("beforeNav rejection blocks client-side navigation", async () => {
|
|
146
|
-
// Arrange
|
|
147
|
-
render(
|
|
148
|
-
<MemoryRouter>
|
|
149
|
-
<div>
|
|
150
|
-
<Clickable
|
|
151
|
-
testId="button"
|
|
152
|
-
href="/foo"
|
|
153
|
-
beforeNav={() => Promise.reject()}
|
|
154
|
-
>
|
|
155
|
-
{() => <span>Click me!</span>}
|
|
156
|
-
</Clickable>
|
|
157
|
-
<Switch>
|
|
158
|
-
<Route path="/foo">
|
|
159
|
-
<div>Hello, world!</div>
|
|
160
|
-
</Route>
|
|
161
|
-
</Switch>
|
|
162
|
-
</div>
|
|
163
|
-
</MemoryRouter>,
|
|
164
|
-
);
|
|
165
|
-
|
|
166
|
-
// Act
|
|
167
|
-
await userEvent.click(await screen.findByTestId("button"));
|
|
168
|
-
|
|
169
|
-
// Assert
|
|
170
|
-
expect(screen.queryByText("Hello, world!")).not.toBeInTheDocument();
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
test("beforeNav rejection blocks calling safeWithNav", async () => {
|
|
174
|
-
// Arrange
|
|
175
|
-
const safeWithNavMock = jest.fn();
|
|
176
|
-
render(
|
|
177
|
-
<MemoryRouter>
|
|
178
|
-
<div>
|
|
179
|
-
<Clickable
|
|
180
|
-
testId="button"
|
|
181
|
-
href="/foo"
|
|
182
|
-
beforeNav={() => Promise.reject()}
|
|
183
|
-
safeWithNav={safeWithNavMock}
|
|
184
|
-
>
|
|
185
|
-
{() => <span>Click me!</span>}
|
|
186
|
-
</Clickable>
|
|
187
|
-
<Switch>
|
|
188
|
-
<Route path="/foo">
|
|
189
|
-
<div>Hello, world!</div>
|
|
190
|
-
</Route>
|
|
191
|
-
</Switch>
|
|
192
|
-
</div>
|
|
193
|
-
</MemoryRouter>,
|
|
194
|
-
);
|
|
195
|
-
|
|
196
|
-
// Act
|
|
197
|
-
await userEvent.click(await screen.findByTestId("button"));
|
|
198
|
-
|
|
199
|
-
// Assert
|
|
200
|
-
expect(safeWithNavMock).not.toHaveBeenCalled();
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
test("beforeNav resolution results in client-side navigation", async () => {
|
|
204
|
-
// Arrange
|
|
205
|
-
render(
|
|
206
|
-
<MemoryRouter>
|
|
207
|
-
<div>
|
|
208
|
-
<Clickable
|
|
209
|
-
testId="button"
|
|
210
|
-
href="/foo"
|
|
211
|
-
beforeNav={() => Promise.resolve()}
|
|
212
|
-
>
|
|
213
|
-
{() => <span>Click me!</span>}
|
|
214
|
-
</Clickable>
|
|
215
|
-
<Switch>
|
|
216
|
-
<Route path="/foo">
|
|
217
|
-
<div>Hello, world!</div>
|
|
218
|
-
</Route>
|
|
219
|
-
</Switch>
|
|
220
|
-
</div>
|
|
221
|
-
</MemoryRouter>,
|
|
222
|
-
);
|
|
223
|
-
|
|
224
|
-
// Act
|
|
225
|
-
await userEvent.click(await screen.findByTestId("button"));
|
|
226
|
-
|
|
227
|
-
// Assert
|
|
228
|
-
await waitFor(async () => {
|
|
229
|
-
expect(
|
|
230
|
-
await screen.findByText("Hello, world!"),
|
|
231
|
-
).toBeInTheDocument();
|
|
232
|
-
});
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
test("beforeNav resolution results in safeWithNav being called", async () => {
|
|
236
|
-
// Arrange
|
|
237
|
-
const safeWithNavMock = jest.fn();
|
|
238
|
-
render(
|
|
239
|
-
<MemoryRouter>
|
|
240
|
-
<div>
|
|
241
|
-
<Clickable
|
|
242
|
-
testId="button"
|
|
243
|
-
href="/foo"
|
|
244
|
-
beforeNav={() => Promise.resolve()}
|
|
245
|
-
safeWithNav={safeWithNavMock}
|
|
246
|
-
>
|
|
247
|
-
{() => <span>Click me!</span>}
|
|
248
|
-
</Clickable>
|
|
249
|
-
<Switch>
|
|
250
|
-
<Route path="/foo">
|
|
251
|
-
<div>Hello, world!</div>
|
|
252
|
-
</Route>
|
|
253
|
-
</Switch>
|
|
254
|
-
</div>
|
|
255
|
-
</MemoryRouter>,
|
|
256
|
-
);
|
|
257
|
-
|
|
258
|
-
// Act
|
|
259
|
-
await userEvent.click(await screen.findByTestId("button"));
|
|
260
|
-
|
|
261
|
-
// Assert
|
|
262
|
-
await waitFor(() => {
|
|
263
|
-
expect(safeWithNavMock).toHaveBeenCalled();
|
|
264
|
-
});
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
test("safeWithNav with skipClientNav=true waits for promise resolution", async () => {
|
|
268
|
-
// Arrange
|
|
269
|
-
render(
|
|
270
|
-
<MemoryRouter>
|
|
271
|
-
<div>
|
|
272
|
-
<Clickable
|
|
273
|
-
testId="button"
|
|
274
|
-
href="/foo"
|
|
275
|
-
safeWithNav={() => Promise.resolve()}
|
|
276
|
-
skipClientNav={true}
|
|
277
|
-
>
|
|
278
|
-
{() => <h1>Click me!</h1>}
|
|
279
|
-
</Clickable>
|
|
280
|
-
<Switch>
|
|
281
|
-
<Route path="/foo">
|
|
282
|
-
<div>Hello, world!</div>
|
|
283
|
-
</Route>
|
|
284
|
-
</Switch>
|
|
285
|
-
</div>
|
|
286
|
-
</MemoryRouter>,
|
|
287
|
-
);
|
|
288
|
-
|
|
289
|
-
// Act
|
|
290
|
-
await userEvent.click(await screen.findByTestId("button"));
|
|
291
|
-
|
|
292
|
-
// Assert
|
|
293
|
-
await waitFor(() => {
|
|
294
|
-
expect(window.location.assign).toHaveBeenCalledWith("/foo");
|
|
295
|
-
});
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
test("beforeNav resolution and safeWithNav with skipClientNav=true waits for promise resolution", async () => {
|
|
299
|
-
// Arrange
|
|
300
|
-
render(
|
|
301
|
-
<MemoryRouter>
|
|
302
|
-
<div>
|
|
303
|
-
<Clickable
|
|
304
|
-
testId="button"
|
|
305
|
-
href="/foo"
|
|
306
|
-
beforeNav={() => Promise.resolve()}
|
|
307
|
-
safeWithNav={() => Promise.resolve()}
|
|
308
|
-
skipClientNav={true}
|
|
309
|
-
>
|
|
310
|
-
{() => <h1>Click me!</h1>}
|
|
311
|
-
</Clickable>
|
|
312
|
-
<Switch>
|
|
313
|
-
<Route path="/foo">
|
|
314
|
-
<div>Hello, world!</div>
|
|
315
|
-
</Route>
|
|
316
|
-
</Switch>
|
|
317
|
-
</div>
|
|
318
|
-
</MemoryRouter>,
|
|
319
|
-
);
|
|
320
|
-
|
|
321
|
-
// Act
|
|
322
|
-
await userEvent.click(await screen.findByTestId("button"));
|
|
323
|
-
|
|
324
|
-
// Assert
|
|
325
|
-
await waitFor(() => {
|
|
326
|
-
expect(window.location.assign).toHaveBeenCalledWith("/foo");
|
|
327
|
-
});
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
test("safeWithNav with skipClientNav=true waits for promise rejection", async () => {
|
|
331
|
-
// Arrange
|
|
332
|
-
render(
|
|
333
|
-
<MemoryRouter>
|
|
334
|
-
<div>
|
|
335
|
-
<Clickable
|
|
336
|
-
testId="button"
|
|
337
|
-
href="/foo"
|
|
338
|
-
safeWithNav={() => Promise.reject()}
|
|
339
|
-
skipClientNav={true}
|
|
340
|
-
>
|
|
341
|
-
{() => <h1>Click me!</h1>}
|
|
342
|
-
</Clickable>
|
|
343
|
-
<Switch>
|
|
344
|
-
<Route path="/foo">
|
|
345
|
-
<div>Hello, world!</div>
|
|
346
|
-
</Route>
|
|
347
|
-
</Switch>
|
|
348
|
-
</div>
|
|
349
|
-
</MemoryRouter>,
|
|
350
|
-
);
|
|
351
|
-
|
|
352
|
-
// Act
|
|
353
|
-
await userEvent.click(await screen.findByTestId("button"));
|
|
354
|
-
|
|
355
|
-
// Assert
|
|
356
|
-
await waitFor(() => {
|
|
357
|
-
expect(window.location.assign).toHaveBeenCalledWith("/foo");
|
|
358
|
-
});
|
|
359
|
-
});
|
|
360
|
-
|
|
361
|
-
test("safeWithNav with skipClientNav=false calls safeWithNav but doesn't wait to navigate", async () => {
|
|
362
|
-
// Arrange
|
|
363
|
-
const safeWithNavMock = jest.fn();
|
|
364
|
-
render(
|
|
365
|
-
<MemoryRouter>
|
|
366
|
-
<div>
|
|
367
|
-
<Clickable
|
|
368
|
-
testId="button"
|
|
369
|
-
href="/foo"
|
|
370
|
-
safeWithNav={safeWithNavMock}
|
|
371
|
-
skipClientNav={false}
|
|
372
|
-
>
|
|
373
|
-
{() => <h1>Click me!</h1>}
|
|
374
|
-
</Clickable>
|
|
375
|
-
<Switch>
|
|
376
|
-
<Route path="/foo">
|
|
377
|
-
<div>Hello, world!</div>
|
|
378
|
-
</Route>
|
|
379
|
-
</Switch>
|
|
380
|
-
</div>
|
|
381
|
-
</MemoryRouter>,
|
|
382
|
-
);
|
|
383
|
-
|
|
384
|
-
// Act
|
|
385
|
-
await userEvent.click(await screen.findByTestId("button"));
|
|
386
|
-
|
|
387
|
-
// Assert
|
|
388
|
-
expect(safeWithNavMock).toHaveBeenCalled();
|
|
389
|
-
// client side nav to /foo
|
|
390
|
-
expect(await screen.findByText("Hello, world!")).toBeInTheDocument();
|
|
391
|
-
// not a full page nav
|
|
392
|
-
expect(window.location.assign).not.toHaveBeenCalledWith("/foo");
|
|
393
|
-
});
|
|
394
|
-
|
|
395
|
-
test("safeWithNav with beforeNav resolution and skipClientNav=false calls safeWithNav but doesn't wait to navigate", async () => {
|
|
396
|
-
// Arrange
|
|
397
|
-
const safeWithNavMock = jest.fn();
|
|
398
|
-
render(
|
|
399
|
-
<MemoryRouter>
|
|
400
|
-
<div>
|
|
401
|
-
<Clickable
|
|
402
|
-
testId="button"
|
|
403
|
-
href="/foo"
|
|
404
|
-
beforeNav={() => Promise.resolve()}
|
|
405
|
-
safeWithNav={safeWithNavMock}
|
|
406
|
-
skipClientNav={false}
|
|
407
|
-
>
|
|
408
|
-
{() => <h1>Click me!</h1>}
|
|
409
|
-
</Clickable>
|
|
410
|
-
<Switch>
|
|
411
|
-
<Route path="/foo">
|
|
412
|
-
<div>Hello, world!</div>
|
|
413
|
-
</Route>
|
|
414
|
-
</Switch>
|
|
415
|
-
</div>
|
|
416
|
-
</MemoryRouter>,
|
|
417
|
-
);
|
|
418
|
-
|
|
419
|
-
// Act
|
|
420
|
-
await userEvent.click(await screen.findByTestId("button"));
|
|
421
|
-
|
|
422
|
-
// Assert
|
|
423
|
-
await waitFor(() => {
|
|
424
|
-
expect(safeWithNavMock).toHaveBeenCalled();
|
|
425
|
-
});
|
|
426
|
-
// client side nav to /foo
|
|
427
|
-
expect(await screen.findByText("Hello, world!")).toBeInTheDocument();
|
|
428
|
-
// not a full page nav
|
|
429
|
-
expect(window.location.assign).not.toHaveBeenCalledWith("/foo");
|
|
430
|
-
});
|
|
431
|
-
|
|
432
|
-
test("should add aria-disabled if disabled is set", async () => {
|
|
433
|
-
// Arrange
|
|
434
|
-
|
|
435
|
-
// Act
|
|
436
|
-
render(
|
|
437
|
-
<Clickable testId="clickable-button" disabled={true}>
|
|
438
|
-
{(eventState: any) => <h1>Click Me!</h1>}
|
|
439
|
-
</Clickable>,
|
|
440
|
-
);
|
|
441
|
-
|
|
442
|
-
const button = await screen.findByTestId("clickable-button");
|
|
443
|
-
|
|
444
|
-
// Assert
|
|
445
|
-
expect(button).toHaveAttribute("aria-disabled", "true");
|
|
446
|
-
});
|
|
447
|
-
|
|
448
|
-
test("should add aria-label if one is passed in", async () => {
|
|
449
|
-
// Arrange
|
|
450
|
-
|
|
451
|
-
// Act
|
|
452
|
-
render(
|
|
453
|
-
<Clickable
|
|
454
|
-
testId="clickable-button"
|
|
455
|
-
aria-label="clickable-button-aria-label"
|
|
456
|
-
>
|
|
457
|
-
{(eventState: any) => <h1>Click Me!</h1>}
|
|
458
|
-
</Clickable>,
|
|
459
|
-
);
|
|
460
|
-
|
|
461
|
-
const button = await screen.findByTestId("clickable-button");
|
|
462
|
-
|
|
463
|
-
// Assert
|
|
464
|
-
expect(button).toHaveAttribute(
|
|
465
|
-
"aria-label",
|
|
466
|
-
"clickable-button-aria-label",
|
|
467
|
-
);
|
|
468
|
-
});
|
|
469
|
-
|
|
470
|
-
test("should not have an aria-label if one is not passed in", async () => {
|
|
471
|
-
// Arrange
|
|
472
|
-
|
|
473
|
-
// Act
|
|
474
|
-
render(
|
|
475
|
-
<Clickable testId="clickable-button">
|
|
476
|
-
{(eventState: any) => <h1>Click Me!</h1>}
|
|
477
|
-
</Clickable>,
|
|
478
|
-
);
|
|
479
|
-
|
|
480
|
-
const button = await screen.findByTestId("clickable-button");
|
|
481
|
-
|
|
482
|
-
// Assert
|
|
483
|
-
expect(button).not.toHaveAttribute("aria-label");
|
|
484
|
-
});
|
|
485
|
-
|
|
486
|
-
test("allow keyboard navigation when disabled is set", async () => {
|
|
487
|
-
// Arrange
|
|
488
|
-
render(
|
|
489
|
-
<div>
|
|
490
|
-
<button>First focusable button</button>
|
|
491
|
-
<Clickable testId="clickable-button" disabled={true}>
|
|
492
|
-
{(eventState: any) => <h1>Click Me!</h1>}
|
|
493
|
-
</Clickable>
|
|
494
|
-
</div>,
|
|
495
|
-
);
|
|
496
|
-
|
|
497
|
-
// Act
|
|
498
|
-
// RTL's focuses on `document.body` by default, so we need to focus on
|
|
499
|
-
// the first button
|
|
500
|
-
await userEvent.tab();
|
|
501
|
-
|
|
502
|
-
// Then we focus on our Clickable button.
|
|
503
|
-
await userEvent.tab();
|
|
504
|
-
|
|
505
|
-
const button = await screen.findByTestId("clickable-button");
|
|
506
|
-
|
|
507
|
-
// Assert
|
|
508
|
-
expect(button).toHaveFocus();
|
|
509
|
-
});
|
|
510
|
-
|
|
511
|
-
test("should not have a tabIndex if one is not set", async () => {
|
|
512
|
-
// Arrange
|
|
513
|
-
|
|
514
|
-
// Act
|
|
515
|
-
render(
|
|
516
|
-
<Clickable testId="clickable-button">
|
|
517
|
-
{(eventState: any) => <h1>Click Me!</h1>}
|
|
518
|
-
</Clickable>,
|
|
519
|
-
);
|
|
520
|
-
|
|
521
|
-
const button = await screen.findByTestId("clickable-button");
|
|
522
|
-
|
|
523
|
-
// Assert
|
|
524
|
-
expect(button).not.toHaveAttribute("tabIndex");
|
|
525
|
-
});
|
|
526
|
-
|
|
527
|
-
test("should have the tabIndex that is passed in", async () => {
|
|
528
|
-
// Arrange
|
|
529
|
-
|
|
530
|
-
// Act
|
|
531
|
-
render(
|
|
532
|
-
<Clickable testId="clickable-button" tabIndex={1}>
|
|
533
|
-
{(eventState: any) => <h1>Click Me!</h1>}
|
|
534
|
-
</Clickable>,
|
|
535
|
-
);
|
|
536
|
-
|
|
537
|
-
const button = await screen.findByTestId("clickable-button");
|
|
538
|
-
|
|
539
|
-
// Assert
|
|
540
|
-
expect(button).toHaveAttribute("tabIndex", "1");
|
|
541
|
-
});
|
|
542
|
-
|
|
543
|
-
test("forwards the ref to the clickable button element", async () => {
|
|
544
|
-
// Arrange
|
|
545
|
-
const ref: React.RefObject<HTMLButtonElement> = React.createRef();
|
|
546
|
-
|
|
547
|
-
// Act
|
|
548
|
-
render(
|
|
549
|
-
<Clickable testId="clickable-button" ref={ref}>
|
|
550
|
-
{(eventState: any) => <h1>Click Me!</h1>}
|
|
551
|
-
</Clickable>,
|
|
552
|
-
);
|
|
553
|
-
|
|
554
|
-
// Assert
|
|
555
|
-
expect(ref.current).toBeInstanceOf(HTMLButtonElement);
|
|
556
|
-
});
|
|
557
|
-
|
|
558
|
-
test("forwards the ref to the clickable anchor element ", async () => {
|
|
559
|
-
// Arrange
|
|
560
|
-
const ref: React.RefObject<HTMLAnchorElement> = React.createRef();
|
|
561
|
-
|
|
562
|
-
// Act
|
|
563
|
-
render(
|
|
564
|
-
<Clickable href="/test-url" testId="clickable-anchor" ref={ref}>
|
|
565
|
-
{(eventState: any) => <h1>Click Me!</h1>}
|
|
566
|
-
</Clickable>,
|
|
567
|
-
);
|
|
568
|
-
|
|
569
|
-
// Assert
|
|
570
|
-
expect(ref.current).toBeInstanceOf(HTMLAnchorElement);
|
|
571
|
-
});
|
|
572
|
-
|
|
573
|
-
describe("raw events", () => {
|
|
574
|
-
/**
|
|
575
|
-
* Clickable expect a function as children so we create a simple wrapper to
|
|
576
|
-
* allow a React.Node to be passed instead.
|
|
577
|
-
*/
|
|
578
|
-
const ClickableWrapper = ({
|
|
579
|
-
children,
|
|
580
|
-
...restProps
|
|
581
|
-
}: {
|
|
582
|
-
children: React.ReactNode;
|
|
583
|
-
onKeyDown?: (e: React.KeyboardEvent) => unknown;
|
|
584
|
-
onKeyUp?: (e: React.KeyboardEvent) => unknown;
|
|
585
|
-
onMouseDown?: (e: React.MouseEvent) => unknown;
|
|
586
|
-
onMouseUp?: (e: React.MouseEvent) => unknown;
|
|
587
|
-
}) => {
|
|
588
|
-
return <Clickable {...restProps}>{() => children}</Clickable>;
|
|
589
|
-
};
|
|
590
|
-
|
|
591
|
-
test("onKeyDown", async () => {
|
|
592
|
-
// Arrange
|
|
593
|
-
let keyCode: any;
|
|
594
|
-
render(
|
|
595
|
-
<ClickableWrapper onKeyDown={(e) => (keyCode = e.keyCode)}>
|
|
596
|
-
Click me!
|
|
597
|
-
</ClickableWrapper>,
|
|
598
|
-
);
|
|
599
|
-
|
|
600
|
-
// Act
|
|
601
|
-
// eslint-disable-next-line testing-library/prefer-user-event
|
|
602
|
-
fireEvent.keyDown(await screen.findByRole("button"), {keyCode: 32});
|
|
603
|
-
|
|
604
|
-
// Assert
|
|
605
|
-
expect(keyCode).toEqual(32);
|
|
606
|
-
});
|
|
607
|
-
|
|
608
|
-
test("onKeyUp", async () => {
|
|
609
|
-
// Arrange
|
|
610
|
-
let keyCode: any;
|
|
611
|
-
render(
|
|
612
|
-
<ClickableWrapper onKeyUp={(e) => (keyCode = e.keyCode)}>
|
|
613
|
-
Click me!
|
|
614
|
-
</ClickableWrapper>,
|
|
615
|
-
);
|
|
616
|
-
|
|
617
|
-
// Act
|
|
618
|
-
// eslint-disable-next-line testing-library/prefer-user-event
|
|
619
|
-
fireEvent.keyUp(await screen.findByRole("button"), {keyCode: 32});
|
|
620
|
-
|
|
621
|
-
// Assert
|
|
622
|
-
expect(keyCode).toEqual(32);
|
|
623
|
-
});
|
|
624
|
-
|
|
625
|
-
test("onMouseDown", async () => {
|
|
626
|
-
// Arrange
|
|
627
|
-
let clientX: any;
|
|
628
|
-
render(
|
|
629
|
-
<Clickable onMouseDown={(e) => (clientX = e.clientX)}>
|
|
630
|
-
{() => "Click me!"}
|
|
631
|
-
</Clickable>,
|
|
632
|
-
);
|
|
633
|
-
|
|
634
|
-
// Act
|
|
635
|
-
// eslint-disable-next-line testing-library/prefer-user-event
|
|
636
|
-
fireEvent.mouseDown(await screen.findByRole("button"), {
|
|
637
|
-
clientX: 10,
|
|
638
|
-
});
|
|
639
|
-
|
|
640
|
-
// Assert
|
|
641
|
-
expect(clientX).toEqual(10);
|
|
642
|
-
});
|
|
643
|
-
|
|
644
|
-
test("onMouseUp", async () => {
|
|
645
|
-
// Arrange
|
|
646
|
-
let clientX: any;
|
|
647
|
-
render(
|
|
648
|
-
<Clickable onMouseUp={(e) => (clientX = e.clientX)}>
|
|
649
|
-
{() => "Click me!"}
|
|
650
|
-
</Clickable>,
|
|
651
|
-
);
|
|
652
|
-
|
|
653
|
-
// Act
|
|
654
|
-
// eslint-disable-next-line testing-library/prefer-user-event
|
|
655
|
-
fireEvent.mouseUp(await screen.findByRole("button"), {clientX: 10});
|
|
656
|
-
|
|
657
|
-
// Assert
|
|
658
|
-
expect(clientX).toEqual(10);
|
|
659
|
-
});
|
|
660
|
-
});
|
|
661
|
-
});
|