fix(18): searchable timezone combobox with type-to-search
Replace the picker with an accessible combobox (role=combobox + role=listbox): focusing shows the full zone list (no typing/erasing needed), typing filters it case-insensitively (underscores ignored, so "york" matches America/New_York), with arrow-key navigation, Enter/click to select, and Escape to close. Fixes the datalist limitation where a pre-filled value collapsed the dropdown to one match. e2e updated to type+click options and a type-to-search case added. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
46d7fcc2d2
commit
d6f6a5ae6f
@@ -4,9 +4,10 @@
|
||||
* Verifies the admin Timezone section with the real 18-02 API endpoints.
|
||||
* Runs on desktop profile only (admin UI is desktop-focused).
|
||||
*
|
||||
* NOTE: the IANA picker is a native <select> grouped by region, which has the
|
||||
* ARIA combobox role. Selecting a zone uses selectOption (not fill), and the
|
||||
* option's value is the full IANA id even though its visible label is shortened.
|
||||
* NOTE: the IANA picker is a searchable combobox — a text input (role=combobox)
|
||||
* that opens a role=listbox of role=option items on focus. Selecting a zone means
|
||||
* focusing the input, typing to filter, then clicking the option (not selectOption).
|
||||
* Option accessible names are the full IANA id (e.g. "America/Chicago").
|
||||
*/
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
@@ -24,14 +25,16 @@ test.describe('Admin Timezone section — 18-04 round-trip', () => {
|
||||
});
|
||||
|
||||
test('Timezone picker (combobox) is visible and pre-filled', async ({ page }) => {
|
||||
// Native <select> has the combobox role
|
||||
// The searchable text input exposes role=combobox
|
||||
const input = page.getByRole('combobox', { name: 'Household timezone' });
|
||||
await expect(input).toBeVisible();
|
||||
const val = await input.inputValue();
|
||||
expect(val.length, 'Picker should have a non-empty timezone').toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test('Save is enabled on first run when timezone is not yet explicit (WR-01)', async ({ page }) => {
|
||||
test('Save is enabled on first run when timezone is not yet explicit (WR-01)', async ({
|
||||
page,
|
||||
}) => {
|
||||
// On first run the GET returns isExplicitlySet:false with the detected zone
|
||||
// pre-filled. Saving that value to make the choice explicit is a meaningful
|
||||
// action, so Save must be ENABLED even though the input matches the displayed
|
||||
@@ -49,17 +52,38 @@ test.describe('Admin Timezone section — 18-04 round-trip', () => {
|
||||
test('Changing the selection enables Save', async ({ page }) => {
|
||||
const tzSection = page.getByRole('region', { name: 'Timezone' });
|
||||
const input = page.getByRole('combobox', { name: 'Household timezone' });
|
||||
await input.selectOption('America/Chicago');
|
||||
await input.click();
|
||||
await input.fill('America/Chicago');
|
||||
await page.getByRole('option', { name: 'America/Chicago' }).click();
|
||||
await expect(input).toHaveValue('America/Chicago');
|
||||
const saveBtn = tzSection.getByRole('button', { name: /Save/ });
|
||||
await expect(saveBtn).toBeEnabled();
|
||||
});
|
||||
|
||||
test('Typing filters the list (type-to-search)', async ({ page }) => {
|
||||
const input = page.getByRole('combobox', { name: 'Household timezone' });
|
||||
const listbox = page.getByRole('listbox', { name: 'Timezones' });
|
||||
|
||||
// Focus opens the full list with no typing required.
|
||||
await input.click();
|
||||
await expect(listbox).toBeVisible();
|
||||
await expect(listbox.getByRole('option').first()).toBeVisible();
|
||||
|
||||
// Human-friendly partial query (case-insensitive, underscores ignored) filters.
|
||||
await input.fill('york');
|
||||
await expect(page.getByRole('option', { name: 'America/New_York' })).toBeVisible();
|
||||
await expect(page.getByRole('option', { name: 'Europe/Paris' })).toHaveCount(0);
|
||||
});
|
||||
|
||||
test('Save persists timezone across reload', async ({ page }) => {
|
||||
const input = page.getByRole('combobox', { name: 'Household timezone' });
|
||||
const tzSection = page.getByRole('region', { name: 'Timezone' });
|
||||
|
||||
// Set to a known value
|
||||
await input.selectOption('America/Chicago');
|
||||
// Set to a known value via the searchable combobox
|
||||
await input.click();
|
||||
await input.fill('America/Chicago');
|
||||
await page.getByRole('option', { name: 'America/Chicago' }).click();
|
||||
await expect(input).toHaveValue('America/Chicago');
|
||||
const saveBtn = tzSection.getByRole('button', { name: /^Save$/ });
|
||||
await expect(saveBtn).toBeEnabled();
|
||||
await saveBtn.click();
|
||||
|
||||
Reference in New Issue
Block a user