diff --git a/src/combobox.js b/src/combobox.js index da0121b..6efe67a 100644 --- a/src/combobox.js +++ b/src/combobox.js @@ -53,6 +53,7 @@ export function comboboxData(config = {}) { isOpen: false, highlighted: -1, placeholder: config.placeholder || '', + _opts: null, // set reactively via setOptions(); see _options getter init() { this.syncQueryFromValue(); @@ -66,8 +67,19 @@ export function comboboxData(config = {}) { } }, + // setOptions lets a parent feed reactive option lists in via x-effect — + // e.g. x-effect="setOptions(vpicMakes)" — which is the reliable way to + // pass a parent component's reactive array into this nested x-data (a + // plain closure over the parent scope would NOT track updates). Falls back + // to config.options when never called (static lists). + setOptions(raw) { + this._opts = normalizeOptions(raw); + // a shrinking list shouldn't leave a stale highlight selected + if (this.highlighted >= this._opts.length) this.highlighted = -1; + }, + get _options() { - return normalizeOptions(config.options); + return this._opts != null ? this._opts : normalizeOptions(config.options); }, syncQueryFromValue() { diff --git a/src/combobox.test.js b/src/combobox.test.js index 2dceb0d..9571557 100644 --- a/src/combobox.test.js +++ b/src/combobox.test.js @@ -54,3 +54,13 @@ test('empty query lists all options (capped) (SEV-392)', () => { c.query = ''; assert.equal(c.filtered.length, 3); }); + +test('setOptions feeds reactive option lists (SEV-392)', () => { + const c = comboboxData({ allowFree: true }); + c.setOptions([]); // async source not loaded yet + assert.equal(c.filtered.length, 0); + c.setOptions(['BMW', 'Tesla']); // makes arrive + assert.equal(c.filtered.length, 2); + c.query = 'tes'; + assert.deepEqual(c.filtered.map((o) => o.value), ['Tesla']); +});