Socket
Socket
Sign inDemoInstall

@polymer/lit-element

Package Overview
Dependencies
Maintainers
11
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.6.0-dev.5 to 0.6.0-dev.6

test/lit-element_test2.d.ts

121

lib/updating-element.d.ts

@@ -33,3 +33,3 @@ /**

/**
* Describes how and whether the property becomes an observed attribute.
* Indicates how and whether the property becomes an observed attribute.
* If the value is `false`, the property is not added to `observedAttributes`.

@@ -41,3 +41,3 @@ * If true or absent, the lowercased property name is observed (e.g. `fooBar` becomes `foobar`).

/**
* Describes how to serialize and deserialize the attribute to/from a property.
* Indicates how to serialize and deserialize the attribute to/from a property.
* If this value is a function, it is used to deserialize the attribute value

@@ -52,3 +52,3 @@ * a the property value. If it's an object, it can have keys for `fromAttribute` and

/**
* Describes if the property should reflect to an attribute.
* Indicates if the property should reflect to an attribute.
* If `true`, when the property is set, the attribute is set using the

@@ -61,10 +61,7 @@ * attribute name determined according to the rules for the `attribute`

/**
* Describes if setting a property should trigger invalidation and updating.
* This function takes the `newValue` and `oldValue` and returns `true` if
* invalidation should occur. If not present, a strict identity check
* (eg. === operator) is used. This is useful if a property should be
* considered dirty only if some condition is met, like if a key of an
* object value changes.
* A function that indicates if a property should be considered changed when it
* is set. The function should take the `newValue` and `oldValue` and return
* `true` if an update should be requested.
*/
shouldInvalidate?(value: T, oldValue: T): boolean;
hasChanged?(value: T, oldValue: T): boolean;
}

@@ -85,3 +82,3 @@ /**

export declare const property: (options?: PropertyDeclaration<any> | undefined) => (proto: Object, name: string) => void;
export interface ShouldInvalidate {
export interface HasChanged {
(value: unknown, old: unknown): boolean;

@@ -91,5 +88,5 @@ }

* Change function that returns true if `value` is different from `oldValue`.
* This method is used as the default for a property's `shouldInvalidate` function.
* This method is used as the default for a property's `hasChanged` function.
*/
export declare const notEqual: ShouldInvalidate;
export declare const notEqual: HasChanged;
/**

@@ -121,5 +118,5 @@ * Base element class which manages element properties and attributes. When

* Creates a property accessor on the element prototype if one does not exist.
* The property setter calls the property's `shouldInvalidate` property option
* or uses a strict identity check to determine if the set should trigger
* invalidation and update.
* The property setter calls the property's `hasChanged` property option
* or uses a strict identity check to determine whether or not to request
* an update.
*/

@@ -137,7 +134,7 @@ static createProperty(name: PropertyKey, options?: PropertyDeclaration): void;

/**
* Returns true if a property should cause invalidation and update.
* Called when a property value is set and uses the `shouldInvalidate`
* Returns true if a property should request an update.
* Called when a property value is set and uses the `hasChanged`
* option for the property if present or a strict identity check.
*/
private static _valueShouldInvalidate;
private static _valueHasChanged;
/**

@@ -184,3 +181,3 @@ * Returns the property value for the given attribute value.

* Otherwise these would shadow the accessor and break these properties.
* The properties are stored in a map which is played back after the constructor
* The properties are stored in a Map which is played back after the constructor
* runs.

@@ -212,13 +209,22 @@ */

/**
* Triggers an invalidation and records an old value for the specified
* property to be presented in the `changedProperties` argument to `update`.
* When manually creating a property setter, this method should be called to
* trigger an invalidation that honors any of the property options specified
* for the given property.
* Requests an update which is processed asynchronously. This should
* be called when an element should update based on some state not triggered
* by setting a property. In this case, pass no arguments. It should also be called
* when manually implementing a property setter. In this case, pass the property
* `name` and `oldValue` to ensure that any configured property options are honored.
* Returns the `updateComplete` Promise which is resolved when the update completes.
*
* @param name {PropertyKey}
* @param oldValue {any}
* @param name {PropertyKey} (optional) name of requesting property
* @param oldValue {any} (optional) old value of requesting property
* @returns {Promise} A Promise that is resolved when the update completes.
*/
protected invalidateProperty(name: PropertyKey, oldValue: any, options?: PropertyDeclaration): void;
requestUpdate(name?: PropertyKey, oldValue?: any): Promise<unknown>;
/**
* Requests an update for a specific property and records change information.
* @param name {PropertyKey} name of requesting property
* @param oldValue {any} old value of requesting property
* @param options {PropertyDeclaration}
*/
private _requestPropertyUpdate;
/**
* Invalidates the element causing it to asynchronously update regardless

@@ -228,8 +234,8 @@ * of whether or not any property changes are pending. This method is

*/
invalidate(): Promise<unknown>;
private readonly _isInvalid;
private _invalidate;
private readonly _hasRequestedUpdate;
/**
* Validates the element by updating it via `update`.
* Validates the element by updating it.
*/
private _flush;
private _validate;
private _markUpdated;

@@ -239,10 +245,9 @@ /**

* that resolves to a boolean value that is `true` if the element completed
* the update without triggering another update. This can happen if a property
* is set in `update()` after the call to `super.update()` for example.
* This getter can be implemented to await additional state. For example, it
* is sometimes useful to await a rendered element before fulfilling this
* promise. To do this, first await `super.updateComplete` then any subsequent
* state.
* the update without triggering another update. This happens if a property
* is set in `updated()`. This getter can be implemented to await additional
* state. For example, it is sometimes useful to await a rendered element before
* fulfilling this Promise. To do this, first await `super.updateComplete`
* then any subsequent state.
*
* @returns {Promise} The promise returns a boolean that indicates if the
* @returns {Promise} The Promise returns a boolean that indicates if the
* update resolved without triggering another update.

@@ -252,5 +257,6 @@ */

/**
* Controls whether or not `update` should be called when the element invalidates.
* By default, this method always returns true, but this can be customized to
* control when to update.
* Controls whether or not `update` should be called when the element requests
* an update. By default, this method always returns `true`, but this can be
* customized to control when to update.
*
* * @param _changedProperties Map of changed properties with old values

@@ -260,13 +266,30 @@ */

/**
* Updates the element. By default this method reflects property values to attributes.
* It should be overridden to render and keep updated DOM in the element's root.
* Within `update()` setting properties does not trigger `invalidate()`, allowing
* property values to be computed and validated before DOM is rendered and updated.
* This means in an override of `update()`, before calling `super.update()`
* setting properties will not trigger another update, but after calling `super.update()`
* setting properties will trigger another update.
* Updates the element. This method reflects property values to attributes.
* It can be overridden to render and keep updated DOM in the element's `renderRoot`.
* Setting properties inside this method will *not* trigger another update.
*
* * @param _changedProperties Map of changed properties with old values
*/
protected update(_changedProperties: PropertyValues): void;
/**
* Invoked whenever the element is updated. Implement to perform
* post-updating tasks via DOM APIs, for example, focusing an element.
*
* Setting properties inside this method will trigger the element to update
* again after this update cycle completes.
*
* * @param _changedProperties Map of changed properties with old values
*/
protected updated(_changedProperties: PropertyValues): void;
/**
* Invoked when the element is first updated. Implement to perform one time
* work on the element after update.
*
* Setting properties inside this method will trigger the element to update
* again after this update cycle completes.
*
* * @param _changedProperties Map of changed properties with old values
*/
protected firstUpdated(_changedProperties: PropertyValues): void;
}
export {};

@@ -26,3 +26,3 @@ /**

* Change function that returns true if `value` is different from `oldValue`.
* This method is used as the default for a property's `shouldInvalidate` function.
* This method is used as the default for a property's `hasChanged` function.
*/

@@ -37,7 +37,7 @@ export const notEqual = (value, old) => {

reflect: false,
shouldInvalidate: notEqual
hasChanged: notEqual
};
const microtaskPromise = new Promise((resolve) => resolve(true));
const STATE_HAS_UPDATED = 1;
const STATE_IS_INVALID = 1 << 2;
const STATE_UPDATE_REQUESTED = 1 << 2;
const STATE_IS_REFLECTING = 1 << 3;

@@ -84,5 +84,5 @@ /**

* Creates a property accessor on the element prototype if one does not exist.
* The property setter calls the property's `shouldInvalidate` property option
* or uses a strict identity check to determine if the set should trigger
* invalidation and update.
* The property setter calls the property's `hasChanged` property option
* or uses a strict identity check to determine whether or not to request
* an update.
*/

@@ -112,3 +112,3 @@ static createProperty(name, options = defaultPropertyDeclaration) {

this[key] = value;
this.invalidateProperty(name, oldValue, options);
this._requestPropertyUpdate(name, oldValue, options);
},

@@ -133,3 +133,3 @@ configurable: true,

this._finalized = true;
// initialize map populated in observedAttributes
// initialize Map populated in observedAttributes
this._attributeToPropertyMap = new Map();

@@ -155,8 +155,8 @@ // make any properties

/**
* Returns true if a property should cause invalidation and update.
* Called when a property value is set and uses the `shouldInvalidate`
* Returns true if a property should request an update.
* Called when a property value is set and uses the `hasChanged`
* option for the property if present or a strict identity check.
*/
static _valueShouldInvalidate(value, old, shouldInvalidate = notEqual) {
return shouldInvalidate(value, old);
static _valueHasChanged(value, old, hasChanged = notEqual) {
return hasChanged(value, old);
}

@@ -206,3 +206,3 @@ /**

* Otherwise these would shadow the accessor and break these properties.
* The properties are stored in a map which is played back after the constructor
* The properties are stored in a Map which is played back after the constructor
* runs.

@@ -245,6 +245,10 @@ */

connectedCallback() {
if ((this._updateState & STATE_HAS_UPDATED) && window.ShadyCSS !== undefined) {
window.ShadyCSS.styleElement(this);
if ((this._updateState & STATE_HAS_UPDATED)) {
if (window.ShadyCSS !== undefined) {
window.ShadyCSS.styleElement(this);
}
}
this.invalidate();
else {
this.requestUpdate();
}
}

@@ -298,31 +302,43 @@ /**

/**
* Triggers an invalidation and records an old value for the specified
* property to be presented in the `changedProperties` argument to `update`.
* When manually creating a property setter, this method should be called to
* trigger an invalidation that honors any of the property options specified
* for the given property.
* Requests an update which is processed asynchronously. This should
* be called when an element should update based on some state not triggered
* by setting a property. In this case, pass no arguments. It should also be called
* when manually implementing a property setter. In this case, pass the property
* `name` and `oldValue` to ensure that any configured property options are honored.
* Returns the `updateComplete` Promise which is resolved when the update completes.
*
* @param name {PropertyKey}
* @param oldValue {any}
* @param name {PropertyKey} (optional) name of requesting property
* @param oldValue {any} (optional) old value of requesting property
* @returns {Promise} A Promise that is resolved when the update completes.
*/
invalidateProperty(name, oldValue, options) {
// if not passed in, take options from class properties.
if (options === undefined) {
options = this.constructor._classProperties.get(name) ||
requestUpdate(name, oldValue) {
if (name !== undefined) {
const options = this.constructor._classProperties.get(name) ||
defaultPropertyDeclaration;
return this._requestPropertyUpdate(name, oldValue, options);
}
if (this.constructor._valueShouldInvalidate(this[name], oldValue, options.shouldInvalidate)) {
// track old value when changing.
if (!this._changedProperties.has(name)) {
this._changedProperties.set(name, oldValue);
return this._invalidate();
}
/**
* Requests an update for a specific property and records change information.
* @param name {PropertyKey} name of requesting property
* @param oldValue {any} old value of requesting property
* @param options {PropertyDeclaration}
*/
_requestPropertyUpdate(name, oldValue, options) {
if (!this.constructor._valueHasChanged(this[name], oldValue, options.hasChanged)) {
return this.updateComplete;
}
// track old value when changing.
if (!this._changedProperties.has(name)) {
this._changedProperties.set(name, oldValue);
}
// add to reflecting properties set
if (options.reflect === true) {
if (this._reflectingProperties === undefined) {
this._reflectingProperties = new Map();
}
// add to reflecting properties set
if (options.reflect === true) {
if (this._reflectingProperties === undefined) {
this._reflectingProperties = new Map();
}
this._reflectingProperties.set(name, options);
}
this.invalidate();
this._reflectingProperties.set(name, options);
}
return this._invalidate();
}

@@ -334,6 +350,6 @@ /**

*/
async invalidate() {
if (!this._isInvalid) {
// mark state invalid...
this._updateState = this._updateState | STATE_IS_INVALID;
async _invalidate() {
if (!this._hasRequestedUpdate) {
// mark state updating...
this._updateState = this._updateState | STATE_UPDATE_REQUESTED;
let resolver;

@@ -343,14 +359,14 @@ const previousValidatePromise = this._updatePromise;

await previousValidatePromise;
this._flush();
resolver(!this._isInvalid);
this._validate();
resolver(!this._hasRequestedUpdate);
}
return this._updatePromise;
return this.updateComplete;
}
get _isInvalid() {
return (this._updateState & STATE_IS_INVALID);
get _hasRequestedUpdate() {
return (this._updateState & STATE_UPDATE_REQUESTED);
}
/**
* Validates the element by updating it via `update`.
* Validates the element by updating it.
*/
_flush() {
_validate() {
// Mixin instance properties once, if they exist.

@@ -361,3 +377,10 @@ if (this._instanceProperties) {

if (this.shouldUpdate(this._changedProperties)) {
this.update(this._changedProperties);
const changedProperties = this._changedProperties;
this.update(changedProperties);
const needsFirstUpdate = !(this._updateState & STATE_HAS_UPDATED);
this._markUpdated();
if (needsFirstUpdate) {
this.firstUpdated(changedProperties);
}
this.updated(changedProperties);
}

@@ -370,3 +393,3 @@ else {

this._changedProperties = new Map();
this._updateState = this._updateState & ~STATE_IS_INVALID | STATE_HAS_UPDATED;
this._updateState = this._updateState & ~STATE_UPDATE_REQUESTED | STATE_HAS_UPDATED;
}

@@ -376,10 +399,9 @@ /**

* that resolves to a boolean value that is `true` if the element completed
* the update without triggering another update. This can happen if a property
* is set in `update()` after the call to `super.update()` for example.
* This getter can be implemented to await additional state. For example, it
* is sometimes useful to await a rendered element before fulfilling this
* promise. To do this, first await `super.updateComplete` then any subsequent
* state.
* the update without triggering another update. This happens if a property
* is set in `updated()`. This getter can be implemented to await additional
* state. For example, it is sometimes useful to await a rendered element before
* fulfilling this Promise. To do this, first await `super.updateComplete`
* then any subsequent state.
*
* @returns {Promise} The promise returns a boolean that indicates if the
* @returns {Promise} The Promise returns a boolean that indicates if the
* update resolved without triggering another update.

@@ -391,5 +413,6 @@ */

/**
* Controls whether or not `update` should be called when the element invalidates.
* By default, this method always returns true, but this can be customized to
* control when to update.
* Controls whether or not `update` should be called when the element requests
* an update. By default, this method always returns `true`, but this can be
* customized to control when to update.
*
* * @param _changedProperties Map of changed properties with old values

@@ -401,9 +424,6 @@ */

/**
* Updates the element. By default this method reflects property values to attributes.
* It should be overridden to render and keep updated DOM in the element's root.
* Within `update()` setting properties does not trigger `invalidate()`, allowing
* property values to be computed and validated before DOM is rendered and updated.
* This means in an override of `update()`, before calling `super.update()`
* setting properties will not trigger another update, but after calling `super.update()`
* setting properties will trigger another update.
* Updates the element. This method reflects property values to attributes.
* It can be overridden to render and keep updated DOM in the element's `renderRoot`.
* Setting properties inside this method will *not* trigger another update.
*
* * @param _changedProperties Map of changed properties with old values

@@ -418,5 +438,23 @@ */

}
// Before this (before calling super)...
this._markUpdated();
}
/**
* Invoked whenever the element is updated. Implement to perform
* post-updating tasks via DOM APIs, for example, focusing an element.
*
* Setting properties inside this method will trigger the element to update
* again after this update cycle completes.
*
* * @param _changedProperties Map of changed properties with old values
*/
updated(_changedProperties) { }
/**
* Invoked when the element is first updated. Implement to perform one time
* work on the element after update.
*
* Setting properties inside this method will trigger the element to update
* again after this update cycle completes.
*
* * @param _changedProperties Map of changed properties with old values
*/
firstUpdated(_changedProperties) { }
}

@@ -423,0 +461,0 @@ /**

@@ -20,3 +20,2 @@ /**

export declare abstract class LitElement extends UpdatingElement {
private _firstRendered;
/**

@@ -31,10 +30,5 @@ * Render method used to render the lit-html TemplateResult to the element's DOM.

* Updates the element. This method reflects property values to attributes
* and calls `render` to render DOM via lit-html. It should be overridden to
* perform tasks on rendered DOM. Within `update()` setting properties does
* not trigger `invalidate()`, allowing property values to be computed and
* validated before DOM is rendered and updated. This means in an override
* of `update()`, before calling `super.update()` setting properties will not
* trigger another update, but after calling `super.update()` setting
* properties will trigger another update.
* * @param changedProperties Map of changed properties with old values
* and calls `render` to render DOM via lit-html. Setting properties inside
* this method will *not* trigger another update.
* * @param _changedProperties Map of changed properties with old values
*/

@@ -44,14 +38,7 @@ protected update(changedProperties: PropertyValues): void;

Invoked on each update to perform rendering tasks. This method must return a
lit-html TemplateResult. Setting properties in `render()` will not trigger
the element to update.
lit-html TemplateResult. Setting properties inside this method will *not*
trigger the element to update.
* @returns {TemplateResult} Must return a lit-html TemplateResult.
*/
protected abstract render(): TemplateResult;
/**
Invoked when the element's DOM is first rendered. Override to perform
post rendering tasks via DOM APIs. For example, focusing a rendered element.
Setting properties in `firstRendered()` will trigger the element to update.
* @returns {TemplateResult} Must return a lit-html TemplateResult.
*/
protected firstRendered?(): void;
}

@@ -19,16 +19,7 @@ /**

export class LitElement extends UpdatingElement {
constructor() {
super(...arguments);
this._firstRendered = false;
}
/**
* Updates the element. This method reflects property values to attributes
* and calls `render` to render DOM via lit-html. It should be overridden to
* perform tasks on rendered DOM. Within `update()` setting properties does
* not trigger `invalidate()`, allowing property values to be computed and
* validated before DOM is rendered and updated. This means in an override
* of `update()`, before calling `super.update()` setting properties will not
* trigger another update, but after calling `super.update()` setting
* properties will trigger another update.
* * @param changedProperties Map of changed properties with old values
* and calls `render` to render DOM via lit-html. Setting properties inside
* this method will *not* trigger another update.
* * @param _changedProperties Map of changed properties with old values
*/

@@ -43,8 +34,2 @@ update(changedProperties) {

}
if (!this._firstRendered) {
this._firstRendered = true;
if (typeof this.firstRendered === 'function') {
this.firstRendered();
}
}
}

@@ -51,0 +36,0 @@ }

{
"name": "@polymer/lit-element",
"version": "0.6.0-dev.5",
"version": "0.6.0-dev.6",
"description": "Polymer based lit-html custom element",

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

"rollup-plugin-filesize": "^4.0.1",
"rollup-plugin-node-resolve": "^3.3.0",
"rollup-plugin-terser": "^1.0.1",

@@ -42,3 +43,3 @@ "tslint": "^5.7.0",

"dependencies": {
"lit-html": ">=0.11.0-dev.3 <2.0.0"
"lit-html": "^0.11.0"
},

@@ -45,0 +46,0 @@ "publishConfig": {

@@ -20,6 +20,6 @@ > ## 🛠 Status: In Development

* As class fields with the `@property()` [decorator](https://github.com/tc39/proposal-decorators#decorators),
if you're using a compiler that supports them, like TypeScript or Babel.
if you're using a compiler that supports them, like [TypeScript](https://www.typescriptlang.org/) or [Babel](https://babeljs.io/docs/en/babel-plugin-proposal-decorators).
* With a static `properties` getter.
* By manually writing getters and setters. This can be useful if tasks should
be performed when a property is set, for example validation. Call `invalidateProperty(name, oldValue)`
be performed when a property is set, for example validation. Call `requestUpdate(name, oldValue)`
in the setter to trigger an update and use any configured property options.

@@ -33,7 +33,7 @@

* `attribute`: Describes how and whether the property becomes an observed attribute.
* `attribute`: Indicates how and whether the property becomes an observed attribute.
If the value is `false`, the property is not added to `observedAttributes`.
If true or absent, the lowercased property name is observed (e.g. `fooBar` becomes `foobar`).
If a string, the string value is observed (e.g `attribute: 'foo-bar'`).
* `type`: Describes how to serialize and deserialize the attribute to/from a property.
* `type`: Indicates how to serialize and deserialize the attribute to/from a property.
If this value is a function, it is used to deserialize the attribute value

@@ -45,3 +45,3 @@ a the property value. If it's an object, it can have keys for `fromAttribute` and

directly to the attribute.
* `reflect`: Describes if the property should reflect to an attribute.
* `reflect`: Indicates if the property should reflect to an attribute.
If `true`, when the property is set, the attribute is set using the

@@ -51,7 +51,5 @@ attribute name determined according to the rules for the `attribute`

the `type` property option.
* `shouldInvalidate`: Describes if setting a property should trigger
invalidation and updating. This function takes the `newValue` and `oldValue` and
returns `true` if invalidation should occur. If not present, a strict identity
check is used. This is useful if a property should be considered dirty only
if some condition is met, like if a key of an object value changes.
* `hasChanged`: A function that indicates if a property should be considered
changed when it is set. The function should take the `newValue` and `oldValue`
and return `true` if an update should be requested.

@@ -85,3 +83,3 @@ * **React to changes:** LitElement reacts to changes in properties and attributes by

* When you're ready to use LitElement in a project, install it via [npm](https://www.npmjs.com/). To run the project in the browser, a module-compatible toolctain is required. We recommend installing the [Polymer CLI](https://github.com/Polymer/polymer-cli) to and using its development server as follows.
* When you're ready to use LitElement in a project, install it via [npm](https://www.npmjs.com/). To run the project in the browser, a module-compatible toolchain is required. We recommend installing the [Polymer CLI](https://github.com/Polymer/polymer-cli) and using its development server as follows.

@@ -92,2 +90,7 @@ 1. Add LitElement to your project:

1. Install the webcomponents polyfill. If you're developing a reusable package, this should be a dev dependency which you load in your tests, demos, etc.
```npm i -D @webcomponents/webcomponentsjs```
1. Create an element by extending LitElement and calling `customElements.define` with your class (see the examples below).

@@ -97,3 +100,3 @@

```npm i -g polymer-cli@next```
```npm i -g polymer-cli```

@@ -122,3 +125,3 @@ 1. Run the development server and open a browser pointing to its URL:

```html
<script src="node_modules/@webcomponents/webcomponents-bundle.js"></script>
<script src="node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js"></script>
<script type="module">

@@ -129,5 +132,13 @@ import {LitElement, html, property} from '@polymer/lit-element';

@property({type: String})
mood = 'happy';
static get properties() {
return {
mood: {type: String}
};
}
constructor() {
super();
this.mood = 'happy';
}
render() {

@@ -152,42 +163,38 @@ return html`<style> .mood { color: green; } </style>

Note, since `render()` is called by `update()`, setting properties does not trigger
`invalidate()`, allowing property values to be computed and validated.
an update, allowing property values to be computed and validated.
* `shouldUpdate(changedProperties)` (protected): Implement to control if updating and rendering
should occur when property values change or `invalidate` is called. The `changedProperties`
argument is an object with keys for the changed properties pointing to their previous values.
By default, this method always returns true, but this can be customized as
should occur when property values change or `requestUpdate()` is called. The `changedProperties`
argument is a Map with keys for the changed properties pointing to their previous values.
By default, this method always returns `true`, but this can be customized as
an optimization to avoid updating work when changes occur, which should not be rendered.
* `update()` (protected): This method calls `render()` and then uses `lit-html` in order to
render the template DOM. 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()`. The `changedProperties` argument is a Map
with keys for the changed properties pointing to their previous values.
Note, calling `super.update()` is required. Before calling `super.update()`,
changes made to properties do not trigger `invalidate()`, after calling `super.update()`,
changes do trigger `invalidate()`.
* `update(changedProperties)` (protected): This method calls `render()` and then uses `lit-html`
in order to render the template DOM. It also updates any reflected attributes based on
property values. Setting properties inside this method will *not* trigger another update..
* `firstRendered()`: (protected) Called after the element's DOM has been
updated the first time. This method can be useful for capturing references to rendered static
nodes that must be directly acted upon, for example in `update()`.
* `firstUpdated(changedProperties)`: (protected) Called after the element's DOM has been
updated the first time, immediately before `updated()` is called.
This method can be useful for capturing references to rendered static nodes that
must be directly acted upon, for example in `updated()`.
Setting properties inside this method will trigger the element to update.
* `updateComplete`: Returns a Promise that resolves when the element has completed
updating that resolves to a boolean value that is `true` if the element completed the
update without triggering another update. This can happen if a property is set in
`update()` after the call to `super.update()` for example. This getter can be
implemented to await additional state. For example, it is sometimes useful to
await a rendered element before fulfilling this promise. To do this, first
await `super.updateComplete` then any subsequent state.
* `updated(changedProperties)`: (protected) Called whenever the element's DOM has been
updated and rendered. Implement to perform post updating tasks via DOM APIs, for example,
focusing an element. Setting properties inside this method will trigger the element to update.
* `invalidate`: Call to request the element to asynchronously update regardless
of whether or not any property changes are pending. This should only be called
when an element should update based on some state not stored in properties,
since setting properties automically calls `invalidate`.
* `updateComplete`: Returns a Promise that resolves when the element has completed
updating. The Promise value is a boolean that is `true` if the element completed the
update without triggering another update. This happens if a property is set inside
`updated()`. This getter can be implemented to await additional state.
For example, it is sometimes useful to await a rendered element before fulfilling
this Promise. To do this, first await `super.updateComplete` then any subsequent state.
* `invalidateProperty(name, oldValue)` (protected): Triggers an invalidation for
a specific property. This is useful when manually implementing a property setter.
Call `invalidateProperty` instead of `invalidate` to ensure that any configured
property options are honored.
* `requestUpdate(name?, oldValue?)`: Call to request the element to asynchronously
update regardless of whether or not any property changes are pending. This should
be called when an element should update based on some state not triggered
by setting a property. In this case, pass no arguments. It should also be called
when manually implementing a property setter. In this case, pass the property
`name` and `oldValue` to ensure that any configured property options are honored.
Returns the `updateComplete` Promise which is resolved when the update completes.

@@ -202,22 +209,27 @@ * `createRenderRoot()` (protected): Implement to customize where the

* When the element is first connected or a property is set (e.g. `element.foo = 5`)
and the property's `shouldInvalidate(value, oldValue)` returns true.
* `invalidate()`: Updates the element after waiting a [microtask](https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/) (at the end
and the property's `hasChanged(value, oldValue)` returns `true`.
* `requestUpdate()`: Updates the element after awaiting a [microtask](https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/) (at the end
of the event loop, before the next paint).
* `shouldUpdate(changedProperties)`: The update proceeds if this returns true, which
* `shouldUpdate(changedProperties)`: The update proceeds if this returns `true`, which
it does by default.
* `update(changedProperties)`: Updates the element. Setting properties inside
update is handled specially. Before calling `super.update()`, setting properties
will *not* trigger an update. After calling `super.update()` setting properties will
trigger an update.
* `update(changedProperties)`: Updates the element. Setting properties inside this
method will *not* trigger the element to update.
* `render()`: Returns a `lit-html` TemplateResult (e.g. <code>html\`Hello ${world}\`</code>)
to render element DOM. Setting properties in `render()` does not trigger an update.
* `firstRendered()`: Called after the DOM is rendered the first time.
Setting properties in `firstRendered()` does trigger an update.
* `updateComplete` promise is resolved with a boolean that is `true` if the
to render element DOM. Setting properties inside this method will *not* trigger
the element to update.
* `firstUpdated(changedProperties)`: Called after the element is updated the first time,
immediately before `updated` is called. Setting properties inside this method will
trigger the element to update.
* `updated(changedProperties)`: Called whenever the element is updated.
Setting properties inside this method will trigger the element to update.
* `updateComplete` Promise is resolved with a boolean that is `true` if the
element is not pending another update, and any code awaiting the element's
`updateComplete` promise runs and observes the element in the updated state.
`updateComplete` Promise runs and observes the element in the updated state.
## Bigger Example
```JavaScript
Note, this example uses decorators to create properties. Decorators are a proposed
standard currently available in [TypeScript](https://www.typescriptlang.org/) or [Babel](https://babeljs.io/docs/en/babel-plugin-proposal-decorators).
```ts
import {LitElement, html, property} from '@polymer/lit-element';

@@ -224,0 +236,0 @@

@@ -17,2 +17,3 @@ /**

import {terser} from 'rollup-plugin-terser';
import resolve from 'rollup-plugin-node-resolve';

@@ -31,2 +32,3 @@ export default {

plugins: [
resolve(),
terser({

@@ -33,0 +35,0 @@ warnings: true,

@@ -40,3 +40,3 @@ /**

/**
* Describes how and whether the property becomes an observed attribute.
* Indicates how and whether the property becomes an observed attribute.
* If the value is `false`, the property is not added to `observedAttributes`.

@@ -49,3 +49,3 @@ * If true or absent, the lowercased property name is observed (e.g. `fooBar` becomes `foobar`).

/**
* Describes how to serialize and deserialize the attribute to/from a property.
* Indicates how to serialize and deserialize the attribute to/from a property.
* If this value is a function, it is used to deserialize the attribute value

@@ -61,3 +61,3 @@ * a the property value. If it's an object, it can have keys for `fromAttribute` and

/**
* Describes if the property should reflect to an attribute.
* Indicates if the property should reflect to an attribute.
* If `true`, when the property is set, the attribute is set using the

@@ -71,10 +71,7 @@ * attribute name determined according to the rules for the `attribute`

/**
* Describes if setting a property should trigger invalidation and updating.
* This function takes the `newValue` and `oldValue` and returns `true` if
* invalidation should occur. If not present, a strict identity check
* (eg. === operator) is used. This is useful if a property should be
* considered dirty only if some condition is met, like if a key of an
* object value changes.
* A function that indicates if a property should be considered changed when it
* is set. The function should take the `newValue` and `oldValue` and return
* `true` if an update should be requested.
*/
shouldInvalidate?(value: T, oldValue: T): boolean;
hasChanged?(value: T, oldValue: T): boolean;

@@ -110,3 +107,3 @@ }

export interface ShouldInvalidate {
export interface HasChanged {
(value: unknown, old: unknown): boolean;

@@ -117,5 +114,5 @@ }

* Change function that returns true if `value` is different from `oldValue`.
* This method is used as the default for a property's `shouldInvalidate` function.
* This method is used as the default for a property's `hasChanged` function.
*/
export const notEqual: ShouldInvalidate = (value: unknown, old: unknown): boolean => {
export const notEqual: HasChanged = (value: unknown, old: unknown): boolean => {
// This ensures (old==NaN, value==NaN) always returns false

@@ -129,3 +126,3 @@ return old !== value && (old === old || value === value);

reflect: false,
shouldInvalidate: notEqual
hasChanged: notEqual
};

@@ -136,5 +133,5 @@

const STATE_HAS_UPDATED = 1;
const STATE_IS_INVALID = 1 << 2;
const STATE_UPDATE_REQUESTED = 1 << 2;
const STATE_IS_REFLECTING = 1 << 3;
type UpdateState = typeof STATE_HAS_UPDATED | typeof STATE_IS_INVALID | typeof STATE_IS_REFLECTING;
type UpdateState = typeof STATE_HAS_UPDATED | typeof STATE_UPDATE_REQUESTED | typeof STATE_IS_REFLECTING;

@@ -185,5 +182,5 @@ /**

* Creates a property accessor on the element prototype if one does not exist.
* The property setter calls the property's `shouldInvalidate` property option
* or uses a strict identity check to determine if the set should trigger
* invalidation and update.
* The property setter calls the property's `hasChanged` property option
* or uses a strict identity check to determine whether or not to request
* an update.
*/

@@ -214,3 +211,3 @@ static createProperty(name: PropertyKey, options: PropertyDeclaration = defaultPropertyDeclaration) {

this[key] = value;
this.invalidateProperty(name, oldValue, options);
this._requestPropertyUpdate(name, oldValue, options);
},

@@ -236,3 +233,3 @@ configurable: true,

this._finalized = true;
// initialize map populated in observedAttributes
// initialize Map populated in observedAttributes
this._attributeToPropertyMap = new Map();

@@ -260,9 +257,9 @@ // make any properties

/**
* Returns true if a property should cause invalidation and update.
* Called when a property value is set and uses the `shouldInvalidate`
* Returns true if a property should request an update.
* Called when a property value is set and uses the `hasChanged`
* option for the property if present or a strict identity check.
*/
private static _valueShouldInvalidate(value: unknown, old: unknown,
shouldInvalidate: ShouldInvalidate = notEqual) {
return shouldInvalidate(value, old);
private static _valueHasChanged(value: unknown, old: unknown,
hasChanged: HasChanged = notEqual) {
return hasChanged(value, old);
}

@@ -342,3 +339,3 @@

* Otherwise these would shadow the accessor and break these properties.
* The properties are stored in a map which is played back after the constructor
* The properties are stored in a Map which is played back after the constructor
* runs.

@@ -384,6 +381,9 @@ */

connectedCallback() {
if ((this._updateState & STATE_HAS_UPDATED) && window.ShadyCSS !== undefined) {
window.ShadyCSS.styleElement(this);
if ((this._updateState & STATE_HAS_UPDATED)) {
if (window.ShadyCSS !== undefined) {
window.ShadyCSS.styleElement(this);
}
} else {
this.requestUpdate();
}
this.invalidate();
}

@@ -441,32 +441,45 @@

/**
* Triggers an invalidation and records an old value for the specified
* property to be presented in the `changedProperties` argument to `update`.
* When manually creating a property setter, this method should be called to
* trigger an invalidation that honors any of the property options specified
* for the given property.
* Requests an update which is processed asynchronously. This should
* be called when an element should update based on some state not triggered
* by setting a property. In this case, pass no arguments. It should also be called
* when manually implementing a property setter. In this case, pass the property
* `name` and `oldValue` to ensure that any configured property options are honored.
* Returns the `updateComplete` Promise which is resolved when the update completes.
*
* @param name {PropertyKey}
* @param oldValue {any}
* @param name {PropertyKey} (optional) name of requesting property
* @param oldValue {any} (optional) old value of requesting property
* @returns {Promise} A Promise that is resolved when the update completes.
*/
protected invalidateProperty(name: PropertyKey, oldValue: any, options?: PropertyDeclaration) {
// if not passed in, take options from class properties.
if (options === undefined) {
options = (this.constructor as typeof UpdatingElement)._classProperties.get(name) ||
requestUpdate(name?: PropertyKey, oldValue?: any) {
if (name !== undefined) {
const options = (this.constructor as typeof UpdatingElement)._classProperties.get(name) ||
defaultPropertyDeclaration;
return this._requestPropertyUpdate(name, oldValue, options);
}
if ((this.constructor as typeof UpdatingElement)._valueShouldInvalidate(this[name as keyof this],
oldValue, options.shouldInvalidate)) {
// track old value when changing.
if (!this._changedProperties.has(name)) {
this._changedProperties.set(name, oldValue);
return this._invalidate();
}
/**
* Requests an update for a specific property and records change information.
* @param name {PropertyKey} name of requesting property
* @param oldValue {any} old value of requesting property
* @param options {PropertyDeclaration}
*/
private _requestPropertyUpdate(name: PropertyKey, oldValue: any, options: PropertyDeclaration) {
if (!(this.constructor as typeof UpdatingElement)._valueHasChanged(this[name as keyof this],
oldValue, options.hasChanged)) {
return this.updateComplete;
}
// track old value when changing.
if (!this._changedProperties.has(name)) {
this._changedProperties.set(name, oldValue);
}
// add to reflecting properties set
if (options.reflect === true) {
if (this._reflectingProperties === undefined) {
this._reflectingProperties = new Map();
}
// add to reflecting properties set
if (options.reflect === true) {
if (this._reflectingProperties === undefined) {
this._reflectingProperties = new Map();
}
this._reflectingProperties.set(name, options);
}
this.invalidate();
this._reflectingProperties.set(name, options);
}
return this._invalidate();
}

@@ -479,6 +492,6 @@

*/
async invalidate() {
if (!this._isInvalid) {
// mark state invalid...
this._updateState = this._updateState | STATE_IS_INVALID;
private async _invalidate() {
if (!this._hasRequestedUpdate) {
// mark state updating...
this._updateState = this._updateState | STATE_UPDATE_REQUESTED;
let resolver: any;

@@ -488,16 +501,16 @@ const previousValidatePromise = this._updatePromise;

await previousValidatePromise;
this._flush();
resolver!(!this._isInvalid);
this._validate();
resolver!(!this._hasRequestedUpdate);
}
return this._updatePromise;
return this.updateComplete;
}
private get _isInvalid() {
return (this._updateState & STATE_IS_INVALID);
private get _hasRequestedUpdate() {
return (this._updateState & STATE_UPDATE_REQUESTED);
}
/**
* Validates the element by updating it via `update`.
* Validates the element by updating it.
*/
private _flush() {
private _validate() {
// Mixin instance properties once, if they exist.

@@ -508,3 +521,10 @@ if (this._instanceProperties) {

if (this.shouldUpdate(this._changedProperties)) {
this.update(this._changedProperties);
const changedProperties = this._changedProperties;
this.update(changedProperties);
const needsFirstUpdate = !(this._updateState & STATE_HAS_UPDATED);
this._markUpdated();
if (needsFirstUpdate) {
this.firstUpdated(changedProperties);
}
this.updated(changedProperties);
} else {

@@ -517,3 +537,3 @@ this._markUpdated();

this._changedProperties = new Map();
this._updateState = this._updateState & ~STATE_IS_INVALID | STATE_HAS_UPDATED;
this._updateState = this._updateState & ~STATE_UPDATE_REQUESTED | STATE_HAS_UPDATED;
}

@@ -524,10 +544,9 @@

* that resolves to a boolean value that is `true` if the element completed
* the update without triggering another update. This can happen if a property
* is set in `update()` after the call to `super.update()` for example.
* This getter can be implemented to await additional state. For example, it
* is sometimes useful to await a rendered element before fulfilling this
* promise. To do this, first await `super.updateComplete` then any subsequent
* state.
* the update without triggering another update. This happens if a property
* is set in `updated()`. This getter can be implemented to await additional
* state. For example, it is sometimes useful to await a rendered element before
* fulfilling this Promise. To do this, first await `super.updateComplete`
* then any subsequent state.
*
* @returns {Promise} The promise returns a boolean that indicates if the
* @returns {Promise} The Promise returns a boolean that indicates if the
* update resolved without triggering another update.

@@ -540,5 +559,6 @@ */

/**
* Controls whether or not `update` should be called when the element invalidates.
* By default, this method always returns true, but this can be customized to
* control when to update.
* Controls whether or not `update` should be called when the element requests
* an update. By default, this method always returns `true`, but this can be
* customized to control when to update.
*
* * @param _changedProperties Map of changed properties with old values

@@ -551,12 +571,9 @@ */

/**
* Updates the element. By default this method reflects property values to attributes.
* It should be overridden to render and keep updated DOM in the element's root.
* Within `update()` setting properties does not trigger `invalidate()`, allowing
* property values to be computed and validated before DOM is rendered and updated.
* This means in an override of `update()`, before calling `super.update()`
* setting properties will not trigger another update, but after calling `super.update()`
* setting properties will trigger another update.
* Updates the element. This method reflects property values to attributes.
* It can be overridden to render and keep updated DOM in the element's `renderRoot`.
* Setting properties inside this method will *not* trigger another update.
*
* * @param _changedProperties Map of changed properties with old values
*/
protected update(_changedProperties: PropertyValues): void {
protected update(_changedProperties: PropertyValues) {
if (this._reflectingProperties !== undefined && this._reflectingProperties.size > 0) {

@@ -568,6 +585,26 @@ for (const [k, v] of this._reflectingProperties) {

}
// Before this (before calling super)...
this._markUpdated();
}
/**
* Invoked whenever the element is updated. Implement to perform
* post-updating tasks via DOM APIs, for example, focusing an element.
*
* Setting properties inside this method will trigger the element to update
* again after this update cycle completes.
*
* * @param _changedProperties Map of changed properties with old values
*/
protected updated(_changedProperties: PropertyValues) {}
/**
* Invoked when the element is first updated. Implement to perform one time
* work on the element after update.
*
* Setting properties inside this method will trigger the element to update
* again after this update cycle completes.
*
* * @param _changedProperties Map of changed properties with old values
*/
protected firstUpdated(_changedProperties: PropertyValues) {}
}

@@ -24,3 +24,2 @@ /**

private _firstRendered = false;
/**

@@ -36,10 +35,5 @@ * Render method used to render the lit-html TemplateResult to the element's DOM.

* Updates the element. This method reflects property values to attributes
* and calls `render` to render DOM via lit-html. It should be overridden to
* perform tasks on rendered DOM. Within `update()` setting properties does
* not trigger `invalidate()`, allowing property values to be computed and
* validated before DOM is rendered and updated. This means in an override
* of `update()`, before calling `super.update()` setting properties will not
* trigger another update, but after calling `super.update()` setting
* properties will trigger another update.
* * @param changedProperties Map of changed properties with old values
* and calls `render` to render DOM via lit-html. Setting properties inside
* this method will *not* trigger another update.
* * @param _changedProperties Map of changed properties with old values
*/

@@ -53,8 +47,2 @@ protected update(changedProperties: PropertyValues) {

}
if (!this._firstRendered) {
this._firstRendered = true;
if (typeof this.firstRendered === 'function') {
this.firstRendered();
}
}
}

@@ -64,16 +52,7 @@

Invoked on each update to perform rendering tasks. This method must return a
lit-html TemplateResult. Setting properties in `render()` will not trigger
the element to update.
lit-html TemplateResult. Setting properties inside this method will *not*
trigger the element to update.
* @returns {TemplateResult} Must return a lit-html TemplateResult.
*/
protected abstract render(): TemplateResult;
/**
Invoked when the element's DOM is first rendered. Override to perform
post rendering tasks via DOM APIs. For example, focusing a rendered element.
Setting properties in `firstRendered()` will trigger the element to update.
* @returns {TemplateResult} Must return a lit-html TemplateResult.
*/
protected firstRendered?(): void;
}

@@ -123,3 +123,5 @@ /**

const name = generateElementName();
customElements.define(name, class extends LitElement {
class E extends LitElement {
inner: LitElement|null = null;
render() { return html`

@@ -133,7 +135,16 @@ <style>

}
});
const el = document.createElement(name);
firstUpdated() {
this.inner = this.shadowRoot!.querySelector('x-inner')! as LitElement;
}
}
customElements.define(name, E);
const el = document.createElement(name) as E;
container.appendChild(el);
// Workaround for Safari 9 Promise timing bugs.
await el.updateComplete && await el.inner!.updateComplete;
await nextFrame();
const div = el.shadowRoot!.querySelector('x-inner')!.shadowRoot!.querySelector('div');
const div = el.inner!.shadowRoot!.querySelector('div');
assert.equal(getComputedStyleValue(div!, 'border-top-width').trim(), '8px');

@@ -155,2 +166,5 @@ });

customElements.define(name1, class extends LitElement {
inner: Element|null = null;
render() { return html`

@@ -164,5 +178,10 @@ <style>

}
firstUpdated() {
this.inner = this.shadowRoot!.querySelector('x-inner1');
}
});
const name2 = generateElementName();
customElements.define(name2, class extends LitElement {
render() { return html`

@@ -175,4 +194,5 @@ <style>

}
});
const el = document.createElement(name1);
const el = document.createElement(name1) as LitElement;
const el2 = document.createElement(name2);

@@ -182,2 +202,6 @@ container.appendChild(el);

let div: Element|null;
// Workaround for Safari 9 Promise timing bugs.
await el.updateComplete;
await nextFrame();

@@ -188,2 +212,6 @@ const inner = el.shadowRoot!.querySelector('x-inner1');

el2!.shadowRoot!.appendChild(inner!);
// Workaround for Safari 9 Promise timing bugs.
await el.updateComplete;
await nextFrame();

@@ -205,3 +233,4 @@ assert.equal(getComputedStyleValue(div!, 'border-top-width').trim(), '8px');

const name = generateElementName();
customElements.define(name, class extends LitElement {
class E extends LitElement {
inner: LitElement|null = null;
render() { return html`

@@ -217,5 +246,14 @@ <style>

}
});
const el = document.createElement(name);
firstUpdated() {
this.inner = this.shadowRoot!.querySelector('x-inner2') as LitElement;
}
}
customElements.define(name, E);
const el = document.createElement(name) as E;
container.appendChild(el);
// Workaround for Safari 9 Promise timing bugs.
await el.updateComplete && await el.inner!.updateComplete;
await nextFrame();

@@ -266,3 +304,3 @@ const div = el.shadowRoot!.querySelector('x-inner2')!.shadowRoot!.querySelector('div');

border = `4px solid orange`;
el.invalidate();
el.requestUpdate();
await el.updateComplete;

@@ -269,0 +307,0 @@ assert.equal(getComputedStyleValue(div!, 'border-top-width').trim(), '6px');

@@ -54,6 +54,6 @@ /**

test('invalidate waits until update/rendering', async () => {
test('`requestUpdate` waits until update/rendering', async () => {
class E extends LitElement {
updated = 0;
render() { return html`${++this.updated}`; }
updateCount = 0;
render() { return html`${++this.updateCount}`; }
}

@@ -63,11 +63,11 @@ customElements.define(generateElementName(), E);

container.appendChild(el);
await el.invalidate();
await el.requestUpdate();
assert.equal(
stripExpressionDelimeters(el.shadowRoot!.innerHTML),
'1');
await el.invalidate();
await el.requestUpdate();
assert.equal(
stripExpressionDelimeters(el.shadowRoot!.innerHTML),
'2');
await el.invalidate();
await el.requestUpdate();
assert.equal(

@@ -78,6 +78,6 @@ stripExpressionDelimeters(el.shadowRoot!.innerHTML),

test('updateComplete waits for invalidate but does not trigger invalidation, async', async () => {
test('`updateComplete` waits for `requestUpdate` but does not trigger update, async', async () => {
class E extends LitElement {
updated = 0;
render() { return html`${++this.updated}`; }
updateCount = 0;
render() { return html`${++this.updateCount}`; }
}

@@ -95,3 +95,3 @@ customElements.define(generateElementName(), E);

'1');
el.invalidate();
el.requestUpdate();
await el.updateComplete;

@@ -107,3 +107,3 @@ assert.equal(

test('shouldUpdate controls update/rendering',
test('`shouldUpdate` controls update/rendering',
async () => {

@@ -113,7 +113,7 @@ class E extends LitElement {

needsUpdate = true;
updated = 0;
updateCount = 0;
shouldUpdate() { return this.needsUpdate; }
render() { return html`${++this.updated}`; }
render() { return html`${++this.updateCount}`; }
}

@@ -128,3 +128,3 @@ customElements.define(generateElementName(), E);

el.needsUpdate = false;
await el.invalidate();
await el.requestUpdate();
assert.equal(

@@ -134,7 +134,7 @@ stripExpressionDelimeters(el.shadowRoot!.innerHTML),

el.needsUpdate = true;
await el.invalidate();
await el.requestUpdate();
assert.equal(
stripExpressionDelimeters(el.shadowRoot!.innerHTML),
'2');
await el.invalidate();
await el.requestUpdate();
assert.equal(

@@ -177,3 +177,3 @@ stripExpressionDelimeters(el.shadowRoot!.innerHTML),

const shouldInvalidate = (value: any, old: any) => old === undefined || value > old;
const hasChanged = (value: any, old: any) => old === undefined || value > old;
const fromAttribute = (value: any) => parseInt(value);

@@ -187,6 +187,6 @@ const toAttribute = (value: any) => `${value}-attr`;

customAttr: {attribute: 'custom', reflect: true},
shouldInvalidate: {shouldInvalidate},
hasChanged: {hasChanged},
fromAttribute: {type: fromAttribute},
toAttribute: {reflect: true, type: {toAttribute}},
all: {attribute: 'all-attr', shouldInvalidate, type: {fromAttribute, toAttribute}, reflect: true},
all: {attribute: 'all-attr', hasChanged, type: {fromAttribute, toAttribute}, reflect: true},
};

@@ -198,3 +198,3 @@ }

customAttr = 'customAttr';
shouldInvalidate = 10;
hasChanged = 10;
fromAttribute = 1;

@@ -204,6 +204,6 @@ toAttribute = 1;

updated = 0;
updateCount = 0;
update(changed: PropertyValues) {
this.updated++;
this.updateCount++;
super.update(changed);

@@ -219,7 +219,7 @@ }

await el.updateComplete;
assert.equal(el.updated, 1);
assert.equal(el.updateCount, 1);
assert.equal(el.noAttr, 'noAttr');
assert.equal(el.atTr, 'attr');
assert.equal(el.customAttr, 'customAttr');
assert.equal(el.shouldInvalidate, 10);
assert.equal(el.hasChanged, 10);
assert.equal(el.fromAttribute, 1);

@@ -237,3 +237,3 @@ assert.equal(el.toAttribute, 1);

await el.updateComplete;
assert.equal(el.updated, 2);
assert.equal(el.updateCount, 2);
assert.equal(el.noAttr, 'noAttr');

@@ -248,3 +248,3 @@ assert.equal(el.atTr, 'attr2');

await el.updateComplete;
assert.equal(el.updated, 3);
assert.equal(el.updateCount, 3);
assert.equal(el.all, 15);

@@ -254,21 +254,21 @@ assert.equal(el.getAttribute('all-attr'), '15-attr');

await el.updateComplete;
assert.equal(el.updated, 4);
assert.equal(el.updateCount, 4);
assert.equal(el.getAttribute('all-attr'), '16-attr');
assert.equal(el.all, 16);
el.shouldInvalidate = 5;
el.hasChanged = 5;
await el.updateComplete;
assert.equal(el.shouldInvalidate, 5);
assert.equal(el.updated, 4);
el.shouldInvalidate = 15;
assert.equal(el.hasChanged, 5);
assert.equal(el.updateCount, 4);
el.hasChanged = 15;
await el.updateComplete;
assert.equal(el.shouldInvalidate, 15);
assert.equal(el.updated, 5);
assert.equal(el.hasChanged, 15);
assert.equal(el.updateCount, 5);
el.setAttribute('all-attr', '5-attr');
await el.updateComplete;
assert.equal(el.all, 5);
assert.equal(el.updated, 5);
assert.equal(el.updateCount, 5);
el.all = 15;
await el.updateComplete;
assert.equal(el.all, 15);
assert.equal(el.updated, 6);
assert.equal(el.updateCount, 6);
});

@@ -278,3 +278,3 @@

const shouldInvalidate = (value: any, old: any) => old === undefined || value > old;
const hasChanged = (value: any, old: any) => old === undefined || value > old;
const fromAttribute = (value: any) => parseInt(value);

@@ -290,4 +290,4 @@ const toAttribute = (value: any) => `${value}-attr`;

customAttr = 'customAttr';
@property({shouldInvalidate})
shouldInvalidate = 10;
@property({hasChanged})
hasChanged = 10;
@property({type: fromAttribute})

@@ -297,9 +297,9 @@ fromAttribute = 1;

toAttribute = 1;
@property({attribute: 'all-attr', shouldInvalidate, type: {fromAttribute, toAttribute}, reflect: true})
@property({attribute: 'all-attr', hasChanged, type: {fromAttribute, toAttribute}, reflect: true})
all = 10;
updated = 0;
updateCount = 0;
update(changed: PropertyValues) {
this.updated++;
this.updateCount++;
super.update(changed);

@@ -315,7 +315,7 @@ }

await el.updateComplete;
assert.equal(el.updated, 1);
assert.equal(el.updateCount, 1);
assert.equal(el.noAttr, 'noAttr');
assert.equal(el.atTr, 'attr');
assert.equal(el.customAttr, 'customAttr');
assert.equal(el.shouldInvalidate, 10);
assert.equal(el.hasChanged, 10);
assert.equal(el.fromAttribute, 1);

@@ -333,3 +333,3 @@ assert.equal(el.toAttribute, 1);

await el.updateComplete;
assert.equal(el.updated, 2);
assert.equal(el.updateCount, 2);
assert.equal(el.noAttr, 'noAttr');

@@ -344,3 +344,3 @@ assert.equal(el.atTr, 'attr2');

await el.updateComplete;
assert.equal(el.updated, 3);
assert.equal(el.updateCount, 3);
assert.equal(el.all, 15);

@@ -350,23 +350,125 @@ assert.equal(el.getAttribute('all-attr'), '15-attr');

await el.updateComplete;
assert.equal(el.updated, 4);
assert.equal(el.updateCount, 4);
assert.equal(el.getAttribute('all-attr'), '16-attr');
assert.equal(el.all, 16);
el.shouldInvalidate = 5;
el.hasChanged = 5;
await el.updateComplete;
assert.equal(el.shouldInvalidate, 5);
assert.equal(el.updated, 4);
el.shouldInvalidate = 15;
assert.equal(el.hasChanged, 5);
assert.equal(el.updateCount, 4);
el.hasChanged = 15;
await el.updateComplete;
assert.equal(el.shouldInvalidate, 15);
assert.equal(el.updated, 5);
assert.equal(el.hasChanged, 15);
assert.equal(el.updateCount, 5);
el.setAttribute('all-attr', '5-attr');
await el.updateComplete;
assert.equal(el.all, 5);
assert.equal(el.updated, 5);
assert.equal(el.updateCount, 5);
el.all = 15;
await el.updateComplete;
assert.equal(el.all, 15);
assert.equal(el.updated, 6);
assert.equal(el.updateCount, 6);
});
test('can mix property options via decorator and via getter', async() => {
const hasChanged = (value: any, old: any) => old === undefined || value > old;
const fromAttribute = (value: any) => parseInt(value);
const toAttribute = (value: any) => `${value}-attr`;
class E extends LitElement {
@property({hasChanged})
hasChanged = 10;
@property({type: fromAttribute})
fromAttribute = 1;
@property({reflect: true, type: {toAttribute}})
toAttribute = 1;
@property({attribute: 'all-attr', hasChanged, type: {fromAttribute, toAttribute}, reflect: true})
all = 10;
updateCount = 0;
static get properties() {
return {
noAttr: {attribute: false},
atTr: {attribute: true},
customAttr: {attribute: 'custom', reflect: true},
};
}
noAttr: string|undefined;
atTr: string|undefined;
customAttr: string|undefined;
constructor() {
super();
this.noAttr = 'noAttr';
this.atTr = 'attr';
this.customAttr = 'customAttr';
}
update(changed: PropertyValues) {
this.updateCount++;
super.update(changed);
}
render() { return html``; }
}
customElements.define(generateElementName(), E);
const el = new E();
container.appendChild(el);
await el.updateComplete;
assert.equal(el.updateCount, 1);
assert.equal(el.noAttr, 'noAttr');
assert.equal(el.atTr, 'attr');
assert.equal(el.customAttr, 'customAttr');
assert.equal(el.hasChanged, 10);
assert.equal(el.fromAttribute, 1);
assert.equal(el.toAttribute, 1);
assert.equal(el.getAttribute('toattribute'), '1-attr');
assert.equal(el.all, 10);
assert.equal(el.getAttribute('all-attr'), '10-attr');
el.setAttribute('noattr', 'noAttr2');
el.setAttribute('attr', 'attr2');
el.setAttribute('custom', 'customAttr2');
el.setAttribute('fromattribute', '2attr');
el.toAttribute = 2;
el.all = 5;
await el.updateComplete;
assert.equal(el.updateCount, 2);
assert.equal(el.noAttr, 'noAttr');
assert.equal(el.atTr, 'attr2');
assert.equal(el.customAttr, 'customAttr2');
assert.equal(el.fromAttribute, 2);
assert.equal(el.toAttribute, 2);
assert.equal(el.getAttribute('toattribute'), '2-attr');
assert.equal(el.all, 5);
el.all = 15;
await el.updateComplete;
assert.equal(el.updateCount, 3);
assert.equal(el.all, 15);
assert.equal(el.getAttribute('all-attr'), '15-attr');
el.setAttribute('all-attr', '16-attr');
await el.updateComplete;
assert.equal(el.updateCount, 4);
assert.equal(el.getAttribute('all-attr'), '16-attr');
assert.equal(el.all, 16);
el.hasChanged = 5;
await el.updateComplete;
assert.equal(el.hasChanged, 5);
assert.equal(el.updateCount, 4);
el.hasChanged = 15;
await el.updateComplete;
assert.equal(el.hasChanged, 15);
assert.equal(el.updateCount, 5);
el.setAttribute('all-attr', '5-attr');
await el.updateComplete;
assert.equal(el.all, 5);
assert.equal(el.updateCount, 5);
el.all = 15;
await el.updateComplete;
assert.equal(el.all, 15);
assert.equal(el.updateCount, 6);
});
test('attributes deserialize from html', async() => {

@@ -435,3 +537,3 @@

}
updated = 0;
updateCount = 0;
foo = 5;

@@ -445,3 +547,3 @@ [zug] = 6;

update(changedProperties: PropertyValues) {
this.updated++;
this.updateCount++;
super.update(changedProperties);

@@ -455,3 +557,3 @@ }

await el.updateComplete;
assert.equal(el.updated, 1);
assert.equal(el.updateCount, 1);
assert.equal(el.foo, 5);

@@ -461,3 +563,3 @@ assert.equal(el[zug], 6);

await el.updateComplete;
assert.equal(el.updated, 2);
assert.equal(el.updateCount, 2);
assert.equal(el.foo, 55);

@@ -467,3 +569,3 @@ assert.equal(el[zug], 6);

await el.updateComplete;
assert.equal(el.updated, 3);
assert.equal(el.updateCount, 3);
assert.equal(el.foo, 55);

@@ -515,3 +617,3 @@ assert.equal(el[zug], 66);

const shouldInvalidate = (value: any, old: any) => old === undefined || value > old;
const hasChanged = (value: any, old: any) => old === undefined || value > old;
const fromAttribute = (value: any) => parseInt(value);

@@ -525,3 +627,3 @@ const toAttribute = (value: any) => `${value}-attr`;

customAttr: {},
shouldInvalidate: {},
hasChanged: {},
};

@@ -533,8 +635,8 @@ }

customAttr = 'customAttr';
shouldInvalidate = 10;
hasChanged = 10;
updated = 0;
updateCount = 0;
update(changed: PropertyValues) {
this.updated++;
this.updateCount++;
super.update(changed);

@@ -552,3 +654,3 @@ }

customAttr: {attribute: 'custom', reflect: true},
shouldInvalidate: {shouldInvalidate},
hasChanged: {hasChanged},
fromAttribute: {},

@@ -570,3 +672,3 @@ toAttribute: {},

toAttribute: {reflect: true, type: {toAttribute}},
all: {attribute: 'all-attr', shouldInvalidate, type: {fromAttribute, toAttribute}, reflect: true},
all: {attribute: 'all-attr', hasChanged, type: {fromAttribute, toAttribute}, reflect: true},
};

@@ -582,7 +684,7 @@ }

await el.updateComplete;
assert.equal(el.updated, 1);
assert.equal(el.updateCount, 1);
assert.equal(el.noAttr, 'noAttr');
assert.equal(el.atTr, 'attr');
assert.equal(el.customAttr, 'customAttr');
assert.equal(el.shouldInvalidate, 10);
assert.equal(el.hasChanged, 10);
assert.equal(el.fromAttribute, 1);

@@ -600,3 +702,3 @@ assert.equal(el.toAttribute, 1);

await el.updateComplete;
assert.equal(el.updated, 2);
assert.equal(el.updateCount, 2);
assert.equal(el.noAttr, 'noAttr');

@@ -611,3 +713,3 @@ assert.equal(el.atTr, 'attr2');

await el.updateComplete;
assert.equal(el.updated, 3);
assert.equal(el.updateCount, 3);
assert.equal(el.all, 15);

@@ -617,21 +719,21 @@ assert.equal(el.getAttribute('all-attr'), '15-attr');

await el.updateComplete;
assert.equal(el.updated, 4);
assert.equal(el.updateCount, 4);
assert.equal(el.getAttribute('all-attr'), '16-attr');
assert.equal(el.all, 16);
el.shouldInvalidate = 5;
el.hasChanged = 5;
await el.updateComplete;
assert.equal(el.shouldInvalidate, 5);
assert.equal(el.updated, 4);
el.shouldInvalidate = 15;
assert.equal(el.hasChanged, 5);
assert.equal(el.updateCount, 4);
el.hasChanged = 15;
await el.updateComplete;
assert.equal(el.shouldInvalidate, 15);
assert.equal(el.updated, 5);
assert.equal(el.hasChanged, 15);
assert.equal(el.updateCount, 5);
el.setAttribute('all-attr', '5-attr');
await el.updateComplete;
assert.equal(el.all, 5);
assert.equal(el.updated, 5);
assert.equal(el.updateCount, 5);
el.all = 15;
await el.updateComplete;
assert.equal(el.all, 15);
assert.equal(el.updated, 6);
assert.equal(el.updateCount, 6);

@@ -780,4 +882,4 @@ });

updatedValue = '';
updatedAttrValue = '';
updateCountValue = '';
updateCountAttrValue = '';

@@ -788,4 +890,4 @@ render() { return html``; }

super.update(props);
this.updatedValue = this.value;
this.updatedAttrValue = this.attrValue;
this.updateCountValue = this.value;
this.updateCountAttrValue = this.attrValue;
}

@@ -798,21 +900,21 @@ }

await el.updateComplete;
assert.equal(el.updatedValue, '1');
assert.equal(el.updatedAttrValue, 'attr');
assert.equal(el.updateCountValue, '1');
assert.equal(el.updateCountAttrValue, 'attr');
el.value = '2';
await el.updateComplete;
assert.equal(el.updatedValue, '2');
assert.equal(el.updatedAttrValue, 'attr');
assert.equal(el.updateCountValue, '2');
assert.equal(el.updateCountAttrValue, 'attr');
el.attrValue = 'attr2';
await el.updateComplete;
assert.equal(el.updatedValue, '2');
assert.equal(el.updatedAttrValue, 'attr2');
assert.equal(el.updateCountValue, '2');
assert.equal(el.updateCountAttrValue, 'attr2');
el.setAttribute('attrvalue', 'attr3');
await el.updateComplete;
assert.equal(el.updatedValue, '2');
assert.equal(el.updatedAttrValue, 'attr3');
assert.equal(el.updateCountValue, '2');
assert.equal(el.updateCountAttrValue, 'attr3');
el.value = '3';
el.setAttribute('attrvalue', 'attr4');
await el.updateComplete;
assert.equal(el.updatedValue, '3');
assert.equal(el.updatedAttrValue, 'attr4');
assert.equal(el.updateCountValue, '3');
assert.equal(el.updateCountAttrValue, 'attr4');
});

@@ -857,4 +959,5 @@

set bar(value) {
const old = this.bar;
this.__bar = Number(value);
this.invalidate();
this.requestUpdate('bar', old);
}

@@ -877,9 +980,9 @@

test('User defined accessor can use property options via `invalidateProperty`', async () => {
test('User defined accessor can use property options via `requestUpdate`', async () => {
const fromAttribute = (value: any) => parseInt(value);
const toAttribute = (value: any) => `${value}-attr`;
const shouldInvalidate = (value: any, old: any) => isNaN(old) || value > old;
const hasChanged = (value: any, old: any) => isNaN(old) || value > old;
class E extends LitElement {
updated = 0;
updateCount = 0;
__bar: any;

@@ -889,3 +992,3 @@

return {
bar: {attribute: 'attr-bar', reflect: true, type: {fromAttribute, toAttribute}, shouldInvalidate}
bar: {attribute: 'attr-bar', reflect: true, type: {fromAttribute, toAttribute}, hasChanged}
};

@@ -901,3 +1004,3 @@ }

super.update(changed);
this.updated++;
this.updateCount++;
}

@@ -910,3 +1013,3 @@

this.__bar = Number(value);
this.invalidateProperty('bar', old);
this.requestUpdate('bar', old);
}

@@ -921,3 +1024,3 @@

await el.updateComplete;
assert.equal(el.updated, 1);
assert.equal(el.updateCount, 1);
assert.equal(el.bar, 5);

@@ -927,3 +1030,3 @@ assert.equal(el.getAttribute('attr-bar'), `5-attr`);

await el.updateComplete;
assert.equal(el.updated, 2);
assert.equal(el.updateCount, 2);
assert.equal(el.bar, 7);

@@ -933,3 +1036,3 @@ assert.equal(el.getAttribute('attr-bar'), `7-attr`);

await el.updateComplete;
assert.equal(el.updated, 2);
assert.equal(el.updateCount, 2);
assert.equal(el.bar, 4);

@@ -939,3 +1042,3 @@ assert.equal(el.getAttribute('attr-bar'), `7-attr`);

await el.updateComplete;
assert.equal(el.updated, 2);
assert.equal(el.updateCount, 2);
assert.equal(el.bar, 3);

@@ -945,3 +1048,3 @@ assert.equal(el.getAttribute('attr-bar'), `3`);

test('updates/renders attributes, properties, and event listeners via lit-html',
test('updates/renders attributes, properties, and event listeners via `lit-html`',
async () => {

@@ -973,10 +1076,14 @@ class E extends LitElement {

test(
'firstRendered called when element first renders', async () => {
'`firstUpdated` called when element first updates', async () => {
class E extends LitElement {
wasUpdated = 0;
wasFirstRendered = 0;
@property()
foo = 1;
wasUpdatedCount = 0;
wasFirstUpdated = 0;
changedProperties: PropertyValues|undefined;
update(changedProperties: PropertyValues) {
this.wasUpdated++;
this.wasUpdatedCount++;
super.update(changedProperties);

@@ -987,4 +1094,5 @@ }

firstRendered() {
this.wasFirstRendered++;
firstUpdated(changedProperties: PropertyValues) {
this.changedProperties = changedProperties;
this.wasFirstUpdated++;
}

@@ -997,14 +1105,17 @@

await el.updateComplete;
assert.equal(el.wasUpdated, 1);
assert.equal(el.wasFirstRendered, 1);
await el.invalidate();
assert.equal(el.wasUpdated, 2);
assert.equal(el.wasFirstRendered, 1);
await el.invalidate();
assert.equal(el.wasUpdated, 3);
assert.equal(el.wasFirstRendered, 1);
const testMap = new Map();
testMap.set('foo', undefined);
assert.deepEqual(el.changedProperties, testMap);
assert.equal(el.wasUpdatedCount, 1);
assert.equal(el.wasFirstUpdated, 1);
await el.requestUpdate();
assert.equal(el.wasUpdatedCount, 2);
assert.equal(el.wasFirstUpdated, 1);
await el.requestUpdate();
assert.equal(el.wasUpdatedCount, 3);
assert.equal(el.wasFirstUpdated, 1);
});
test(
'render lifecycle order: shouldUpdate, update, render, firstRendered, after update, updateComplete', async () => {
'render lifecycle order', async () => {
class E extends LitElement {

@@ -1033,6 +1144,10 @@ static get properties() { return {

firstRendered() {
this.info.push('firstRendered');
firstUpdated() {
this.info.push('firstUpdated');
}
updated() {
this.info.push('updated');
}
}

@@ -1046,6 +1161,6 @@ customElements.define(generateElementName(), E);

el.info,
[ 'shouldUpdate', 'before-update', 'render', 'firstRendered', 'after-update', 'updateComplete' ]);
[ 'shouldUpdate', 'before-update', 'render', 'after-update', 'firstUpdated', 'updated', 'updateComplete' ]);
});
test('setting properties in update does not trigger invalidation', async () => {
test('setting properties in update does not trigger update', async () => {
class E extends LitElement {

@@ -1060,6 +1175,6 @@

foo = 0;
updated = 0;
updateCount = 0;
update(props: PropertyValues) {
this.updated++;
this.updateCount++;
this.foo++;

@@ -1079,3 +1194,3 @@ super.update(props);

assert.equal(el.foo, 1);
assert.equal(el.updated, 1);
assert.equal(el.updateCount, 1);
assert.equal(el.shadowRoot!.textContent, '1');

@@ -1085,3 +1200,3 @@ el.foo = 5;

assert.equal(el.foo, 6);
assert.equal(el.updated, 2);
assert.equal(el.updateCount, 2);
assert.equal(el.shadowRoot!.textContent, '6');

@@ -1146,3 +1261,3 @@ });

test('setting properties after `super.update` does trigger invalidation and does not block updateComplete', async () => {
test('can make properties for native accessors', async () => {
class E extends LitElement {

@@ -1152,12 +1267,70 @@

return {
id: {reflect: true},
name: {reflect: true},
title: {reflect: true},
foo: {}
};
}
name: string;
foo: string;
changedProperties: PropertyValues|undefined = undefined;
constructor() {
super();
this.id = 'id';
this.name = 'name';
this.title = 'title';
this.foo = 'foo';
}
update(changedProperties: PropertyValues) {
(this as any).zot = (this as any).foo + (this as any).bar;
super.update(changedProperties);
this.changedProperties = changedProperties;
}
render() {
return html`${this.id}-${this.name}-${this.title}-${this.foo}`;
}
}
customElements.define(generateElementName(), E);
const el = new E() as any;
container.appendChild(el);
await el.updateComplete;
const testMap = new Map();
testMap.set('id', undefined);
testMap.set('name', undefined);
testMap.set('title', undefined);
testMap.set('foo', undefined);
assert.deepEqual(el.changedProperties, testMap);
assert.equal(el.shadowRoot!.textContent, 'id-name-title-foo');
assert.equal((window as any).id, el);
el.id = 'id2';
el.name = 'name2';
await el.updateComplete;
assert.equal(el.shadowRoot!.textContent, 'id2-name2-title-foo');
assert.equal((window as any).id2, el);
});
test('setting properties in `updated` does trigger update and does not block updateComplete', async () => {
class E extends LitElement {
static get properties() {
return {
foo: {}
};
}
foo = 0;
updated = 0;
updateCount = 0;
fooMax = 2;
update(changed: PropertyValues) {
this.updated++;
this.updateCount++;
super.update(changed);
}
updated() {
if (this.foo < this.fooMax) {

@@ -1179,7 +1352,7 @@ this.foo++;

assert.equal(el.foo, 1);
assert.equal(el.updated, 1);
assert.equal(el.updateCount, 1);
result = await el.updateComplete;
assert.isFalse(result);
assert.equal(el.foo, 2);
assert.equal(el.updated, 2);
assert.equal(el.updateCount, 2);
result = await el.updateComplete;

@@ -1189,3 +1362,3 @@ assert.isTrue(result);

test('setting properties after `super.update` can await until updateComplete returns true', async () => {
test('setting properties in `updated` can await until updateComplete returns true', async () => {
class E extends LitElement {

@@ -1199,7 +1372,10 @@

foo = 0;
updated = 0;
updateCount = 0;
update(changed: PropertyValues) {
this.updated++;
this.updateCount++;
super.update(changed);
}
updated() {
if (this.foo < 10) {

@@ -1222,3 +1398,3 @@ this.foo++;

test('updateComplete can block properties set after `super.update`', async () => {
test('`updateComplete` can block properties set in `updated`', async () => {
class E extends LitElement {

@@ -1232,8 +1408,11 @@

foo = 1;
updated = 0;
updateCount = 0;
fooMax = 10;
update(changed: PropertyValues) {
this.updated++;
this.updateCount++;
super.update(changed);
}
updated() {
if (this.foo < this.fooMax) {

@@ -1248,7 +1427,4 @@ this.foo++;

get updateComplete() {
return (async () => {
while (!await super.updateComplete) {}
return true;
})();
get updateComplete(): Promise<any> {
return super.updateComplete.then((v) => v || this.updateComplete);
}

@@ -1263,6 +1439,6 @@

assert.equal(el.foo, 10);
assert.equal(el.updated, 10);
assert.equal(el.updateCount, 10);
});
test('can await promise in updateComplete', async () => {
test('can await promise in `updateComplete`', async () => {
class E extends LitElement {

@@ -1302,3 +1478,3 @@

test('can await sub-element updateComplete', async () => {
test('`requestUpdate` resolved at `updateComplete` time', async () => {
class E extends LitElement {

@@ -1312,3 +1488,3 @@

promiseFulfilled = false;
foo = 'hi';
foo = 0;

@@ -1331,2 +1507,37 @@ render() {

}
customElements.define(generateElementName(), E);
const el = new E();
container.appendChild(el);
let result = await el.updateComplete;
assert.isTrue(result);
assert.isTrue(el.promiseFulfilled);
el.promiseFulfilled = false;
result = await el.requestUpdate() as boolean;
assert.isTrue(result);
assert.isTrue(el.promiseFulfilled);
});
test('can await sub-element `updateComplete`', async () => {
class E extends LitElement {
static get properties() {
return {
foo: {}
};
}
promiseFulfilled = false;
foo = 'hi';
render() {
return html`${this.foo}`;
}
get updateComplete() {
return super.updateComplete.then(() => new Promise((resolve) => setTimeout(() => {
this.promiseFulfilled = true;
resolve(true);
}, 1)));
}
}
customElements.define('x-1224', E);

@@ -1342,3 +1553,3 @@

firstRendered() {
firstUpdated() {
this.inner = this.shadowRoot!.querySelector('x-1224');

@@ -1348,7 +1559,6 @@ }

get updateComplete() {
return (async () => {
await super.updateComplete;
return super.updateComplete.then(() => {
this.inner!.foo = 'yo';
return await this.inner!.updateComplete && await super.updateComplete;
})();
return this.inner!.updateComplete;
});
}

@@ -1435,3 +1645,3 @@

firstRendered() {
firstUpdated() {
this.inner = this.shadowRoot!.querySelector('x-2448');

@@ -1441,5 +1651,3 @@ }

get updateComplete() {
return (async () => {
return await super.updateComplete && await this.inner!.updateComplete;
})();
return super.updateComplete.then(() => this.inner!.updateComplete);
}

@@ -1446,0 +1654,0 @@

@@ -108,3 +108,7 @@ /**

const name = generateElementName();
customElements.define(name, class extends LitElement {
class E extends LitElement {
constructor() {
super(...arguments);
this.inner = null;
}
render() {

@@ -119,7 +123,13 @@ return html `

}
});
firstUpdated() {
this.inner = this.shadowRoot.querySelector('x-inner');
}
}
customElements.define(name, E);
const el = document.createElement(name);
container.appendChild(el);
// Workaround for Safari 9 Promise timing bugs.
await el.updateComplete && await el.inner.updateComplete;
await nextFrame();
const div = el.shadowRoot.querySelector('x-inner').shadowRoot.querySelector('div');
const div = el.inner.shadowRoot.querySelector('div');
assert.equal(getComputedStyleValue(div, 'border-top-width').trim(), '8px');

@@ -141,2 +151,6 @@ });

customElements.define(name1, class extends LitElement {
constructor() {
super(...arguments);
this.inner = null;
}
render() {

@@ -151,2 +165,5 @@ return html `

}
firstUpdated() {
this.inner = this.shadowRoot.querySelector('x-inner1');
}
});

@@ -169,2 +186,4 @@ const name2 = generateElementName();

let div;
// Workaround for Safari 9 Promise timing bugs.
await el.updateComplete;
await nextFrame();

@@ -175,2 +194,4 @@ const inner = el.shadowRoot.querySelector('x-inner1');

el2.shadowRoot.appendChild(inner);
// Workaround for Safari 9 Promise timing bugs.
await el.updateComplete;
await nextFrame();

@@ -192,3 +213,7 @@ assert.equal(getComputedStyleValue(div, 'border-top-width').trim(), '8px');

const name = generateElementName();
customElements.define(name, class extends LitElement {
class E extends LitElement {
constructor() {
super(...arguments);
this.inner = null;
}
render() {

@@ -205,5 +230,11 @@ return html `

}
});
firstUpdated() {
this.inner = this.shadowRoot.querySelector('x-inner2');
}
}
customElements.define(name, E);
const el = document.createElement(name);
container.appendChild(el);
// Workaround for Safari 9 Promise timing bugs.
await el.updateComplete && await el.inner.updateComplete;
await nextFrame();

@@ -250,3 +281,3 @@ const div = el.shadowRoot.querySelector('x-inner2').shadowRoot.querySelector('div');

border = `4px solid orange`;
el.invalidate();
el.requestUpdate();
await el.updateComplete;

@@ -253,0 +284,0 @@ assert.equal(getComputedStyleValue(div, 'border-top-width').trim(), '6px');

@@ -50,9 +50,9 @@ /**

});
test('invalidate waits until update/rendering', async () => {
test('`requestUpdate` waits until update/rendering', async () => {
class E extends LitElement {
constructor() {
super(...arguments);
this.updated = 0;
this.updateCount = 0;
}
render() { return html `${++this.updated}`; }
render() { return html `${++this.updateCount}`; }
}

@@ -62,16 +62,16 @@ customElements.define(generateElementName(), E);

container.appendChild(el);
await el.invalidate();
await el.requestUpdate();
assert.equal(stripExpressionDelimeters(el.shadowRoot.innerHTML), '1');
await el.invalidate();
await el.requestUpdate();
assert.equal(stripExpressionDelimeters(el.shadowRoot.innerHTML), '2');
await el.invalidate();
await el.requestUpdate();
assert.equal(stripExpressionDelimeters(el.shadowRoot.innerHTML), '3');
});
test('updateComplete waits for invalidate but does not trigger invalidation, async', async () => {
test('`updateComplete` waits for `requestUpdate` but does not trigger update, async', async () => {
class E extends LitElement {
constructor() {
super(...arguments);
this.updated = 0;
this.updateCount = 0;
}
render() { return html `${++this.updated}`; }
render() { return html `${++this.updateCount}`; }
}

@@ -85,3 +85,3 @@ customElements.define(generateElementName(), E);

assert.equal(stripExpressionDelimeters(el.shadowRoot.innerHTML), '1');
el.invalidate();
el.requestUpdate();
await el.updateComplete;

@@ -92,3 +92,3 @@ assert.equal(stripExpressionDelimeters(el.shadowRoot.innerHTML), '2');

});
test('shouldUpdate controls update/rendering', async () => {
test('`shouldUpdate` controls update/rendering', async () => {
class E extends LitElement {

@@ -98,6 +98,6 @@ constructor() {

this.needsUpdate = true;
this.updated = 0;
this.updateCount = 0;
}
shouldUpdate() { return this.needsUpdate; }
render() { return html `${++this.updated}`; }
render() { return html `${++this.updateCount}`; }
}

@@ -110,8 +110,8 @@ customElements.define(generateElementName(), E);

el.needsUpdate = false;
await el.invalidate();
await el.requestUpdate();
assert.equal(stripExpressionDelimeters(el.shadowRoot.innerHTML), '1');
el.needsUpdate = true;
await el.invalidate();
await el.requestUpdate();
assert.equal(stripExpressionDelimeters(el.shadowRoot.innerHTML), '2');
await el.invalidate();
await el.requestUpdate();
assert.equal(stripExpressionDelimeters(el.shadowRoot.innerHTML), '3');

@@ -145,3 +145,3 @@ });

test('property options', async () => {
const shouldInvalidate = (value, old) => old === undefined || value > old;
const hasChanged = (value, old) => old === undefined || value > old;
const fromAttribute = (value) => parseInt(value);

@@ -155,7 +155,7 @@ const toAttribute = (value) => `${value}-attr`;

this.customAttr = 'customAttr';
this.shouldInvalidate = 10;
this.hasChanged = 10;
this.fromAttribute = 1;
this.toAttribute = 1;
this.all = 10;
this.updated = 0;
this.updateCount = 0;
}

@@ -167,10 +167,10 @@ static get properties() {

customAttr: { attribute: 'custom', reflect: true },
shouldInvalidate: { shouldInvalidate },
hasChanged: { hasChanged },
fromAttribute: { type: fromAttribute },
toAttribute: { reflect: true, type: { toAttribute } },
all: { attribute: 'all-attr', shouldInvalidate, type: { fromAttribute, toAttribute }, reflect: true },
all: { attribute: 'all-attr', hasChanged, type: { fromAttribute, toAttribute }, reflect: true },
};
}
update(changed) {
this.updated++;
this.updateCount++;
super.update(changed);

@@ -184,7 +184,7 @@ }

await el.updateComplete;
assert.equal(el.updated, 1);
assert.equal(el.updateCount, 1);
assert.equal(el.noAttr, 'noAttr');
assert.equal(el.atTr, 'attr');
assert.equal(el.customAttr, 'customAttr');
assert.equal(el.shouldInvalidate, 10);
assert.equal(el.hasChanged, 10);
assert.equal(el.fromAttribute, 1);

@@ -202,3 +202,3 @@ assert.equal(el.toAttribute, 1);

await el.updateComplete;
assert.equal(el.updated, 2);
assert.equal(el.updateCount, 2);
assert.equal(el.noAttr, 'noAttr');

@@ -213,3 +213,3 @@ assert.equal(el.atTr, 'attr2');

await el.updateComplete;
assert.equal(el.updated, 3);
assert.equal(el.updateCount, 3);
assert.equal(el.all, 15);

@@ -219,24 +219,24 @@ assert.equal(el.getAttribute('all-attr'), '15-attr');

await el.updateComplete;
assert.equal(el.updated, 4);
assert.equal(el.updateCount, 4);
assert.equal(el.getAttribute('all-attr'), '16-attr');
assert.equal(el.all, 16);
el.shouldInvalidate = 5;
el.hasChanged = 5;
await el.updateComplete;
assert.equal(el.shouldInvalidate, 5);
assert.equal(el.updated, 4);
el.shouldInvalidate = 15;
assert.equal(el.hasChanged, 5);
assert.equal(el.updateCount, 4);
el.hasChanged = 15;
await el.updateComplete;
assert.equal(el.shouldInvalidate, 15);
assert.equal(el.updated, 5);
assert.equal(el.hasChanged, 15);
assert.equal(el.updateCount, 5);
el.setAttribute('all-attr', '5-attr');
await el.updateComplete;
assert.equal(el.all, 5);
assert.equal(el.updated, 5);
assert.equal(el.updateCount, 5);
el.all = 15;
await el.updateComplete;
assert.equal(el.all, 15);
assert.equal(el.updated, 6);
assert.equal(el.updateCount, 6);
});
test('property options via decorator', async () => {
const shouldInvalidate = (value, old) => old === undefined || value > old;
const hasChanged = (value, old) => old === undefined || value > old;
const fromAttribute = (value) => parseInt(value);

@@ -250,10 +250,10 @@ const toAttribute = (value) => `${value}-attr`;

this.customAttr = 'customAttr';
this.shouldInvalidate = 10;
this.hasChanged = 10;
this.fromAttribute = 1;
this.toAttribute = 1;
this.all = 10;
this.updated = 0;
this.updateCount = 0;
}
update(changed) {
this.updated++;
this.updateCount++;
super.update(changed);

@@ -273,4 +273,4 @@ }

__decorate([
property({ shouldInvalidate })
], E.prototype, "shouldInvalidate", void 0);
property({ hasChanged })
], E.prototype, "hasChanged", void 0);
__decorate([

@@ -283,3 +283,3 @@ property({ type: fromAttribute })

__decorate([
property({ attribute: 'all-attr', shouldInvalidate, type: { fromAttribute, toAttribute }, reflect: true })
property({ attribute: 'all-attr', hasChanged, type: { fromAttribute, toAttribute }, reflect: true })
], E.prototype, "all", void 0);

@@ -290,7 +290,7 @@ customElements.define(generateElementName(), E);

await el.updateComplete;
assert.equal(el.updated, 1);
assert.equal(el.updateCount, 1);
assert.equal(el.noAttr, 'noAttr');
assert.equal(el.atTr, 'attr');
assert.equal(el.customAttr, 'customAttr');
assert.equal(el.shouldInvalidate, 10);
assert.equal(el.hasChanged, 10);
assert.equal(el.fromAttribute, 1);

@@ -308,3 +308,3 @@ assert.equal(el.toAttribute, 1);

await el.updateComplete;
assert.equal(el.updated, 2);
assert.equal(el.updateCount, 2);
assert.equal(el.noAttr, 'noAttr');

@@ -319,3 +319,3 @@ assert.equal(el.atTr, 'attr2');

await el.updateComplete;
assert.equal(el.updated, 3);
assert.equal(el.updateCount, 3);
assert.equal(el.all, 15);

@@ -325,22 +325,119 @@ assert.equal(el.getAttribute('all-attr'), '15-attr');

await el.updateComplete;
assert.equal(el.updated, 4);
assert.equal(el.updateCount, 4);
assert.equal(el.getAttribute('all-attr'), '16-attr');
assert.equal(el.all, 16);
el.shouldInvalidate = 5;
el.hasChanged = 5;
await el.updateComplete;
assert.equal(el.shouldInvalidate, 5);
assert.equal(el.updated, 4);
el.shouldInvalidate = 15;
assert.equal(el.hasChanged, 5);
assert.equal(el.updateCount, 4);
el.hasChanged = 15;
await el.updateComplete;
assert.equal(el.shouldInvalidate, 15);
assert.equal(el.updated, 5);
assert.equal(el.hasChanged, 15);
assert.equal(el.updateCount, 5);
el.setAttribute('all-attr', '5-attr');
await el.updateComplete;
assert.equal(el.all, 5);
assert.equal(el.updated, 5);
assert.equal(el.updateCount, 5);
el.all = 15;
await el.updateComplete;
assert.equal(el.all, 15);
assert.equal(el.updated, 6);
assert.equal(el.updateCount, 6);
});
test('can mix property options via decorator and via getter', async () => {
const hasChanged = (value, old) => old === undefined || value > old;
const fromAttribute = (value) => parseInt(value);
const toAttribute = (value) => `${value}-attr`;
class E extends LitElement {
constructor() {
super();
this.hasChanged = 10;
this.fromAttribute = 1;
this.toAttribute = 1;
this.all = 10;
this.updateCount = 0;
this.noAttr = 'noAttr';
this.atTr = 'attr';
this.customAttr = 'customAttr';
}
static get properties() {
return {
noAttr: { attribute: false },
atTr: { attribute: true },
customAttr: { attribute: 'custom', reflect: true },
};
}
update(changed) {
this.updateCount++;
super.update(changed);
}
render() { return html ``; }
}
__decorate([
property({ hasChanged })
], E.prototype, "hasChanged", void 0);
__decorate([
property({ type: fromAttribute })
], E.prototype, "fromAttribute", void 0);
__decorate([
property({ reflect: true, type: { toAttribute } })
], E.prototype, "toAttribute", void 0);
__decorate([
property({ attribute: 'all-attr', hasChanged, type: { fromAttribute, toAttribute }, reflect: true })
], E.prototype, "all", void 0);
customElements.define(generateElementName(), E);
const el = new E();
container.appendChild(el);
await el.updateComplete;
assert.equal(el.updateCount, 1);
assert.equal(el.noAttr, 'noAttr');
assert.equal(el.atTr, 'attr');
assert.equal(el.customAttr, 'customAttr');
assert.equal(el.hasChanged, 10);
assert.equal(el.fromAttribute, 1);
assert.equal(el.toAttribute, 1);
assert.equal(el.getAttribute('toattribute'), '1-attr');
assert.equal(el.all, 10);
assert.equal(el.getAttribute('all-attr'), '10-attr');
el.setAttribute('noattr', 'noAttr2');
el.setAttribute('attr', 'attr2');
el.setAttribute('custom', 'customAttr2');
el.setAttribute('fromattribute', '2attr');
el.toAttribute = 2;
el.all = 5;
await el.updateComplete;
assert.equal(el.updateCount, 2);
assert.equal(el.noAttr, 'noAttr');
assert.equal(el.atTr, 'attr2');
assert.equal(el.customAttr, 'customAttr2');
assert.equal(el.fromAttribute, 2);
assert.equal(el.toAttribute, 2);
assert.equal(el.getAttribute('toattribute'), '2-attr');
assert.equal(el.all, 5);
el.all = 15;
await el.updateComplete;
assert.equal(el.updateCount, 3);
assert.equal(el.all, 15);
assert.equal(el.getAttribute('all-attr'), '15-attr');
el.setAttribute('all-attr', '16-attr');
await el.updateComplete;
assert.equal(el.updateCount, 4);
assert.equal(el.getAttribute('all-attr'), '16-attr');
assert.equal(el.all, 16);
el.hasChanged = 5;
await el.updateComplete;
assert.equal(el.hasChanged, 5);
assert.equal(el.updateCount, 4);
el.hasChanged = 15;
await el.updateComplete;
assert.equal(el.hasChanged, 15);
assert.equal(el.updateCount, 5);
el.setAttribute('all-attr', '5-attr');
await el.updateComplete;
assert.equal(el.all, 5);
assert.equal(el.updateCount, 5);
el.all = 15;
await el.updateComplete;
assert.equal(el.all, 15);
assert.equal(el.updateCount, 6);
});
test('attributes deserialize from html', async () => {

@@ -401,3 +498,3 @@ const fromAttribute = (value) => parseInt(value);

super(...arguments);
this.updated = 0;
this.updateCount = 0;
this.foo = 5;

@@ -416,3 +513,3 @@ this[_a] = 6;

update(changedProperties) {
this.updated++;
this.updateCount++;
super.update(changedProperties);

@@ -426,3 +523,3 @@ }

await el.updateComplete;
assert.equal(el.updated, 1);
assert.equal(el.updateCount, 1);
assert.equal(el.foo, 5);

@@ -432,3 +529,3 @@ assert.equal(el[zug], 6);

await el.updateComplete;
assert.equal(el.updated, 2);
assert.equal(el.updateCount, 2);
assert.equal(el.foo, 55);

@@ -438,3 +535,3 @@ assert.equal(el[zug], 6);

await el.updateComplete;
assert.equal(el.updated, 3);
assert.equal(el.updateCount, 3);
assert.equal(el.foo, 55);

@@ -476,3 +573,3 @@ assert.equal(el[zug], 66);

test('property options compose when subclassing', async () => {
const shouldInvalidate = (value, old) => old === undefined || value > old;
const hasChanged = (value, old) => old === undefined || value > old;
const fromAttribute = (value) => parseInt(value);

@@ -486,4 +583,4 @@ const toAttribute = (value) => `${value}-attr`;

this.customAttr = 'customAttr';
this.shouldInvalidate = 10;
this.updated = 0;
this.hasChanged = 10;
this.updateCount = 0;
}

@@ -495,7 +592,7 @@ static get properties() {

customAttr: {},
shouldInvalidate: {},
hasChanged: {},
};
}
update(changed) {
this.updated++;
this.updateCount++;
super.update(changed);

@@ -516,3 +613,3 @@ }

customAttr: { attribute: 'custom', reflect: true },
shouldInvalidate: { shouldInvalidate },
hasChanged: { hasChanged },
fromAttribute: {},

@@ -528,3 +625,3 @@ toAttribute: {},

toAttribute: { reflect: true, type: { toAttribute } },
all: { attribute: 'all-attr', shouldInvalidate, type: { fromAttribute, toAttribute }, reflect: true },
all: { attribute: 'all-attr', hasChanged, type: { fromAttribute, toAttribute }, reflect: true },
};

@@ -537,7 +634,7 @@ }

await el.updateComplete;
assert.equal(el.updated, 1);
assert.equal(el.updateCount, 1);
assert.equal(el.noAttr, 'noAttr');
assert.equal(el.atTr, 'attr');
assert.equal(el.customAttr, 'customAttr');
assert.equal(el.shouldInvalidate, 10);
assert.equal(el.hasChanged, 10);
assert.equal(el.fromAttribute, 1);

@@ -555,3 +652,3 @@ assert.equal(el.toAttribute, 1);

await el.updateComplete;
assert.equal(el.updated, 2);
assert.equal(el.updateCount, 2);
assert.equal(el.noAttr, 'noAttr');

@@ -566,3 +663,3 @@ assert.equal(el.atTr, 'attr2');

await el.updateComplete;
assert.equal(el.updated, 3);
assert.equal(el.updateCount, 3);
assert.equal(el.all, 15);

@@ -572,21 +669,21 @@ assert.equal(el.getAttribute('all-attr'), '15-attr');

await el.updateComplete;
assert.equal(el.updated, 4);
assert.equal(el.updateCount, 4);
assert.equal(el.getAttribute('all-attr'), '16-attr');
assert.equal(el.all, 16);
el.shouldInvalidate = 5;
el.hasChanged = 5;
await el.updateComplete;
assert.equal(el.shouldInvalidate, 5);
assert.equal(el.updated, 4);
el.shouldInvalidate = 15;
assert.equal(el.hasChanged, 5);
assert.equal(el.updateCount, 4);
el.hasChanged = 15;
await el.updateComplete;
assert.equal(el.shouldInvalidate, 15);
assert.equal(el.updated, 5);
assert.equal(el.hasChanged, 15);
assert.equal(el.updateCount, 5);
el.setAttribute('all-attr', '5-attr');
await el.updateComplete;
assert.equal(el.all, 5);
assert.equal(el.updated, 5);
assert.equal(el.updateCount, 5);
el.all = 15;
await el.updateComplete;
assert.equal(el.all, 15);
assert.equal(el.updated, 6);
assert.equal(el.updateCount, 6);
});

@@ -717,4 +814,4 @@ test('superclass properties not affected by subclass', async () => {

this.attrValue = 'attr';
this.updatedValue = '';
this.updatedAttrValue = '';
this.updateCountValue = '';
this.updateCountAttrValue = '';
}

@@ -730,4 +827,4 @@ static get properties() {

super.update(props);
this.updatedValue = this.value;
this.updatedAttrValue = this.attrValue;
this.updateCountValue = this.value;
this.updateCountAttrValue = this.attrValue;
}

@@ -740,21 +837,21 @@ }

await el.updateComplete;
assert.equal(el.updatedValue, '1');
assert.equal(el.updatedAttrValue, 'attr');
assert.equal(el.updateCountValue, '1');
assert.equal(el.updateCountAttrValue, 'attr');
el.value = '2';
await el.updateComplete;
assert.equal(el.updatedValue, '2');
assert.equal(el.updatedAttrValue, 'attr');
assert.equal(el.updateCountValue, '2');
assert.equal(el.updateCountAttrValue, 'attr');
el.attrValue = 'attr2';
await el.updateComplete;
assert.equal(el.updatedValue, '2');
assert.equal(el.updatedAttrValue, 'attr2');
assert.equal(el.updateCountValue, '2');
assert.equal(el.updateCountAttrValue, 'attr2');
el.setAttribute('attrvalue', 'attr3');
await el.updateComplete;
assert.equal(el.updatedValue, '2');
assert.equal(el.updatedAttrValue, 'attr3');
assert.equal(el.updateCountValue, '2');
assert.equal(el.updateCountAttrValue, 'attr3');
el.value = '3';
el.setAttribute('attrvalue', 'attr4');
await el.updateComplete;
assert.equal(el.updatedValue, '3');
assert.equal(el.updatedAttrValue, 'attr4');
assert.equal(el.updateCountValue, '3');
assert.equal(el.updateCountAttrValue, 'attr4');
});

@@ -792,4 +889,5 @@ test('updates/renders changes when attributes change', async () => {

set bar(value) {
const old = this.bar;
this.__bar = Number(value);
this.invalidate();
this.requestUpdate('bar', old);
}

@@ -810,10 +908,10 @@ render() {

});
test('User defined accessor can use property options via `invalidateProperty`', async () => {
test('User defined accessor can use property options via `requestUpdate`', async () => {
const fromAttribute = (value) => parseInt(value);
const toAttribute = (value) => `${value}-attr`;
const shouldInvalidate = (value, old) => isNaN(old) || value > old;
const hasChanged = (value, old) => isNaN(old) || value > old;
class E extends LitElement {
constructor() {
super();
this.updated = 0;
this.updateCount = 0;
this.bar = 5;

@@ -823,3 +921,3 @@ }

return {
bar: { attribute: 'attr-bar', reflect: true, type: { fromAttribute, toAttribute }, shouldInvalidate }
bar: { attribute: 'attr-bar', reflect: true, type: { fromAttribute, toAttribute }, hasChanged }
};

@@ -829,3 +927,3 @@ }

super.update(changed);
this.updated++;
this.updateCount++;
}

@@ -836,3 +934,3 @@ get bar() { return this.__bar; }

this.__bar = Number(value);
this.invalidateProperty('bar', old);
this.requestUpdate('bar', old);
}

@@ -845,3 +943,3 @@ render() { return html ``; }

await el.updateComplete;
assert.equal(el.updated, 1);
assert.equal(el.updateCount, 1);
assert.equal(el.bar, 5);

@@ -851,3 +949,3 @@ assert.equal(el.getAttribute('attr-bar'), `5-attr`);

await el.updateComplete;
assert.equal(el.updated, 2);
assert.equal(el.updateCount, 2);
assert.equal(el.bar, 7);

@@ -857,3 +955,3 @@ assert.equal(el.getAttribute('attr-bar'), `7-attr`);

await el.updateComplete;
assert.equal(el.updated, 2);
assert.equal(el.updateCount, 2);
assert.equal(el.bar, 4);

@@ -863,7 +961,7 @@ assert.equal(el.getAttribute('attr-bar'), `7-attr`);

await el.updateComplete;
assert.equal(el.updated, 2);
assert.equal(el.updateCount, 2);
assert.equal(el.bar, 3);
assert.equal(el.getAttribute('attr-bar'), `3`);
});
test('updates/renders attributes, properties, and event listeners via lit-html', async () => {
test('updates/renders attributes, properties, and event listeners via `lit-html`', async () => {
class E extends LitElement {

@@ -888,18 +986,23 @@ render() {

});
test('firstRendered called when element first renders', async () => {
test('`firstUpdated` called when element first updates', async () => {
class E extends LitElement {
constructor() {
super(...arguments);
this.wasUpdated = 0;
this.wasFirstRendered = 0;
this.foo = 1;
this.wasUpdatedCount = 0;
this.wasFirstUpdated = 0;
}
update(changedProperties) {
this.wasUpdated++;
this.wasUpdatedCount++;
super.update(changedProperties);
}
render() { return html ``; }
firstRendered() {
this.wasFirstRendered++;
firstUpdated(changedProperties) {
this.changedProperties = changedProperties;
this.wasFirstUpdated++;
}
}
__decorate([
property()
], E.prototype, "foo", void 0);
customElements.define(generateElementName(), E);

@@ -909,12 +1012,15 @@ const el = new E();

await el.updateComplete;
assert.equal(el.wasUpdated, 1);
assert.equal(el.wasFirstRendered, 1);
await el.invalidate();
assert.equal(el.wasUpdated, 2);
assert.equal(el.wasFirstRendered, 1);
await el.invalidate();
assert.equal(el.wasUpdated, 3);
assert.equal(el.wasFirstRendered, 1);
const testMap = new Map();
testMap.set('foo', undefined);
assert.deepEqual(el.changedProperties, testMap);
assert.equal(el.wasUpdatedCount, 1);
assert.equal(el.wasFirstUpdated, 1);
await el.requestUpdate();
assert.equal(el.wasUpdatedCount, 2);
assert.equal(el.wasFirstUpdated, 1);
await el.requestUpdate();
assert.equal(el.wasUpdatedCount, 3);
assert.equal(el.wasFirstUpdated, 1);
});
test('render lifecycle order: shouldUpdate, update, render, firstRendered, after update, updateComplete', async () => {
test('render lifecycle order', async () => {
class E extends LitElement {

@@ -943,5 +1049,8 @@ constructor() {

}
firstRendered() {
this.info.push('firstRendered');
firstUpdated() {
this.info.push('firstUpdated');
}
updated() {
this.info.push('updated');
}
}

@@ -953,5 +1062,5 @@ customElements.define(generateElementName(), E);

el.info.push('updateComplete');
assert.deepEqual(el.info, ['shouldUpdate', 'before-update', 'render', 'firstRendered', 'after-update', 'updateComplete']);
assert.deepEqual(el.info, ['shouldUpdate', 'before-update', 'render', 'after-update', 'firstUpdated', 'updated', 'updateComplete']);
});
test('setting properties in update does not trigger invalidation', async () => {
test('setting properties in update does not trigger update', async () => {
class E extends LitElement {

@@ -962,3 +1071,3 @@ constructor() {

this.foo = 0;
this.updated = 0;
this.updateCount = 0;
}

@@ -971,3 +1080,3 @@ static get properties() {

update(props) {
this.updated++;
this.updateCount++;
this.foo++;

@@ -985,3 +1094,3 @@ super.update(props);

assert.equal(el.foo, 1);
assert.equal(el.updated, 1);
assert.equal(el.updateCount, 1);
assert.equal(el.shadowRoot.textContent, '1');

@@ -991,3 +1100,3 @@ el.foo = 5;

assert.equal(el.foo, 6);
assert.equal(el.updated, 2);
assert.equal(el.updateCount, 2);
assert.equal(el.shadowRoot.textContent, '6');

@@ -1048,8 +1157,53 @@ });

});
test('setting properties after `super.update` does trigger invalidation and does not block updateComplete', async () => {
test('can make properties for native accessors', async () => {
class E extends LitElement {
constructor() {
super();
this.changedProperties = undefined;
this.id = 'id';
this.name = 'name';
this.title = 'title';
this.foo = 'foo';
}
static get properties() {
return {
id: { reflect: true },
name: { reflect: true },
title: { reflect: true },
foo: {}
};
}
update(changedProperties) {
this.zot = this.foo + this.bar;
super.update(changedProperties);
this.changedProperties = changedProperties;
}
render() {
return html `${this.id}-${this.name}-${this.title}-${this.foo}`;
}
}
customElements.define(generateElementName(), E);
const el = new E();
container.appendChild(el);
await el.updateComplete;
const testMap = new Map();
testMap.set('id', undefined);
testMap.set('name', undefined);
testMap.set('title', undefined);
testMap.set('foo', undefined);
assert.deepEqual(el.changedProperties, testMap);
assert.equal(el.shadowRoot.textContent, 'id-name-title-foo');
assert.equal(window.id, el);
el.id = 'id2';
el.name = 'name2';
await el.updateComplete;
assert.equal(el.shadowRoot.textContent, 'id2-name2-title-foo');
assert.equal(window.id2, el);
});
test('setting properties in `updated` does trigger update and does not block updateComplete', async () => {
class E extends LitElement {
constructor() {
super(...arguments);
this.foo = 0;
this.updated = 0;
this.updateCount = 0;
this.fooMax = 2;

@@ -1063,4 +1217,6 @@ }

update(changed) {
this.updated++;
this.updateCount++;
super.update(changed);
}
updated() {
if (this.foo < this.fooMax) {

@@ -1080,11 +1236,11 @@ this.foo++;

assert.equal(el.foo, 1);
assert.equal(el.updated, 1);
assert.equal(el.updateCount, 1);
result = await el.updateComplete;
assert.isFalse(result);
assert.equal(el.foo, 2);
assert.equal(el.updated, 2);
assert.equal(el.updateCount, 2);
result = await el.updateComplete;
assert.isTrue(result);
});
test('setting properties after `super.update` can await until updateComplete returns true', async () => {
test('setting properties in `updated` can await until updateComplete returns true', async () => {
class E extends LitElement {

@@ -1094,3 +1250,3 @@ constructor() {

this.foo = 0;
this.updated = 0;
this.updateCount = 0;
}

@@ -1103,4 +1259,6 @@ static get properties() {

update(changed) {
this.updated++;
this.updateCount++;
super.update(changed);
}
updated() {
if (this.foo < 10) {

@@ -1120,3 +1278,3 @@ this.foo++;

});
test('updateComplete can block properties set after `super.update`', async () => {
test('`updateComplete` can block properties set in `updated`', async () => {
class E extends LitElement {

@@ -1126,3 +1284,3 @@ constructor() {

this.foo = 1;
this.updated = 0;
this.updateCount = 0;
this.fooMax = 10;

@@ -1136,4 +1294,6 @@ }

update(changed) {
this.updated++;
this.updateCount++;
super.update(changed);
}
updated() {
if (this.foo < this.fooMax) {

@@ -1147,6 +1307,3 @@ this.foo++;

get updateComplete() {
return (async () => {
while (!await super.updateComplete) { }
return true;
})();
return super.updateComplete.then((v) => v || this.updateComplete);
}

@@ -1160,5 +1317,5 @@ }

assert.equal(el.foo, 10);
assert.equal(el.updated, 10);
assert.equal(el.updateCount, 10);
});
test('can await promise in updateComplete', async () => {
test('can await promise in `updateComplete`', async () => {
class E extends LitElement {

@@ -1196,3 +1353,3 @@ constructor() {

});
test('can await sub-element updateComplete', async () => {
test('`requestUpdate` resolved at `updateComplete` time', async () => {
class E extends LitElement {

@@ -1202,3 +1359,3 @@ constructor() {

this.promiseFulfilled = false;
this.foo = 'hi';
this.foo = 0;
}

@@ -1224,2 +1381,35 @@ static get properties() {

}
customElements.define(generateElementName(), E);
const el = new E();
container.appendChild(el);
let result = await el.updateComplete;
assert.isTrue(result);
assert.isTrue(el.promiseFulfilled);
el.promiseFulfilled = false;
result = await el.requestUpdate();
assert.isTrue(result);
assert.isTrue(el.promiseFulfilled);
});
test('can await sub-element `updateComplete`', async () => {
class E extends LitElement {
constructor() {
super(...arguments);
this.promiseFulfilled = false;
this.foo = 'hi';
}
static get properties() {
return {
foo: {}
};
}
render() {
return html `${this.foo}`;
}
get updateComplete() {
return super.updateComplete.then(() => new Promise((resolve) => setTimeout(() => {
this.promiseFulfilled = true;
resolve(true);
}, 1)));
}
}
customElements.define('x-1224', E);

@@ -1234,11 +1424,10 @@ class F extends LitElement {

}
firstRendered() {
firstUpdated() {
this.inner = this.shadowRoot.querySelector('x-1224');
}
get updateComplete() {
return (async () => {
await super.updateComplete;
return super.updateComplete.then(() => {
this.inner.foo = 'yo';
return await this.inner.updateComplete && await super.updateComplete;
})();
return this.inner.updateComplete;
});
}

@@ -1321,9 +1510,7 @@ }

}
firstRendered() {
firstUpdated() {
this.inner = this.shadowRoot.querySelector('x-2448');
}
get updateComplete() {
return (async () => {
return await super.updateComplete && await this.inner.updateComplete;
})();
return super.updateComplete.then(() => this.inner.updateComplete);
}

@@ -1330,0 +1517,0 @@ }

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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