@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
278 lines (244 loc) • 8.88 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"></script>
<script src="../../../web-component-tester/browser.js"></script>
<link rel="import" href="../../polymer.html">
<body>
<dom-module id="base-el">
<template>
<style>
:host {
display: block;
color: red;
}
code {
color: black;
}
</style>
<!-- dummy nodes with notes be removed by subclass -->
<i i=[[i]]></i>
<i i=[[i]]></i>
<i i=[[i]]></i>
<i i=[[i]]></i>
<i i=[[i]]></i>
<input id="input" value="{{foo::input}}" on-custom="handleCustomEvent">
<span>base element: <code>foo = [[foo]]</code></span>
</template>
<script>
HTMLImports.whenReady(function() {
class BaseEl extends Polymer.Element {
static get is() { return 'base-el'; }
static get properties() {
return { foo: { type: String, value: 5 } };
}
constructor() {
super();
this.handleCustomEvent = sinon.spy();
}
}
customElements.define(BaseEl.is, BaseEl);
window.BaseEl = BaseEl;
});
</script>
</dom-module>
<dom-module id="child-el">
<script>
HTMLImports.whenReady(function() {
class ChildEl extends window.BaseEl {
static get is() { return 'child-el'; }
static get properties() {
return { bar: { type: String, value: 3 } };
}
}
customElements.define(ChildEl.is, ChildEl);
window.ChildEl = ChildEl;
});
</script>
</dom-module>
<dom-module id="grand-child-el">
<script>
HTMLImports.whenReady(function() {
class GrandChildEl extends window.ChildEl {
static get is() { return 'grand-child-el'; }
static get properties() {
return { bar: { type: String, value: 3 } };
}
}
customElements.define(GrandChildEl.is, GrandChildEl);
window.GrandChildEl = GrandChildEl;
});
</script>
</dom-module>
<dom-module id="child-el-with-template">
<template>should ignore this template</template>
<script>
HTMLImports.whenReady(function() {
class ChildElWithTemplate extends window.GrandChildEl {
static get properties() {
return { bar: { type: String, value: 3 } };
}
static get template() {
var template = window.GrandChildEl.template.cloneNode(true);
var div = document.createElement('div');
div.textContent = 'child';
template.content.appendChild(div);
// Move input to the end
template.content.appendChild(template.content.querySelector('#input'));
// Remove all the <i>'s, such that the total number of nodes in
// this template is less than the super
Array.from(template.content.querySelectorAll('i'))
.forEach(i=>i.parentNode.removeChild(i));
return template;
}
}
customElements.define('child-el-with-template', ChildElWithTemplate);
window.ChildElWithTemplate = ChildElWithTemplate;
});
</script>
</dom-module>
<dom-module id="parent-element-template-overriding">
<script>
HTMLImports.whenReady(function() {
class ParentElementWithTemplate extends Polymer.Element {
static get template() {
return Polymer.html`This template should not exist`;
}
}
class ChildWithNoTemplate extends ParentElementWithTemplate {
static get template() {
return null;
}
}
customElements.define('child-with-no-template', ChildWithNoTemplate);
});
</script>
</dom-module>
<test-fixture id="basic">
<template>
<base-el></base-el>
<child-el></child-el>
</template>
</test-fixture>
<test-fixture id="basic-with-attributes">
<template>
<base-el></base-el>
<child-el foo="7" bar="7"></child-el>
</template>
</test-fixture>
<test-fixture id="with-template">
<template>
<base-el></base-el>
<child-el-with-template></child-el-with-template>
</template>
</test-fixture>
<script>
suite('ChildElement extends BaseElement', function() {
test('child has base properties', function() {
var f = fixture('basic');
var child = f[1];
assert.equal(child.foo, 5);
assert.equal(child.bar, 3);
});
test('child can change base properties', function() {
var f = fixture('basic-with-attributes');
var child = f[1];
assert.equal(child.foo, 7);
assert.equal(child.bar, 7);
});
test('child has base template and style', function() {
var f = fixture('basic');
var base = f[0];
var child = f[1];
// Child template is the same as the base template.
assert.equal(child.shadowRoot.childNodes.length, child.shadowRoot.childNodes.length);
for (var i=0; i < child.shadowRoot.childNodes.length; i++) {
var childEl = child.shadowRoot.childNodes[i];
var baseEl = child.shadowRoot.childNodes[i];
assert.equal(childEl.innerHTML, baseEl.innerHTML);
}
// And it's something that we expect.
var code = child.shadowRoot.querySelector('code');
assert.equal(code.innerHTML, 'foo = 5');
// And the base style is the same.
assert.equal(getComputedStyle(base).color, getComputedStyle(child).color);
// Id map works as expected
assert.equal(child.$.input.localName, 'input');
assert.equal(child.$.input.id, 'input');
// 2-way bindings work as expected
child.$.input.value = 'changed';
child.$.input.dispatchEvent(new CustomEvent('input'));
assert.equal(child.foo, 'changed');
// Declarative event listeners work as expected
assert.equal(child.handleCustomEvent.callCount, 0);
child.$.input.dispatchEvent(new CustomEvent('custom'));
assert.equal(child.handleCustomEvent.callCount, 1);
});
test('child with properties has updated base template', function() {
var f = fixture('basic-with-attributes');
var base = f[0];
var child = f[1];
// Child template is not the same as the base template.
assert.notEqual(child.shadowRoot.innerHTML, base.shadowRoot.innerHTML);
// And it's something that we expect.
var code = child.shadowRoot.querySelector('code');
assert.equal(code.innerHTML, 'foo = 7');
});
});
suite('ChildElement extends BaseElement and the template', function() {
test('child has base properties', function() {
var f = fixture('with-template');
var child = f[1];
assert.equal(child.foo, 5);
assert.equal(child.bar, 3);
});
test('child has derived template and style', function() {
var f = fixture('with-template');
var base = f[0];
var child = f[1];
// Child template is not the same as the base template.
assert.notEqual(child.shadowRoot.innerHTML, base.shadowRoot.innerHTML);
// And it's something that we expect.
assert.equal(child.shadowRoot.querySelector('code').innerHTML, 'foo = 5');
assert.equal(child.shadowRoot.querySelector('div').innerHTML, 'child');
// And the base style is the same.
assert.equal(getComputedStyle(base).color, getComputedStyle(child).color);
// Id map works as expected
assert.equal(child.$.input.localName, 'input');
assert.equal(child.$.input.id, 'input');
// 2-way bindings work as expected
child.$.input.value = 'changed';
child.$.input.dispatchEvent(new CustomEvent('input'));
assert.equal(child.foo, 'changed');
// Declarative event listeners work as expected
assert.equal(child.handleCustomEvent.callCount, 0);
child.$.input.dispatchEvent(new CustomEvent('custom'));
assert.equal(child.handleCustomEvent.callCount, 1);
});
});
suite('child overriding a template', function() {
let el;
setup(function() {
el = document.createElement('child-with-no-template');
document.body.appendChild(el);
});
teardown(function() {
document.body.removeChild(el);
});
test('returning null nullifies the parent template', function() {
assert.equal(el.shadowRoot, null);
});
});
</script>
</body>
</html>