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

scoping.mddocs/

0

# Element Scoping

1

2

Scoping utilities for limiting queries to specific DOM containers, essential for Storybook story isolation. The `within` function creates a scoped set of queries bound to a specific container element.

3

4

## Capabilities

5

6

### Within Function

7

8

Creates a new set of query functions bound to a specific container element, ensuring queries only search within that container.

9

10

```typescript { .api }

11

/**

12

* Create scoped queries bound to a specific container element

13

* @param element - Container element to scope queries to

14

* @returns Object containing all query functions bound to the container

15

*/

16

function within(element: HTMLElement): BoundQueries;

17

18

/**

19

* All query functions bound to a specific container element

20

* Essential for Storybook story isolation - use instead of screen object

21

*/

22

interface BoundQueries {

23

// getBy* queries - throw if not found

24

getByRole(role: string, options?: ByRoleOptions): HTMLElement;

25

getByText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement;

26

getByTestId(testId: string, options?: SelectorMatcherOptions): HTMLElement;

27

getByLabelText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement;

28

getByPlaceholderText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement;

29

getByAltText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement;

30

getByTitle(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement;

31

getByDisplayValue(value: string | RegExp, options?: SelectorMatcherOptions): HTMLElement;

32

33

// getAllBy* queries - throw if none found, return array

34

getAllByRole(role: string, options?: ByRoleOptions): HTMLElement[];

35

getAllByText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement[];

36

getAllByTestId(testId: string, options?: SelectorMatcherOptions): HTMLElement[];

37

getAllByLabelText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement[];

38

getAllByPlaceholderText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement[];

39

getAllByAltText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement[];

40

getAllByTitle(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement[];

41

getAllByDisplayValue(value: string | RegExp, options?: SelectorMatcherOptions): HTMLElement[];

42

43

// queryBy* queries - return null if not found

44

queryByRole(role: string, options?: ByRoleOptions): HTMLElement | null;

45

queryByText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement | null;

46

queryByTestId(testId: string, options?: SelectorMatcherOptions): HTMLElement | null;

47

queryByLabelText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement | null;

48

queryByPlaceholderText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement | null;

49

queryByAltText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement | null;

50

queryByTitle(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement | null;

51

queryByDisplayValue(value: string | RegExp, options?: SelectorMatcherOptions): HTMLElement | null;

52

queryByAttribute(attribute: string, value: string | RegExp, options?: SelectorMatcherOptions): HTMLElement | null;

53

54

// queryAllBy* queries - return empty array if none found

55

queryAllByRole(role: string, options?: ByRoleOptions): HTMLElement[];

56

queryAllByText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement[];

57

queryAllByTestId(testId: string, options?: SelectorMatcherOptions): HTMLElement[];

58

queryAllByLabelText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement[];

59

queryAllByPlaceholderText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement[];

60

queryAllByAltText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement[];

61

queryAllByTitle(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement[];

62

queryAllByDisplayValue(value: string | RegExp, options?: SelectorMatcherOptions): HTMLElement[];

63

queryAllByAttribute(attribute: string, value: string | RegExp, options?: SelectorMatcherOptions): HTMLElement[];

64

65

// findBy* queries - async, reject if not found within timeout

66

findByRole(role: string, options?: ByRoleOptions, waitForOptions?: WaitForOptions): Promise<HTMLElement>;

67

findByText(text: string | RegExp, options?: SelectorMatcherOptions, waitForOptions?: WaitForOptions): Promise<HTMLElement>;

68

findByTestId(testId: string, options?: SelectorMatcherOptions, waitForOptions?: WaitForOptions): Promise<HTMLElement>;

69

findByLabelText(text: string | RegExp, options?: SelectorMatcherOptions, waitForOptions?: WaitForOptions): Promise<HTMLElement>;

70

findByPlaceholderText(text: string | RegExp, options?: SelectorMatcherOptions, waitForOptions?: WaitForOptions): Promise<HTMLElement>;

71

findByAltText(text: string | RegExp, options?: SelectorMatcherOptions, waitForOptions?: WaitForOptions): Promise<HTMLElement>;

72

findByTitle(text: string | RegExp, options?: SelectorMatcherOptions, waitForOptions?: WaitForOptions): Promise<HTMLElement>;

73

findByDisplayValue(value: string | RegExp, options?: SelectorMatcherOptions, waitForOptions?: WaitForOptions): Promise<HTMLElement>;

74

75

// findAllBy* queries - async, reject if none found within timeout

76

findAllByRole(role: string, options?: ByRoleOptions, waitForOptions?: WaitForOptions): Promise<HTMLElement[]>;

77

findAllByText(text: string | RegExp, options?: SelectorMatcherOptions, waitForOptions?: WaitForOptions): Promise<HTMLElement[]>;

78

findAllByTestId(testId: string, options?: SelectorMatcherOptions, waitForOptions?: WaitForOptions): Promise<HTMLElement[]>;

79

findAllByLabelText(text: string | RegExp, options?: SelectorMatcherOptions, waitForOptions?: WaitForOptions): Promise<HTMLElement[]>;

80

findAllByPlaceholderText(text: string | RegExp, options?: SelectorMatcherOptions, waitForOptions?: WaitForOptions): Promise<HTMLElement[]>;

81

findAllByAltText(text: string | RegExp, options?: SelectorMatcherOptions, waitForOptions?: WaitForOptions): Promise<HTMLElement[]>;

82

findAllByTitle(text: string | RegExp, options?: SelectorMatcherOptions, waitForOptions?: WaitForOptions): Promise<HTMLElement[]>;

83

findAllByDisplayValue(value: string | RegExp, options?: SelectorMatcherOptions, waitForOptions?: WaitForOptions): Promise<HTMLElement[]>;

84

}

85

```

86

87

### Get Queries For Element

88

89

Creates bound query functions for a specific element, similar to `within` but with a different interface.

90

91

```typescript { .api }

92

/**

93

* Get all bound query functions for a specific element

94

* @param element - Container element to bind queries to

95

* @returns Object containing all query functions bound to the element

96

*/

97

function getQueriesForElement(element: HTMLElement): BoundQueries;

98

```

99

100

## Usage Examples

101

102

### Basic Scoping

103

104

```typescript

105

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

106

107

export const ScopingExample = {

108

play: async ({ canvasElement }) => {

109

// Create scoped queries for the story canvas

110

const canvas = within(canvasElement);

111

112

// All queries are now scoped to canvasElement

113

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

114

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

115

const error = canvas.queryByTestId('error-message');

116

117

// Queries will only find elements within canvasElement

118

const results = canvas.getAllByRole('listitem');

119

}

120

};

121

```

122

123

### Nested Scoping

124

125

```typescript

126

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

127

128

export const NestedScopingExample = {

129

play: async ({ canvasElement }) => {

130

const canvas = within(canvasElement);

131

132

// Find a nested container

133

const modal = canvas.getByRole('dialog');

134

135

// Create scoped queries for the modal

136

const modalQueries = within(modal);

137

138

// Queries are now scoped to the modal only

139

const modalButton = modalQueries.getByRole('button', { name: /close/i });

140

const modalInput = modalQueries.getByLabelText(/search/i);

141

142

// This will only search within the modal, not the entire canvas

143

const modalContent = modalQueries.getByText(/modal content/i);

144

}

145

};

146

```

147

148

### Component Testing

149

150

```typescript

151

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

152

153

export const ComponentTestingExample = {

154

play: async ({ canvasElement }) => {

155

const canvas = within(canvasElement);

156

157

// Test a specific component within the story

158

const searchComponent = canvas.getByTestId('search-component');

159

const searchQueries = within(searchComponent);

160

161

// Interact only within the search component

162

const searchInput = searchQueries.getByRole('textbox');

163

const searchButton = searchQueries.getByRole('button', { name: /search/i });

164

165

await userEvent.type(searchInput, 'test query');

166

await userEvent.click(searchButton);

167

168

// Verify results within the component

169

const results = searchQueries.getAllByRole('option');

170

expect(results).toHaveLength(3);

171

}

172

};

173

```

174

175

### Form Section Testing

176

177

```typescript

178

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

179

180

export const FormSectionExample = {

181

play: async ({ canvasElement }) => {

182

const canvas = within(canvasElement);

183

184

// Test different sections of a form independently

185

const personalInfoSection = canvas.getByTestId('personal-info');

186

const personalInfo = within(personalInfoSection);

187

188

const addressSection = canvas.getByTestId('address-info');

189

const addressInfo = within(addressSection);

190

191

// Fill personal info section

192

await userEvent.type(personalInfo.getByLabelText(/first name/i), 'John');

193

await userEvent.type(personalInfo.getByLabelText(/last name/i), 'Doe');

194

195

// Fill address info section

196

await userEvent.type(addressInfo.getByLabelText(/street/i), '123 Main St');

197

await userEvent.type(addressInfo.getByLabelText(/city/i), 'Anytown');

198

199

// Verify each section independently

200

expect(personalInfo.getByDisplayValue('John')).toBeInTheDocument();

201

expect(addressInfo.getByDisplayValue('123 Main St')).toBeInTheDocument();

202

}

203

};

204

```

205

206

### Table Testing

207

208

```typescript

209

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

210

211

export const TableTestingExample = {

212

play: async ({ canvasElement }) => {

213

const canvas = within(canvasElement);

214

215

// Find the table

216

const table = canvas.getByRole('table');

217

const tableQueries = within(table);

218

219

// Test specific rows

220

const rows = tableQueries.getAllByRole('row');

221

expect(rows).toHaveLength(5); // Header + 4 data rows

222

223

// Test a specific row

224

const firstDataRow = rows[1]; // Skip header row

225

const rowQueries = within(firstDataRow);

226

227

// Find cells within the row

228

const nameCell = rowQueries.getByRole('cell', { name: /john doe/i });

229

const actionButton = rowQueries.getByRole('button', { name: /edit/i });

230

231

expect(nameCell).toBeInTheDocument();

232

expect(actionButton).toBeEnabled();

233

}

234

};

235

```

236

237

### Screen Object Alternative

238

239

```typescript

240

// ❌ Deprecated - shows warning in Storybook

241

import { screen } from "@storybook/testing-library";

242

243

export const ScreenExample = {

244

play: async () => {

245

// This will show a deprecation warning

246

const button = screen.getByRole('button');

247

}

248

};

249

250

// ✅ Recommended - use within() instead

251

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

252

253

export const WithinExample = {

254

play: async ({ canvasElement }) => {

255

const canvas = within(canvasElement);

256

const button = canvas.getByRole('button');

257

}

258

};

259

```

260

261

## Advanced Patterns

262

263

### Dynamic Container Selection

264

265

```typescript

266

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

267

268

export const DynamicContainerExample = {

269

play: async ({ canvasElement }) => {

270

const canvas = within(canvasElement);

271

272

// Find container based on current state

273

const activeTab = canvas.getByRole('tabpanel', { hidden: false });

274

const tabQueries = within(activeTab);

275

276

// Work with content in the active tab only

277

const tabContent = tabQueries.getByText(/tab content/i);

278

const tabButton = tabQueries.getByRole('button');

279

}

280

};

281

```

282

283

### Multi-Container Testing

284

285

```typescript

286

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

287

288

export const MultiContainerExample = {

289

play: async ({ canvasElement }) => {

290

const canvas = within(canvasElement);

291

292

// Test interaction between multiple containers

293

const sourceList = canvas.getByTestId('source-list');

294

const targetList = canvas.getByTestId('target-list');

295

296

const sourceQueries = within(sourceList);

297

const targetQueries = within(targetList);

298

299

// Move item from source to target

300

const item = sourceQueries.getByText('Item 1');

301

const moveButton = sourceQueries.getByRole('button', { name: /move/i });

302

303

await userEvent.click(moveButton);

304

305

// Verify item moved to target

306

expect(sourceQueries.queryByText('Item 1')).not.toBeInTheDocument();

307

expect(targetQueries.getByText('Item 1')).toBeInTheDocument();

308

}

309

};

310

```