0
# Event Handling
1
2
Direct DOM event triggering capabilities for lower-level interaction testing, with full instrumentation support. The `fireEvent` object provides methods for dispatching DOM events directly, useful for testing edge cases and low-level interactions.
3
4
## Capabilities
5
6
### Fire Event Object
7
8
Main interface for triggering DOM events directly. All fireEvent methods are instrumented for Storybook interaction tracking.
9
10
```typescript { .api }
11
/**
12
* Fire DOM events directly on elements
13
* All methods are instrumented for Storybook interaction tracking
14
*/
15
interface FireEvent {
16
/**
17
* Dispatch a custom event on an element
18
* @param element - Target element
19
* @param event - Event object to dispatch
20
* @returns Boolean indicating if event was not cancelled
21
*/
22
(element: Element, event: Event): boolean;
23
24
/**
25
* Fire a click event
26
* @param element - Element to click
27
* @param options - Mouse event options
28
* @returns Boolean indicating if event was not cancelled
29
*/
30
click(element: Element, options?: MouseEventInit): boolean;
31
32
/**
33
* Fire a double-click event
34
* @param element - Element to double-click
35
* @param options - Mouse event options
36
* @returns Boolean indicating if event was not cancelled
37
*/
38
dblClick(element: Element, options?: MouseEventInit): boolean;
39
40
/**
41
* Fire a mouse down event
42
* @param element - Target element
43
* @param options - Mouse event options
44
* @returns Boolean indicating if event was not cancelled
45
*/
46
mouseDown(element: Element, options?: MouseEventInit): boolean;
47
48
/**
49
* Fire a mouse up event
50
* @param element - Target element
51
* @param options - Mouse event options
52
* @returns Boolean indicating if event was not cancelled
53
*/
54
mouseUp(element: Element, options?: MouseEventInit): boolean;
55
56
/**
57
* Fire a mouse enter event
58
* @param element - Target element
59
* @param options - Mouse event options
60
* @returns Boolean indicating if event was not cancelled
61
*/
62
mouseEnter(element: Element, options?: MouseEventInit): boolean;
63
64
/**
65
* Fire a mouse leave event
66
* @param element - Target element
67
* @param options - Mouse event options
68
* @returns Boolean indicating if event was not cancelled
69
*/
70
mouseLeave(element: Element, options?: MouseEventInit): boolean;
71
72
/**
73
* Fire a mouse move event
74
* @param element - Target element
75
* @param options - Mouse event options
76
* @returns Boolean indicating if event was not cancelled
77
*/
78
mouseMove(element: Element, options?: MouseEventInit): boolean;
79
80
/**
81
* Fire a mouse over event
82
* @param element - Target element
83
* @param options - Mouse event options
84
* @returns Boolean indicating if event was not cancelled
85
*/
86
mouseOver(element: Element, options?: MouseEventInit): boolean;
87
88
/**
89
* Fire a mouse out event
90
* @param element - Target element
91
* @param options - Mouse event options
92
* @returns Boolean indicating if event was not cancelled
93
*/
94
mouseOut(element: Element, options?: MouseEventInit): boolean;
95
96
/**
97
* Fire a change event (for form elements)
98
* @param element - Form element
99
* @param options - Event options
100
* @returns Boolean indicating if event was not cancelled
101
*/
102
change(element: Element, options?: EventInit): boolean;
103
104
/**
105
* Fire an input event (for form elements)
106
* @param element - Form element
107
* @param options - Event options
108
* @returns Boolean indicating if event was not cancelled
109
*/
110
input(element: Element, options?: EventInit): boolean;
111
112
/**
113
* Fire a focus event
114
* @param element - Element to focus
115
* @param options - Focus event options
116
* @returns Boolean indicating if event was not cancelled
117
*/
118
focus(element: Element, options?: FocusEventInit): boolean;
119
120
/**
121
* Fire a blur event
122
* @param element - Element to blur
123
* @param options - Focus event options
124
* @returns Boolean indicating if event was not cancelled
125
*/
126
blur(element: Element, options?: FocusEventInit): boolean;
127
128
/**
129
* Fire a focus-in event
130
* @param element - Target element
131
* @param options - Focus event options
132
* @returns Boolean indicating if event was not cancelled
133
*/
134
focusIn(element: Element, options?: FocusEventInit): boolean;
135
136
/**
137
* Fire a focus-out event
138
* @param element - Target element
139
* @param options - Focus event options
140
* @returns Boolean indicating if event was not cancelled
141
*/
142
focusOut(element: Element, options?: FocusEventInit): boolean;
143
144
/**
145
* Fire a key down event
146
* @param element - Target element
147
* @param options - Keyboard event options
148
* @returns Boolean indicating if event was not cancelled
149
*/
150
keyDown(element: Element, options?: KeyboardEventInit): boolean;
151
152
/**
153
* Fire a key up event
154
* @param element - Target element
155
* @param options - Keyboard event options
156
* @returns Boolean indicating if event was not cancelled
157
*/
158
keyUp(element: Element, options?: KeyboardEventInit): boolean;
159
160
/**
161
* Fire a key press event (deprecated but available)
162
* @param element - Target element
163
* @param options - Keyboard event options
164
* @returns Boolean indicating if event was not cancelled
165
*/
166
keyPress(element: Element, options?: KeyboardEventInit): boolean;
167
168
/**
169
* Fire a submit event on a form
170
* @param element - Form element
171
* @param options - Event options
172
* @returns Boolean indicating if event was not cancelled
173
*/
174
submit(element: Element, options?: EventInit): boolean;
175
176
/**
177
* Fire a reset event on a form
178
* @param element - Form element
179
* @param options - Event options
180
* @returns Boolean indicating if event was not cancelled
181
*/
182
reset(element: Element, options?: EventInit): boolean;
183
184
/**
185
* Fire a select event on an input
186
* @param element - Input element
187
* @param options - Event options
188
* @returns Boolean indicating if event was not cancelled
189
*/
190
select(element: Element, options?: EventInit): boolean;
191
192
/**
193
* Fire a scroll event
194
* @param element - Scrollable element
195
* @param options - Event options
196
* @returns Boolean indicating if event was not cancelled
197
*/
198
scroll(element: Element, options?: EventInit): boolean;
199
200
/**
201
* Fire a wheel event
202
* @param element - Target element
203
* @param options - Wheel event options
204
* @returns Boolean indicating if event was not cancelled
205
*/
206
wheel(element: Element, options?: WheelEventInit): boolean;
207
208
/**
209
* Fire a context menu event (right-click)
210
* @param element - Target element
211
* @param options - Mouse event options
212
* @returns Boolean indicating if event was not cancelled
213
*/
214
contextMenu(element: Element, options?: MouseEventInit): boolean;
215
216
/**
217
* Fire a drag event
218
* @param element - Target element
219
* @param options - Drag event options
220
* @returns Boolean indicating if event was not cancelled
221
*/
222
drag(element: Element, options?: DragEventInit): boolean;
223
224
/**
225
* Fire a drag start event
226
* @param element - Target element
227
* @param options - Drag event options
228
* @returns Boolean indicating if event was not cancelled
229
*/
230
dragStart(element: Element, options?: DragEventInit): boolean;
231
232
/**
233
* Fire a drag end event
234
* @param element - Target element
235
* @param options - Drag event options
236
* @returns Boolean indicating if event was not cancelled
237
*/
238
dragEnd(element: Element, options?: DragEventInit): boolean;
239
240
/**
241
* Fire a drag enter event
242
* @param element - Target element
243
* @param options - Drag event options
244
* @returns Boolean indicating if event was not cancelled
245
*/
246
dragEnter(element: Element, options?: DragEventInit): boolean;
247
248
/**
249
* Fire a drag leave event
250
* @param element - Target element
251
* @param options - Drag event options
252
* @returns Boolean indicating if event was not cancelled
253
*/
254
dragLeave(element: Element, options?: DragEventInit): boolean;
255
256
/**
257
* Fire a drag over event
258
* @param element - Target element
259
* @param options - Drag event options
260
* @returns Boolean indicating if event was not cancelled
261
*/
262
dragOver(element: Element, options?: DragEventInit): boolean;
263
264
/**
265
* Fire a drop event
266
* @param element - Target element
267
* @param options - Drag event options
268
* @returns Boolean indicating if event was not cancelled
269
*/
270
drop(element: Element, options?: DragEventInit): boolean;
271
272
/**
273
* Fire a touch start event
274
* @param element - Target element
275
* @param options - Touch event options
276
* @returns Boolean indicating if event was not cancelled
277
*/
278
touchStart(element: Element, options?: TouchEventInit): boolean;
279
280
/**
281
* Fire a touch end event
282
* @param element - Target element
283
* @param options - Touch event options
284
* @returns Boolean indicating if event was not cancelled
285
*/
286
touchEnd(element: Element, options?: TouchEventInit): boolean;
287
288
/**
289
* Fire a touch move event
290
* @param element - Target element
291
* @param options - Touch event options
292
* @returns Boolean indicating if event was not cancelled
293
*/
294
touchMove(element: Element, options?: TouchEventInit): boolean;
295
296
/**
297
* Fire a touch cancel event
298
* @param element - Target element
299
* @param options - Touch event options
300
* @returns Boolean indicating if event was not cancelled
301
*/
302
touchCancel(element: Element, options?: TouchEventInit): boolean;
303
304
/**
305
* Fire a load event
306
* @param element - Target element
307
* @param options - Event options
308
* @returns Boolean indicating if event was not cancelled
309
*/
310
load(element: Element, options?: EventInit): boolean;
311
312
/**
313
* Fire an error event
314
* @param element - Target element
315
* @param options - Event options
316
* @returns Boolean indicating if event was not cancelled
317
*/
318
error(element: Element, options?: EventInit): boolean;
319
320
/**
321
* Fire a resize event
322
* @param element - Target element
323
* @param options - Event options
324
* @returns Boolean indicating if event was not cancelled
325
*/
326
resize(element: Element, options?: EventInit): boolean;
327
}
328
329
declare const fireEvent: FireEvent;
330
```
331
332
### Create Event Function
333
334
Creates DOM event objects that can be dispatched manually.
335
336
```typescript { .api }
337
/**
338
* Create a DOM event object
339
* @param eventType - Type of event to create (e.g., 'click', 'keydown')
340
* @param element - Element the event will be dispatched on
341
* @param options - Event initialization options
342
* @returns Created event object
343
*/
344
function createEvent(
345
eventType: string,
346
element: Element,
347
options?: EventInit
348
): Event;
349
```
350
351
## Usage Examples
352
353
### Basic Event Firing
354
355
```typescript
356
import { within, fireEvent, waitFor } from "@storybook/testing-library";
357
358
export const BasicEventExample = {
359
play: async ({ canvasElement }) => {
360
const canvas = within(canvasElement);
361
362
// Fire a click event
363
const button = canvas.getByRole('button', { name: /click me/i });
364
fireEvent.click(button);
365
366
// Verify the click was handled
367
await waitFor(() => {
368
expect(canvas.getByText(/button clicked/i)).toBeInTheDocument();
369
});
370
}
371
};
372
```
373
374
### Form Events
375
376
```typescript
377
import { within, fireEvent, waitFor } from "@storybook/testing-library";
378
379
export const FormEventsExample = {
380
play: async ({ canvasElement }) => {
381
const canvas = within(canvasElement);
382
383
const input = canvas.getByLabelText(/email/i);
384
385
// Fire focus event
386
fireEvent.focus(input);
387
expect(input).toHaveFocus();
388
389
// Fire input event with value change
390
fireEvent.input(input, { target: { value: 'user@example.com' } });
391
392
// Fire change event
393
fireEvent.change(input, { target: { value: 'user@example.com' } });
394
395
// Fire blur event
396
fireEvent.blur(input);
397
398
// Verify form validation
399
await waitFor(() => {
400
expect(canvas.queryByText(/invalid email/i)).not.toBeInTheDocument();
401
});
402
}
403
};
404
```
405
406
### Keyboard Events
407
408
```typescript
409
import { within, fireEvent, waitFor } from "@storybook/testing-library";
410
411
export const KeyboardEventsExample = {
412
play: async ({ canvasElement }) => {
413
const canvas = within(canvasElement);
414
415
const input = canvas.getByLabelText(/search/i);
416
fireEvent.focus(input);
417
418
// Fire key down events
419
fireEvent.keyDown(input, { key: 'A', code: 'KeyA' });
420
fireEvent.keyDown(input, { key: 'B', code: 'KeyB' });
421
fireEvent.keyDown(input, { key: 'C', code: 'KeyC' });
422
423
// Fire Enter key
424
fireEvent.keyDown(input, { key: 'Enter', code: 'Enter' });
425
426
// Verify search was triggered
427
await waitFor(() => {
428
expect(canvas.getByText(/searching for: ABC/i)).toBeInTheDocument();
429
});
430
}
431
};
432
```
433
434
### Mouse Events
435
436
```typescript
437
import { within, fireEvent, waitFor } from "@storybook/testing-library";
438
439
export const MouseEventsExample = {
440
play: async ({ canvasElement }) => {
441
const canvas = within(canvasElement);
442
443
const draggable = canvas.getByTestId('draggable-item');
444
const dropzone = canvas.getByTestId('drop-zone');
445
446
// Simulate mouse drag
447
fireEvent.mouseDown(draggable, { clientX: 100, clientY: 100 });
448
fireEvent.mouseMove(draggable, { clientX: 150, clientY: 150 });
449
fireEvent.mouseUp(draggable, { clientX: 200, clientY: 200 });
450
451
// Hover events
452
fireEvent.mouseEnter(dropzone);
453
fireEvent.mouseOver(dropzone);
454
fireEvent.mouseLeave(dropzone);
455
456
// Context menu
457
fireEvent.contextMenu(draggable);
458
459
await waitFor(() => {
460
expect(canvas.getByRole('menu')).toBeInTheDocument();
461
});
462
}
463
};
464
```
465
466
### Drag and Drop Events
467
468
```typescript
469
import { within, fireEvent, waitFor } from "@storybook/testing-library";
470
471
export const DragDropExample = {
472
play: async ({ canvasElement }) => {
473
const canvas = within(canvasElement);
474
475
const draggable = canvas.getByTestId('draggable');
476
const dropzone = canvas.getByTestId('dropzone');
477
478
// Start drag operation
479
fireEvent.dragStart(draggable, {
480
dataTransfer: {
481
setData: (format: string, data: string) => {},
482
getData: (format: string) => 'dragged-item'
483
}
484
});
485
486
// Drag over drop zone
487
fireEvent.dragEnter(dropzone);
488
fireEvent.dragOver(dropzone);
489
490
// Complete drop
491
fireEvent.drop(dropzone, {
492
dataTransfer: {
493
getData: (format: string) => 'dragged-item'
494
}
495
});
496
497
fireEvent.dragEnd(draggable);
498
499
// Verify drop was handled
500
await waitFor(() => {
501
expect(canvas.getByText(/item dropped/i)).toBeInTheDocument();
502
});
503
}
504
};
505
```
506
507
### Touch Events
508
509
```typescript
510
import { within, fireEvent, waitFor } from "@storybook/testing-library";
511
512
export const TouchEventsExample = {
513
play: async ({ canvasElement }) => {
514
const canvas = within(canvasElement);
515
516
const touchTarget = canvas.getByTestId('touch-target');
517
518
// Simulate touch interaction
519
fireEvent.touchStart(touchTarget, {
520
touches: [{ clientX: 100, clientY: 100 }]
521
});
522
523
fireEvent.touchMove(touchTarget, {
524
touches: [{ clientX: 150, clientY: 150 }]
525
});
526
527
fireEvent.touchEnd(touchTarget, {
528
changedTouches: [{ clientX: 150, clientY: 150 }]
529
});
530
531
// Verify touch interaction
532
await waitFor(() => {
533
expect(canvas.getByText(/touched/i)).toBeInTheDocument();
534
});
535
}
536
};
537
```
538
539
### Custom Events
540
541
```typescript
542
import { within, fireEvent, createEvent, waitFor } from "@storybook/testing-library";
543
544
export const CustomEventsExample = {
545
play: async ({ canvasElement }) => {
546
const canvas = within(canvasElement);
547
548
const component = canvas.getByTestId('custom-component');
549
550
// Create and fire custom event
551
const customEvent = createEvent('customEvent', component, {
552
bubbles: true,
553
detail: { message: 'Hello from custom event' }
554
});
555
556
fireEvent(component, customEvent);
557
558
// Verify custom event was handled
559
await waitFor(() => {
560
expect(canvas.getByText(/custom event received/i)).toBeInTheDocument();
561
});
562
}
563
};
564
```
565
566
### Event Options and Modifiers
567
568
```typescript
569
import { within, fireEvent } from "@storybook/testing-library";
570
571
export const EventOptionsExample = {
572
play: async ({ canvasElement }) => {
573
const canvas = within(canvasElement);
574
575
const button = canvas.getByRole('button', { name: /special click/i });
576
577
// Click with modifier keys
578
fireEvent.click(button, {
579
ctrlKey: true,
580
shiftKey: true,
581
button: 0, // Left mouse button
582
clientX: 100,
583
clientY: 200
584
});
585
586
// Keyboard event with modifiers
587
const input = canvas.getByLabelText(/shortcut input/i);
588
fireEvent.keyDown(input, {
589
key: 'S',
590
code: 'KeyS',
591
ctrlKey: true,
592
shiftKey: true
593
});
594
}
595
};
596
```
597
598
### Form Submission
599
600
```typescript
601
import { within, fireEvent, waitFor } from "@storybook/testing-library";
602
603
export const FormSubmissionExample = {
604
play: async ({ canvasElement }) => {
605
const canvas = within(canvasElement);
606
607
const form = canvas.getByRole('form');
608
const emailInput = canvas.getByLabelText(/email/i);
609
const passwordInput = canvas.getByLabelText(/password/i);
610
611
// Fill form
612
fireEvent.change(emailInput, { target: { value: 'user@example.com' } });
613
fireEvent.change(passwordInput, { target: { value: 'password123' } });
614
615
// Submit form
616
fireEvent.submit(form);
617
618
// Verify submission
619
await waitFor(() => {
620
expect(canvas.getByText(/form submitted/i)).toBeInTheDocument();
621
});
622
}
623
};
624
```
625
626
### Scroll Events
627
628
```typescript
629
import { within, fireEvent, waitFor } from "@storybook/testing-library";
630
631
export const ScrollEventsExample = {
632
play: async ({ canvasElement }) => {
633
const canvas = within(canvasElement);
634
635
const scrollableContainer = canvas.getByTestId('scrollable');
636
637
// Fire scroll event
638
fireEvent.scroll(scrollableContainer, { target: { scrollY: 100 } });
639
640
// Fire wheel event for mouse wheel scrolling
641
fireEvent.wheel(scrollableContainer, { deltaY: 100 });
642
643
// Verify scroll behavior
644
await waitFor(() => {
645
expect(canvas.getByText(/scrolled/i)).toBeInTheDocument();
646
});
647
}
648
};
649
```
650
651
## When to Use fireEvent vs userEvent
652
653
### Use fireEvent when:
654
- Testing low-level event handling
655
- Need precise control over event properties
656
- Testing edge cases or error conditions
657
- Working with custom events
658
- Need synchronous event firing
659
660
### Use userEvent when:
661
- Simulating realistic user interactions
662
- Testing the complete user experience
663
- Want higher-level interaction methods
664
- Need async behavior that matches real users
665
666
```typescript
667
import { within, fireEvent, userEvent } from "@storybook/testing-library";
668
669
export const ComparisonExample = {
670
play: async ({ canvasElement }) => {
671
const canvas = within(canvasElement);
672
673
const input = canvas.getByLabelText(/username/i);
674
675
// fireEvent - direct, low-level
676
fireEvent.change(input, { target: { value: 'testuser' } });
677
678
// userEvent - realistic, high-level
679
await userEvent.type(input, 'testuser');
680
}
681
};
682
```