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.6 to 0.6.0

CHANGELOG.md

2

demo/ts-element.js

@@ -7,3 +7,3 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {

};
import { LitElement, html, property } from '../lit-element.js';
import { html, LitElement, property } from '../lit-element.js';
class TSElement extends LitElement {

@@ -10,0 +10,0 @@ constructor() {

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

/**
* Deserializing function called to convert an attribute value to a property value.
* Deserializing function called to convert an attribute value to a property
* value.
*/
fromAttribute?(value: string): T;
/**
* Serializing function called to convert a property value to an attribute value.
* Serializing function called to convert a property value to an attribute
* value.
*/

@@ -36,14 +38,16 @@ toAttribute?(value: T): string | null;

* 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'`).
* 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'`).
*/
attribute?: boolean | string;
/**
* 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
* a the property value. If it's an object, it can have keys for `fromAttribute` and
* `toAttribute` where `fromAttribute` is the deserialize function and `toAttribute`
* is a serialize function used to set the property to an attribute. If no `toAttribute`
* function is provided and `reflect` is set to `true`, the property value is set
* directly to the attribute.
* 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 a the property value. If it's an object, it can have keys
* for `fromAttribute` and `toAttribute` where `fromAttribute` is the
* deserialize function and `toAttribute` is a serialize function used to set
* the property to an attribute. If no `toAttribute` function is provided and
* `reflect` is set to `true`, the property value is set directly to the
* attribute.
*/

@@ -55,10 +59,10 @@ type?: AttributeType<T>;

* attribute name determined according to the rules for the `attribute`
* propety option and the value of the property serialized using the rules from
* the `type` property option.
* property option and the value of the property serialized using the rules
* from the `type` property option.
*/
reflect?: boolean;
/**
* 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.
* 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.
*/

@@ -76,7 +80,2 @@ hasChanged?(value: T, oldValue: T): boolean;

export declare type PropertyValues = Map<PropertyKey, unknown>;
/**
* Decorator which creates a property. Optionally a `PropertyDeclaration` object
* can be supplied to describe how the property should be configured.
*/
export declare const property: (options?: PropertyDeclaration<any> | undefined) => (proto: Object, name: string) => void;
export interface HasChanged {

@@ -169,4 +168,4 @@ (value: unknown, old: unknown): boolean;

/**
* Performs element initialization. By default this calls `createRenderRoot` to
* create the element `renderRoot` node and captures any pre-set values for
* Performs element initialization. By default this calls `createRenderRoot`
* to create the element `renderRoot` node and captures any pre-set values for
* registered properties.

@@ -178,4 +177,10 @@ */

* Otherwise these would shadow the accessor and break these properties.
* The properties are stored in a Map which is played back after the constructor
* runs.
* The properties are stored in a Map which is played back after the
* constructor runs. Note, on very old versions of Safari (<=9) or Chrome
* (<=41), properties created for native platform properties like (`id` or
* `name`) may not have default values set in the element constructor. On
* these browsers native properties appear on instances and therefore their
* default value will overwrite any element default (e.g. if the element sets
* this.id = 'id' in the constructor, the 'id' will become '' since this is
* the native platform default).
*/

@@ -208,6 +213,7 @@ private _saveInstanceProperties;

* 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.
* 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.
*

@@ -239,9 +245,9 @@ * @param name {PropertyKey} (optional) name of requesting property

/**
* 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 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 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. The Promise result is `false` if
* a property was 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.
*

@@ -262,4 +268,5 @@ * @returns {Promise} The Promise returns a boolean that indicates if the

* 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.
* 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.
*

@@ -266,0 +273,0 @@ * * @param _changedProperties Map of changed properties with old values

@@ -14,9 +14,2 @@ /**

*/
/**
* Decorator which creates a property. Optionally a `PropertyDeclaration` object
* can be supplied to describe how the property should be configured.
*/
export const property = (options) => (proto, name) => {
proto.constructor.createProperty(name, options);
};
// serializer/deserializers for boolean attribute

@@ -98,3 +91,4 @@ const fromBooleanAttribute = (value) => value !== null;

this._classProperties.set(name, options);
// Allow user defined accessors by not replacing an existing own-property accessor.
// Allow user defined accessors by not replacing an existing own-property
// accessor.
if (this.prototype.hasOwnProperty(name)) {

@@ -105,5 +99,3 @@ return;

Object.defineProperty(this.prototype, name, {
get() {
return this[key];
},
get() { return this[key]; },
set(value) {

@@ -137,6 +129,11 @@ const oldValue = this[name];

// support symbols in properties (IE11 does not support this)
const propKeys = [...Object.getOwnPropertyNames(props),
...(typeof Object.getOwnPropertySymbols === 'function') ? Object.getOwnPropertySymbols(props) : []];
const propKeys = [
...Object.getOwnPropertyNames(props),
...(typeof Object.getOwnPropertySymbols === 'function')
? Object.getOwnPropertySymbols(props)
: []
];
for (const p of propKeys) {
// note, use of `any` is due to TypeSript lack of support for symbol in index types
// note, use of `any` is due to TypeSript lack of support for symbol in
// index types
this.createProperty(p, props[p]);

@@ -150,4 +147,8 @@ }

const attribute = options !== undefined && options.attribute;
return attribute === false ? undefined : (typeof attribute === 'string' ?
attribute : (typeof name === 'string' ? name.toLowerCase() : undefined));
return attribute === false
? undefined
: (typeof attribute === 'string'
? attribute
: (typeof name === 'string' ? name.toLowerCase()
: undefined));
}

@@ -173,4 +174,5 @@ /**

// Note: special case `Boolean` so users can use it as a `type`.
const fromAttribute = type === Boolean ? fromBooleanAttribute :
(typeof type === 'function' ? type : type.fromAttribute);
const fromAttribute = type === Boolean
? fromBooleanAttribute
: (typeof type === 'function' ? type : type.fromAttribute);
return fromAttribute ? fromAttribute(value) : value;

@@ -190,9 +192,12 @@ }

// Note: special case `Boolean` so users can use it as a `type`.
const toAttribute = options.type === Boolean ? toBooleanAttribute :
(options.type && options.type.toAttribute || String);
const toAttribute = options.type === Boolean
? toBooleanAttribute
: (options.type &&
options.type.toAttribute ||
String);
return toAttribute(value);
}
/**
* Performs element initialization. By default this calls `createRenderRoot` to
* create the element `renderRoot` node and captures any pre-set values for
* Performs element initialization. By default this calls `createRenderRoot`
* to create the element `renderRoot` node and captures any pre-set values for
* registered properties.

@@ -207,7 +212,14 @@ */

* Otherwise these would shadow the accessor and break these properties.
* The properties are stored in a Map which is played back after the constructor
* runs.
* The properties are stored in a Map which is played back after the
* constructor runs. Note, on very old versions of Safari (<=9) or Chrome
* (<=41), properties created for native platform properties like (`id` or
* `name`) may not have default values set in the element constructor. On
* these browsers native properties appear on instances and therefore their
* default value will overwrite any element default (e.g. if the element sets
* this.id = 'id' in the constructor, the 'id' will become '' since this is
* the native platform default).
*/
_saveInstanceProperties() {
for (const [p] of this.constructor._classProperties) {
for (const [p] of this.constructor
._classProperties) {
if (this.hasOwnProperty(p)) {

@@ -297,3 +309,4 @@ const value = this[p];

const options = ctor._classProperties.get(propName);
this[propName] = ctor._propertyValueFromAttribute(value, options);
this[propName] =
ctor._propertyValueFromAttribute(value, options);
}

@@ -305,6 +318,7 @@ }

* 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.
* 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.
*

@@ -317,3 +331,4 @@ * @param name {PropertyKey} (optional) name of requesting property

if (name !== undefined) {
const options = this.constructor._classProperties.get(name) ||
const options = this.constructor
._classProperties.get(name) ||
defaultPropertyDeclaration;

@@ -331,3 +346,4 @@ return this._requestPropertyUpdate(name, oldValue, options);

_requestPropertyUpdate(name, oldValue, options) {
if (!this.constructor._valueHasChanged(this[name], oldValue, options.hasChanged)) {
if (!this.constructor
._valueHasChanged(this[name], oldValue, options.hasChanged)) {
return this.updateComplete;

@@ -380,5 +396,5 @@ }

this.update(changedProperties);
const needsFirstUpdate = !(this._updateState & STATE_HAS_UPDATED);
this._markUpdated();
if (needsFirstUpdate) {
if (!(this._updateState & STATE_HAS_UPDATED)) {
this._updateState = this._updateState | STATE_HAS_UPDATED;
this.firstUpdated(changedProperties);

@@ -394,12 +410,12 @@ }

this._changedProperties = new Map();
this._updateState = this._updateState & ~STATE_UPDATE_REQUESTED | STATE_HAS_UPDATED;
this._updateState = this._updateState & ~STATE_UPDATE_REQUESTED;
}
/**
* 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 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 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. The Promise result is `false` if
* a property was 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.
*

@@ -409,5 +425,3 @@ * @returns {Promise} The Promise returns a boolean that indicates if the

*/
get updateComplete() {
return this._updatePromise;
}
get updateComplete() { return this._updatePromise; }
/**

@@ -425,4 +439,5 @@ * Controls whether or not `update` should be called when the element requests

* 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.
* 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.
*

@@ -432,3 +447,4 @@ * * @param _changedProperties Map of changed properties with old values

update(_changedProperties) {
if (this._reflectingProperties !== undefined && this._reflectingProperties.size > 0) {
if (this._reflectingProperties !== undefined &&
this._reflectingProperties.size > 0) {
for (const [k, v] of this._reflectingProperties) {

@@ -435,0 +451,0 @@ this._propertyToAttribute(k, this[k], v);

@@ -14,10 +14,12 @@ /**

*/
import { TemplateResult } from 'lit-html';
import { render } from 'lit-html/lib/shady-render';
import { TemplateResult } from 'lit-html';
import { UpdatingElement, PropertyValues } from './lib/updating-element.js';
import { PropertyValues, UpdatingElement } from './lib/updating-element.js';
export * from './lib/updating-element.js';
export * from './lib/decorators.js';
export { html, svg } from 'lit-html/lit-html';
export declare abstract class LitElement extends UpdatingElement {
/**
* Render method used to render the lit-html TemplateResult to the element's DOM.
* Render method used to render the lit-html TemplateResult to the element's
* DOM.
* @param {TemplateResult} Template to render.

@@ -24,0 +26,0 @@ * @param {Element|DocumentFragment} Node into which to render.

@@ -1,17 +0,5 @@

/**
* @license
* Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at
* http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at
* http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at
* http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at
* http://polymer.github.io/PATENTS.txt
*/
import { render } from 'lit-html/lib/shady-render';
import { UpdatingElement } from './lib/updating-element.js';
export * from './lib/updating-element.js';
export * from './lib/decorators.js';
export { html, svg } from 'lit-html/lit-html';

@@ -28,3 +16,4 @@ export class LitElement extends UpdatingElement {

if (typeof this.render === 'function') {
this.constructor.render(this.render(), this.renderRoot, this.localName);
this.constructor
.render(this.render(), this.renderRoot, this.localName);
}

@@ -37,3 +26,4 @@ else {

/**
* Render method used to render the lit-html TemplateResult to the element's DOM.
* Render method used to render the lit-html TemplateResult to the element's
* DOM.
* @param {TemplateResult} Template to render.

@@ -40,0 +30,0 @@ * @param {Element|DocumentFragment} Node into which to render.

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

@@ -26,3 +26,3 @@ "license": "BSD-3-Clause",

"@types/mocha": "^5.2.4",
"@webcomponents/webcomponentsjs": "^2.1.0",
"@webcomponents/webcomponentsjs": "^2.1.1",
"chai": "^4.0.2",

@@ -32,7 +32,7 @@ "mocha": "^5.0.5",

"rollup-plugin-filesize": "^4.0.1",
"rollup-plugin-node-resolve": "^3.3.0",
"rollup-plugin-node-resolve": "^3.4.0",
"rollup-plugin-terser": "^1.0.1",
"tslint": "^5.7.0",
"typedoc": "^0.8.0",
"typescript": "^3.0.1",
"typescript": "^3.0.3",
"uglify-es": "^3.3.9",

@@ -44,3 +44,3 @@ "wct-browser-legacy": "^1.0.1",

"dependencies": {
"lit-html": "^0.11.0"
"lit-html": "^0.11.2"
},

@@ -47,0 +47,0 @@ "publishConfig": {

@@ -26,3 +26,3 @@ > ## 🛠 Status: In Development

Properties can be given an options argument which is an object that describes how to
Properties can be given an `options` argument which is an object that describes how to
process the property. This can be done either in the `@property({...})` decorator or in the

@@ -34,20 +34,21 @@ object returned from the `properties` getter, e.g. `static get properties { return { foo: {...} }`.

* `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 the value is `false`, the property is not added to the static `observedAttributes` getter.
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`: 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
a the property value. If it's an object, it can have keys for `fromAttribute` and
`toAttribute` where `fromAttribute` is the deserialize function and `toAttribute`
is a serialize function used to set the property to an attribute. If no `toAttribute`
function is provided and `reflect` is set to `true`, the property value is set
directly to the attribute.
* `reflect`: Indicates if the property should reflect to an attribute.
If `true`, when the property is set, the attribute is set using the
attribute name determined according to the rules for the `attribute`
propety option and the value of the property serialized using the rules from
the `type` property option.
* `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.
The value can be a function used for both serialization and deserialization, or it can
be an object with individual functions via the optional keys, `fromAttribute` and `toAttribute`.
`type` defaults to the `String` constructor, and so does the `toAttribute` and `fromAttribute`
keys.
* `reflect`: Indicates whether the property should reflect to its associated
attribute (as determined by the attribute option).
If `true`, when the property is set, the attribute which name is determined
according to the rules for the `attribute` property option, will be set to the
value of the property serialized using the rules from the `type` property option.
Note, `type: Boolean` has special handling by default which means that truthy
values result in the presense of the attribute, where as falsy values result
in the absense of the attribute.
* `hasChanged`: A function that indicates whether a property should be considered
changed when it is set and thus result in an update. The function should take the
`newValue` and `oldValue` and return `true` if an update should be requested.

@@ -74,6 +75,6 @@ * **React to changes:** LitElement reacts to changes in properties and attributes by

* Runs in all [supported](#supported-browsers) browsers: [StackBlitz](https://stackblitz.com/edit/lit-element-example?file=index.js), [Glitch](https://glitch.com/edit/#!/hello-lit-element?path=index.html)
* Runs in all [supported](#supported-browsers) browsers: [Glitch](https://glitch.com/edit/#!/hello-lit-element?path=index.html)
* Runs in browsers with [JavaScript Modules](https://caniuse.com/#search=modules): [JSFiddle](https://jsfiddle.net/j6mf6gpo/), [JSBin](http://jsbin.com/zezilad/edit?html,output),
[CodePen](https://codepen.io/sorvell/pen/BxZgPN).
* Runs in browsers with [JavaScript Modules](https://caniuse.com/#search=modules): [JSFiddle](https://jsfiddle.net/rzhofu81/), [JSBin](http://jsbin.com/vecuyan/edit?html,output),
[CodePen](https://codepen.io/sorvell/pen/RYQyoe?editors=1000).

@@ -167,3 +168,3 @@ * You can also copy [this HTML file](https://gist.githubusercontent.com/sorvell/48f4b7be35c8748e8f6db5c66d36ee29/raw/2427328cf1ebae5077902a6bff5ddd8db45e83e4/index.html) into a local file and run it in any browser that supports [JavaScript Modules]((https://caniuse.com/#search=modules)).

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..
property values. Setting properties inside this method will *not* trigger another update.

@@ -182,4 +183,4 @@ * `firstUpdated(changedProperties)`: (protected) Called after the element's DOM has been

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.
update without triggering another update. The Promise result is `false` if a
property was 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

@@ -203,4 +204,5 @@ this Promise. To do this, first await `super.updateComplete` then any subsequent state.

* When the element is first connected or a property is set (e.g. `element.foo = 5`)
and the property's `hasChanged(value, oldValue)` returns `true`.
* A property is set (e.g. `element.foo = 5`).
* If the property's `hasChanged(value, oldValue)` returns `false`, the element does not
update. If it returns `true`, `requestUpdate()` is called to schedule an update.
* `requestUpdate()`: Updates the element after awaiting a [microtask](https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/) (at the end

@@ -211,3 +213,3 @@ of the event loop, before the next paint).

* `update(changedProperties)`: Updates the element. Setting properties inside this
method will *not* trigger the element to update.
method will *not* trigger another update.
* `render()`: Returns a `lit-html` TemplateResult (e.g. <code>html\`Hello ${world}\`</code>)

@@ -283,1 +285,6 @@ to render element DOM. Setting properties inside this method will *not* trigger

* On very old versions of Safari (<=9) or Chrome (<=41), properties created for native
platform properties like (`id` or `name`) may not have default values set in the element constructor.
On these browsers native properties appear on instances and therefore their default value
will overwrite any element default (e.g. if the element sets this.id = 'id' in the constructor,
the 'id' will become '' since this is the native platform default).

@@ -1,9 +0,8 @@

import { LitElement, html, property } from '../lit-element.js';
import {html, LitElement, property} from '../lit-element.js';
class TSElement extends LitElement {
@property()
message = 'Hi';
@property() message = 'Hi';
@property({attribute: 'more-info', type: (value: string) => `[${value}]`})
@property({attribute : 'more-info', type: (value: string) => `[${value}]`})
extra = '';

@@ -21,4 +20,3 @@

}
}
customElements.define('ts-element', TSElement);

@@ -21,3 +21,4 @@ /**

/**
* Deserializing function called to convert an attribute value to a property value.
* Deserializing function called to convert an attribute value to a property
* value.
*/

@@ -27,6 +28,6 @@ fromAttribute?(value: string): T;

/**
* Serializing function called to convert a property value to an attribute value.
* Serializing function called to convert a property value to an attribute
* value.
*/
toAttribute?(value: T): string|null;
}

@@ -44,4 +45,5 @@

* 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'`).
* 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'`).
*/

@@ -51,9 +53,10 @@ attribute?: boolean|string;

/**
* 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
* a the property value. If it's an object, it can have keys for `fromAttribute` and
* `toAttribute` where `fromAttribute` is the deserialize function and `toAttribute`
* is a serialize function used to set the property to an attribute. If no `toAttribute`
* function is provided and `reflect` is set to `true`, the property value is set
* directly to the attribute.
* 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 a the property value. If it's an object, it can have keys
* for `fromAttribute` and `toAttribute` where `fromAttribute` is the
* deserialize function and `toAttribute` is a serialize function used to set
* the property to an attribute. If no `toAttribute` function is provided and
* `reflect` is set to `true`, the property value is set directly to the
* attribute.
*/

@@ -66,4 +69,4 @@ type?: AttributeType<T>;

* attribute name determined according to the rules for the `attribute`
* propety option and the value of the property serialized using the rules from
* the `type` property option.
* property option and the value of the property serialized using the rules
* from the `type` property option.
*/

@@ -73,8 +76,7 @@ reflect?: boolean;

/**
* 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.
* 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.
*/
hasChanged?(value: T, oldValue: T): boolean;
}

@@ -97,10 +99,2 @@

/**
* Decorator which creates a property. Optionally a `PropertyDeclaration` object
* can be supplied to describe how the property should be configured.
*/
export const property = (options?: PropertyDeclaration) => (proto: Object, name: string) => {
(proto.constructor as typeof UpdatingElement).createProperty(name, options);
};
// serializer/deserializers for boolean attribute

@@ -124,6 +118,6 @@ const fromBooleanAttribute = (value: string) => value !== null;

const defaultPropertyDeclaration: PropertyDeclaration = {
attribute: true,
type: String,
reflect: false,
hasChanged: notEqual
attribute : true,
type : String,
reflect : false,
hasChanged : notEqual
};

@@ -136,3 +130,4 @@

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

@@ -187,3 +182,5 @@ /**

*/
static createProperty(name: PropertyKey, options: PropertyDeclaration = defaultPropertyDeclaration) {
static createProperty(name: PropertyKey,
options:
PropertyDeclaration = defaultPropertyDeclaration) {
// ensure private storage for property declarations.

@@ -196,7 +193,8 @@ if (!this.hasOwnProperty('_classProperties')) {

superProperties.forEach((v: any, k: PropertyKey) =>
this._classProperties.set(k, v));
this._classProperties.set(k, v));
}
}
this._classProperties.set(name, options);
// Allow user defined accessors by not replacing an existing own-property accessor.
// Allow user defined accessors by not replacing an existing own-property
// accessor.
if (this.prototype.hasOwnProperty(name)) {

@@ -207,5 +205,3 @@ return;

Object.defineProperty(this.prototype, name, {
get() {
return this[key];
},
get() { return this[key]; },
set(value) {

@@ -216,4 +212,4 @@ const oldValue = this[name];

},
configurable: true,
enumerable: true
configurable : true,
enumerable : true
});

@@ -241,6 +237,11 @@ }

// support symbols in properties (IE11 does not support this)
const propKeys = [...Object.getOwnPropertyNames(props),
...(typeof Object.getOwnPropertySymbols === 'function') ? Object.getOwnPropertySymbols(props) : []];
const propKeys = [
...Object.getOwnPropertyNames(props),
...(typeof Object.getOwnPropertySymbols === 'function')
? Object.getOwnPropertySymbols(props)
: []
];
for (const p of propKeys) {
// note, use of `any` is due to TypeSript lack of support for symbol in index types
// note, use of `any` is due to TypeSript lack of support for symbol in
// index types
this.createProperty(p, (props as any)[p]);

@@ -253,6 +254,11 @@ }

*/
private static _attributeNameForProperty(name: PropertyKey, options?: PropertyDeclaration) {
private static _attributeNameForProperty(name: PropertyKey,
options?: PropertyDeclaration) {
const attribute = options !== undefined && options.attribute;
return attribute === false ? undefined : (typeof attribute === 'string' ?
attribute : (typeof name === 'string' ? name.toLowerCase() : undefined));
return attribute === false
? undefined
: (typeof attribute === 'string'
? attribute
: (typeof name === 'string' ? name.toLowerCase()
: undefined));
}

@@ -266,3 +272,3 @@

private static _valueHasChanged(value: unknown, old: unknown,
hasChanged: HasChanged = notEqual) {
hasChanged: HasChanged = notEqual) {
return hasChanged(value, old);

@@ -276,3 +282,4 @@ }

*/
private static _propertyValueFromAttribute(value: string, options?: PropertyDeclaration) {
private static _propertyValueFromAttribute(value: string,
options?: PropertyDeclaration) {
const type = options && options.type;

@@ -283,4 +290,6 @@ if (type === undefined) {

// Note: special case `Boolean` so users can use it as a `type`.
const fromAttribute = type === Boolean ? fromBooleanAttribute :
(typeof type === 'function' ? type : type.fromAttribute);
const fromAttribute =
type === Boolean
? fromBooleanAttribute
: (typeof type === 'function' ? type : type.fromAttribute);
return fromAttribute ? fromAttribute(value) : value;

@@ -296,3 +305,4 @@ }

*/
private static _propertyValueToAttribute(value: unknown, options?: PropertyDeclaration) {
private static _propertyValueToAttribute(value: unknown,
options?: PropertyDeclaration) {
if (options === undefined || options.reflect === undefined) {

@@ -302,4 +312,8 @@ return;

// Note: special case `Boolean` so users can use it as a `type`.
const toAttribute = options.type === Boolean ? toBooleanAttribute :
(options.type && (options.type as AttributeSerializer).toAttribute || String);
const toAttribute =
options.type === Boolean
? toBooleanAttribute
: (options.type &&
(options.type as AttributeSerializer).toAttribute ||
String);
return toAttribute(value);

@@ -321,3 +335,4 @@ }

*/
private _reflectingProperties: Map<PropertyKey, PropertyDeclaration>|undefined = undefined;
private _reflectingProperties: Map<PropertyKey, PropertyDeclaration>|
undefined = undefined;

@@ -336,4 +351,4 @@ /**

/**
* Performs element initialization. By default this calls `createRenderRoot` to
* create the element `renderRoot` node and captures any pre-set values for
* Performs element initialization. By default this calls `createRenderRoot`
* to create the element `renderRoot` node and captures any pre-set values for
* registered properties.

@@ -349,7 +364,14 @@ */

* Otherwise these would shadow the accessor and break these properties.
* The properties are stored in a Map which is played back after the constructor
* runs.
* The properties are stored in a Map which is played back after the
* constructor runs. Note, on very old versions of Safari (<=9) or Chrome
* (<=41), properties created for native platform properties like (`id` or
* `name`) may not have default values set in the element constructor. On
* these browsers native properties appear on instances and therefore their
* default value will overwrite any element default (e.g. if the element sets
* this.id = 'id' in the constructor, the 'id' will become '' since this is
* the native platform default).
*/
private _saveInstanceProperties() {
for (const [p] of (this.constructor as typeof UpdatingElement)._classProperties) {
for (const [p] of (this.constructor as typeof UpdatingElement)
._classProperties) {
if (this.hasOwnProperty(p)) {

@@ -409,3 +431,4 @@ const value = this[p as keyof this];

private _propertyToAttribute(name: PropertyKey, value: unknown,
private _propertyToAttribute(
name: PropertyKey, value: unknown,
options: PropertyDeclaration = defaultPropertyDeclaration) {

@@ -445,3 +468,4 @@ const ctor = (this.constructor as typeof UpdatingElement);

const options = ctor._classProperties.get(propName);
this[propName as keyof this] = ctor._propertyValueFromAttribute(value, options);
this[propName as keyof this] =
ctor._propertyValueFromAttribute(value, options);
}

@@ -454,6 +478,7 @@ }

* 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.
* 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.
*

@@ -466,4 +491,5 @@ * @param name {PropertyKey} (optional) name of requesting property

if (name !== undefined) {
const options = (this.constructor as typeof UpdatingElement)._classProperties.get(name) ||
defaultPropertyDeclaration;
const options = (this.constructor as typeof UpdatingElement)
._classProperties.get(name) ||
defaultPropertyDeclaration;
return this._requestPropertyUpdate(name, oldValue, options);

@@ -480,5 +506,7 @@ }

*/
private _requestPropertyUpdate(name: PropertyKey, oldValue: any, options: PropertyDeclaration) {
if (!(this.constructor as typeof UpdatingElement)._valueHasChanged(this[name as keyof this],
oldValue, options.hasChanged)) {
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;

@@ -534,5 +562,5 @@ }

this.update(changedProperties);
const needsFirstUpdate = !(this._updateState & STATE_HAS_UPDATED);
this._markUpdated();
if (needsFirstUpdate) {
if (!(this._updateState & STATE_HAS_UPDATED)) {
this._updateState = this._updateState | STATE_HAS_UPDATED;
this.firstUpdated(changedProperties);

@@ -545,16 +573,15 @@ }

}
private _markUpdated() {
this._changedProperties = new Map();
this._updateState = this._updateState & ~STATE_UPDATE_REQUESTED | STATE_HAS_UPDATED;
this._updateState = this._updateState & ~STATE_UPDATE_REQUESTED;
}
/**
* 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 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 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. The Promise result is `false` if
* a property was 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.
*

@@ -564,5 +591,3 @@ * @returns {Promise} The Promise returns a boolean that indicates if the

*/
get updateComplete() {
return this._updatePromise;
}
get updateComplete() { return this._updatePromise; }

@@ -582,4 +607,5 @@ /**

* 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.
* 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.
*

@@ -589,3 +615,4 @@ * * @param _changedProperties Map of changed properties with old values

protected update(_changedProperties: PropertyValues) {
if (this._reflectingProperties !== undefined && this._reflectingProperties.size > 0) {
if (this._reflectingProperties !== undefined &&
this._reflectingProperties.size > 0) {
for (const [k, v] of this._reflectingProperties) {

@@ -619,3 +646,2 @@ this._propertyToAttribute(k, this[k as keyof this], v);

protected firstUpdated(_changedProperties: PropertyValues) {}
}

@@ -14,14 +14,16 @@ /**

*/
import {TemplateResult} from 'lit-html';
import {render} from 'lit-html/lib/shady-render';
import {TemplateResult} from 'lit-html';
import {UpdatingElement, PropertyValues} from './lib/updating-element.js';
import {PropertyValues, UpdatingElement} from './lib/updating-element.js';
export * from './lib/updating-element.js';
export * from './lib/decorators.js';
export {html, svg} from 'lit-html/lit-html';
export abstract class LitElement extends UpdatingElement {
/**
* Render method used to render the lit-html TemplateResult to the element's DOM.
* Render method used to render the lit-html TemplateResult to the element's
* DOM.
* @param {TemplateResult} Template to render.

@@ -42,3 +44,4 @@ * @param {Element|DocumentFragment} Node into which to render.

if (typeof this.render === 'function') {
(this.constructor as typeof LitElement).render(this.render(), this.renderRoot!, this.localName!);
(this.constructor as typeof LitElement)
.render(this.render(), this.renderRoot!, this.localName!);
} else {

@@ -45,0 +48,0 @@ throw new Error('render() not implemented');

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

import '@webcomponents/shadycss/apply-shim.min.js';
import {

@@ -22,3 +23,7 @@ html,

import {generateElementName, nextFrame, getComputedStyleValue} from './test-helpers.js';
import {
generateElementName,
getComputedStyleValue,
nextFrame
} from './test-helpers.js';

@@ -50,3 +55,4 @@ declare global {

customElements.define(name, class extends LitElement {
render() { return html`
render() {
return html`
<style>

@@ -75,3 +81,4 @@ div {

customElements.define(name, class extends LitElement {
render() { return html`
render() {
return html`
<style>

@@ -96,3 +103,4 @@ div {

customElements.define(name, class extends LitElement {
render() { return html`
render() {
return html`
<style>

@@ -118,3 +126,4 @@ :host {

customElements.define('x-inner', class extends LitElement {
render() { return html`
render() {
return html`
<style>

@@ -132,3 +141,4 @@ div {

render() { return html`
render() {
return html`
<style>

@@ -158,5 +168,7 @@ x-inner {

test('elements with custom properties can move between elements', async () => {
customElements.define('x-inner1', class extends LitElement {
render() { return html`
test('elements with custom properties can move between elements',
async () => {
customElements.define('x-inner1', class extends LitElement {
render() {
return html`
<style>

@@ -168,10 +180,10 @@ div {

<div>Testing...</div>`;
}
});
const name1 = generateElementName();
customElements.define(name1, class extends LitElement {
}
});
const name1 = generateElementName();
customElements.define(name1, class extends LitElement {
inner: Element|null = null;
inner: Element|null = null;
render() { return html`
render() {
return html`
<style>

@@ -183,12 +195,12 @@ x-inner1 {

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

@@ -199,30 +211,32 @@ x-inner1 {

</style>`;
}
}
});
const el = document.createElement(name1) as LitElement;
const el2 = document.createElement(name2);
container.appendChild(el);
container.appendChild(el2);
let div: Element|null;
});
const el = document.createElement(name1) as LitElement;
const el2 = document.createElement(name2);
container.appendChild(el);
container.appendChild(el2);
let div: Element|null;
// Workaround for Safari 9 Promise timing bugs.
await el.updateComplete;
// Workaround for Safari 9 Promise timing bugs.
await el.updateComplete;
await nextFrame();
const inner = el.shadowRoot!.querySelector('x-inner1');
div = inner!.shadowRoot!.querySelector('div');
assert.equal(getComputedStyleValue(div!, 'border-top-width').trim(),
'2px');
el2!.shadowRoot!.appendChild(inner!);
await nextFrame();
const inner = el.shadowRoot!.querySelector('x-inner1');
div = inner!.shadowRoot!.querySelector('div');
assert.equal(getComputedStyleValue(div!, 'border-top-width').trim(), '2px');
el2!.shadowRoot!.appendChild(inner!);
// Workaround for Safari 9 Promise timing bugs.
await el.updateComplete;
// Workaround for Safari 9 Promise timing bugs.
await el.updateComplete;
await nextFrame();
assert.equal(getComputedStyleValue(div!, 'border-top-width').trim(),
'8px');
});
await nextFrame();
assert.equal(getComputedStyleValue(div!, 'border-top-width').trim(), '8px');
});
test('@apply renders in nested elements', async () => {
customElements.define('x-inner2', class extends LitElement {
render() { return html`
render() {
return html`
<style>

@@ -239,3 +253,4 @@ div {

inner: LitElement|null = null;
render() { return html`
render() {
return html`
<style>

@@ -263,10 +278,10 @@ x-inner2 {

await nextFrame();
const div = el.shadowRoot!.querySelector('x-inner2')!.shadowRoot!.querySelector('div');
assert.equal(getComputedStyleValue(div!, 'border-top-width').trim(), '10px');
const div = el.shadowRoot!.querySelector(
'x-inner2')!.shadowRoot!.querySelector('div');
assert.equal(getComputedStyleValue(div!, 'border-top-width').trim(),
'10px');
});
});
suite('ShadyDOM', () => {
let container: HTMLElement;

@@ -289,7 +304,9 @@

test('properties in styles render with initial value and cannot be changed', async () => {
let border = `6px solid blue`;
const name = generateElementName();
customElements.define(name, class extends LitElement {
render() { return html`
test('properties in styles render with initial value and cannot be changed',
async () => {
let border = `6px solid blue`;
const name = generateElementName();
customElements.define(name, class extends LitElement {
render() {
return html`
<style>

@@ -301,15 +318,16 @@ div {

<div>Testing...</div>`;
}
});
const el = document.createElement(name) as LitElement;
container.appendChild(el);
await el.updateComplete;
const div = el.shadowRoot!.querySelector('div');
assert.equal(getComputedStyleValue(div!, 'border-top-width').trim(), '6px');
border = `4px solid orange`;
el.requestUpdate();
await el.updateComplete;
assert.equal(getComputedStyleValue(div!, 'border-top-width').trim(), '6px');
});
}
});
const el = document.createElement(name) as LitElement;
container.appendChild(el);
await el.updateComplete;
const div = el.shadowRoot!.querySelector('div');
assert.equal(getComputedStyleValue(div!, 'border-top-width').trim(),
'6px');
border = `4px solid orange`;
el.requestUpdate();
await el.updateComplete;
assert.equal(getComputedStyleValue(div!, 'border-top-width').trim(),
'6px');
});
});

@@ -15,5 +15,14 @@ /**

import {html, LitElement, PropertyDeclarations, PropertyValues, property} from '../lit-element.js';
import {
html,
LitElement,
property,
PropertyDeclarations,
PropertyValues
} from '../lit-element.js';
import {stripExpressionDelimeters, generateElementName} from './test-helpers.js';
import {
generateElementName,
stripExpressionDelimeters
} from './test-helpers.js';

@@ -47,5 +56,4 @@ const assert = chai.assert;

assert.ok(el.shadowRoot);
assert.equal(
stripExpressionDelimeters(el.shadowRoot!.innerHTML),
rendered);
assert.equal(stripExpressionDelimeters(el.shadowRoot!.innerHTML),
rendered);
resolve();

@@ -65,18 +73,38 @@ });

await el.requestUpdate();
assert.equal(
stripExpressionDelimeters(el.shadowRoot!.innerHTML),
'1');
assert.equal(stripExpressionDelimeters(el.shadowRoot!.innerHTML), '1');
await el.requestUpdate();
assert.equal(
stripExpressionDelimeters(el.shadowRoot!.innerHTML),
'2');
assert.equal(stripExpressionDelimeters(el.shadowRoot!.innerHTML), '2');
await el.requestUpdate();
assert.equal(
stripExpressionDelimeters(el.shadowRoot!.innerHTML),
'3');
assert.equal(stripExpressionDelimeters(el.shadowRoot!.innerHTML), '3');
});
test('`updateComplete` waits for `requestUpdate` but does not trigger update, async', async () => {
test(
'`updateComplete` waits for `requestUpdate` but does not trigger update, async',
async () => {
class E extends LitElement {
updateCount = 0;
render() { return html`${++this.updateCount}`; }
}
customElements.define(generateElementName(), E);
const el = new E();
container.appendChild(el);
await el.updateComplete;
assert.equal(stripExpressionDelimeters(el.shadowRoot!.innerHTML), '1');
await el.updateComplete;
assert.equal(stripExpressionDelimeters(el.shadowRoot!.innerHTML), '1');
el.requestUpdate();
await el.updateComplete;
assert.equal(stripExpressionDelimeters(el.shadowRoot!.innerHTML), '2');
await el.updateComplete;
assert.equal(stripExpressionDelimeters(el.shadowRoot!.innerHTML), '2');
});
test('`shouldUpdate` controls update/rendering', async () => {
class E extends LitElement {
needsUpdate = true;
updateCount = 0;
shouldUpdate() { return this.needsUpdate; }
render() { return html`${++this.updateCount}`; }

@@ -88,54 +116,13 @@ }

await el.updateComplete;
assert.equal(
stripExpressionDelimeters(el.shadowRoot!.innerHTML),
'1');
await el.updateComplete;
assert.equal(
stripExpressionDelimeters(el.shadowRoot!.innerHTML),
'1');
el.requestUpdate();
await el.updateComplete;
assert.equal(
stripExpressionDelimeters(el.shadowRoot!.innerHTML),
'2');
await el.updateComplete;
assert.equal(
stripExpressionDelimeters(el.shadowRoot!.innerHTML),
'2');
assert.equal(stripExpressionDelimeters(el.shadowRoot!.innerHTML), '1');
el.needsUpdate = false;
await el.requestUpdate();
assert.equal(stripExpressionDelimeters(el.shadowRoot!.innerHTML), '1');
el.needsUpdate = true;
await el.requestUpdate();
assert.equal(stripExpressionDelimeters(el.shadowRoot!.innerHTML), '2');
await el.requestUpdate();
assert.equal(stripExpressionDelimeters(el.shadowRoot!.innerHTML), '3');
});
test('`shouldUpdate` controls update/rendering',
async () => {
class E extends LitElement {
needsUpdate = true;
updateCount = 0;
shouldUpdate() { return this.needsUpdate; }
render() { return html`${++this.updateCount}`; }
}
customElements.define(generateElementName(), E);
const el = new E();
container.appendChild(el);
await el.updateComplete;
assert.equal(
stripExpressionDelimeters(el.shadowRoot!.innerHTML),
'1');
el.needsUpdate = false;
await el.requestUpdate();
assert.equal(
stripExpressionDelimeters(el.shadowRoot!.innerHTML),
'1');
el.needsUpdate = true;
await el.requestUpdate();
assert.equal(
stripExpressionDelimeters(el.shadowRoot!.innerHTML),
'2');
await el.requestUpdate();
assert.equal(
stripExpressionDelimeters(el.shadowRoot!.innerHTML),
'3');
});
test('can set render target to light dom', async () => {

@@ -166,10 +153,8 @@ const rendered = `hello world`;

assert.ok(el.shadowRoot);
assert.equal(
stripExpressionDelimeters(el.shadowRoot!.innerHTML),
rendered);
assert.equal(stripExpressionDelimeters(el.shadowRoot!.innerHTML), rendered);
});
test('property options', async() => {
const hasChanged = (value: any, old: any) => old === undefined || value > old;
test('property options', async () => {
const hasChanged = (value: any, old: any) =>
old === undefined || value > old;
const fromAttribute = (value: any) => parseInt(value);

@@ -180,9 +165,14 @@ const toAttribute = (value: any) => `${value}-attr`;

return {
noAttr: {attribute: false},
atTr: {attribute: true},
customAttr: {attribute: 'custom', reflect: true},
hasChanged: {hasChanged},
fromAttribute: {type: fromAttribute},
toAttribute: {reflect: true, type: {toAttribute}},
all: {attribute: 'all-attr', hasChanged, type: {fromAttribute, toAttribute}, reflect: true},
noAttr : {attribute : false},
atTr : {attribute : true},
customAttr : {attribute : 'custom', reflect : true},
hasChanged : {hasChanged},
fromAttribute : {type : fromAttribute},
toAttribute : {reflect : true, type : {toAttribute}},
all : {
attribute : 'all-attr',
hasChanged,
type : {fromAttribute, toAttribute},
reflect : true
},
};

@@ -207,3 +197,2 @@ }

render() { return html``; }
}

@@ -267,5 +256,5 @@ customElements.define(generateElementName(), E);

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

@@ -275,15 +264,15 @@ const toAttribute = (value: any) => `${value}-attr`;

@property({attribute: false})
noAttr = 'noAttr';
@property({attribute: true})
atTr = 'attr';
@property({attribute: 'custom', reflect: true})
@property({attribute : false}) noAttr = 'noAttr';
@property({attribute : true}) atTr = 'attr';
@property({attribute : 'custom', reflect: true})
customAttr = 'customAttr';
@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})
@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;

@@ -299,3 +288,2 @@

render() { return html``; }
}

@@ -359,5 +347,5 @@ customElements.define(generateElementName(), E);

test('can mix property options via decorator and via getter', async() => {
const hasChanged = (value: any, old: any) => old === undefined || value > old;
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);

@@ -367,9 +355,11 @@ const toAttribute = (value: any) => `${value}-attr`;

@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})
@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;

@@ -381,5 +371,5 @@

return {
noAttr: {attribute: false},
atTr: {attribute: true},
customAttr: {attribute: 'custom', reflect: true},
noAttr : {attribute : false},
atTr : {attribute : true},
customAttr : {attribute : 'custom', reflect : true},
};

@@ -405,3 +395,2 @@ }

render() { return html``; }
}

@@ -465,6 +454,8 @@ customElements.define(generateElementName(), E);

test('attributes deserialize from html', async() => {
test('attributes deserialize from html', async () => {
const fromAttribute = (value: any) => parseInt(value);
const toAttributeOnly = (value: any) => typeof value === 'string' && value.indexOf(`-attr`) > 0 ? value : `${value}-attr`;
const toAttributeOnly = (value: any) =>
typeof value === 'string' && value.indexOf(`-attr`) > 0
? value
: `${value}-attr`;
const toAttribute = (value: any) => `${value}-attr`;

@@ -474,8 +465,13 @@ class E extends LitElement {

return {
noAttr: {attribute: false},
atTr: {attribute: true},
customAttr: {attribute: 'custom', reflect: true},
fromAttribute: {type: fromAttribute},
toAttribute: {reflect: true, type: {toAttribute: toAttributeOnly}},
all: {attribute: 'all-attr', type: {fromAttribute, toAttribute}, reflect: true},
noAttr : {attribute : false},
atTr : {attribute : true},
customAttr : {attribute : 'custom', reflect : true},
fromAttribute : {type : fromAttribute},
toAttribute :
{reflect : true, type : {toAttribute : toAttributeOnly}},
all : {
attribute : 'all-attr',
type : {fromAttribute, toAttribute},
reflect : true
},
};

@@ -492,3 +488,2 @@ }

render() { return html``; }
}

@@ -519,4 +514,3 @@ const name = generateElementName();

if (Object.getOwnPropertySymbols) {
test('properties defined using symbols', async() => {
test('properties defined using symbols', async () => {
const zug = Symbol();

@@ -526,8 +520,3 @@

static get properties() {
return {
foo: {},
[zug]: {}
};
}
static get properties() { return {foo : {}, [zug] : {}}; }
updateCount = 0;

@@ -537,5 +526,3 @@ foo = 5;

render() {
return html``;
}
render() { return html``; }

@@ -546,3 +533,2 @@ update(changedProperties: PropertyValues) {

}
}

@@ -568,4 +554,3 @@ customElements.define(generateElementName(), E);

test('properties as symbols can set property options', async() => {
test('properties as symbols can set property options', async () => {
const zug = Symbol();

@@ -577,3 +562,7 @@

return {
[zug]: {attribute: 'zug', reflect: true, type: (value: string) => Number(value) + 100}
[zug] : {
attribute : 'zug',
reflect : true,
type : (value: string) => Number(value) + 100
}
};

@@ -587,6 +576,3 @@ }

render() {
return html``;
}
render() { return html``; }
}

@@ -610,6 +596,5 @@ customElements.define(generateElementName(), E);

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

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

return {
noAttr: {attribute: false},
atTr: {attribute: true},
customAttr: {},
hasChanged: {},
noAttr : {attribute : false},
atTr : {attribute : true},
customAttr : {},
hasChanged : {},
};

@@ -641,3 +626,2 @@ }

render() { return html``; }
}

@@ -649,6 +633,6 @@ customElements.define(generateElementName(), E);

return {
customAttr: {attribute: 'custom', reflect: true},
hasChanged: {hasChanged},
fromAttribute: {},
toAttribute: {},
customAttr : {attribute : 'custom', reflect : true},
hasChanged : {hasChanged},
fromAttribute : {},
toAttribute : {},
};

@@ -660,3 +644,2 @@ }

all = 10;
}

@@ -667,8 +650,12 @@

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

@@ -732,12 +719,10 @@

assert.equal(el.updateCount, 6);
});
test('superclass properties not affected by subclass', async() => {
test('superclass properties not affected by subclass', async () => {
class E extends LitElement {
static get properties(): PropertyDeclarations {
return {
foo: {attribute: 'zug', reflect: true},
bar: {reflect: true}
foo : {attribute : 'zug', reflect : true},
bar : {reflect : true}
};

@@ -750,3 +735,2 @@ }

render() { return html``; }
}

@@ -757,6 +741,3 @@ customElements.define(generateElementName(), E);

static get properties(): PropertyDeclarations {
return {
foo: {attribute: false},
nug: {}
};
return {foo : {attribute : false}, nug : {}};
}

@@ -769,3 +750,2 @@

render() { return html``; }
}

@@ -796,3 +776,3 @@ customElements.define(generateElementName(), F);

test('Attributes reflect', async () => {
test('Attributes reflect', async () => {
const suffix = '-reflected';

@@ -802,3 +782,6 @@ class E extends LitElement {

return {
foo: {reflect: true, type: {toAttribute: (value: any) => `${value}${suffix}`}}
foo : {
reflect : true,
type : {toAttribute : (value: any) => `${value}${suffix}`}
}
};

@@ -824,5 +807,3 @@ }

static get properties() {
return {
bar: {type: Boolean, reflect: true}
};
return {bar : {type : Boolean, reflect : true}};
}

@@ -849,3 +830,3 @@

class E extends LitElement {
static get properties() { return { foo: {}}; }
static get properties() { return {foo : {}}; }

@@ -861,20 +842,12 @@ foo = 'one';

await el.updateComplete;
assert.equal(
stripExpressionDelimeters(el.shadowRoot!.innerHTML),
'one');
assert.equal(stripExpressionDelimeters(el.shadowRoot!.innerHTML), 'one');
el.foo = 'changed';
await el.updateComplete;
assert.equal(
stripExpressionDelimeters(el.shadowRoot!.innerHTML),
'changed');
assert.equal(stripExpressionDelimeters(el.shadowRoot!.innerHTML),
'changed');
});
test('updates/renders when properties and attributes change', async() => {
test('updates/renders when properties and attributes change', async () => {
class E extends LitElement {
static get properties() {
return {
value: {},
attrValue: {}
};
}
static get properties() { return {value : {}, attrValue : {}}; }

@@ -923,5 +896,3 @@ value = '1';

class E extends LitElement {
static get properties() {
return {foo: {}};
}
static get properties() { return {foo : {}}; }

@@ -937,10 +908,7 @@ foo = 'one';

assert.ok(el.shadowRoot);
assert.equal(
stripExpressionDelimeters(el.shadowRoot!.innerHTML),
'one');
assert.equal(stripExpressionDelimeters(el.shadowRoot!.innerHTML), 'one');
el.setAttribute('foo', 'changed');
await el.updateComplete;
assert.equal(
stripExpressionDelimeters(el.shadowRoot!.innerHTML),
'changed');
assert.equal(stripExpressionDelimeters(el.shadowRoot!.innerHTML),
'changed');
});

@@ -952,3 +920,3 @@

static get properties() { return {foo: {}, bar: {}}; }
static get properties() { return {foo : {}, bar : {}}; }

@@ -981,33 +949,105 @@ info: string[] = [];

test('User defined accessor can use property options via `requestUpdate`', async () => {
const fromAttribute = (value: any) => parseInt(value);
const toAttribute = (value: any) => `${value}-attr`;
const hasChanged = (value: any, old: any) => isNaN(old) || value > old;
class E extends LitElement {
test('User defined accessor can use property options via `requestUpdate`',
async () => {
const fromAttribute = (value: any) => parseInt(value);
const toAttribute = (value: any) => `${value}-attr`;
const hasChanged = (value: any, old: any) => isNaN(old) || value > old;
class E extends LitElement {
updateCount = 0;
__bar: any;
updateCount = 0;
__bar: any;
static get properties() {
return {
bar: {attribute: 'attr-bar', reflect: true, type: {fromAttribute, toAttribute}, hasChanged}
};
}
static get properties() {
return {
bar : {
attribute : 'attr-bar',
reflect : true,
type : {fromAttribute, toAttribute},
hasChanged
}
};
}
constructor() {
super();
this.bar = 5;
}
constructor() {
super();
this.bar = 5;
}
update(changed: PropertyValues) {
super.update(changed);
this.updateCount++;
}
update(changed: PropertyValues) {
super.update(changed);
this.updateCount++;
}
get bar() { return this.__bar; }
get bar() { return this.__bar; }
set bar(value) {
const old = this.bar;
this.__bar = Number(value);
this.requestUpdate('bar', old);
set bar(value) {
const old = this.bar;
this.__bar = Number(value);
this.requestUpdate('bar', old);
}
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.bar, 5);
assert.equal(el.getAttribute('attr-bar'), `5-attr`);
el.setAttribute('attr-bar', '7');
await el.updateComplete;
assert.equal(el.updateCount, 2);
assert.equal(el.bar, 7);
assert.equal(el.getAttribute('attr-bar'), `7-attr`);
el.bar = 4;
await el.updateComplete;
assert.equal(el.updateCount, 2);
assert.equal(el.bar, 4);
assert.equal(el.getAttribute('attr-bar'), `7-attr`);
el.setAttribute('attr-bar', '3');
await el.updateComplete;
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 () => {
class E extends LitElement {
_event?: Event;
render() {
const attr = 'attr';
const prop = 'prop';
const event = (e: Event) => { this._event = e; };
return html
`<div attr="${attr}" .prop="${prop}" @zug="${event}"></div>`;
}
}
customElements.define(generateElementName(), E);
const el = new E();
container.appendChild(el);
await el.updateComplete;
const d = el.shadowRoot!.querySelector('div')!;
assert.equal(d.getAttribute('attr'), 'attr');
assert.equal((d as any).prop, 'prop');
const e = new Event('zug');
d.dispatchEvent(e);
assert.equal(el._event, e);
});
test('`firstUpdated` called when element first updates', async () => {
class E extends LitElement {
@property() foo = 1;
wasUpdatedCount = 0;
wasFirstUpdated = 0;
changedProperties: PropertyValues|undefined;
update(changedProperties: PropertyValues) {
this.wasUpdatedCount++;
super.update(changedProperties);
}

@@ -1017,2 +1057,6 @@

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

@@ -1023,55 +1067,23 @@ customElements.define(generateElementName(), E);

await el.updateComplete;
assert.equal(el.updateCount, 1);
assert.equal(el.bar, 5);
assert.equal(el.getAttribute('attr-bar'), `5-attr`);
el.setAttribute('attr-bar', '7');
await el.updateComplete;
assert.equal(el.updateCount, 2);
assert.equal(el.bar, 7);
assert.equal(el.getAttribute('attr-bar'), `7-attr`);
el.bar = 4;
await el.updateComplete;
assert.equal(el.updateCount, 2);
assert.equal(el.bar, 4);
assert.equal(el.getAttribute('attr-bar'), `7-attr`);
el.setAttribute('attr-bar', '3');
await el.updateComplete;
assert.equal(el.updateCount, 2);
assert.equal(el.bar, 3);
assert.equal(el.getAttribute('attr-bar'), `3`);
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('updates/renders attributes, properties, and event listeners via `lit-html`',
async () => {
class E extends LitElement {
_event?: Event;
render() {
const attr = 'attr';
const prop = 'prop';
const event = (e: Event) => { this._event = e; };
return html
`<div attr="${attr}" .prop="${prop}" @zug="${event}"></div>`;
}
}
customElements.define(generateElementName(), E);
const el = new E();
container.appendChild(el);
await el.updateComplete;
const d = el.shadowRoot!.querySelector('div')! as (HTMLDivElement &
{prop: string});
assert.equal(d.getAttribute('attr'), 'attr');
assert.equal(d.prop, 'prop');
const e = new Event('zug');
d.dispatchEvent(e);
assert.equal(el._event, e);
});
test(
'`firstUpdated` called when element first updates', async () => {
'`firstUpdated` called when element first updates even if first `shouldUpdate` returned false',
async () => {
class E extends LitElement {
@property()
foo = 1;
@property() foo = 1;
triedToUpdatedCount = 0;
wasUpdatedCount = 0;

@@ -1081,2 +1093,7 @@ wasFirstUpdated = 0;

shouldUpdate() {
this.triedToUpdatedCount++;
return this.triedToUpdatedCount > 1;
}
update(changedProperties: PropertyValues) {

@@ -1093,4 +1110,4 @@ this.wasUpdatedCount++;

}
}
}
customElements.define(generateElementName(), E);

@@ -1100,67 +1117,58 @@ const el = new E();

await el.updateComplete;
assert.equal(el.triedToUpdatedCount, 1);
assert.equal(el.wasUpdatedCount, 0);
assert.equal(el.wasFirstUpdated, 0);
await el.requestUpdate();
const testMap = new Map();
testMap.set('foo', undefined);
assert.deepEqual(el.changedProperties, testMap);
assert.equal(el.triedToUpdatedCount, 2);
assert.equal(el.wasUpdatedCount, 1);
assert.equal(el.wasFirstUpdated, 1);
await el.requestUpdate();
assert.equal(el.triedToUpdatedCount, 3);
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', async () => {
class E extends LitElement {
static get properties() { return {
foo: {type: Number}
}; }
test('render lifecycle order', async () => {
class E extends LitElement {
static get properties() { return {foo : {type : Number}}; }
info: Array<string> = [];
info: Array<string> = [];
shouldUpdate() {
this.info.push('shouldUpdate');
return true;
}
shouldUpdate() {
this.info.push('shouldUpdate');
return true;
}
render() {
this.info.push('render');
return html`hi`;
}
render() {
this.info.push('render');
return html`hi`;
}
update(props: PropertyValues) {
this.info.push('before-update');
super.update(props);
this.info.push('after-update');
}
update(props: PropertyValues) {
this.info.push('before-update');
super.update(props);
this.info.push('after-update');
}
firstUpdated() {
this.info.push('firstUpdated');
}
firstUpdated() { this.info.push('firstUpdated'); }
updated() {
this.info.push('updated');
}
updated() { this.info.push('updated'); }
}
customElements.define(generateElementName(), E);
const el = new E();
container.appendChild(el);
await el.updateComplete;
el.info.push('updateComplete');
assert.deepEqual(el.info, [
'shouldUpdate', 'before-update', 'render', 'after-update', 'firstUpdated',
'updated', 'updateComplete'
]);
});
}
customElements.define(generateElementName(), E);
const el = new E();
container.appendChild(el);
await el.updateComplete;
el.info.push('updateComplete');
assert.deepEqual(
el.info,
[ 'shouldUpdate', 'before-update', 'render', 'after-update', 'firstUpdated', 'updated', 'updateComplete' ]);
});
test('setting properties in update does not trigger update', async () => {
class E extends LitElement {
static get properties() {
return {
foo: {}
};
}
static get properties() { return {foo : {}}; }
promiseFulfilled = false;

@@ -1176,6 +1184,3 @@ foo = 0;

render() {
return html`${this.foo}`;
}
render() { return html`${this.foo}`; }
}

@@ -1196,58 +1201,55 @@ customElements.define(generateElementName(), E);

test('setting properties in update reflects to attribute and is included in `changedProperties`', async () => {
class E extends LitElement {
test(
'setting properties in update reflects to attribute and is included in `changedProperties`',
async () => {
class E extends LitElement {
static get properties() {
return {
foo: {},
bar: {},
zot: {reflect: true}
};
}
static get properties() {
return {foo : {}, bar : {}, zot : {reflect : true}};
}
changedProperties: PropertyValues|undefined = undefined;
changedProperties: PropertyValues|undefined = undefined;
update(changedProperties: PropertyValues) {
(this as any).zot = (this as any).foo + (this as any).bar;
super.update(changedProperties);
this.changedProperties = changedProperties;
}
update(changedProperties: PropertyValues) {
(this as any).zot = (this as any).foo + (this as any).bar;
super.update(changedProperties);
this.changedProperties = changedProperties;
}
render() {
return html``;
}
render() { return html``; }
}
customElements.define(generateElementName(), E);
const el = new E() as any;
container.appendChild(el);
await el.updateComplete;
const testMap = new Map();
testMap.set('zot', undefined);
assert.deepEqual(el.changedProperties, testMap);
assert.isNaN(el.zot);
assert.equal(el.getAttribute('zot'), 'NaN');
el.bar = 1;
el.foo = 1;
await el.updateComplete;
assert.equal(el.foo, 1);
assert.equal(el.bar, 1);
assert.equal(el.zot, 2);
testMap.clear();
testMap.set('foo', undefined);
testMap.set('bar', undefined);
testMap.set('zot', NaN);
assert.deepEqual(el.changedProperties, testMap);
assert.equal(el.getAttribute('zot'), '2');
el.bar = 2;
await el.updateComplete;
assert.equal(el.bar, 2);
assert.equal(el.zot, 3);
testMap.clear();
testMap.set('bar', 1);
testMap.set('zot', 2);
assert.deepEqual(el.changedProperties, testMap);
assert.equal(el.getAttribute('zot'), '3');
});
}
customElements.define(generateElementName(), E);
const el = new E() as any;
container.appendChild(el);
await el.updateComplete;
const testMap = new Map();
testMap.set('zot', undefined);
assert.deepEqual(el.changedProperties, testMap);
assert.isNaN(el.zot);
assert.equal(el.getAttribute('zot'), 'NaN');
el.bar = 1;
el.foo = 1;
await el.updateComplete;
assert.equal(el.foo, 1);
assert.equal(el.bar, 1);
assert.equal(el.zot, 2);
testMap.clear();
testMap.set('foo', undefined);
testMap.set('bar', undefined);
testMap.set('zot', NaN);
assert.deepEqual(el.changedProperties, testMap);
assert.equal(el.getAttribute('zot'), '2');
el.bar = 2;
await el.updateComplete;
assert.equal(el.bar, 2);
assert.equal(el.zot, 3);
testMap.clear();
testMap.set('bar', 1);
testMap.set('zot', 2);
assert.deepEqual(el.changedProperties, testMap);
assert.equal(el.getAttribute('zot'), '3');
});
// Note, on older browsers (e.g. old Safari/Chrome), native properties
// cannot have default values. These will be overwritten by instance values.
test('can make properties for native accessors', async () => {

@@ -1258,22 +1260,14 @@ class E extends LitElement {

return {
id: {reflect: true},
name: {reflect: true},
title: {reflect: true},
foo: {}
id : {reflect : true},
name : {reflect : true},
title : {reflect : true},
foo : {}
};
}
name: string;
foo: string;
name: string|undefined;
foo = '';
changedProperties: PropertyValues|undefined = undefined;
constructor() {
super();
this.id = 'id';
this.name = 'name';
this.title = 'title';
this.foo = 'foo';
}
update(changedProperties: PropertyValues) {

@@ -1288,3 +1282,2 @@ (this as any).zot = (this as any).foo + (this as any).bar;

}
}

@@ -1295,8 +1288,7 @@ customElements.define(generateElementName(), E);

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);
el.foo = 'foo';
el.id = 'id';
el.name = 'name';
el.title = 'title';
await el.updateComplete;
assert.equal(el.shadowRoot!.textContent, 'id-name-title-foo');

@@ -1311,87 +1303,74 @@ assert.equal((window as any).id, el);

test('setting properties in `updated` does trigger update and does not block updateComplete', async () => {
class E extends LitElement {
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;
updateCount = 0;
fooMax = 2;
static get properties() { return {foo : {}}; }
foo = 0;
updateCount = 0;
fooMax = 2;
update(changed: PropertyValues) {
this.updateCount++;
super.update(changed);
}
update(changed: PropertyValues) {
this.updateCount++;
super.update(changed);
}
updated() {
if (this.foo < this.fooMax) {
this.foo++;
updated() {
if (this.foo < this.fooMax) {
this.foo++;
}
}
render() { return html``; }
}
}
customElements.define(generateElementName(), E);
const el = new E();
container.appendChild(el);
let result = await el.updateComplete;
assert.isFalse(result);
assert.equal(el.foo, 1);
assert.equal(el.updateCount, 1);
result = await el.updateComplete;
assert.isFalse(result);
assert.equal(el.foo, 2);
assert.equal(el.updateCount, 2);
result = await el.updateComplete;
assert.isTrue(result);
});
render() {
return html``;
}
test(
'setting properties in `updated` can await until updateComplete returns true',
async () => {
class E extends LitElement {
}
customElements.define(generateElementName(), E);
const el = new E();
container.appendChild(el);
let result = await el.updateComplete;
assert.isFalse(result);
assert.equal(el.foo, 1);
assert.equal(el.updateCount, 1);
result = await el.updateComplete;
assert.isFalse(result);
assert.equal(el.foo, 2);
assert.equal(el.updateCount, 2);
result = await el.updateComplete;
assert.isTrue(result);
});
static get properties() { return {foo : {}}; }
foo = 0;
updateCount = 0;
test('setting properties in `updated` can await until updateComplete returns true', async () => {
class E extends LitElement {
update(changed: PropertyValues) {
this.updateCount++;
super.update(changed);
}
static get properties() {
return {
foo: {}
};
}
foo = 0;
updateCount = 0;
updated() {
if (this.foo < 10) {
this.foo++;
}
}
update(changed: PropertyValues) {
this.updateCount++;
super.update(changed);
}
updated() {
if (this.foo < 10) {
this.foo++;
render() { return html``; }
}
}
customElements.define(generateElementName(), E);
const el = new E();
container.appendChild(el);
while (!await el.updateComplete) {
}
assert.equal(el.foo, 10);
});
render() {
return html``;
}
}
customElements.define(generateElementName(), E);
const el = new E();
container.appendChild(el);
while (!await el.updateComplete) {}
assert.equal(el.foo, 10);
});
test('`updateComplete` can block properties set in `updated`', async () => {
class E extends LitElement {
static get properties() {
return {
foo: {}
};
}
static get properties() { return {foo : {}}; }
foo = 1;

@@ -1412,5 +1391,3 @@ updateCount = 0;

render() {
return html``;
}
render() { return html``; }

@@ -1420,3 +1397,2 @@ get updateComplete(): Promise<any> {

}
}

@@ -1435,13 +1411,7 @@ customElements.define(generateElementName(), E);

static get properties() {
return {
foo: {}
};
}
static get properties() { return {foo : {}}; }
promiseFulfilled = false;
foo = 0;
render() {
return html`${this.foo}`;
}
render() { return html`${this.foo}`; }

@@ -1451,10 +1421,9 @@ get updateComplete() {

return await super.updateComplete && await new Promise((resolve) => {
setTimeout(() => {
this.promiseFulfilled = true;
resolve(true);
}, 1);
});
setTimeout(() => {
this.promiseFulfilled = true;
resolve(true);
}, 1);
});
})();
}
}

@@ -1472,13 +1441,7 @@ customElements.define(generateElementName(), E);

static get properties() {
return {
foo: {}
};
}
static get properties() { return {foo : {}}; }
promiseFulfilled = false;
foo = 0;
render() {
return html`${this.foo}`;
}
render() { return html`${this.foo}`; }

@@ -1488,10 +1451,9 @@ get updateComplete() {

return await super.updateComplete && await new Promise((resolve) => {
setTimeout(() => {
this.promiseFulfilled = true;
resolve(true);
}, 1);
});
setTimeout(() => {
this.promiseFulfilled = true;
resolve(true);
}, 1);
});
})();
}
}

@@ -1513,21 +1475,15 @@ customElements.define(generateElementName(), E);

static get properties() {
return {
foo: {}
};
}
static get properties() { return {foo : {}}; }
promiseFulfilled = false;
foo = 'hi';
render() {
return html`${this.foo}`;
}
render() { return html`${this.foo}`; }
get updateComplete() {
return super.updateComplete.then(() => new Promise((resolve) => setTimeout(() => {
this.promiseFulfilled = true;
resolve(true);
}, 1)));
return super.updateComplete.then(
() => new Promise((resolve) => setTimeout(() => {
this.promiseFulfilled = true;
resolve(true);
}, 1)));
}
}

@@ -1540,9 +1496,5 @@ customElements.define('x-1224', E);

render() {
return html`<x-1224></x-1224>`;
}
render() { return html`<x-1224></x-1224>`; }
firstUpdated() {
this.inner = this.shadowRoot!.querySelector('x-1224');
}
firstUpdated() { this.inner = this.shadowRoot!.querySelector('x-1224'); }

@@ -1555,3 +1507,2 @@ get updateComplete() {

}
}

@@ -1576,9 +1527,3 @@ customElements.define(generateElementName(), F);

class E extends LitElement {
static get properties() {
return {
foo: {},
bar: {},
zug: {}
};
}
static get properties() { return {foo : {}, bar : {}, zug : {}}; }

@@ -1589,5 +1534,3 @@ foo = '';

render() {
return html`test`;
}
render() { return html`test`; }
}

@@ -1605,7 +1548,3 @@ customElements.define(name, E);

static get properties() {
return {
foo: {},
attr: {},
bool: {type: Boolean}
};
return {foo : {}, attr : {}, bool : {type : Boolean}};
}

@@ -1615,6 +1554,3 @@ foo = 'hi';

render() {
return html`${this.foo}`;
}
render() { return html`${this.foo}`; }
}

@@ -1627,8 +1563,3 @@ customElements.define('x-2448', E);

static get properties() {
return {
bar: {},
bool: {type: Boolean}
};
}
static get properties() { return {bar : {}, bool : {type : Boolean}}; }
bar = 'outer';

@@ -1638,8 +1569,7 @@ bool = false;

render() {
return html`<x-2448 .foo="${this.bar}" attr="${this.bar}" .bool="${this.bool}"></x-2448>`;
return html`<x-2448 .foo="${this.bar}" attr="${this.bar}" .bool="${
this.bool}"></x-2448>`;
}
firstUpdated() {
this.inner = this.shadowRoot!.querySelector('x-2448');
}
firstUpdated() { this.inner = this.shadowRoot!.querySelector('x-2448'); }

@@ -1649,3 +1579,2 @@ get updateComplete() {

}
}

@@ -1668,3 +1597,2 @@ customElements.define(generateElementName(), F);

});
});

@@ -21,8 +21,7 @@ /**

export const nextFrame = () => new Promise((resolve) => requestAnimationFrame(resolve));
export const nextFrame = () =>
new Promise((resolve) => requestAnimationFrame(resolve));
export const getComputedStyleValue = (element: Element, property: string) =>
window.ShadyCSS ? window.ShadyCSS.getComputedStyleValue(element, property) :
getComputedStyle(element).getPropertyValue(property);
window.ShadyCSS ? window.ShadyCSS.getComputedStyleValue(element, property)
: getComputedStyle(element).getPropertyValue(property);

@@ -16,3 +16,3 @@ /**

import { html, LitElement, } from '../lit-element.js';
import { generateElementName, nextFrame, getComputedStyleValue } from './test-helpers.js';
import { generateElementName, getComputedStyleValue, nextFrame } from './test-helpers.js';
const assert = chai.assert;

@@ -19,0 +19,0 @@ suite('Styling', () => {

@@ -21,3 +21,3 @@ /**

import { html, LitElement, property } from '../lit-element.js';
import { stripExpressionDelimeters, generateElementName } from './test-helpers.js';
import { generateElementName, stripExpressionDelimeters } from './test-helpers.js';
const assert = chai.assert;

@@ -163,3 +163,8 @@ suite('LitElement', () => {

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

@@ -270,3 +275,8 @@ }

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

@@ -368,3 +378,8 @@ customElements.define(generateElementName(), E);

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

@@ -429,3 +444,5 @@ customElements.define(generateElementName(), E);

const fromAttribute = (value) => parseInt(value);
const toAttributeOnly = (value) => typeof value === 'string' && value.indexOf(`-attr`) > 0 ? value : `${value}-attr`;
const toAttributeOnly = (value) => typeof value === 'string' && value.indexOf(`-attr`) > 0
? value
: `${value}-attr`;
const toAttribute = (value) => `${value}-attr`;

@@ -449,3 +466,7 @@ class E extends LitElement {

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

@@ -488,11 +509,4 @@ }

}
static get properties() {
return {
foo: {},
[zug]: {}
};
}
render() {
return html ``;
}
static get properties() { return { foo: {}, [zug]: {} }; }
render() { return html ``; }
update(changedProperties) {

@@ -527,3 +541,7 @@ this.updateCount++;

return {
[zug]: { attribute: 'zug', reflect: true, type: (value) => Number(value) + 100 }
[zug]: {
attribute: 'zug',
reflect: true,
type: (value) => Number(value) + 100
}
};

@@ -535,5 +553,3 @@ }

}
render() {
return html ``;
}
render() { return html ``; }
}

@@ -605,3 +621,8 @@ customElements.define(generateElementName(), E);

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

@@ -690,6 +711,3 @@ }

static get properties() {
return {
foo: { attribute: false },
nug: {}
};
return { foo: { attribute: false }, nug: {} };
}

@@ -727,3 +745,6 @@ render() { return html ``; }

return {
foo: { reflect: true, type: { toAttribute: (value) => `${value}${suffix}` } }
foo: {
reflect: true,
type: { toAttribute: (value) => `${value}${suffix}` }
}
};

@@ -749,5 +770,3 @@ }

static get properties() {
return {
bar: { type: Boolean, reflect: true }
};
return { bar: { type: Boolean, reflect: true } };
}

@@ -796,8 +815,3 @@ render() { return html ``; }

}
static get properties() {
return {
value: {},
attrValue: {}
};
}
static get properties() { return { value: {}, attrValue: {} }; }
render() { return html ``; }

@@ -841,5 +855,3 @@ update(props) {

}
static get properties() {
return { foo: {} };
}
static get properties() { return { foo: {} }; }
render() { return html `${this.foo}`; }

@@ -897,3 +909,8 @@ }

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

@@ -993,2 +1010,46 @@ }

});
test('`firstUpdated` called when element first updates even if first `shouldUpdate` returned false', async () => {
class E extends LitElement {
constructor() {
super(...arguments);
this.foo = 1;
this.triedToUpdatedCount = 0;
this.wasUpdatedCount = 0;
this.wasFirstUpdated = 0;
}
shouldUpdate() {
this.triedToUpdatedCount++;
return this.triedToUpdatedCount > 1;
}
update(changedProperties) {
this.wasUpdatedCount++;
super.update(changedProperties);
}
render() { return html ``; }
firstUpdated(changedProperties) {
this.changedProperties = changedProperties;
this.wasFirstUpdated++;
}
}
__decorate([
property()
], E.prototype, "foo", void 0);
customElements.define(generateElementName(), E);
const el = new E();
container.appendChild(el);
await el.updateComplete;
assert.equal(el.triedToUpdatedCount, 1);
assert.equal(el.wasUpdatedCount, 0);
assert.equal(el.wasFirstUpdated, 0);
await el.requestUpdate();
const testMap = new Map();
assert.deepEqual(el.changedProperties, testMap);
assert.equal(el.triedToUpdatedCount, 2);
assert.equal(el.wasUpdatedCount, 1);
assert.equal(el.wasFirstUpdated, 1);
await el.requestUpdate();
assert.equal(el.triedToUpdatedCount, 3);
assert.equal(el.wasUpdatedCount, 2);
assert.equal(el.wasFirstUpdated, 1);
});
test('render lifecycle order', async () => {

@@ -1000,7 +1061,3 @@ class E extends LitElement {

}
static get properties() {
return {
foo: { type: Number }
};
}
static get properties() { return { foo: { type: Number } }; }
shouldUpdate() {

@@ -1019,8 +1076,4 @@ this.info.push('shouldUpdate');

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

@@ -1032,3 +1085,6 @@ customElements.define(generateElementName(), E);

el.info.push('updateComplete');
assert.deepEqual(el.info, ['shouldUpdate', 'before-update', 'render', 'after-update', 'firstUpdated', 'updated', 'updateComplete']);
assert.deepEqual(el.info, [
'shouldUpdate', 'before-update', 'render', 'after-update', 'firstUpdated',
'updated', 'updateComplete'
]);
});

@@ -1043,7 +1099,3 @@ test('setting properties in update does not trigger update', async () => {

}
static get properties() {
return {
foo: {}
};
}
static get properties() { return { foo: {} }; }
update(props) {

@@ -1054,5 +1106,3 @@ this.updateCount++;

}
render() {
return html `${this.foo}`;
}
render() { return html `${this.foo}`; }
}

@@ -1079,7 +1129,3 @@ customElements.define(generateElementName(), E);

static get properties() {
return {
foo: {},
bar: {},
zot: { reflect: true }
};
return { foo: {}, bar: {}, zot: { reflect: true } };
}

@@ -1091,5 +1137,3 @@ update(changedProperties) {

}
render() {
return html ``;
}
render() { return html ``; }
}

@@ -1127,11 +1171,10 @@ customElements.define(generateElementName(), E);

});
// Note, on older browsers (e.g. old Safari/Chrome), native properties
// cannot have default values. These will be overwritten by instance values.
test('can make properties for native accessors', async () => {
class E extends LitElement {
constructor() {
super();
super(...arguments);
this.foo = '';
this.changedProperties = undefined;
this.id = 'id';
this.name = 'name';
this.title = 'title';
this.foo = 'foo';
}

@@ -1159,8 +1202,7 @@ static get properties() {

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);
el.foo = 'foo';
el.id = 'id';
el.name = 'name';
el.title = 'title';
await el.updateComplete;
assert.equal(el.shadowRoot.textContent, 'id-name-title-foo');

@@ -1182,7 +1224,3 @@ assert.equal(window.id, el);

}
static get properties() {
return {
foo: {}
};
}
static get properties() { return { foo: {} }; }
update(changed) {

@@ -1197,5 +1235,3 @@ this.updateCount++;

}
render() {
return html ``;
}
render() { return html ``; }
}

@@ -1223,7 +1259,3 @@ customElements.define(generateElementName(), E);

}
static get properties() {
return {
foo: {}
};
}
static get properties() { return { foo: {} }; }
update(changed) {

@@ -1238,5 +1270,3 @@ this.updateCount++;

}
render() {
return html ``;
}
render() { return html ``; }
}

@@ -1246,3 +1276,4 @@ customElements.define(generateElementName(), E);

container.appendChild(el);
while (!await el.updateComplete) { }
while (!await el.updateComplete) {
}
assert.equal(el.foo, 10);

@@ -1258,7 +1289,3 @@ });

}
static get properties() {
return {
foo: {}
};
}
static get properties() { return { foo: {} }; }
update(changed) {

@@ -1273,5 +1300,3 @@ this.updateCount++;

}
render() {
return html ``;
}
render() { return html ``; }
get updateComplete() {

@@ -1296,10 +1321,4 @@ return super.updateComplete.then((v) => v || this.updateComplete);

}
static get properties() {
return {
foo: {}
};
}
render() {
return html `${this.foo}`;
}
static get properties() { return { foo: {} }; }
render() { return html `${this.foo}`; }
get updateComplete() {

@@ -1330,10 +1349,4 @@ return (async () => {

}
static get properties() {
return {
foo: {}
};
}
render() {
return html `${this.foo}`;
}
static get properties() { return { foo: {} }; }
render() { return html `${this.foo}`; }
get updateComplete() {

@@ -1368,10 +1381,4 @@ return (async () => {

}
static get properties() {
return {
foo: {}
};
}
render() {
return html `${this.foo}`;
}
static get properties() { return { foo: {} }; }
render() { return html `${this.foo}`; }
get updateComplete() {

@@ -1390,8 +1397,4 @@ return super.updateComplete.then(() => new Promise((resolve) => setTimeout(() => {

}
render() {
return html `<x-1224></x-1224>`;
}
firstUpdated() {
this.inner = this.shadowRoot.querySelector('x-1224');
}
render() { return html `<x-1224></x-1224>`; }
firstUpdated() { this.inner = this.shadowRoot.querySelector('x-1224'); }
get updateComplete() {

@@ -1427,12 +1430,4 @@ return super.updateComplete.then(() => {

}
static get properties() {
return {
foo: {},
bar: {},
zug: {}
};
}
render() {
return html `test`;
}
static get properties() { return { foo: {}, bar: {}, zug: {} }; }
render() { return html `test`; }
}

@@ -1453,11 +1448,5 @@ customElements.define(name, E);

static get properties() {
return {
foo: {},
attr: {},
bool: { type: Boolean }
};
return { foo: {}, attr: {}, bool: { type: Boolean } };
}
render() {
return html `${this.foo}`;
}
render() { return html `${this.foo}`; }
}

@@ -1472,14 +1461,7 @@ customElements.define('x-2448', E);

}
static get properties() {
return {
bar: {},
bool: { type: Boolean }
};
}
static get properties() { return { bar: {}, bool: { type: Boolean } }; }
render() {
return html `<x-2448 .foo="${this.bar}" attr="${this.bar}" .bool="${this.bool}"></x-2448>`;
}
firstUpdated() {
this.inner = this.shadowRoot.querySelector('x-2448');
}
firstUpdated() { this.inner = this.shadowRoot.querySelector('x-2448'); }
get updateComplete() {

@@ -1486,0 +1468,0 @@ return super.updateComplete.then(() => this.inner.updateComplete);

@@ -18,4 +18,4 @@ /**

export const nextFrame = () => new Promise((resolve) => requestAnimationFrame(resolve));
export const getComputedStyleValue = (element, property) => window.ShadyCSS ? window.ShadyCSS.getComputedStyleValue(element, property) :
getComputedStyle(element).getPropertyValue(property);
export const getComputedStyleValue = (element, property) => window.ShadyCSS ? window.ShadyCSS.getComputedStyleValue(element, property)
: getComputedStyle(element).getPropertyValue(property);
//# sourceMappingURL=test-helpers.js.map

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

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