Skip to content

Commit

Permalink
Merge pull request #3947 from Polymer/ready-attached-order
Browse files Browse the repository at this point in the history
Ready/attached ordering
  • Loading branch information
kevinpschaaf authored Sep 16, 2016
2 parents 922e87a + ba3cf58 commit 1701b5c
Show file tree
Hide file tree
Showing 3 changed files with 199 additions and 18 deletions.
52 changes: 34 additions & 18 deletions src/elements/element.html
Original file line number Diff line number Diff line change
Expand Up @@ -188,28 +188,44 @@

ready() {
super.ready();
if (!this.root) {
if (this._template) {
if (this.attachShadow) {
this.root = this.attachShadow({mode: 'open'});
} else {
throw new Error(`ShadowDOM not available. ` +
// BREAKME(sorvell): move to developer conditional when supported.
`Polymer.Element can
create dom as children instead of in ShadowDOM by setting
\`this.root = this;\` before \`ready\`.`);
}
} else {
this.root = this;
}
}
if (this._template) {
hostStack.beginHosting(this);
let dom = this._stampTemplate(this._template);
this.root.appendChild(dom);
this.root = this._stampTemplate(this._template);
this._flushProperties();
this.root = this._attachDom(this.root);
hostStack.endHosting(this);
} else {
this.root = this;
this._flushProperties();
}
}

/**
* Attach an element's stamped dom to itself. By default,
* this method creates a `shadowRoot` and adds the dom to it.
* However, this method may be overridden to allow an element
* to put its dom in another location.
*
* @method _attachDom
* @param {NodeList} dom to attach to the element.
* @returns {Node} node to which the dom has been attached.
*/
_attachDom(dom) {
if (this.attachShadow) {
if (dom) {
if (!this.shadowRoot) {
this.attachShadow({mode: 'open'});
}
this.shadowRoot.appendChild(dom);
return this.shadowRoot;
}
} else {
throw new Error(`ShadowDOM not available. ` +
// BREAKME(sorvell): move to developer conditional when supported.
`Polymer.Element can
create dom as children instead of in ShadowDOM by setting
\`this.root = this;\` before \`ready\`.`);
}
this._flushProperties();
}

attributeChangedCallback(name, old, value) {
Expand Down
1 change: 1 addition & 0 deletions test/runner.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
'unit/template-whitespace.html',
'unit/resolveurl.html',
'unit/case-map.html',
'unit/ready-attached-order.html',
'unit/attributes.html',
'unit/async.html',
'unit/behaviors.html',
Expand Down
164 changes: 164 additions & 0 deletions test/unit/ready-attached-order.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
<!doctype html>
<!--
@license
Copyright (c) 2014 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">
</head>
<body>

<script>
window.clearTestLists = function() {
window.actualAttachedList = [];
window.actualReadyList = [];
window.actualReadyBeforeAtachedList = [];
}

clearTestLists();

window.readyBehavior = {
moniker: function() {
return this.is + (this.id ? '#' + this.id : '');
},
ready: function() {
this._readied = true;
window.actualReadyList.push(this.moniker());
},

attached: function() {
this.attachedTime$Keys = Object.keys(this.$);
window.actualAttachedList.push(this.moniker());
if (!this._readied) {
window.actualReadyBeforeAtachedList.push(this.moniker());
}
}
};
</script>

<dom-module id="x-zot">
<template>
x-zot<slot></slot>
</template>
<script>
HTMLImports.whenReady(function() {
Polymer({
is: 'x-zot',
behaviors: [readyBehavior]
});
});
</script>
</dom-module>

<dom-module id="x-bar">
<template>
<x-zot></x-zot>
</template>
<script>
HTMLImports.whenReady(function() {
Polymer({
is: 'x-bar',
behaviors: [readyBehavior]
});
});
</script>
</dom-module>

<dom-module id="x-foo">
<template>
<x-bar id="bar1"></x-bar>
<x-bar id="bar2"></x-bar>
</template>
<script>
HTMLImports.whenReady(function() {
Polymer({
is: 'x-foo',
behaviors: [readyBehavior]
});
});
</script>
</dom-module>

<dom-module id="x-ready">
<template>
<x-zot id="a">
<x-zot id="b"></x-zot>
<x-zot id="c">
<x-zot id="d"></x-zot>
</x-zot>
</x-zot>
<x-foo id="foo"></x-foo>
</template>
<script>
HTMLImports.whenReady(function() {
Polymer({
is: 'x-ready',
behaviors: [readyBehavior]
});
});
</script>
</dom-module>

<script>

suite('ready and attached ordering', function() {

let el;

setup(function() {
clearTestLists();
el = document.createElement('x-ready');
document.body.appendChild(el);
if (customElements.flush) {
customElements.flush();
}
});

teardown(function() {
document.body.removeChild(el);
});

test('element ready called bottom-up', function() {
assert.deepEqual(window.actualReadyList, ['x-zot#a', 'x-zot#b',
'x-zot#c', 'x-zot#d', 'x-zot', 'x-bar#bar1', 'x-zot', 'x-bar#bar2',
'x-foo#foo', 'x-ready']);
});

test('element attached called top-down', function() {
// on native, top element is last because it kicks off the process.
var expectedAttachedList = ['x-zot#a', 'x-zot#b',
'x-zot#c', 'x-zot#d', 'x-foo#foo', 'x-bar#bar1', 'x-zot', 'x-bar#bar2',
'x-zot'];
// NOTE: attached ordering differs under polyfill because it is
// async and therefore *always* in document order.
if (customElements.flush) {
expectedAttachedList.unshift('x-ready');
} else {
expectedAttachedList.push('x-ready');
}
assert.deepEqual(window.actualAttachedList, expectedAttachedList);
});

test('element attached called after ready', function() {
assert.equal(window.actualReadyBeforeAtachedList.length, 0);
});

test('element has $ references at attached time', function() {
assert.sameMembers(el.attachedTime$Keys, ['a', 'b', 'c', 'd', 'foo']);
assert.sameMembers(el.$.foo.attachedTime$Keys, ['bar1', 'bar2']);
})

});

</script>
</body>
</html>

0 comments on commit 1701b5c

Please sign in to comment.