Instrumented version of Testing Library for Storybook Interactions addon
—
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.
Creates a new set of query functions bound to a specific container element, ensuring queries only search within that container.
/**
* Create scoped queries bound to a specific container element
* @param element - Container element to scope queries to
* @returns Object containing all query functions bound to the container
*/
function within(element: HTMLElement): BoundQueries;
/**
* All query functions bound to a specific container element
* Essential for Storybook story isolation - use instead of screen object
*/
interface BoundQueries {
// getBy* queries - throw if not found
getByRole(role: string, options?: ByRoleOptions): HTMLElement;
getByText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement;
getByTestId(testId: string, options?: SelectorMatcherOptions): HTMLElement;
getByLabelText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement;
getByPlaceholderText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement;
getByAltText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement;
getByTitle(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement;
getByDisplayValue(value: string | RegExp, options?: SelectorMatcherOptions): HTMLElement;
// getAllBy* queries - throw if none found, return array
getAllByRole(role: string, options?: ByRoleOptions): HTMLElement[];
getAllByText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement[];
getAllByTestId(testId: string, options?: SelectorMatcherOptions): HTMLElement[];
getAllByLabelText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement[];
getAllByPlaceholderText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement[];
getAllByAltText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement[];
getAllByTitle(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement[];
getAllByDisplayValue(value: string | RegExp, options?: SelectorMatcherOptions): HTMLElement[];
// queryBy* queries - return null if not found
queryByRole(role: string, options?: ByRoleOptions): HTMLElement | null;
queryByText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement | null;
queryByTestId(testId: string, options?: SelectorMatcherOptions): HTMLElement | null;
queryByLabelText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement | null;
queryByPlaceholderText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement | null;
queryByAltText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement | null;
queryByTitle(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement | null;
queryByDisplayValue(value: string | RegExp, options?: SelectorMatcherOptions): HTMLElement | null;
queryByAttribute(attribute: string, value: string | RegExp, options?: SelectorMatcherOptions): HTMLElement | null;
// queryAllBy* queries - return empty array if none found
queryAllByRole(role: string, options?: ByRoleOptions): HTMLElement[];
queryAllByText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement[];
queryAllByTestId(testId: string, options?: SelectorMatcherOptions): HTMLElement[];
queryAllByLabelText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement[];
queryAllByPlaceholderText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement[];
queryAllByAltText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement[];
queryAllByTitle(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement[];
queryAllByDisplayValue(value: string | RegExp, options?: SelectorMatcherOptions): HTMLElement[];
queryAllByAttribute(attribute: string, value: string | RegExp, options?: SelectorMatcherOptions): HTMLElement[];
// findBy* queries - async, reject if not found within timeout
findByRole(role: string, options?: ByRoleOptions, waitForOptions?: WaitForOptions): Promise<HTMLElement>;
findByText(text: string | RegExp, options?: SelectorMatcherOptions, waitForOptions?: WaitForOptions): Promise<HTMLElement>;
findByTestId(testId: string, options?: SelectorMatcherOptions, waitForOptions?: WaitForOptions): Promise<HTMLElement>;
findByLabelText(text: string | RegExp, options?: SelectorMatcherOptions, waitForOptions?: WaitForOptions): Promise<HTMLElement>;
findByPlaceholderText(text: string | RegExp, options?: SelectorMatcherOptions, waitForOptions?: WaitForOptions): Promise<HTMLElement>;
findByAltText(text: string | RegExp, options?: SelectorMatcherOptions, waitForOptions?: WaitForOptions): Promise<HTMLElement>;
findByTitle(text: string | RegExp, options?: SelectorMatcherOptions, waitForOptions?: WaitForOptions): Promise<HTMLElement>;
findByDisplayValue(value: string | RegExp, options?: SelectorMatcherOptions, waitForOptions?: WaitForOptions): Promise<HTMLElement>;
// findAllBy* queries - async, reject if none found within timeout
findAllByRole(role: string, options?: ByRoleOptions, waitForOptions?: WaitForOptions): Promise<HTMLElement[]>;
findAllByText(text: string | RegExp, options?: SelectorMatcherOptions, waitForOptions?: WaitForOptions): Promise<HTMLElement[]>;
findAllByTestId(testId: string, options?: SelectorMatcherOptions, waitForOptions?: WaitForOptions): Promise<HTMLElement[]>;
findAllByLabelText(text: string | RegExp, options?: SelectorMatcherOptions, waitForOptions?: WaitForOptions): Promise<HTMLElement[]>;
findAllByPlaceholderText(text: string | RegExp, options?: SelectorMatcherOptions, waitForOptions?: WaitForOptions): Promise<HTMLElement[]>;
findAllByAltText(text: string | RegExp, options?: SelectorMatcherOptions, waitForOptions?: WaitForOptions): Promise<HTMLElement[]>;
findAllByTitle(text: string | RegExp, options?: SelectorMatcherOptions, waitForOptions?: WaitForOptions): Promise<HTMLElement[]>;
findAllByDisplayValue(value: string | RegExp, options?: SelectorMatcherOptions, waitForOptions?: WaitForOptions): Promise<HTMLElement[]>;
}Creates bound query functions for a specific element, similar to within but with a different interface.
/**
* Get all bound query functions for a specific element
* @param element - Container element to bind queries to
* @returns Object containing all query functions bound to the element
*/
function getQueriesForElement(element: HTMLElement): BoundQueries;import { within } from "@storybook/testing-library";
export const ScopingExample = {
play: async ({ canvasElement }) => {
// Create scoped queries for the story canvas
const canvas = within(canvasElement);
// All queries are now scoped to canvasElement
const button = canvas.getByRole('button', { name: /submit/i });
const input = canvas.getByLabelText(/email/i);
const error = canvas.queryByTestId('error-message');
// Queries will only find elements within canvasElement
const results = canvas.getAllByRole('listitem');
}
};import { within } from "@storybook/testing-library";
export const NestedScopingExample = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
// Find a nested container
const modal = canvas.getByRole('dialog');
// Create scoped queries for the modal
const modalQueries = within(modal);
// Queries are now scoped to the modal only
const modalButton = modalQueries.getByRole('button', { name: /close/i });
const modalInput = modalQueries.getByLabelText(/search/i);
// This will only search within the modal, not the entire canvas
const modalContent = modalQueries.getByText(/modal content/i);
}
};import { within, userEvent } from "@storybook/testing-library";
export const ComponentTestingExample = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
// Test a specific component within the story
const searchComponent = canvas.getByTestId('search-component');
const searchQueries = within(searchComponent);
// Interact only within the search component
const searchInput = searchQueries.getByRole('textbox');
const searchButton = searchQueries.getByRole('button', { name: /search/i });
await userEvent.type(searchInput, 'test query');
await userEvent.click(searchButton);
// Verify results within the component
const results = searchQueries.getAllByRole('option');
expect(results).toHaveLength(3);
}
};import { within, userEvent } from "@storybook/testing-library";
export const FormSectionExample = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
// Test different sections of a form independently
const personalInfoSection = canvas.getByTestId('personal-info');
const personalInfo = within(personalInfoSection);
const addressSection = canvas.getByTestId('address-info');
const addressInfo = within(addressSection);
// Fill personal info section
await userEvent.type(personalInfo.getByLabelText(/first name/i), 'John');
await userEvent.type(personalInfo.getByLabelText(/last name/i), 'Doe');
// Fill address info section
await userEvent.type(addressInfo.getByLabelText(/street/i), '123 Main St');
await userEvent.type(addressInfo.getByLabelText(/city/i), 'Anytown');
// Verify each section independently
expect(personalInfo.getByDisplayValue('John')).toBeInTheDocument();
expect(addressInfo.getByDisplayValue('123 Main St')).toBeInTheDocument();
}
};import { within } from "@storybook/testing-library";
export const TableTestingExample = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
// Find the table
const table = canvas.getByRole('table');
const tableQueries = within(table);
// Test specific rows
const rows = tableQueries.getAllByRole('row');
expect(rows).toHaveLength(5); // Header + 4 data rows
// Test a specific row
const firstDataRow = rows[1]; // Skip header row
const rowQueries = within(firstDataRow);
// Find cells within the row
const nameCell = rowQueries.getByRole('cell', { name: /john doe/i });
const actionButton = rowQueries.getByRole('button', { name: /edit/i });
expect(nameCell).toBeInTheDocument();
expect(actionButton).toBeEnabled();
}
};// ❌ Deprecated - shows warning in Storybook
import { screen } from "@storybook/testing-library";
export const ScreenExample = {
play: async () => {
// This will show a deprecation warning
const button = screen.getByRole('button');
}
};
// ✅ Recommended - use within() instead
import { within } from "@storybook/testing-library";
export const WithinExample = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const button = canvas.getByRole('button');
}
};import { within } from "@storybook/testing-library";
export const DynamicContainerExample = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
// Find container based on current state
const activeTab = canvas.getByRole('tabpanel', { hidden: false });
const tabQueries = within(activeTab);
// Work with content in the active tab only
const tabContent = tabQueries.getByText(/tab content/i);
const tabButton = tabQueries.getByRole('button');
}
};import { within, userEvent } from "@storybook/testing-library";
export const MultiContainerExample = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
// Test interaction between multiple containers
const sourceList = canvas.getByTestId('source-list');
const targetList = canvas.getByTestId('target-list');
const sourceQueries = within(sourceList);
const targetQueries = within(targetList);
// Move item from source to target
const item = sourceQueries.getByText('Item 1');
const moveButton = sourceQueries.getByRole('button', { name: /move/i });
await userEvent.click(moveButton);
// Verify item moved to target
expect(sourceQueries.queryByText('Item 1')).not.toBeInTheDocument();
expect(targetQueries.getByText('Item 1')).toBeInTheDocument();
}
};Install with Tessl CLI
npx tessl i tessl/npm-storybook--testing-library