Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions src/BaseElement.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,18 @@ class BaseElement extends HTMLElement {
}
});

// release dom references
this.$refs = {};

// clear state
this._state = {};

// cancel pending updates
if (this._batchUpdate) {
cancelAnimationFrame(this._batchUpdate);
this._requestedUpdates = [];
}

this.triggerHook('disconnected');
}

Expand Down Expand Up @@ -297,16 +309,16 @@ class BaseElement extends HTMLElement {
* Will trigger update() when a property was changed
* @param {string} property
* @param {any} value
* @param {boolean} reflectAttribute
* @param {boolean} isAttribute
*/
defineProperty(property, value, reflectAttribute = false) {
defineProperty(property, value, isAttribute = false) {
if (this._state.hasOwnProperty(property)) {
// property has already been defined as an attribute nothing to do here
return;
}

// if property did not come from an attribute but has the option to reflect // enabled or custom fn
if (!reflectAttribute && this._options.propertyOptions[property]?.reflect) {
if (!isAttribute && this._options.propertyOptions[property]?.reflect) {
this.reflectProperty({ property: property, newValue: value });
}

Expand All @@ -325,6 +337,10 @@ class BaseElement extends HTMLElement {
this._state[property].subscribe(this);
}

if (Object.getOwnPropertyDescriptor(this, property)) {
// instance already has a defined property
return;
}
Object.defineProperty(this, property, {
get: () => {
return this._state[property];
Expand All @@ -341,7 +357,7 @@ class BaseElement extends HTMLElement {
newValue.subscribe(this);
}

if (reflectAttribute || this._options.propertyOptions[property]?.reflect) {
if (isAttribute || this._options.propertyOptions[property]?.reflect) {
this.reflectProperty({ property, newValue, newValueString });
}

Expand Down
76 changes: 76 additions & 0 deletions test/unit/vanilla-renderer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,37 @@ class OnePropTag extends TemplateElement {
}
}
customElements.define('one-prop-tag', OnePropTag);

class NestedElementTag extends TemplateElement {
properties() {
return {
label: 'initial',
};
}

template() {
return html`<button>${this.label}</button>`;
}
}

customElements.define('nested-element-tag', NestedElementTag);

class ArrayRenderingNestedElementsTag extends TemplateElement {
properties() {
return {
list: [1],
};
}

template() {
return html` <div>
${this.list.map((index) => html`<nested-element-tag label="${'label' + index}"></nested-element-tag>`)}
</div>`;
}
}

customElements.define('array-rendering-nested-elements-tag', ArrayRenderingNestedElementsTag);

describe(`template rendering`, () => {
it('renders template in light dom by default', async () => {
const el = await fixture(`<${lightTag}></${lightTag}>`);
Expand Down Expand Up @@ -369,4 +400,49 @@ describe(`vanilla-renderer`, () => {
await nextFrame();
assert.equal(stripCommentMarkers(arrayElement.innerHTML), '<div>1</div><div>2</div><div>3</div>');
});

it('Renders a list of nested template elements', async () => {
const arrayElement = await fixture(
`<array-rendering-nested-elements-tag></array-rendering-nested-elements-tag>`,
);
await nextFrame();

assert.equal(arrayElement.innerText, 'label1');
arrayElement.list = [2, 3, 4];

await nextFrame();
await nextFrame();
assert.equal(arrayElement.innerText, 'label2 label3 label4');
arrayElement.list = [5];

await nextFrame();
await nextFrame();
assert.equal(arrayElement.innerText, 'label5');
});

it('Renders attribute values even after being reconnected to the DOM', async () => {
const el = await fixture(`<nested-element-tag label="test"></nested-element-tag>`);
await nextFrame();

assert.equal(el.innerText, 'test');
el.label = 'test2';

await nextFrame();
assert.equal(el.innerText, 'test2');

const parentNode = el.parentNode;
parentNode.removeChild(el);
await nextFrame();
el.setAttribute('label', 'test3');
// Reattach the element to its parent node
await nextFrame();
parentNode.appendChild(el);
await nextFrame();

assert.equal(
stripCommentMarkers(el.outerHTML),
'<nested-element-tag label="test3"><button>test3</button></nested-element-tag>',
);
assert.equal(el.innerText, 'test3');
});
});