From fc372a8214c43464308499588565ebd607e6b7c2 Mon Sep 17 00:00:00 2001 From: Segun Adebayo Date: Sun, 22 Feb 2026 21:57:36 +0000 Subject: [PATCH 1/2] fix: docs tokens --- website/components/code-area.tsx | 1 + website/components/multi-framework.tsx | 2 ++ website/data/snippets/react/number-input/usage.mdx | 3 ++- website/data/snippets/vue/number-input/usage.mdx | 4 ++-- website/panda.config.ts | 2 +- 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/website/components/code-area.tsx b/website/components/code-area.tsx index 34c9c6f76a..66f1cd6411 100644 --- a/website/components/code-area.tsx +++ b/website/components/code-area.tsx @@ -22,6 +22,7 @@ export function CodeArea(props: CodeAreaProps) { margin: "0", padding: "40px 24px !important", height: "full", + borderWidth: "0", }, }} > diff --git a/website/components/multi-framework.tsx b/website/components/multi-framework.tsx index 0a3de541ae..27e431779d 100644 --- a/website/components/multi-framework.tsx +++ b/website/components/multi-framework.tsx @@ -88,9 +88,11 @@ export function MultiframeworkTabs() { css={{ "& #playground": { marginY: "0", + height: "full", }, "& [data-part=root]": { transform: "scale(1.5) translateY(40px)", + marginTop: "-120px", }, }} > diff --git a/website/data/snippets/react/number-input/usage.mdx b/website/data/snippets/react/number-input/usage.mdx index 2ebd3240cb..c9d3cf94d4 100644 --- a/website/data/snippets/react/number-input/usage.mdx +++ b/website/data/snippets/react/number-input/usage.mdx @@ -1,9 +1,10 @@ ```jsx import * as numberInput from "@zag-js/number-input" import { useMachine, normalizeProps } from "@zag-js/react" +import { useId } from "react" export function NumberInput() { - const service = useMachine(numberInput.machine, { id: "1" }) + const service = useMachine(numberInput.machine, { id: useId() }) const api = numberInput.connect(service, normalizeProps) diff --git a/website/data/snippets/vue/number-input/usage.mdx b/website/data/snippets/vue/number-input/usage.mdx index 83862f2ab3..3a5c338f8c 100644 --- a/website/data/snippets/vue/number-input/usage.mdx +++ b/website/data/snippets/vue/number-input/usage.mdx @@ -2,9 +2,9 @@ diff --git a/website/panda.config.ts b/website/panda.config.ts index 0bf55072bd..906de9d50a 100644 --- a/website/panda.config.ts +++ b/website/panda.config.ts @@ -144,7 +144,7 @@ export default defineConfig({ block: { value: { _light: "{colors.gray.50}", - _dark: "{colors.gray.800/20}", + _dark: "{colors.gray.800}", }, }, inline: { From 342b12cfd4674bbe9826e32a370f5a9e8f27af58 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sun, 22 Feb 2026 22:13:24 +0000 Subject: [PATCH 2/2] fix(combobox): return correct items in onValueChange for controlled usage (#2980) * Initial plan * fix: combobox onValueChange returns empty items array with controlled value Co-authored-by: anubra266 <30869823+anubra266@users.noreply.github.com> * test(combobox): add e2e tests for onValueChange items callback Co-authored-by: anubra266 <30869823+anubra266@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: anubra266 <30869823+anubra266@users.noreply.github.com> --- .changeset/fix-combobox-items-on-value-change.md | 5 +++++ e2e/combobox.e2e.ts | 15 +++++++++++++++ e2e/models/combobox.model.ts | 12 ++++++++++++ examples/next-ts/pages/combobox.tsx | 5 +++++ .../machines/combobox/src/combobox.machine.ts | 13 ++++++------- 5 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 .changeset/fix-combobox-items-on-value-change.md diff --git a/.changeset/fix-combobox-items-on-value-change.md b/.changeset/fix-combobox-items-on-value-change.md new file mode 100644 index 0000000000..446dcd48c6 --- /dev/null +++ b/.changeset/fix-combobox-items-on-value-change.md @@ -0,0 +1,5 @@ +--- +"@zag-js/combobox": patch +--- + +Fix `onValueChange` returning empty `items` array when using controlled value diff --git a/e2e/combobox.e2e.ts b/e2e/combobox.e2e.ts index 598bc84c9c..dfd634dcd5 100644 --- a/e2e/combobox.e2e.ts +++ b/e2e/combobox.e2e.ts @@ -198,6 +198,21 @@ test.describe("combobox", () => { await I.seeInputHasValue("") }) + test("[callback] onValueChange should include selected item in items", async () => { + await I.clickTrigger() + await I.clickItem("Zambia") + await I.seeOnValueChangeItems("Zambia") + }) + + test("[callback] onValueChange items should be empty after clearing", async () => { + await I.clickTrigger() + await I.clickItem("Zambia") + await I.seeOnValueChangeItems("Zambia") + await I.clickTrigger() + await I.clickClearTrigger() + await I.seeOnValueChangeItemsIsEmpty() + }) + test("[no value] enter behavior for custom values", async () => { await I.controls.select("inputBehavior", "none") await I.type("foo") diff --git a/e2e/models/combobox.model.ts b/e2e/models/combobox.model.ts index 072dbca4fd..494ac3d892 100644 --- a/e2e/models/combobox.model.ts +++ b/e2e/models/combobox.model.ts @@ -128,4 +128,16 @@ export class ComboboxModel extends Model { dontSeeValueText = async () => { await expect(this.valueText).toHaveText("") } + + get onValueChangeItems() { + return this.page.locator("[data-testid=on-value-change-items]") + } + + seeOnValueChangeItems = async (text: string) => { + await expect(this.onValueChangeItems).toContainText(text) + } + + seeOnValueChangeItemsIsEmpty = async () => { + await expect(this.onValueChangeItems).toHaveText("") + } } diff --git a/examples/next-ts/pages/combobox.tsx b/examples/next-ts/pages/combobox.tsx index 3a0f03449f..73671bd354 100644 --- a/examples/next-ts/pages/combobox.tsx +++ b/examples/next-ts/pages/combobox.tsx @@ -17,6 +17,7 @@ export default function Page() { const controls = useControls(comboboxControls) const [options, setOptions] = useState(comboboxData) + const [selectedItems, setSelectedItems] = useState([]) const collection = combobox.collection({ items: options, @@ -34,6 +35,9 @@ export default function Page() { const filtered = matchSorter(comboboxData, inputValue, { keys: ["label"] }) setOptions(filtered.length > 0 ? filtered : comboboxData) }, + onValueChange({ items }) { + setSelectedItems(items as Item[]) + }, ...controls.context, }) @@ -47,6 +51,7 @@ export default function Page() { +
{selectedItems.map((item) => item.label).join(", ")}

diff --git a/packages/machines/combobox/src/combobox.machine.ts b/packages/machines/combobox/src/combobox.machine.ts index 916447e09c..0579b368fc 100644 --- a/packages/machines/combobox/src/combobox.machine.ts +++ b/packages/machines/combobox/src/combobox.machine.ts @@ -67,15 +67,14 @@ export const machine = createMachine({ const prevSelectedItems = context.get("selectedItems") const collection = prop("collection") - // When controlled, use prop value for selectedItems so they stay in sync when controller ignores selection - const effectiveValue = prop("value") || value + const findItems = (vals: string[]) => + vals.map((v) => prevSelectedItems.find((item) => collection.getItemValue(item) === v) || collection.find(v)) - const nextItems = effectiveValue.map((v) => { - const item = prevSelectedItems.find((item) => collection.getItemValue(item) === v) - return item || collection.find(v) - }) + const nextItems = findItems(value) - context.set("selectedItems", nextItems) + // When controlled, use prop value for selectedItems so they stay in sync when controller ignores selection + const effectiveValue = prop("value") || value + context.set("selectedItems", findItems(effectiveValue)) prop("onValueChange")?.({ value, items: nextItems }) },