Skip to content

Commit

Permalink
Only call lit-html render if LitElement subclass implements render
Browse files Browse the repository at this point in the history
- #712 Introduced a breaking behavior change for situations where
  `render` is unimplemented, and DOM is added before being connected to
  the document.
  • Loading branch information
dfreedm committed Mar 6, 2020
1 parent 672e457 commit 5709aed
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 34 deletions.
21 changes: 15 additions & 6 deletions src/lit-element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ declare global {

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

/**
* Sentinal value used to avoid calling lit-html's render function when
* subclasses do not implement `render`
*/
const renderNotImplemented = {};

export class LitElement extends UpdatingElement {
/**
* Ensure this class is marked as `finalized` as an optimization ensuring
Expand Down Expand Up @@ -204,11 +210,14 @@ export class LitElement extends UpdatingElement {
// before that.
const templateResult = this.render();
super.update(changedProperties);
(this.constructor as typeof LitElement)
.render(
templateResult,
this.renderRoot,
{scopeName: this.localName, eventContext: this});
// If render is not implemented by the component, don't call lit-html render
if (templateResult !== renderNotImplemented) {
(this.constructor as typeof LitElement)
.render(
templateResult,
this.renderRoot,
{scopeName: this.localName, eventContext: this});
}
// When native Shadow DOM is used but adoptedStyles are not supported,
// insert styling after rendering to ensure adoptedStyles have highest
// priority.
Expand All @@ -229,6 +238,6 @@ export class LitElement extends UpdatingElement {
* update.
*/
protected render(): unknown {
return undefined;
return renderNotImplemented;
}
}
87 changes: 59 additions & 28 deletions src/test/lit-element_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,39 +195,42 @@ suite('LitElement', () => {
assert.equal(window['litElementVersions'].length, 1);
});

test('event fired during rendering element can trigger an update', async () => {
class E extends LitElement {
connectedCallback() {
super.connectedCallback();
this.dispatchEvent(new CustomEvent('foo', {bubbles: true, detail: 'foo'}));
}
}
customElements.define('x-child-61012', E);

class F extends LitElement {

static get properties() {
return {foo: {type: String}};
}
test(
'event fired during rendering element can trigger an update',
async () => {
class E extends LitElement {
connectedCallback() {
super.connectedCallback();
this.dispatchEvent(
new CustomEvent('foo', {bubbles: true, detail: 'foo'}));
}
}
customElements.define('x-child-61012', E);

foo = '';
class F extends LitElement {
static get properties() {
return {foo: {type: String}};
}

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

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

}
_handleFoo(e: CustomEvent) {
this.foo = e.detail;
}
}

customElements.define(generateElementName(), F);
const el = new F();
container.appendChild(el);
while (!(await el.updateComplete)) {}
assert.equal(el.shadowRoot!.textContent, 'foo');
});
customElements.define(generateElementName(), F);
const el = new F();
container.appendChild(el);
while (!(await el.updateComplete)) {
}
assert.equal(el.shadowRoot!.textContent, 'foo');
});

test(
'exceptions in `render` throw but do not prevent further updates',
Expand Down Expand Up @@ -266,4 +269,32 @@ suite('LitElement', () => {
assert.equal(a.foo, 20);
assert.equal(a.shadowRoot!.textContent, '20');
});

test(
'if `render` is unimplemented, do not overwrite renderRoot', async () => {
class A extends LitElement {
addedDom: HTMLElement|null = null;
createRenderRoot() {
return this;
}
connectedCallback() {
super.connectedCallback();
this.addedDom = this.renderRoot.querySelector('div');
}
}
customElements.define(generateElementName(), A);
const a = new A();
const testDom = document.createElement('div');
a.appendChild(testDom);
container.appendChild(a);
await a.updateComplete;
assert.equal(
a.addedDom,
testDom,
'testDom should be found in connectedCallback');
assert.equal(
testDom.parentNode,
a,
'testDom should be a child of the component');
});
});

0 comments on commit 5709aed

Please sign in to comment.