diff --git a/packages/ui/src/combobox.js b/packages/ui/src/combobox.js index 7229a97a6..708ac1a4a 100644 --- a/packages/ui/src/combobox.js +++ b/packages/ui/src/combobox.js @@ -43,19 +43,23 @@ export default function (Alpine) { Alpine.magic('comboboxOption', el => { let data = Alpine.$data(el) - let optionEl = Alpine.findClosest(el, i => i.__optionKey) + // It's not great depending on the existance of the attribute in the DOM + // but it's probably the fastest and most reliable at this point... + let optionEl = Alpine.findClosest(el, i => { + return i.hasAttribute('x-combobox:option') + }) if (! optionEl) throw 'No x-combobox:option directive found...' return { get isActive() { - return data.__context.isActiveKey(optionEl.__optionKey) + return data.__context.isActiveKey(Alpine.$data(optionEl).__optionKey) }, get isSelected() { return data.__isSelected(optionEl) }, get isDisabled() { - return data.__context.isDisabled(optionEl.__optionKey) + return data.__context.isDisabled(Alpine.$data(optionEl).__optionKey) }, } }) @@ -453,18 +457,20 @@ function handleOption(el, Alpine) { // Initialize... 'x-data'() { return { + '__optionKey': null, + init() { - let key = this.$el.__optionKey = (Math.random() + 1).toString(36).substring(7) + this.__optionKey = (Math.random() + 1).toString(36).substring(7) let value = Alpine.extractProp(this.$el, 'value') let disabled = Alpine.extractProp(this.$el, 'disabled', false, false) // memoize the context as it's not going to change // and calling this.$data on mouse action is expensive - this.__context.registerItem(key, this.$el, value, disabled) + this.__context.registerItem(this.__optionKey, this.$el, value, disabled) }, destroy() { - this.__context.unregisterItem(this.$el.__optionKey) + this.__context.unregisterItem(this.__optionKey) } } }, @@ -498,7 +504,6 @@ function handleOption(el, Alpine) { }) } - // Little utility to defer a callback into the microtask queue... function microtask(callback) { return new Promise(resolve => queueMicrotask(() => resolve(callback()))) diff --git a/packages/ui/src/listbox.js b/packages/ui/src/listbox.js index 73375d398..97e0eb048 100644 --- a/packages/ui/src/listbox.js +++ b/packages/ui/src/listbox.js @@ -48,19 +48,23 @@ export default function (Alpine) { Alpine.magic('listboxOption', (el) => { let data = Alpine.$data(el) - let optionEl = Alpine.findClosest(el, i => i.__optionKey) + // It's not great depending on the existance of the attribute in the DOM + // but it's probably the fastest and most reliable at this point... + let optionEl = Alpine.findClosest(el, i => { + return i.hasAttribute('x-listbox:option') + }) if (! optionEl) throw 'No x-listbox:option directive found...' return { get isActive() { - return data.__context.isActiveKey(optionEl.__optionKey) + return data.__context.isActiveKey(Alpine.$data(optionEl).__optionKey) }, get isSelected() { return data.__isSelected(optionEl) }, get isDisabled() { - return data.__context.isDisabled(optionEl.__optionKey) + return data.__context.isDisabled(Alpine.$data(optionEl).__optionKey) }, } }) @@ -346,16 +350,18 @@ function handleOption(el, Alpine) { // Initialize... 'x-data'() { return { + '__optionKey': null, + init() { - let key = el.__optionKey = (Math.random() + 1).toString(36).substring(7) + this.__optionKey = (Math.random() + 1).toString(36).substring(7) let value = Alpine.extractProp(el, 'value') let disabled = Alpine.extractProp(el, 'disabled', false, false) - this.$data.__context.registerItem(key, el, value, disabled) + this.$data.__context.registerItem(this.__optionKey, el, value, disabled) }, destroy() { - this.$data.__context.unregisterItem(this.$el.__optionKey) + this.$data.__context.unregisterItem(this.__optionKey) }, } }, diff --git a/tests/cypress/integration/plugins/ui/combobox.spec.js b/tests/cypress/integration/plugins/ui/combobox.spec.js index 794fab15e..17a08a485 100644 --- a/tests/cypress/integration/plugins/ui/combobox.spec.js +++ b/tests/cypress/integration/plugins/ui/combobox.spec.js @@ -1,4 +1,4 @@ -import { beVisible, beHidden, haveAttribute, haveClasses, notHaveClasses, haveText, contain, notContain, html, notBeVisible, notHaveAttribute, notExist, haveFocus, test, haveValue, haveLength} from '../../../utils' +import { beVisible, beHidden, haveAttribute, haveClasses, notHaveClasses, haveText, contain, notContain, html, notBeVisible, notHaveAttribute, notExist, haveFocus, test, haveValue, haveLength, ensureNoConsoleWarns} from '../../../utils' test('it works with x-model', [html` @@ -1630,3 +1630,39 @@ test('can remove an option without other options getting removed', get('[check="3"]').should(notBeVisible()) }, ); + +test('works with morph', + [html` +