diff --git a/.circleci/config.yml b/.circleci/config.yml
index e140409c4..1e22885ff 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -57,6 +57,7 @@ jobs:
command: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
- run: git config --global user.email circleci@circleci
- run: git config --global user.name CircleCI
+ - run: npm run build:types
- run:
name: Publish package
command: "./node_modules/.bin/lerna publish --message 'chore: release new versions' --exact --yes"
diff --git a/.gitignore b/.gitignore
index 1df5c5078..c0ec93709 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,6 +20,10 @@ yarn-error.log
/build/
/bundlesize/dist/
+## types
+*.d.ts
+!packages/*/types/*
+
## temp folders
/.tmp/
/coverage/
diff --git a/package.json b/package.json
index 4f19dcd20..3ee31dc1a 100644
--- a/package.json
+++ b/package.json
@@ -6,6 +6,8 @@
"packages/*"
],
"scripts": {
+ "build:docs": "wca analyze \"packages/tabs/**/*.js\"",
+ "build:types": "tsc -p tsconfig.build.types.json",
"bundlesize": "rollup -c bundlesize/rollup.config.js && bundlesize",
"dev-server": "es-dev-server",
"format": "npm run format:eslint && npm run format:prettier",
@@ -16,6 +18,7 @@
"lint:eslint": "eslint --ext .js,.html .",
"lint:markdownlint": "git ls-files '*.md' | xargs markdownlint --ignore '**/CHANGELOG.md'",
"lint:prettier": "prettier \"**/*.js\" --list-different || (echo '↑↑ these files are not prettier formatted ↑↑' && exit 1)",
+ "lint:types": "tsc",
"lint:versions": "node ./scripts/lint-versions.js",
"start": "npm run storybook",
"storybook": "start-storybook -p 9001",
@@ -33,10 +36,11 @@
"@open-wc/building-rollup": "^1.2.1",
"@open-wc/demoing-storybook": "^2.0.2",
"@open-wc/eslint-config": "^1.0.0",
- "@open-wc/testing": "^2.5.0",
+ "@open-wc/testing": "^2.5.18",
"@open-wc/testing-helpers": "^1.0.0",
"@storybook/addon-a11y": "~5.0.0",
"@web/test-runner": "^0.6.18",
+ "@types/chai-dom": "^0.0.8",
"@webcomponents/webcomponentsjs": "^2.2.5",
"babel-eslint": "^8.2.6",
"babel-polyfill": "^6.26.0",
@@ -56,6 +60,8 @@
"rimraf": "^2.6.3",
"rollup": "^2.0.0",
"sinon": "^7.2.2",
+ "typescript": "^3.8.3",
+ "web-component-analyzer": "^1.0.3",
"webpack-merge": "^4.1.5",
"whatwg-fetch": "^3.0.0"
},
diff --git a/packages/accordion/package.json b/packages/accordion/package.json
index 2fa567917..2cc9ca1b2 100644
--- a/packages/accordion/package.json
+++ b/packages/accordion/package.json
@@ -13,11 +13,14 @@
"main": "index.js",
"module": "index.js",
"files": [
+ "*.d.ts",
"*.js",
"docs",
"src",
"test",
- "translations"
+ "test-helpers",
+ "translations",
+ "types"
],
"scripts": {
"prepublishOnly": "../../scripts/npm-prepublish.js",
diff --git a/packages/ajax/package.json b/packages/ajax/package.json
index edef4c223..1c998c981 100644
--- a/packages/ajax/package.json
+++ b/packages/ajax/package.json
@@ -13,11 +13,14 @@
"main": "index.js",
"module": "index.js",
"files": [
+ "*.d.ts",
"*.js",
"docs",
"src",
"test",
- "translations"
+ "test-helpers",
+ "translations",
+ "types"
],
"scripts": {
"prepublishOnly": "../../scripts/npm-prepublish.js",
diff --git a/packages/button/package.json b/packages/button/package.json
index 4b95097f5..25afc4c13 100644
--- a/packages/button/package.json
+++ b/packages/button/package.json
@@ -13,11 +13,14 @@
"main": "index.js",
"module": "index.js",
"files": [
+ "*.d.ts",
"*.js",
"docs",
"src",
"test",
- "translations"
+ "test-helpers",
+ "translations",
+ "types"
],
"scripts": {
"prepublishOnly": "../../scripts/npm-prepublish.js",
diff --git a/packages/calendar/package.json b/packages/calendar/package.json
index e0f28dec4..d1b2ae038 100644
--- a/packages/calendar/package.json
+++ b/packages/calendar/package.json
@@ -13,12 +13,14 @@
"main": "index.js",
"module": "index.js",
"files": [
+ "*.d.ts",
"*.js",
"docs",
"src",
"test",
"test-helpers",
- "translations"
+ "translations",
+ "types"
],
"scripts": {
"prepublishOnly": "../../scripts/npm-prepublish.js",
diff --git a/packages/checkbox-group/package.json b/packages/checkbox-group/package.json
index 3dfb6baf0..a338c99e2 100644
--- a/packages/checkbox-group/package.json
+++ b/packages/checkbox-group/package.json
@@ -13,11 +13,14 @@
"main": "index.js",
"module": "index.js",
"files": [
+ "*.d.ts",
"*.js",
"docs",
"src",
"test",
- "translations"
+ "test-helpers",
+ "translations",
+ "types"
],
"scripts": {
"prepublishOnly": "../../scripts/npm-prepublish.js",
diff --git a/packages/core/index.js b/packages/core/index.js
index 0985137e7..5b06d917f 100644
--- a/packages/core/index.js
+++ b/packages/core/index.js
@@ -1,8 +1,3 @@
-/**
- * Info for TypeScript users:
- * For now please import types from lit-element and lit-html directly.
- */
-
// lit-element
export {
css,
diff --git a/packages/core/package.json b/packages/core/package.json
index 8f19358eb..c0a367df0 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -13,11 +13,14 @@
"main": "index.js",
"module": "index.js",
"files": [
+ "*.d.ts",
"*.js",
"docs",
"src",
+ "test",
"test-helpers",
- "translations"
+ "translations",
+ "types"
],
"scripts": {
"prepublishOnly": "../../scripts/npm-prepublish.js",
@@ -26,7 +29,7 @@
},
"sideEffects": false,
"dependencies": {
- "@open-wc/dedupe-mixin": "^1.2.1",
+ "@open-wc/dedupe-mixin": "^1.2.18",
"@open-wc/scoped-elements": "^1.0.3",
"lit-element": "^2.2.1",
"lit-html": "^1.0.0"
diff --git a/packages/core/src/DelegateMixin.js b/packages/core/src/DelegateMixin.js
index 04f5ee9a1..3c4dba16c 100644
--- a/packages/core/src/DelegateMixin.js
+++ b/packages/core/src/DelegateMixin.js
@@ -1,203 +1,177 @@
/* eslint-disable class-methods-use-this */
-
import { dedupeMixin } from '@open-wc/dedupe-mixin';
/**
- * # DelegateMixin
- * Forwards defined events, methods, properties and attributes to the defined target.
- *
- * @example
- * get delegations() {
- * return {
- * ...super.delegations,
- * target: () => this.shadowRoot.getElementById('button1'),
- * events: ['click'],
- * methods: ['click'],
- * properties: ['disabled'],
- * attributes: ['disabled'],
- * };
- * }
- *
- * render() {
- * return html`
- * with delegation
- * `;
- * }
- *
- * @type {function()}
- * @polymerMixin
- * @mixinFunction
+ * @typedef {import('../types/DelegateMixinTypes').DelegateMixin} DelegateMixin
*/
-export const DelegateMixin = dedupeMixin(
- superclass =>
- // eslint-disable-next-line
- class DelegateMixin extends superclass {
- constructor() {
- super();
- this.__eventsQueue = [];
- this.__propertiesQueue = {};
- this.__setupPropertyDelegation();
- }
- /**
- * @returns {{target: null, events: Array, methods: Array, properties: Array, attributes: Array}}
- */
- get delegations() {
- return {
- target: null,
- events: [],
- methods: [],
- properties: [],
- attributes: [],
- };
- }
+/**
+ * @typedef DelegateEvent
+ * @property {string} type - Type of event
+ * @property {Array>} args - Event arguments
+ */
- connectedCallback() {
- if (super.connectedCallback) {
- super.connectedCallback();
- }
- this._connectDelegateMixin();
- }
+/** @type {DelegateMixin} */
+const DelegateMixinImplementation = superclass =>
+ // eslint-disable-next-line
+ class DelegateMixin extends superclass {
+ constructor() {
+ super();
- updated(...args) {
- super.updated(...args);
- this._connectDelegateMixin();
- }
+ /** @type {DelegateEvent[]} */
+ this.__eventsQueue = [];
- /**
- * @param {string} type
- * @param {Object} args
- */
- addEventListener(type, ...args) {
- const delegatedEvents = this.delegations.events;
- if (delegatedEvents.indexOf(type) > -1) {
- if (this.delegationTarget) {
- this.delegationTarget.addEventListener(type, ...args);
- } else {
- this.__eventsQueue.push({ type, args });
- }
+ /** @type {Object.} */
+ this.__propertiesQueue = {};
+ this.__setupPropertyDelegation();
+ }
+
+ /**
+ * @returns {{target: Function, events: string[], methods: string[], properties: string[], attributes: string[]}}
+ */
+ get delegations() {
+ return {
+ target: () => {},
+ events: [],
+ methods: [],
+ properties: [],
+ attributes: [],
+ };
+ }
+
+ connectedCallback() {
+ if (super.connectedCallback) {
+ super.connectedCallback();
+ }
+ this._connectDelegateMixin();
+ }
+
+ /** @param {import('lit-element').PropertyValues } changedProperties */
+ updated(changedProperties) {
+ super.updated(changedProperties);
+ this._connectDelegateMixin();
+ }
+
+ /**
+ * @param {string} type
+ * @param {...Object} args
+ */
+ addEventListener(type, ...args) {
+ const delegatedEvents = this.delegations.events;
+ if (delegatedEvents.indexOf(type) > -1) {
+ if (this.delegationTarget) {
+ this.delegationTarget.addEventListener(type, ...args);
} else {
- super.addEventListener(type, ...args);
+ this.__eventsQueue.push({ type, args });
}
+ } else {
+ super.addEventListener(type, ...args);
}
+ }
- /**
- * @param {string} name
- * @param {string} value
- */
- setAttribute(name, value) {
- const attributeNames = this.delegations.attributes;
- if (attributeNames.indexOf(name) > -1) {
- if (this.delegationTarget) {
- this.delegationTarget.setAttribute(name, value);
- }
- super.removeAttribute(name);
- } else {
- super.setAttribute(name, value);
- }
- }
-
- /**
- * @param {string} name
- */
- removeAttribute(name) {
- const attributeNames = this.delegations.attributes;
- if (attributeNames.indexOf(name) > -1) {
- if (this.delegationTarget) {
- this.delegationTarget.removeAttribute(name);
- }
+ /**
+ * @param {string} name
+ * @param {string} value
+ */
+ setAttribute(name, value) {
+ const attributeNames = this.delegations.attributes;
+ if (attributeNames.indexOf(name) > -1) {
+ if (this.delegationTarget) {
+ this.delegationTarget.setAttribute(name, value);
}
super.removeAttribute(name);
+ } else {
+ super.setAttribute(name, value);
}
+ }
- /**
- * @protected
- */
- _connectDelegateMixin() {
- if (this.__connectedDelegateMixin) return;
-
- if (!this.delegationTarget) {
- this.delegationTarget = this.delegations.target();
- }
-
+ /**
+ * @param {string} name
+ */
+ removeAttribute(name) {
+ const attributeNames = this.delegations.attributes;
+ if (attributeNames.indexOf(name) > -1) {
if (this.delegationTarget) {
- this.__emptyEventListenerQueue();
- this.__emptyPropertiesQueue();
- this.__initialAttributeDelegation();
-
- this.__connectedDelegateMixin = true;
+ this.delegationTarget.removeAttribute(name);
}
}
+ super.removeAttribute(name);
+ }
- /**
- * @private
- */
- __setupPropertyDelegation() {
- const propertyNames = this.delegations.properties.concat(this.delegations.methods);
- propertyNames.forEach(propertyName => {
- Object.defineProperty(this, propertyName, {
- get() {
- const target = this.delegationTarget;
- if (target) {
- if (typeof target[propertyName] === 'function') {
- return target[propertyName].bind(target);
- }
- return target[propertyName];
+ _connectDelegateMixin() {
+ if (this.__connectedDelegateMixin) return;
+
+ if (!this.delegationTarget) {
+ this.delegationTarget = this.delegations.target();
+ }
+
+ if (this.delegationTarget) {
+ this.__emptyEventListenerQueue();
+ this.__emptyPropertiesQueue();
+ this.__initialAttributeDelegation();
+
+ this.__connectedDelegateMixin = true;
+ }
+ }
+
+ __setupPropertyDelegation() {
+ const propertyNames = this.delegations.properties.concat(this.delegations.methods);
+ propertyNames.forEach(propertyName => {
+ Object.defineProperty(this, propertyName, {
+ get() {
+ const target = this.delegationTarget;
+ if (target) {
+ if (typeof target[propertyName] === 'function') {
+ return target[propertyName].bind(target);
}
- if (this.__propertiesQueue[propertyName]) {
- return this.__propertiesQueue[propertyName];
+ return target[propertyName];
+ }
+ if (this.__propertiesQueue[propertyName]) {
+ return this.__propertiesQueue[propertyName];
+ }
+ // This is the moment the attribute is not delegated (and thus removed) yet.
+ // and the property is not set, but the attribute is (it serves as a fallback for
+ // __propertiesQueue).
+ return this.getAttribute(propertyName);
+ },
+ set(newValue) {
+ if (this.delegationTarget) {
+ const oldValue = this.delegationTarget[propertyName];
+ this.delegationTarget[propertyName] = newValue;
+ // connect with observer system if available
+ if (typeof this.triggerObserversFor === 'function') {
+ this.triggerObserversFor(propertyName, newValue, oldValue);
}
- // This is the moment the attribute is not delegated (and thus removed) yet.
- // and the property is not set, but the attribute is (it serves as a fallback for
- // __propertiesQueue).
- return this.getAttribute(propertyName);
- },
- set(newValue) {
- if (this.delegationTarget) {
- const oldValue = this.delegationTarget[propertyName];
- this.delegationTarget[propertyName] = newValue;
- // connect with observer system if available
- if (typeof this.triggerObserversFor === 'function') {
- this.triggerObserversFor(propertyName, newValue, oldValue);
- }
- } else {
- this.__propertiesQueue[propertyName] = newValue;
- }
- },
- });
+ } else {
+ this.__propertiesQueue[propertyName] = newValue;
+ }
+ },
});
- }
+ });
+ }
- /**
- * @private
- */
- __initialAttributeDelegation() {
- const attributeNames = this.delegations.attributes;
- attributeNames.forEach(attributeName => {
- const attributeValue = this.getAttribute(attributeName);
- if (typeof attributeValue === 'string') {
- this.delegationTarget.setAttribute(attributeName, attributeValue);
- super.removeAttribute(attributeName);
- }
- });
- }
+ __initialAttributeDelegation() {
+ const attributeNames = this.delegations.attributes;
+ attributeNames.forEach(attributeName => {
+ const attributeValue = this.getAttribute(attributeName);
+ if (typeof attributeValue === 'string') {
+ this.delegationTarget.setAttribute(attributeName, attributeValue);
+ super.removeAttribute(attributeName);
+ }
+ });
+ }
- /**
- * @private
- */
- __emptyEventListenerQueue() {
- this.__eventsQueue.forEach(ev => {
- this.delegationTarget.addEventListener(ev.type, ...ev.args);
- });
- }
+ __emptyEventListenerQueue() {
+ this.__eventsQueue.forEach(ev => {
+ this.delegationTarget.addEventListener(ev.type, ...ev.args);
+ });
+ }
- /**
- * @private
- */
- __emptyPropertiesQueue() {
- Object.keys(this.__propertiesQueue).forEach(propName => {
- this.delegationTarget[propName] = this.__propertiesQueue[propName];
- });
- }
- },
-);
+ __emptyPropertiesQueue() {
+ Object.keys(this.__propertiesQueue).forEach(propName => {
+ this.delegationTarget[propName] = this.__propertiesQueue[propName];
+ });
+ }
+ };
+
+export const DelegateMixin = dedupeMixin(DelegateMixinImplementation);
diff --git a/packages/core/src/DisabledMixin.js b/packages/core/src/DisabledMixin.js
index 3119670f6..1dea190e8 100644
--- a/packages/core/src/DisabledMixin.js
+++ b/packages/core/src/DisabledMixin.js
@@ -1,64 +1,67 @@
import { dedupeMixin } from '@open-wc/dedupe-mixin';
/**
- * #DisabledMixin
- *
- * @polymerMixin
- * @mixinFunction
+ * @typedef {import('../types/DisabledMixinTypes').DisabledMixin} DisabledMixin
*/
-export const DisabledMixin = dedupeMixin(
- superclass =>
- // eslint-disable-next-line no-shadow
- class DisabledMixin extends superclass {
- static get properties() {
- return {
- disabled: {
- type: Boolean,
- reflect: true,
- },
- };
- }
- constructor() {
- super();
+/** @type {DisabledMixin} */
+const DisabledMixinImplementation = superclass =>
+ // eslint-disable-next-line no-shadow
+ class DisabledMixinHost extends superclass {
+ static get properties() {
+ return {
+ disabled: {
+ type: Boolean,
+ reflect: true,
+ },
+ };
+ }
+
+ constructor() {
+ super();
+ this.__requestedToBeDisabled = false;
+ this.__isUserSettingDisabled = true;
+ this.__restoreDisabledTo = false;
+ this.disabled = false;
+ }
+
+ makeRequestToBeDisabled() {
+ if (this.__requestedToBeDisabled === false) {
+ this.__requestedToBeDisabled = true;
+ this.__restoreDisabledTo = this.disabled;
+ this.__internalSetDisabled(true);
+ }
+ }
+
+ retractRequestToBeDisabled() {
+ if (this.__requestedToBeDisabled === true) {
this.__requestedToBeDisabled = false;
- this.__isUserSettingDisabled = true;
-
- this.__restoreDisabledTo = false;
- this.disabled = false;
+ this.__internalSetDisabled(this.__restoreDisabledTo);
}
+ }
- makeRequestToBeDisabled() {
- if (this.__requestedToBeDisabled === false) {
- this.__requestedToBeDisabled = true;
+ /** @param {boolean} value */
+ __internalSetDisabled(value) {
+ this.__isUserSettingDisabled = false;
+ this.disabled = value;
+ this.__isUserSettingDisabled = true;
+ }
+
+ /**
+ * @param {PropertyKey} name
+ * @param {?} oldValue
+ */
+ _requestUpdate(name, oldValue) {
+ super._requestUpdate(name, oldValue);
+ if (name === 'disabled') {
+ if (this.__isUserSettingDisabled) {
this.__restoreDisabledTo = this.disabled;
+ }
+ if (this.disabled === false && this.__requestedToBeDisabled === true) {
this.__internalSetDisabled(true);
}
}
+ }
+ };
- retractRequestToBeDisabled() {
- if (this.__requestedToBeDisabled === true) {
- this.__requestedToBeDisabled = false;
- this.__internalSetDisabled(this.__restoreDisabledTo);
- }
- }
-
- __internalSetDisabled(value) {
- this.__isUserSettingDisabled = false;
- this.disabled = value;
- this.__isUserSettingDisabled = true;
- }
-
- _requestUpdate(name, oldValue) {
- super._requestUpdate(name, oldValue);
- if (name === 'disabled') {
- if (this.__isUserSettingDisabled) {
- this.__restoreDisabledTo = this.disabled;
- }
- if (this.disabled === false && this.__requestedToBeDisabled === true) {
- this.__internalSetDisabled(true);
- }
- }
- }
- },
-);
+export const DisabledMixin = dedupeMixin(DisabledMixinImplementation);
diff --git a/packages/core/src/DisabledWithTabIndexMixin.js b/packages/core/src/DisabledWithTabIndexMixin.js
index e0570bacf..0ecea056e 100644
--- a/packages/core/src/DisabledWithTabIndexMixin.js
+++ b/packages/core/src/DisabledWithTabIndexMixin.js
@@ -2,85 +2,91 @@ import { dedupeMixin } from '@open-wc/dedupe-mixin';
import { DisabledMixin } from './DisabledMixin.js';
/**
- * #DisabledWithTabIndexMixin
- *
- * @polymerMixin
- * @mixinFunction
+ * @typedef {import('../types/DisabledWithTabIndexMixinTypes').DisabledWithTabIndexMixin} DisabledWithTabIndexMixin
*/
-export const DisabledWithTabIndexMixin = dedupeMixin(
- superclass =>
- // eslint-disable-next-line no-shadow
- class DisabledWithTabIndexMixin extends DisabledMixin(superclass) {
- static get properties() {
- return {
- // we use a property here as if we use the native tabIndex we can not set a default value
- // in the constructor as it synchronously sets the attribute which is not allowed in the
- // constructor phase
- tabIndex: {
- type: Number,
- reflect: true,
- attribute: 'tabindex',
- },
- };
+
+/** @type {DisabledWithTabIndexMixin} */
+const DisabledWithTabIndexMixinImplementation = superclass =>
+ // eslint-disable-next-line no-shadow
+ class DisabledWithTabIndexMixinHost extends DisabledMixin(superclass) {
+ static get properties() {
+ return {
+ // we use a property here as if we use the native tabIndex we can not set a default value
+ // in the constructor as it synchronously sets the attribute which is not allowed in the
+ // constructor phase
+ tabIndex: {
+ type: Number,
+ reflect: true,
+ attribute: 'tabindex',
+ },
+ };
+ }
+
+ constructor() {
+ super();
+ this.__isUserSettingTabIndex = true;
+ this.__restoreTabIndexTo = 0;
+ this.__internalSetTabIndex(0);
+ }
+
+ makeRequestToBeDisabled() {
+ super.makeRequestToBeDisabled();
+ if (this.__requestedToBeDisabled === false && this.tabIndex != null) {
+ this.__restoreTabIndexTo = this.tabIndex;
}
+ }
- constructor() {
- super();
- this.__isUserSettingTabIndex = true;
-
- this.__restoreTabIndexTo = 0;
- this.__internalSetTabIndex(0);
+ retractRequestToBeDisabled() {
+ super.retractRequestToBeDisabled();
+ if (this.__requestedToBeDisabled === true) {
+ this.__internalSetTabIndex(this.__restoreTabIndexTo);
}
+ }
- makeRequestToBeDisabled() {
- super.makeRequestToBeDisabled();
- if (this.__requestedToBeDisabled === false) {
- this.__restoreTabIndexTo = this.tabIndex;
- }
- }
+ /**
+ * @param {number} value
+ */
+ __internalSetTabIndex(value) {
+ this.__isUserSettingTabIndex = false;
+ this.tabIndex = value;
+ this.__isUserSettingTabIndex = true;
+ }
- retractRequestToBeDisabled() {
- super.retractRequestToBeDisabled();
- if (this.__requestedToBeDisabled === true) {
+ /**
+ * @param {PropertyKey} name
+ * @param {?} oldValue
+ */
+ _requestUpdate(name, oldValue) {
+ super._requestUpdate(name, oldValue);
+
+ if (name === 'disabled') {
+ if (this.disabled) {
+ this.__internalSetTabIndex(-1);
+ } else {
this.__internalSetTabIndex(this.__restoreTabIndexTo);
}
}
- __internalSetTabIndex(value) {
- this.__isUserSettingTabIndex = false;
- this.tabIndex = value;
- this.__isUserSettingTabIndex = true;
- }
-
- _requestUpdate(name, oldValue) {
- super._requestUpdate(name, oldValue);
-
- if (name === 'disabled') {
- if (this.disabled) {
- this.__internalSetTabIndex(-1);
- } else {
- this.__internalSetTabIndex(this.__restoreTabIndexTo);
- }
+ if (name === 'tabIndex') {
+ if (this.__isUserSettingTabIndex && this.tabIndex != null) {
+ this.__restoreTabIndexTo = this.tabIndex;
}
- if (name === 'tabIndex') {
- if (this.__isUserSettingTabIndex) {
- this.__restoreTabIndexTo = this.tabIndex;
- }
-
- if (this.tabIndex !== -1 && this.__requestedToBeDisabled === true) {
- this.__internalSetTabIndex(-1);
- }
- }
- }
-
- firstUpdated(changedProperties) {
- super.firstUpdated(changedProperties);
- // for ShadyDom the timing is a little different and we need to make sure
- // the tabindex gets correctly updated here
- if (this.disabled) {
+ if (this.tabIndex !== -1 && this.__requestedToBeDisabled === true) {
this.__internalSetTabIndex(-1);
}
}
- },
-);
+ }
+
+ /** @param {import('lit-element').PropertyValues } changedProperties */
+ firstUpdated(changedProperties) {
+ super.firstUpdated(changedProperties);
+ // for ShadyDom the timing is a little different and we need to make sure
+ // the tabindex gets correctly updated here
+ if (this.disabled) {
+ this.__internalSetTabIndex(-1);
+ }
+ }
+ };
+
+export const DisabledWithTabIndexMixin = dedupeMixin(DisabledWithTabIndexMixinImplementation);
diff --git a/packages/core/src/LionSingleton.js b/packages/core/src/LionSingleton.js
index 5353ff8c5..de864ab3f 100644
--- a/packages/core/src/LionSingleton.js
+++ b/packages/core/src/LionSingleton.js
@@ -6,10 +6,11 @@
*/
export class LionSingleton {
/**
- * @param {function()} mixin
+ * @param {function} mixin
*/
static addInstanceMixin(mixin) {
if (!this.__instanceMixins) {
+ /** @type {function[]} */
this.__instanceMixins = [];
}
this.__instanceMixins.push(mixin);
@@ -26,6 +27,8 @@ export class LionSingleton {
Klass = mixin(Klass);
});
}
+ // Ignoring, because it's up to the extension layer to accept arguments in its constructor
+ // @ts-ignore-next-line
return new Klass(...args);
}
diff --git a/packages/core/src/SlotMixin.js b/packages/core/src/SlotMixin.js
index 5d1e81f76..7ccc31b34 100644
--- a/packages/core/src/SlotMixin.js
+++ b/packages/core/src/SlotMixin.js
@@ -1,78 +1,59 @@
/* eslint-disable class-methods-use-this */
-
import { dedupeMixin } from '@open-wc/dedupe-mixin';
+
/**
- * # SlotMixin
- * `SlotMixin`, when attached to the DOM it creates content for defined slots in the Light DOM.
- * The content element is created using a factory function and is assigned a slot name from the key.
- * Existing slot content is not overridden.
- *
- * The purpose is to have the default content in the Light DOM rather than hidden in Shadow DOM
- * like default slot content works natively.
- *
- * @example
- * get slots() {
- * return {
- * ...super.slots,
- * // appends
to the Light DOM of this element
- * foo: () => document.createElement('div'),
- * };
- * }
- *
- * @type {function()}
- * @polymerMixin
- * @mixinFunction
+ * @typedef {import('../types/SlotMixinTypes').SlotMixin} SlotMixin
+ * @typedef {import('../types/SlotMixinTypes').SlotsMap} SlotsMap
*/
-export const SlotMixin = dedupeMixin(
- superclass =>
- // eslint-disable-next-line no-unused-vars, no-shadow
- class SlotMixin extends superclass {
- /**
- * @returns {{}}
- */
- get slots() {
- return {};
- }
- constructor() {
- super();
- this.__privateSlots = new Set(null);
- }
+/** @type {SlotMixin} */
+const SlotMixinImplementation = superclass =>
+ // eslint-disable-next-line no-unused-vars, no-shadow
+ class SlotMixinHost extends superclass {
+ /**
+ * @return {SlotsMap}
+ */
+ get slots() {
+ return {};
+ }
- connectedCallback() {
- if (super.connectedCallback) {
- super.connectedCallback();
- }
- this._connectSlotMixin();
- }
+ constructor() {
+ super();
+ this.__privateSlots = new Set(null);
+ }
- /**
- * @protected
- */
- _connectSlotMixin() {
- if (!this.__isConnectedSlotMixin) {
- Object.keys(this.slots).forEach(slotName => {
- if (!this.querySelector(`[slot=${slotName}]`)) {
- const slotFactory = this.slots[slotName];
- const slotContent = slotFactory();
- if (slotContent instanceof Element) {
- slotContent.setAttribute('slot', slotName);
- this.appendChild(slotContent);
- this.__privateSlots.add(slotName);
- } // ignore non-elements to enable conditional slots
+ connectedCallback() {
+ if (super.connectedCallback) {
+ super.connectedCallback();
+ }
+ this._connectSlotMixin();
+ }
+
+ _connectSlotMixin() {
+ if (!this.__isConnectedSlotMixin) {
+ Object.keys(this.slots).forEach(slotName => {
+ if (!this.querySelector(`[slot=${slotName}]`)) {
+ const slotFactory = this.slots[slotName];
+ const slotContent = slotFactory();
+ // ignore non-elements to enable conditional slots
+ if (slotContent instanceof Element) {
+ slotContent.setAttribute('slot', slotName);
+ this.appendChild(slotContent);
+ this.__privateSlots.add(slotName);
}
- });
- this.__isConnectedSlotMixin = true;
- }
+ }
+ });
+ this.__isConnectedSlotMixin = true;
}
+ }
- /**
- * @protected
- * @param {string} slotName Name of the slot
- * @return {boolean} true if given slot name been created by SlotMixin
- */
- _isPrivateSlot(slotName) {
- return this.__privateSlots.has(slotName);
- }
- },
-);
+ /**
+ * @param {string} slotName Name of the slot
+ * @return {boolean} true if given slot name been created by SlotMixin
+ */
+ _isPrivateSlot(slotName) {
+ return this.__privateSlots.has(slotName);
+ }
+ };
+
+export const SlotMixin = dedupeMixin(SlotMixinImplementation);
diff --git a/packages/core/src/UpdateStylesMixin.js b/packages/core/src/UpdateStylesMixin.js
index 3ad4f37e3..753760dd6 100644
--- a/packages/core/src/UpdateStylesMixin.js
+++ b/packages/core/src/UpdateStylesMixin.js
@@ -1,59 +1,78 @@
/* global ShadyCSS */
import { dedupeMixin } from '@open-wc/dedupe-mixin';
-export const UpdateStylesMixin = dedupeMixin(
- superclass =>
- // eslint-disable-next-line no-shadow
- class UpdateStylesMixin extends superclass {
- /**
- * @example
- *
- *
- *
- *
- * $0.updateStyles({'background': 'orange', '--foo': '#fff'})
- * Chrome, Firefox:
- * IE:
- * => to head:
- *
- * @param {Object} updateStyles
- */
- updateStyles(updateStyles) {
- const styleString = this.getAttribute('style') || this.getAttribute('data-style') || '';
- const currentStyles = styleString.split(';').reduce((acc, stylePair) => {
- const parts = stylePair.split(':');
- if (parts.length === 2) {
- /* eslint-disable-next-line prefer-destructuring */
- acc[parts[0]] = parts[1];
- }
- return acc;
- }, {});
+/**
+ * @typedef {import('../types/UpdateStylesMixinTypes').UpdateStylesMixin} UpdateStylesMixin
+ * @typedef {import('../types/UpdateStylesMixinTypes').StylesMap} StylesMap
+ */
- const newStyles = { ...currentStyles, ...updateStyles };
- let newStylesString = '';
- if (typeof ShadyCSS === 'object' && !ShadyCSS.nativeShadow) {
- // No ShadowDOM => IE, Edge
- const newCssVariablesObj = {};
- Object.keys(newStyles).forEach(key => {
- if (key.indexOf('--') === -1) {
- newStylesString += `${key}:${newStyles[key]};`;
- } else {
- newCssVariablesObj[key] = newStyles[key];
- }
- });
- this.setAttribute('style', newStylesString);
- ShadyCSS.styleSubtree(this, newCssVariablesObj);
- } else {
- // has shadowdom => Chrome, Firefox, Safari
- Object.keys(newStyles).forEach(key => {
- newStylesString += `${key}: ${newStyles[key]};`;
- });
- this.setAttribute('style', newStylesString);
+/** @type {UpdateStylesMixin} */
+const UpdateStylesMixinImplementation = superclass =>
+ // eslint-disable-next-line no-shadow
+ class UpdateStylesMixinHost extends superclass {
+ /**
+ * @example
+ *
+ *
+ *
+ *
+ * $0.updateStyles({'background': 'orange', '--foo': '#fff'})
+ * Chrome, Firefox:
+ * IE:
+ * => to head:
+ *
+ * @param {StylesMap} updateStyles
+ */
+ updateStyles(updateStyles) {
+ const styleString = this.getAttribute('style') || this.getAttribute('data-style') || '';
+
+ /**
+ * reducer function
+ * @param {Object.} acc
+ * @param {string} stylePair
+ */
+ const reducer = (acc, stylePair) => {
+ /** @type {Array.} */
+ const parts = stylePair.split(':');
+ if (parts.length === 2) {
+ // eslint-disable-next-line prefer-destructuring
+ acc[parts[0]] = parts[1];
}
+ return acc;
+ };
+ const currentStyles = styleString.split(';').reduce(reducer, {});
+
+ const newStyles = { ...currentStyles, ...updateStyles };
+ let newStylesString = '';
+ // @ts-ignore not sure how to type ShadyCSS..
+ if (typeof ShadyCSS === 'object' && !ShadyCSS.nativeShadow) {
+ // No ShadowDOM => IE, Edge
+
+ /** @type {Object.} */
+ const newCssVariablesObj = {};
+
+ Object.keys(newStyles).forEach(key => {
+ if (key.indexOf('--') === -1) {
+ newStylesString += `${key}:${newStyles[key]};`;
+ } else {
+ newCssVariablesObj[key] = newStyles[key];
+ }
+ });
+ this.setAttribute('style', newStylesString);
+ // @ts-ignore not sure how to type ShadyCSS..
+ ShadyCSS.styleSubtree(this, newCssVariablesObj);
+ } else {
+ // has shadowdom => Chrome, Firefox, Safari
+ Object.keys(newStyles).forEach(key => {
+ newStylesString += `${key}: ${newStyles[key]};`;
+ });
+ this.setAttribute('style', newStylesString);
}
- },
-);
+ }
+ };
+
+export const UpdateStylesMixin = dedupeMixin(UpdateStylesMixinImplementation);
diff --git a/packages/core/src/differentKeyEventNamesShimIE.js b/packages/core/src/differentKeyEventNamesShimIE.js
index f67a28ae9..c62b5e8c5 100644
--- a/packages/core/src/differentKeyEventNamesShimIE.js
+++ b/packages/core/src/differentKeyEventNamesShimIE.js
@@ -3,6 +3,7 @@ if (typeof window.KeyboardEvent !== 'function') {
const event = KeyboardEvent.prototype;
const descriptor = Object.getOwnPropertyDescriptor(event, 'key');
if (descriptor) {
+ /** @type {Object.} */
const keys = {
Win: 'Meta',
Scroll: 'ScrollLock',
@@ -26,10 +27,13 @@ if (typeof window.KeyboardEvent !== 'function') {
Object.defineProperty(event, 'key', {
// eslint-disable-next-line object-shorthand, func-names
get: function () {
- const key = descriptor.get.call(this);
+ if (descriptor.get) {
+ const key = descriptor.get.call(this);
- // eslint-disable-next-line no-prototype-builtins
- return keys.hasOwnProperty(key) ? keys[key] : key;
+ // eslint-disable-next-line no-prototype-builtins
+ return keys.hasOwnProperty(key) ? keys[key] : key;
+ }
+ return undefined;
},
});
}
diff --git a/packages/core/test/DelegateMixin.test.js b/packages/core/test/DelegateMixin.test.js
index 09191b15e..a9265bf7e 100644
--- a/packages/core/test/DelegateMixin.test.js
+++ b/packages/core/test/DelegateMixin.test.js
@@ -14,7 +14,7 @@ describe('DelegateMixin', () => {
get delegations() {
return {
...super.delegations,
- target: () => this.shadowRoot.getElementById('button1'),
+ target: () => this.shadowRoot?.getElementById('button1'),
events: ['click'],
};
}
@@ -27,7 +27,7 @@ describe('DelegateMixin', () => {
const element = await fixture(`<${tag}>${tag}>`);
const cb = sinon.spy();
element.addEventListener('click', cb);
- element.shadowRoot.getElementById('button1').click();
+ element.shadowRoot?.getElementById('button1')?.click();
expect(cb.callCount).to.equal(1);
});
@@ -37,7 +37,7 @@ describe('DelegateMixin', () => {
get delegations() {
return {
...super.delegations,
- target: () => this.shadowRoot.getElementById('button1'),
+ target: () => this.shadowRoot?.getElementById('button1'),
events: ['click'],
};
}
@@ -47,12 +47,12 @@ describe('DelegateMixin', () => {
}
},
);
- const element = document.createElement(tag);
+ const element = /** @type {LitElement} */ (document.createElement(tag));
const cb = sinon.spy();
element.addEventListener('click', cb);
document.body.appendChild(element);
await element.updateComplete;
- element.shadowRoot.getElementById('button1').click();
+ element.shadowRoot?.getElementById('button1')?.click();
expect(cb.callCount).to.equal(1);
// cleanup
@@ -80,37 +80,37 @@ describe('DelegateMixin', () => {
},
);
- const element = await fixture(`
- <${tag}>click me ${tag}>`);
+ const element = await fixture(`<${tag}>click me ${tag}>`);
const cb = sinon.spy();
element.addEventListener('click', cb);
- Array.from(element.children)
- .find(child => child.slot === 'button')
- .click();
+ const childEl = /** @type {HTMLElement} */ (Array.from(element.children)?.find(
+ child => child.slot === 'button',
+ ));
+ childEl?.click();
expect(cb.callCount).to.equal(1);
});
it('will still support other events', async () => {
- const tag = defineCE(
- class extends DelegateMixin(LitElement) {
- get delegations() {
- return {
- ...super.delegations,
- target: () => this.shadowRoot.getElementById('button1'),
- events: ['click'],
- };
- }
+ class FooDelegate extends DelegateMixin(LitElement) {
+ get delegations() {
+ return {
+ ...super.delegations,
+ target: () => this.shadowRoot?.getElementById('button1'),
+ events: ['click'],
+ };
+ }
- render() {
- return html`with delegation `;
- }
+ render() {
+ return html`with delegation `;
+ }
- foo() {
- this.dispatchEvent(new CustomEvent('foo-event', { bubbles: true, composed: true }));
- }
- },
- );
- const element = await fixture(`<${tag}>${tag}>`);
+ foo() {
+ this.dispatchEvent(new CustomEvent('foo-event', { bubbles: true, composed: true }));
+ }
+ }
+
+ const tag = defineCE(FooDelegate);
+ const element = /** @type {FooDelegate} */ (await fixture(`<${tag}>${tag}>`));
const cb = sinon.spy();
element.addEventListener('foo-event', cb);
element.foo();
@@ -123,7 +123,7 @@ describe('DelegateMixin', () => {
get delegations() {
return {
...super.delegations,
- target: () => this.shadowRoot.getElementById('button1'),
+ target: () => this.shadowRoot?.getElementById('button1'),
methods: ['click'],
};
}
@@ -133,9 +133,9 @@ describe('DelegateMixin', () => {
}
},
);
- const element = await fixture(`<${tag}>${tag}>`);
+ const element = /** @type {HTMLElement} */ (await fixture(`<${tag}>${tag}>`));
const cb = sinon.spy();
- element.shadowRoot.getElementById('button1').addEventListener('click', cb);
+ element.shadowRoot?.getElementById('button1')?.addEventListener('click', cb);
element.click();
expect(cb.callCount).to.equal(1);
});
@@ -147,56 +147,69 @@ describe('DelegateMixin', () => {
this.foo = { a: 'a', b: 'b' };
}
+ /**
+ * @param {?} a
+ * @param {?} b
+ */
setFooAandB(a, b) {
this.foo.a = a;
this.foo.b = b;
}
}
customElements.define('delegate-argument-sub', DelegateArgumentSub);
- const tag = defineCE(
- class extends DelegateMixin(LitElement) {
- get delegations() {
- return {
- ...super.delegations,
- target: () => this.shadowRoot.getElementById('sub'),
- methods: ['setFooAandB'],
- };
- }
- render() {
- return html` `;
- }
- },
- );
+ class DelegateArgumentParent extends DelegateMixin(LitElement) {
+ get delegations() {
+ return {
+ ...super.delegations,
+ target: () => this.shadowRoot?.getElementById('sub'),
+ methods: ['setFooAandB'],
+ };
+ }
- const element = await fixture(`<${tag}>${tag}>`);
- element.disabled = true;
+ render() {
+ return html` `;
+ }
+ }
+ const tag = defineCE(DelegateArgumentParent);
+
+ const element = /** @type {DelegateArgumentParent} */ (await fixture(`<${tag}>${tag}>`));
+
+ // @ts-ignore because this method, even though it doesn't exist on the parent, gets delegated through delegations to the child, where it does exist!
element.setFooAandB('newA', 'newB');
- expect(element.shadowRoot.getElementById('sub').foo.a).to.equal('newA');
- expect(element.shadowRoot.getElementById('sub').foo.b).to.equal('newB');
+
+ const sub = /** @type {DelegateArgumentSub} */ (element.shadowRoot?.getElementById('sub'));
+ expect(sub.foo.a).to.equal('newA');
+ expect(sub.foo.b).to.equal('newB');
});
it('will set delegated properties', async () => {
- const tag = defineCE(
- class extends DelegateMixin(LitElement) {
- get delegations() {
- return {
- ...super.delegations,
- target: () => this.shadowRoot.getElementById('button1'),
- properties: ['disabled'],
- };
- }
+ class PropDelegate extends DelegateMixin(LitElement) {
+ get delegations() {
+ return {
+ ...super.delegations,
+ target: () => this.shadowRoot?.getElementById('button1'),
+ properties: ['disabled'],
+ };
+ }
- render() {
- return html`with delegation `;
- }
- },
- );
- const element = await fixture(`<${tag}>${tag}>`);
+ render() {
+ return html`with delegation `;
+ }
+ }
+ const tag = defineCE(PropDelegate);
+ const element = /** @type {PropDelegate} */ (await fixture(`<${tag}>${tag}>`));
+
+ // @ts-ignore ignoring this one, because disabled is delegated through target so it indeed does not inherently exist on the div element
element.disabled = true;
+
await element.updateComplete;
- expect(element.shadowRoot.getElementById('button1').disabled).to.equal(true);
- expect(element.shadowRoot.getElementById('button1').hasAttribute('disabled')).to.equal(true);
+
+ /** @typedef {Object.} Btn */
+ /** @typedef {Btn & HTMLElement} DelegatedBtn */
+ const btn = /** @type {DelegatedBtn} */ (element.shadowRoot?.getElementById('button1'));
+ expect(btn?.disabled).to.equal(true);
+ expect(btn?.hasAttribute('disabled')).to.equal(true);
});
it('delegates properties before delegation target is attached to DOM', async () => {
@@ -205,7 +218,7 @@ describe('DelegateMixin', () => {
get delegations() {
return {
...super.delegations,
- target: () => this.shadowRoot.getElementById('button1'),
+ target: () => this.shadowRoot?.getElementById('button1'),
properties: ['disabled'],
};
}
@@ -215,12 +228,18 @@ describe('DelegateMixin', () => {
}
},
);
- const element = document.createElement(tag);
+ /** @typedef {Object.} Btn */
+ /** @typedef {Btn & LitElement} DelegatedEl */
+ const element = /** @type {DelegatedEl} */ (document.createElement(tag));
+
element.disabled = true;
document.body.appendChild(element);
await element.updateComplete;
- expect(element.shadowRoot.getElementById('button1').disabled).to.equal(true);
+ /** @typedef {Btn & HTMLElement} DelegatedBtn */
+ const btn = /** @type {DelegatedBtn} */ (element.shadowRoot?.getElementById('button1'));
+
+ expect(btn?.disabled).to.equal(true);
// cleanup
document.body.removeChild(element);
});
@@ -231,7 +250,7 @@ describe('DelegateMixin', () => {
get delegations() {
return {
...super.delegations,
- target: () => this.shadowRoot.getElementById('button1'),
+ target: () => this.shadowRoot?.getElementById('button1'),
attributes: ['disabled'],
};
}
@@ -241,11 +260,11 @@ describe('DelegateMixin', () => {
}
},
);
- const element = await fixture(`<${tag}>${tag}>`);
+ const element = /** @type {LitElement} */ (await fixture(`<${tag}>${tag}>`));
element.setAttribute('disabled', '');
await element.updateComplete;
expect(element.hasAttribute('disabled')).to.equal(false);
- expect(element.shadowRoot.getElementById('button1').hasAttribute('disabled')).to.equal(true);
+ expect(element.shadowRoot?.getElementById('button1')?.hasAttribute('disabled')).to.equal(true);
});
it('will read inital attributes', async () => {
@@ -254,7 +273,7 @@ describe('DelegateMixin', () => {
get delegations() {
return {
...super.delegations,
- target: () => this.shadowRoot.getElementById('button1'),
+ target: () => this.shadowRoot?.getElementById('button1'),
attributes: ['disabled'],
};
}
@@ -266,7 +285,7 @@ describe('DelegateMixin', () => {
);
const element = await fixture(`<${tag} disabled>${tag}>`);
expect(element.hasAttribute('disabled')).to.equal(false);
- expect(element.shadowRoot.getElementById('button1').hasAttribute('disabled')).to.equal(true);
+ expect(element.shadowRoot?.getElementById('button1')?.hasAttribute('disabled')).to.equal(true);
});
it('will delegate removeAttribute', async () => {
@@ -275,7 +294,7 @@ describe('DelegateMixin', () => {
get delegations() {
return {
...super.delegations,
- target: () => this.shadowRoot.getElementById('button1'),
+ target: () => this.shadowRoot?.getElementById('button1'),
attributes: ['disabled'],
};
}
@@ -285,135 +304,146 @@ describe('DelegateMixin', () => {
}
},
);
- const element = await fixture(`<${tag} disabled>${tag}>`);
- element.removeAttribute('disabled', '');
+ const element = /** @type {LitElement} */ (await fixture(`<${tag} disabled>${tag}>`));
+ element.removeAttribute('disabled');
await element.updateComplete;
expect(element.hasAttribute('disabled')).to.equal(false);
- expect(element.shadowRoot.getElementById('button1').hasAttribute('disabled')).to.equal(false);
+ expect(element.shadowRoot?.getElementById('button1')?.hasAttribute('disabled')).to.equal(false);
});
it('respects user defined values for delegated attributes and properties', async () => {
- const tag = defineCE(
- class extends DelegateMixin(LitElement) {
- get delegations() {
- return {
- ...super.delegations,
- // this just means itś config is set to the queue when called before connectedCallback
- target: () => this.scheduledElement,
- attributes: ['type'],
- properties: ['type'],
- };
- }
+ class ScheduledElement extends DelegateMixin(LitElement) {
+ get delegations() {
+ return {
+ ...super.delegations,
+ // this just means itś config is set to the queue when called before connectedCallback
+ target: () => this.scheduledElement,
+ attributes: ['type'],
+ properties: ['type'],
+ };
+ }
- get scheduledElement() {
- return this.querySelector('input');
- }
+ get scheduledElement() {
+ return this.querySelector('input');
+ }
- constructor() {
- super();
- this.type = 'email'; // 1. here we set the delegated prop and it should be scheduled
- }
+ constructor() {
+ super();
+ this.type = 'email'; // 1. here we set the delegated prop and it should be scheduled
+ }
- connectedCallback() {
- // 2. this is where we add teh delegation target (so after 1)
- this.appendChild(document.createElement('input'));
- super.connectedCallback(); // let the DelegateMixin do its work
- }
- },
- );
+ connectedCallback() {
+ // 2. this is where we add teh delegation target (so after 1)
+ this.appendChild(document.createElement('input'));
+ super.connectedCallback(); // let the DelegateMixin do its work
+ }
+ }
+
+ const tag = defineCE(ScheduledElement);
const tagName = unsafeStatic(tag);
+
// Here, the Application Developerd tries to set the type via attribute
- const elementAttr = await fixture(`<${tag} type="radio">${tag}>`);
- expect(elementAttr.scheduledElement.type).to.equal('radio');
+ const elementAttr = /** @type {ScheduledElement} */ (await fixture(
+ `<${tag} type="radio">${tag}>`,
+ ));
+ expect(elementAttr.scheduledElement?.type).to.equal('radio');
// Here, the Application Developer tries to set the type via property
- const elementProp = await fixture(html`<${tagName} .type=${'radio'}>${tagName}>`);
- expect(elementProp.scheduledElement.type).to.equal('radio');
+ const elementProp = /** @type {ScheduledElement} */ (await fixture(
+ html`<${tagName} .type=${'radio'}>${tagName}>`,
+ ));
+ expect(elementProp.scheduledElement?.type).to.equal('radio');
});
it(`uses attribute value as a fallback for delegated property getter
when property not set by user and delegationTarget not ready`, async () => {
- const tag = defineCE(
- class extends DelegateMixin(LitElement) {
- get delegations() {
- return {
- ...super.delegations,
- target: () => this.delegatedEl,
- properties: ['type'],
- attributes: ['type'],
- };
- }
+ class FallbackEl extends DelegateMixin(LitElement) {
+ get delegations() {
+ return {
+ ...super.delegations,
+ target: () => this.delegatedEl,
+ properties: ['type'],
+ attributes: ['type'],
+ };
+ }
- get delegatedEl() {
- // returns null, so we can test that "cached" attr is used as fallback
- return null;
- }
- },
- );
- const element = await fixture(`<${tag} type="radio">${tag}>`);
+ get delegatedEl() {
+ // returns null, so we can test that "cached" attr is used as fallback
+ return null;
+ }
+ }
+ const tag = defineCE(FallbackEl);
+ const element = /** @type {FallbackEl} */ (await fixture(`<${tag} type="radio">${tag}>`));
expect(element.delegatedEl).to.equal(null);
+ // @ts-ignore ignoring this one, because type is delegated through target so it indeed does not inherently exist on the div element
expect(element.type).to.equal('radio'); // value retrieved from host instead of delegatedTarget
});
it('works with connectedCallback', async () => {
- const tag = await defineCE(
- class extends DelegateMixin(HTMLElement) {
- get delegations() {
- return {
- ...super.delegations,
- target: () => this.querySelector('div'),
- properties: ['foo'],
- };
- }
- },
- );
- const element = await fixture(`<${tag}>
${tag}>`);
+ class ConnectedElement extends DelegateMixin(HTMLElement) {
+ get delegations() {
+ return {
+ ...super.delegations,
+ target: () => this.querySelector('div'),
+ properties: ['foo'],
+ };
+ }
+ }
+ const tag = await defineCE(ConnectedElement);
+ const element = /** @type {ConnectedElement} */ (await fixture(`<${tag}>
${tag}>`));
+
+ // @ts-ignore ignoring this one, because foo is delegated through target so it indeed does not inherently exist on the div element
element.foo = 'new';
- expect(element.querySelector('div').foo).to.equal('new');
+ // @ts-ignore ignoring this one, because foo is delegated through target so it indeed does not inherently exist on the div element
+ expect(element.querySelector('div')?.foo).to.equal('new');
});
it('works with shadow dom', async () => {
- const tag = await defineCE(
- class extends DelegateMixin(LitElement) {
- get delegations() {
- return {
- ...super.delegations,
- target: () => this.shadowRoot.querySelector('div'),
- properties: ['foo'],
- };
- }
+ class A extends DelegateMixin(LitElement) {
+ get delegations() {
+ return {
+ ...super.delegations,
+ target: () => this.shadowRoot?.querySelector('div'),
+ properties: ['foo'],
+ };
+ }
- render() {
- return html`
`;
- }
- },
- );
+ render() {
+ return html`
`;
+ }
+ }
+ const tag = await defineCE(A);
const element = await fixture(`<${tag}>${tag}>`);
+
+ // @ts-ignore ignoring this one, because foo is delegated through target so it indeed does not inherently exist on the div element
element.foo = 'new';
- expect(element.shadowRoot.querySelector('div').foo).to.equal('new');
+ // @ts-ignore ignoring this one, because foo is delegated through target so it indeed does not inherently exist on the div element
+ expect(element.shadowRoot?.querySelector('div')?.foo).to.equal('new');
});
it('works with light dom', async () => {
- const tag = await defineCE(
- class extends DelegateMixin(LitElement) {
- get delegations() {
- return {
- ...super.delegations,
- target: () => this.querySelector('div'),
- properties: ['foo'],
- };
- }
+ class A extends DelegateMixin(LitElement) {
+ get delegations() {
+ return {
+ ...super.delegations,
+ target: () => this.querySelector('div'),
+ properties: ['foo'],
+ };
+ }
- createRenderRoot() {
- return this;
- }
+ createRenderRoot() {
+ return this;
+ }
- render() {
- return html`
`;
- }
- },
- );
+ render() {
+ return html`
`;
+ }
+ }
+
+ const tag = await defineCE(A);
const element = await fixture(`<${tag}>${tag}>`);
+ // @ts-ignore ignoring this one, because foo is delegated through target so it indeed does not inherently exist on the div element
element.foo = 'new';
- expect(element.querySelector('div').foo).to.equal('new');
+ // @ts-ignore ignoring this one, because foo is delegated through target so it indeed does not inherently exist on the div element
+ expect(element.querySelector('div')?.foo).to.equal('new');
});
});
diff --git a/packages/core/test/DisabledMixin.test.js b/packages/core/test/DisabledMixin.test.js
index c9bcf11a7..52f1bc9cc 100644
--- a/packages/core/test/DisabledMixin.test.js
+++ b/packages/core/test/DisabledMixin.test.js
@@ -3,21 +3,26 @@ import { LitElement } from '../index.js';
import { DisabledMixin } from '../src/DisabledMixin.js';
describe('DisabledMixin', () => {
+ class CanBeDisabled extends DisabledMixin(LitElement) {}
before(() => {
- class CanBeDisabled extends DisabledMixin(LitElement) {}
customElements.define('can-be-disabled', CanBeDisabled);
});
it('reflects disabled to attribute', async () => {
- const el = await fixture(html` `);
+ const el = /** @type {CanBeDisabled} */ (await fixture(
+ html` `,
+ ));
expect(el.hasAttribute('disabled')).to.be.false;
+ el.makeRequestToBeDisabled();
el.disabled = true;
await el.updateComplete;
expect(el.hasAttribute('disabled')).to.be.true;
});
it('can be requested to be disabled', async () => {
- const el = await fixture(html` `);
+ const el = /** @type {CanBeDisabled} */ (await fixture(
+ html` `,
+ ));
el.makeRequestToBeDisabled();
expect(el.disabled).to.be.true;
await el.updateComplete;
@@ -25,7 +30,9 @@ describe('DisabledMixin', () => {
});
it('will not allow to become enabled after makeRequestToBeDisabled()', async () => {
- const el = await fixture(html` `);
+ const el = /** @type {CanBeDisabled} */ (await fixture(
+ html` `,
+ ));
el.makeRequestToBeDisabled();
expect(el.disabled).to.be.true;
@@ -34,14 +41,18 @@ describe('DisabledMixin', () => {
});
it('will stay disabled after retractRequestToBeDisabled() if it was disabled before', async () => {
- const el = await fixture(html` `);
+ const el = /** @type {CanBeDisabled} */ (await fixture(
+ html` `,
+ ));
el.makeRequestToBeDisabled();
el.retractRequestToBeDisabled();
expect(el.disabled).to.be.true;
});
it('will become enabled after retractRequestToBeDisabled() if it was enabled before', async () => {
- const el = await fixture(html` `);
+ const el = /** @type {CanBeDisabled} */ (await fixture(
+ html` `,
+ ));
el.makeRequestToBeDisabled();
expect(el.disabled).to.be.true;
el.retractRequestToBeDisabled();
@@ -49,7 +60,9 @@ describe('DisabledMixin', () => {
});
it('may allow multiple calls to makeRequestToBeDisabled()', async () => {
- const el = await fixture(html` `);
+ const el = /** @type {CanBeDisabled} */ (await fixture(
+ html` `,
+ ));
el.makeRequestToBeDisabled();
el.makeRequestToBeDisabled();
el.retractRequestToBeDisabled();
@@ -57,7 +70,9 @@ describe('DisabledMixin', () => {
});
it('will restore last state after retractRequestToBeDisabled()', async () => {
- const el = await fixture(html` `);
+ const el = /** @type {CanBeDisabled} */ (await fixture(
+ html` `,
+ ));
el.makeRequestToBeDisabled();
el.disabled = true;
el.retractRequestToBeDisabled();
diff --git a/packages/core/test/DisabledWithTabIndexMixin.test.js b/packages/core/test/DisabledWithTabIndexMixin.test.js
index 3a037f8bc..4afd740da 100644
--- a/packages/core/test/DisabledWithTabIndexMixin.test.js
+++ b/packages/core/test/DisabledWithTabIndexMixin.test.js
@@ -4,23 +4,23 @@ import { LitElement } from '../index.js';
import { DisabledWithTabIndexMixin } from '../src/DisabledWithTabIndexMixin.js';
describe('DisabledWithTabIndexMixin', () => {
+ class WithTabIndex extends DisabledWithTabIndexMixin(LitElement) {}
before(() => {
- class WithTabIndex extends DisabledWithTabIndexMixin(LitElement) {}
customElements.define('can-be-disabled-with-tab-index', WithTabIndex);
});
it('has an initial tabIndex of 0', async () => {
- const el = await fixture(html`
+ const el = /** @type {WithTabIndex} */ (await fixture(html`
- `);
+ `));
expect(el.tabIndex).to.equal(0);
expect(el.getAttribute('tabindex')).to.equal('0');
});
it('sets tabIndex to -1 if disabled', async () => {
- const el = await fixture(html`
+ const el = /** @type {WithTabIndex} */ (await fixture(html`
- `);
+ `));
el.disabled = true;
expect(el.tabIndex).to.equal(-1);
await el.updateComplete;
@@ -28,9 +28,9 @@ describe('DisabledWithTabIndexMixin', () => {
});
it('disabled does not override user provided tabindex', async () => {
- const el = await fixture(html`
+ const el = /** @type {WithTabIndex} */ (await fixture(html`
- `);
+ `));
expect(el.getAttribute('tabindex')).to.equal('-1');
el.disabled = false;
await el.updateComplete;
@@ -38,9 +38,9 @@ describe('DisabledWithTabIndexMixin', () => {
});
it('can be disabled imperatively', async () => {
- const el = await fixture(html`
+ const el = /** @type {WithTabIndex} */ (await fixture(html`
- `);
+ `));
expect(el.getAttribute('tabindex')).to.equal('-1');
el.disabled = false;
@@ -55,9 +55,9 @@ describe('DisabledWithTabIndexMixin', () => {
});
it('will not allow to change tabIndex after makeRequestToBeDisabled()', async () => {
- const el = await fixture(html`
+ const el = /** @type {WithTabIndex} */ (await fixture(html`
- `);
+ `));
el.makeRequestToBeDisabled();
el.tabIndex = 5;
@@ -67,9 +67,9 @@ describe('DisabledWithTabIndexMixin', () => {
});
it('will restore last tabIndex after retractRequestToBeDisabled()', async () => {
- const el = await fixture(html`
+ const el = /** @type {WithTabIndex} */ (await fixture(html`
- `);
+ `));
el.makeRequestToBeDisabled();
expect(el.tabIndex).to.equal(-1);
await el.updateComplete;
@@ -96,9 +96,9 @@ describe('DisabledWithTabIndexMixin', () => {
});
it('may allow multiple calls to retractRequestToBeDisabled', async () => {
- const el = await fixture(html`
+ const el = /** @type {WithTabIndex} */ (await fixture(html`
- `);
+ `));
el.retractRequestToBeDisabled();
el.retractRequestToBeDisabled();
expect(el.disabled).to.be.true;
diff --git a/packages/core/test/LionSingleton.test.js b/packages/core/test/LionSingleton.test.js
index b6208fb3b..27d808294 100644
--- a/packages/core/test/LionSingleton.test.js
+++ b/packages/core/test/LionSingleton.test.js
@@ -11,6 +11,11 @@ describe('LionSingleton', () => {
it('supports parameters for .getInstance(foo, bar)', async () => {
class MySingleton extends LionSingleton {
+ /**
+ *
+ * @param {string} foo
+ * @param {string} bar
+ */
constructor(foo, bar) {
super();
this.foo = foo;
@@ -59,6 +64,7 @@ describe('LionSingleton', () => {
});
it('can at any time add mixins via .addInstanceMixin()', () => {
+ // @ts-ignore because we're getting rid of LionSingleton altogether
const MyMixin = superclass =>
class extends superclass {
constructor() {
@@ -72,6 +78,7 @@ describe('LionSingleton', () => {
const mySingleton = MySingleton.getInstance();
expect(mySingleton.myMixin).to.be.true;
+ // @ts-ignore because we're getting rid of LionSingleton altogether
const OtherMixin = superclass =>
class extends superclass {
constructor() {
@@ -89,6 +96,7 @@ describe('LionSingleton', () => {
});
it('can provide new instances (with applied Mixins) via .getNewInstance()', async () => {
+ // @ts-ignore because we're getting rid of LionSingleton altogether
const MyMixin = superclass =>
class extends superclass {
constructor() {
@@ -100,13 +108,19 @@ describe('LionSingleton', () => {
MySingleton.addInstanceMixin(MyMixin);
const singletonOne = MySingleton.getNewInstance();
+ // @ts-ignore because we're getting rid of LionSingleton altogether
singletonOne.one = true;
+ // @ts-ignore because we're getting rid of LionSingleton altogether
expect(singletonOne.myMixin).to.be.true;
+ // @ts-ignore because we're getting rid of LionSingleton altogether
expect(singletonOne.one).to.be.true;
const singletonTwo = MySingleton.getNewInstance();
+ // @ts-ignore because we're getting rid of LionSingleton altogether
expect(singletonTwo.myMixin).to.be.true;
+ // @ts-ignore because we're getting rid of LionSingleton altogether
expect(singletonTwo.one).to.be.undefined;
+ // @ts-ignore because we're getting rid of LionSingleton altogether
expect(singletonOne.one).to.be.true; // to be sure
});
});
diff --git a/packages/core/test/SlotMixin.test.js b/packages/core/test/SlotMixin.test.js
index 0ff42feba..675c875ca 100644
--- a/packages/core/test/SlotMixin.test.js
+++ b/packages/core/test/SlotMixin.test.js
@@ -14,8 +14,8 @@ describe('SlotMixin', () => {
}
},
);
- const element = await fixture(`<${tag}>${tag}>`);
- expect(element.children[0].slot).to.equal('feedback');
+ const el = await fixture(`<${tag}>${tag}>`);
+ expect(el.children[0].slot).to.equal('feedback');
});
it('does not override user provided slots', async () => {
@@ -31,7 +31,7 @@ describe('SlotMixin', () => {
);
const el = await fixture(`<${tag}>user-content
${tag}>`);
expect(el.children[0].tagName).to.equal('P');
- expect(el.children[0].innerText).to.equal('user-content');
+ expect(/** @type HTMLParagraphElement */ (el.children[0]).innerText).to.equal('user-content');
});
it('supports complex dom trees as element', async () => {
@@ -57,10 +57,12 @@ describe('SlotMixin', () => {
}
},
);
- const element = await fixture(`<${tag}>${tag}>`);
- expect(element.children[0].slot).to.equal('feedback');
- expect(element.children[0].getAttribute('foo')).to.equal('bar');
- expect(element.children[0].children[0].innerText).to.equal('cat');
+ const el = await fixture(`<${tag}>${tag}>`);
+ expect(el.children[0].slot).to.equal('feedback');
+ expect(el.children[0].getAttribute('foo')).to.equal('bar');
+ expect(/** @type HTMLParagraphElement */ (el.children[0].children[0]).innerText).to.equal(
+ 'cat',
+ );
});
it('supports conditional slots', async () => {
@@ -82,35 +84,37 @@ describe('SlotMixin', () => {
}
},
);
- const elementSlot = await fixture(`<${tag}><${tag}>`);
- expect(elementSlot.querySelector('#someSlot')).to.exist;
+ const elSlot = await fixture(`<${tag}><${tag}>`);
+ expect(elSlot.querySelector('#someSlot')).to.exist;
renderSlot = false;
- const elementNoSlot = await fixture(`<${tag}><${tag}>`);
- expect(elementNoSlot.querySelector('#someSlot')).to.not.exist;
+ const elNoSlot = await fixture(`<${tag}><${tag}>`);
+ expect(elNoSlot.querySelector('#someSlot')).to.not.exist;
});
it("allows to check which slots have been created via this._isPrivateSlot('slotname')", async () => {
let renderSlot = true;
- const tag = defineCE(
- class extends SlotMixin(HTMLElement) {
- get slots() {
- return {
- ...super.slots,
- conditional: () => (renderSlot ? document.createElement('div') : undefined),
- };
- }
+ class SlotPrivateText extends SlotMixin(HTMLElement) {
+ get slots() {
+ return {
+ ...super.slots,
+ conditional: () => (renderSlot ? document.createElement('div') : undefined),
+ };
+ }
- didCreateConditionalSlot() {
- return this._isPrivateSlot('conditional');
- }
- },
- );
- const el = await fixture(`<${tag}><${tag}>`);
+ didCreateConditionalSlot() {
+ return this._isPrivateSlot('conditional');
+ }
+ }
+
+ const tag = defineCE(SlotPrivateText);
+ const el = /** @type {SlotPrivateText} */ (await fixture(`<${tag}><${tag}>`));
expect(el.didCreateConditionalSlot()).to.be.true;
- const elUserSlot = await fixture(`<${tag}>foo
<${tag}>`);
+ const elUserSlot = /** @type {SlotPrivateText} */ (await fixture(
+ `<${tag}>foo
<${tag}>`,
+ ));
expect(elUserSlot.didCreateConditionalSlot()).to.be.false;
renderSlot = false;
- const elNoSlot = await fixture(`<${tag}><${tag}>`);
+ const elNoSlot = /** @type {SlotPrivateText} */ (await fixture(`<${tag}><${tag}>`));
expect(elNoSlot.didCreateConditionalSlot()).to.be.false;
});
});
diff --git a/packages/core/test/UpdateStylesMixin.test.js b/packages/core/test/UpdateStylesMixin.test.js
index c8c99f307..8c2dc3843 100644
--- a/packages/core/test/UpdateStylesMixin.test.js
+++ b/packages/core/test/UpdateStylesMixin.test.js
@@ -4,82 +4,75 @@ import { UpdateStylesMixin } from '../src/UpdateStylesMixin.js';
describe('UpdateStylesMixin', () => {
it('handles css variables && direct e.g. host css properties correctly', async () => {
- const tag = defineCE(
- class extends UpdateStylesMixin(LitElement) {
- static get styles() {
- return [
- css`
- :host {
- text-align: right;
+ class UpdateStylesElement extends UpdateStylesMixin(LitElement) {
+ static get styles() {
+ return [
+ css`
+ :host {
+ text-align: right;
- --color: rgb(128, 128, 128);
- }
+ --color: rgb(128, 128, 128);
+ }
- h1 {
- color: var(--color);
- }
- `,
- ];
- }
+ h1 {
+ color: var(--color);
+ }
+ `,
+ ];
+ }
- render() {
- return html``;
- }
- },
- );
- const el = await fixture(`<${tag}>${tag}>`);
- expect(window.getComputedStyle(el.shadowRoot.getElementById('header')).color).to.equal(
- 'rgb(128, 128, 128)',
- );
+ render() {
+ return html``;
+ }
+ }
+
+ const tag = defineCE(UpdateStylesElement);
+ const el = /** @type {UpdateStylesElement} */ (await fixture(`<${tag}>${tag}>`));
+ const header = /** @type {Element} */ (el.shadowRoot?.getElementById('header'));
+
+ expect(window.getComputedStyle(header).color).to.equal('rgb(128, 128, 128)');
expect(window.getComputedStyle(el).textAlign).to.equal('right');
el.updateStyles({
'--color': 'rgb(255, 0, 0)',
'text-align': 'center',
});
- await tag.updateComplete;
- expect(window.getComputedStyle(el.shadowRoot.getElementById('header')).color).to.equal(
- 'rgb(255, 0, 0)',
- );
+ await el.updateComplete;
+ expect(window.getComputedStyle(header).color).to.equal('rgb(255, 0, 0)');
expect(window.getComputedStyle(el).textAlign).to.equal('center');
});
it('preserves existing styles', async () => {
- const tag = defineCE(
- class extends UpdateStylesMixin(LitElement) {
- static get styles() {
- return [
- css`
- :host {
- --color: rgb(128, 128, 128);
- }
+ class UpdateStylesElement extends UpdateStylesMixin(LitElement) {
+ static get styles() {
+ return [
+ css`
+ :host {
+ --color: rgb(128, 128, 128);
+ }
- h1 {
- color: var(--color);
- }
- `,
- ];
- }
+ h1 {
+ color: var(--color);
+ }
+ `,
+ ];
+ }
- render() {
- return html``;
- }
- },
- );
- const el = await fixture(`<${tag}>${tag}>`);
- expect(window.getComputedStyle(el.shadowRoot.getElementById('header')).color).to.equal(
- 'rgb(128, 128, 128)',
- );
+ render() {
+ return html``;
+ }
+ }
+ const tag = defineCE(UpdateStylesElement);
+ const el = /** @type {UpdateStylesElement} */ (await fixture(`<${tag}>${tag}>`));
+ const header = /** @type {Element} */ (el.shadowRoot?.getElementById('header'));
+
+ expect(window.getComputedStyle(header).color).to.equal('rgb(128, 128, 128)');
el.updateStyles({ '--color': 'rgb(255, 0, 0)' });
- await tag.updateComplete;
- expect(window.getComputedStyle(el.shadowRoot.getElementById('header')).color).to.equal(
- 'rgb(255, 0, 0)',
- );
+ expect(window.getComputedStyle(header).color).to.equal('rgb(255, 0, 0)');
el.updateStyles({ 'text-align': 'left' });
- await tag.updateComplete;
- const styles = window.getComputedStyle(el.shadowRoot.getElementById('header'));
+ const styles = window.getComputedStyle(header);
expect(styles.color).to.equal('rgb(255, 0, 0)');
expect(styles.textAlign).to.equal('left');
});
diff --git a/packages/core/types/DelegateMixinTypes.d.ts b/packages/core/types/DelegateMixinTypes.d.ts
new file mode 100644
index 000000000..0840c3a64
--- /dev/null
+++ b/packages/core/types/DelegateMixinTypes.d.ts
@@ -0,0 +1,51 @@
+import { Constructor } from '@open-wc/dedupe-mixin';
+
+export type Delegations = {
+ target: Function;
+ events: string[];
+ methods: string[];
+ properties: string[];
+ attributes: string[];
+};
+
+export declare class DelegateMixinHost {
+ delegations: Delegations;
+
+ protected _connectDelegateMixin(): void;
+
+ private __setupPropertyDelegation(): void;
+
+ private __initialAttributeDelegation(): void;
+
+ private __emptyEventListenerQueue(): void;
+
+ private __emptyPropertiesQueue(): void;
+}
+
+/**
+ * # DelegateMixin
+ * Forwards defined events, methods, properties and attributes to the defined target.
+ *
+ * @example
+ * get delegations() {
+ * return {
+ * ...super.delegations,
+ * target: () => this.shadowRoot.getElementById('button1'),
+ * events: ['click'],
+ * methods: ['click'],
+ * properties: ['disabled'],
+ * attributes: ['disabled'],
+ * };
+ * }
+ *
+ * render() {
+ * return html`
+ * with delegation
+ * `;
+ * }
+ */
+declare function DelegateMixinImplementation>(
+ superclass: T,
+): T & Constructor;
+
+export type DelegateMixin = typeof DelegateMixinImplementation;
diff --git a/packages/core/types/DisabledMixinTypes.d.ts b/packages/core/types/DisabledMixinTypes.d.ts
new file mode 100644
index 000000000..ade1a1178
--- /dev/null
+++ b/packages/core/types/DisabledMixinTypes.d.ts
@@ -0,0 +1,29 @@
+import { Constructor } from '@open-wc/dedupe-mixin';
+
+export declare class DisabledMixinHost {
+ static get properties(): {
+ disabled: {
+ type: BooleanConstructor;
+ reflect: boolean;
+ };
+ };
+ disabled: boolean;
+
+ /**
+ * Makes request to make the element disabled
+ */
+ public makeRequestToBeDisabled(): void;
+
+ /**
+ * Retract request to make the element disabled and restore disabled to previous
+ */
+ public retractRequestToBeDisabled(): void;
+
+ private __internalSetDisabled(value: boolean): void;
+}
+
+export declare function DisabledMixinImplementation>(
+ superclass: T,
+): T & Constructor;
+
+export type DisabledMixin = typeof DisabledMixinImplementation;
diff --git a/packages/core/types/DisabledWithTabIndexMixinTypes.d.ts b/packages/core/types/DisabledWithTabIndexMixinTypes.d.ts
new file mode 100644
index 000000000..358005a3b
--- /dev/null
+++ b/packages/core/types/DisabledWithTabIndexMixinTypes.d.ts
@@ -0,0 +1,29 @@
+import { Constructor } from '@open-wc/dedupe-mixin';
+import { DisabledMixinHost } from './DisabledMixinTypes';
+export declare class DisabledWithTabIndexMixinHost {
+ static get properties(): {
+ tabIndex: {
+ type: NumberConstructor;
+ reflect: boolean;
+ attribute: string;
+ };
+ };
+ tabIndex: number;
+ /**
+ * Makes request to make the element disabled and set the tabindex
+ */
+ public makeRequestToBeDisabled(): void;
+
+ /**
+ * Retract request to make the element disabled and restore disabled and tabindex to previous
+ */
+ public retractRequestToBeDisabled(): void;
+
+ private __internalSetTabIndex(value: boolean): void;
+}
+
+export declare function DisabledWithTabIndexMixinImplementation>(
+ superclass: T,
+): T & Constructor & Constructor;
+
+export type DisabledWithTabIndexMixin = typeof DisabledWithTabIndexMixinImplementation;
diff --git a/packages/core/types/SlotMixinTypes.d.ts b/packages/core/types/SlotMixinTypes.d.ts
new file mode 100644
index 000000000..d911b267f
--- /dev/null
+++ b/packages/core/types/SlotMixinTypes.d.ts
@@ -0,0 +1,53 @@
+import { Constructor } from '@open-wc/dedupe-mixin';
+
+declare function slotFunction(): HTMLElement | undefined;
+
+export type SlotsMap = {
+ [key: string]: typeof slotFunction;
+};
+
+export declare class SlotMixinHost {
+ /**
+ * Obtains all the slots to create
+ */
+ slots: SlotsMap;
+
+ /**
+ * Starts the creation of slots
+ */
+ protected _connectSlotMixin(): void;
+
+ /**
+ * Useful to decide if a given slot should be manipulated depending on if it was auto generated
+ * or not.
+ *
+ * @param {string} slotName Name of the slot
+ * @return {boolean} true if given slot name been created by SlotMixin
+ */
+ protected _isPrivateSlot(slotName: string): boolean;
+}
+
+/**
+ * # SlotMixin
+ *
+ * `SlotMixin`, when attached to the DOM it creates content for defined slots in the Light DOM.
+ * The content element is created using a factory function and is assigned a slot name from the key.
+ * Existing slot content is not overridden.
+ *
+ * The purpose is to have the default content in the Light DOM rather than hidden in Shadow DOM
+ * like default slot content works natively.
+ *
+ * @example
+ * get slots() {
+ * return {
+ * ...super.slots,
+ * // appends
to the Light DOM of this element
+ * foo: () => document.createElement('div'),
+ * };
+ * }
+ */
+export declare function SlotMixinImplementation>(
+ superclass: T,
+): T & Constructor;
+
+export type SlotMixin = typeof SlotMixinImplementation;
diff --git a/packages/core/types/UpdateStylesMixinTypes.d.ts b/packages/core/types/UpdateStylesMixinTypes.d.ts
new file mode 100644
index 000000000..a0d931c3c
--- /dev/null
+++ b/packages/core/types/UpdateStylesMixinTypes.d.ts
@@ -0,0 +1,34 @@
+import { Constructor } from '@open-wc/dedupe-mixin';
+
+export type StylesMap = {
+ [key: string]: string;
+};
+export declare class UpdateStylesMixinHost {
+ /**
+ * @example
+ *
+ *
+ *
+ *
+ * $0.updateStyles({'background': 'orange', '--foo': '#fff'})
+ * Chrome, Firefox:
+ * IE:
+ * => to head:
+ *
+ * @param {StylesMap} updateStyles
+ */
+ public updateStyles(updateStyles: StylesMap): void;
+}
+
+/**
+ * # UpdateStylesMixin
+ */
+declare function UpdateStylesMixinImplementation>(
+ superclass: T,
+): T & Constructor;
+
+export type UpdateStylesMixin = typeof UpdateStylesMixinImplementation;
diff --git a/packages/dialog/package.json b/packages/dialog/package.json
index afad2c209..6474bceee 100644
--- a/packages/dialog/package.json
+++ b/packages/dialog/package.json
@@ -13,10 +13,14 @@
"main": "index.js",
"module": "index.js",
"files": [
+ "*.d.ts",
"*.js",
"docs",
"src",
- "test"
+ "test",
+ "test-helpers",
+ "translations",
+ "types"
],
"scripts": {
"prepublishOnly": "../../scripts/npm-prepublish.js",
diff --git a/packages/fieldset/package.json b/packages/fieldset/package.json
index 56bbd4482..70283cc9e 100644
--- a/packages/fieldset/package.json
+++ b/packages/fieldset/package.json
@@ -13,11 +13,14 @@
"main": "index.js",
"module": "index.js",
"files": [
+ "*.d.ts",
"*.js",
"docs",
"src",
"test",
- "translations"
+ "test-helpers",
+ "translations",
+ "types"
],
"scripts": {
"prepublishOnly": "../../scripts/npm-prepublish.js",
diff --git a/packages/form-core/package.json b/packages/form-core/package.json
index 801aa430e..2fcc70928 100644
--- a/packages/form-core/package.json
+++ b/packages/form-core/package.json
@@ -13,13 +13,14 @@
"main": "index.js",
"module": "index.js",
"files": [
+ "*.d.ts",
"*.js",
"docs",
"src",
"test",
"test-helpers",
- "test-suites",
- "translations"
+ "translations",
+ "types"
],
"scripts": {
"prepublishOnly": "../../scripts/npm-prepublish.js",
diff --git a/packages/form-integrations/package.json b/packages/form-integrations/package.json
index 7e1ea4da1..796eae0b6 100644
--- a/packages/form-integrations/package.json
+++ b/packages/form-integrations/package.json
@@ -13,12 +13,14 @@
"main": "index.js",
"module": "index.js",
"files": [
+ "*.d.ts",
"*.js",
- "dev-assets",
"docs",
"src",
"test",
- "translations"
+ "test-helpers",
+ "translations",
+ "types"
],
"scripts": {
"prepublishOnly": "../../scripts/npm-prepublish.js"
diff --git a/packages/form/package.json b/packages/form/package.json
index abcf0112d..a6876f6e5 100644
--- a/packages/form/package.json
+++ b/packages/form/package.json
@@ -13,11 +13,14 @@
"main": "index.js",
"module": "index.js",
"files": [
+ "*.d.ts",
"*.js",
"docs",
"src",
"test",
- "translations"
+ "test-helpers",
+ "translations",
+ "types"
],
"scripts": {
"prepublishOnly": "../../scripts/npm-prepublish.js",
diff --git a/packages/icon/package.json b/packages/icon/package.json
index d1abcd8e6..3ebf276b4 100644
--- a/packages/icon/package.json
+++ b/packages/icon/package.json
@@ -13,11 +13,14 @@
"main": "index.js",
"module": "index.js",
"files": [
+ "*.d.ts",
"*.js",
"docs",
"src",
"test",
- "translations"
+ "test-helpers",
+ "translations",
+ "types"
],
"scripts": {
"prepublishOnly": "../../scripts/npm-prepublish.js",
diff --git a/packages/input-amount/package.json b/packages/input-amount/package.json
index 25a098866..1954b5039 100644
--- a/packages/input-amount/package.json
+++ b/packages/input-amount/package.json
@@ -13,11 +13,14 @@
"main": "index.js",
"module": "index.js",
"files": [
+ "*.d.ts",
"*.js",
"docs",
"src",
"test",
- "translations"
+ "test-helpers",
+ "translations",
+ "types"
],
"scripts": {
"prepublishOnly": "../../scripts/npm-prepublish.js",
diff --git a/packages/input-date/package.json b/packages/input-date/package.json
index fb0d8246c..cf0c1c8eb 100644
--- a/packages/input-date/package.json
+++ b/packages/input-date/package.json
@@ -13,11 +13,14 @@
"main": "index.js",
"module": "index.js",
"files": [
+ "*.d.ts",
"*.js",
"docs",
"src",
"test",
- "translations"
+ "test-helpers",
+ "translations",
+ "types"
],
"scripts": {
"prepublishOnly": "../../scripts/npm-prepublish.js",
diff --git a/packages/input-datepicker/package.json b/packages/input-datepicker/package.json
index e018f6904..86c820738 100644
--- a/packages/input-datepicker/package.json
+++ b/packages/input-datepicker/package.json
@@ -13,12 +13,14 @@
"main": "index.js",
"module": "index.js",
"files": [
+ "*.d.ts",
"*.js",
"docs",
"src",
"test",
"test-helpers",
- "translations"
+ "translations",
+ "types"
],
"scripts": {
"prepublishOnly": "../../scripts/npm-prepublish.js",
diff --git a/packages/input-email/package.json b/packages/input-email/package.json
index 63489e474..fb2b5f716 100644
--- a/packages/input-email/package.json
+++ b/packages/input-email/package.json
@@ -13,11 +13,14 @@
"main": "index.js",
"module": "index.js",
"files": [
+ "*.d.ts",
"*.js",
"docs",
"src",
"test",
- "translations"
+ "test-helpers",
+ "translations",
+ "types"
],
"scripts": {
"prepublishOnly": "../../scripts/npm-prepublish.js",
diff --git a/packages/input-iban/package.json b/packages/input-iban/package.json
index d2631de57..73cee7044 100644
--- a/packages/input-iban/package.json
+++ b/packages/input-iban/package.json
@@ -13,11 +13,14 @@
"main": "index.js",
"module": "index.js",
"files": [
+ "*.d.ts",
"*.js",
"docs",
"src",
"test",
- "translations"
+ "test-helpers",
+ "translations",
+ "types"
],
"scripts": {
"prepublishOnly": "../../scripts/npm-prepublish.js",
diff --git a/packages/input-range/package.json b/packages/input-range/package.json
index 360c5e03e..f9deb8faa 100644
--- a/packages/input-range/package.json
+++ b/packages/input-range/package.json
@@ -13,9 +13,14 @@
"main": "index.js",
"module": "index.js",
"files": [
+ "*.d.ts",
"*.js",
+ "docs",
"src",
- "test"
+ "test",
+ "test-helpers",
+ "translations",
+ "types"
],
"scripts": {
"prepublishOnly": "../../scripts/npm-prepublish.js",
diff --git a/packages/input/package.json b/packages/input/package.json
index 364a071a6..422ab07d3 100644
--- a/packages/input/package.json
+++ b/packages/input/package.json
@@ -13,11 +13,14 @@
"main": "index.js",
"module": "index.js",
"files": [
+ "*.d.ts",
"*.js",
"docs",
"src",
"test",
- "translations"
+ "test-helpers",
+ "translations",
+ "types"
],
"scripts": {
"prepublishOnly": "../../scripts/npm-prepublish.js",
diff --git a/packages/localize/package.json b/packages/localize/package.json
index 3a169c91e..045a8c143 100644
--- a/packages/localize/package.json
+++ b/packages/localize/package.json
@@ -13,12 +13,14 @@
"main": "index.js",
"module": "index.js",
"files": [
+ "*.d.ts",
"*.js",
"docs",
"src",
"test",
"test-helpers",
- "translations"
+ "translations",
+ "types"
],
"scripts": {
"prepublishOnly": "../../scripts/npm-prepublish.js",
diff --git a/packages/overlays/package.json b/packages/overlays/package.json
index e4887d4c2..762861c94 100644
--- a/packages/overlays/package.json
+++ b/packages/overlays/package.json
@@ -13,13 +13,14 @@
"main": "index.js",
"module": "index.js",
"files": [
+ "*.d.ts",
"*.js",
"docs",
"src",
"test",
"test-helpers",
- "test-suites",
- "translations"
+ "translations",
+ "types"
],
"scripts": {
"prepublishOnly": "../../scripts/npm-prepublish.js",
diff --git a/packages/providence-analytics/package.json b/packages/providence-analytics/package.json
index 1a9d4a440..cc95d6f92 100644
--- a/packages/providence-analytics/package.json
+++ b/packages/providence-analytics/package.json
@@ -49,7 +49,7 @@
"parse5": "^5.1.1",
"read-package-tree": "5.3.1",
"semver": "^7.1.3",
- "typescript": "^3.6.4"
+ "typescript": "^3.8.3"
},
"devDependencies": {
"mermaid": "^8.2.6",
diff --git a/packages/radio-group/package.json b/packages/radio-group/package.json
index 8cd568e34..fd3d4972f 100644
--- a/packages/radio-group/package.json
+++ b/packages/radio-group/package.json
@@ -13,11 +13,14 @@
"main": "index.js",
"module": "index.js",
"files": [
+ "*.d.ts",
"*.js",
"docs",
"src",
"test",
- "translations"
+ "test-helpers",
+ "translations",
+ "types"
],
"scripts": {
"prepublishOnly": "../../scripts/npm-prepublish.js",
diff --git a/packages/select-rich/package.json b/packages/select-rich/package.json
index 6f71cc176..9911631c9 100644
--- a/packages/select-rich/package.json
+++ b/packages/select-rich/package.json
@@ -13,11 +13,14 @@
"main": "index.js",
"module": "index.js",
"files": [
+ "*.d.ts",
"*.js",
"docs",
"src",
"test",
- "translations"
+ "test-helpers",
+ "translations",
+ "types"
],
"scripts": {
"prepublishOnly": "../../scripts/npm-prepublish.js",
diff --git a/packages/select/package.json b/packages/select/package.json
index 6c6401e34..63e6f6c30 100644
--- a/packages/select/package.json
+++ b/packages/select/package.json
@@ -13,11 +13,14 @@
"main": "index.js",
"module": "index.js",
"files": [
+ "*.d.ts",
"*.js",
"docs",
"src",
"test",
- "translations"
+ "test-helpers",
+ "translations",
+ "types"
],
"scripts": {
"prepublishOnly": "../../scripts/npm-prepublish.js",
diff --git a/packages/singleton-manager/package.json b/packages/singleton-manager/package.json
index 1a6eecb1b..c109c683a 100644
--- a/packages/singleton-manager/package.json
+++ b/packages/singleton-manager/package.json
@@ -13,11 +13,14 @@
"main": "index.js",
"module": "index.js",
"files": [
+ "*.d.ts",
"*.js",
"docs",
"src",
"test",
- "test-helpers"
+ "test-helpers",
+ "translations",
+ "types"
],
"scripts": {
"prepublishOnly": "../../scripts/npm-prepublish.js",
diff --git a/packages/steps/package.json b/packages/steps/package.json
index 59b3cf536..171352851 100644
--- a/packages/steps/package.json
+++ b/packages/steps/package.json
@@ -13,11 +13,14 @@
"main": "index.js",
"module": "index.js",
"files": [
+ "*.d.ts",
"*.js",
"docs",
"src",
"test",
- "translations"
+ "test-helpers",
+ "translations",
+ "types"
],
"scripts": {
"prepublishOnly": "../../scripts/npm-prepublish.js",
diff --git a/packages/switch/package.json b/packages/switch/package.json
index 532c54fb5..594eeee4d 100644
--- a/packages/switch/package.json
+++ b/packages/switch/package.json
@@ -13,11 +13,14 @@
"main": "index.js",
"module": "index.js",
"files": [
+ "*.d.ts",
"*.js",
"docs",
"src",
"test",
- "translations"
+ "test-helpers",
+ "translations",
+ "types"
],
"scripts": {
"prepublishOnly": "../../scripts/npm-prepublish.js",
diff --git a/packages/tabs/package.json b/packages/tabs/package.json
index c90c8ad55..0d892b12f 100644
--- a/packages/tabs/package.json
+++ b/packages/tabs/package.json
@@ -13,11 +13,14 @@
"main": "index.js",
"module": "index.js",
"files": [
+ "*.d.ts",
"*.js",
"docs",
"src",
"test",
- "translations"
+ "test-helpers",
+ "translations",
+ "types"
],
"scripts": {
"prepublishOnly": "../../scripts/npm-prepublish.js",
diff --git a/packages/tabs/src/LionTabs.js b/packages/tabs/src/LionTabs.js
index d1f742e78..a55fc1c69 100644
--- a/packages/tabs/src/LionTabs.js
+++ b/packages/tabs/src/LionTabs.js
@@ -1,77 +1,114 @@
import { css, html, LitElement } from '@lion/core';
-const uuid = () => Math.random().toString(36).substr(2, 10);
+/**
+ * @typedef {Object} StoreEntry
+ * @property {HTMLElement} el Dom Element
+ * @property {string} uid Unique ID for the entry
+ * @property {HTMLElement} button Button HTMLElement for the entry
+ * @property {HTMLElement} panel Panel HTMLElement for the entry
+ * @property {EventHandlerNonNull} clickHandler executed on click event
+ * @property {EventHandlerNonNull} keydownHandler executed on keydown event
+ * @property {EventHandlerNonNull} keyupHandler executed on keyup event
+ */
-const setupPanel = ({ element, uid }) => {
- element.setAttribute('id', `panel-${uid}`);
- element.setAttribute('role', 'tabpanel');
- element.setAttribute('aria-labelledby', `button-${uid}`);
-};
+function uuid() {
+ return Math.random().toString(36).substr(2, 10);
+}
-const selectPanel = element => {
- element.setAttribute('selected', true);
-};
+/**
+ * @param {StoreEntry} options
+ */
+function setupPanel({ el, uid }) {
+ el.setAttribute('id', `panel-${uid}`);
+ el.setAttribute('role', 'tabpanel');
+ el.setAttribute('aria-labelledby', `button-${uid}`);
+}
-const deselectPanel = element => {
- element.removeAttribute('selected');
-};
+/**
+ * @param {HTMLElement} el
+ */
+function selectPanel(el) {
+ el.setAttribute('selected', 'true');
+}
-const setupButton = ({ element, uid, clickHandler, keydownHandler, keyupHandler }) => {
- element.setAttribute('id', `button-${uid}`);
- element.setAttribute('role', 'tab');
- element.setAttribute('aria-controls', `panel-${uid}`);
- element.addEventListener('click', clickHandler);
- element.addEventListener('keyup', keyupHandler);
- element.addEventListener('keydown', keydownHandler);
-};
+/**
+ * @param {HTMLElement} el
+ */
+function deselectPanel(el) {
+ el.removeAttribute('selected');
+}
-const cleanButton = ({ element, clickHandler, keydownHandler, keyupHandler }) => {
- element.removeAttribute('id');
- element.removeAttribute('role');
- element.removeAttribute('aria-controls');
- element.removeEventListener('click', clickHandler);
- element.removeEventListener('keyup', keyupHandler);
- element.removeEventListener('keydown', keydownHandler);
-};
+/**
+ * @param {StoreEntry} options
+ */
+function setupButton({ el, uid, clickHandler, keydownHandler, keyupHandler }) {
+ el.setAttribute('id', `button-${uid}`);
+ el.setAttribute('role', 'tab');
+ el.setAttribute('aria-controls', `panel-${uid}`);
+ el.addEventListener('click', clickHandler);
+ el.addEventListener('keyup', keyupHandler);
+ el.addEventListener('keydown', keydownHandler);
+}
-const selectButton = (element, withFocus = false) => {
+/**
+ * @param {StoreEntry} options
+ */
+function cleanButton({ el, clickHandler, keydownHandler, keyupHandler }) {
+ el.removeAttribute('id');
+ el.removeAttribute('role');
+ el.removeAttribute('aria-controls');
+ el.removeEventListener('click', clickHandler);
+ el.removeEventListener('keyup', keyupHandler);
+ el.removeEventListener('keydown', keydownHandler);
+}
+
+/**
+ * @param {HTMLElement} el
+ * @param {boolean} withFocus
+ */
+function selectButton(el, withFocus = false) {
if (withFocus) {
- element.focus();
+ el.focus();
}
- element.setAttribute('selected', true);
- element.setAttribute('aria-selected', true);
- element.setAttribute('tabindex', 0);
-};
+ el.setAttribute('selected', 'true');
+ el.setAttribute('aria-selected', 'true');
+ el.setAttribute('tabindex', '0');
+}
-const deselectButton = element => {
- element.removeAttribute('selected');
- element.setAttribute('aria-selected', false);
- element.setAttribute('tabindex', -1);
-};
+/**
+ * @param {HTMLElement} el
+ */
+function deselectButton(el) {
+ el.removeAttribute('selected');
+ el.setAttribute('aria-selected', 'false');
+ el.setAttribute('tabindex', '-1');
+}
-const handleButtonKeydown = e => {
- switch (e.key) {
+/**
+ * @param {Event} ev
+ */
+function handleButtonKeydown(ev) {
+ const _ev = /** @type {KeyboardEvent} */ (ev);
+ switch (_ev.key) {
case 'ArrowDown':
case 'ArrowRight':
case 'ArrowUp':
case 'ArrowLeft':
case 'Home':
case 'End':
- e.preventDefault();
+ _ev.preventDefault();
/* no default */
}
-};
+}
export class LionTabs extends LitElement {
static get properties() {
return {
- /**
- * index number of the selected tab.
- */
selectedIndex: {
type: Number,
- value: 0,
+ attribute: 'selected-index',
+ reflect: true,
},
};
}
@@ -117,28 +154,42 @@ export class LionTabs extends LitElement {
constructor() {
super();
+ /**
+ * An index number of the selected tab
+ */
this.selectedIndex = 0;
}
- firstUpdated() {
- super.firstUpdated();
+ /** @param {import('lit-element').PropertyValues } changedProperties */
+ firstUpdated(changedProperties) {
+ super.firstUpdated(changedProperties);
this.__setupSlots();
}
__setupSlots() {
- const tabSlot = this.shadowRoot.querySelector('slot[name=tab]');
- const handleSlotChange = () => {
- this.__cleanStore();
- this.__setupStore();
- this.__updateSelected(false);
- };
- tabSlot.addEventListener('slotchange', handleSlotChange);
+ if (this.shadowRoot) {
+ const tabSlot = this.shadowRoot.querySelector('slot[name=tab]');
+ const handleSlotChange = () => {
+ this.__cleanStore();
+ this.__setupStore();
+ this.__updateSelected(false);
+ };
+
+ if (tabSlot) {
+ tabSlot.addEventListener('slotchange', handleSlotChange);
+ }
+ }
}
__setupStore() {
+ /** @type {StoreEntry[]} */
this.__store = [];
- const buttons = this.querySelectorAll('[slot="tab"]');
- const panels = this.querySelectorAll('[slot="panel"]');
+ const buttons = /** @type {HTMLElement[]} */ (Array.from(
+ this.querySelectorAll('[slot="tab"]'),
+ ));
+ const panels = /** @type {HTMLElement[]} */ (Array.from(
+ this.querySelectorAll('[slot="panel"]'),
+ ));
if (buttons.length !== panels.length) {
// eslint-disable-next-line no-console
console.warn(
@@ -149,19 +200,25 @@ export class LionTabs extends LitElement {
buttons.forEach((button, index) => {
const uid = uuid();
const panel = panels[index];
+
+ /** @type {StoreEntry} */
const entry = {
uid,
+ el: button,
button,
panel,
clickHandler: this.__createButtonClickHandler(index),
- keydownHandler: handleButtonKeydown,
+ keydownHandler: handleButtonKeydown.bind(this),
keyupHandler: this.__handleButtonKeyup.bind(this),
};
- setupPanel({ element: entry.panel, ...entry });
- setupButton({ element: entry.button, ...entry });
+ setupPanel({ ...entry, el: entry.panel });
+ setupButton(entry);
deselectPanel(entry.panel);
deselectButton(entry.button);
- this.__store.push(entry);
+
+ if (this.__store) {
+ this.__store.push(entry);
+ }
});
}
@@ -170,44 +227,57 @@ export class LionTabs extends LitElement {
return;
}
this.__store.forEach(entry => {
- cleanButton({ element: entry.button, ...entry });
+ cleanButton(entry);
});
}
+ /**
+ * @param {number} index
+ * @returns {EventHandlerNonNull}
+ */
__createButtonClickHandler(index) {
return () => {
this._setSelectedIndexWithFocus(index);
};
}
- __handleButtonKeyup(e) {
- switch (e.key) {
- case 'ArrowDown':
- case 'ArrowRight':
- if (this.selectedIndex + 1 >= this._pairCount) {
+ /**
+ * @param {Event} ev
+ */
+ __handleButtonKeyup(ev) {
+ const _ev = /** @type {KeyboardEvent} */ (ev);
+ if (typeof this.selectedIndex === 'number') {
+ switch (_ev.key) {
+ case 'ArrowDown':
+ case 'ArrowRight':
+ if (this.selectedIndex + 1 >= this._pairCount) {
+ this._setSelectedIndexWithFocus(0);
+ } else {
+ this._setSelectedIndexWithFocus(this.selectedIndex + 1);
+ }
+ break;
+ case 'ArrowUp':
+ case 'ArrowLeft':
+ if (this.selectedIndex <= 0) {
+ this._setSelectedIndexWithFocus(this._pairCount - 1);
+ } else {
+ this._setSelectedIndexWithFocus(this.selectedIndex - 1);
+ }
+ break;
+ case 'Home':
this._setSelectedIndexWithFocus(0);
- } else {
- this._setSelectedIndexWithFocus(this.selectedIndex + 1);
- }
- break;
- case 'ArrowUp':
- case 'ArrowLeft':
- if (this.selectedIndex <= 0) {
+ break;
+ case 'End':
this._setSelectedIndexWithFocus(this._pairCount - 1);
- } else {
- this._setSelectedIndexWithFocus(this.selectedIndex - 1);
- }
- break;
- case 'Home':
- this._setSelectedIndexWithFocus(0);
- break;
- case 'End':
- this._setSelectedIndexWithFocus(this._pairCount - 1);
- break;
- /* no default */
+ break;
+ /* no default */
+ }
}
}
+ /**
+ * @param {number} value The new index
+ */
set selectedIndex(value) {
const stale = this.__selectedIndex;
this.__selectedIndex = value;
@@ -216,6 +286,9 @@ export class LionTabs extends LitElement {
this.requestUpdate('selectedIndex', stale);
}
+ /**
+ * @param {number} value The new index for focus
+ */
_setSelectedIndexWithFocus(value) {
const stale = this.__selectedIndex;
this.__selectedIndex = value;
@@ -224,24 +297,29 @@ export class LionTabs extends LitElement {
this.requestUpdate('selectedIndex', stale);
}
+ /**
+ * @return {number}
+ */
get selectedIndex() {
- return this.__selectedIndex;
+ return this.__selectedIndex || 0;
}
get _pairCount() {
- return this.__store.length;
+ return (this.__store && this.__store.length) || 0;
}
__updateSelected(withFocus = false) {
- if (!(this.__store && this.__store[this.selectedIndex])) {
+ if (
+ !(this.__store && typeof this.selectedIndex === 'number' && this.__store[this.selectedIndex])
+ ) {
return;
}
- const previousButton = Array.from(this.children).find(
+ const previousButton = /** @type {HTMLElement} */ (Array.from(this.children).find(
child => child.slot === 'tab' && child.hasAttribute('selected'),
- );
- const previousPanel = Array.from(this.children).find(
+ ));
+ const previousPanel = /** @type {HTMLElement} */ (Array.from(this.children).find(
child => child.slot === 'panel' && child.hasAttribute('selected'),
- );
+ ));
if (previousButton) {
deselectButton(previousButton);
}
diff --git a/packages/tabs/test/lion-tabs.test.js b/packages/tabs/test/lion-tabs.test.js
index c58765612..5ed322f8e 100644
--- a/packages/tabs/test/lion-tabs.test.js
+++ b/packages/tabs/test/lion-tabs.test.js
@@ -1,6 +1,9 @@
import { expect, fixture, html } from '@open-wc/testing';
import sinon from 'sinon';
+/**
+ * @typedef {import('../src/LionTabs.js').LionTabs} LionTabs
+ */
import '../lion-tabs.js';
const basicTabs = html`
@@ -17,36 +20,34 @@ const basicTabs = html`
describe('', () => {
describe('Tabs', () => {
it('sets selectedIndex to 0 by default', async () => {
- const el = await fixture(basicTabs);
+ const el = /** @type {LionTabs} */ (await fixture(basicTabs));
expect(el.selectedIndex).to.equal(0);
});
it('can programmatically set selectedIndex', async () => {
- const el = await fixture(html`
+ const el = /** @type {LionTabs} */ (await fixture(html`
tab 1
panel 1
tab 2
panel 2
- `);
+ `));
expect(el.selectedIndex).to.equal(1);
- expect(
- Array.from(el.children).find(
- child => child.slot === 'tab' && child.hasAttribute('selected'),
- ).textContent,
- ).to.equal('tab 2');
+ let selectedTab = /** @type {Element} */ (Array.from(el.children).find(
+ child => child.slot === 'tab' && child.hasAttribute('selected'),
+ ));
+ expect(selectedTab.textContent).to.equal('tab 2');
el.selectedIndex = 0;
- expect(
- Array.from(el.children).find(
- child => child.slot === 'tab' && child.hasAttribute('selected'),
- ).textContent,
- ).to.equal('tab 1');
+ selectedTab = /** @type {Element} */ (Array.from(el.children).find(
+ child => child.slot === 'tab' && child.hasAttribute('selected'),
+ ));
+ expect(selectedTab.textContent).to.equal('tab 1');
});
it('has [selected] on current selected tab which serves as styling hook', async () => {
- const el = await fixture(basicTabs);
+ const el = /** @type {LionTabs} */ (await fixture(basicTabs));
const tabs = el.querySelectorAll('[slot=tab]');
el.selectedIndex = 0;
expect(tabs[0]).to.have.attribute('selected');
@@ -58,7 +59,7 @@ describe('', () => {
});
it('sends event "selected-changed" for every selected state change', async () => {
- const el = await fixture(basicTabs);
+ const el = /** @type {LionTabs} */ (await fixture(basicTabs));
const spy = sinon.spy();
el.addEventListener('selected-changed', spy);
el.selectedIndex = 1;
@@ -75,18 +76,18 @@ describe('', () => {
`);
expect(spy.callCount).to.equal(1);
- console.warn.restore();
+ spy.restore();
});
});
describe('Tabs ([slot=tab])', () => {
it('adds role=tab', async () => {
- const el = await fixture(html`
+ const el = /** @type {LionTabs} */ (await fixture(html`
tab
panel
- `);
+ `));
expect(Array.from(el.children).find(child => child.slot === 'tab')).to.have.attribute(
'role',
'tab',
@@ -101,7 +102,7 @@ describe('', () => {
describe('Tab Panels (slot=panel)', () => {
it('are visible when corresponding tab is selected ', async () => {
- const el = await fixture(basicTabs);
+ const el = /** @type {LionTabs} */ (await fixture(basicTabs));
const panels = el.querySelectorAll('[slot=panel]');
el.selectedIndex = 0;
expect(panels[0]).to.be.visible;
@@ -125,14 +126,14 @@ describe('', () => {
*/
describe('User interaction', () => {
it('selects a tab on click', async () => {
- const el = await fixture(basicTabs);
+ const el = /** @type {LionTabs} */ (await fixture(basicTabs));
const tabs = el.querySelectorAll('[slot=tab]');
tabs[1].dispatchEvent(new Event('click'));
expect(el.selectedIndex).to.equal(1);
});
it('selects next tab on [arrow-right] and [arrow-down]', async () => {
- const el = await fixture(basicTabs);
+ const el = /** @type {LionTabs} */ (await fixture(basicTabs));
const tabs = el.querySelectorAll('[slot=tab]');
tabs[0].dispatchEvent(new KeyboardEvent('keyup', { key: 'ArrowRight' }));
expect(el.selectedIndex).to.equal(1);
@@ -141,7 +142,7 @@ describe('', () => {
});
it('selects previous tab on [arrow-left] and [arrow-up]', async () => {
- const el = await fixture(html`
+ const el = /** @type {LionTabs} */ (await fixture(html`
tab 1
panel 1
@@ -150,7 +151,7 @@ describe('', () => {
tab 3
panel 3
- `);
+ `));
const tabs = el.querySelectorAll('[slot=tab]');
el.selectedIndex = 2;
tabs[2].dispatchEvent(new KeyboardEvent('keyup', { key: 'ArrowLeft' }));
@@ -160,29 +161,29 @@ describe('', () => {
});
it('selects first tab on [home]', async () => {
- const el = await fixture(html`
+ const el = /** @type {LionTabs} */ (await fixture(html`
tab 1
panel 1
tab 2
panel 2
- `);
+ `));
const tabs = el.querySelectorAll('[slot=tab]');
tabs[1].dispatchEvent(new KeyboardEvent('keyup', { key: 'Home' }));
expect(el.selectedIndex).to.equal(0);
});
it('selects last tab on [end]', async () => {
- const el = await fixture(basicTabs);
+ const el = /** @type {LionTabs} */ (await fixture(basicTabs));
const tabs = el.querySelectorAll('[slot=tab]');
tabs[0].dispatchEvent(new KeyboardEvent('keyup', { key: 'End' }));
expect(el.selectedIndex).to.equal(2);
});
it('selects first tab on [arrow-right] if on last tab', async () => {
- const el = await fixture(html`
-
+ const el = /** @type {LionTabs} */ (await fixture(html`
+
tab 1
panel 1
tab 2
@@ -190,14 +191,14 @@ describe('', () => {
tab 3
panel 3
- `);
+ `));
const tabs = el.querySelectorAll('[slot=tab]');
tabs[2].dispatchEvent(new KeyboardEvent('keyup', { key: 'ArrowRight' }));
expect(el.selectedIndex).to.equal(0);
});
it('selects last tab on [arrow-left] if on first tab', async () => {
- const el = await fixture(html`
+ const el = /** @type {LionTabs} */ (await fixture(html`
tab 1
panel 1
@@ -206,7 +207,7 @@ describe('', () => {
tab 3
panel 3
- `);
+ `));
const tabs = el.querySelectorAll('[slot=tab]');
tabs[0].dispatchEvent(new KeyboardEvent('keyup', { key: 'ArrowLeft' }));
expect(el.selectedIndex).to.equal(2);
@@ -215,7 +216,7 @@ describe('', () => {
describe('Content distribution', () => {
it('should work with append children', async () => {
- const el = await fixture(basicTabs);
+ const el = /** @type {LionTabs} */ (await fixture(basicTabs));
const c = 2;
const n = el.children.length / 2;
for (let i = n + 1; i < n + c + 1; i += 1) {
@@ -230,29 +231,28 @@ describe('', () => {
}
el.selectedIndex = el.children.length / 2 - 1;
await el.updateComplete;
- expect(
- Array.from(el.children).find(
- child => child.slot === 'tab' && child.hasAttribute('selected'),
- ).textContent,
- ).to.equal('tab 5');
- expect(
- Array.from(el.children).find(
- child => child.slot === 'panel' && child.hasAttribute('selected'),
- ).textContent,
- ).to.equal('panel 5');
+ const selectedTab = Array.from(el.children).find(
+ child => child.slot === 'tab' && child.hasAttribute('selected'),
+ );
+ expect(selectedTab && selectedTab.textContent).to.equal('tab 5');
+
+ const selectedPanel = Array.from(el.children).find(
+ child => child.slot === 'panel' && child.hasAttribute('selected'),
+ );
+ expect(selectedPanel && selectedPanel.textContent).to.equal('panel 5');
});
});
describe('Initializing without Focus', () => {
it('does not focus a tab when setting selectedIndex property', async () => {
- const el = await fixture(html`
+ const el = /** @type {LionTabs} */ (await fixture(html`
tab 1
panel 1
tab 2
panel 2
- `);
+ `));
el.selectedIndex = 1;
expect(el.querySelector('[slot="tab"]:nth-of-type(2)') === document.activeElement).to.be
@@ -260,56 +260,56 @@ describe('', () => {
});
it('does not focus a tab on firstUpdate', async () => {
- const el = await fixture(html`
+ const el = /** @type {LionTabs} */ (await fixture(html`
tab 1
panel 1
tab 2
panel 2
- `);
+ `));
const tabs = Array.from(el.children).filter(child => child.slot === 'tab');
expect(tabs.some(tab => tab === document.activeElement)).to.be.false;
});
it('focuses on a tab when setting with _setSelectedIndexWithFocus method', async () => {
- const el = await fixture(html`
+ const el = /** @type {LionTabs} */ (await fixture(html`
tab 1
panel 1
tab 2
panel 2
- `);
+ `));
el._setSelectedIndexWithFocus(1);
expect(el.querySelector('[slot="tab"]:nth-of-type(2)') === document.activeElement).to.be.true;
});
});
it('focuses on a tab when the selected tab is changed by user interaction', async () => {
- const el = await fixture(html`
+ const el = /** @type {LionTabs} */ (await fixture(html`
tab 1
panel 1
tab 2
panel 2
- `);
- const secondTab = el.querySelector('[slot="tab"]:nth-of-type(2)');
+ `));
+ const secondTab = /** @type {Element} */ (el.querySelector('[slot="tab"]:nth-of-type(2)'));
secondTab.dispatchEvent(new MouseEvent('click'));
expect(secondTab === document.activeElement).to.be.true;
});
describe('Accessibility', () => {
it('does not make panels focusable', async () => {
- const el = await fixture(html`
+ const el = /** @type {LionTabs} */ (await fixture(html`
tab 1
panel 1
tab 2
panel 2
- `);
+ `));
expect(Array.from(el.children).find(child => child.slot === 'panel')).to.not.have.attribute(
'tabindex',
);
@@ -319,7 +319,7 @@ describe('', () => {
});
it('makes selected tab focusable (other tabs are unfocusable)', async () => {
- const el = await fixture(html`
+ const el = /** @type {LionTabs} */ (await fixture(html`
tab 1
panel 1
@@ -328,7 +328,7 @@ describe('', () => {
tab 3
panel 3
- `);
+ `));
const tabs = el.querySelectorAll('[slot=tab]');
expect(tabs[0]).to.have.attribute('tabindex', '0');
expect(tabs[1]).to.have.attribute('tabindex', '-1');
@@ -337,14 +337,14 @@ describe('', () => {
describe('Tabs', () => {
it('links ids of content items to tab via [aria-controls]', async () => {
- const el = await fixture(html`
+ const el = /** @type {LionTabs} */ (await fixture(html`
tab 1
panel 1
tab 2
panel 2
- `);
+ `));
const tabs = el.querySelectorAll('[slot=tab]');
const panels = el.querySelectorAll('[slot=panel]');
expect(tabs[0].getAttribute('aria-controls')).to.equal(panels[0].id);
@@ -352,7 +352,7 @@ describe('', () => {
});
it('adds aria-selected=“true” to selected tab', async () => {
- const el = await fixture(html`
+ const el = /** @type {LionTabs} */ (await fixture(html`
tab 1
panel 1
@@ -361,7 +361,7 @@ describe('', () => {
tab 3
panel 3
- `);
+ `));
const tabs = el.querySelectorAll('[slot=tab]');
expect(tabs[0].getAttribute('aria-selected')).to.equal('true');
@@ -372,28 +372,28 @@ describe('', () => {
describe('panels', () => {
it('adds role="tabpanel" to panels', async () => {
- const el = await fixture(html`
+ const el = /** @type {LionTabs} */ (await fixture(html`
tab 1
panel 1
tab 2
panel 2
- `);
+ `));
const panels = el.querySelectorAll('[slot=panel]');
expect(panels[0]).to.have.attribute('role', 'tabpanel');
expect(panels[1]).to.have.attribute('role', 'tabpanel');
});
it('adds aria-labelledby referring to tab ids', async () => {
- const el = await fixture(html`
+ const el = /** @type {LionTabs} */ (await fixture(html`
tab 1
panel 1
tab 2
panel 2
- `);
+ `));
const panels = el.querySelectorAll('[slot=panel]');
const tabs = el.querySelectorAll('[slot=tab]');
expect(panels[0]).to.have.attribute('aria-labelledby', tabs[0].id);
diff --git a/packages/textarea/package.json b/packages/textarea/package.json
index c49767679..5b7f95091 100644
--- a/packages/textarea/package.json
+++ b/packages/textarea/package.json
@@ -13,11 +13,14 @@
"main": "index.js",
"module": "index.js",
"files": [
+ "*.d.ts",
"*.js",
"docs",
"src",
"test",
- "translations"
+ "test-helpers",
+ "translations",
+ "types"
],
"scripts": {
"prepublishOnly": "../../scripts/npm-prepublish.js",
diff --git a/packages/tooltip/package.json b/packages/tooltip/package.json
index e71de956d..8e58c026b 100644
--- a/packages/tooltip/package.json
+++ b/packages/tooltip/package.json
@@ -13,11 +13,14 @@
"main": "index.js",
"module": "index.js",
"files": [
+ "*.d.ts",
"*.js",
"docs",
"src",
"test",
- "translations"
+ "test-helpers",
+ "translations",
+ "types"
],
"scripts": {
"prepublishOnly": "../../scripts/npm-prepublish.js",
diff --git a/packages/validate-messages/package.json b/packages/validate-messages/package.json
index 7cff66dc1..df34a89ed 100644
--- a/packages/validate-messages/package.json
+++ b/packages/validate-messages/package.json
@@ -13,11 +13,14 @@
"main": "index.js",
"module": "index.js",
"files": [
+ "*.d.ts",
"*.js",
"docs",
"src",
"test",
- "translations"
+ "test-helpers",
+ "translations",
+ "types"
],
"scripts": {
"prepublishOnly": "../../scripts/npm-prepublish.js",
diff --git a/tsconfig.build.types.json b/tsconfig.build.types.json
new file mode 100644
index 000000000..eb0daa6a4
--- /dev/null
+++ b/tsconfig.build.types.json
@@ -0,0 +1,8 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "declaration": true,
+ "emitDeclarationOnly": true,
+ "noEmit": false
+ }
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 000000000..02514af21
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,24 @@
+{
+ "compilerOptions": {
+ "target": "esnext",
+ "module": "esnext",
+ "moduleResolution": "node",
+ "lib": ["es2017", "dom"],
+ "allowJs": true,
+ "checkJs": true,
+ "noEmit": true,
+ "strict": true,
+ "noImplicitThis": true,
+ "alwaysStrict": true,
+ "types": ["node", "mocha"],
+ "esModuleInterop": true
+ },
+ "include": ["packages/core/**/*.js", "packages/tabs/**/*.js"],
+ "exclude": [
+ "node_modules",
+ "**/node_modules/*",
+ "**/coverage/*",
+ "**/dist/**/*",
+ "packages/**/test-helpers"
+ ]
+}
diff --git a/web-test-runner.config.js b/web-test-runner.config.js
index f31e99520..8e89074df 100644
--- a/web-test-runner.config.js
+++ b/web-test-runner.config.js
@@ -1,6 +1,7 @@
module.exports = {
nodeResolve: true,
sessionStartTimeout: 30000,
+ concurrency: 5,
coverageConfig: {
threshold: {
statements: 80,
diff --git a/yarn.lock b/yarn.lock
index 496fcb7a9..869c7b1d3 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2413,11 +2413,16 @@
"@open-wc/semantic-dom-diff" "^0.13.16"
"@types/chai" "^4.1.7"
-"@open-wc/dedupe-mixin@^1.2.1", "@open-wc/dedupe-mixin@^1.2.17":
+"@open-wc/dedupe-mixin@^1.2.17":
version "1.2.17"
resolved "https://registry.yarnpkg.com/@open-wc/dedupe-mixin/-/dedupe-mixin-1.2.17.tgz#50fb903fc8785639487d7987caae45d7bba08ec7"
integrity sha512-9A3WohqNxEloJa4y1DuBL5zH12cNRNW1vsrkiaLMnOGuQdhibs2XY1oliudsKpvIeNjDXRVRPUdIIzn65BypCw==
+"@open-wc/dedupe-mixin@^1.2.18":
+ version "1.2.18"
+ resolved "https://registry.yarnpkg.com/@open-wc/dedupe-mixin/-/dedupe-mixin-1.2.18.tgz#2a86672fb3558fe2a2e1c5587dbaa0b485567ef1"
+ integrity sha512-1HpblP5edeENi0SKms7B+PKYdxHMBIQpaf0nAgTVsZeYgM9OJ3r9nrK/0MOUBZODAOZ1quvO3wlpuljq2hZPWA==
+
"@open-wc/demoing-storybook@^2.0.2":
version "2.3.11"
resolved "https://registry.yarnpkg.com/@open-wc/demoing-storybook/-/demoing-storybook-2.3.11.tgz#4c4887760591c6e58fb25852c92f0357c3445246"
@@ -2516,7 +2521,7 @@
lit-element "^2.2.1"
lit-html "^1.0.0"
-"@open-wc/testing@^2.5.0":
+"@open-wc/testing@^2.5.18":
version "2.5.18"
resolved "https://registry.yarnpkg.com/@open-wc/testing/-/testing-2.5.18.tgz#b05215535a22515fbd98183eface1c33fd804a5d"
integrity sha512-poFIaGEsHseNEbAE/pGjzZGbSksxqsL4CRr+MSPUEotzhbVa3BzA3JzPHfn3FD1zVGlBcNEU0kFa0jj/Goc52w==
@@ -2807,6 +2812,13 @@
resolved "https://registry.yarnpkg.com/@types/caniuse-api/-/caniuse-api-3.0.0.tgz#af31cc52062be0ab24583be072fd49b634dcc2fe"
integrity sha512-wT1VfnScjAftZsvLYaefu/UuwYJdYBwD2JDL2OQd01plGmuAoir5V6HnVHgrfh7zEwcasoiyO2wQ+W58sNh2sw==
+"@types/chai-dom@^0.0.8":
+ version "0.0.8"
+ resolved "https://registry.yarnpkg.com/@types/chai-dom/-/chai-dom-0.0.8.tgz#832b68d78390c80030e64d8fd4990e514484e91d"
+ integrity sha512-F5vAuz2hp7VY+0UmkFLgTwyeHDddIw1c/JbAQZp2lgpZOd3ujlHFijWO4sHdo7sun9HrzwnSJlZfINdKrUwI+w==
+ dependencies:
+ "@types/chai" "*"
+
"@types/chai-dom@^0.0.9":
version "0.0.9"
resolved "https://registry.yarnpkg.com/@types/chai-dom/-/chai-dom-0.0.9.tgz#77379354efec2568284ca355fff6a4f85f5a66f4"
@@ -6438,6 +6450,18 @@ fast-glob@^2.2.6:
merge2 "^1.2.3"
micromatch "^3.1.10"
+fast-glob@^3.1.0:
+ version "3.2.4"
+ resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.4.tgz#d20aefbf99579383e7f3cc66529158c9b98554d3"
+ integrity sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==
+ dependencies:
+ "@nodelib/fs.stat" "^2.0.2"
+ "@nodelib/fs.walk" "^1.2.3"
+ glob-parent "^5.1.0"
+ merge2 "^1.3.0"
+ micromatch "^4.0.2"
+ picomatch "^2.2.1"
+
fast-glob@^3.1.1:
version "3.2.2"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.2.tgz#ade1a9d91148965d4bf7c51f72e1ca662d32e63d"
@@ -13026,6 +13050,11 @@ try-to-catch@^1.0.2:
resolved "https://registry.yarnpkg.com/try-to-catch/-/try-to-catch-1.1.1.tgz#770162dd13b9a0e55da04db5b7f888956072038a"
integrity sha512-ikUlS+/BcImLhNYyIgZcEmq4byc31QpC+46/6Jm5ECWkVFhf8SM2Fp/0pMVXPX6vk45SMCwrP4Taxucne8I0VA==
+ts-simple-type@~0.3.6:
+ version "0.3.7"
+ resolved "https://registry.yarnpkg.com/ts-simple-type/-/ts-simple-type-0.3.7.tgz#1e77222c3d90d7093f80a954e74c725fd99c911c"
+ integrity sha512-bDXWURwpDpe1mA5E9eldmI0Mpt9zGprhtN/ZTLOJjsAMyeMy1UT7WvGRQghYewIYBYxDZurChhe4DrsPbcCVrA==
+
tsconfig-paths@^3.9.0:
version "3.9.0"
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b"
@@ -13127,10 +13156,10 @@ typedarray@^0.0.6:
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
-typescript@^3.6.4:
- version "3.9.5"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.5.tgz#586f0dba300cde8be52dd1ac4f7e1009c1b13f36"
- integrity sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ==
+typescript@^3.5.3, typescript@^3.8.3:
+ version "3.9.6"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.6.tgz#8f3e0198a34c3ae17091b35571d3afd31999365a"
+ integrity sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw==
typical@^4.0.0:
version "4.0.0"
@@ -13629,6 +13658,16 @@ wcwidth@^1.0.0, wcwidth@^1.0.1:
dependencies:
defaults "^1.0.3"
+web-component-analyzer@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/web-component-analyzer/-/web-component-analyzer-1.0.3.tgz#da73dff15d6a8f4864311664476f0f436274e97d"
+ integrity sha512-QA6GVVJrKRPHLVqPv4evY0H+du1yY+E1q8c82bdY5e10+pWsRfeYA+Hsh2r8yl1EGQVC55SeV3tGvJ6+CxaH/Q==
+ dependencies:
+ fast-glob "^3.1.0"
+ ts-simple-type "~0.3.6"
+ typescript "^3.5.3"
+ yargs "^15.0.2"
+
web-namespaces@^1.0.0, web-namespaces@^1.1.2:
version "1.1.4"
resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec"