@polymer/polymer
Version:
The Polymer library makes it easy to create your own web components. Give your element some markup and properties, and then use it on a site. Polymer provides features like dynamic templates and data binding to reduce the amount of boilerplate you need to
485 lines (405 loc) • 16.2 kB
HTML
<!--
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<html>
<head>
<meta charset="utf-8">
<script src="../../../webcomponentsjs/webcomponents-lite.js"
register="true" shadydom="true"></script>
<script src="../../../web-component-tester/browser.js"></script>
<link rel="import" href="../../polymer.html">
</head>
<body>
<template></template>
<script>
// TODO(sorvell): cannot register element in main document under polyfill.
HTMLImports.whenReady(function() {
var template = document.querySelector('template');
Polymer({
is: 'x-content-test',
_template: template
});
});
/*
* Test the `<slot>` element distribution algorithm by verifying the
* resulting composed tree structure.
*/
function testRender(descr, hostInnerHtml, shadowRootHtml, expectedHtml) {
test(descr, function() {
// Create an instance of the test element.
var host = document.createElement('x-content-test');
// Populate the initial pool of light DOM children.
host.innerHTML = hostInnerHtml;
document.body.appendChild(host);
// Pretend we're stamping the template contents.
if (shadowRootHtml) {
host.shadowRoot.innerHTML = shadowRootHtml;
}
// Invoke distribution and verify the resulting tree.
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), expectedHtml);
document.body.removeChild(host);
});
test(descr + ' fragment', function() {
// Create an instance of the test element.
var host = document.createElement('x-content-test');
// Populate the initial pool of light DOM children.
host.innerHTML = hostInnerHtml;
document.body.appendChild(host);
// Pretend we're stamping the template contents.
var div = document.createElement('div');
div.innerHTML = shadowRootHtml;
var frag = document.createDocumentFragment();
while (div.firstChild) {
frag.appendChild(div.firstChild);
}
host.shadowRoot.appendChild(frag);
// Invoke distribution and verify the resulting tree.
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), expectedHtml);
document.body.removeChild(host);
});
}
testRender('Empty shadow', 'abc', '', '');
testRender('Simple shadow', 'abc', 'def', 'def');
testRender('Fallback shadow', 'abc',
'<slot name="xxx">fallback</name>', 'fallback');
testRender('Fallback shadow, empty host', '',
'<slot name="xxx">fallback</name>', 'fallback');
testRender('Content', 'abc',
'<slot>fallback</slot>', 'abc');
testRender('Content before', 'abc',
'before<slot>fallback</slot>', 'beforeabc');
testRender('Content after', 'abc',
'<slot>fallback</slot>after', 'abcafter');
suite('render content', function() {
testRender('no select', '<a href="">Link</a> <b>bold</b>',
'<slot></slot>',
'<a href="">Link</a> <b>bold</b>');
testRender('select ""', '<a href="">Link</a> <b>bold</b>',
'<slot></slot>',
'<a href="">Link</a> <b>bold</b>');
testRender('slot a',
'<a slot="a">a</a> <a slot="b">b</a>',
'<slot name="a"></slot>',
'<a slot="a">a</a>');
testRender('slot b a',
'<a slot="a">a</a> <a slot="b">b</a>',
'<slot name="b"></slot><slot name="a"></slot>',
'<a slot="b">b</a><a slot="a">a</a>');
testRender('slot b a 2',
'<a slot="a">a</a> <a slot="b">b</a> <a slot="b">c</a>',
'<slot name="b"></slot><slot name="a"></slot>',
'<a slot="b">b</a><a slot="b">c</a><a slot="a">a</a>');
testRender('slot c catchall',
'<span>a</span><span>b</span><span slot="c">c</span><span>d</span>',
'<slot name="c"></slot><slot></slot>',
'<span slot="c">c</span><span>a</span><span>b</span><span>d</span>');
});
test('Reproject', function() {
var host = document.createElement('x-content-test');
document.body.appendChild(host);
host.innerHTML = '<a></a>';
// pre-distirbution grab first composed node.
var a = getComposedChildAtIndex(host, 0);
host.shadowRoot.innerHTML = '<x-content-test id="p"></x-content-test>';
// force upgrade on polyfilled browsers
var p = host.shadowRoot.firstChild;
p.innerHTML = '<b slot="b"></b><slot slot="a"></slot>';
var b = p.firstChild;
var content = p.lastChild;
p.shadowRoot.innerHTML =
'a: <slot name="a"></slot>b: <slot name="b"></slot>';
var textNodeA = p.shadowRoot.firstChild;
var contentA = p.shadowRoot.childNodes[1];
var textNodeB = p.shadowRoot.childNodes[2];
var contentB = p.shadowRoot.childNodes[3];
function testRender() {
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host),
'<x-content-test id="p">a: <a></a>b: <b slot="b"></b></x-content-test>');
assertArrayEqual(host.childNodes, [a]);
assert.strictEqual(a.parentNode, host);
assertArrayEqual(host.shadowRoot.childNodes, [p]);
assert.strictEqual(p.parentNode, host.shadowRoot);
assertArrayEqual(p.childNodes, [b, content]);
assert.strictEqual(b.parentNode, p);
assert.strictEqual(content.parentNode, p);
assertArrayEqual(p.shadowRoot.childNodes,
[textNodeA, contentA, textNodeB, contentB]);
}
testRender();
testRender();
document.body.removeChild(host);
});
suite('Mutate light DOM', function() {
var host;
setup(function() {
host = document.createElement('x-content-test');
document.body.appendChild(host);
});
teardown(function() {
document.body.removeChild(host);
});
test('removeAllChildNodes - mutate host', function() {
host.innerHTML = '<a>Hello</a>';
host.shadowRoot.innerHTML = '<slot>fallback</slot>';
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), '<a>Hello</a>');
host.firstChild.textContent = '';
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), '<a></a>');
host.innerHTML = '';
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), 'fallback');
});
test('removeAllChildNodes - mutate shadow', function() {
host.innerHTML = '<a>Hello</a>';
host.shadowRoot.innerHTML = '<slot></slot><b>after</b>';
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), '<a>Hello</a><b>after</b>');
host.shadowRoot.childNodes[1].textContent = '';
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), '<a>Hello</a><b></b>');
host.shadowRoot.innerHTML = '';
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), '');
});
test('removeAllChildNodes - mutate shadow fallback', function() {
host.innerHTML = '<a>Hello</a>';
host.shadowRoot.innerHTML = '<slot name="xxx"><b>fallback</b></slot>';
var b = host.shadowRoot.firstChild.firstChild;
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), '<b>fallback</b>');
b.textContent = '';
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), '<b></b>');
host.shadowRoot.firstChild.innerHTML = '';
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), '');
host.shadowRoot.innerHTML = '';
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), '');
});
test('removeChild - mutate host', function() {
host.innerHTML = '<a>Hello</a>';
host.shadowRoot.innerHTML = '<slot>fallback</slot>';
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), '<a>Hello</a>');
host.firstChild.removeChild(host.firstChild.firstChild);
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), '<a></a>');
host.innerHTML = '';
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), 'fallback');
});
test('removeChild - mutate host 2', function() {
host.innerHTML = '<a></a><b></b>';
var a = getComposedChildAtIndex(host, 0);
var b = getComposedChildAtIndex(host, 1);
host.shadowRoot.innerHTML = '<slot>fallback</slot>';
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), '<a></a><b></b>');
host.removeChild(b);
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), '<a></a>');
host.removeChild(a);
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), 'fallback');
});
test('removeChild - mutate shadow', function() {
host.innerHTML = '<a>Hello</a>';
host.shadowRoot.innerHTML = '<slot></slot><b>after</b>';
var b = host.shadowRoot.lastChild;
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), '<a>Hello</a><b>after</b>');
b.removeChild(b.firstChild);
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), '<a>Hello</a><b></b>');
host.shadowRoot.removeChild(b);
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), '<a>Hello</a>');
host.shadowRoot.removeChild(host.shadowRoot.firstChild);
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), '');
});
test('setAttribute slot', function() {
host.innerHTML = '<a slot="a">Hello</a><b slot="b">World</b>';
host.shadowRoot.innerHTML = '<slot name="b">fallback b</slot>' +
'<slot name="a">fallback a</slot>';
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), '<b slot="b">World</b><a slot="a">Hello</a>');
host.shadowRoot.firstChild.setAttribute('name', 'xxx');
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), 'fallback b<a slot="a">Hello</a>');
host.shadowRoot.firstChild.setAttribute('name', '');
host.shadowRoot.lastChild.setAttribute('name', '');
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), 'fallback bfallback a');
});
test('appendChild - mutate host', function() {
host.innerHTML = '<a>Hello</a>';
host.shadowRoot.innerHTML = '<slot></slot>';
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), '<a>Hello</a>');
var b = document.createElement('b');
host.appendChild(b);
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), '<a>Hello</a><b></b>');
});
test('appendChild - mutate shadow', function() {
host.innerHTML = '<a>Hello</a>';
host.shadowRoot.innerHTML = '<slot></slot>';
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), '<a>Hello</a>');
var b = document.createElement('b');
host.shadowRoot.appendChild(b);
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), '<a>Hello</a><b></b>');
});
test('insertBefore - mutate host', function() {
host.innerHTML = '<a>Hello</a>';
var a = getComposedChildAtIndex(host, 0);
host.shadowRoot.innerHTML = '<slot></slot>';
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), '<a>Hello</a>');
var b = document.createElement('b');
host.insertBefore(b, a);
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), '<b></b><a>Hello</a>');
});
test('insertBefore - mutate shadow', function() {
host.innerHTML = '<a>Hello</a>';
host.shadowRoot.innerHTML = '<slot></slot>';
var content = host.shadowRoot.firstChild;
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), '<a>Hello</a>');
var b = document.createElement('b');
host.shadowRoot.insertBefore(b, content);
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), '<b></b><a>Hello</a>');
});
test('replaceChild - mutate host', function() {
host.innerHTML = '<a>Hello</a>';
host.shadowRoot.innerHTML = '<slot></slot>';
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), '<a>Hello</a>');
var b = document.createElement('b');
host.replaceChild(b, host.firstChild);
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), '<b></b>');
});
test('replaceChild - mutate shadow', function() {
host.innerHTML = '<a>Hello</a>';
host.shadowRoot.innerHTML = '<slot></slot>';
var content = host.shadowRoot.firstChild;
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), '<a>Hello</a>');
var b = document.createElement('b');
host.shadowRoot.replaceChild(b, content);
ShadyDOM.flush();
assert.strictEqual(getComposedHTML(host), '<b></b>');
});
test('querySelectorAll (shadowRoot)', function() {
host.innerHTML = '<div id="main"></div>';
host.shadowRoot.innerHTML = '<slot></slot><span id="main"></span>' +
'<x-content-test></x-content-test>';
// force upgrade on polyfilled browsers
ShadyDOM.flush();
var hostLocalMain = getComposedChildAtIndex(host.shadowRoot, 1);
var child = getComposedChildAtIndex(host.shadowRoot, 100);
child.innerHTML = '<div id="sub"></div>';
var childLightSub = getComposedChildAtIndex(child, 0);
child.shadowRoot.innerHTML = '<slot></slot><span id="sub"></span>';
var childLocalSub = child.shadowRoot.lastChild;
ShadyDOM.flush();
assert.deepEqual(host.root.querySelectorAll('span#main'), [hostLocalMain]);
assert.deepEqual(host.root.querySelectorAll('div#sub'), [childLightSub]);
assert.deepEqual(child.root.querySelectorAll('span#sub'), [childLocalSub]);
});
test('querySelectorAll (light dom)', function() {
host.innerHTML = '<div id="main"></div>';
var hostLightMain = getComposedChildAtIndex(host, 0);
host.shadowRoot.innerHTML = '<slot></slot><span id="main"></span>' +
'<x-content-test></x-content-test>';
var child = getComposedChildAtIndex(host.shadowRoot, 100);
child.innerHTML = '<div id="sub"></div>';
var childLightSub = getComposedChildAtIndex(child, 100) ;
child.shadowRoot.innerHTML = '<slot></slot><span id="sub"></span>';
ShadyDOM.flush();
assert.deepEqual(host.querySelectorAll('div#main'), [hostLightMain]);
assert.deepEqual(host.querySelectorAll('#sub'), []);
assert.deepEqual(child.querySelectorAll('div#sub'), [childLightSub]);
});
});
suite('Add shadowRoot to element with childNodes', function() {
var el;
setup(function() {
el = document.createElement('div');
el.testHTML = '<div slot="a">hi</div>';
el.innerHTML = el.testHTML;
el.attachShadow({mode: 'open'});
});
test('empty root', function() {
ShadyDOM.flush();
assert.equal(getComposedHTML(el), '');
});
test('simple root', function() {
var shadow = '<div>shadow</div>';
el.shadowRoot.innerHTML = shadow;
ShadyDOM.flush();
assert.equal(getComposedHTML(el), shadow);
});
test('slot matching', function() {
var shadow = '<div><slot name="a"></slot></div>';
el.shadowRoot.innerHTML = shadow;
ShadyDOM.flush();
assert.equal(getComposedHTML(el), '<div>' + el.testHTML + '</div>');
});
test('slot not matching', function() {
var shadow = '<div><slot name="b"></slot></div>';
el.shadowRoot.innerHTML = shadow;
ShadyDOM.flush();
assert.equal(getComposedHTML(el), '<div></div>');
});
});
function getEffectiveChildNodes(node) {
var list = [];
var c$ = node.childNodes;
for (var i=0, l=c$.length, c; (i<l) && (c=c$[i]); i++) {
if (c.localName && (c.localName === 'content' || c.localName === 'slot')) {
list = list.concat(c.assignedNodes({flatten: true}));
} else {
list.push(c);
}
}
return list;
}
function getComposedHTML(node) {
// Ignore shady CSS scoping
return ShadyDOM.nativeTree.innerHTML(node).replace(/ class="[^"]*"/g, '');
}
function getComposedChildAtIndex(node, index) {
var c$ = getEffectiveChildNodes(node);
if (c$) {
index = index || 0;
index = Math.max(0, Math.min(index, c$.length-1));
return c$[index];
}
}
function assertArrayEqual(a, b, msg) {
assert.equal(a.length, b.length, msg);
for (var i = 0; i < a.length; i++) {
assert.equal(a[i], b[i], msg);
}
}
</script>
</body>
</html>