diff --git a/lib/legacy/polymer.dom.js b/lib/legacy/polymer.dom.js index 442d428147..6d7f0eb667 100644 --- a/lib/legacy/polymer.dom.js +++ b/lib/legacy/polymer.dom.js @@ -395,6 +395,12 @@ if (window['ShadyDOM'] && window['ShadyDOM']['inUse'] && window['ShadyDOM']['noP } }); + // Note, `classList` is here only for legacy compatibility since it does not + // trigger distribution in v1 Shadow DOM. + forwardReadOnlyProperties(Wrapper.prototype, [ + 'classList' + ]); + DomApiImpl = Wrapper; Object.defineProperties(EventApi.prototype, { @@ -416,12 +422,17 @@ if (window['ShadyDOM'] && window['ShadyDOM']['inUse'] && window['ShadyDOM']['noP } else { + // Methods that can provoke distribution or must return the logical, not + // composed tree. forwardMethods(DomApiNative.prototype, [ 'cloneNode', 'appendChild', 'insertBefore', 'removeChild', 'replaceChild', 'setAttribute', 'removeAttribute', 'querySelector', 'querySelectorAll' ]); + // Properties that should return the logical, not composed tree. Note, `classList` + // is here only for legacy compatibility since it does not trigger distribution + // in v1 Shadow DOM. forwardReadOnlyProperties(DomApiNative.prototype, [ 'parentNode', 'firstChild', 'lastChild', 'nextSibling', 'previousSibling', 'firstElementChild', diff --git a/test/unit/polymer-dom-nopatch.html b/test/unit/polymer-dom-nopatch.html index 54017de804..6a7e697110 100644 --- a/test/unit/polymer-dom-nopatch.html +++ b/test/unit/polymer-dom-nopatch.html @@ -355,6 +355,144 @@ assert.equal(useNativeCSSProperties, Boolean(!window.ShadyCSS || window.ShadyCSS.nativeCss)); }); +}); + +suite('forwarded native api', function() { + + let el; + + setup(function() { + el = document.createElement('x-container-slot'); + document.body.appendChild(el); + }); + + teardown(function() { + document.body.removeChild(el); + }); + + test('accessors are available', function() { + const d = dom(el); + assert.isDefined(d.parentNode); + assert.isDefined(d.firstChild); + assert.isDefined(d.lastChild); + assert.isDefined(d.nextSibling); + assert.isDefined(d.previousSibling); + assert.isDefined(d.firstElementChild); + assert.isDefined(d.lastElementChild); + assert.isDefined(d.nextElementSibling); + assert.isDefined(d.previousElementSibling); + assert.isDefined(d.childNodes); + assert.isDefined(d.children); + assert.isDefined(d.classList); + assert.isDefined(d.textContent); + assert.isDefined(d.innerHTML); + }); + + test('cloneNode', function() { + const clone = dom(el).cloneNode(el); + assert.ok(clone); + assert.equal(clone.localName, 'x-container-slot'); + }); + + test('appendChild', function() { + const d1 = document.createElement('div'); + dom(el).appendChild(d1); + assert.equal(dom(el).firstChild, d1); + assert.equal(dom(d1).parentNode, el); + }); + + test('insertBefore', function() { + const d1 = document.createElement('div'); + const d2 = document.createElement('div'); + dom(el).appendChild(d1); + dom(el).insertBefore(d2, d1); + assert.equal(dom(d2).nextSibling, d1); + }); + + test('removeChild', function() { + const d1 = document.createElement('div'); + dom(el).appendChild(d1); + dom(el).removeChild(d1); + assert.equal(dom(d1).parentNode, null); + }); + + test('replaceChild', function() { + const d1 = document.createElement('div'); + const d2 = document.createElement('div'); + dom(el).appendChild(d1); + dom(el).replaceChild(d2, d1); + assert.equal(dom(d1).parentNode, null); + assert.equal(dom(el).firstChild, d2); + assert.equal(dom(d2).parentNode, el); + }); + + test('replaceChild', function() { + dom(el).setAttribute('foo', 'foo'); + assert.equal(el.getAttribute('foo'), 'foo'); + }); + + test('replaceChild', function() { + dom(el).setAttribute('foo', 'foo'); + dom(el).removeAttribute('foo'); + assert.isFalse(el.hasAttribute('foo')); + }); + + test('querySelector', function() { + const d1 = document.createElement('div'); + dom(el).appendChild(d1); + const query = dom(el).querySelector('div'); + assert.equal(query, d1); + }); + + test('querySelectorAll', function() { + const d1 = document.createElement('div'); + const d2 = document.createElement('div'); + dom(el).appendChild(d1); + dom(el).appendChild(d2); + const query = dom(el).querySelectorAll('div'); + assert.equal(query[0], d1); + assert.equal(query[1], d2); + assert.equal(query.length, 2); + }); + + test('tree accessors', function() { + const d1 = document.createElement('div'); + const d2 = document.createElement('div'); + dom(el).appendChild(d1); + dom(el).appendChild(d2); + const pel = dom(el); + const pd1 = dom(d1); + const pd2 = dom(d2); + assert.equal(pd1.parentNode, el); + assert.equal(pel.firstChild, d1); + assert.equal(pel.lastChild, d2); + assert.equal(pel.firstElementChild, d1); + assert.equal(pel.lastElementChild, d2); + assert.equal(pd1.nextSibling, d2); + assert.equal(pd2.previousSibling, d1); + assert.equal(pd1.nextElementSibling, d2); + assert.equal(pd2.previousElementSibling, d1); + assert.equal(pel.childNodes[0], d1); + assert.equal(pel.childNodes[1], d2); + assert.equal(pel.children[0], d1); + assert.equal(pel.children[1], d2); + }); + + test('innerHTML', function() { + dom(el).innerHTML = '
'; + assert.equal(dom(el).firstChild.localName, 'div'); + }); + + test('textContent', function() { + dom(el).innerHTML = 'hi'; + assert.equal(dom(dom(el).firstChild).textContent, 'hi'); + }); + + test('classList', function() { + dom(el).classList.add('foo'); + assert.equal(el.className, 'foo'); + }); + }); diff --git a/test/unit/polymer-dom.html b/test/unit/polymer-dom.html index 8bea7e1c1f..30cc025654 100644 --- a/test/unit/polymer-dom.html +++ b/test/unit/polymer-dom.html @@ -342,6 +342,144 @@ assert.equal(useNativeCSSProperties, Boolean(!window.ShadyCSS || window.ShadyCSS.nativeCss)); }); +}); + +suite('forwarded native api', function() { + + let el; + + setup(function() { + el = document.createElement('x-container-slot'); + document.body.appendChild(el); + }); + + teardown(function() { + document.body.removeChild(el); + }); + + test('accessors are available', function() { + const d = dom(el); + assert.isDefined(d.parentNode); + assert.isDefined(d.firstChild); + assert.isDefined(d.lastChild); + assert.isDefined(d.nextSibling); + assert.isDefined(d.previousSibling); + assert.isDefined(d.firstElementChild); + assert.isDefined(d.lastElementChild); + assert.isDefined(d.nextElementSibling); + assert.isDefined(d.previousElementSibling); + assert.isDefined(d.childNodes); + assert.isDefined(d.children); + assert.isDefined(d.classList); + assert.isDefined(d.textContent); + assert.isDefined(d.innerHTML); + }); + + test('cloneNode', function() { + const clone = dom(el).cloneNode(el); + assert.ok(clone); + assert.equal(clone.localName, 'x-container-slot'); + }); + + test('appendChild', function() { + const d1 = document.createElement('div'); + dom(el).appendChild(d1); + assert.equal(dom(el).firstChild, d1); + assert.equal(dom(d1).parentNode, el); + }); + + test('insertBefore', function() { + const d1 = document.createElement('div'); + const d2 = document.createElement('div'); + dom(el).appendChild(d1); + dom(el).insertBefore(d2, d1); + assert.equal(dom(d2).nextSibling, d1); + }); + + test('removeChild', function() { + const d1 = document.createElement('div'); + dom(el).appendChild(d1); + dom(el).removeChild(d1); + assert.equal(dom(d1).parentNode, null); + }); + + test('replaceChild', function() { + const d1 = document.createElement('div'); + const d2 = document.createElement('div'); + dom(el).appendChild(d1); + dom(el).replaceChild(d2, d1); + assert.equal(dom(d1).parentNode, null); + assert.equal(dom(el).firstChild, d2); + assert.equal(dom(d2).parentNode, el); + }); + + test('replaceChild', function() { + dom(el).setAttribute('foo', 'foo'); + assert.equal(el.getAttribute('foo'), 'foo'); + }); + + test('replaceChild', function() { + dom(el).setAttribute('foo', 'foo'); + dom(el).removeAttribute('foo'); + assert.isFalse(el.hasAttribute('foo')); + }); + + test('querySelector', function() { + const d1 = document.createElement('div'); + dom(el).appendChild(d1); + const query = dom(el).querySelector('div'); + assert.equal(query, d1); + }); + + test('querySelectorAll', function() { + const d1 = document.createElement('div'); + const d2 = document.createElement('div'); + dom(el).appendChild(d1); + dom(el).appendChild(d2); + const query = dom(el).querySelectorAll('div'); + assert.equal(query[0], d1); + assert.equal(query[1], d2); + assert.equal(query.length, 2); + }); + + test('tree accessors', function() { + const d1 = document.createElement('div'); + const d2 = document.createElement('div'); + dom(el).appendChild(d1); + dom(el).appendChild(d2); + const pel = dom(el); + const pd1 = dom(d1); + const pd2 = dom(d2); + assert.equal(pd1.parentNode, el); + assert.equal(pel.firstChild, d1); + assert.equal(pel.lastChild, d2); + assert.equal(pel.firstElementChild, d1); + assert.equal(pel.lastElementChild, d2); + assert.equal(pd1.nextSibling, d2); + assert.equal(pd2.previousSibling, d1); + assert.equal(pd1.nextElementSibling, d2); + assert.equal(pd2.previousElementSibling, d1); + assert.equal(pel.childNodes[0], d1); + assert.equal(pel.childNodes[1], d2); + assert.equal(pel.children[0], d1); + assert.equal(pel.children[1], d2); + }); + + test('innerHTML', function() { + dom(el).innerHTML = ''; + assert.equal(dom(el).firstChild.localName, 'div'); + }); + + test('textContent', function() { + dom(el).innerHTML = 'hi'; + assert.equal(dom(dom(el).firstChild).textContent, 'hi'); + }); + + test('classList', function() { + dom(el).classList.add('foo'); + assert.equal(el.className, 'foo'); + }); + });