diff --git a/README.md b/README.md index 4eba19018..8af2480c0 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Stay here for contribution-related information. > Looking for V2 docs? [here they are](https://github.com/alpinejs/alpine/tree/v2.8.2) -

Alpine Compoenent Patterns

+

Alpine Component Patterns

## Contribution Guide: diff --git a/packages/alpinejs/src/directives/x-for.js b/packages/alpinejs/src/directives/x-for.js index 052133696..12ed0f0ad 100644 --- a/packages/alpinejs/src/directives/x-for.js +++ b/packages/alpinejs/src/directives/x-for.js @@ -158,6 +158,8 @@ function loop(el, iteratorNames, evaluateItems, evaluateKey) { let marker = document.createElement('div') mutateDom(() => { + if (! elForSpot) warn(`x-for ":key" is undefined or invalid`, templateEl) + elForSpot.after(marker) elInSpot.after(elForSpot) elForSpot._x_currentIfEl && elForSpot.after(elForSpot._x_currentIfEl) diff --git a/packages/alpinejs/src/directives/x-model.js b/packages/alpinejs/src/directives/x-model.js index c18950af2..0a8debcc3 100644 --- a/packages/alpinejs/src/directives/x-model.js +++ b/packages/alpinejs/src/directives/x-model.js @@ -46,10 +46,6 @@ directive('model', (el, { modifiers, expression }, { effect, cleanup }) => { }) } } - - if (modifiers.includes('fill') && el.hasAttribute('value') && (getValue() === null || getValue() === '')) { - setValue(el.value) - } if (typeof expression === 'string' && el.type === 'radio') { // Radio buttons only work properly when they share a name attribute. @@ -73,7 +69,10 @@ directive('model', (el, { modifiers, expression }, { effect, cleanup }) => { let removeListener = isCloning ? () => {} : on(el, event, modifiers, (e) => { setValue(getInputValue(el, modifiers, e, getValue())) }) - + + if (modifiers.includes('fill') && [null, ''].includes(getValue())) { + el.dispatchEvent(new Event(event, {})); + } // Register the listener removal callback on the element, so that // in addition to the cleanup function, x-modelable may call it. // Also, make this a keyed object if we decide to reintroduce @@ -134,9 +133,9 @@ function getInputValue(el, modifiers, event, currentValue) { // Check for event.detail due to an issue where IE11 handles other events as a CustomEvent. // Safari autofill triggers event as CustomEvent and assigns value to target // so we return event.target.value instead of event.detail - if (event instanceof CustomEvent && event.detail !== undefined) { - return typeof event.detail != 'undefined' ? event.detail : event.target.value - } else if (el.type === 'checkbox') { + if (event instanceof CustomEvent && event.detail !== undefined) + return event.detail ?? event.target.value + else if (el.type === 'checkbox') { // If the data we are binding to is an array, toggle its value inside the array. if (Array.isArray(currentValue)) { let newValue = modifiers.includes('number') ? safeParseNumber(event.target.value) : event.target.value diff --git a/packages/alpinejs/src/directives/x-transition.js b/packages/alpinejs/src/directives/x-transition.js index f81933e35..7838ee956 100644 --- a/packages/alpinejs/src/directives/x-transition.js +++ b/packages/alpinejs/src/directives/x-transition.js @@ -7,8 +7,8 @@ import { once } from '../utils/once' directive('transition', (el, { value, modifiers, expression }, { evaluate }) => { if (typeof expression === 'function') expression = evaluate(expression) - - if (! expression) { + if (expression === false) return + if (!expression || typeof expression === 'boolean') { registerTransitionsFromHelper(el, modifiers, value) } else { registerTransitionsFromClassString(el, expression, value) diff --git a/packages/alpinejs/src/lifecycle.js b/packages/alpinejs/src/lifecycle.js index 7a6f93551..6a62a61ab 100644 --- a/packages/alpinejs/src/lifecycle.js +++ b/packages/alpinejs/src/lifecycle.js @@ -4,7 +4,13 @@ import { dispatch } from './utils/dispatch' import { walk } from "./utils/walk" import { warn } from './utils/warn' +let started = false + export function start() { + if (started) warn('Alpine has already been initialized on this page. Calling Alpine.start() more than once can cause problems.') + + started = true + if (! document.body) warn('Unable to initialize. Trying to load Alpine before `` is available. Did you forget to add `defer` in Alpine\'s ` +
+ + thing +
+ `, + ({ get }) => + { + get('span').should(beVisible()) + get('span').should(beVisible()) + } + +); \ No newline at end of file diff --git a/tests/cypress/utils.js b/tests/cypress/utils.js index ef126a3a4..2c1a2f7cd 100644 --- a/tests/cypress/utils.js +++ b/tests/cypress/utils.js @@ -87,7 +87,7 @@ function injectHtmlAndBootAlpine(cy, templateAndPotentiallyScripts, callback, pa }) } -export let haveData = (key, value) => ([ el ]) => expect(root(el)._x_dataStack[0][key]).to.equal(value) +export let haveData = (key, value) => ([el]) => expect(root(el)._x_dataStack[0][key]).to.deep.equal(value); export let haveFocus = () => el => expect(el).to.have.focus @@ -97,6 +97,8 @@ export let haveAttribute = (name, value) => el => expect(el).to.have.attr(name, export let notHaveAttribute = (name, value) => el => expect(el).not.to.have.attr(name, value) +export let haveProperty = (name, value) => el => expect(el).to.have.prop(name, value) + export let haveText = text => el => expect(el).to.have.text(text) export let notHaveText = text => el => expect(el).not.to.have.text(text)