@sproutsocial/seeds-react-modal 2.2.5 → 2.4.0
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/.turbo/turbo-build.log +19 -19
- package/CHANGELOG.md +41 -0
- package/dist/{ModalAction-gIgCE73I.d.mts → ModalAction-BHG3Zbd9.d.mts} +151 -6
- package/dist/{ModalAction-gIgCE73I.d.ts → ModalAction-BHG3Zbd9.d.ts} +151 -6
- package/dist/esm/{chunk-UP2XQN57.js → chunk-TQ44T5IM.js} +114 -17
- package/dist/esm/chunk-TQ44T5IM.js.map +1 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/v2/index.js +9 -3
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +87 -19
- package/dist/index.js.map +1 -1
- package/dist/v2/index.d.mts +77 -3
- package/dist/v2/index.d.ts +77 -3
- package/dist/v2/index.js +121 -21
- package/dist/v2/index.js.map +1 -1
- package/package.json +7 -7
- package/src/__tests__/v2/Modal.test.tsx +972 -0
- package/src/v2/Modal.tsx +189 -0
- package/src/v2/ModalTypes.ts +57 -10
- package/src/v2/ModalV2.stories.tsx +278 -3
- package/src/v2/components/ModalContent.tsx +13 -1
- package/src/v2/components/ModalExternalTrigger.tsx +104 -0
- package/src/v2/components/index.ts +1 -0
- package/src/v2/index.ts +7 -1
- package/dist/esm/chunk-UP2XQN57.js.map +0 -1
|
@@ -0,0 +1,972 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import React from "react";
|
|
3
|
+
import {
|
|
4
|
+
render,
|
|
5
|
+
fireEvent,
|
|
6
|
+
cleanup,
|
|
7
|
+
screen,
|
|
8
|
+
waitFor,
|
|
9
|
+
renderHook,
|
|
10
|
+
act,
|
|
11
|
+
} from "@sproutsocial/seeds-react-testing-library";
|
|
12
|
+
import userEvent from "@testing-library/user-event";
|
|
13
|
+
import {
|
|
14
|
+
Modal,
|
|
15
|
+
ModalBody,
|
|
16
|
+
useModalExternalTrigger,
|
|
17
|
+
ModalExternalTrigger,
|
|
18
|
+
} from "../../v2";
|
|
19
|
+
import { Button } from "@sproutsocial/seeds-react-button";
|
|
20
|
+
|
|
21
|
+
// Mock matchMedia for mobile detection
|
|
22
|
+
beforeAll(() => {
|
|
23
|
+
Object.defineProperty(window, "matchMedia", {
|
|
24
|
+
writable: true,
|
|
25
|
+
value: jest.fn().mockImplementation((query) => {
|
|
26
|
+
const listeners = new Map();
|
|
27
|
+
return {
|
|
28
|
+
matches: false,
|
|
29
|
+
media: query,
|
|
30
|
+
onchange: null,
|
|
31
|
+
addListener: jest.fn(),
|
|
32
|
+
removeListener: jest.fn(),
|
|
33
|
+
addEventListener: jest.fn((_event, listener) => {
|
|
34
|
+
listeners.set(listener, listener);
|
|
35
|
+
}),
|
|
36
|
+
removeEventListener: jest.fn((_event, listener) => {
|
|
37
|
+
listeners.delete(listener);
|
|
38
|
+
}),
|
|
39
|
+
dispatchEvent: jest.fn(),
|
|
40
|
+
};
|
|
41
|
+
}),
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
afterEach(() => cleanup());
|
|
46
|
+
|
|
47
|
+
describe("Modal V2 - Dialog.Content Event Handlers and Convenience Props", () => {
|
|
48
|
+
describe("onInteractOutside", () => {
|
|
49
|
+
it("should prevent modal from closing when clicking outside if onInteractOutside calls preventDefault", async () => {
|
|
50
|
+
const handleInteractOutside = jest.fn((e) => e.preventDefault());
|
|
51
|
+
const handleOpenChange = jest.fn();
|
|
52
|
+
|
|
53
|
+
render(
|
|
54
|
+
<Modal
|
|
55
|
+
open={true}
|
|
56
|
+
onOpenChange={handleOpenChange}
|
|
57
|
+
onInteractOutside={handleInteractOutside}
|
|
58
|
+
title="Test Modal"
|
|
59
|
+
description="Test description"
|
|
60
|
+
closeButtonAriaLabel="Close"
|
|
61
|
+
>
|
|
62
|
+
<ModalBody>Content</ModalBody>
|
|
63
|
+
</Modal>
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const overlay = screen.getByTestId("modal-overlay");
|
|
67
|
+
await userEvent.click(overlay);
|
|
68
|
+
|
|
69
|
+
expect(handleInteractOutside).toHaveBeenCalled();
|
|
70
|
+
expect(handleOpenChange).not.toHaveBeenCalled();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("should allow modal to close when clicking outside if onInteractOutside does not prevent default", async () => {
|
|
74
|
+
const handleInteractOutside = jest.fn();
|
|
75
|
+
const handleOpenChange = jest.fn();
|
|
76
|
+
|
|
77
|
+
render(
|
|
78
|
+
<Modal
|
|
79
|
+
open={true}
|
|
80
|
+
onOpenChange={handleOpenChange}
|
|
81
|
+
onInteractOutside={handleInteractOutside}
|
|
82
|
+
title="Test Modal"
|
|
83
|
+
description="Test description"
|
|
84
|
+
closeButtonAriaLabel="Close"
|
|
85
|
+
>
|
|
86
|
+
<ModalBody>Content</ModalBody>
|
|
87
|
+
</Modal>
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const overlay = screen.getByTestId("modal-overlay");
|
|
91
|
+
await userEvent.click(overlay);
|
|
92
|
+
|
|
93
|
+
expect(handleInteractOutside).toHaveBeenCalled();
|
|
94
|
+
expect(handleOpenChange).toHaveBeenCalledWith(false);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("should close modal by default when clicking outside if no onInteractOutside handler provided", async () => {
|
|
98
|
+
const handleOpenChange = jest.fn();
|
|
99
|
+
|
|
100
|
+
render(
|
|
101
|
+
<Modal
|
|
102
|
+
open={true}
|
|
103
|
+
onOpenChange={handleOpenChange}
|
|
104
|
+
title="Test Modal"
|
|
105
|
+
description="Test description"
|
|
106
|
+
closeButtonAriaLabel="Close"
|
|
107
|
+
>
|
|
108
|
+
<ModalBody>Content</ModalBody>
|
|
109
|
+
</Modal>
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const overlay = screen.getByTestId("modal-overlay");
|
|
113
|
+
await userEvent.click(overlay);
|
|
114
|
+
|
|
115
|
+
expect(handleOpenChange).toHaveBeenCalledWith(false);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("should not call onInteractOutside when modal is closed", async () => {
|
|
119
|
+
const handleInteractOutside = jest.fn();
|
|
120
|
+
|
|
121
|
+
render(
|
|
122
|
+
<Modal
|
|
123
|
+
open={false}
|
|
124
|
+
onInteractOutside={handleInteractOutside}
|
|
125
|
+
title="Test Modal"
|
|
126
|
+
description="Test description"
|
|
127
|
+
closeButtonAriaLabel="Close"
|
|
128
|
+
>
|
|
129
|
+
<ModalBody>Content</ModalBody>
|
|
130
|
+
</Modal>
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
// Click on document body since modal isn't rendered when closed
|
|
134
|
+
await userEvent.click(document.body);
|
|
135
|
+
|
|
136
|
+
expect(handleInteractOutside).not.toHaveBeenCalled();
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
describe("onEscapeKeyDown", () => {
|
|
141
|
+
it("should prevent modal from closing when pressing Escape if onEscapeKeyDown calls preventDefault", async () => {
|
|
142
|
+
const handleEscapeKeyDown = jest.fn((e) => e.preventDefault());
|
|
143
|
+
const handleOpenChange = jest.fn();
|
|
144
|
+
|
|
145
|
+
render(
|
|
146
|
+
<Modal
|
|
147
|
+
open={true}
|
|
148
|
+
onOpenChange={handleOpenChange}
|
|
149
|
+
onEscapeKeyDown={handleEscapeKeyDown}
|
|
150
|
+
title="Test Modal"
|
|
151
|
+
description="Test description"
|
|
152
|
+
closeButtonAriaLabel="Close"
|
|
153
|
+
>
|
|
154
|
+
<ModalBody>Content</ModalBody>
|
|
155
|
+
</Modal>
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
const dialog = screen.getByRole("dialog");
|
|
159
|
+
fireEvent.keyDown(dialog, { key: "Escape", code: "Escape" });
|
|
160
|
+
|
|
161
|
+
expect(handleEscapeKeyDown).toHaveBeenCalled();
|
|
162
|
+
expect(handleOpenChange).not.toHaveBeenCalled();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("should allow modal to close when pressing Escape if onEscapeKeyDown does not prevent default", async () => {
|
|
166
|
+
const handleEscapeKeyDown = jest.fn();
|
|
167
|
+
const handleOpenChange = jest.fn();
|
|
168
|
+
|
|
169
|
+
render(
|
|
170
|
+
<Modal
|
|
171
|
+
open={true}
|
|
172
|
+
onOpenChange={handleOpenChange}
|
|
173
|
+
onEscapeKeyDown={handleEscapeKeyDown}
|
|
174
|
+
title="Test Modal"
|
|
175
|
+
description="Test description"
|
|
176
|
+
closeButtonAriaLabel="Close"
|
|
177
|
+
>
|
|
178
|
+
<ModalBody>Content</ModalBody>
|
|
179
|
+
</Modal>
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
const dialog = screen.getByRole("dialog");
|
|
183
|
+
fireEvent.keyDown(dialog, { key: "Escape", code: "Escape" });
|
|
184
|
+
|
|
185
|
+
expect(handleEscapeKeyDown).toHaveBeenCalled();
|
|
186
|
+
expect(handleOpenChange).toHaveBeenCalledWith(false);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("should close modal by default when pressing Escape if no onEscapeKeyDown handler provided", async () => {
|
|
190
|
+
const handleOpenChange = jest.fn();
|
|
191
|
+
|
|
192
|
+
render(
|
|
193
|
+
<Modal
|
|
194
|
+
open={true}
|
|
195
|
+
onOpenChange={handleOpenChange}
|
|
196
|
+
title="Test Modal"
|
|
197
|
+
description="Test description"
|
|
198
|
+
closeButtonAriaLabel="Close"
|
|
199
|
+
>
|
|
200
|
+
<ModalBody>Content</ModalBody>
|
|
201
|
+
</Modal>
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
const dialog = screen.getByRole("dialog");
|
|
205
|
+
fireEvent.keyDown(dialog, { key: "Escape", code: "Escape" });
|
|
206
|
+
|
|
207
|
+
expect(handleOpenChange).toHaveBeenCalledWith(false);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it("should not call onEscapeKeyDown when modal is closed", async () => {
|
|
211
|
+
const handleEscapeKeyDown = jest.fn();
|
|
212
|
+
|
|
213
|
+
render(
|
|
214
|
+
<Modal
|
|
215
|
+
open={false}
|
|
216
|
+
onEscapeKeyDown={handleEscapeKeyDown}
|
|
217
|
+
title="Test Modal"
|
|
218
|
+
description="Test description"
|
|
219
|
+
closeButtonAriaLabel="Close"
|
|
220
|
+
>
|
|
221
|
+
<ModalBody>Content</ModalBody>
|
|
222
|
+
</Modal>
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
// Press Escape on document body since modal isn't rendered when closed
|
|
226
|
+
fireEvent.keyDown(document.body, { key: "Escape", code: "Escape" });
|
|
227
|
+
|
|
228
|
+
expect(handleEscapeKeyDown).not.toHaveBeenCalled();
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
describe("onPointerDownOutside", () => {
|
|
233
|
+
it("should call onPointerDownOutside when clicking outside modal", async () => {
|
|
234
|
+
const handlePointerDownOutside = jest.fn();
|
|
235
|
+
|
|
236
|
+
render(
|
|
237
|
+
<Modal
|
|
238
|
+
open={true}
|
|
239
|
+
onPointerDownOutside={handlePointerDownOutside}
|
|
240
|
+
title="Test Modal"
|
|
241
|
+
description="Test description"
|
|
242
|
+
closeButtonAriaLabel="Close"
|
|
243
|
+
>
|
|
244
|
+
<ModalBody>Content</ModalBody>
|
|
245
|
+
</Modal>
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
const overlay = screen.getByTestId("modal-overlay");
|
|
249
|
+
await userEvent.click(overlay);
|
|
250
|
+
|
|
251
|
+
expect(handlePointerDownOutside).toHaveBeenCalled();
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it("should prevent modal from closing if onPointerDownOutside calls preventDefault", async () => {
|
|
255
|
+
const handlePointerDownOutside = jest.fn((e) => e.preventDefault());
|
|
256
|
+
const handleOpenChange = jest.fn();
|
|
257
|
+
|
|
258
|
+
render(
|
|
259
|
+
<Modal
|
|
260
|
+
open={true}
|
|
261
|
+
onOpenChange={handleOpenChange}
|
|
262
|
+
onPointerDownOutside={handlePointerDownOutside}
|
|
263
|
+
title="Test Modal"
|
|
264
|
+
description="Test description"
|
|
265
|
+
closeButtonAriaLabel="Close"
|
|
266
|
+
>
|
|
267
|
+
<ModalBody>Content</ModalBody>
|
|
268
|
+
</Modal>
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
const overlay = screen.getByTestId("modal-overlay");
|
|
272
|
+
await userEvent.click(overlay);
|
|
273
|
+
|
|
274
|
+
expect(handlePointerDownOutside).toHaveBeenCalled();
|
|
275
|
+
expect(handleOpenChange).not.toHaveBeenCalled();
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
describe("onOpenAutoFocus", () => {
|
|
280
|
+
it("should call onOpenAutoFocus when modal opens", async () => {
|
|
281
|
+
const handleOpenAutoFocus = jest.fn((e) => e.preventDefault());
|
|
282
|
+
|
|
283
|
+
const { rerender } = render(
|
|
284
|
+
<Modal
|
|
285
|
+
open={false}
|
|
286
|
+
onOpenAutoFocus={handleOpenAutoFocus}
|
|
287
|
+
title="Test Modal"
|
|
288
|
+
description="Test description"
|
|
289
|
+
closeButtonAriaLabel="Close"
|
|
290
|
+
>
|
|
291
|
+
<ModalBody>Content</ModalBody>
|
|
292
|
+
</Modal>
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
act(() => {
|
|
296
|
+
rerender(
|
|
297
|
+
<Modal
|
|
298
|
+
open={true}
|
|
299
|
+
onOpenAutoFocus={handleOpenAutoFocus}
|
|
300
|
+
title="Test Modal"
|
|
301
|
+
description="Test description"
|
|
302
|
+
closeButtonAriaLabel="Close"
|
|
303
|
+
>
|
|
304
|
+
<ModalBody>Content</ModalBody>
|
|
305
|
+
</Modal>
|
|
306
|
+
);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
await waitFor(() => {
|
|
310
|
+
expect(handleOpenAutoFocus).toHaveBeenCalled();
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
describe("onCloseAutoFocus", () => {
|
|
316
|
+
it("should call onCloseAutoFocus when modal closes", async () => {
|
|
317
|
+
const handleCloseAutoFocus = jest.fn((e) => e.preventDefault());
|
|
318
|
+
|
|
319
|
+
const { rerender } = render(
|
|
320
|
+
<Modal
|
|
321
|
+
open={true}
|
|
322
|
+
onCloseAutoFocus={handleCloseAutoFocus}
|
|
323
|
+
title="Test Modal"
|
|
324
|
+
description="Test description"
|
|
325
|
+
closeButtonAriaLabel="Close"
|
|
326
|
+
>
|
|
327
|
+
<ModalBody>Content</ModalBody>
|
|
328
|
+
</Modal>
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
act(() => {
|
|
332
|
+
rerender(
|
|
333
|
+
<Modal
|
|
334
|
+
open={false}
|
|
335
|
+
onCloseAutoFocus={handleCloseAutoFocus}
|
|
336
|
+
title="Test Modal"
|
|
337
|
+
description="Test description"
|
|
338
|
+
closeButtonAriaLabel="Close"
|
|
339
|
+
>
|
|
340
|
+
<ModalBody>Content</ModalBody>
|
|
341
|
+
</Modal>
|
|
342
|
+
);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
await waitFor(() => {
|
|
346
|
+
expect(handleCloseAutoFocus).toHaveBeenCalled();
|
|
347
|
+
});
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
describe("Combined event handlers", () => {
|
|
352
|
+
it("should support multiple event handlers at once", async () => {
|
|
353
|
+
const handleInteractOutside = jest.fn((e) => e.preventDefault());
|
|
354
|
+
const handleEscapeKeyDown = jest.fn((e) => e.preventDefault());
|
|
355
|
+
const handlePointerDownOutside = jest.fn();
|
|
356
|
+
const handleOpenChange = jest.fn();
|
|
357
|
+
|
|
358
|
+
render(
|
|
359
|
+
<Modal
|
|
360
|
+
open={true}
|
|
361
|
+
onOpenChange={handleOpenChange}
|
|
362
|
+
onInteractOutside={handleInteractOutside}
|
|
363
|
+
onEscapeKeyDown={handleEscapeKeyDown}
|
|
364
|
+
onPointerDownOutside={handlePointerDownOutside}
|
|
365
|
+
title="Test Modal"
|
|
366
|
+
description="Test description"
|
|
367
|
+
closeButtonAriaLabel="Close"
|
|
368
|
+
>
|
|
369
|
+
<ModalBody>Content</ModalBody>
|
|
370
|
+
</Modal>
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
// Test Escape key
|
|
374
|
+
const dialog = screen.getByRole("dialog");
|
|
375
|
+
fireEvent.keyDown(dialog, { key: "Escape", code: "Escape" });
|
|
376
|
+
|
|
377
|
+
expect(handleEscapeKeyDown).toHaveBeenCalled();
|
|
378
|
+
expect(handleOpenChange).not.toHaveBeenCalled();
|
|
379
|
+
|
|
380
|
+
// Test outside click
|
|
381
|
+
const overlay = screen.getByTestId("modal-overlay");
|
|
382
|
+
await userEvent.click(overlay);
|
|
383
|
+
|
|
384
|
+
expect(handlePointerDownOutside).toHaveBeenCalled();
|
|
385
|
+
expect(handleInteractOutside).toHaveBeenCalled();
|
|
386
|
+
expect(handleOpenChange).not.toHaveBeenCalled();
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
describe("Draggable modal", () => {
|
|
391
|
+
it("should not support onInteractOutside for draggable modals (TypeScript)", () => {
|
|
392
|
+
expect(true).toBe(true);
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
it("should prevent closing on outside click for draggable modals by default", async () => {
|
|
396
|
+
const handleOpenChange = jest.fn();
|
|
397
|
+
|
|
398
|
+
render(
|
|
399
|
+
<Modal
|
|
400
|
+
open={true}
|
|
401
|
+
draggable={true}
|
|
402
|
+
onOpenChange={handleOpenChange}
|
|
403
|
+
title="Test Modal"
|
|
404
|
+
description="Test description"
|
|
405
|
+
closeButtonAriaLabel="Close"
|
|
406
|
+
>
|
|
407
|
+
<ModalBody>Content</ModalBody>
|
|
408
|
+
</Modal>
|
|
409
|
+
);
|
|
410
|
+
|
|
411
|
+
// For draggable modals, showOverlay is false by default
|
|
412
|
+
// so we need to click outside the modal content itself
|
|
413
|
+
// Click on body outside the dialog
|
|
414
|
+
fireEvent.pointerDown(document.body);
|
|
415
|
+
|
|
416
|
+
// Modal should not close for draggable modals
|
|
417
|
+
expect(handleOpenChange).not.toHaveBeenCalled();
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
it("should support other event handlers for draggable modals", async () => {
|
|
421
|
+
const handleEscapeKeyDown = jest.fn();
|
|
422
|
+
const handleOpenAutoFocus = jest.fn();
|
|
423
|
+
|
|
424
|
+
render(
|
|
425
|
+
<Modal
|
|
426
|
+
open={true}
|
|
427
|
+
draggable={true}
|
|
428
|
+
onEscapeKeyDown={handleEscapeKeyDown}
|
|
429
|
+
onOpenAutoFocus={handleOpenAutoFocus}
|
|
430
|
+
title="Test Modal"
|
|
431
|
+
description="Test description"
|
|
432
|
+
closeButtonAriaLabel="Close"
|
|
433
|
+
>
|
|
434
|
+
<ModalBody>Content</ModalBody>
|
|
435
|
+
</Modal>
|
|
436
|
+
);
|
|
437
|
+
|
|
438
|
+
await waitFor(() => {
|
|
439
|
+
expect(handleOpenAutoFocus).toHaveBeenCalled();
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
const dialog = screen.getByRole("dialog");
|
|
443
|
+
fireEvent.keyDown(dialog, { key: "Escape", code: "Escape" });
|
|
444
|
+
|
|
445
|
+
expect(handleEscapeKeyDown).toHaveBeenCalled();
|
|
446
|
+
});
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
describe("Integration with close button", () => {
|
|
450
|
+
it("should still allow closing via close button even when onInteractOutside prevents outside clicks", async () => {
|
|
451
|
+
const handleInteractOutside = jest.fn((e) => e.preventDefault());
|
|
452
|
+
const handleOpenChange = jest.fn();
|
|
453
|
+
|
|
454
|
+
render(
|
|
455
|
+
<Modal
|
|
456
|
+
open={true}
|
|
457
|
+
onOpenChange={handleOpenChange}
|
|
458
|
+
onInteractOutside={handleInteractOutside}
|
|
459
|
+
title="Test Modal"
|
|
460
|
+
description="Test description"
|
|
461
|
+
closeButtonAriaLabel="Close modal"
|
|
462
|
+
>
|
|
463
|
+
<ModalBody>Content</ModalBody>
|
|
464
|
+
</Modal>
|
|
465
|
+
);
|
|
466
|
+
|
|
467
|
+
const closeButton = screen.getByRole("button", { name: "Close modal" });
|
|
468
|
+
await userEvent.click(closeButton);
|
|
469
|
+
|
|
470
|
+
expect(handleOpenChange).toHaveBeenCalledWith(false);
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
it("should still allow closing via close button even when onEscapeKeyDown prevents Escape key", async () => {
|
|
474
|
+
const handleEscapeKeyDown = jest.fn((e) => e.preventDefault());
|
|
475
|
+
const handleOpenChange = jest.fn();
|
|
476
|
+
|
|
477
|
+
render(
|
|
478
|
+
<Modal
|
|
479
|
+
open={true}
|
|
480
|
+
onOpenChange={handleOpenChange}
|
|
481
|
+
onEscapeKeyDown={handleEscapeKeyDown}
|
|
482
|
+
title="Test Modal"
|
|
483
|
+
description="Test description"
|
|
484
|
+
closeButtonAriaLabel="Close modal"
|
|
485
|
+
>
|
|
486
|
+
<ModalBody>Content</ModalBody>
|
|
487
|
+
</Modal>
|
|
488
|
+
);
|
|
489
|
+
|
|
490
|
+
const closeButton = screen.getByRole("button", { name: "Close modal" });
|
|
491
|
+
await userEvent.click(closeButton);
|
|
492
|
+
|
|
493
|
+
expect(handleOpenChange).toHaveBeenCalledWith(false);
|
|
494
|
+
});
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
describe("Uncontrolled modal", () => {
|
|
498
|
+
it("should work with uncontrolled modal using defaultOpen and event handlers", async () => {
|
|
499
|
+
const handleInteractOutside = jest.fn((e) => e.preventDefault());
|
|
500
|
+
|
|
501
|
+
function UncontrolledModalTest() {
|
|
502
|
+
return (
|
|
503
|
+
<Modal
|
|
504
|
+
defaultOpen={true}
|
|
505
|
+
onInteractOutside={handleInteractOutside}
|
|
506
|
+
modalTrigger={<Button>Open</Button>}
|
|
507
|
+
title="Test Modal"
|
|
508
|
+
description="Test description"
|
|
509
|
+
closeButtonAriaLabel="Close"
|
|
510
|
+
>
|
|
511
|
+
<ModalBody>Content</ModalBody>
|
|
512
|
+
</Modal>
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
render(<UncontrolledModalTest />);
|
|
517
|
+
|
|
518
|
+
const overlay = screen.getByTestId("modal-overlay");
|
|
519
|
+
await userEvent.click(overlay);
|
|
520
|
+
|
|
521
|
+
expect(handleInteractOutside).toHaveBeenCalled();
|
|
522
|
+
|
|
523
|
+
// Modal should still be open since we prevented default
|
|
524
|
+
expect(screen.getByRole("dialog")).toBeInTheDocument();
|
|
525
|
+
});
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
describe("Convenience boolean props", () => {
|
|
529
|
+
describe("disableOutsideClickClose", () => {
|
|
530
|
+
it("should prevent modal from closing when clicking outside", async () => {
|
|
531
|
+
const handleOpenChange = jest.fn();
|
|
532
|
+
|
|
533
|
+
render(
|
|
534
|
+
<Modal
|
|
535
|
+
open={true}
|
|
536
|
+
onOpenChange={handleOpenChange}
|
|
537
|
+
disableOutsideClickClose
|
|
538
|
+
title="Test Modal"
|
|
539
|
+
description="Test description"
|
|
540
|
+
closeButtonAriaLabel="Close"
|
|
541
|
+
>
|
|
542
|
+
<ModalBody>Content</ModalBody>
|
|
543
|
+
</Modal>
|
|
544
|
+
);
|
|
545
|
+
|
|
546
|
+
const overlay = screen.getByTestId("modal-overlay");
|
|
547
|
+
await userEvent.click(overlay);
|
|
548
|
+
|
|
549
|
+
expect(handleOpenChange).not.toHaveBeenCalled();
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
it("should still call custom onInteractOutside handler when disableOutsideClickClose is true", async () => {
|
|
553
|
+
const handleInteractOutside = jest.fn();
|
|
554
|
+
const handleOpenChange = jest.fn();
|
|
555
|
+
|
|
556
|
+
render(
|
|
557
|
+
<Modal
|
|
558
|
+
open={true}
|
|
559
|
+
onOpenChange={handleOpenChange}
|
|
560
|
+
disableOutsideClickClose
|
|
561
|
+
onInteractOutside={handleInteractOutside}
|
|
562
|
+
title="Test Modal"
|
|
563
|
+
description="Test description"
|
|
564
|
+
closeButtonAriaLabel="Close"
|
|
565
|
+
>
|
|
566
|
+
<ModalBody>Content</ModalBody>
|
|
567
|
+
</Modal>
|
|
568
|
+
);
|
|
569
|
+
|
|
570
|
+
const overlay = screen.getByTestId("modal-overlay");
|
|
571
|
+
await userEvent.click(overlay);
|
|
572
|
+
|
|
573
|
+
expect(handleInteractOutside).toHaveBeenCalled();
|
|
574
|
+
expect(handleOpenChange).not.toHaveBeenCalled();
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
it("should allow closing via close button even when disableOutsideClickClose is true", async () => {
|
|
578
|
+
const handleOpenChange = jest.fn();
|
|
579
|
+
|
|
580
|
+
render(
|
|
581
|
+
<Modal
|
|
582
|
+
open={true}
|
|
583
|
+
onOpenChange={handleOpenChange}
|
|
584
|
+
disableOutsideClickClose
|
|
585
|
+
title="Test Modal"
|
|
586
|
+
description="Test description"
|
|
587
|
+
closeButtonAriaLabel="Close modal"
|
|
588
|
+
>
|
|
589
|
+
<ModalBody>Content</ModalBody>
|
|
590
|
+
</Modal>
|
|
591
|
+
);
|
|
592
|
+
|
|
593
|
+
const closeButton = screen.getByRole("button", { name: "Close modal" });
|
|
594
|
+
await userEvent.click(closeButton);
|
|
595
|
+
|
|
596
|
+
expect(handleOpenChange).toHaveBeenCalledWith(false);
|
|
597
|
+
});
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
describe("disableEscapeKeyClose", () => {
|
|
601
|
+
it("should prevent modal from closing when pressing Escape", async () => {
|
|
602
|
+
const handleOpenChange = jest.fn();
|
|
603
|
+
|
|
604
|
+
render(
|
|
605
|
+
<Modal
|
|
606
|
+
open={true}
|
|
607
|
+
onOpenChange={handleOpenChange}
|
|
608
|
+
disableEscapeKeyClose
|
|
609
|
+
title="Test Modal"
|
|
610
|
+
description="Test description"
|
|
611
|
+
closeButtonAriaLabel="Close"
|
|
612
|
+
>
|
|
613
|
+
<ModalBody>Content</ModalBody>
|
|
614
|
+
</Modal>
|
|
615
|
+
);
|
|
616
|
+
|
|
617
|
+
const dialog = screen.getByRole("dialog");
|
|
618
|
+
fireEvent.keyDown(dialog, { key: "Escape", code: "Escape" });
|
|
619
|
+
|
|
620
|
+
expect(handleOpenChange).not.toHaveBeenCalled();
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
it("should still call custom onEscapeKeyDown handler when disableEscapeKeyClose is true", async () => {
|
|
624
|
+
const handleEscapeKeyDown = jest.fn();
|
|
625
|
+
const handleOpenChange = jest.fn();
|
|
626
|
+
|
|
627
|
+
render(
|
|
628
|
+
<Modal
|
|
629
|
+
open={true}
|
|
630
|
+
onOpenChange={handleOpenChange}
|
|
631
|
+
disableEscapeKeyClose
|
|
632
|
+
onEscapeKeyDown={handleEscapeKeyDown}
|
|
633
|
+
title="Test Modal"
|
|
634
|
+
description="Test description"
|
|
635
|
+
closeButtonAriaLabel="Close"
|
|
636
|
+
>
|
|
637
|
+
<ModalBody>Content</ModalBody>
|
|
638
|
+
</Modal>
|
|
639
|
+
);
|
|
640
|
+
|
|
641
|
+
const dialog = screen.getByRole("dialog");
|
|
642
|
+
fireEvent.keyDown(dialog, { key: "Escape", code: "Escape" });
|
|
643
|
+
|
|
644
|
+
expect(handleEscapeKeyDown).toHaveBeenCalled();
|
|
645
|
+
expect(handleOpenChange).not.toHaveBeenCalled();
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
it("should allow closing via close button even when disableEscapeKeyClose is true", async () => {
|
|
649
|
+
const handleOpenChange = jest.fn();
|
|
650
|
+
|
|
651
|
+
render(
|
|
652
|
+
<Modal
|
|
653
|
+
open={true}
|
|
654
|
+
onOpenChange={handleOpenChange}
|
|
655
|
+
disableEscapeKeyClose
|
|
656
|
+
title="Test Modal"
|
|
657
|
+
closeButtonAriaLabel="Close modal"
|
|
658
|
+
>
|
|
659
|
+
<ModalBody>Content</ModalBody>
|
|
660
|
+
</Modal>
|
|
661
|
+
);
|
|
662
|
+
|
|
663
|
+
const closeButton = screen.getByRole("button", { name: "Close modal" });
|
|
664
|
+
await userEvent.click(closeButton);
|
|
665
|
+
|
|
666
|
+
expect(handleOpenChange).toHaveBeenCalledWith(false);
|
|
667
|
+
});
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
describe("Combined convenience props", () => {
|
|
671
|
+
it("should prevent closing via both outside click and Escape when both props are true", async () => {
|
|
672
|
+
const handleOpenChange = jest.fn();
|
|
673
|
+
|
|
674
|
+
render(
|
|
675
|
+
<Modal
|
|
676
|
+
open={true}
|
|
677
|
+
onOpenChange={handleOpenChange}
|
|
678
|
+
disableOutsideClickClose
|
|
679
|
+
disableEscapeKeyClose
|
|
680
|
+
title="Test Modal"
|
|
681
|
+
description="Test description"
|
|
682
|
+
closeButtonAriaLabel="Close"
|
|
683
|
+
>
|
|
684
|
+
<ModalBody>Content</ModalBody>
|
|
685
|
+
</Modal>
|
|
686
|
+
);
|
|
687
|
+
|
|
688
|
+
// Try to close via outside click
|
|
689
|
+
const overlay = screen.getByTestId("modal-overlay");
|
|
690
|
+
await userEvent.click(overlay);
|
|
691
|
+
expect(handleOpenChange).not.toHaveBeenCalled();
|
|
692
|
+
|
|
693
|
+
// Try to close via Escape key
|
|
694
|
+
const dialog = screen.getByRole("dialog");
|
|
695
|
+
fireEvent.keyDown(dialog, { key: "Escape", code: "Escape" });
|
|
696
|
+
expect(handleOpenChange).not.toHaveBeenCalled();
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
it("should call both custom handlers when both convenience props and handlers are provided", async () => {
|
|
700
|
+
const handleInteractOutside = jest.fn();
|
|
701
|
+
const handleEscapeKeyDown = jest.fn();
|
|
702
|
+
const handleOpenChange = jest.fn();
|
|
703
|
+
|
|
704
|
+
render(
|
|
705
|
+
<Modal
|
|
706
|
+
open={true}
|
|
707
|
+
onOpenChange={handleOpenChange}
|
|
708
|
+
disableOutsideClickClose
|
|
709
|
+
disableEscapeKeyClose
|
|
710
|
+
onInteractOutside={handleInteractOutside}
|
|
711
|
+
onEscapeKeyDown={handleEscapeKeyDown}
|
|
712
|
+
title="Test Modal"
|
|
713
|
+
description="Test description"
|
|
714
|
+
closeButtonAriaLabel="Close"
|
|
715
|
+
>
|
|
716
|
+
<ModalBody>Content</ModalBody>
|
|
717
|
+
</Modal>
|
|
718
|
+
);
|
|
719
|
+
|
|
720
|
+
// Click outside
|
|
721
|
+
const overlay = screen.getByTestId("modal-overlay");
|
|
722
|
+
await userEvent.click(overlay);
|
|
723
|
+
expect(handleInteractOutside).toHaveBeenCalled();
|
|
724
|
+
expect(handleOpenChange).not.toHaveBeenCalled();
|
|
725
|
+
|
|
726
|
+
// Press Escape
|
|
727
|
+
const dialog = screen.getByRole("dialog");
|
|
728
|
+
fireEvent.keyDown(dialog, { key: "Escape", code: "Escape" });
|
|
729
|
+
expect(handleEscapeKeyDown).toHaveBeenCalled();
|
|
730
|
+
expect(handleOpenChange).not.toHaveBeenCalled();
|
|
731
|
+
});
|
|
732
|
+
});
|
|
733
|
+
|
|
734
|
+
describe("Convenience props with draggable modals", () => {
|
|
735
|
+
it("should not support disableOutsideClickClose for draggable modals (TypeScript)", () => {
|
|
736
|
+
expect(true).toBe(true);
|
|
737
|
+
});
|
|
738
|
+
|
|
739
|
+
it("should support disableEscapeKeyClose for draggable modals", async () => {
|
|
740
|
+
const handleOpenChange = jest.fn();
|
|
741
|
+
|
|
742
|
+
render(
|
|
743
|
+
<Modal
|
|
744
|
+
open={true}
|
|
745
|
+
draggable={true}
|
|
746
|
+
onOpenChange={handleOpenChange}
|
|
747
|
+
disableEscapeKeyClose
|
|
748
|
+
title="Test Modal"
|
|
749
|
+
description="Test description"
|
|
750
|
+
closeButtonAriaLabel="Close"
|
|
751
|
+
>
|
|
752
|
+
<ModalBody>Content</ModalBody>
|
|
753
|
+
</Modal>
|
|
754
|
+
);
|
|
755
|
+
|
|
756
|
+
const dialog = screen.getByRole("dialog");
|
|
757
|
+
fireEvent.keyDown(dialog, { key: "Escape", code: "Escape" });
|
|
758
|
+
|
|
759
|
+
expect(handleOpenChange).not.toHaveBeenCalled();
|
|
760
|
+
});
|
|
761
|
+
});
|
|
762
|
+
});
|
|
763
|
+
});
|
|
764
|
+
|
|
765
|
+
describe("useModalExternalTrigger hook", () => {
|
|
766
|
+
it("should return triggerRef, triggerProps function, and onCloseAutoFocus callback", () => {
|
|
767
|
+
const { result } = renderHook(() => useModalExternalTrigger());
|
|
768
|
+
|
|
769
|
+
expect(result.current.triggerRef).toBeDefined();
|
|
770
|
+
expect(result.current.triggerRef.current).toBeNull();
|
|
771
|
+
expect(typeof result.current.triggerProps).toBe("function");
|
|
772
|
+
expect(typeof result.current.onCloseAutoFocus).toBe("function");
|
|
773
|
+
});
|
|
774
|
+
|
|
775
|
+
it("should return correct ARIA props based on isOpen state", () => {
|
|
776
|
+
const { result } = renderHook(() => useModalExternalTrigger());
|
|
777
|
+
|
|
778
|
+
const propsWhenClosed = result.current.triggerProps(false);
|
|
779
|
+
expect(propsWhenClosed).toEqual({
|
|
780
|
+
"aria-haspopup": "dialog",
|
|
781
|
+
"aria-expanded": false,
|
|
782
|
+
});
|
|
783
|
+
|
|
784
|
+
const propsWhenOpen = result.current.triggerProps(true);
|
|
785
|
+
expect(propsWhenOpen).toEqual({
|
|
786
|
+
"aria-haspopup": "dialog",
|
|
787
|
+
"aria-expanded": true,
|
|
788
|
+
});
|
|
789
|
+
});
|
|
790
|
+
|
|
791
|
+
it("should include aria-controls when modalId is provided", () => {
|
|
792
|
+
const { result } = renderHook(() => useModalExternalTrigger("test-modal"));
|
|
793
|
+
|
|
794
|
+
const props = result.current.triggerProps(false);
|
|
795
|
+
expect(props).toEqual({
|
|
796
|
+
"aria-haspopup": "dialog",
|
|
797
|
+
"aria-expanded": false,
|
|
798
|
+
"aria-controls": "test-modal",
|
|
799
|
+
});
|
|
800
|
+
});
|
|
801
|
+
|
|
802
|
+
it("should prevent default and focus trigger on onCloseAutoFocus", () => {
|
|
803
|
+
const { result } = renderHook(() => useModalExternalTrigger());
|
|
804
|
+
|
|
805
|
+
const mockElement = { focus: jest.fn() };
|
|
806
|
+
// @ts-ignore - setting current for test
|
|
807
|
+
result.current.triggerRef.current = mockElement as any;
|
|
808
|
+
|
|
809
|
+
const mockEvent = { preventDefault: jest.fn() } as any;
|
|
810
|
+
result.current.onCloseAutoFocus(mockEvent);
|
|
811
|
+
|
|
812
|
+
expect(mockEvent.preventDefault).toHaveBeenCalled();
|
|
813
|
+
expect(mockElement.focus).toHaveBeenCalled();
|
|
814
|
+
});
|
|
815
|
+
|
|
816
|
+
it("should not throw if triggerRef.current is null", () => {
|
|
817
|
+
const { result } = renderHook(() => useModalExternalTrigger());
|
|
818
|
+
|
|
819
|
+
const mockEvent = { preventDefault: jest.fn() } as any;
|
|
820
|
+
|
|
821
|
+
expect(() => {
|
|
822
|
+
result.current.onCloseAutoFocus(mockEvent);
|
|
823
|
+
}).not.toThrow();
|
|
824
|
+
|
|
825
|
+
expect(mockEvent.preventDefault).toHaveBeenCalled();
|
|
826
|
+
});
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
describe("ModalExternalTrigger component", () => {
|
|
830
|
+
it("should render with Button and display children", () => {
|
|
831
|
+
render(
|
|
832
|
+
<ModalExternalTrigger isOpen={false} onTrigger={jest.fn()}>
|
|
833
|
+
Open Modal
|
|
834
|
+
</ModalExternalTrigger>
|
|
835
|
+
);
|
|
836
|
+
|
|
837
|
+
const button = screen.getByRole("button");
|
|
838
|
+
expect(button).toBeInTheDocument();
|
|
839
|
+
expect(button).toHaveTextContent("Open Modal");
|
|
840
|
+
});
|
|
841
|
+
|
|
842
|
+
it("should have correct ARIA attributes when closed", () => {
|
|
843
|
+
render(
|
|
844
|
+
<ModalExternalTrigger isOpen={false} onTrigger={jest.fn()}>
|
|
845
|
+
Open Modal
|
|
846
|
+
</ModalExternalTrigger>
|
|
847
|
+
);
|
|
848
|
+
|
|
849
|
+
const button = screen.getByRole("button");
|
|
850
|
+
expect(button).toHaveAttribute("aria-haspopup", "dialog");
|
|
851
|
+
expect(button).toHaveAttribute("aria-expanded", "false");
|
|
852
|
+
});
|
|
853
|
+
|
|
854
|
+
it("should have correct ARIA attributes when open", () => {
|
|
855
|
+
render(
|
|
856
|
+
<ModalExternalTrigger isOpen={true} onTrigger={jest.fn()}>
|
|
857
|
+
Open Modal
|
|
858
|
+
</ModalExternalTrigger>
|
|
859
|
+
);
|
|
860
|
+
|
|
861
|
+
const button = screen.getByRole("button");
|
|
862
|
+
expect(button).toHaveAttribute("aria-haspopup", "dialog");
|
|
863
|
+
expect(button).toHaveAttribute("aria-expanded", "true");
|
|
864
|
+
});
|
|
865
|
+
|
|
866
|
+
it("should include aria-controls when modalId is provided", () => {
|
|
867
|
+
render(
|
|
868
|
+
<ModalExternalTrigger
|
|
869
|
+
isOpen={false}
|
|
870
|
+
onTrigger={jest.fn()}
|
|
871
|
+
modalId="test-modal"
|
|
872
|
+
>
|
|
873
|
+
Open Modal
|
|
874
|
+
</ModalExternalTrigger>
|
|
875
|
+
);
|
|
876
|
+
|
|
877
|
+
const button = screen.getByRole("button");
|
|
878
|
+
expect(button).toHaveAttribute("aria-controls", "test-modal");
|
|
879
|
+
});
|
|
880
|
+
|
|
881
|
+
it("should call onTrigger when clicked", async () => {
|
|
882
|
+
const onTrigger = jest.fn();
|
|
883
|
+
|
|
884
|
+
render(
|
|
885
|
+
<ModalExternalTrigger isOpen={false} onTrigger={onTrigger}>
|
|
886
|
+
Open Modal
|
|
887
|
+
</ModalExternalTrigger>
|
|
888
|
+
);
|
|
889
|
+
|
|
890
|
+
const button = screen.getByRole("button");
|
|
891
|
+
await userEvent.click(button);
|
|
892
|
+
|
|
893
|
+
expect(onTrigger).toHaveBeenCalledTimes(1);
|
|
894
|
+
});
|
|
895
|
+
|
|
896
|
+
it("should call custom onClick before onTrigger", async () => {
|
|
897
|
+
const onClick = jest.fn();
|
|
898
|
+
const onTrigger = jest.fn();
|
|
899
|
+
|
|
900
|
+
render(
|
|
901
|
+
<ModalExternalTrigger
|
|
902
|
+
isOpen={false}
|
|
903
|
+
onTrigger={onTrigger}
|
|
904
|
+
onClick={onClick}
|
|
905
|
+
>
|
|
906
|
+
Open Modal
|
|
907
|
+
</ModalExternalTrigger>
|
|
908
|
+
);
|
|
909
|
+
|
|
910
|
+
const button = screen.getByRole("button");
|
|
911
|
+
await userEvent.click(button);
|
|
912
|
+
|
|
913
|
+
expect(onClick).toHaveBeenCalled();
|
|
914
|
+
expect(onTrigger).toHaveBeenCalled();
|
|
915
|
+
// onClick should be called before onTrigger
|
|
916
|
+
expect(onClick.mock.invocationCallOrder[0]!).toBeLessThan(
|
|
917
|
+
onTrigger.mock.invocationCallOrder[0]!
|
|
918
|
+
);
|
|
919
|
+
});
|
|
920
|
+
|
|
921
|
+
it("should not call onTrigger if onClick prevents default", async () => {
|
|
922
|
+
const onClick = jest.fn((e) => e.preventDefault());
|
|
923
|
+
const onTrigger = jest.fn();
|
|
924
|
+
|
|
925
|
+
render(
|
|
926
|
+
<ModalExternalTrigger
|
|
927
|
+
isOpen={false}
|
|
928
|
+
onTrigger={onTrigger}
|
|
929
|
+
onClick={onClick}
|
|
930
|
+
>
|
|
931
|
+
Open Modal
|
|
932
|
+
</ModalExternalTrigger>
|
|
933
|
+
);
|
|
934
|
+
|
|
935
|
+
const button = screen.getByRole("button");
|
|
936
|
+
await userEvent.click(button);
|
|
937
|
+
|
|
938
|
+
expect(onClick).toHaveBeenCalled();
|
|
939
|
+
expect(onTrigger).not.toHaveBeenCalled();
|
|
940
|
+
});
|
|
941
|
+
|
|
942
|
+
it("should forward ref correctly", () => {
|
|
943
|
+
const ref = React.createRef<HTMLButtonElement>();
|
|
944
|
+
|
|
945
|
+
render(
|
|
946
|
+
<ModalExternalTrigger ref={ref} isOpen={false} onTrigger={jest.fn()}>
|
|
947
|
+
Open Modal
|
|
948
|
+
</ModalExternalTrigger>
|
|
949
|
+
);
|
|
950
|
+
|
|
951
|
+
expect(ref.current).toBeInstanceOf(HTMLButtonElement);
|
|
952
|
+
});
|
|
953
|
+
|
|
954
|
+
it("should support all Button props", () => {
|
|
955
|
+
render(
|
|
956
|
+
<ModalExternalTrigger
|
|
957
|
+
isOpen={false}
|
|
958
|
+
onTrigger={jest.fn()}
|
|
959
|
+
appearance="primary"
|
|
960
|
+
size="large"
|
|
961
|
+
disabled={true}
|
|
962
|
+
data-testid="test-button"
|
|
963
|
+
>
|
|
964
|
+
Open Modal
|
|
965
|
+
</ModalExternalTrigger>
|
|
966
|
+
);
|
|
967
|
+
|
|
968
|
+
const button = screen.getByTestId("test-button");
|
|
969
|
+
expect(button).toBeInTheDocument();
|
|
970
|
+
expect(button).toHaveAttribute("disabled");
|
|
971
|
+
});
|
|
972
|
+
});
|