diff --git a/compat/test/browser/events.test.js b/compat/test/browser/events.test.js index b7a9d1d7fe..d59dbef8ed 100644 --- a/compat/test/browser/events.test.js +++ b/compat/test/browser/events.test.js @@ -2,7 +2,8 @@ import { render } from 'preact'; import { setupScratch, teardown, - createEvent + createEvent, + supportsPassiveEvents } from '../../../test/_util/helpers'; import React, { createElement } from 'preact/compat'; @@ -309,4 +310,24 @@ describe('preact/compat events', () => { scratch.firstChild.dispatchEvent(createEvent('focusout')); expect(spy).to.be.calledOnce; }); + + if (supportsPassiveEvents()) { + it('should use capturing for event props ending with *Capture', () => { + let click = sinon.spy(); + + render( +
+ +
, + scratch + ); + + expect(proto.addEventListener).to.have.been.calledOnce; + expect(proto.addEventListener).to.have.been.calledWithExactly( + 'touchmove', + sinon.match.func, + true + ); + }); + } }); diff --git a/hooks/src/index.js b/hooks/src/index.js index eadbd86c20..92eb1e113b 100644 --- a/hooks/src/index.js +++ b/hooks/src/index.js @@ -25,6 +25,7 @@ let oldBeforeRender = options._render; let oldAfterDiff = options.diffed; let oldCommit = options._commit; let oldBeforeUnmount = options.unmount; +let oldRoot = options._root; const RAF_TIMEOUT = 100; let prevRaf; @@ -35,6 +36,14 @@ options._diff = vnode => { if (oldBeforeDiff) oldBeforeDiff(vnode); }; +options._root = (vnode, parentDom) => { + if (parentDom._children && parentDom._children._mask) { + vnode._mask = parentDom._children._mask; + } + + if (oldRoot) oldRoot(vnode, parentDom); +}; + /** @type {(vnode: import('./internal').VNode) => void} */ options._render = vnode => { if (oldBeforeRender) oldBeforeRender(vnode); diff --git a/hooks/test/browser/useId.test.js b/hooks/test/browser/useId.test.js index e7fc7f947b..fe2546fd1a 100644 --- a/hooks/test/browser/useId.test.js +++ b/hooks/test/browser/useId.test.js @@ -433,4 +433,27 @@ describe('useId', () => { rerender(); expect(first).not.to.equal(scratch.innerHTML); }); + + it('should return a unique id across invocations of render', () => { + const Id = () => { + const id = useId(); + return
My id is {id}
; + }; + + const App = props => { + return ( +
+ + {props.secondId ? : null} +
+ ); + }; + + render(createElement(App, { secondId: false }), scratch); + expect(scratch.innerHTML).to.equal('
My id is P0-0
'); + render(createElement(App, { secondId: true }), scratch); + expect(scratch.innerHTML).to.equal( + '
My id is P0-0
My id is P0-1
' + ); + }); }); diff --git a/src/diff/children.js b/src/diff/children.js index 369014af39..8256e45d74 100644 --- a/src/diff/children.js +++ b/src/diff/children.js @@ -367,7 +367,11 @@ function insert(parentVNode, oldDom, parentDom) { oldDom = parentVNode._dom; } - return oldDom && oldDom.nextSibling; + do { + oldDom = oldDom && oldDom.nextSibling; + } while (oldDom != null && oldDom.nodeType === 8); + + return oldDom; } /** diff --git a/src/diff/props.js b/src/diff/props.js index 6421330bd0..3ec7e91141 100644 --- a/src/diff/props.js +++ b/src/diff/props.js @@ -52,7 +52,7 @@ export function setProperty(dom, name, value, oldValue, isSvg) { // Benchmark for comparison: https://esbench.com/bench/574c954bdb965b9a00965ac6 else if (name[0] === 'o' && name[1] === 'n') { useCapture = - name !== (name = name.replace(/(PointerCapture)$|Capture$/, '$1')); + name !== (name = name.replace(/(PointerCapture)$|Capture$/i, '$1')); // Infer correct casing for DOM built-in events: if (name.toLowerCase() in dom) name = name.toLowerCase().slice(2); diff --git a/test/browser/hydrate.test.js b/test/browser/hydrate.test.js index 0b23c6f12c..b03c0e2a66 100644 --- a/test/browser/hydrate.test.js +++ b/test/browser/hydrate.test.js @@ -50,11 +50,11 @@ describe('hydrate()', () => { beforeEach(() => { scratch = setupScratch(); attributesSpy = spyOnElementAttributes(); + clearLog(); }); afterEach(() => { teardown(scratch); - clearLog(); }); it('should reuse existing DOM', () => { @@ -92,6 +92,7 @@ describe('hydrate()', () => { scratch ); expect(scratch.innerHTML).to.equal('

01

'); + expect(getLog()).to.deep.equal(['Comment.remove()']); }); it('should reuse existing DOM when given components', () => { @@ -458,5 +459,13 @@ describe('hydrate()', () => { scratch.innerHTML = '

hello foo

'; hydrate(

hello {'foo'}

, scratch); expect(scratch.innerHTML).to.equal('

hello foo

'); + expect(getLog()).to.deep.equal(['Comment.remove()']); + }); + + it('should skip over multiple comment nodes', () => { + scratch.innerHTML = '

hello foo

'; + hydrate(

hello {'foo'}

, scratch); + expect(scratch.innerHTML).to.equal('

hello foo

'); + expect(getLog()).to.deep.equal(['Comment.remove()', 'Comment.remove()']); }); });