Skip to content

Commit

Permalink
Merge pull request #78 from PolymerLabs/template-subclass
Browse files Browse the repository at this point in the history
Template subclass
  • Loading branch information
Steve Orvell authored Aug 22, 2016
2 parents 746b7ef + b47ebdc commit ed01601
Show file tree
Hide file tree
Showing 5 changed files with 250 additions and 24 deletions.
26 changes: 15 additions & 11 deletions src/elements/element.html
Original file line number Diff line number Diff line change
Expand Up @@ -115,13 +115,8 @@
this.createProperties(config.properties);
this.createMethodObservers(config.observers);
if (this.template) {
var template = this.prepareTemplate(this.template);
// TODO(dfreedm): remove when style stuff is refactored
// away from using element as input.
proto.__notStyleScopeCacheable = template.__notStyleScopeCacheable;
this.data.clearPropagateEffects(proto);
this.data.bindTemplate(proto, template);
proto._template = template;
var template = this.template.cloneNode(true);
this.finalizeTemplate(template);
}
}
}
Expand All @@ -135,12 +130,16 @@
// TODO(sorvell): support more ways to acquire template.
// this requires `is` on constructor...
this._template = Polymer.DomModule.import(this.is,
'template', this.__importDoc || document);
'template', this.__importDoc || document) ||
// note: implemented so a subclass can retrieve the super
// template; call the super impl this way so that `this` points
// to the superclass.
Object.getPrototypeOf(this.prototype).constructor.template;
}
return this._template;
}

static prepareTemplate(template) {
static finalizeTemplate(template) {
// TODO(sorvell): remove need for this...
Polymer.CompatStyleUtil.normalizeForBC(template.content);
// TODO(sorvell): cannot use `this` here, refactor this to only do
Expand All @@ -155,8 +154,13 @@
__placeholder: this.__placeholder,
}
Polymer.StyleLib.prepareTemplate(info, template);
template.__notStyleScopeCacheable = info.__notStyleScopeCacheable;
return template;
// TODO(dfreedm): remove when style stuff is refactored
// away from using element as input.
var proto = this.prototype;
proto.__notStyleScopeCacheable = info.__notStyleScopeCacheable;
this.data.clearPropagateEffects(proto);
this.data.bindTemplate(proto, template);
proto._template = template;
}

constructor() {
Expand Down
18 changes: 11 additions & 7 deletions src/shady/element-mixin.html
Original file line number Diff line number Diff line change
Expand Up @@ -548,15 +548,19 @@
},

cloneNode: function(deep) {
var n = nativeCloneNode.call(this, false);
if (deep) {
var c$ = this.childNodes;
for (var i=0, nc; i < c$.length; i++) {
nc = c$[i].cloneNode(true);
n.appendChild(nc);
if (this.localName == 'template') {
return nativeCloneNode.call(this, deep);
} else {
var n = nativeCloneNode.call(this, false);
if (deep) {
var c$ = this.childNodes;
for (var i=0, nc; i < c$.length; i++) {
nc = c$[i].cloneNode(true);
n.appendChild(nc);
}
}
return n;
}
return n;
},

importNode: function(externalNode, deep) {
Expand Down
1 change: 1 addition & 0 deletions test/runner.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
'unit/polymer.element.html',
'unit/polymer.compatelement.html',
'unit/debounce.html',
'unit/inheritance.html',
'unit/path.html',
'unit/dom-repeat.html',
'unit/dom-if.html'
Expand Down
36 changes: 30 additions & 6 deletions test/smoke/polymer.element.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,17 @@
<body>
<dom-module id="my-core-element">
<template>
{{name}}: {{foo}}
<style>
.happy {
background: green;
}
</style>
<div class="happy">{{name}}: {{foo}}</div>
</template>
<script>
class MyElement extends Polymer.Element {

// TODO(sorvell): naive static getter has a natural allocation
// TODO(sorvell): naive static getter has a natural allocation
// penalty v. post definition 'set' (e.g. MyElement.meta = {});
static get config() {
return {
Expand All @@ -44,7 +49,7 @@

// TODO(sorvell): `is` is required on class only if we want to support
// auto-magic template inheritance. Template can be auto-detected
// using localName unless inheriting from a superclass that has not
// using localName unless inheriting from a superclass that has not
// yet been created. In that case, we need this data here.
// Alternatively, there could be a more explicit way to determine
// template.
Expand Down Expand Up @@ -79,6 +84,17 @@
}

customElements.define(MyElement.is, MyElement);
</script>

<template id="extra">
<style>
.happier {
background: tomato;
}
</style>
<div class="happier">{{name}}: {{sub}}</div>
</template>
<script>

function MyMixin(Base) {
return class extends Base {
Expand Down Expand Up @@ -115,13 +131,15 @@
_observate(mixin, sub, nug) {
console.log('_observate', mixin, sub, nug);
}

}

}

class MySubClass extends MyMixin(MyElement) {

//class MySubClass extends MyElement {

static get config() {
return {
properties: {
Expand All @@ -137,6 +155,12 @@
return 'my-sub';
}

static finalizeTemplate(template) {
var t = document.querySelector('#extra');
template.content.appendChild(t.content.cloneNode(true));
super.finalizeTemplate(template);
}

_mixinHandler() {
super._mixinHandler();
console.log('_mixinHandler', this.localName);
Expand All @@ -155,6 +179,6 @@
<my-core-element></my-core-element>
<hr>
<my-sub></my-sub>

</body>
</html>
193 changes: 193 additions & 0 deletions test/unit/inheritance.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
<!doctype html>
<!--
@license
Copyright (c) 2016 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>
<span>base element: <code>foo = [[foo]]</code></span>
</template>

<script>
class BaseEl extends Polymer.Element {
static get is() { return 'base-el' }
static get config() {
return {
properties: { foo: { type: String, value: 5 } }
}
}
}
customElements.define(BaseEl.is, BaseEl);
</script>
</dom-module>

<dom-module id="child-el">
<script>
class ChildEl extends BaseEl {
static get is() { return 'child-el' }
static get config() {
return {
properties: { bar: { type: String, value: 3 } }
}
}
}
customElements.define(ChildEl.is, ChildEl);
</script>
</dom-module>

<dom-module id="grand-child-el">
<script>
class GrandChildEl extends ChildEl {
static get is() { return 'grand-child-el' }
static get config() {
return {
properties: { bar: { type: String, value: 3 } }
}
}
}
customElements.define(GrandChildEl.is, GrandChildEl);
</script>
</dom-module>

<dom-module id="child-el-with-template">
<script>
class ChildElWithTemplate extends GrandChildEl {
static get is() { return 'child-el-with-template' }
static get config() {
return {
properties: { bar: { type: String, value: 3 } }
}
}
static finalizeTemplate(template) {
var div = document.createElement('div');
div.textContent = 'child';
template.content.appendChild(div);
super.finalizeTemplate(template);
}
}
customElements.define(ChildElWithTemplate.is, ChildElWithTemplate);
</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);
});

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);
});
});
</script>
</body>
</html>

0 comments on commit ed01601

Please sign in to comment.