ソースを参照

Bug - Morph text nodes. (#2524)

* Add failing test

* Fix diffing when a tag contains text nodes mixed to dom elements

* Use nodes() when looping on node children to get the next node to process
Simone Todaro 3 年 前
コミット
977c76e706

+ 21 - 21
packages/morph/src/morph.js

@@ -6,23 +6,23 @@ function breakpoint(message) {
     if (! debug) return
     if (! debug) return
 
 
     message && logger(message.replace('\n', '\\n'))
     message && logger(message.replace('\n', '\\n'))
-   
+
     return new Promise(resolve => resolveStep = () => resolve())
     return new Promise(resolve => resolveStep = () => resolve())
 }
 }
 
 
 export async function morph(from, toHtml, options) {
 export async function morph(from, toHtml, options) {
     assignOptions(options)
     assignOptions(options)
-    
+
     let toEl = createElement(toHtml)
     let toEl = createElement(toHtml)
 
 
     // If there is no x-data on the element we're morphing,
     // If there is no x-data on the element we're morphing,
     // let's seed it with the outer Alpine scope on the page.
     // let's seed it with the outer Alpine scope on the page.
     if (window.Alpine && ! from._x_dataStack) {
     if (window.Alpine && ! from._x_dataStack) {
         toEl._x_dataStack = window.Alpine.closestDataStack(from)
         toEl._x_dataStack = window.Alpine.closestDataStack(from)
-        
+
         toEl._x_dataStack && window.Alpine.clone(from, toEl)
         toEl._x_dataStack && window.Alpine.clone(from, toEl)
     }
     }
-    
+
     await breakpoint()
     await breakpoint()
 
 
     patch(from, toEl)
     patch(from, toEl)
@@ -57,7 +57,7 @@ function assignOptions(options = {}) {
     adding = options.adding || noop
     adding = options.adding || noop
     added = options.added || noop
     added = options.added || noop
     key = options.key || defaultGetKey
     key = options.key || defaultGetKey
-    lookahead = options.lookahead || true 
+    lookahead = options.lookahead || true
     debug = options.debug || false
     debug = options.debug || false
 }
 }
 
 
@@ -71,12 +71,12 @@ async function patch(from, to) {
     // don't see a way to enable it currently:
     // don't see a way to enable it currently:
     //
     //
     // if (from.isEqualNode(to)) return
     // if (from.isEqualNode(to)) return
-   
+
     if (differentElementNamesTypesOrKeys(from, to)) {
     if (differentElementNamesTypesOrKeys(from, to)) {
         let result = patchElement(from, to)
         let result = patchElement(from, to)
-        
+
         await breakpoint('Swap elements')
         await breakpoint('Swap elements')
-       
+
         return result
         return result
     }
     }
 
 
@@ -89,7 +89,7 @@ async function patch(from, to) {
     if (textOrComment(to)) {
     if (textOrComment(to)) {
         await patchNodeValue(from, to)
         await patchNodeValue(from, to)
         updated(from, to)
         updated(from, to)
-        
+
         return
         return
     }
     }
 
 
@@ -152,7 +152,7 @@ async function patchAttributes(from, to) {
 
 
         if (! to.hasAttribute(name)) {
         if (! to.hasAttribute(name)) {
             from.removeAttribute(name)
             from.removeAttribute(name)
-           
+
             await breakpoint('Remove attribute')
             await breakpoint('Remove attribute')
         }
         }
     }
     }
@@ -212,7 +212,7 @@ async function patchChildren(from, to) {
                 currentFrom = addNodeBefore(currentTo, currentFrom)
                 currentFrom = addNodeBefore(currentTo, currentFrom)
 
 
                 domKey = getKey(currentFrom)
                 domKey = getKey(currentFrom)
-                
+
                 await breakpoint('Move element (lookahead)')
                 await breakpoint('Move element (lookahead)')
             }
             }
         }
         }
@@ -222,8 +222,8 @@ async function patchChildren(from, to) {
                 domKeyHoldovers[domKey] = currentFrom
                 domKeyHoldovers[domKey] = currentFrom
                 currentFrom = addNodeBefore(currentTo, currentFrom)
                 currentFrom = addNodeBefore(currentTo, currentFrom)
                 domKeyHoldovers[domKey].remove()
                 domKeyHoldovers[domKey].remove()
-                currentFrom = dom(currentFrom).nodes.next()
-                currentTo = dom(currentTo).nodes.next()
+                currentFrom = dom(currentFrom).nodes().next()
+                currentTo = dom(currentTo).nodes().next()
 
 
                 await breakpoint('No "to" key')
                 await breakpoint('No "to" key')
 
 
@@ -233,7 +233,7 @@ async function patchChildren(from, to) {
             if (toKey && ! domKey) {
             if (toKey && ! domKey) {
                 if (domKeyDomNodeMap[toKey]) {
                 if (domKeyDomNodeMap[toKey]) {
                     currentFrom = dom(currentFrom).replace(domKeyDomNodeMap[toKey])
                     currentFrom = dom(currentFrom).replace(domKeyDomNodeMap[toKey])
-                    
+
                     await breakpoint('No "from" key')
                     await breakpoint('No "from" key')
                 }
                 }
             }
             }
@@ -244,7 +244,7 @@ async function patchChildren(from, to) {
 
 
                 if (domKeyNode) {
                 if (domKeyNode) {
                     currentFrom = dom(currentFrom).replace(domKeyNode)
                     currentFrom = dom(currentFrom).replace(domKeyNode)
-                    
+
                     await breakpoint('Move "from" key')
                     await breakpoint('Move "from" key')
                 } else {
                 } else {
                     domKeyHoldovers[domKey] = currentFrom
                     domKeyHoldovers[domKey] = currentFrom
@@ -252,9 +252,9 @@ async function patchChildren(from, to) {
                     domKeyHoldovers[domKey].remove()
                     domKeyHoldovers[domKey].remove()
                     currentFrom = dom(currentFrom).next()
                     currentFrom = dom(currentFrom).next()
                     currentTo = dom(currentTo).next()
                     currentTo = dom(currentTo).next()
-                   
+
                     await breakpoint('I dont even know what this does')
                     await breakpoint('I dont even know what this does')
-                    
+
                     continue
                     continue
                 }
                 }
             }
             }
@@ -263,8 +263,8 @@ async function patchChildren(from, to) {
         // Patch elements
         // Patch elements
         await patch(currentFrom, currentTo)
         await patch(currentFrom, currentTo)
 
 
-        currentTo = currentTo && dom(currentTo).next()
-        currentFrom = currentFrom && dom(currentFrom).next()
+        currentTo = currentTo && dom(currentTo).nodes().next()
+        currentFrom = currentFrom && dom(currentFrom).nodes().next()
     }
     }
 
 
     // Cleanup extra froms
     // Cleanup extra froms
@@ -367,7 +367,7 @@ class DomManager {
         this.traversals = {
         this.traversals = {
             'first': 'firstChild',
             'first': 'firstChild',
             'next': 'nextSibling',
             'next': 'nextSibling',
-            'parent': 'parentNode', 
+            'parent': 'parentNode',
         }; return this
         }; return this
     }
     }
 
 
@@ -386,7 +386,7 @@ class DomManager {
     replace(replacement) {
     replace(replacement) {
         this.el[this.traversals['parent']].replaceChild(replacement, this.el); return replacement
         this.el[this.traversals['parent']].replaceChild(replacement, this.el); return replacement
     }
     }
-    
+
     append(appendee) {
     append(appendee) {
         this.el.appendChild(appendee); return appendee
         this.el.appendChild(appendee); return appendee
     }
     }

+ 10 - 1
tests/cypress/integration/plugins/morph.spec.js

@@ -1,4 +1,4 @@
-import { haveText, html, test } from '../../utils'
+import { haveText, haveHtml, html, test } from '../../utils'
 
 
 test('can morph components and preserve Alpine state',
 test('can morph components and preserve Alpine state',
     [html`
     [html`
@@ -133,3 +133,12 @@ test('can morph teleports',
         get('h2').should(haveText('there'))
         get('h2').should(haveText('there'))
     },
     },
 )
 )
+
+test('can morph text nodes',
+    [html`<h2>Foo <br> Bar</h2>`],
+    ({ get }, reload, window, document) => {
+        let toHtml = html`<h2>Foo <br> Baz</h2>`
+        get('h2').then(([el]) => window.Alpine.morph(el, toHtml))
+        get('h2').should(haveHtml('Foo <br> Baz'))
+    },
+)

+ 2 - 0
tests/cypress/utils.js

@@ -95,6 +95,8 @@ export let haveText = text => el => expect(el).to.have.text(text)
 
 
 export let notHaveText = text => el => expect(el).not.to.have.text(text)
 export let notHaveText = text => el => expect(el).not.to.have.text(text)
 
 
+export let haveHtml = html => el => expect(el).to.have.html(html)
+
 export let beChecked = () => el => expect(el).to.be.checked
 export let beChecked = () => el => expect(el).to.be.checked
 
 
 export let notBeChecked = () => el => expect(el).not.to.be.checked
 export let notBeChecked = () => el => expect(el).not.to.be.checked