diff --git a/.changeset/yellow-coins-draw.md b/.changeset/yellow-coins-draw.md new file mode 100644 index 0000000000..e15965e892 --- /dev/null +++ b/.changeset/yellow-coins-draw.md @@ -0,0 +1,5 @@ +--- +"@zag-js/combobox": patch +--- + +Fix a case where item highlight was looping even though loop property was false diff --git a/.xstate/combobox.js b/.xstate/combobox.js index dd3410f354..72716bb824 100644 --- a/.xstate/combobox.js +++ b/.xstate/combobox.js @@ -129,7 +129,7 @@ const fetchMachine = createMachine({ actions: ["highlightFirstSelectedItem", "invokeOnOpen"] }, { target: "interacting", - actions: ["highlightNextItem", "invokeOnOpen"] + actions: ["highlightFirstItem", "invokeOnOpen"] }], "INPUT.ARROW_DOWN+ALT": { target: "interacting", diff --git a/e2e/combobox.e2e.ts b/e2e/combobox.e2e.ts index ab50efcbec..0dd5dccaf9 100644 --- a/e2e/combobox.e2e.ts +++ b/e2e/combobox.e2e.ts @@ -78,7 +78,7 @@ test.describe("combobox", () => { await expect(page.locator(content)).toBeHidden() }) - test("[keyboard] on arrow down, open and highlight first enabled option", async ({ page }) => { + test("[keyboard / loop] on arrow down, open and highlight first enabled option", async ({ page }) => { await page.focus(input) await page.keyboard.press("ArrowDown") const option = page.locator(options).first() @@ -86,7 +86,27 @@ test.describe("combobox", () => { await expectToBeHighlighted(option) }) - test("[keyboard] on arrow up, open and highlight last enabled option", async ({ page }) => { + test("[keyboard / no-loop] on arrow down, open and highlight first enabled option", async ({ page }) => { + await controls(page).bool("loop", false) + + await page.focus(input) + await page.keyboard.press("ArrowDown") + const option = page.locator(options).first() + await expect(page.locator(content)).toBeVisible() + await expectToBeHighlighted(option) + }) + + test("[keyboard / loop] on arrow up, open and highlight last enabled option", async ({ page }) => { + await page.focus(input) + await page.keyboard.press("ArrowUp") + const option = page.locator(options).last() + await expect(page.locator(content)).toBeVisible() + await expectToBeHighlighted(option) + }) + + test("[keyboard / no-loop] on arrow up, open and highlight last enabled option", async ({ page }) => { + await controls(page).bool("loop", false) + await page.focus(input) await page.keyboard.press("ArrowUp") const option = page.locator(options).last() @@ -137,6 +157,20 @@ test.describe("combobox", () => { await expectToBeHighlighted(option_els.first()) }) + test("[keyboard / arrowdown / no-loop]", async ({ page }) => { + await controls(page).bool("loop", false) + + await page.type(input, "mal") + + const option_els = page.locator(options) + + await repeat(4, () => page.keyboard.press("ArrowDown")) + + await expectToBeHighlighted(option_els.last()) + await page.keyboard.press("ArrowDown") + await expectToBeHighlighted(option_els.last()) + }) + test("[keyboard / arrowup / loop]", async ({ page }) => { await page.type(input, "mal") const option_els = page.locator(options) @@ -144,6 +178,15 @@ test.describe("combobox", () => { await expectToBeHighlighted(option_els.last()) }) + test("[keyboard / arrowup / no-loop]", async ({ page }) => { + await controls(page).bool("loop", false) + + await page.type(input, "mal") + const option_els = page.locator(options) + await page.keyboard.press("ArrowUp") + await expectToBeHighlighted(option_els.first()) + }) + test("[pointer / open-on-click]", async ({ page }) => { await controls(page).bool("openOnClick") await page.click(input, { force: true }) diff --git a/packages/machines/combobox/src/combobox.machine.ts b/packages/machines/combobox/src/combobox.machine.ts index a2092861d7..d8b84c06c2 100644 --- a/packages/machines/combobox/src/combobox.machine.ts +++ b/packages/machines/combobox/src/combobox.machine.ts @@ -157,7 +157,7 @@ export function machine(userContext: UserDefinedContex }, { target: "interacting", - actions: ["highlightNextItem", "invokeOnOpen"], + actions: ["highlightFirstItem", "invokeOnOpen"], }, ], "INPUT.ARROW_DOWN+ALT": { @@ -558,11 +558,11 @@ export function machine(userContext: UserDefinedContex set.highlightedItem(ctx, value) }, highlightNextItem(ctx) { - const value = ctx.collection.next(ctx.highlightedValue) ?? ctx.collection.first() + const value = ctx.collection.next(ctx.highlightedValue) ?? (ctx.loop ? ctx.collection.first() : null) set.highlightedItem(ctx, value) }, highlightPrevItem(ctx) { - const value = ctx.collection.prev(ctx.highlightedValue) ?? ctx.collection.last() + const value = ctx.collection.prev(ctx.highlightedValue) ?? (ctx.loop ? ctx.collection.last() : null) set.highlightedItem(ctx, value) }, highlightFirstSelectedItem(ctx) { diff --git a/shared/src/data.ts b/shared/src/data.ts index a6c7ee8cfe..30925847c6 100644 --- a/shared/src/data.ts +++ b/shared/src/data.ts @@ -36,7 +36,7 @@ export const comboboxData = [ { label: "Albania", code: "AL" }, { label: "Algeria", code: "DZ" }, { label: "American Samoa", code: "AS" }, - { label: "AndorrA", code: "AD" }, + { label: "Andorra", code: "AD" }, { label: "Angola", code: "AO" }, // { label: "Angola", code: "AO" }, { label: "Anguilla", code: "AI" }, diff --git a/website/components/machines/combobox.tsx b/website/components/machines/combobox.tsx index 8780045788..18f940b2c1 100644 --- a/website/components/machines/combobox.tsx +++ b/website/components/machines/combobox.tsx @@ -22,8 +22,7 @@ const comboboxData = [ { label: "Albania", code: "AL" }, { label: "Algeria", code: "DZ" }, { label: "American Samoa", code: "AS" }, - { label: "AndorrA", code: "AD" }, - { label: "Angola", code: "AO" }, + { label: "Andorra", code: "AD" }, { label: "Angola", code: "AO" }, { label: "Anguilla", code: "AI" }, { label: "Antarctica", code: "AQ" },