or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

async-utilities.mdconfiguration.mdevents.mdindex.mdqueries.mdscoping.mduser-interactions.md

user-interactions.mddocs/

0

# User Interactions

1

2

Instrumented user-event functionality for simulating realistic user interactions with full keyboard, mouse, and input device support. All user interactions are captured by Storybook's instrumentation for display in the Interactions addon.

3

4

## Capabilities

5

6

### User Event Object

7

8

The main interface for simulating user interactions. All methods are asynchronous and return Promises.

9

10

```typescript { .api }

11

/**

12

* Instrumented user-event object for simulating realistic user interactions

13

* All methods are wrapped with Storybook instrumentation for interaction tracking

14

*/

15

interface UserEvent {

16

/**

17

* Click on an element

18

* @param element - Element to click

19

* @param options - Click configuration options

20

*/

21

click(element: Element, options?: ClickOptions): Promise<void>;

22

23

/**

24

* Double-click on an element

25

* @param element - Element to double-click

26

* @param options - Click configuration options

27

*/

28

dblClick(element: Element, options?: ClickOptions): Promise<void>;

29

30

/**

31

* Type text into an element

32

* @param element - Input element to type into

33

* @param text - Text to type

34

* @param options - Typing configuration options

35

*/

36

type(element: Element, text: string, options?: TypeOptions): Promise<void>;

37

38

/**

39

* Clear the content of an input element

40

* @param element - Input element to clear

41

*/

42

clear(element: Element): Promise<void>;

43

44

/**

45

* Select options in a select element

46

* @param element - Select element

47

* @param values - Option values to select (string or array)

48

* @param options - Selection options

49

*/

50

selectOptions(element: Element, values: string | string[], options?: SelectOptions): Promise<void>;

51

52

/**

53

* Deselect options in a select element

54

* @param element - Select element

55

* @param values - Option values to deselect (string or array)

56

* @param options - Selection options

57

*/

58

deselectOptions(element: Element, values: string | string[], options?: SelectOptions): Promise<void>;

59

60

/**

61

* Upload files to a file input

62

* @param element - File input element

63

* @param file - File or array of files to upload

64

*/

65

upload(element: Element, file: File | File[]): Promise<void>;

66

67

/**

68

* Tab to the next focusable element

69

* @param options - Tab options

70

*/

71

tab(options?: TabOptions): Promise<void>;

72

73

/**

74

* Hover over an element

75

* @param element - Element to hover over

76

* @param options - Hover options

77

*/

78

hover(element: Element, options?: HoverOptions): Promise<void>;

79

80

/**

81

* Stop hovering over an element

82

* @param element - Element to unhover

83

* @param options - Hover options

84

*/

85

unhover(element: Element, options?: HoverOptions): Promise<void>;

86

87

/**

88

* Press and hold keyboard keys

89

* @param keys - Key or combination of keys to press

90

* @param options - Keyboard options

91

*/

92

keyboard(keys: string, options?: KeyboardOptions): Promise<void>;

93

94

/**

95

* Copy selected text to clipboard

96

*/

97

copy(): Promise<void>;

98

99

/**

100

* Cut selected text to clipboard

101

*/

102

cut(): Promise<void>;

103

104

/**

105

* Paste text from clipboard

106

*/

107

paste(): Promise<void>;

108

}

109

110

declare const userEvent: UserEvent;

111

```

112

113

### Click Interactions

114

115

```typescript { .api }

116

/**

117

* Options for click interactions

118

*/

119

interface ClickOptions {

120

/** Mouse button to use (0=left, 1=middle, 2=right) */

121

button?: number;

122

/** Control key modifier */

123

ctrlKey?: boolean;

124

/** Shift key modifier */

125

shiftKey?: boolean;

126

/** Alt key modifier */

127

altKey?: boolean;

128

/** Meta key modifier (Cmd on Mac, Windows key on PC) */

129

metaKey?: boolean;

130

/** Skip default behavior (e.g., don't focus element) */

131

skipDefaultPrevented?: boolean;

132

}

133

```

134

135

**Usage Examples:**

136

137

```typescript

138

import { within, userEvent } from "@storybook/testing-library";

139

140

export const ClickInteractions = {

141

play: async ({ canvasElement }) => {

142

const canvas = within(canvasElement);

143

144

// Basic click

145

const button = canvas.getByRole('button', { name: /submit/i });

146

await userEvent.click(button);

147

148

// Click with modifiers

149

const link = canvas.getByRole('link', { name: /external/i });

150

await userEvent.click(link, { ctrlKey: true }); // Ctrl+click

151

152

// Right-click (context menu)

153

await userEvent.click(button, { button: 2 });

154

155

// Double-click

156

const item = canvas.getByTestId('double-click-item');

157

await userEvent.dblClick(item);

158

}

159

};

160

```

161

162

### Typing Interactions

163

164

```typescript { .api }

165

/**

166

* Options for typing interactions

167

*/

168

interface TypeOptions {

169

/** Delay between keystrokes in milliseconds */

170

delay?: number;

171

/** Skip clicking the element before typing */

172

skipClick?: boolean;

173

/** Skip auto-closing of characters like quotes and brackets */

174

skipAutoClose?: boolean;

175

/** Initial selection start position */

176

initialSelectionStart?: number;

177

/** Initial selection end position */

178

initialSelectionEnd?: number;

179

}

180

```

181

182

**Usage Examples:**

183

184

```typescript

185

import { within, userEvent } from "@storybook/testing-library";

186

187

export const TypingInteractions = {

188

play: async ({ canvasElement }) => {

189

const canvas = within(canvasElement);

190

191

// Basic typing

192

const input = canvas.getByLabelText(/username/i);

193

await userEvent.type(input, 'john.doe@example.com');

194

195

// Typing with delay

196

const slowInput = canvas.getByLabelText(/description/i);

197

await userEvent.type(slowInput, 'Slow typing...', { delay: 100 });

198

199

// Clear and type

200

const existingInput = canvas.getByDisplayValue('existing text');

201

await userEvent.clear(existingInput);

202

await userEvent.type(existingInput, 'new text');

203

204

// Type special characters

205

const passwordInput = canvas.getByLabelText(/password/i);

206

await userEvent.type(passwordInput, 'P@ssw0rd!23');

207

}

208

};

209

```

210

211

### Selection Interactions

212

213

```typescript { .api }

214

/**

215

* Options for select interactions

216

*/

217

interface SelectOptions {

218

/** Skip clicking the element before selecting */

219

skipClick?: boolean;

220

}

221

```

222

223

**Usage Examples:**

224

225

```typescript

226

import { within, userEvent } from "@storybook/testing-library";

227

228

export const SelectionInteractions = {

229

play: async ({ canvasElement }) => {

230

const canvas = within(canvasElement);

231

232

// Select single option

233

const countrySelect = canvas.getByLabelText(/country/i);

234

await userEvent.selectOptions(countrySelect, 'USA');

235

236

// Select multiple options

237

const skillsSelect = canvas.getByLabelText(/skills/i);

238

await userEvent.selectOptions(skillsSelect, ['JavaScript', 'TypeScript', 'React']);

239

240

// Deselect options

241

await userEvent.deselectOptions(skillsSelect, 'JavaScript');

242

243

// Select by value

244

await userEvent.selectOptions(countrySelect, { target: { value: 'canada' } });

245

}

246

};

247

```

248

249

### File Upload Interactions

250

251

**Usage Examples:**

252

253

```typescript

254

import { within, userEvent } from "@storybook/testing-library";

255

256

export const FileUploadInteractions = {

257

play: async ({ canvasElement }) => {

258

const canvas = within(canvasElement);

259

260

// Upload single file

261

const fileInput = canvas.getByLabelText(/upload file/i);

262

const file = new File(['file content'], 'test.txt', { type: 'text/plain' });

263

await userEvent.upload(fileInput, file);

264

265

// Upload multiple files

266

const multiFileInput = canvas.getByLabelText(/upload multiple/i);

267

const files = [

268

new File(['image1'], 'image1.png', { type: 'image/png' }),

269

new File(['image2'], 'image2.jpg', { type: 'image/jpeg' })

270

];

271

await userEvent.upload(multiFileInput, files);

272

}

273

};

274

```

275

276

### Keyboard Interactions

277

278

```typescript { .api }

279

/**

280

* Options for keyboard interactions

281

*/

282

interface KeyboardOptions {

283

/** Skip auto-releasing pressed keys */

284

skipAutoClose?: boolean;

285

}

286

287

/**

288

* Options for tab navigation

289

*/

290

interface TabOptions {

291

/** Tab backwards (Shift+Tab) */

292

shift?: boolean;

293

}

294

```

295

296

**Usage Examples:**

297

298

```typescript

299

import { within, userEvent } from "@storybook/testing-library";

300

301

export const KeyboardInteractions = {

302

play: async ({ canvasElement }) => {

303

const canvas = within(canvasElement);

304

305

// Tab navigation

306

await userEvent.tab(); // Tab forward

307

await userEvent.tab({ shift: true }); // Tab backward

308

309

// Keyboard shortcuts

310

await userEvent.keyboard('{ctrl}a'); // Select all

311

await userEvent.keyboard('{ctrl}c'); // Copy

312

await userEvent.keyboard('{ctrl}v'); // Paste

313

314

// Special keys

315

await userEvent.keyboard('{Enter}'); // Enter key

316

await userEvent.keyboard('{Escape}'); // Escape key

317

await userEvent.keyboard('{ArrowDown}'); // Arrow down

318

319

// Key combinations

320

await userEvent.keyboard('{ctrl}{shift}k'); // Ctrl+Shift+K

321

322

// Use convenience methods

323

await userEvent.copy();

324

await userEvent.cut();

325

await userEvent.paste();

326

}

327

};

328

```

329

330

### Hover Interactions

331

332

```typescript { .api }

333

/**

334

* Options for hover interactions

335

*/

336

interface HoverOptions {

337

/** Skip pointer events */

338

skipPointerEvents?: boolean;

339

}

340

```

341

342

**Usage Examples:**

343

344

```typescript

345

import { within, userEvent, waitFor } from "@storybook/testing-library";

346

347

export const HoverInteractions = {

348

play: async ({ canvasElement }) => {

349

const canvas = within(canvasElement);

350

351

// Hover over element

352

const tooltip = canvas.getByTestId('tooltip-trigger');

353

await userEvent.hover(tooltip);

354

355

// Wait for tooltip to appear

356

await waitFor(() => {

357

expect(canvas.getByRole('tooltip')).toBeVisible();

358

});

359

360

// Stop hovering

361

await userEvent.unhover(tooltip);

362

363

// Tooltip should disappear

364

await waitFor(() => {

365

expect(canvas.queryByRole('tooltip')).not.toBeInTheDocument();

366

});

367

}

368

};

369

```

370

371

## Advanced Usage Patterns

372

373

### Realistic User Flows

374

375

```typescript

376

import { within, userEvent, waitFor } from "@storybook/testing-library";

377

378

export const CompleteUserFlow = {

379

play: async ({ canvasElement }) => {

380

const canvas = within(canvasElement);

381

382

// Fill out a form realistically

383

const emailInput = canvas.getByLabelText(/email/i);

384

const passwordInput = canvas.getByLabelText(/password/i);

385

const submitButton = canvas.getByRole('button', { name: /sign in/i });

386

387

// Focus and type email

388

await userEvent.click(emailInput);

389

await userEvent.type(emailInput, 'user@example.com');

390

391

// Tab to password field

392

await userEvent.tab();

393

await userEvent.type(passwordInput, 'securepassword');

394

395

// Submit form

396

await userEvent.click(submitButton);

397

398

// Wait for success message

399

await waitFor(() => {

400

expect(canvas.getByText(/welcome back/i)).toBeInTheDocument();

401

});

402

}

403

};

404

```

405

406

### Error Handling

407

408

```typescript

409

import { within, userEvent } from "@storybook/testing-library";

410

411

export const ErrorHandling = {

412

play: async ({ canvasElement }) => {

413

const canvas = within(canvasElement);

414

415

try {

416

// Attempt to interact with element that might not exist

417

const button = canvas.queryByRole('button', { name: /optional/i });

418

if (button) {

419

await userEvent.click(button);

420

}

421

} catch (error) {

422

console.error('User interaction failed:', error);

423

}

424

}

425

};

426

```