Component Lab B · Data grids

Playwright Table Lab

Learn reliable Playwright table testing across sorting, filters, pagination, editing, selection, virtualization, and nested data.

PRACTICE FIXTURE — intentionally built for automation practice

1. Simple static employee table

A stable semantic baseline exposes every required employee column before interactive behavior is added.

Five deterministic employees
Employee IDNameDepartmentLocationRoleSalary ₹StatusJoining Date
emp-001Rahul SharmaEngineeringBengaluruQA Analyst₹52,900Inactive2018-02-02
emp-002Rahul IyerQuality AssuranceMumbaiQA Analyst₹60,800Active2019-03-03
emp-003Rahul PatelProductDelhiEngineer₹68,700Active2020-04-04
emp-004Rahul MukherjeeDesignPuneQA Analyst₹76,700Active2021-05-05
emp-005Rahul FernandesFinanceHyderabadQA Analyst₹84,600Active2022-06-06

2. Multi-column sortable table

Each header cycles ascending, descending, and unsorted while preserving other sort keys and exposing aria-sort plus a visible priority.

No columns sorted.

Sortable API employees

3. Instant and debounced table search

Instant search updates on every keystroke. Debounced search applies only after a visible 350 ms quiet period.

Search is ready.

Search results
IDNameDepartmentLocation

4. Combined filters and no-results state

Department, location, and status filters combine predictably and render an explicit empty state.

All employees shown.

Filtered employees
NameDepartmentLocationStatusSalary ₹

5. Client-side and server-side pagination

Compare slicing an already-loaded collection with page/limit requests whose status reports the exact visible range.

Client-side pagination

Client page
IDNameDepartment

Server-side API pagination

Server page
IDNameDepartment

6. Inline editing with sandbox persistence

Edit Role in place, validate it, save through PATCH, cancel without mutation, reload to prove persistence, or reset to seed.

Choose an employee to edit.

Editable employees
IDNameDepartmentRoleAction

7–9. Dynamic, selectable, and expandable rows

Add/delete updates a count, selection distinguishes one page from every page, bulk action changes status, and details expand beside the owning row.

Dynamic rows

Dynamic employees
IDNameAction

Selectable rows and bulk action

0 employees selected.

Selectable employees
SelectNameStatus
Page 1

Expandable row details

Employee details
NameDepartmentDetails

10. Sticky header, first column, and horizontal scroll

A constrained viewport keeps context visible while both axes move across the wide table.

Wide employee view
Employee IDNameDepartmentLocationRoleSalary ₹StatusJoining Date

11. Virtualized 10,000-row table

The viewport calculates deterministic records but mounts only a small visible window, including buffer rows.

Preparing 10,000 deterministic rows.

Employee IDNameDepartmentStatus

12. Deterministic asynchronous row update

A bounded delay changes one row from Updating to Active and announces both states through a live region.

Employee emp-009 is Pending.

Async employee
IDNameStatus
emp-009Ishaan KhanPending

13. Customer API table

Refresh the current sandbox’s customer records after API setup, then verify created data through the UI.

Customer rows have not loaded.

Customers in the current isolated API sandbox
IDNameEmailCityTier
Select Refresh customer table.

14. Nested business tables

Customer orders expose line items, while departments expose employees and projects without losing the parent context.

Customer → Orders → Order Items

Synthetic customer orders
CustomerCityOrders
Ananya RaoPune
View 2 orders
Ananya Rao orders
OrderStatusItems
ORD-4803Paid
ORD-4803 items
ProductQuantity
Wireless Mouse2
USB-C Cable1
ORD-4804ShippedKeyboard × 1
Priya IyerBengaluru1 order

Department → Employees → Projects

Department projects
DepartmentEmployees
Engineering
View employees
Engineering employees and projects
EmployeeProjects
Rahul SharmaCheckout reliability, Trace triage
Ishaan KhanAPI observability
Quality Assurance18 employees

Passing examples

Start with observable behavior

These snippets match executable tests stored inexamples/playwright-ts/.

Find a row with scoped content
const engineering = page.getByRole('row', {
  name: /Rahul Sharma Engineering/,
});
await engineering.getByRole('button', { name: 'Edit' }).click();
Read a column into an array
const salaries = await page
  .locator('#filter-table tbody tr:not([hidden]) td[data-column="salary"]')
  .allTextContents();
const total = salaries.reduce((sum, value) =>
  sum + Number(value.replace(/[^0-9]/g, '')), 0);
Paginate until found
for (;;) {
  const match = page.getByRole('row', { name: /Rahul.*Engineering/ });
  if (await match.count()) break;
  const next = page.getByRole('button', { name: 'Next server page' });
  if (await next.isDisabled()) throw new Error('Employee not found');
  await next.click();
}
Handle virtualization
const viewport = page.getByTestId('virtual-viewport');
await viewport.evaluate((node) => { node.scrollTop = 47 * 9000; });
await expect(page.getByRole('row', { name: /Virtual Tester 090/ })).toBeVisible();
expect(await page.locator('#virtual-body [role="row"]').count()).toBeLessThan(40);