From 1d782e35a647a7c9438f13be7e8d866b6efc02f4 Mon Sep 17 00:00:00 2001 From: Olof Bjerke Date: Wed, 23 Nov 2022 10:23:59 +0100 Subject: [PATCH 1/2] Allow custom elements to clear value and checked properties with undefined --- src/diff/index.js | 4 ++- src/diff/props.js | 13 ++++++--- test/browser/render.test.js | 53 +++++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 4 deletions(-) diff --git a/src/diff/index.js b/src/diff/index.js index 242bf84581..315df8fc76 100644 --- a/src/diff/index.js +++ b/src/diff/index.js @@ -332,6 +332,8 @@ function diffElementNodes( let oldProps = oldVNode.props; let newProps = newVNode.props; let nodeType = newVNode.type; + let isCustomElement = ('' + newVNode.type).includes('-'); + let i = 0; // Tracks entering and exiting SVG namespace when descending through the tree. @@ -420,7 +422,7 @@ function diffElementNodes( } } - diffProps(dom, newProps, oldProps, isSvg, isHydrating); + diffProps(dom, newProps, oldProps, isSvg, isHydrating, isCustomElement); // If the new vnode didn't have dangerouslySetInnerHTML, diff its children if (newHtml) { diff --git a/src/diff/props.js b/src/diff/props.js index df5710925a..ed5b8cb75e 100644 --- a/src/diff/props.js +++ b/src/diff/props.js @@ -9,8 +9,16 @@ import options from '../options'; * @param {object} oldProps The old props * @param {boolean} isSvg Whether or not this node is an SVG node * @param {boolean} hydrate Whether or not we are in hydration mode + * @param {boolean} isCustomElement Whether or not we are diffing a custom element. */ -export function diffProps(dom, newProps, oldProps, isSvg, hydrate) { +export function diffProps( + dom, + newProps, + oldProps, + isSvg, + hydrate, + isCustomElement +) { let i; for (i in oldProps) { @@ -24,8 +32,7 @@ export function diffProps(dom, newProps, oldProps, isSvg, hydrate) { (!hydrate || typeof newProps[i] == 'function') && i !== 'children' && i !== 'key' && - i !== 'value' && - i !== 'checked' && + (isCustomElement || (i !== 'value' && i !== 'checked')) && oldProps[i] !== newProps[i] ) { setProperty(dom, i, newProps[i], oldProps[i], isSvg); diff --git a/test/browser/render.test.js b/test/browser/render.test.js index bd084a40b6..d8f2f5a733 100644 --- a/test/browser/render.test.js +++ b/test/browser/render.test.js @@ -489,6 +489,59 @@ describe('render()', () => { expect(scratch.innerHTML).to.equal(''); }); + describe('Custom elements properties', () => { + // Properties set to null/undefined are cleared in the DOM through an empty string. + const clearedPropertyValue = ''; + + class TestComponent extends HTMLElement { + constructor() { + super(); + this.value = {}; + this.checked = undefined; + } + } + customElements.define('test-component', TestComponent); + + it(`should set complex value property`, () => { + const value = { foo: 'bar' }; + render(, scratch); + + expect(scratch.querySelector('test-component').value).to.equal(value); + }); + + it(`should set checked property`, () => { + let initialValue = true; + render(, scratch); + expect(scratch.querySelector('test-component').checked).to.equal(true); + }); + + clearPropertyWith(null); + clearPropertyWith(undefined); + + function clearPropertyWith(clearValue) { + it(`should clear existing value property with ${clearValue}`, () => { + render(, scratch); + + render(, scratch); + + expect(scratch.querySelector('test-component').value).to.equal( + clearedPropertyValue + ); + }); + + it(`should clear existing checked property with ${clearValue}`, () => { + let initialValue = true; + render(, scratch); + + render(, scratch); + + expect(scratch.querySelector('test-component').checked).to.equal( + clearedPropertyValue + ); + }); + } + }); + it('should mask value on password input elements', () => { render(, scratch); expect(scratch.innerHTML).to.equal(''); From 9a3af7e9e6cd95bf06c62ae23c8951b49f132824 Mon Sep 17 00:00:00 2001 From: Olof Bjerke Date: Wed, 23 Nov 2022 10:39:39 +0100 Subject: [PATCH 2/2] Use indexOf since includes is not supported in IE11 --- src/diff/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/diff/index.js b/src/diff/index.js index 315df8fc76..ff03e5f954 100644 --- a/src/diff/index.js +++ b/src/diff/index.js @@ -332,7 +332,7 @@ function diffElementNodes( let oldProps = oldVNode.props; let newProps = newVNode.props; let nodeType = newVNode.type; - let isCustomElement = ('' + newVNode.type).includes('-'); + let isCustomElement = ('' + newVNode.type).indexOf('-') > -1; let i = 0;