From aa9fe11a1f3fcad224426883d6c6ea715609ae07 Mon Sep 17 00:00:00 2001 From: Gavin Joyce Date: Sat, 20 Dec 2025 11:23:59 +0000 Subject: [PATCH] Fix Combobox click-outside behavior for non-primitive values Fixes #188 - Combobox now correctly preserves selected values when clicking outside after selecting a non-primitive option. Root cause: In handleOptionClick, setSelectedOption was called before onChange, so this.args.value hadn't been updated yet. Changes: - Swap order in -option.js: call onChange first, then setSelectedOption - Set inputValue in setSelectedOption so input immediately reflects selection - Add test from PR #189 for non-primitive values with displayValue --- ember-headlessui/addon/components/combobox.js | 3 +- .../addon/components/combobox/-option.js | 2 +- .../integration/components/combobox-test.js | 48 +++++++++++++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/ember-headlessui/addon/components/combobox.js b/ember-headlessui/addon/components/combobox.js index 3f6543db..345f2aae 100644 --- a/ember-headlessui/addon/components/combobox.js +++ b/ember-headlessui/addon/components/combobox.js @@ -435,7 +435,8 @@ export default class ComboboxComponent extends Component { this.inputElement?.focus(); } - this._originalValue = this.inputValue; + this._originalValue = this.args.value; + this.inputValue = this.args.value; } @action diff --git a/ember-headlessui/addon/components/combobox/-option.js b/ember-headlessui/addon/components/combobox/-option.js index ece806a1..19f2127a 100644 --- a/ember-headlessui/addon/components/combobox/-option.js +++ b/ember-headlessui/addon/components/combobox/-option.js @@ -34,8 +34,8 @@ export default class ComboboxOptionComponent extends Component { if (this.args.disabled) return; - this.args.setSelectedOption(this, e); this.callOnChangeWithSelectedValue(); + this.args.setSelectedOption(this, e); } @action diff --git a/test-app/tests/integration/components/combobox-test.js b/test-app/tests/integration/components/combobox-test.js index 8b43ca29..6e43c35c 100644 --- a/test-app/tests/integration/components/combobox-test.js +++ b/test-app/tests/integration/components/combobox-test.js @@ -237,6 +237,54 @@ module('Integration | Component | ', function (hooks) { assert.dom(getComboboxInput()).hasValue('B'); }); + test('selecting an option puts the display value into Combobox.Input when displayValue is provided and values are objects', async function (assert) { + this.set('onChange', (value) => { + this.set('value', value); + }); + + this.setProperties({ + value: null, + a: { value: 'a' }, + b: { value: 'b' }, + c: { value: 'c' }, + displayValue: (option) => { + return option?.value?.toUpperCase() || 'None'; + }, + }); + + await render(hbs` + + + Trigger + + Option A + Option B + Option C + + + `); + + await click(getComboboxButton()); + assertComboboxList({ state: ComboboxState.Visible }); + + await click(getComboboxOptions()[1]); + assert.dom(getComboboxInput()).hasValue('B'); + + await click(getComboboxButton()); + assertComboboxList({ state: ComboboxState.Visible }); + + assert.dom(getComboboxInput()).hasValue('B'); + + await click(document.body); + assertComboboxList({ state: ComboboxState.InvisibleUnmounted }); + + assert.dom(getComboboxInput()).hasValue('B'); + }); + test('opening and closing the combobox should not trigger spurious onChange events on the input', async function (assert) { let inputOnChangeCallCount = 0; let inputOnChangeValues = [];