Socket
Socket
Sign inDemoInstall

@polymer/lit-element

Package Overview
Dependencies
Maintainers
4
Versions
24
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@polymer/lit-element - npm Package Compare versions

Comparing version 0.3.0 to 0.4.0

117

lit-element.js

@@ -64,8 +64,24 @@ import { PropertiesMixin } from '@polymer/polymer/lib/mixins/properties-mixin.js';

}
/**
* Override which sets up element rendering by calling* `_createRoot`
* and `_firstRendered`.
*/
ready() {
this._root = this._createRoot();
super.ready();
this._firstRendered();
}
/**
* Returns an
* Called after the element DOM is rendered for the first time.
* Implement to perform tasks after first rendering like capturing a
* reference to a static node which must be directly manipulated.
* This should not be commonly needed. For tasks which should be performed
* before first render, use the element constructor.
*/
_firstRendered() { }
/**
* Implement to customize where the element's template is rendered by
* returning an element into which to render. By default this creates
* a shadowRoot for the element. To render into the element's childNodes,
* return `this`.
* @returns {Element|DocumentFragment} Returns a node into which to render.

@@ -77,5 +93,6 @@ */

/**
* Override which always returns true so that `_propertiesChanged`
* is called whenver properties are invalidated. This ensures `render`
* is always called in response to `invalidate`.
* Override which returns the value of `_shouldRender` which users
* should implement to control rendering. If this method returns false,
* _propertiesChanged will not be called and no rendering will occur even
* if property values change or `_requestRender` is called.
* @param _props Current element properties

@@ -87,7 +104,24 @@ * @param _changedProps Changing element properties

_shouldPropertiesChange(_props, _changedProps, _prevProps) {
const shouldRender = this._shouldRender(_props, _changedProps, _prevProps);
if (!shouldRender && this.__resolveRenderComplete) {
this.__resolveRenderComplete(false);
}
return shouldRender;
}
/**
* Implement to control if rendering should occur when property values
* change or `_requestRender` is called. By default, this method always returns
* true, but this can be customized as an optimization to avoid rendering work
* when changes occur which should not be rendered.
* @param _props Current element properties
* @param _changedProps Changing element properties
* @param _prevProps Previous element properties
* @returns {boolean} Default implementation always returns true.
*/
_shouldRender(_props, _changedProps, _prevProps) {
return true;
}
/**
* Override which always calls `render` and `didRender` to perform
* element rendering.
* Override which performs element rendering by calling
* `_render`, `_applyRender`, and finally `_didRender`.
* @param props Current element properties

@@ -98,15 +132,25 @@ * @param changedProps Changing element properties

_propertiesChanged(props, changedProps, prevProps) {
this.__isChanging = true;
this.__isInvalid = false;
super._propertiesChanged(props, changedProps, prevProps);
const result = this.render(props);
const result = this._render(props);
if (result && this._root !== undefined) {
render(result, this._root, this.localName);
this._applyRender(result, this._root);
}
this.didRender(props, changedProps, prevProps);
this._didRender(props, changedProps, prevProps);
if (this.__resolveRenderComplete) {
this.__resolveRenderComplete();
this.__resolveRenderComplete(true);
}
}
_flushProperties() {
this.__isChanging = true;
this.__isInvalid = false;
super._flushProperties();
this.__isChanging = false;
}
/**
* Override which warns when a user attempts to change a property during
* the rendering lifecycle. This is an anti-pattern and should be avoided.
* @param property {string}
* @param value {any}
* @param old {any}
*/
_shouldPropertyChange(property, value, old) {

@@ -122,13 +166,30 @@ const change = super._shouldPropertyChange(property, value, old);

/**
* Returns a lit-html TemplateResult which is rendered into the element's
* shadowRoot. This method must be implemented.
* Implement to describe the DOM which should be rendered in the element.
* Ideally, the implementation is a pure function using only props to describe
* the element template. The implementation must a `lit-html` TemplateResult.
* By default this template is rendered into the element's shadowRoot.
* This can be customized by implementing `_createRoot`. This method must be
* implemented.
* @param {*} _props Current element properties
* @returns {TemplateResult} Must return a lit-html TemplateResult.
*/
render(_props) {
_render(_props) {
throw new Error('render() not implemented');
}
/**
* Called after element dom has been rendered. Implement to
* directly access element DOM.
* Renders the given lit-html template `result` into the given `node`.
* Implement to customize the way rendering is applied. This is should not
* typically be needed and is provided for advanced use cases.
* @param result {TemplateResult} `lit-html` template result to render
* @param node {Element|DocumentFragment} node into which to render
*/
_applyRender(result, node) {
render(result, node, this.localName);
}
/**
* Called after element DOM has been rendered. Implement to
* directly control rendered DOM. Typically this is not needed as `lit-html`
* can be used in the `_render` method to set properties, attributes, and
* event listeners. However, it is sometimes useful for calling methods on
* rendered elements, like calling `focus()` on an element to focus it.
* @param _props Current element properties

@@ -138,7 +199,8 @@ * @param _changedProps Changing element properties

*/
didRender(_props, _changedProps, _prevProps) { }
_didRender(_props, _changedProps, _prevProps) { }
/**
* Provokes the element to asynchronously re-render.
* Call to request the element to asynchronously re-render regardless
* of whether or not any property changes are pending.
*/
invalidate() { this._invalidateProperties(); }
_requestRender() { this._invalidateProperties(); }
/**

@@ -153,15 +215,22 @@ * Override which provides tracking of invalidated state.

* Returns a promise which resolves after the element next renders.
* The promise resolves to `true` if the element rendered and `false` if the
* element did not render.
* This is useful when users (e.g. tests) need to react to the rendered state
* of the element after a change is made.
* This can also be useful in event handlers if it is desireable to wait
* to send an event until after rendering. If possible implement the
* `_didRender` method to directly respond to rendering within the
* rendering lifecycle.
*/
get renderComplete() {
if (!this.__renderComplete) {
// TODO(sorvell): handle rejected render.
this.__renderComplete = new Promise((resolve) => {
this.__resolveRenderComplete =
() => {
(value) => {
this.__resolveRenderComplete = this.__renderComplete = null;
resolve();
resolve(value);
};
});
if (!this.__isInvalid && this.__resolveRenderComplete) {
this.__resolveRenderComplete();
Promise.resolve().then(() => this.__resolveRenderComplete(false));
}

@@ -168,0 +237,0 @@ }

{
"name": "@polymer/lit-element",
"version": "0.3.0",
"version": "0.4.0",
"description": "Polymer based lit-html custom element",

@@ -23,2 +23,3 @@ "license": "BSD-3-Clause",

"devDependencies": {
"@webcomponents/webcomponentsjs": "^2.0.0-0",
"@types/chai": "^4.0.1",

@@ -37,3 +38,3 @@ "@types/mocha": "^5.0.0",

"dependencies": {
"@polymer/polymer": "^3.0.0-pre.12",
"@polymer/polymer": "^3.0.0-pre.13",
"lit-html": "^0.9.0"

@@ -40,0 +41,0 @@ },

@@ -1,11 +0,101 @@

# lit-element
# LitElement
## Base class for creating custom elements using Polymer and lit-html.
## A simple base class for creating custom elements rendered with lit-html.
LitElement uses [lit-html](https://github.com/Polymer/lit-html) to render into the
element's [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM)
and [Polymer's](https://github.com/Polymer/polymer)
[PropertiesMixin](https://github.com/Polymer/polymer/blob/master/lib/mixins/properties-mixin.html)
to help manage element properties and attributes. LitElement reacts to changes in properties
and renders declaratively using `lit-html`.
* **React to changes:** LitElement reacts to changes in properties and attributes by
asynchronously rendering, ensuring changes are batched. This reduces overhead
and maintains consistent state.
* **Declarative rendering** LitElement uses `lit-html` to declaratively describe
how an element should render. Then `lit-html` ensures that updates
are fast by creating the static DOM once and smartly updating only the parts of
the DOM that change. Pass a javascript string to the `html` tag function,
describing dynamic parts with standard javascript template expressions:
* static elements: ``` html`<div>Hi</div>` ```
* expression: ``` html`<div>${disabled ? 'Off' : 'On'}</div>` ```
* attribute: ``` html`<div class$="${color} special"></div>` ```
* event handler: ``` html`<button on-click="${(e) => this._clickHandler(e)}"></button>` ```
## Minimal Example
1. Create a class that extends `LitElement`.
1. Implement a static `properties` getter that returns the element's properties
(which automatically become observed attributes).
1. Then implement a `_render(props)` method and use the element's
current properties (props) to return a `lit-html` template result to render
into the element. This is the only method that must be implemented by subclasses.
```javascript
import {LitElement, html} from 'node_modules/@polymer/lit-element/lit-element.js'
import {LitElement, html} from '@polymer/lit-element';
class MyElement extends LitElement {
// Public property API that triggers re-render (synched with attributes)
static get properties() { return { mood: String }}
_render({mood}) {
return html`You are ${mood} today!`;
}
}
customElements.define('my-element', MyElement);
```
```html
<my-element mood="happy"></my-element>
```
## API Documentation
See the [source](https://github.com/PolymerLabs/lit-element/blob/master/src/lit-element.ts#L90)
for detailed API info, here are some highlights. Note, the leading underscore
is used to indicate that these methods are
[protected](https://en.wikipedia.org/wiki/Class_(computer_programming)#Member_accessibility);
they are not private and can and should be implemented by subclasses.
These methods generally are called as part of the rendering lifecycle and should
not be called in user code unless otherwise indicated.
* `_createRoot()`: Implement to customize where the
element's template is rendered by returning an element into which to
render. By default this creates a shadowRoot for the element.
To render into the element's childNodes, return `this`.
* `_firstRendered()`: Called after the element DOM is rendered for the first time.
* `_shouldRender(props, changedProps, prevProps)`: Implement to control if rendering
should occur when property values change or `invalidate` is called.
By default, this method always returns true, but this can be customized as
an optimization to avoid rendering work when changes occur which should not be rendered.
* `_render(props)`: Implement to describe the element's DOM using `lit-html`. Ideally,
the `_render` implementation is a pure function using only `props` to describe
the element template. This is the only method that must be implemented by subclasses.
* `_didRender(props, changedProps, prevProps)`: Called after element DOM has been rendered.
Implement to directly control rendered DOM. Typically this is not needed as `lit-html`
can be used in the `_render` method to set properties, attributes, and
event listeners. However, it is sometimes useful for calling methods on
rendered elements, for example focusing an input:
`this.shadowRoot.querySelector('input').focus()`.
* `renderComplete`: Returns a promise which resolves after the element next renders.
* `_requestRender`: Call to request the element to asynchronously re-render regardless
of whether or not any property changes are pending.
## Bigger Example
```javascript
import {LitElement, html} from '@polymer/lit-element';
class MyElement extends LitElement {
// Public property API that triggers re-render (synced with attributes)
static get properties() {

@@ -21,15 +111,11 @@ return {

this.foo = 'foo';
}
ready() {
this.addEventListener('click', async (e) => {
this.whales++;
await this.nextRendered;
await this.renderComplete;
this.dispatchEvent(new CustomEvent('whales', {detail: {whales: this.whales}}))
});
super.ready();
}
// Render method should return a `TemplateResult` using the provided lit-html `html` tag function
render({foo, whales}) {
_render({foo, whales}) {
return html`

@@ -56,4 +142,3 @@ <style>

## Known Issues
* This element does not yet work with the ShadyCSS polyfill. Support is coming soon!
* API is subject to minor changes.
* See [lit-html](https://github.com/Polymer/lit-html) for more info.
* When the Shady DOM polyfill and ShadyCSS shim are used, styles may be [out of order](https://github.com/PolymerLabs/lit-element/issues/34).
* Rendering is not supported in IE11 due to a lit-html [issue](https://github.com/Polymer/lit-html/issues/210).

@@ -99,9 +99,26 @@ /**

/**
* Override which sets up element rendering by calling* `_createRoot`
* and `_firstRendered`.
*/
ready() {
this._root = this._createRoot();
super.ready();
this._firstRendered();
}
/**
* Returns an
* Called after the element DOM is rendered for the first time.
* Implement to perform tasks after first rendering like capturing a
* reference to a static node which must be directly manipulated.
* This should not be commonly needed. For tasks which should be performed
* before first render, use the element constructor.
*/
_firstRendered() {}
/**
* Implement to customize where the element's template is rendered by
* returning an element into which to render. By default this creates
* a shadowRoot for the element. To render into the element's childNodes,
* return `this`.
* @returns {Element|DocumentFragment} Returns a node into which to render.

@@ -114,5 +131,6 @@ */

/**
* Override which always returns true so that `_propertiesChanged`
* is called whenver properties are invalidated. This ensures `render`
* is always called in response to `invalidate`.
* Override which returns the value of `_shouldRender` which users
* should implement to control rendering. If this method returns false,
* _propertiesChanged will not be called and no rendering will occur even
* if property values change or `_requestRender` is called.
* @param _props Current element properties

@@ -124,2 +142,20 @@ * @param _changedProps Changing element properties

_shouldPropertiesChange(_props: object, _changedProps: object, _prevProps: object) {
const shouldRender = this._shouldRender(_props, _changedProps, _prevProps);
if (!shouldRender && this.__resolveRenderComplete) {
this.__resolveRenderComplete(false);
}
return shouldRender;
}
/**
* Implement to control if rendering should occur when property values
* change or `_requestRender` is called. By default, this method always returns
* true, but this can be customized as an optimization to avoid rendering work
* when changes occur which should not be rendered.
* @param _props Current element properties
* @param _changedProps Changing element properties
* @param _prevProps Previous element properties
* @returns {boolean} Default implementation always returns true.
*/
protected _shouldRender(_props: object, _changedProps: object, _prevProps: object) {
return true;

@@ -129,4 +165,4 @@ }

/**
* Override which always calls `render` and `didRender` to perform
* element rendering.
* Override which performs element rendering by calling
* `_render`, `_applyRender`, and finally `_didRender`.
* @param props Current element properties

@@ -137,16 +173,27 @@ * @param changedProps Changing element properties

_propertiesChanged(props: object, changedProps: object, prevProps: object) {
this.__isChanging = true;
this.__isInvalid = false;
super._propertiesChanged(props, changedProps, prevProps);
const result = this.render(props);
const result = this._render(props);
if (result && this._root !== undefined) {
render(result, this._root!, this.localName!);
this._applyRender(result, this._root!);
}
this.didRender(props, changedProps, prevProps);
this._didRender(props, changedProps, prevProps);
if (this.__resolveRenderComplete) {
this.__resolveRenderComplete();
this.__resolveRenderComplete(true);
}
}
_flushProperties() {
this.__isChanging = true;
this.__isInvalid = false;
super._flushProperties();
this.__isChanging = false;
}
/**
* Override which warns when a user attempts to change a property during
* the rendering lifecycle. This is an anti-pattern and should be avoided.
* @param property {string}
* @param value {any}
* @param old {any}
*/
_shouldPropertyChange(property: string, value: any, old: any) {

@@ -164,8 +211,12 @@ const change = super._shouldPropertyChange(property, value, old);

/**
* Returns a lit-html TemplateResult which is rendered into the element's
* shadowRoot. This method must be implemented.
* Implement to describe the DOM which should be rendered in the element.
* Ideally, the implementation is a pure function using only props to describe
* the element template. The implementation must a `lit-html` TemplateResult.
* By default this template is rendered into the element's shadowRoot.
* This can be customized by implementing `_createRoot`. This method must be
* implemented.
* @param {*} _props Current element properties
* @returns {TemplateResult} Must return a lit-html TemplateResult.
*/
protected render(_props: object): TemplateResult {
protected _render(_props: object): TemplateResult {
throw new Error('render() not implemented');

@@ -175,4 +226,18 @@ }

/**
* Called after element dom has been rendered. Implement to
* directly access element DOM.
* Renders the given lit-html template `result` into the given `node`.
* Implement to customize the way rendering is applied. This is should not
* typically be needed and is provided for advanced use cases.
* @param result {TemplateResult} `lit-html` template result to render
* @param node {Element|DocumentFragment} node into which to render
*/
protected _applyRender(result: TemplateResult, node: Element|DocumentFragment) {
render(result, node, this.localName!);
}
/**
* Called after element DOM has been rendered. Implement to
* directly control rendered DOM. Typically this is not needed as `lit-html`
* can be used in the `_render` method to set properties, attributes, and
* event listeners. However, it is sometimes useful for calling methods on
* rendered elements, like calling `focus()` on an element to focus it.
* @param _props Current element properties

@@ -182,8 +247,9 @@ * @param _changedProps Changing element properties

*/
protected didRender(_props: object, _changedProps: object, _prevProps: object) {}
protected _didRender(_props: object, _changedProps: object, _prevProps: object) {}
/**
* Provokes the element to asynchronously re-render.
* Call to request the element to asynchronously re-render regardless
* of whether or not any property changes are pending.
*/
invalidate() { this._invalidateProperties(); }
protected _requestRender() { this._invalidateProperties(); }

@@ -200,15 +266,22 @@ /**

* Returns a promise which resolves after the element next renders.
* The promise resolves to `true` if the element rendered and `false` if the
* element did not render.
* This is useful when users (e.g. tests) need to react to the rendered state
* of the element after a change is made.
* This can also be useful in event handlers if it is desireable to wait
* to send an event until after rendering. If possible implement the
* `_didRender` method to directly respond to rendering within the
* rendering lifecycle.
*/
get renderComplete() {
if (!this.__renderComplete) {
// TODO(sorvell): handle rejected render.
this.__renderComplete = new Promise((resolve) => {
this.__resolveRenderComplete =
() => {
(value: boolean) => {
this.__resolveRenderComplete = this.__renderComplete = null;
resolve();
resolve(value);
}
});
if (!this.__isInvalid && this.__resolveRenderComplete) {
this.__resolveRenderComplete();
Promise.resolve().then(() => this.__resolveRenderComplete!(false));
}

@@ -218,2 +291,2 @@ }

}
}
}

@@ -20,4 +20,5 @@ /**

renderAttributes,
styleString
styleString,
} from '../lit-element.js';
import {TemplateResult} from 'lit-html/lit-html.js';

@@ -47,3 +48,3 @@ /// <reference path="../../node_modules/@types/mocha/index.d.ts" />

customElements.define('x-1', class extends LitElement {
render() { return html`${rendered}` }
_render() { return html`${rendered}` }
});

@@ -59,3 +60,3 @@ const el = document.createElement('x-1');

customElements.define('x-1a', class extends LitElement {
render() { return html`${rendered}` }
_render() { return html`${rendered}` }

@@ -73,3 +74,3 @@ _createRoot() { return this; }

class E extends LitElement {
render() { return html`${rendered}` }
_render() { return html`${rendered}` }
};

@@ -91,3 +92,3 @@ customElements.define('x-2', E);

render(props: any) { return html`${props.foo}` }
_render(props: any) { return html`${props.foo}` }
}

@@ -114,3 +115,3 @@ customElements.define('x-3', E);

render(props: any) { return html`${props.foo}` }
_render(props: any) { return html`${props.foo}` }
}

@@ -129,3 +130,3 @@ customElements.define('x-4', E);

test('renders changes made at `ready` time', () => {
test('_firstRendered call after first render and not subsequent renders', async () => {
class E extends LitElement {

@@ -137,9 +138,11 @@ static get properties() {

foo = 'one';
firstRenderedCount = 0;
domAtFirstRendered = '';
ready() {
this.foo = 'changed';
super.ready();
_firstRendered() {
this.firstRenderedCount++;
this.domAtFirstRendered = this.shadowRoot!.innerHTML;
}
render(props: any) { return html`${props.foo}` }
_render(props: any) { return html`${props.foo}` }
}

@@ -149,6 +152,70 @@ customElements.define('x-5', E);

container.appendChild(el);
assert.equal(el.firstRenderedCount, 1);
assert.ok(el.shadowRoot);
assert.equal((el.shadowRoot as ShadowRoot).innerHTML, 'changed');
assert.equal((el.shadowRoot as ShadowRoot).innerHTML, el.domAtFirstRendered);
assert.equal(el.foo, el.domAtFirstRendered);
el.foo = 'two';
await el.renderComplete;
assert.equal(el.firstRenderedCount, 1);
assert.equal((el.shadowRoot as ShadowRoot).innerHTML, el.foo);
assert.notEqual(el.foo, el.domAtFirstRendered);
});
test('User defined accessor can trigger rendering', async () => {
class E extends LitElement {
__bar: any;
static get properties() {
return { foo: Number, bar: Number }
}
info: any[] = [];
foo = 0;
get bar() { return this._getProperty('bar'); }
set bar(value) {
this.__bar = value;
this._setProperty('bar', value);
}
_render(props: any) {
this.info.push('render');
return html`${props.foo}${props.bar}`
}
}
customElements.define('x-6', E);
const el = new E();
container.appendChild(el);
el.setAttribute('bar', '20');
await el.renderComplete;
assert.equal(el.bar, 20);
assert.equal(el.__bar, 20);
assert.equal(el.shadowRoot!.innerHTML, '020');
});
test('render attributes, properties, and event listeners via lit-html',
function() {
class E extends LitElement {
_event?: Event;
_render() {
const attr = 'attr';
const prop = 'prop';
const event = (e: Event) => { this._event = e; };
return html
`<div attr$="${attr}" prop="${prop}" on-zug="${event}"></div>`;
}
}
customElements.define('x-7', E);
const el = new E();
container.appendChild(el);
const d = el.shadowRoot!.querySelector('div')!;
assert.equal(d.getAttribute('attr'), 'attr');
assert.equal((d as any).prop, 'prop');
const e = new Event('zug');
d.dispatchEvent(e);
assert.equal(el._event, e);
});
test('renderComplete waits until next rendering', async () => {

@@ -162,5 +229,5 @@ class E extends LitElement {

render(props: any) { return html`${props.foo}` }
_render(props: any) { return html`${props.foo}` }
}
customElements.define('x-6', E);
customElements.define('x-8', E);
const el = new E();

@@ -179,3 +246,3 @@ container.appendChild(el);

test('propertiesChanged called after render', async () => {
test('_shouldRender controls rendering', async () => {
class E extends LitElement {

@@ -186,26 +253,37 @@ static get properties() {

info: Array<{text : string}> = [];
foo = 0;
renderCount = 0;
allowRender = true;
render(props: any) { return html`${props.foo}` }
_render() {
this.renderCount++;
return html`hi`;
}
_propertiesChanged(props: any, changedProps: any, prevProps: any) {
super._propertiesChanged(props, changedProps, prevProps);
this.info.push({text : this.shadowRoot!.innerHTML});
_shouldRender() {
return this.allowRender;
}
}
customElements.define('x-7', E);
customElements.define('x-9', E);
const el = new E();
container.appendChild(el);
assert.equal(el.info.length, 1);
assert.equal(el.info[0].text, '0');
el.foo = 5;
assert.equal(el.renderCount, 1);
el.foo++;
await el.renderComplete;
assert.equal(el.info.length, 2);
assert.equal(el.info[1].text, '5');
assert.equal(el.renderCount, 2);
el.allowRender = false;
el.foo++;
await el.renderComplete;
assert.equal(el.renderCount, 2);
el.allowRender = true;
el.foo++;
await el.renderComplete;
assert.equal(el.renderCount, 3);
});
test('didRender called after render', async () => {
test('renderComplete returns true if rendering happened and false otherwise', async () => {
class E extends LitElement {
needsRender = true;
static get properties() {

@@ -215,78 +293,64 @@ return { foo: Number }

info: Array<{text : string}> = [];
_shouldRender() {
return this.needsRender;
}
requestRender() {
this._requestRender();
}
foo = 0;
render(props: any) { return html`${props.foo}` }
didRender() { this.info.push({text : this.shadowRoot!.innerHTML}); }
_render(props: any) { return html`${props.foo}` }
}
customElements.define('x-8', E);
customElements.define('x-9.1', E);
const el = new E();
container.appendChild(el);
assert.equal(el.info.length, 1);
assert.equal(el.info[0].text, '0');
el.foo = 5;
await el.renderComplete;
assert.equal(el.info.length, 2);
assert.equal(el.info[1].text, '5');
el.foo++;
let rendered;
rendered = await el.renderComplete;
assert.equal(rendered, true);
assert.equal((el.shadowRoot as ShadowRoot).innerHTML, '1');
el.needsRender = false;
el.foo++;
rendered = await el.renderComplete;
assert.equal(rendered, false);
assert.equal((el.shadowRoot as ShadowRoot).innerHTML, '1');
el.needsRender = true;
el.foo++;
rendered = await el.renderComplete;
assert.equal(rendered, true);
assert.equal((el.shadowRoot as ShadowRoot).innerHTML, '3');
el.requestRender();
rendered = await el.renderComplete;
assert.equal(rendered, true);
rendered = await el.renderComplete;
assert.equal(rendered, false);
});
test(
'Rendering order is render, propertiesChanged, didRender, renderComplete',
async () => {
class E extends LitElement {
static get properties() {
return { foo: Number }
}
info: string[] = [];
foo = 0;
render(props: any) {
this.info.push('render');
return html`${props.foo}`
}
didRender() { this.info.push('didRender'); }
_propertiesChanged(props: any, changedProps: any, prevProps: any) {
super._propertiesChanged(props, changedProps, prevProps);
this.info.push('propertiesChanged');
}
}
customElements.define('x-9', E);
const el = new E();
container.appendChild(el);
assert.deepEqual(el.info,
[ 'render', 'didRender', 'propertiesChanged' ]);
el.info = [];
el.foo++;
await el.renderComplete;
assert.deepEqual(el.info,
[ 'render', 'didRender', 'propertiesChanged' ]);
});
test('User defined accessor can trigger rendering', async () => {
test('render lifecycle order: _shouldRender, _render, _applyRender, _didRender', async () => {
class E extends LitElement {
__bar: any;
static get properties() {
return { foo: Number, bar: Number }
return { foo: Number }
}
info: any[] = [];
foo = 0;
info: Array<string> = [];
get bar() { return this._getProperty('bar'); }
_shouldRender() {
this.info.push('_shouldRender');
return true;
}
set bar(value) {
this.__bar = value;
this._setProperty('bar', value);
_render() {
this.info.push('_render');
return html`hi`;
}
render(props: any) {
this.info.push('render');
return html`${props.foo}${props.bar}`
_applyRender(result: TemplateResult, root: Element|DocumentFragment) {
this.info.push('_applyRender');
super._applyRender(result, root);
}
_didRender() {
this.info.push('_didRender'); }
}

@@ -296,7 +360,5 @@ customElements.define('x-10', E);

container.appendChild(el);
el.setAttribute('bar', '20');
await el.renderComplete;
assert.equal(el.bar, 20);
assert.equal(el.__bar, 20);
assert.equal(el.shadowRoot!.innerHTML, '020');
assert.deepEqual(el.info, ['_shouldRender', '_render',
'_applyRender', '_didRender']);
});

@@ -313,3 +375,3 @@

render({foo, bar}: any) {
_render({foo, bar}: any) {
renderAttributes(this, {foo, bar});

@@ -341,3 +403,3 @@ return html`${foo}${bar}`

render({foo, bar, baz}: any) {
_render({foo, bar, baz}: any) {
return html

@@ -351,14 +413,14 @@ `<div class$="${classString({foo, bar, zonk : baz})}"></div>`;

const d = el.shadowRoot!.querySelector('div')!;
assert.equal(d.className, 'bar');
assert.include(d.className, 'bar');
el.foo = 1;
el.baz = true;
await el.renderComplete;
assert.equal(d.className, 'foo bar zonk');
assert.include(d.className, 'foo bar zonk');
el.bar = false;
await el.renderComplete;
assert.equal(d.className, 'foo zonk');
assert.include(d.className, 'foo zonk');
el.foo = 0;
el.baz = false;
await el.renderComplete;
assert.equal(d.className, '');
assert.notInclude(d.className, 'foo bar zonk');
});

@@ -376,3 +438,3 @@

render({transitionDuration, borderTop, zug}: any) {
_render({transitionDuration, borderTop, zug}: any) {
return html`<div style$="${styleString({

@@ -389,8 +451,10 @@ transitionDuration,

const d = el.shadowRoot!.querySelector('div')!;
assert.equal(d.style.cssText, 'transition-duration: 0ms; height: 0px;');
assert.include(d.style.cssText, 'transition-duration: 0ms;');
assert.include(d.style.cssText, 'height: 0px;');
el.transitionDuration = `100ms`;
el.borderTop = `5px`;
await el.renderComplete;
assert.equal(d.style.cssText,
'transition-duration: 100ms; border-top: 5px; height: 0px;');
assert.include(d.style.cssText, 'transition-duration: 100ms;');
assert.include(d.style.cssText, 'height: 0px;');
assert.include(d.style.cssText, 'border-top: 5px');
el.transitionDuration = ``;

@@ -403,26 +467,2 @@ el.borderTop = ``;

test('render attributes, properties, and event listeners via lit-html',
function() {
class E extends LitElement {
_event?: Event;
render() {
const attr = 'attr';
const prop = 'prop';
const event = (e: Event) => { this._event = e; };
return html
`<div attr$="${attr}" prop="${prop}" on-zug="${event}"></div>`;
}
}
customElements.define('x-14', E);
const el = new E();
container.appendChild(el);
const d = el.shadowRoot!.querySelector('div')!;
assert.equal(d.getAttribute('attr'), 'attr');
assert.equal((d as any).prop, 'prop');
const e = new Event('zug');
d.dispatchEvent(e);
assert.equal(el._event, e);
});
test('warns when setting properties re-entrantly', async () => {

@@ -432,3 +472,3 @@ class E extends LitElement {

render() {
_render() {
this._setProperty('foo', this._toggle ? 'fooToggle' : 'foo');

@@ -438,5 +478,9 @@ return html`hi`;

didRender() {
_didRender() {
this._setProperty('zonk', this._toggle ? 'zonkToggle' : 'zonk');
}
requestRender() {
this._requestRender();
}
}

@@ -446,3 +490,3 @@ const calls: any[] = [];

console.trace = function() { calls.push(arguments); };
customElements.define('x-15', E);
customElements.define('x-14', E);
const el = new E();

@@ -452,3 +496,3 @@ container.appendChild(el);

el._toggle = true;
el.invalidate();
el.requestRender();
await el.renderComplete;

@@ -455,0 +499,0 @@ assert.equal(calls.length, 4);

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc