Skip to content

Commit 0df1580

Browse files
authored
Only call lit-html render if LitElement subclass implements render (#917)
* Only call lit-html render if LitElement subclass implements render - #712 Introduced a breaking behavior change for situations where `render` is unimplemented, and DOM is added before being connected to the document. * update CHANGELOG * Remove `connectedCallback` in render test
1 parent 672e457 commit 0df1580

File tree

3 files changed

+67
-34
lines changed

3 files changed

+67
-34
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
3232
`LitElement` renders when updates are triggered as a result of rendering ([#549](https://github.com/Polymer/lit-element/issues/549)).
3333
* Properties annotated with the `eventOptions` decorator will now survive property renaming optimizations when used with tsickle and Closure JS Compiler.
3434
* Moved style gathering from `finalize` to `initialize` to be more lazy, and create stylesheets on the first instance initializing [#866](https://github.com/Polymer/lit-element/pull/866).
35+
* Fixed behavior change for components that do not implement `render()` introduced in ([#712](https://github.com/Polymer/lit-element/pull/712)) ([#917](https://github.com/Polymer/lit-element/pull/917))
3536

3637
## [2.2.1] - 2019-07-23
3738
### Changed

src/lit-element.ts

+15-6
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ declare global {
3535

3636
export interface CSSResultArray extends Array<CSSResult|CSSResultArray> {}
3737

38+
/**
39+
* Sentinal value used to avoid calling lit-html's render function when
40+
* subclasses do not implement `render`
41+
*/
42+
const renderNotImplemented = {};
43+
3844
export class LitElement extends UpdatingElement {
3945
/**
4046
* Ensure this class is marked as `finalized` as an optimization ensuring
@@ -204,11 +210,14 @@ export class LitElement extends UpdatingElement {
204210
// before that.
205211
const templateResult = this.render();
206212
super.update(changedProperties);
207-
(this.constructor as typeof LitElement)
208-
.render(
209-
templateResult,
210-
this.renderRoot,
211-
{scopeName: this.localName, eventContext: this});
213+
// If render is not implemented by the component, don't call lit-html render
214+
if (templateResult !== renderNotImplemented) {
215+
(this.constructor as typeof LitElement)
216+
.render(
217+
templateResult,
218+
this.renderRoot,
219+
{scopeName: this.localName, eventContext: this});
220+
}
212221
// When native Shadow DOM is used but adoptedStyles are not supported,
213222
// insert styling after rendering to ensure adoptedStyles have highest
214223
// priority.
@@ -229,6 +238,6 @@ export class LitElement extends UpdatingElement {
229238
* update.
230239
*/
231240
protected render(): unknown {
232-
return undefined;
241+
return renderNotImplemented;
233242
}
234243
}

src/test/lit-element_test.ts

+51-28
Original file line numberDiff line numberDiff line change
@@ -195,39 +195,42 @@ suite('LitElement', () => {
195195
assert.equal(window['litElementVersions'].length, 1);
196196
});
197197

198-
test('event fired during rendering element can trigger an update', async () => {
199-
class E extends LitElement {
200-
connectedCallback() {
201-
super.connectedCallback();
202-
this.dispatchEvent(new CustomEvent('foo', {bubbles: true, detail: 'foo'}));
203-
}
204-
}
205-
customElements.define('x-child-61012', E);
206-
207-
class F extends LitElement {
208-
209-
static get properties() {
210-
return {foo: {type: String}};
211-
}
198+
test(
199+
'event fired during rendering element can trigger an update',
200+
async () => {
201+
class E extends LitElement {
202+
connectedCallback() {
203+
super.connectedCallback();
204+
this.dispatchEvent(
205+
new CustomEvent('foo', {bubbles: true, detail: 'foo'}));
206+
}
207+
}
208+
customElements.define('x-child-61012', E);
212209

213-
foo = '';
210+
class F extends LitElement {
211+
static get properties() {
212+
return {foo: {type: String}};
213+
}
214214

215-
render() {
216-
return html`<x-child-61012 @foo=${this._handleFoo}></x-child-61012><span>${this.foo}</span>`;
217-
}
215+
foo = '';
218216

219-
_handleFoo(e: CustomEvent) {
220-
this.foo = e.detail;
221-
}
217+
render() {
218+
return html`<x-child-61012 @foo=${
219+
this._handleFoo}></x-child-61012><span>${this.foo}</span>`;
220+
}
222221

223-
}
222+
_handleFoo(e: CustomEvent) {
223+
this.foo = e.detail;
224+
}
225+
}
224226

225-
customElements.define(generateElementName(), F);
226-
const el = new F();
227-
container.appendChild(el);
228-
while (!(await el.updateComplete)) {}
229-
assert.equal(el.shadowRoot!.textContent, 'foo');
230-
});
227+
customElements.define(generateElementName(), F);
228+
const el = new F();
229+
container.appendChild(el);
230+
while (!(await el.updateComplete)) {
231+
}
232+
assert.equal(el.shadowRoot!.textContent, 'foo');
233+
});
231234

232235
test(
233236
'exceptions in `render` throw but do not prevent further updates',
@@ -266,4 +269,24 @@ suite('LitElement', () => {
266269
assert.equal(a.foo, 20);
267270
assert.equal(a.shadowRoot!.textContent, '20');
268271
});
272+
273+
test(
274+
'if `render` is unimplemented, do not overwrite renderRoot', async () => {
275+
class A extends LitElement {
276+
addedDom: HTMLElement|null = null;
277+
createRenderRoot() {
278+
return this;
279+
}
280+
}
281+
customElements.define(generateElementName(), A);
282+
const a = new A();
283+
const testDom = document.createElement('div');
284+
a.appendChild(testDom);
285+
container.appendChild(a);
286+
await a.updateComplete;
287+
assert.equal(
288+
testDom.parentNode,
289+
a,
290+
'testDom should be a child of the component');
291+
});
269292
});

0 commit comments

Comments
 (0)