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
```