Merge pull request #675 from ing-bank/feat/types
Add Types for @lion/core, @lion/tabs, and add type lint and types generate to build
This commit is contained in:
commit
b63c07bb9a
60 changed files with 1386 additions and 900 deletions
|
|
@ -57,6 +57,7 @@ jobs:
|
||||||
command: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
|
command: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
|
||||||
- run: git config --global user.email circleci@circleci
|
- run: git config --global user.email circleci@circleci
|
||||||
- run: git config --global user.name CircleCI
|
- run: git config --global user.name CircleCI
|
||||||
|
- run: npm run build:types
|
||||||
- run:
|
- run:
|
||||||
name: Publish package
|
name: Publish package
|
||||||
command: "./node_modules/.bin/lerna publish --message 'chore: release new versions' --exact --yes"
|
command: "./node_modules/.bin/lerna publish --message 'chore: release new versions' --exact --yes"
|
||||||
|
|
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -20,6 +20,10 @@ yarn-error.log
|
||||||
/build/
|
/build/
|
||||||
/bundlesize/dist/
|
/bundlesize/dist/
|
||||||
|
|
||||||
|
## types
|
||||||
|
*.d.ts
|
||||||
|
!packages/*/types/*
|
||||||
|
|
||||||
## temp folders
|
## temp folders
|
||||||
/.tmp/
|
/.tmp/
|
||||||
/coverage/
|
/coverage/
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@
|
||||||
"packages/*"
|
"packages/*"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"build:docs": "wca analyze \"packages/tabs/**/*.js\"",
|
||||||
|
"build:types": "tsc -p tsconfig.build.types.json",
|
||||||
"bundlesize": "rollup -c bundlesize/rollup.config.js && bundlesize",
|
"bundlesize": "rollup -c bundlesize/rollup.config.js && bundlesize",
|
||||||
"dev-server": "es-dev-server",
|
"dev-server": "es-dev-server",
|
||||||
"format": "npm run format:eslint && npm run format:prettier",
|
"format": "npm run format:eslint && npm run format:prettier",
|
||||||
|
|
@ -16,6 +18,7 @@
|
||||||
"lint:eslint": "eslint --ext .js,.html .",
|
"lint:eslint": "eslint --ext .js,.html .",
|
||||||
"lint:markdownlint": "git ls-files '*.md' | xargs markdownlint --ignore '**/CHANGELOG.md'",
|
"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:prettier": "prettier \"**/*.js\" --list-different || (echo '↑↑ these files are not prettier formatted ↑↑' && exit 1)",
|
||||||
|
"lint:types": "tsc",
|
||||||
"lint:versions": "node ./scripts/lint-versions.js",
|
"lint:versions": "node ./scripts/lint-versions.js",
|
||||||
"start": "npm run storybook",
|
"start": "npm run storybook",
|
||||||
"storybook": "start-storybook -p 9001",
|
"storybook": "start-storybook -p 9001",
|
||||||
|
|
@ -33,10 +36,11 @@
|
||||||
"@open-wc/building-rollup": "^1.2.1",
|
"@open-wc/building-rollup": "^1.2.1",
|
||||||
"@open-wc/demoing-storybook": "^2.0.2",
|
"@open-wc/demoing-storybook": "^2.0.2",
|
||||||
"@open-wc/eslint-config": "^1.0.0",
|
"@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",
|
"@open-wc/testing-helpers": "^1.0.0",
|
||||||
"@storybook/addon-a11y": "~5.0.0",
|
"@storybook/addon-a11y": "~5.0.0",
|
||||||
"@web/test-runner": "^0.6.18",
|
"@web/test-runner": "^0.6.18",
|
||||||
|
"@types/chai-dom": "^0.0.8",
|
||||||
"@webcomponents/webcomponentsjs": "^2.2.5",
|
"@webcomponents/webcomponentsjs": "^2.2.5",
|
||||||
"babel-eslint": "^8.2.6",
|
"babel-eslint": "^8.2.6",
|
||||||
"babel-polyfill": "^6.26.0",
|
"babel-polyfill": "^6.26.0",
|
||||||
|
|
@ -56,6 +60,8 @@
|
||||||
"rimraf": "^2.6.3",
|
"rimraf": "^2.6.3",
|
||||||
"rollup": "^2.0.0",
|
"rollup": "^2.0.0",
|
||||||
"sinon": "^7.2.2",
|
"sinon": "^7.2.2",
|
||||||
|
"typescript": "^3.8.3",
|
||||||
|
"web-component-analyzer": "^1.0.3",
|
||||||
"webpack-merge": "^4.1.5",
|
"webpack-merge": "^4.1.5",
|
||||||
"whatwg-fetch": "^3.0.0"
|
"whatwg-fetch": "^3.0.0"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,14 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
"*.d.ts",
|
||||||
"*.js",
|
"*.js",
|
||||||
"docs",
|
"docs",
|
||||||
"src",
|
"src",
|
||||||
"test",
|
"test",
|
||||||
"translations"
|
"test-helpers",
|
||||||
|
"translations",
|
||||||
|
"types"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,14 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
"*.d.ts",
|
||||||
"*.js",
|
"*.js",
|
||||||
"docs",
|
"docs",
|
||||||
"src",
|
"src",
|
||||||
"test",
|
"test",
|
||||||
"translations"
|
"test-helpers",
|
||||||
|
"translations",
|
||||||
|
"types"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,14 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
"*.d.ts",
|
||||||
"*.js",
|
"*.js",
|
||||||
"docs",
|
"docs",
|
||||||
"src",
|
"src",
|
||||||
"test",
|
"test",
|
||||||
"translations"
|
"test-helpers",
|
||||||
|
"translations",
|
||||||
|
"types"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
||||||
|
|
|
||||||
|
|
@ -13,12 +13,14 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
"*.d.ts",
|
||||||
"*.js",
|
"*.js",
|
||||||
"docs",
|
"docs",
|
||||||
"src",
|
"src",
|
||||||
"test",
|
"test",
|
||||||
"test-helpers",
|
"test-helpers",
|
||||||
"translations"
|
"translations",
|
||||||
|
"types"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,14 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
"*.d.ts",
|
||||||
"*.js",
|
"*.js",
|
||||||
"docs",
|
"docs",
|
||||||
"src",
|
"src",
|
||||||
"test",
|
"test",
|
||||||
"translations"
|
"test-helpers",
|
||||||
|
"translations",
|
||||||
|
"types"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,3 @@
|
||||||
/**
|
|
||||||
* Info for TypeScript users:
|
|
||||||
* For now please import types from lit-element and lit-html directly.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// lit-element
|
// lit-element
|
||||||
export {
|
export {
|
||||||
css,
|
css,
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,14 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
"*.d.ts",
|
||||||
"*.js",
|
"*.js",
|
||||||
"docs",
|
"docs",
|
||||||
"src",
|
"src",
|
||||||
|
"test",
|
||||||
"test-helpers",
|
"test-helpers",
|
||||||
"translations"
|
"translations",
|
||||||
|
"types"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
||||||
|
|
@ -26,7 +29,7 @@
|
||||||
},
|
},
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@open-wc/dedupe-mixin": "^1.2.1",
|
"@open-wc/dedupe-mixin": "^1.2.18",
|
||||||
"@open-wc/scoped-elements": "^1.0.3",
|
"@open-wc/scoped-elements": "^1.0.3",
|
||||||
"lit-element": "^2.2.1",
|
"lit-element": "^2.2.1",
|
||||||
"lit-html": "^1.0.0"
|
"lit-html": "^1.0.0"
|
||||||
|
|
|
||||||
|
|
@ -1,203 +1,177 @@
|
||||||
/* eslint-disable class-methods-use-this */
|
/* eslint-disable class-methods-use-this */
|
||||||
|
|
||||||
import { dedupeMixin } from '@open-wc/dedupe-mixin';
|
import { dedupeMixin } from '@open-wc/dedupe-mixin';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* # DelegateMixin
|
* @typedef {import('../types/DelegateMixinTypes').DelegateMixin} 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`
|
|
||||||
* <button id="button1">with delegation</button>
|
|
||||||
* `;
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @type {function()}
|
|
||||||
* @polymerMixin
|
|
||||||
* @mixinFunction
|
|
||||||
*/
|
*/
|
||||||
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}}
|
* @typedef DelegateEvent
|
||||||
*/
|
* @property {string} type - Type of event
|
||||||
get delegations() {
|
* @property {Array<?>} args - Event arguments
|
||||||
return {
|
*/
|
||||||
target: null,
|
|
||||||
events: [],
|
|
||||||
methods: [],
|
|
||||||
properties: [],
|
|
||||||
attributes: [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedCallback() {
|
/** @type {DelegateMixin} */
|
||||||
if (super.connectedCallback) {
|
const DelegateMixinImplementation = superclass =>
|
||||||
super.connectedCallback();
|
// eslint-disable-next-line
|
||||||
}
|
class DelegateMixin extends superclass {
|
||||||
this._connectDelegateMixin();
|
constructor() {
|
||||||
}
|
super();
|
||||||
|
|
||||||
updated(...args) {
|
/** @type {DelegateEvent[]} */
|
||||||
super.updated(...args);
|
this.__eventsQueue = [];
|
||||||
this._connectDelegateMixin();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/** @type {Object.<string,?>} */
|
||||||
* @param {string} type
|
this.__propertiesQueue = {};
|
||||||
* @param {Object} args
|
this.__setupPropertyDelegation();
|
||||||
*/
|
}
|
||||||
addEventListener(type, ...args) {
|
|
||||||
const delegatedEvents = this.delegations.events;
|
/**
|
||||||
if (delegatedEvents.indexOf(type) > -1) {
|
* @returns {{target: Function, events: string[], methods: string[], properties: string[], attributes: string[]}}
|
||||||
if (this.delegationTarget) {
|
*/
|
||||||
this.delegationTarget.addEventListener(type, ...args);
|
get delegations() {
|
||||||
} else {
|
return {
|
||||||
this.__eventsQueue.push({ type, args });
|
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 {
|
} else {
|
||||||
super.addEventListener(type, ...args);
|
this.__eventsQueue.push({ type, args });
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
super.addEventListener(type, ...args);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
* @param {string} value
|
* @param {string} value
|
||||||
*/
|
*/
|
||||||
setAttribute(name, value) {
|
setAttribute(name, value) {
|
||||||
const attributeNames = this.delegations.attributes;
|
const attributeNames = this.delegations.attributes;
|
||||||
if (attributeNames.indexOf(name) > -1) {
|
if (attributeNames.indexOf(name) > -1) {
|
||||||
if (this.delegationTarget) {
|
if (this.delegationTarget) {
|
||||||
this.delegationTarget.setAttribute(name, value);
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
super.removeAttribute(name);
|
super.removeAttribute(name);
|
||||||
|
} else {
|
||||||
|
super.setAttribute(name, value);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @protected
|
* @param {string} name
|
||||||
*/
|
*/
|
||||||
_connectDelegateMixin() {
|
removeAttribute(name) {
|
||||||
if (this.__connectedDelegateMixin) return;
|
const attributeNames = this.delegations.attributes;
|
||||||
|
if (attributeNames.indexOf(name) > -1) {
|
||||||
if (!this.delegationTarget) {
|
|
||||||
this.delegationTarget = this.delegations.target();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.delegationTarget) {
|
if (this.delegationTarget) {
|
||||||
this.__emptyEventListenerQueue();
|
this.delegationTarget.removeAttribute(name);
|
||||||
this.__emptyPropertiesQueue();
|
|
||||||
this.__initialAttributeDelegation();
|
|
||||||
|
|
||||||
this.__connectedDelegateMixin = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
super.removeAttribute(name);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
_connectDelegateMixin() {
|
||||||
* @private
|
if (this.__connectedDelegateMixin) return;
|
||||||
*/
|
|
||||||
__setupPropertyDelegation() {
|
if (!this.delegationTarget) {
|
||||||
const propertyNames = this.delegations.properties.concat(this.delegations.methods);
|
this.delegationTarget = this.delegations.target();
|
||||||
propertyNames.forEach(propertyName => {
|
}
|
||||||
Object.defineProperty(this, propertyName, {
|
|
||||||
get() {
|
if (this.delegationTarget) {
|
||||||
const target = this.delegationTarget;
|
this.__emptyEventListenerQueue();
|
||||||
if (target) {
|
this.__emptyPropertiesQueue();
|
||||||
if (typeof target[propertyName] === 'function') {
|
this.__initialAttributeDelegation();
|
||||||
return target[propertyName].bind(target);
|
|
||||||
}
|
this.__connectedDelegateMixin = true;
|
||||||
return target[propertyName];
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__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 target[propertyName];
|
||||||
return this.__propertiesQueue[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.
|
} else {
|
||||||
// and the property is not set, but the attribute is (it serves as a fallback for
|
this.__propertiesQueue[propertyName] = newValue;
|
||||||
// __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;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
__initialAttributeDelegation() {
|
||||||
* @private
|
const attributeNames = this.delegations.attributes;
|
||||||
*/
|
attributeNames.forEach(attributeName => {
|
||||||
__initialAttributeDelegation() {
|
const attributeValue = this.getAttribute(attributeName);
|
||||||
const attributeNames = this.delegations.attributes;
|
if (typeof attributeValue === 'string') {
|
||||||
attributeNames.forEach(attributeName => {
|
this.delegationTarget.setAttribute(attributeName, attributeValue);
|
||||||
const attributeValue = this.getAttribute(attributeName);
|
super.removeAttribute(attributeName);
|
||||||
if (typeof attributeValue === 'string') {
|
}
|
||||||
this.delegationTarget.setAttribute(attributeName, attributeValue);
|
});
|
||||||
super.removeAttribute(attributeName);
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
__emptyEventListenerQueue() {
|
||||||
* @private
|
this.__eventsQueue.forEach(ev => {
|
||||||
*/
|
this.delegationTarget.addEventListener(ev.type, ...ev.args);
|
||||||
__emptyEventListenerQueue() {
|
});
|
||||||
this.__eventsQueue.forEach(ev => {
|
}
|
||||||
this.delegationTarget.addEventListener(ev.type, ...ev.args);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
__emptyPropertiesQueue() {
|
||||||
* @private
|
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);
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
|
||||||
|
|
@ -1,64 +1,67 @@
|
||||||
import { dedupeMixin } from '@open-wc/dedupe-mixin';
|
import { dedupeMixin } from '@open-wc/dedupe-mixin';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* #DisabledMixin
|
* @typedef {import('../types/DisabledMixinTypes').DisabledMixin} DisabledMixin
|
||||||
*
|
|
||||||
* @polymerMixin
|
|
||||||
* @mixinFunction
|
|
||||||
*/
|
*/
|
||||||
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() {
|
/** @type {DisabledMixin} */
|
||||||
super();
|
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.__requestedToBeDisabled = false;
|
||||||
this.__isUserSettingDisabled = true;
|
this.__internalSetDisabled(this.__restoreDisabledTo);
|
||||||
|
|
||||||
this.__restoreDisabledTo = false;
|
|
||||||
this.disabled = false;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
makeRequestToBeDisabled() {
|
/** @param {boolean} value */
|
||||||
if (this.__requestedToBeDisabled === false) {
|
__internalSetDisabled(value) {
|
||||||
this.__requestedToBeDisabled = true;
|
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;
|
this.__restoreDisabledTo = this.disabled;
|
||||||
|
}
|
||||||
|
if (this.disabled === false && this.__requestedToBeDisabled === true) {
|
||||||
this.__internalSetDisabled(true);
|
this.__internalSetDisabled(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
retractRequestToBeDisabled() {
|
export const DisabledMixin = dedupeMixin(DisabledMixinImplementation);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
|
||||||
|
|
@ -2,85 +2,91 @@ import { dedupeMixin } from '@open-wc/dedupe-mixin';
|
||||||
import { DisabledMixin } from './DisabledMixin.js';
|
import { DisabledMixin } from './DisabledMixin.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* #DisabledWithTabIndexMixin
|
* @typedef {import('../types/DisabledWithTabIndexMixinTypes').DisabledWithTabIndexMixin} DisabledWithTabIndexMixin
|
||||||
*
|
|
||||||
* @polymerMixin
|
|
||||||
* @mixinFunction
|
|
||||||
*/
|
*/
|
||||||
export const DisabledWithTabIndexMixin = dedupeMixin(
|
|
||||||
superclass =>
|
/** @type {DisabledWithTabIndexMixin} */
|
||||||
// eslint-disable-next-line no-shadow
|
const DisabledWithTabIndexMixinImplementation = superclass =>
|
||||||
class DisabledWithTabIndexMixin extends DisabledMixin(superclass) {
|
// eslint-disable-next-line no-shadow
|
||||||
static get properties() {
|
class DisabledWithTabIndexMixinHost extends DisabledMixin(superclass) {
|
||||||
return {
|
static get properties() {
|
||||||
// we use a property here as if we use the native tabIndex we can not set a default value
|
return {
|
||||||
// in the constructor as it synchronously sets the attribute which is not allowed in the
|
// we use a property here as if we use the native tabIndex we can not set a default value
|
||||||
// constructor phase
|
// in the constructor as it synchronously sets the attribute which is not allowed in the
|
||||||
tabIndex: {
|
// constructor phase
|
||||||
type: Number,
|
tabIndex: {
|
||||||
reflect: true,
|
type: Number,
|
||||||
attribute: 'tabindex',
|
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() {
|
retractRequestToBeDisabled() {
|
||||||
super();
|
super.retractRequestToBeDisabled();
|
||||||
this.__isUserSettingTabIndex = true;
|
if (this.__requestedToBeDisabled === true) {
|
||||||
|
this.__internalSetTabIndex(this.__restoreTabIndexTo);
|
||||||
this.__restoreTabIndexTo = 0;
|
|
||||||
this.__internalSetTabIndex(0);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
makeRequestToBeDisabled() {
|
/**
|
||||||
super.makeRequestToBeDisabled();
|
* @param {number} value
|
||||||
if (this.__requestedToBeDisabled === false) {
|
*/
|
||||||
this.__restoreTabIndexTo = this.tabIndex;
|
__internalSetTabIndex(value) {
|
||||||
}
|
this.__isUserSettingTabIndex = false;
|
||||||
}
|
this.tabIndex = value;
|
||||||
|
this.__isUserSettingTabIndex = true;
|
||||||
|
}
|
||||||
|
|
||||||
retractRequestToBeDisabled() {
|
/**
|
||||||
super.retractRequestToBeDisabled();
|
* @param {PropertyKey} name
|
||||||
if (this.__requestedToBeDisabled === true) {
|
* @param {?} oldValue
|
||||||
|
*/
|
||||||
|
_requestUpdate(name, oldValue) {
|
||||||
|
super._requestUpdate(name, oldValue);
|
||||||
|
|
||||||
|
if (name === 'disabled') {
|
||||||
|
if (this.disabled) {
|
||||||
|
this.__internalSetTabIndex(-1);
|
||||||
|
} else {
|
||||||
this.__internalSetTabIndex(this.__restoreTabIndexTo);
|
this.__internalSetTabIndex(this.__restoreTabIndexTo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
__internalSetTabIndex(value) {
|
if (name === 'tabIndex') {
|
||||||
this.__isUserSettingTabIndex = false;
|
if (this.__isUserSettingTabIndex && this.tabIndex != null) {
|
||||||
this.tabIndex = value;
|
this.__restoreTabIndexTo = this.tabIndex;
|
||||||
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.tabIndex !== -1 && this.__requestedToBeDisabled === true) {
|
||||||
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) {
|
|
||||||
this.__internalSetTabIndex(-1);
|
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);
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,11 @@
|
||||||
*/
|
*/
|
||||||
export class LionSingleton {
|
export class LionSingleton {
|
||||||
/**
|
/**
|
||||||
* @param {function()} mixin
|
* @param {function} mixin
|
||||||
*/
|
*/
|
||||||
static addInstanceMixin(mixin) {
|
static addInstanceMixin(mixin) {
|
||||||
if (!this.__instanceMixins) {
|
if (!this.__instanceMixins) {
|
||||||
|
/** @type {function[]} */
|
||||||
this.__instanceMixins = [];
|
this.__instanceMixins = [];
|
||||||
}
|
}
|
||||||
this.__instanceMixins.push(mixin);
|
this.__instanceMixins.push(mixin);
|
||||||
|
|
@ -26,6 +27,8 @@ export class LionSingleton {
|
||||||
Klass = mixin(Klass);
|
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);
|
return new Klass(...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,78 +1,59 @@
|
||||||
/* eslint-disable class-methods-use-this */
|
/* eslint-disable class-methods-use-this */
|
||||||
|
|
||||||
import { dedupeMixin } from '@open-wc/dedupe-mixin';
|
import { dedupeMixin } from '@open-wc/dedupe-mixin';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* # SlotMixin
|
* @typedef {import('../types/SlotMixinTypes').SlotMixin} SlotMixin
|
||||||
* `SlotMixin`, when attached to the DOM it creates content for defined slots in the Light DOM.
|
* @typedef {import('../types/SlotMixinTypes').SlotsMap} SlotsMap
|
||||||
* 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 <div slot="foo"></div> to the Light DOM of this element
|
|
||||||
* foo: () => document.createElement('div'),
|
|
||||||
* };
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @type {function()}
|
|
||||||
* @polymerMixin
|
|
||||||
* @mixinFunction
|
|
||||||
*/
|
*/
|
||||||
export const SlotMixin = dedupeMixin(
|
|
||||||
superclass =>
|
|
||||||
// eslint-disable-next-line no-unused-vars, no-shadow
|
|
||||||
class SlotMixin extends superclass {
|
|
||||||
/**
|
|
||||||
* @returns {{}}
|
|
||||||
*/
|
|
||||||
get slots() {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
/** @type {SlotMixin} */
|
||||||
super();
|
const SlotMixinImplementation = superclass =>
|
||||||
this.__privateSlots = new Set(null);
|
// eslint-disable-next-line no-unused-vars, no-shadow
|
||||||
}
|
class SlotMixinHost extends superclass {
|
||||||
|
/**
|
||||||
|
* @return {SlotsMap}
|
||||||
|
*/
|
||||||
|
get slots() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
constructor() {
|
||||||
if (super.connectedCallback) {
|
super();
|
||||||
super.connectedCallback();
|
this.__privateSlots = new Set(null);
|
||||||
}
|
}
|
||||||
this._connectSlotMixin();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
connectedCallback() {
|
||||||
* @protected
|
if (super.connectedCallback) {
|
||||||
*/
|
super.connectedCallback();
|
||||||
_connectSlotMixin() {
|
}
|
||||||
if (!this.__isConnectedSlotMixin) {
|
this._connectSlotMixin();
|
||||||
Object.keys(this.slots).forEach(slotName => {
|
}
|
||||||
if (!this.querySelector(`[slot=${slotName}]`)) {
|
|
||||||
const slotFactory = this.slots[slotName];
|
_connectSlotMixin() {
|
||||||
const slotContent = slotFactory();
|
if (!this.__isConnectedSlotMixin) {
|
||||||
if (slotContent instanceof Element) {
|
Object.keys(this.slots).forEach(slotName => {
|
||||||
slotContent.setAttribute('slot', slotName);
|
if (!this.querySelector(`[slot=${slotName}]`)) {
|
||||||
this.appendChild(slotContent);
|
const slotFactory = this.slots[slotName];
|
||||||
this.__privateSlots.add(slotName);
|
const slotContent = slotFactory();
|
||||||
} // ignore non-elements to enable conditional slots
|
// 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
|
||||||
* @param {string} slotName Name of the slot
|
* @return {boolean} true if given slot name been created by SlotMixin
|
||||||
* @return {boolean} true if given slot name been created by SlotMixin
|
*/
|
||||||
*/
|
_isPrivateSlot(slotName) {
|
||||||
_isPrivateSlot(slotName) {
|
return this.__privateSlots.has(slotName);
|
||||||
return this.__privateSlots.has(slotName);
|
}
|
||||||
}
|
};
|
||||||
},
|
|
||||||
);
|
export const SlotMixin = dedupeMixin(SlotMixinImplementation);
|
||||||
|
|
|
||||||
|
|
@ -1,59 +1,78 @@
|
||||||
/* global ShadyCSS */
|
/* global ShadyCSS */
|
||||||
import { dedupeMixin } from '@open-wc/dedupe-mixin';
|
import { dedupeMixin } from '@open-wc/dedupe-mixin';
|
||||||
|
|
||||||
export const UpdateStylesMixin = dedupeMixin(
|
/**
|
||||||
superclass =>
|
* @typedef {import('../types/UpdateStylesMixinTypes').UpdateStylesMixin} UpdateStylesMixin
|
||||||
// eslint-disable-next-line no-shadow
|
* @typedef {import('../types/UpdateStylesMixinTypes').StylesMap} StylesMap
|
||||||
class UpdateStylesMixin extends superclass {
|
*/
|
||||||
/**
|
|
||||||
* @example
|
|
||||||
* <my-element>
|
|
||||||
* <style>
|
|
||||||
* :host {
|
|
||||||
* color: var(--foo);
|
|
||||||
* }
|
|
||||||
* </style>
|
|
||||||
* </my-element>
|
|
||||||
*
|
|
||||||
* $0.updateStyles({'background': 'orange', '--foo': '#fff'})
|
|
||||||
* Chrome, Firefox: <my-element style="background: orange; --foo: bar;">
|
|
||||||
* IE: <my-element>
|
|
||||||
* => to head: <style>color: #fff</style>
|
|
||||||
*
|
|
||||||
* @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;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
const newStyles = { ...currentStyles, ...updateStyles };
|
/** @type {UpdateStylesMixin} */
|
||||||
let newStylesString = '';
|
const UpdateStylesMixinImplementation = superclass =>
|
||||||
if (typeof ShadyCSS === 'object' && !ShadyCSS.nativeShadow) {
|
// eslint-disable-next-line no-shadow
|
||||||
// No ShadowDOM => IE, Edge
|
class UpdateStylesMixinHost extends superclass {
|
||||||
const newCssVariablesObj = {};
|
/**
|
||||||
Object.keys(newStyles).forEach(key => {
|
* @example
|
||||||
if (key.indexOf('--') === -1) {
|
* <my-element>
|
||||||
newStylesString += `${key}:${newStyles[key]};`;
|
* <style>
|
||||||
} else {
|
* :host {
|
||||||
newCssVariablesObj[key] = newStyles[key];
|
* color: var(--foo);
|
||||||
}
|
* }
|
||||||
});
|
* </style>
|
||||||
this.setAttribute('style', newStylesString);
|
* </my-element>
|
||||||
ShadyCSS.styleSubtree(this, newCssVariablesObj);
|
*
|
||||||
} else {
|
* $0.updateStyles({'background': 'orange', '--foo': '#fff'})
|
||||||
// has shadowdom => Chrome, Firefox, Safari
|
* Chrome, Firefox: <my-element style="background: orange; --foo: bar;">
|
||||||
Object.keys(newStyles).forEach(key => {
|
* IE: <my-element>
|
||||||
newStylesString += `${key}: ${newStyles[key]};`;
|
* => to head: <style>color: #fff</style>
|
||||||
});
|
*
|
||||||
this.setAttribute('style', newStylesString);
|
* @param {StylesMap} updateStyles
|
||||||
|
*/
|
||||||
|
updateStyles(updateStyles) {
|
||||||
|
const styleString = this.getAttribute('style') || this.getAttribute('data-style') || '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* reducer function
|
||||||
|
* @param {Object.<string, string>} acc
|
||||||
|
* @param {string} stylePair
|
||||||
|
*/
|
||||||
|
const reducer = (acc, stylePair) => {
|
||||||
|
/** @type {Array.<string>} */
|
||||||
|
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.<string, string>} */
|
||||||
|
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);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ if (typeof window.KeyboardEvent !== 'function') {
|
||||||
const event = KeyboardEvent.prototype;
|
const event = KeyboardEvent.prototype;
|
||||||
const descriptor = Object.getOwnPropertyDescriptor(event, 'key');
|
const descriptor = Object.getOwnPropertyDescriptor(event, 'key');
|
||||||
if (descriptor) {
|
if (descriptor) {
|
||||||
|
/** @type {Object.<string, string>} */
|
||||||
const keys = {
|
const keys = {
|
||||||
Win: 'Meta',
|
Win: 'Meta',
|
||||||
Scroll: 'ScrollLock',
|
Scroll: 'ScrollLock',
|
||||||
|
|
@ -26,10 +27,13 @@ if (typeof window.KeyboardEvent !== 'function') {
|
||||||
Object.defineProperty(event, 'key', {
|
Object.defineProperty(event, 'key', {
|
||||||
// eslint-disable-next-line object-shorthand, func-names
|
// eslint-disable-next-line object-shorthand, func-names
|
||||||
get: function () {
|
get: function () {
|
||||||
const key = descriptor.get.call(this);
|
if (descriptor.get) {
|
||||||
|
const key = descriptor.get.call(this);
|
||||||
|
|
||||||
// eslint-disable-next-line no-prototype-builtins
|
// eslint-disable-next-line no-prototype-builtins
|
||||||
return keys.hasOwnProperty(key) ? keys[key] : key;
|
return keys.hasOwnProperty(key) ? keys[key] : key;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ describe('DelegateMixin', () => {
|
||||||
get delegations() {
|
get delegations() {
|
||||||
return {
|
return {
|
||||||
...super.delegations,
|
...super.delegations,
|
||||||
target: () => this.shadowRoot.getElementById('button1'),
|
target: () => this.shadowRoot?.getElementById('button1'),
|
||||||
events: ['click'],
|
events: ['click'],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -27,7 +27,7 @@ describe('DelegateMixin', () => {
|
||||||
const element = await fixture(`<${tag}></${tag}>`);
|
const element = await fixture(`<${tag}></${tag}>`);
|
||||||
const cb = sinon.spy();
|
const cb = sinon.spy();
|
||||||
element.addEventListener('click', cb);
|
element.addEventListener('click', cb);
|
||||||
element.shadowRoot.getElementById('button1').click();
|
element.shadowRoot?.getElementById('button1')?.click();
|
||||||
expect(cb.callCount).to.equal(1);
|
expect(cb.callCount).to.equal(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -37,7 +37,7 @@ describe('DelegateMixin', () => {
|
||||||
get delegations() {
|
get delegations() {
|
||||||
return {
|
return {
|
||||||
...super.delegations,
|
...super.delegations,
|
||||||
target: () => this.shadowRoot.getElementById('button1'),
|
target: () => this.shadowRoot?.getElementById('button1'),
|
||||||
events: ['click'],
|
events: ['click'],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -47,12 +47,12 @@ describe('DelegateMixin', () => {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const element = document.createElement(tag);
|
const element = /** @type {LitElement} */ (document.createElement(tag));
|
||||||
const cb = sinon.spy();
|
const cb = sinon.spy();
|
||||||
element.addEventListener('click', cb);
|
element.addEventListener('click', cb);
|
||||||
document.body.appendChild(element);
|
document.body.appendChild(element);
|
||||||
await element.updateComplete;
|
await element.updateComplete;
|
||||||
element.shadowRoot.getElementById('button1').click();
|
element.shadowRoot?.getElementById('button1')?.click();
|
||||||
expect(cb.callCount).to.equal(1);
|
expect(cb.callCount).to.equal(1);
|
||||||
|
|
||||||
// cleanup
|
// cleanup
|
||||||
|
|
@ -80,37 +80,37 @@ describe('DelegateMixin', () => {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const element = await fixture(`
|
const element = await fixture(`<${tag}><button slot="button">click me</button></${tag}>`);
|
||||||
<${tag}><button slot="button">click me</button></${tag}>`);
|
|
||||||
const cb = sinon.spy();
|
const cb = sinon.spy();
|
||||||
element.addEventListener('click', cb);
|
element.addEventListener('click', cb);
|
||||||
Array.from(element.children)
|
const childEl = /** @type {HTMLElement} */ (Array.from(element.children)?.find(
|
||||||
.find(child => child.slot === 'button')
|
child => child.slot === 'button',
|
||||||
.click();
|
));
|
||||||
|
childEl?.click();
|
||||||
expect(cb.callCount).to.equal(1);
|
expect(cb.callCount).to.equal(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('will still support other events', async () => {
|
it('will still support other events', async () => {
|
||||||
const tag = defineCE(
|
class FooDelegate extends DelegateMixin(LitElement) {
|
||||||
class extends DelegateMixin(LitElement) {
|
get delegations() {
|
||||||
get delegations() {
|
return {
|
||||||
return {
|
...super.delegations,
|
||||||
...super.delegations,
|
target: () => this.shadowRoot?.getElementById('button1'),
|
||||||
target: () => this.shadowRoot.getElementById('button1'),
|
events: ['click'],
|
||||||
events: ['click'],
|
};
|
||||||
};
|
}
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`<button id="button1">with delegation</button>`;
|
return html`<button id="button1">with delegation</button>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
foo() {
|
foo() {
|
||||||
this.dispatchEvent(new CustomEvent('foo-event', { bubbles: true, composed: true }));
|
this.dispatchEvent(new CustomEvent('foo-event', { bubbles: true, composed: true }));
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
);
|
|
||||||
const element = await fixture(`<${tag}></${tag}>`);
|
const tag = defineCE(FooDelegate);
|
||||||
|
const element = /** @type {FooDelegate} */ (await fixture(`<${tag}></${tag}>`));
|
||||||
const cb = sinon.spy();
|
const cb = sinon.spy();
|
||||||
element.addEventListener('foo-event', cb);
|
element.addEventListener('foo-event', cb);
|
||||||
element.foo();
|
element.foo();
|
||||||
|
|
@ -123,7 +123,7 @@ describe('DelegateMixin', () => {
|
||||||
get delegations() {
|
get delegations() {
|
||||||
return {
|
return {
|
||||||
...super.delegations,
|
...super.delegations,
|
||||||
target: () => this.shadowRoot.getElementById('button1'),
|
target: () => this.shadowRoot?.getElementById('button1'),
|
||||||
methods: ['click'],
|
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();
|
const cb = sinon.spy();
|
||||||
element.shadowRoot.getElementById('button1').addEventListener('click', cb);
|
element.shadowRoot?.getElementById('button1')?.addEventListener('click', cb);
|
||||||
element.click();
|
element.click();
|
||||||
expect(cb.callCount).to.equal(1);
|
expect(cb.callCount).to.equal(1);
|
||||||
});
|
});
|
||||||
|
|
@ -147,56 +147,69 @@ describe('DelegateMixin', () => {
|
||||||
this.foo = { a: 'a', b: 'b' };
|
this.foo = { a: 'a', b: 'b' };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {?} a
|
||||||
|
* @param {?} b
|
||||||
|
*/
|
||||||
setFooAandB(a, b) {
|
setFooAandB(a, b) {
|
||||||
this.foo.a = a;
|
this.foo.a = a;
|
||||||
this.foo.b = b;
|
this.foo.b = b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customElements.define('delegate-argument-sub', DelegateArgumentSub);
|
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() {
|
class DelegateArgumentParent extends DelegateMixin(LitElement) {
|
||||||
return html`<delegate-argument-sub id="sub"></delegate-argument-sub>`;
|
get delegations() {
|
||||||
}
|
return {
|
||||||
},
|
...super.delegations,
|
||||||
);
|
target: () => this.shadowRoot?.getElementById('sub'),
|
||||||
|
methods: ['setFooAandB'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const element = await fixture(`<${tag}></${tag}>`);
|
render() {
|
||||||
element.disabled = true;
|
return html`<delegate-argument-sub id="sub"></delegate-argument-sub>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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');
|
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 () => {
|
it('will set delegated properties', async () => {
|
||||||
const tag = defineCE(
|
class PropDelegate extends DelegateMixin(LitElement) {
|
||||||
class extends DelegateMixin(LitElement) {
|
get delegations() {
|
||||||
get delegations() {
|
return {
|
||||||
return {
|
...super.delegations,
|
||||||
...super.delegations,
|
target: () => this.shadowRoot?.getElementById('button1'),
|
||||||
target: () => this.shadowRoot.getElementById('button1'),
|
properties: ['disabled'],
|
||||||
properties: ['disabled'],
|
};
|
||||||
};
|
}
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`<button id="button1">with delegation</button>`;
|
return html`<button id="button1">with delegation</button>`;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
);
|
const tag = defineCE(PropDelegate);
|
||||||
const element = await fixture(`<${tag}></${tag}>`);
|
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;
|
element.disabled = true;
|
||||||
|
|
||||||
await element.updateComplete;
|
await element.updateComplete;
|
||||||
expect(element.shadowRoot.getElementById('button1').disabled).to.equal(true);
|
|
||||||
expect(element.shadowRoot.getElementById('button1').hasAttribute('disabled')).to.equal(true);
|
/** @typedef {Object.<string,boolean>} 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 () => {
|
it('delegates properties before delegation target is attached to DOM', async () => {
|
||||||
|
|
@ -205,7 +218,7 @@ describe('DelegateMixin', () => {
|
||||||
get delegations() {
|
get delegations() {
|
||||||
return {
|
return {
|
||||||
...super.delegations,
|
...super.delegations,
|
||||||
target: () => this.shadowRoot.getElementById('button1'),
|
target: () => this.shadowRoot?.getElementById('button1'),
|
||||||
properties: ['disabled'],
|
properties: ['disabled'],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -215,12 +228,18 @@ describe('DelegateMixin', () => {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const element = document.createElement(tag);
|
/** @typedef {Object.<string,boolean>} Btn */
|
||||||
|
/** @typedef {Btn & LitElement} DelegatedEl */
|
||||||
|
const element = /** @type {DelegatedEl} */ (document.createElement(tag));
|
||||||
|
|
||||||
element.disabled = true;
|
element.disabled = true;
|
||||||
document.body.appendChild(element);
|
document.body.appendChild(element);
|
||||||
await element.updateComplete;
|
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
|
// cleanup
|
||||||
document.body.removeChild(element);
|
document.body.removeChild(element);
|
||||||
});
|
});
|
||||||
|
|
@ -231,7 +250,7 @@ describe('DelegateMixin', () => {
|
||||||
get delegations() {
|
get delegations() {
|
||||||
return {
|
return {
|
||||||
...super.delegations,
|
...super.delegations,
|
||||||
target: () => this.shadowRoot.getElementById('button1'),
|
target: () => this.shadowRoot?.getElementById('button1'),
|
||||||
attributes: ['disabled'],
|
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', '');
|
element.setAttribute('disabled', '');
|
||||||
await element.updateComplete;
|
await element.updateComplete;
|
||||||
expect(element.hasAttribute('disabled')).to.equal(false);
|
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 () => {
|
it('will read inital attributes', async () => {
|
||||||
|
|
@ -254,7 +273,7 @@ describe('DelegateMixin', () => {
|
||||||
get delegations() {
|
get delegations() {
|
||||||
return {
|
return {
|
||||||
...super.delegations,
|
...super.delegations,
|
||||||
target: () => this.shadowRoot.getElementById('button1'),
|
target: () => this.shadowRoot?.getElementById('button1'),
|
||||||
attributes: ['disabled'],
|
attributes: ['disabled'],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -266,7 +285,7 @@ describe('DelegateMixin', () => {
|
||||||
);
|
);
|
||||||
const element = await fixture(`<${tag} disabled></${tag}>`);
|
const element = await fixture(`<${tag} disabled></${tag}>`);
|
||||||
expect(element.hasAttribute('disabled')).to.equal(false);
|
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 () => {
|
it('will delegate removeAttribute', async () => {
|
||||||
|
|
@ -275,7 +294,7 @@ describe('DelegateMixin', () => {
|
||||||
get delegations() {
|
get delegations() {
|
||||||
return {
|
return {
|
||||||
...super.delegations,
|
...super.delegations,
|
||||||
target: () => this.shadowRoot.getElementById('button1'),
|
target: () => this.shadowRoot?.getElementById('button1'),
|
||||||
attributes: ['disabled'],
|
attributes: ['disabled'],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -285,135 +304,146 @@ describe('DelegateMixin', () => {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const element = await fixture(`<${tag} disabled></${tag}>`);
|
const element = /** @type {LitElement} */ (await fixture(`<${tag} disabled></${tag}>`));
|
||||||
element.removeAttribute('disabled', '');
|
element.removeAttribute('disabled');
|
||||||
await element.updateComplete;
|
await element.updateComplete;
|
||||||
expect(element.hasAttribute('disabled')).to.equal(false);
|
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 () => {
|
it('respects user defined values for delegated attributes and properties', async () => {
|
||||||
const tag = defineCE(
|
class ScheduledElement extends DelegateMixin(LitElement) {
|
||||||
class extends DelegateMixin(LitElement) {
|
get delegations() {
|
||||||
get delegations() {
|
return {
|
||||||
return {
|
...super.delegations,
|
||||||
...super.delegations,
|
// this just means itś config is set to the queue when called before connectedCallback
|
||||||
// this just means itś config is set to the queue when called before connectedCallback
|
target: () => this.scheduledElement,
|
||||||
target: () => this.scheduledElement,
|
attributes: ['type'],
|
||||||
attributes: ['type'],
|
properties: ['type'],
|
||||||
properties: ['type'],
|
};
|
||||||
};
|
}
|
||||||
}
|
|
||||||
|
|
||||||
get scheduledElement() {
|
get scheduledElement() {
|
||||||
return this.querySelector('input');
|
return this.querySelector('input');
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.type = 'email'; // 1. here we set the delegated prop and it should be scheduled
|
this.type = 'email'; // 1. here we set the delegated prop and it should be scheduled
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
// 2. this is where we add teh delegation target (so after 1)
|
// 2. this is where we add teh delegation target (so after 1)
|
||||||
this.appendChild(document.createElement('input'));
|
this.appendChild(document.createElement('input'));
|
||||||
super.connectedCallback(); // let the DelegateMixin do its work
|
super.connectedCallback(); // let the DelegateMixin do its work
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
);
|
|
||||||
|
const tag = defineCE(ScheduledElement);
|
||||||
const tagName = unsafeStatic(tag);
|
const tagName = unsafeStatic(tag);
|
||||||
|
|
||||||
// Here, the Application Developerd tries to set the type via attribute
|
// Here, the Application Developerd tries to set the type via attribute
|
||||||
const elementAttr = await fixture(`<${tag} type="radio"></${tag}>`);
|
const elementAttr = /** @type {ScheduledElement} */ (await fixture(
|
||||||
expect(elementAttr.scheduledElement.type).to.equal('radio');
|
`<${tag} type="radio"></${tag}>`,
|
||||||
|
));
|
||||||
|
expect(elementAttr.scheduledElement?.type).to.equal('radio');
|
||||||
// Here, the Application Developer tries to set the type via property
|
// Here, the Application Developer tries to set the type via property
|
||||||
const elementProp = await fixture(html`<${tagName} .type=${'radio'}></${tagName}>`);
|
const elementProp = /** @type {ScheduledElement} */ (await fixture(
|
||||||
expect(elementProp.scheduledElement.type).to.equal('radio');
|
html`<${tagName} .type=${'radio'}></${tagName}>`,
|
||||||
|
));
|
||||||
|
expect(elementProp.scheduledElement?.type).to.equal('radio');
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`uses attribute value as a fallback for delegated property getter
|
it(`uses attribute value as a fallback for delegated property getter
|
||||||
when property not set by user and delegationTarget not ready`, async () => {
|
when property not set by user and delegationTarget not ready`, async () => {
|
||||||
const tag = defineCE(
|
class FallbackEl extends DelegateMixin(LitElement) {
|
||||||
class extends DelegateMixin(LitElement) {
|
get delegations() {
|
||||||
get delegations() {
|
return {
|
||||||
return {
|
...super.delegations,
|
||||||
...super.delegations,
|
target: () => this.delegatedEl,
|
||||||
target: () => this.delegatedEl,
|
properties: ['type'],
|
||||||
properties: ['type'],
|
attributes: ['type'],
|
||||||
attributes: ['type'],
|
};
|
||||||
};
|
}
|
||||||
}
|
|
||||||
|
|
||||||
get delegatedEl() {
|
get delegatedEl() {
|
||||||
// returns null, so we can test that "cached" attr is used as fallback
|
// returns null, so we can test that "cached" attr is used as fallback
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
);
|
const tag = defineCE(FallbackEl);
|
||||||
const element = await fixture(`<${tag} type="radio"></${tag}>`);
|
const element = /** @type {FallbackEl} */ (await fixture(`<${tag} type="radio"></${tag}>`));
|
||||||
expect(element.delegatedEl).to.equal(null);
|
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
|
expect(element.type).to.equal('radio'); // value retrieved from host instead of delegatedTarget
|
||||||
});
|
});
|
||||||
|
|
||||||
it('works with connectedCallback', async () => {
|
it('works with connectedCallback', async () => {
|
||||||
const tag = await defineCE(
|
class ConnectedElement extends DelegateMixin(HTMLElement) {
|
||||||
class extends DelegateMixin(HTMLElement) {
|
get delegations() {
|
||||||
get delegations() {
|
return {
|
||||||
return {
|
...super.delegations,
|
||||||
...super.delegations,
|
target: () => this.querySelector('div'),
|
||||||
target: () => this.querySelector('div'),
|
properties: ['foo'],
|
||||||
properties: ['foo'],
|
};
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
},
|
const tag = await defineCE(ConnectedElement);
|
||||||
);
|
const element = /** @type {ConnectedElement} */ (await fixture(`<${tag}><div></div></${tag}>`));
|
||||||
const element = await fixture(`<${tag}><div></div></${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';
|
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 () => {
|
it('works with shadow dom', async () => {
|
||||||
const tag = await defineCE(
|
class A extends DelegateMixin(LitElement) {
|
||||||
class extends DelegateMixin(LitElement) {
|
get delegations() {
|
||||||
get delegations() {
|
return {
|
||||||
return {
|
...super.delegations,
|
||||||
...super.delegations,
|
target: () => this.shadowRoot?.querySelector('div'),
|
||||||
target: () => this.shadowRoot.querySelector('div'),
|
properties: ['foo'],
|
||||||
properties: ['foo'],
|
};
|
||||||
};
|
}
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`<div></div>`;
|
return html`<div></div>`;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
);
|
const tag = await defineCE(A);
|
||||||
const element = await fixture(`<${tag}></${tag}>`);
|
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';
|
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 () => {
|
it('works with light dom', async () => {
|
||||||
const tag = await defineCE(
|
class A extends DelegateMixin(LitElement) {
|
||||||
class extends DelegateMixin(LitElement) {
|
get delegations() {
|
||||||
get delegations() {
|
return {
|
||||||
return {
|
...super.delegations,
|
||||||
...super.delegations,
|
target: () => this.querySelector('div'),
|
||||||
target: () => this.querySelector('div'),
|
properties: ['foo'],
|
||||||
properties: ['foo'],
|
};
|
||||||
};
|
}
|
||||||
}
|
|
||||||
|
|
||||||
createRenderRoot() {
|
createRenderRoot() {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`<div></div>`;
|
return html`<div></div>`;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
);
|
|
||||||
|
const tag = await defineCE(A);
|
||||||
const element = await fixture(`<${tag}></${tag}>`);
|
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';
|
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');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -3,21 +3,26 @@ import { LitElement } from '../index.js';
|
||||||
import { DisabledMixin } from '../src/DisabledMixin.js';
|
import { DisabledMixin } from '../src/DisabledMixin.js';
|
||||||
|
|
||||||
describe('DisabledMixin', () => {
|
describe('DisabledMixin', () => {
|
||||||
|
class CanBeDisabled extends DisabledMixin(LitElement) {}
|
||||||
before(() => {
|
before(() => {
|
||||||
class CanBeDisabled extends DisabledMixin(LitElement) {}
|
|
||||||
customElements.define('can-be-disabled', CanBeDisabled);
|
customElements.define('can-be-disabled', CanBeDisabled);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('reflects disabled to attribute', async () => {
|
it('reflects disabled to attribute', async () => {
|
||||||
const el = await fixture(html`<can-be-disabled></can-be-disabled>`);
|
const el = /** @type {CanBeDisabled} */ (await fixture(
|
||||||
|
html`<can-be-disabled></can-be-disabled>`,
|
||||||
|
));
|
||||||
expect(el.hasAttribute('disabled')).to.be.false;
|
expect(el.hasAttribute('disabled')).to.be.false;
|
||||||
|
el.makeRequestToBeDisabled();
|
||||||
el.disabled = true;
|
el.disabled = true;
|
||||||
await el.updateComplete;
|
await el.updateComplete;
|
||||||
expect(el.hasAttribute('disabled')).to.be.true;
|
expect(el.hasAttribute('disabled')).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can be requested to be disabled', async () => {
|
it('can be requested to be disabled', async () => {
|
||||||
const el = await fixture(html`<can-be-disabled></can-be-disabled>`);
|
const el = /** @type {CanBeDisabled} */ (await fixture(
|
||||||
|
html`<can-be-disabled></can-be-disabled>`,
|
||||||
|
));
|
||||||
el.makeRequestToBeDisabled();
|
el.makeRequestToBeDisabled();
|
||||||
expect(el.disabled).to.be.true;
|
expect(el.disabled).to.be.true;
|
||||||
await el.updateComplete;
|
await el.updateComplete;
|
||||||
|
|
@ -25,7 +30,9 @@ describe('DisabledMixin', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('will not allow to become enabled after makeRequestToBeDisabled()', async () => {
|
it('will not allow to become enabled after makeRequestToBeDisabled()', async () => {
|
||||||
const el = await fixture(html`<can-be-disabled></can-be-disabled>`);
|
const el = /** @type {CanBeDisabled} */ (await fixture(
|
||||||
|
html`<can-be-disabled></can-be-disabled>`,
|
||||||
|
));
|
||||||
el.makeRequestToBeDisabled();
|
el.makeRequestToBeDisabled();
|
||||||
expect(el.disabled).to.be.true;
|
expect(el.disabled).to.be.true;
|
||||||
|
|
||||||
|
|
@ -34,14 +41,18 @@ describe('DisabledMixin', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('will stay disabled after retractRequestToBeDisabled() if it was disabled before', async () => {
|
it('will stay disabled after retractRequestToBeDisabled() if it was disabled before', async () => {
|
||||||
const el = await fixture(html`<can-be-disabled disabled></can-be-disabled>`);
|
const el = /** @type {CanBeDisabled} */ (await fixture(
|
||||||
|
html`<can-be-disabled disabled></can-be-disabled>`,
|
||||||
|
));
|
||||||
el.makeRequestToBeDisabled();
|
el.makeRequestToBeDisabled();
|
||||||
el.retractRequestToBeDisabled();
|
el.retractRequestToBeDisabled();
|
||||||
expect(el.disabled).to.be.true;
|
expect(el.disabled).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('will become enabled after retractRequestToBeDisabled() if it was enabled before', async () => {
|
it('will become enabled after retractRequestToBeDisabled() if it was enabled before', async () => {
|
||||||
const el = await fixture(html`<can-be-disabled></can-be-disabled>`);
|
const el = /** @type {CanBeDisabled} */ (await fixture(
|
||||||
|
html`<can-be-disabled></can-be-disabled>`,
|
||||||
|
));
|
||||||
el.makeRequestToBeDisabled();
|
el.makeRequestToBeDisabled();
|
||||||
expect(el.disabled).to.be.true;
|
expect(el.disabled).to.be.true;
|
||||||
el.retractRequestToBeDisabled();
|
el.retractRequestToBeDisabled();
|
||||||
|
|
@ -49,7 +60,9 @@ describe('DisabledMixin', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('may allow multiple calls to makeRequestToBeDisabled()', async () => {
|
it('may allow multiple calls to makeRequestToBeDisabled()', async () => {
|
||||||
const el = await fixture(html`<can-be-disabled></can-be-disabled>`);
|
const el = /** @type {CanBeDisabled} */ (await fixture(
|
||||||
|
html`<can-be-disabled></can-be-disabled>`,
|
||||||
|
));
|
||||||
el.makeRequestToBeDisabled();
|
el.makeRequestToBeDisabled();
|
||||||
el.makeRequestToBeDisabled();
|
el.makeRequestToBeDisabled();
|
||||||
el.retractRequestToBeDisabled();
|
el.retractRequestToBeDisabled();
|
||||||
|
|
@ -57,7 +70,9 @@ describe('DisabledMixin', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('will restore last state after retractRequestToBeDisabled()', async () => {
|
it('will restore last state after retractRequestToBeDisabled()', async () => {
|
||||||
const el = await fixture(html`<can-be-disabled></can-be-disabled>`);
|
const el = /** @type {CanBeDisabled} */ (await fixture(
|
||||||
|
html`<can-be-disabled></can-be-disabled>`,
|
||||||
|
));
|
||||||
el.makeRequestToBeDisabled();
|
el.makeRequestToBeDisabled();
|
||||||
el.disabled = true;
|
el.disabled = true;
|
||||||
el.retractRequestToBeDisabled();
|
el.retractRequestToBeDisabled();
|
||||||
|
|
|
||||||
|
|
@ -4,23 +4,23 @@ import { LitElement } from '../index.js';
|
||||||
import { DisabledWithTabIndexMixin } from '../src/DisabledWithTabIndexMixin.js';
|
import { DisabledWithTabIndexMixin } from '../src/DisabledWithTabIndexMixin.js';
|
||||||
|
|
||||||
describe('DisabledWithTabIndexMixin', () => {
|
describe('DisabledWithTabIndexMixin', () => {
|
||||||
|
class WithTabIndex extends DisabledWithTabIndexMixin(LitElement) {}
|
||||||
before(() => {
|
before(() => {
|
||||||
class WithTabIndex extends DisabledWithTabIndexMixin(LitElement) {}
|
|
||||||
customElements.define('can-be-disabled-with-tab-index', WithTabIndex);
|
customElements.define('can-be-disabled-with-tab-index', WithTabIndex);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has an initial tabIndex of 0', async () => {
|
it('has an initial tabIndex of 0', async () => {
|
||||||
const el = await fixture(html`
|
const el = /** @type {WithTabIndex} */ (await fixture(html`
|
||||||
<can-be-disabled-with-tab-index></can-be-disabled-with-tab-index>
|
<can-be-disabled-with-tab-index></can-be-disabled-with-tab-index>
|
||||||
`);
|
`));
|
||||||
expect(el.tabIndex).to.equal(0);
|
expect(el.tabIndex).to.equal(0);
|
||||||
expect(el.getAttribute('tabindex')).to.equal('0');
|
expect(el.getAttribute('tabindex')).to.equal('0');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sets tabIndex to -1 if disabled', async () => {
|
it('sets tabIndex to -1 if disabled', async () => {
|
||||||
const el = await fixture(html`
|
const el = /** @type {WithTabIndex} */ (await fixture(html`
|
||||||
<can-be-disabled-with-tab-index></can-be-disabled-with-tab-index>
|
<can-be-disabled-with-tab-index></can-be-disabled-with-tab-index>
|
||||||
`);
|
`));
|
||||||
el.disabled = true;
|
el.disabled = true;
|
||||||
expect(el.tabIndex).to.equal(-1);
|
expect(el.tabIndex).to.equal(-1);
|
||||||
await el.updateComplete;
|
await el.updateComplete;
|
||||||
|
|
@ -28,9 +28,9 @@ describe('DisabledWithTabIndexMixin', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('disabled does not override user provided tabindex', async () => {
|
it('disabled does not override user provided tabindex', async () => {
|
||||||
const el = await fixture(html`
|
const el = /** @type {WithTabIndex} */ (await fixture(html`
|
||||||
<can-be-disabled-with-tab-index tabindex="5" disabled></can-be-disabled-with-tab-index>
|
<can-be-disabled-with-tab-index tabindex="5" disabled></can-be-disabled-with-tab-index>
|
||||||
`);
|
`));
|
||||||
expect(el.getAttribute('tabindex')).to.equal('-1');
|
expect(el.getAttribute('tabindex')).to.equal('-1');
|
||||||
el.disabled = false;
|
el.disabled = false;
|
||||||
await el.updateComplete;
|
await el.updateComplete;
|
||||||
|
|
@ -38,9 +38,9 @@ describe('DisabledWithTabIndexMixin', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can be disabled imperatively', async () => {
|
it('can be disabled imperatively', async () => {
|
||||||
const el = await fixture(html`
|
const el = /** @type {WithTabIndex} */ (await fixture(html`
|
||||||
<can-be-disabled-with-tab-index disabled></can-be-disabled-with-tab-index>
|
<can-be-disabled-with-tab-index disabled></can-be-disabled-with-tab-index>
|
||||||
`);
|
`));
|
||||||
expect(el.getAttribute('tabindex')).to.equal('-1');
|
expect(el.getAttribute('tabindex')).to.equal('-1');
|
||||||
|
|
||||||
el.disabled = false;
|
el.disabled = false;
|
||||||
|
|
@ -55,9 +55,9 @@ describe('DisabledWithTabIndexMixin', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('will not allow to change tabIndex after makeRequestToBeDisabled()', async () => {
|
it('will not allow to change tabIndex after makeRequestToBeDisabled()', async () => {
|
||||||
const el = await fixture(html`
|
const el = /** @type {WithTabIndex} */ (await fixture(html`
|
||||||
<can-be-disabled-with-tab-index></can-be-disabled-with-tab-index>
|
<can-be-disabled-with-tab-index></can-be-disabled-with-tab-index>
|
||||||
`);
|
`));
|
||||||
el.makeRequestToBeDisabled();
|
el.makeRequestToBeDisabled();
|
||||||
|
|
||||||
el.tabIndex = 5;
|
el.tabIndex = 5;
|
||||||
|
|
@ -67,9 +67,9 @@ describe('DisabledWithTabIndexMixin', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('will restore last tabIndex after retractRequestToBeDisabled()', async () => {
|
it('will restore last tabIndex after retractRequestToBeDisabled()', async () => {
|
||||||
const el = await fixture(html`
|
const el = /** @type {WithTabIndex} */ (await fixture(html`
|
||||||
<can-be-disabled-with-tab-index tabindex="5"></can-be-disabled-with-tab-index>
|
<can-be-disabled-with-tab-index tabindex="5"></can-be-disabled-with-tab-index>
|
||||||
`);
|
`));
|
||||||
el.makeRequestToBeDisabled();
|
el.makeRequestToBeDisabled();
|
||||||
expect(el.tabIndex).to.equal(-1);
|
expect(el.tabIndex).to.equal(-1);
|
||||||
await el.updateComplete;
|
await el.updateComplete;
|
||||||
|
|
@ -96,9 +96,9 @@ describe('DisabledWithTabIndexMixin', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('may allow multiple calls to retractRequestToBeDisabled', async () => {
|
it('may allow multiple calls to retractRequestToBeDisabled', async () => {
|
||||||
const el = await fixture(html`
|
const el = /** @type {WithTabIndex} */ (await fixture(html`
|
||||||
<can-be-disabled-with-tab-index disabled></can-be-disabled-with-tab-index>
|
<can-be-disabled-with-tab-index disabled></can-be-disabled-with-tab-index>
|
||||||
`);
|
`));
|
||||||
el.retractRequestToBeDisabled();
|
el.retractRequestToBeDisabled();
|
||||||
el.retractRequestToBeDisabled();
|
el.retractRequestToBeDisabled();
|
||||||
expect(el.disabled).to.be.true;
|
expect(el.disabled).to.be.true;
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,11 @@ describe('LionSingleton', () => {
|
||||||
|
|
||||||
it('supports parameters for .getInstance(foo, bar)', async () => {
|
it('supports parameters for .getInstance(foo, bar)', async () => {
|
||||||
class MySingleton extends LionSingleton {
|
class MySingleton extends LionSingleton {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} foo
|
||||||
|
* @param {string} bar
|
||||||
|
*/
|
||||||
constructor(foo, bar) {
|
constructor(foo, bar) {
|
||||||
super();
|
super();
|
||||||
this.foo = foo;
|
this.foo = foo;
|
||||||
|
|
@ -59,6 +64,7 @@ describe('LionSingleton', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can at any time add mixins via .addInstanceMixin()', () => {
|
it('can at any time add mixins via .addInstanceMixin()', () => {
|
||||||
|
// @ts-ignore because we're getting rid of LionSingleton altogether
|
||||||
const MyMixin = superclass =>
|
const MyMixin = superclass =>
|
||||||
class extends superclass {
|
class extends superclass {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
@ -72,6 +78,7 @@ describe('LionSingleton', () => {
|
||||||
const mySingleton = MySingleton.getInstance();
|
const mySingleton = MySingleton.getInstance();
|
||||||
expect(mySingleton.myMixin).to.be.true;
|
expect(mySingleton.myMixin).to.be.true;
|
||||||
|
|
||||||
|
// @ts-ignore because we're getting rid of LionSingleton altogether
|
||||||
const OtherMixin = superclass =>
|
const OtherMixin = superclass =>
|
||||||
class extends superclass {
|
class extends superclass {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
@ -89,6 +96,7 @@ describe('LionSingleton', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can provide new instances (with applied Mixins) via .getNewInstance()', async () => {
|
it('can provide new instances (with applied Mixins) via .getNewInstance()', async () => {
|
||||||
|
// @ts-ignore because we're getting rid of LionSingleton altogether
|
||||||
const MyMixin = superclass =>
|
const MyMixin = superclass =>
|
||||||
class extends superclass {
|
class extends superclass {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
@ -100,13 +108,19 @@ describe('LionSingleton', () => {
|
||||||
|
|
||||||
MySingleton.addInstanceMixin(MyMixin);
|
MySingleton.addInstanceMixin(MyMixin);
|
||||||
const singletonOne = MySingleton.getNewInstance();
|
const singletonOne = MySingleton.getNewInstance();
|
||||||
|
// @ts-ignore because we're getting rid of LionSingleton altogether
|
||||||
singletonOne.one = true;
|
singletonOne.one = true;
|
||||||
|
// @ts-ignore because we're getting rid of LionSingleton altogether
|
||||||
expect(singletonOne.myMixin).to.be.true;
|
expect(singletonOne.myMixin).to.be.true;
|
||||||
|
// @ts-ignore because we're getting rid of LionSingleton altogether
|
||||||
expect(singletonOne.one).to.be.true;
|
expect(singletonOne.one).to.be.true;
|
||||||
|
|
||||||
const singletonTwo = MySingleton.getNewInstance();
|
const singletonTwo = MySingleton.getNewInstance();
|
||||||
|
// @ts-ignore because we're getting rid of LionSingleton altogether
|
||||||
expect(singletonTwo.myMixin).to.be.true;
|
expect(singletonTwo.myMixin).to.be.true;
|
||||||
|
// @ts-ignore because we're getting rid of LionSingleton altogether
|
||||||
expect(singletonTwo.one).to.be.undefined;
|
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
|
expect(singletonOne.one).to.be.true; // to be sure
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@ describe('SlotMixin', () => {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const element = await fixture(`<${tag}></${tag}>`);
|
const el = await fixture(`<${tag}></${tag}>`);
|
||||||
expect(element.children[0].slot).to.equal('feedback');
|
expect(el.children[0].slot).to.equal('feedback');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not override user provided slots', async () => {
|
it('does not override user provided slots', async () => {
|
||||||
|
|
@ -31,7 +31,7 @@ describe('SlotMixin', () => {
|
||||||
);
|
);
|
||||||
const el = await fixture(`<${tag}><p slot="feedback">user-content</p></${tag}>`);
|
const el = await fixture(`<${tag}><p slot="feedback">user-content</p></${tag}>`);
|
||||||
expect(el.children[0].tagName).to.equal('P');
|
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 () => {
|
it('supports complex dom trees as element', async () => {
|
||||||
|
|
@ -57,10 +57,12 @@ describe('SlotMixin', () => {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const element = await fixture(`<${tag}></${tag}>`);
|
const el = await fixture(`<${tag}></${tag}>`);
|
||||||
expect(element.children[0].slot).to.equal('feedback');
|
expect(el.children[0].slot).to.equal('feedback');
|
||||||
expect(element.children[0].getAttribute('foo')).to.equal('bar');
|
expect(el.children[0].getAttribute('foo')).to.equal('bar');
|
||||||
expect(element.children[0].children[0].innerText).to.equal('cat');
|
expect(/** @type HTMLParagraphElement */ (el.children[0].children[0]).innerText).to.equal(
|
||||||
|
'cat',
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('supports conditional slots', async () => {
|
it('supports conditional slots', async () => {
|
||||||
|
|
@ -82,35 +84,37 @@ describe('SlotMixin', () => {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const elementSlot = await fixture(`<${tag}><${tag}>`);
|
const elSlot = await fixture(`<${tag}><${tag}>`);
|
||||||
expect(elementSlot.querySelector('#someSlot')).to.exist;
|
expect(elSlot.querySelector('#someSlot')).to.exist;
|
||||||
renderSlot = false;
|
renderSlot = false;
|
||||||
const elementNoSlot = await fixture(`<${tag}><${tag}>`);
|
const elNoSlot = await fixture(`<${tag}><${tag}>`);
|
||||||
expect(elementNoSlot.querySelector('#someSlot')).to.not.exist;
|
expect(elNoSlot.querySelector('#someSlot')).to.not.exist;
|
||||||
});
|
});
|
||||||
|
|
||||||
it("allows to check which slots have been created via this._isPrivateSlot('slotname')", async () => {
|
it("allows to check which slots have been created via this._isPrivateSlot('slotname')", async () => {
|
||||||
let renderSlot = true;
|
let renderSlot = true;
|
||||||
const tag = defineCE(
|
class SlotPrivateText extends SlotMixin(HTMLElement) {
|
||||||
class extends SlotMixin(HTMLElement) {
|
get slots() {
|
||||||
get slots() {
|
return {
|
||||||
return {
|
...super.slots,
|
||||||
...super.slots,
|
conditional: () => (renderSlot ? document.createElement('div') : undefined),
|
||||||
conditional: () => (renderSlot ? document.createElement('div') : undefined),
|
};
|
||||||
};
|
}
|
||||||
}
|
|
||||||
|
|
||||||
didCreateConditionalSlot() {
|
didCreateConditionalSlot() {
|
||||||
return this._isPrivateSlot('conditional');
|
return this._isPrivateSlot('conditional');
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
);
|
|
||||||
const el = await fixture(`<${tag}><${tag}>`);
|
const tag = defineCE(SlotPrivateText);
|
||||||
|
const el = /** @type {SlotPrivateText} */ (await fixture(`<${tag}><${tag}>`));
|
||||||
expect(el.didCreateConditionalSlot()).to.be.true;
|
expect(el.didCreateConditionalSlot()).to.be.true;
|
||||||
const elUserSlot = await fixture(`<${tag}><p slot="conditional">foo</p><${tag}>`);
|
const elUserSlot = /** @type {SlotPrivateText} */ (await fixture(
|
||||||
|
`<${tag}><p slot="conditional">foo</p><${tag}>`,
|
||||||
|
));
|
||||||
expect(elUserSlot.didCreateConditionalSlot()).to.be.false;
|
expect(elUserSlot.didCreateConditionalSlot()).to.be.false;
|
||||||
renderSlot = false;
|
renderSlot = false;
|
||||||
const elNoSlot = await fixture(`<${tag}><${tag}>`);
|
const elNoSlot = /** @type {SlotPrivateText} */ (await fixture(`<${tag}><${tag}>`));
|
||||||
expect(elNoSlot.didCreateConditionalSlot()).to.be.false;
|
expect(elNoSlot.didCreateConditionalSlot()).to.be.false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -4,82 +4,75 @@ import { UpdateStylesMixin } from '../src/UpdateStylesMixin.js';
|
||||||
|
|
||||||
describe('UpdateStylesMixin', () => {
|
describe('UpdateStylesMixin', () => {
|
||||||
it('handles css variables && direct e.g. host css properties correctly', async () => {
|
it('handles css variables && direct e.g. host css properties correctly', async () => {
|
||||||
const tag = defineCE(
|
class UpdateStylesElement extends UpdateStylesMixin(LitElement) {
|
||||||
class extends UpdateStylesMixin(LitElement) {
|
static get styles() {
|
||||||
static get styles() {
|
return [
|
||||||
return [
|
css`
|
||||||
css`
|
:host {
|
||||||
:host {
|
text-align: right;
|
||||||
text-align: right;
|
|
||||||
|
|
||||||
--color: rgb(128, 128, 128);
|
--color: rgb(128, 128, 128);
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
color: var(--color);
|
color: var(--color);
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`<h1 id="header">hey</h1>`;
|
return html`<h1 id="header">hey</h1>`;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
);
|
|
||||||
const el = await fixture(`<${tag}></${tag}>`);
|
const tag = defineCE(UpdateStylesElement);
|
||||||
expect(window.getComputedStyle(el.shadowRoot.getElementById('header')).color).to.equal(
|
const el = /** @type {UpdateStylesElement} */ (await fixture(`<${tag}></${tag}>`));
|
||||||
'rgb(128, 128, 128)',
|
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');
|
expect(window.getComputedStyle(el).textAlign).to.equal('right');
|
||||||
el.updateStyles({
|
el.updateStyles({
|
||||||
'--color': 'rgb(255, 0, 0)',
|
'--color': 'rgb(255, 0, 0)',
|
||||||
'text-align': 'center',
|
'text-align': 'center',
|
||||||
});
|
});
|
||||||
|
|
||||||
await tag.updateComplete;
|
await el.updateComplete;
|
||||||
expect(window.getComputedStyle(el.shadowRoot.getElementById('header')).color).to.equal(
|
expect(window.getComputedStyle(header).color).to.equal('rgb(255, 0, 0)');
|
||||||
'rgb(255, 0, 0)',
|
|
||||||
);
|
|
||||||
expect(window.getComputedStyle(el).textAlign).to.equal('center');
|
expect(window.getComputedStyle(el).textAlign).to.equal('center');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('preserves existing styles', async () => {
|
it('preserves existing styles', async () => {
|
||||||
const tag = defineCE(
|
class UpdateStylesElement extends UpdateStylesMixin(LitElement) {
|
||||||
class extends UpdateStylesMixin(LitElement) {
|
static get styles() {
|
||||||
static get styles() {
|
return [
|
||||||
return [
|
css`
|
||||||
css`
|
:host {
|
||||||
:host {
|
--color: rgb(128, 128, 128);
|
||||||
--color: rgb(128, 128, 128);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
color: var(--color);
|
color: var(--color);
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`<h1 id="header">hey</h1>`;
|
return html`<h1 id="header">hey</h1>`;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
);
|
const tag = defineCE(UpdateStylesElement);
|
||||||
const el = await fixture(`<${tag}></${tag}>`);
|
const el = /** @type {UpdateStylesElement} */ (await fixture(`<${tag}></${tag}>`));
|
||||||
expect(window.getComputedStyle(el.shadowRoot.getElementById('header')).color).to.equal(
|
const header = /** @type {Element} */ (el.shadowRoot?.getElementById('header'));
|
||||||
'rgb(128, 128, 128)',
|
|
||||||
);
|
expect(window.getComputedStyle(header).color).to.equal('rgb(128, 128, 128)');
|
||||||
el.updateStyles({ '--color': 'rgb(255, 0, 0)' });
|
el.updateStyles({ '--color': 'rgb(255, 0, 0)' });
|
||||||
|
|
||||||
await tag.updateComplete;
|
expect(window.getComputedStyle(header).color).to.equal('rgb(255, 0, 0)');
|
||||||
expect(window.getComputedStyle(el.shadowRoot.getElementById('header')).color).to.equal(
|
|
||||||
'rgb(255, 0, 0)',
|
|
||||||
);
|
|
||||||
el.updateStyles({ 'text-align': 'left' });
|
el.updateStyles({ 'text-align': 'left' });
|
||||||
|
|
||||||
await tag.updateComplete;
|
const styles = window.getComputedStyle(header);
|
||||||
const styles = window.getComputedStyle(el.shadowRoot.getElementById('header'));
|
|
||||||
expect(styles.color).to.equal('rgb(255, 0, 0)');
|
expect(styles.color).to.equal('rgb(255, 0, 0)');
|
||||||
expect(styles.textAlign).to.equal('left');
|
expect(styles.textAlign).to.equal('left');
|
||||||
});
|
});
|
||||||
|
|
|
||||||
51
packages/core/types/DelegateMixinTypes.d.ts
vendored
Normal file
51
packages/core/types/DelegateMixinTypes.d.ts
vendored
Normal file
|
|
@ -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`
|
||||||
|
* <button id="button1">with delegation</button>
|
||||||
|
* `;
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
declare function DelegateMixinImplementation<T extends Constructor<HTMLElement>>(
|
||||||
|
superclass: T,
|
||||||
|
): T & Constructor<DelegateMixinHost>;
|
||||||
|
|
||||||
|
export type DelegateMixin = typeof DelegateMixinImplementation;
|
||||||
29
packages/core/types/DisabledMixinTypes.d.ts
vendored
Normal file
29
packages/core/types/DisabledMixinTypes.d.ts
vendored
Normal file
|
|
@ -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<T extends Constructor<HTMLElement>>(
|
||||||
|
superclass: T,
|
||||||
|
): T & Constructor<DisabledMixinHost>;
|
||||||
|
|
||||||
|
export type DisabledMixin = typeof DisabledMixinImplementation;
|
||||||
29
packages/core/types/DisabledWithTabIndexMixinTypes.d.ts
vendored
Normal file
29
packages/core/types/DisabledWithTabIndexMixinTypes.d.ts
vendored
Normal file
|
|
@ -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<T extends Constructor<HTMLElement>>(
|
||||||
|
superclass: T,
|
||||||
|
): T & Constructor<DisabledWithTabIndexMixinHost> & Constructor<DisabledMixinHost>;
|
||||||
|
|
||||||
|
export type DisabledWithTabIndexMixin = typeof DisabledWithTabIndexMixinImplementation;
|
||||||
53
packages/core/types/SlotMixinTypes.d.ts
vendored
Normal file
53
packages/core/types/SlotMixinTypes.d.ts
vendored
Normal file
|
|
@ -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 <div slot="foo"></div> to the Light DOM of this element
|
||||||
|
* foo: () => document.createElement('div'),
|
||||||
|
* };
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
export declare function SlotMixinImplementation<T extends Constructor<HTMLElement>>(
|
||||||
|
superclass: T,
|
||||||
|
): T & Constructor<SlotMixinHost>;
|
||||||
|
|
||||||
|
export type SlotMixin = typeof SlotMixinImplementation;
|
||||||
34
packages/core/types/UpdateStylesMixinTypes.d.ts
vendored
Normal file
34
packages/core/types/UpdateStylesMixinTypes.d.ts
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { Constructor } from '@open-wc/dedupe-mixin';
|
||||||
|
|
||||||
|
export type StylesMap = {
|
||||||
|
[key: string]: string;
|
||||||
|
};
|
||||||
|
export declare class UpdateStylesMixinHost {
|
||||||
|
/**
|
||||||
|
* @example
|
||||||
|
* <my-element>
|
||||||
|
* <style>
|
||||||
|
* :host {
|
||||||
|
* color: var(--foo);
|
||||||
|
* }
|
||||||
|
* </style>
|
||||||
|
* </my-element>
|
||||||
|
*
|
||||||
|
* $0.updateStyles({'background': 'orange', '--foo': '#fff'})
|
||||||
|
* Chrome, Firefox: <my-element style="background: orange; --foo: bar;">
|
||||||
|
* IE: <my-element>
|
||||||
|
* => to head: <style>color: #fff</style>
|
||||||
|
*
|
||||||
|
* @param {StylesMap} updateStyles
|
||||||
|
*/
|
||||||
|
public updateStyles(updateStyles: StylesMap): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* # UpdateStylesMixin
|
||||||
|
*/
|
||||||
|
declare function UpdateStylesMixinImplementation<T extends Constructor<HTMLElement>>(
|
||||||
|
superclass: T,
|
||||||
|
): T & Constructor<UpdateStylesMixinHost>;
|
||||||
|
|
||||||
|
export type UpdateStylesMixin = typeof UpdateStylesMixinImplementation;
|
||||||
|
|
@ -13,10 +13,14 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
"*.d.ts",
|
||||||
"*.js",
|
"*.js",
|
||||||
"docs",
|
"docs",
|
||||||
"src",
|
"src",
|
||||||
"test"
|
"test",
|
||||||
|
"test-helpers",
|
||||||
|
"translations",
|
||||||
|
"types"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,14 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
"*.d.ts",
|
||||||
"*.js",
|
"*.js",
|
||||||
"docs",
|
"docs",
|
||||||
"src",
|
"src",
|
||||||
"test",
|
"test",
|
||||||
"translations"
|
"test-helpers",
|
||||||
|
"translations",
|
||||||
|
"types"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
||||||
|
|
|
||||||
|
|
@ -13,13 +13,14 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
"*.d.ts",
|
||||||
"*.js",
|
"*.js",
|
||||||
"docs",
|
"docs",
|
||||||
"src",
|
"src",
|
||||||
"test",
|
"test",
|
||||||
"test-helpers",
|
"test-helpers",
|
||||||
"test-suites",
|
"translations",
|
||||||
"translations"
|
"types"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
||||||
|
|
|
||||||
|
|
@ -13,12 +13,14 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
"*.d.ts",
|
||||||
"*.js",
|
"*.js",
|
||||||
"dev-assets",
|
|
||||||
"docs",
|
"docs",
|
||||||
"src",
|
"src",
|
||||||
"test",
|
"test",
|
||||||
"translations"
|
"test-helpers",
|
||||||
|
"translations",
|
||||||
|
"types"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublishOnly": "../../scripts/npm-prepublish.js"
|
"prepublishOnly": "../../scripts/npm-prepublish.js"
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,14 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
"*.d.ts",
|
||||||
"*.js",
|
"*.js",
|
||||||
"docs",
|
"docs",
|
||||||
"src",
|
"src",
|
||||||
"test",
|
"test",
|
||||||
"translations"
|
"test-helpers",
|
||||||
|
"translations",
|
||||||
|
"types"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,14 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
"*.d.ts",
|
||||||
"*.js",
|
"*.js",
|
||||||
"docs",
|
"docs",
|
||||||
"src",
|
"src",
|
||||||
"test",
|
"test",
|
||||||
"translations"
|
"test-helpers",
|
||||||
|
"translations",
|
||||||
|
"types"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,14 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
"*.d.ts",
|
||||||
"*.js",
|
"*.js",
|
||||||
"docs",
|
"docs",
|
||||||
"src",
|
"src",
|
||||||
"test",
|
"test",
|
||||||
"translations"
|
"test-helpers",
|
||||||
|
"translations",
|
||||||
|
"types"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,14 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
"*.d.ts",
|
||||||
"*.js",
|
"*.js",
|
||||||
"docs",
|
"docs",
|
||||||
"src",
|
"src",
|
||||||
"test",
|
"test",
|
||||||
"translations"
|
"test-helpers",
|
||||||
|
"translations",
|
||||||
|
"types"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
||||||
|
|
|
||||||
|
|
@ -13,12 +13,14 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
"*.d.ts",
|
||||||
"*.js",
|
"*.js",
|
||||||
"docs",
|
"docs",
|
||||||
"src",
|
"src",
|
||||||
"test",
|
"test",
|
||||||
"test-helpers",
|
"test-helpers",
|
||||||
"translations"
|
"translations",
|
||||||
|
"types"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,14 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
"*.d.ts",
|
||||||
"*.js",
|
"*.js",
|
||||||
"docs",
|
"docs",
|
||||||
"src",
|
"src",
|
||||||
"test",
|
"test",
|
||||||
"translations"
|
"test-helpers",
|
||||||
|
"translations",
|
||||||
|
"types"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,14 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
"*.d.ts",
|
||||||
"*.js",
|
"*.js",
|
||||||
"docs",
|
"docs",
|
||||||
"src",
|
"src",
|
||||||
"test",
|
"test",
|
||||||
"translations"
|
"test-helpers",
|
||||||
|
"translations",
|
||||||
|
"types"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,14 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
"*.d.ts",
|
||||||
"*.js",
|
"*.js",
|
||||||
|
"docs",
|
||||||
"src",
|
"src",
|
||||||
"test"
|
"test",
|
||||||
|
"test-helpers",
|
||||||
|
"translations",
|
||||||
|
"types"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,14 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
"*.d.ts",
|
||||||
"*.js",
|
"*.js",
|
||||||
"docs",
|
"docs",
|
||||||
"src",
|
"src",
|
||||||
"test",
|
"test",
|
||||||
"translations"
|
"test-helpers",
|
||||||
|
"translations",
|
||||||
|
"types"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
||||||
|
|
|
||||||
|
|
@ -13,12 +13,14 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
"*.d.ts",
|
||||||
"*.js",
|
"*.js",
|
||||||
"docs",
|
"docs",
|
||||||
"src",
|
"src",
|
||||||
"test",
|
"test",
|
||||||
"test-helpers",
|
"test-helpers",
|
||||||
"translations"
|
"translations",
|
||||||
|
"types"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
||||||
|
|
|
||||||
|
|
@ -13,13 +13,14 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
"*.d.ts",
|
||||||
"*.js",
|
"*.js",
|
||||||
"docs",
|
"docs",
|
||||||
"src",
|
"src",
|
||||||
"test",
|
"test",
|
||||||
"test-helpers",
|
"test-helpers",
|
||||||
"test-suites",
|
"translations",
|
||||||
"translations"
|
"types"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@
|
||||||
"parse5": "^5.1.1",
|
"parse5": "^5.1.1",
|
||||||
"read-package-tree": "5.3.1",
|
"read-package-tree": "5.3.1",
|
||||||
"semver": "^7.1.3",
|
"semver": "^7.1.3",
|
||||||
"typescript": "^3.6.4"
|
"typescript": "^3.8.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"mermaid": "^8.2.6",
|
"mermaid": "^8.2.6",
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,14 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
"*.d.ts",
|
||||||
"*.js",
|
"*.js",
|
||||||
"docs",
|
"docs",
|
||||||
"src",
|
"src",
|
||||||
"test",
|
"test",
|
||||||
"translations"
|
"test-helpers",
|
||||||
|
"translations",
|
||||||
|
"types"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,14 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
"*.d.ts",
|
||||||
"*.js",
|
"*.js",
|
||||||
"docs",
|
"docs",
|
||||||
"src",
|
"src",
|
||||||
"test",
|
"test",
|
||||||
"translations"
|
"test-helpers",
|
||||||
|
"translations",
|
||||||
|
"types"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,14 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
"*.d.ts",
|
||||||
"*.js",
|
"*.js",
|
||||||
"docs",
|
"docs",
|
||||||
"src",
|
"src",
|
||||||
"test",
|
"test",
|
||||||
"translations"
|
"test-helpers",
|
||||||
|
"translations",
|
||||||
|
"types"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,14 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
"*.d.ts",
|
||||||
"*.js",
|
"*.js",
|
||||||
"docs",
|
"docs",
|
||||||
"src",
|
"src",
|
||||||
"test",
|
"test",
|
||||||
"test-helpers"
|
"test-helpers",
|
||||||
|
"translations",
|
||||||
|
"types"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,14 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
"*.d.ts",
|
||||||
"*.js",
|
"*.js",
|
||||||
"docs",
|
"docs",
|
||||||
"src",
|
"src",
|
||||||
"test",
|
"test",
|
||||||
"translations"
|
"test-helpers",
|
||||||
|
"translations",
|
||||||
|
"types"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,14 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
"*.d.ts",
|
||||||
"*.js",
|
"*.js",
|
||||||
"docs",
|
"docs",
|
||||||
"src",
|
"src",
|
||||||
"test",
|
"test",
|
||||||
"translations"
|
"test-helpers",
|
||||||
|
"translations",
|
||||||
|
"types"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,14 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
"*.d.ts",
|
||||||
"*.js",
|
"*.js",
|
||||||
"docs",
|
"docs",
|
||||||
"src",
|
"src",
|
||||||
"test",
|
"test",
|
||||||
"translations"
|
"test-helpers",
|
||||||
|
"translations",
|
||||||
|
"types"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
||||||
|
|
|
||||||
|
|
@ -1,77 +1,114 @@
|
||||||
import { css, html, LitElement } from '@lion/core';
|
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 }) => {
|
function uuid() {
|
||||||
element.setAttribute('id', `panel-${uid}`);
|
return Math.random().toString(36).substr(2, 10);
|
||||||
element.setAttribute('role', 'tabpanel');
|
}
|
||||||
element.setAttribute('aria-labelledby', `button-${uid}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
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}`);
|
* @param {HTMLElement} el
|
||||||
element.setAttribute('role', 'tab');
|
*/
|
||||||
element.setAttribute('aria-controls', `panel-${uid}`);
|
function deselectPanel(el) {
|
||||||
element.addEventListener('click', clickHandler);
|
el.removeAttribute('selected');
|
||||||
element.addEventListener('keyup', keyupHandler);
|
}
|
||||||
element.addEventListener('keydown', keydownHandler);
|
|
||||||
};
|
|
||||||
|
|
||||||
const cleanButton = ({ element, clickHandler, keydownHandler, keyupHandler }) => {
|
/**
|
||||||
element.removeAttribute('id');
|
* @param {StoreEntry} options
|
||||||
element.removeAttribute('role');
|
*/
|
||||||
element.removeAttribute('aria-controls');
|
function setupButton({ el, uid, clickHandler, keydownHandler, keyupHandler }) {
|
||||||
element.removeEventListener('click', clickHandler);
|
el.setAttribute('id', `button-${uid}`);
|
||||||
element.removeEventListener('keyup', keyupHandler);
|
el.setAttribute('role', 'tab');
|
||||||
element.removeEventListener('keydown', keydownHandler);
|
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) {
|
if (withFocus) {
|
||||||
element.focus();
|
el.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
element.setAttribute('selected', true);
|
el.setAttribute('selected', 'true');
|
||||||
element.setAttribute('aria-selected', true);
|
el.setAttribute('aria-selected', 'true');
|
||||||
element.setAttribute('tabindex', 0);
|
el.setAttribute('tabindex', '0');
|
||||||
};
|
}
|
||||||
|
|
||||||
const deselectButton = element => {
|
/**
|
||||||
element.removeAttribute('selected');
|
* @param {HTMLElement} el
|
||||||
element.setAttribute('aria-selected', false);
|
*/
|
||||||
element.setAttribute('tabindex', -1);
|
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 'ArrowDown':
|
||||||
case 'ArrowRight':
|
case 'ArrowRight':
|
||||||
case 'ArrowUp':
|
case 'ArrowUp':
|
||||||
case 'ArrowLeft':
|
case 'ArrowLeft':
|
||||||
case 'Home':
|
case 'Home':
|
||||||
case 'End':
|
case 'End':
|
||||||
e.preventDefault();
|
_ev.preventDefault();
|
||||||
/* no default */
|
/* no default */
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
export class LionTabs extends LitElement {
|
export class LionTabs extends LitElement {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
/**
|
|
||||||
* index number of the selected tab.
|
|
||||||
*/
|
|
||||||
selectedIndex: {
|
selectedIndex: {
|
||||||
type: Number,
|
type: Number,
|
||||||
value: 0,
|
attribute: 'selected-index',
|
||||||
|
reflect: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -117,28 +154,42 @@ export class LionTabs extends LitElement {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
/**
|
||||||
|
* An index number of the selected tab
|
||||||
|
*/
|
||||||
this.selectedIndex = 0;
|
this.selectedIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
firstUpdated() {
|
/** @param {import('lit-element').PropertyValues } changedProperties */
|
||||||
super.firstUpdated();
|
firstUpdated(changedProperties) {
|
||||||
|
super.firstUpdated(changedProperties);
|
||||||
this.__setupSlots();
|
this.__setupSlots();
|
||||||
}
|
}
|
||||||
|
|
||||||
__setupSlots() {
|
__setupSlots() {
|
||||||
const tabSlot = this.shadowRoot.querySelector('slot[name=tab]');
|
if (this.shadowRoot) {
|
||||||
const handleSlotChange = () => {
|
const tabSlot = this.shadowRoot.querySelector('slot[name=tab]');
|
||||||
this.__cleanStore();
|
const handleSlotChange = () => {
|
||||||
this.__setupStore();
|
this.__cleanStore();
|
||||||
this.__updateSelected(false);
|
this.__setupStore();
|
||||||
};
|
this.__updateSelected(false);
|
||||||
tabSlot.addEventListener('slotchange', handleSlotChange);
|
};
|
||||||
|
|
||||||
|
if (tabSlot) {
|
||||||
|
tabSlot.addEventListener('slotchange', handleSlotChange);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
__setupStore() {
|
__setupStore() {
|
||||||
|
/** @type {StoreEntry[]} */
|
||||||
this.__store = [];
|
this.__store = [];
|
||||||
const buttons = this.querySelectorAll('[slot="tab"]');
|
const buttons = /** @type {HTMLElement[]} */ (Array.from(
|
||||||
const panels = this.querySelectorAll('[slot="panel"]');
|
this.querySelectorAll('[slot="tab"]'),
|
||||||
|
));
|
||||||
|
const panels = /** @type {HTMLElement[]} */ (Array.from(
|
||||||
|
this.querySelectorAll('[slot="panel"]'),
|
||||||
|
));
|
||||||
if (buttons.length !== panels.length) {
|
if (buttons.length !== panels.length) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.warn(
|
console.warn(
|
||||||
|
|
@ -149,19 +200,25 @@ export class LionTabs extends LitElement {
|
||||||
buttons.forEach((button, index) => {
|
buttons.forEach((button, index) => {
|
||||||
const uid = uuid();
|
const uid = uuid();
|
||||||
const panel = panels[index];
|
const panel = panels[index];
|
||||||
|
|
||||||
|
/** @type {StoreEntry} */
|
||||||
const entry = {
|
const entry = {
|
||||||
uid,
|
uid,
|
||||||
|
el: button,
|
||||||
button,
|
button,
|
||||||
panel,
|
panel,
|
||||||
clickHandler: this.__createButtonClickHandler(index),
|
clickHandler: this.__createButtonClickHandler(index),
|
||||||
keydownHandler: handleButtonKeydown,
|
keydownHandler: handleButtonKeydown.bind(this),
|
||||||
keyupHandler: this.__handleButtonKeyup.bind(this),
|
keyupHandler: this.__handleButtonKeyup.bind(this),
|
||||||
};
|
};
|
||||||
setupPanel({ element: entry.panel, ...entry });
|
setupPanel({ ...entry, el: entry.panel });
|
||||||
setupButton({ element: entry.button, ...entry });
|
setupButton(entry);
|
||||||
deselectPanel(entry.panel);
|
deselectPanel(entry.panel);
|
||||||
deselectButton(entry.button);
|
deselectButton(entry.button);
|
||||||
this.__store.push(entry);
|
|
||||||
|
if (this.__store) {
|
||||||
|
this.__store.push(entry);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -170,44 +227,57 @@ export class LionTabs extends LitElement {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.__store.forEach(entry => {
|
this.__store.forEach(entry => {
|
||||||
cleanButton({ element: entry.button, ...entry });
|
cleanButton(entry);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} index
|
||||||
|
* @returns {EventHandlerNonNull}
|
||||||
|
*/
|
||||||
__createButtonClickHandler(index) {
|
__createButtonClickHandler(index) {
|
||||||
return () => {
|
return () => {
|
||||||
this._setSelectedIndexWithFocus(index);
|
this._setSelectedIndexWithFocus(index);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
__handleButtonKeyup(e) {
|
/**
|
||||||
switch (e.key) {
|
* @param {Event} ev
|
||||||
case 'ArrowDown':
|
*/
|
||||||
case 'ArrowRight':
|
__handleButtonKeyup(ev) {
|
||||||
if (this.selectedIndex + 1 >= this._pairCount) {
|
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);
|
this._setSelectedIndexWithFocus(0);
|
||||||
} else {
|
break;
|
||||||
this._setSelectedIndexWithFocus(this.selectedIndex + 1);
|
case 'End':
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'ArrowUp':
|
|
||||||
case 'ArrowLeft':
|
|
||||||
if (this.selectedIndex <= 0) {
|
|
||||||
this._setSelectedIndexWithFocus(this._pairCount - 1);
|
this._setSelectedIndexWithFocus(this._pairCount - 1);
|
||||||
} else {
|
break;
|
||||||
this._setSelectedIndexWithFocus(this.selectedIndex - 1);
|
/* no default */
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case 'Home':
|
|
||||||
this._setSelectedIndexWithFocus(0);
|
|
||||||
break;
|
|
||||||
case 'End':
|
|
||||||
this._setSelectedIndexWithFocus(this._pairCount - 1);
|
|
||||||
break;
|
|
||||||
/* no default */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} value The new index
|
||||||
|
*/
|
||||||
set selectedIndex(value) {
|
set selectedIndex(value) {
|
||||||
const stale = this.__selectedIndex;
|
const stale = this.__selectedIndex;
|
||||||
this.__selectedIndex = value;
|
this.__selectedIndex = value;
|
||||||
|
|
@ -216,6 +286,9 @@ export class LionTabs extends LitElement {
|
||||||
this.requestUpdate('selectedIndex', stale);
|
this.requestUpdate('selectedIndex', stale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} value The new index for focus
|
||||||
|
*/
|
||||||
_setSelectedIndexWithFocus(value) {
|
_setSelectedIndexWithFocus(value) {
|
||||||
const stale = this.__selectedIndex;
|
const stale = this.__selectedIndex;
|
||||||
this.__selectedIndex = value;
|
this.__selectedIndex = value;
|
||||||
|
|
@ -224,24 +297,29 @@ export class LionTabs extends LitElement {
|
||||||
this.requestUpdate('selectedIndex', stale);
|
this.requestUpdate('selectedIndex', stale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
get selectedIndex() {
|
get selectedIndex() {
|
||||||
return this.__selectedIndex;
|
return this.__selectedIndex || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
get _pairCount() {
|
get _pairCount() {
|
||||||
return this.__store.length;
|
return (this.__store && this.__store.length) || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
__updateSelected(withFocus = false) {
|
__updateSelected(withFocus = false) {
|
||||||
if (!(this.__store && this.__store[this.selectedIndex])) {
|
if (
|
||||||
|
!(this.__store && typeof this.selectedIndex === 'number' && this.__store[this.selectedIndex])
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const previousButton = Array.from(this.children).find(
|
const previousButton = /** @type {HTMLElement} */ (Array.from(this.children).find(
|
||||||
child => child.slot === 'tab' && child.hasAttribute('selected'),
|
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'),
|
child => child.slot === 'panel' && child.hasAttribute('selected'),
|
||||||
);
|
));
|
||||||
if (previousButton) {
|
if (previousButton) {
|
||||||
deselectButton(previousButton);
|
deselectButton(previousButton);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
import { expect, fixture, html } from '@open-wc/testing';
|
import { expect, fixture, html } from '@open-wc/testing';
|
||||||
import sinon from 'sinon';
|
import sinon from 'sinon';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {import('../src/LionTabs.js').LionTabs} LionTabs
|
||||||
|
*/
|
||||||
import '../lion-tabs.js';
|
import '../lion-tabs.js';
|
||||||
|
|
||||||
const basicTabs = html`
|
const basicTabs = html`
|
||||||
|
|
@ -17,36 +20,34 @@ const basicTabs = html`
|
||||||
describe('<lion-tabs>', () => {
|
describe('<lion-tabs>', () => {
|
||||||
describe('Tabs', () => {
|
describe('Tabs', () => {
|
||||||
it('sets selectedIndex to 0 by default', async () => {
|
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);
|
expect(el.selectedIndex).to.equal(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can programmatically set selectedIndex', async () => {
|
it('can programmatically set selectedIndex', async () => {
|
||||||
const el = await fixture(html`
|
const el = /** @type {LionTabs} */ (await fixture(html`
|
||||||
<lion-tabs .selectedIndex=${1}>
|
<lion-tabs .selectedIndex=${1}>
|
||||||
<div slot="tab">tab 1</div>
|
<div slot="tab">tab 1</div>
|
||||||
<div slot="panel">panel 1</div>
|
<div slot="panel">panel 1</div>
|
||||||
<div slot="tab">tab 2</div>
|
<div slot="tab">tab 2</div>
|
||||||
<div slot="panel">panel 2</div>
|
<div slot="panel">panel 2</div>
|
||||||
</lion-tabs>
|
</lion-tabs>
|
||||||
`);
|
`));
|
||||||
expect(el.selectedIndex).to.equal(1);
|
expect(el.selectedIndex).to.equal(1);
|
||||||
expect(
|
let selectedTab = /** @type {Element} */ (Array.from(el.children).find(
|
||||||
Array.from(el.children).find(
|
child => child.slot === 'tab' && child.hasAttribute('selected'),
|
||||||
child => child.slot === 'tab' && child.hasAttribute('selected'),
|
));
|
||||||
).textContent,
|
expect(selectedTab.textContent).to.equal('tab 2');
|
||||||
).to.equal('tab 2');
|
|
||||||
|
|
||||||
el.selectedIndex = 0;
|
el.selectedIndex = 0;
|
||||||
expect(
|
selectedTab = /** @type {Element} */ (Array.from(el.children).find(
|
||||||
Array.from(el.children).find(
|
child => child.slot === 'tab' && child.hasAttribute('selected'),
|
||||||
child => child.slot === 'tab' && child.hasAttribute('selected'),
|
));
|
||||||
).textContent,
|
expect(selectedTab.textContent).to.equal('tab 1');
|
||||||
).to.equal('tab 1');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has [selected] on current selected tab which serves as styling hook', async () => {
|
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]');
|
const tabs = el.querySelectorAll('[slot=tab]');
|
||||||
el.selectedIndex = 0;
|
el.selectedIndex = 0;
|
||||||
expect(tabs[0]).to.have.attribute('selected');
|
expect(tabs[0]).to.have.attribute('selected');
|
||||||
|
|
@ -58,7 +59,7 @@ describe('<lion-tabs>', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sends event "selected-changed" for every selected state change', async () => {
|
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();
|
const spy = sinon.spy();
|
||||||
el.addEventListener('selected-changed', spy);
|
el.addEventListener('selected-changed', spy);
|
||||||
el.selectedIndex = 1;
|
el.selectedIndex = 1;
|
||||||
|
|
@ -75,18 +76,18 @@ describe('<lion-tabs>', () => {
|
||||||
</lion-tabs>
|
</lion-tabs>
|
||||||
`);
|
`);
|
||||||
expect(spy.callCount).to.equal(1);
|
expect(spy.callCount).to.equal(1);
|
||||||
console.warn.restore();
|
spy.restore();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Tabs ([slot=tab])', () => {
|
describe('Tabs ([slot=tab])', () => {
|
||||||
it('adds role=tab', async () => {
|
it('adds role=tab', async () => {
|
||||||
const el = await fixture(html`
|
const el = /** @type {LionTabs} */ (await fixture(html`
|
||||||
<lion-tabs>
|
<lion-tabs>
|
||||||
<button slot="tab">tab</button>
|
<button slot="tab">tab</button>
|
||||||
<div slot="panel">panel</div>
|
<div slot="panel">panel</div>
|
||||||
</lion-tabs>
|
</lion-tabs>
|
||||||
`);
|
`));
|
||||||
expect(Array.from(el.children).find(child => child.slot === 'tab')).to.have.attribute(
|
expect(Array.from(el.children).find(child => child.slot === 'tab')).to.have.attribute(
|
||||||
'role',
|
'role',
|
||||||
'tab',
|
'tab',
|
||||||
|
|
@ -101,7 +102,7 @@ describe('<lion-tabs>', () => {
|
||||||
|
|
||||||
describe('Tab Panels (slot=panel)', () => {
|
describe('Tab Panels (slot=panel)', () => {
|
||||||
it('are visible when corresponding tab is selected ', async () => {
|
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]');
|
const panels = el.querySelectorAll('[slot=panel]');
|
||||||
el.selectedIndex = 0;
|
el.selectedIndex = 0;
|
||||||
expect(panels[0]).to.be.visible;
|
expect(panels[0]).to.be.visible;
|
||||||
|
|
@ -125,14 +126,14 @@ describe('<lion-tabs>', () => {
|
||||||
*/
|
*/
|
||||||
describe('User interaction', () => {
|
describe('User interaction', () => {
|
||||||
it('selects a tab on click', async () => {
|
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]');
|
const tabs = el.querySelectorAll('[slot=tab]');
|
||||||
tabs[1].dispatchEvent(new Event('click'));
|
tabs[1].dispatchEvent(new Event('click'));
|
||||||
expect(el.selectedIndex).to.equal(1);
|
expect(el.selectedIndex).to.equal(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('selects next tab on [arrow-right] and [arrow-down]', async () => {
|
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]');
|
const tabs = el.querySelectorAll('[slot=tab]');
|
||||||
tabs[0].dispatchEvent(new KeyboardEvent('keyup', { key: 'ArrowRight' }));
|
tabs[0].dispatchEvent(new KeyboardEvent('keyup', { key: 'ArrowRight' }));
|
||||||
expect(el.selectedIndex).to.equal(1);
|
expect(el.selectedIndex).to.equal(1);
|
||||||
|
|
@ -141,7 +142,7 @@ describe('<lion-tabs>', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('selects previous tab on [arrow-left] and [arrow-up]', async () => {
|
it('selects previous tab on [arrow-left] and [arrow-up]', async () => {
|
||||||
const el = await fixture(html`
|
const el = /** @type {LionTabs} */ (await fixture(html`
|
||||||
<lion-tabs .selectedIndex=${1}>
|
<lion-tabs .selectedIndex=${1}>
|
||||||
<button slot="tab">tab 1</button>
|
<button slot="tab">tab 1</button>
|
||||||
<div slot="panel">panel 1</div>
|
<div slot="panel">panel 1</div>
|
||||||
|
|
@ -150,7 +151,7 @@ describe('<lion-tabs>', () => {
|
||||||
<button slot="tab">tab 3</button>
|
<button slot="tab">tab 3</button>
|
||||||
<div slot="panel">panel 3</div>
|
<div slot="panel">panel 3</div>
|
||||||
</lion-tabs>
|
</lion-tabs>
|
||||||
`);
|
`));
|
||||||
const tabs = el.querySelectorAll('[slot=tab]');
|
const tabs = el.querySelectorAll('[slot=tab]');
|
||||||
el.selectedIndex = 2;
|
el.selectedIndex = 2;
|
||||||
tabs[2].dispatchEvent(new KeyboardEvent('keyup', { key: 'ArrowLeft' }));
|
tabs[2].dispatchEvent(new KeyboardEvent('keyup', { key: 'ArrowLeft' }));
|
||||||
|
|
@ -160,29 +161,29 @@ describe('<lion-tabs>', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('selects first tab on [home]', async () => {
|
it('selects first tab on [home]', async () => {
|
||||||
const el = await fixture(html`
|
const el = /** @type {LionTabs} */ (await fixture(html`
|
||||||
<lion-tabs .selectedIndex=${1}>
|
<lion-tabs .selectedIndex=${1}>
|
||||||
<button slot="tab">tab 1</button>
|
<button slot="tab">tab 1</button>
|
||||||
<div slot="panel">panel 1</div>
|
<div slot="panel">panel 1</div>
|
||||||
<button slot="tab">tab 2</button>
|
<button slot="tab">tab 2</button>
|
||||||
<div slot="panel">panel 2</div>
|
<div slot="panel">panel 2</div>
|
||||||
</lion-tabs>
|
</lion-tabs>
|
||||||
`);
|
`));
|
||||||
const tabs = el.querySelectorAll('[slot=tab]');
|
const tabs = el.querySelectorAll('[slot=tab]');
|
||||||
tabs[1].dispatchEvent(new KeyboardEvent('keyup', { key: 'Home' }));
|
tabs[1].dispatchEvent(new KeyboardEvent('keyup', { key: 'Home' }));
|
||||||
expect(el.selectedIndex).to.equal(0);
|
expect(el.selectedIndex).to.equal(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('selects last tab on [end]', async () => {
|
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]');
|
const tabs = el.querySelectorAll('[slot=tab]');
|
||||||
tabs[0].dispatchEvent(new KeyboardEvent('keyup', { key: 'End' }));
|
tabs[0].dispatchEvent(new KeyboardEvent('keyup', { key: 'End' }));
|
||||||
expect(el.selectedIndex).to.equal(2);
|
expect(el.selectedIndex).to.equal(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('selects first tab on [arrow-right] if on last tab', async () => {
|
it('selects first tab on [arrow-right] if on last tab', async () => {
|
||||||
const el = await fixture(html`
|
const el = /** @type {LionTabs} */ (await fixture(html`
|
||||||
<lion-tabs selectedIndex="2">
|
<lion-tabs .selectedIndex=${2}>
|
||||||
<button slot="tab">tab 1</button>
|
<button slot="tab">tab 1</button>
|
||||||
<div slot="panel">panel 1</div>
|
<div slot="panel">panel 1</div>
|
||||||
<button slot="tab">tab 2</button>
|
<button slot="tab">tab 2</button>
|
||||||
|
|
@ -190,14 +191,14 @@ describe('<lion-tabs>', () => {
|
||||||
<button slot="tab">tab 3</button>
|
<button slot="tab">tab 3</button>
|
||||||
<div slot="panel">panel 3</div>
|
<div slot="panel">panel 3</div>
|
||||||
</lion-tabs>
|
</lion-tabs>
|
||||||
`);
|
`));
|
||||||
const tabs = el.querySelectorAll('[slot=tab]');
|
const tabs = el.querySelectorAll('[slot=tab]');
|
||||||
tabs[2].dispatchEvent(new KeyboardEvent('keyup', { key: 'ArrowRight' }));
|
tabs[2].dispatchEvent(new KeyboardEvent('keyup', { key: 'ArrowRight' }));
|
||||||
expect(el.selectedIndex).to.equal(0);
|
expect(el.selectedIndex).to.equal(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('selects last tab on [arrow-left] if on first tab', async () => {
|
it('selects last tab on [arrow-left] if on first tab', async () => {
|
||||||
const el = await fixture(html`
|
const el = /** @type {LionTabs} */ (await fixture(html`
|
||||||
<lion-tabs>
|
<lion-tabs>
|
||||||
<button slot="tab">tab 1</button>
|
<button slot="tab">tab 1</button>
|
||||||
<div slot="panel">panel 1</div>
|
<div slot="panel">panel 1</div>
|
||||||
|
|
@ -206,7 +207,7 @@ describe('<lion-tabs>', () => {
|
||||||
<button slot="tab">tab 3</button>
|
<button slot="tab">tab 3</button>
|
||||||
<div slot="panel">panel 3</div>
|
<div slot="panel">panel 3</div>
|
||||||
</lion-tabs>
|
</lion-tabs>
|
||||||
`);
|
`));
|
||||||
const tabs = el.querySelectorAll('[slot=tab]');
|
const tabs = el.querySelectorAll('[slot=tab]');
|
||||||
tabs[0].dispatchEvent(new KeyboardEvent('keyup', { key: 'ArrowLeft' }));
|
tabs[0].dispatchEvent(new KeyboardEvent('keyup', { key: 'ArrowLeft' }));
|
||||||
expect(el.selectedIndex).to.equal(2);
|
expect(el.selectedIndex).to.equal(2);
|
||||||
|
|
@ -215,7 +216,7 @@ describe('<lion-tabs>', () => {
|
||||||
|
|
||||||
describe('Content distribution', () => {
|
describe('Content distribution', () => {
|
||||||
it('should work with append children', async () => {
|
it('should work with append children', async () => {
|
||||||
const el = await fixture(basicTabs);
|
const el = /** @type {LionTabs} */ (await fixture(basicTabs));
|
||||||
const c = 2;
|
const c = 2;
|
||||||
const n = el.children.length / 2;
|
const n = el.children.length / 2;
|
||||||
for (let i = n + 1; i < n + c + 1; i += 1) {
|
for (let i = n + 1; i < n + c + 1; i += 1) {
|
||||||
|
|
@ -230,29 +231,28 @@ describe('<lion-tabs>', () => {
|
||||||
}
|
}
|
||||||
el.selectedIndex = el.children.length / 2 - 1;
|
el.selectedIndex = el.children.length / 2 - 1;
|
||||||
await el.updateComplete;
|
await el.updateComplete;
|
||||||
expect(
|
const selectedTab = Array.from(el.children).find(
|
||||||
Array.from(el.children).find(
|
child => child.slot === 'tab' && child.hasAttribute('selected'),
|
||||||
child => child.slot === 'tab' && child.hasAttribute('selected'),
|
);
|
||||||
).textContent,
|
expect(selectedTab && selectedTab.textContent).to.equal('tab 5');
|
||||||
).to.equal('tab 5');
|
|
||||||
expect(
|
const selectedPanel = Array.from(el.children).find(
|
||||||
Array.from(el.children).find(
|
child => child.slot === 'panel' && child.hasAttribute('selected'),
|
||||||
child => child.slot === 'panel' && child.hasAttribute('selected'),
|
);
|
||||||
).textContent,
|
expect(selectedPanel && selectedPanel.textContent).to.equal('panel 5');
|
||||||
).to.equal('panel 5');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Initializing without Focus', () => {
|
describe('Initializing without Focus', () => {
|
||||||
it('does not focus a tab when setting selectedIndex property', async () => {
|
it('does not focus a tab when setting selectedIndex property', async () => {
|
||||||
const el = await fixture(html`
|
const el = /** @type {LionTabs} */ (await fixture(html`
|
||||||
<lion-tabs>
|
<lion-tabs>
|
||||||
<button slot="tab">tab 1</button>
|
<button slot="tab">tab 1</button>
|
||||||
<div slot="panel">panel 1</div>
|
<div slot="panel">panel 1</div>
|
||||||
<button slot="tab">tab 2</button>
|
<button slot="tab">tab 2</button>
|
||||||
<div slot="panel">panel 2</div>
|
<div slot="panel">panel 2</div>
|
||||||
</lion-tabs>
|
</lion-tabs>
|
||||||
`);
|
`));
|
||||||
|
|
||||||
el.selectedIndex = 1;
|
el.selectedIndex = 1;
|
||||||
expect(el.querySelector('[slot="tab"]:nth-of-type(2)') === document.activeElement).to.be
|
expect(el.querySelector('[slot="tab"]:nth-of-type(2)') === document.activeElement).to.be
|
||||||
|
|
@ -260,56 +260,56 @@ describe('<lion-tabs>', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not focus a tab on firstUpdate', async () => {
|
it('does not focus a tab on firstUpdate', async () => {
|
||||||
const el = await fixture(html`
|
const el = /** @type {LionTabs} */ (await fixture(html`
|
||||||
<lion-tabs>
|
<lion-tabs>
|
||||||
<button slot="tab">tab 1</button>
|
<button slot="tab">tab 1</button>
|
||||||
<div slot="panel">panel 1</div>
|
<div slot="panel">panel 1</div>
|
||||||
<button slot="tab">tab 2</button>
|
<button slot="tab">tab 2</button>
|
||||||
<div slot="panel">panel 2</div>
|
<div slot="panel">panel 2</div>
|
||||||
</lion-tabs>
|
</lion-tabs>
|
||||||
`);
|
`));
|
||||||
const tabs = Array.from(el.children).filter(child => child.slot === 'tab');
|
const tabs = Array.from(el.children).filter(child => child.slot === 'tab');
|
||||||
expect(tabs.some(tab => tab === document.activeElement)).to.be.false;
|
expect(tabs.some(tab => tab === document.activeElement)).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('focuses on a tab when setting with _setSelectedIndexWithFocus method', async () => {
|
it('focuses on a tab when setting with _setSelectedIndexWithFocus method', async () => {
|
||||||
const el = await fixture(html`
|
const el = /** @type {LionTabs} */ (await fixture(html`
|
||||||
<lion-tabs>
|
<lion-tabs>
|
||||||
<button slot="tab">tab 1</button>
|
<button slot="tab">tab 1</button>
|
||||||
<div slot="panel">panel 1</div>
|
<div slot="panel">panel 1</div>
|
||||||
<button slot="tab">tab 2</button>
|
<button slot="tab">tab 2</button>
|
||||||
<div slot="panel">panel 2</div>
|
<div slot="panel">panel 2</div>
|
||||||
</lion-tabs>
|
</lion-tabs>
|
||||||
`);
|
`));
|
||||||
el._setSelectedIndexWithFocus(1);
|
el._setSelectedIndexWithFocus(1);
|
||||||
expect(el.querySelector('[slot="tab"]:nth-of-type(2)') === document.activeElement).to.be.true;
|
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 () => {
|
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`
|
||||||
<lion-tabs>
|
<lion-tabs>
|
||||||
<button slot="tab">tab 1</button>
|
<button slot="tab">tab 1</button>
|
||||||
<div slot="panel">panel 1</div>
|
<div slot="panel">panel 1</div>
|
||||||
<button slot="tab">tab 2</button>
|
<button slot="tab">tab 2</button>
|
||||||
<div slot="panel">panel 2</div>
|
<div slot="panel">panel 2</div>
|
||||||
</lion-tabs>
|
</lion-tabs>
|
||||||
`);
|
`));
|
||||||
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'));
|
secondTab.dispatchEvent(new MouseEvent('click'));
|
||||||
expect(secondTab === document.activeElement).to.be.true;
|
expect(secondTab === document.activeElement).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Accessibility', () => {
|
describe('Accessibility', () => {
|
||||||
it('does not make panels focusable', async () => {
|
it('does not make panels focusable', async () => {
|
||||||
const el = await fixture(html`
|
const el = /** @type {LionTabs} */ (await fixture(html`
|
||||||
<lion-tabs>
|
<lion-tabs>
|
||||||
<button slot="tab">tab 1</button>
|
<button slot="tab">tab 1</button>
|
||||||
<div slot="panel">panel 1</div>
|
<div slot="panel">panel 1</div>
|
||||||
<button slot="tab">tab 2</button>
|
<button slot="tab">tab 2</button>
|
||||||
<div slot="panel">panel 2</div>
|
<div slot="panel">panel 2</div>
|
||||||
</lion-tabs>
|
</lion-tabs>
|
||||||
`);
|
`));
|
||||||
expect(Array.from(el.children).find(child => child.slot === 'panel')).to.not.have.attribute(
|
expect(Array.from(el.children).find(child => child.slot === 'panel')).to.not.have.attribute(
|
||||||
'tabindex',
|
'tabindex',
|
||||||
);
|
);
|
||||||
|
|
@ -319,7 +319,7 @@ describe('<lion-tabs>', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('makes selected tab focusable (other tabs are unfocusable)', async () => {
|
it('makes selected tab focusable (other tabs are unfocusable)', async () => {
|
||||||
const el = await fixture(html`
|
const el = /** @type {LionTabs} */ (await fixture(html`
|
||||||
<lion-tabs>
|
<lion-tabs>
|
||||||
<button slot="tab">tab 1</button>
|
<button slot="tab">tab 1</button>
|
||||||
<div slot="panel">panel 1</div>
|
<div slot="panel">panel 1</div>
|
||||||
|
|
@ -328,7 +328,7 @@ describe('<lion-tabs>', () => {
|
||||||
<button slot="tab">tab 3</button>
|
<button slot="tab">tab 3</button>
|
||||||
<div slot="panel">panel 3</div>
|
<div slot="panel">panel 3</div>
|
||||||
</lion-tabs>
|
</lion-tabs>
|
||||||
`);
|
`));
|
||||||
const tabs = el.querySelectorAll('[slot=tab]');
|
const tabs = el.querySelectorAll('[slot=tab]');
|
||||||
expect(tabs[0]).to.have.attribute('tabindex', '0');
|
expect(tabs[0]).to.have.attribute('tabindex', '0');
|
||||||
expect(tabs[1]).to.have.attribute('tabindex', '-1');
|
expect(tabs[1]).to.have.attribute('tabindex', '-1');
|
||||||
|
|
@ -337,14 +337,14 @@ describe('<lion-tabs>', () => {
|
||||||
|
|
||||||
describe('Tabs', () => {
|
describe('Tabs', () => {
|
||||||
it('links ids of content items to tab via [aria-controls]', async () => {
|
it('links ids of content items to tab via [aria-controls]', async () => {
|
||||||
const el = await fixture(html`
|
const el = /** @type {LionTabs} */ (await fixture(html`
|
||||||
<lion-tabs>
|
<lion-tabs>
|
||||||
<button id="t1" slot="tab">tab 1</button>
|
<button id="t1" slot="tab">tab 1</button>
|
||||||
<div id="p1" slot="panel">panel 1</div>
|
<div id="p1" slot="panel">panel 1</div>
|
||||||
<button id="t2" slot="tab">tab 2</button>
|
<button id="t2" slot="tab">tab 2</button>
|
||||||
<div id="p2" slot="panel">panel 2</div>
|
<div id="p2" slot="panel">panel 2</div>
|
||||||
</lion-tabs>
|
</lion-tabs>
|
||||||
`);
|
`));
|
||||||
const tabs = el.querySelectorAll('[slot=tab]');
|
const tabs = el.querySelectorAll('[slot=tab]');
|
||||||
const panels = el.querySelectorAll('[slot=panel]');
|
const panels = el.querySelectorAll('[slot=panel]');
|
||||||
expect(tabs[0].getAttribute('aria-controls')).to.equal(panels[0].id);
|
expect(tabs[0].getAttribute('aria-controls')).to.equal(panels[0].id);
|
||||||
|
|
@ -352,7 +352,7 @@ describe('<lion-tabs>', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('adds aria-selected=“true” to selected tab', async () => {
|
it('adds aria-selected=“true” to selected tab', async () => {
|
||||||
const el = await fixture(html`
|
const el = /** @type {LionTabs} */ (await fixture(html`
|
||||||
<lion-tabs>
|
<lion-tabs>
|
||||||
<button id="t1" slot="tab">tab 1</button>
|
<button id="t1" slot="tab">tab 1</button>
|
||||||
<div id="p1" slot="panel">panel 1</div>
|
<div id="p1" slot="panel">panel 1</div>
|
||||||
|
|
@ -361,7 +361,7 @@ describe('<lion-tabs>', () => {
|
||||||
<button id="t3" slot="tab">tab 3</button>
|
<button id="t3" slot="tab">tab 3</button>
|
||||||
<div id="p3" slot="panel">panel 3</div>
|
<div id="p3" slot="panel">panel 3</div>
|
||||||
</lion-tabs>
|
</lion-tabs>
|
||||||
`);
|
`));
|
||||||
|
|
||||||
const tabs = el.querySelectorAll('[slot=tab]');
|
const tabs = el.querySelectorAll('[slot=tab]');
|
||||||
expect(tabs[0].getAttribute('aria-selected')).to.equal('true');
|
expect(tabs[0].getAttribute('aria-selected')).to.equal('true');
|
||||||
|
|
@ -372,28 +372,28 @@ describe('<lion-tabs>', () => {
|
||||||
|
|
||||||
describe('panels', () => {
|
describe('panels', () => {
|
||||||
it('adds role="tabpanel" to panels', async () => {
|
it('adds role="tabpanel" to panels', async () => {
|
||||||
const el = await fixture(html`
|
const el = /** @type {LionTabs} */ (await fixture(html`
|
||||||
<lion-tabs>
|
<lion-tabs>
|
||||||
<button slot="tab">tab 1</button>
|
<button slot="tab">tab 1</button>
|
||||||
<div slot="panel">panel 1</div>
|
<div slot="panel">panel 1</div>
|
||||||
<button slot="tab">tab 2</button>
|
<button slot="tab">tab 2</button>
|
||||||
<div slot="panel">panel 2</div>
|
<div slot="panel">panel 2</div>
|
||||||
</lion-tabs>
|
</lion-tabs>
|
||||||
`);
|
`));
|
||||||
const panels = el.querySelectorAll('[slot=panel]');
|
const panels = el.querySelectorAll('[slot=panel]');
|
||||||
expect(panels[0]).to.have.attribute('role', 'tabpanel');
|
expect(panels[0]).to.have.attribute('role', 'tabpanel');
|
||||||
expect(panels[1]).to.have.attribute('role', 'tabpanel');
|
expect(panels[1]).to.have.attribute('role', 'tabpanel');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('adds aria-labelledby referring to tab ids', async () => {
|
it('adds aria-labelledby referring to tab ids', async () => {
|
||||||
const el = await fixture(html`
|
const el = /** @type {LionTabs} */ (await fixture(html`
|
||||||
<lion-tabs>
|
<lion-tabs>
|
||||||
<button slot="tab">tab 1</button>
|
<button slot="tab">tab 1</button>
|
||||||
<div slot="panel">panel 1</div>
|
<div slot="panel">panel 1</div>
|
||||||
<button slot="tab">tab 2</button>
|
<button slot="tab">tab 2</button>
|
||||||
<div slot="panel">panel 2</div>
|
<div slot="panel">panel 2</div>
|
||||||
</lion-tabs>
|
</lion-tabs>
|
||||||
`);
|
`));
|
||||||
const panels = el.querySelectorAll('[slot=panel]');
|
const panels = el.querySelectorAll('[slot=panel]');
|
||||||
const tabs = el.querySelectorAll('[slot=tab]');
|
const tabs = el.querySelectorAll('[slot=tab]');
|
||||||
expect(panels[0]).to.have.attribute('aria-labelledby', tabs[0].id);
|
expect(panels[0]).to.have.attribute('aria-labelledby', tabs[0].id);
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,14 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
"*.d.ts",
|
||||||
"*.js",
|
"*.js",
|
||||||
"docs",
|
"docs",
|
||||||
"src",
|
"src",
|
||||||
"test",
|
"test",
|
||||||
"translations"
|
"test-helpers",
|
||||||
|
"translations",
|
||||||
|
"types"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,14 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
"*.d.ts",
|
||||||
"*.js",
|
"*.js",
|
||||||
"docs",
|
"docs",
|
||||||
"src",
|
"src",
|
||||||
"test",
|
"test",
|
||||||
"translations"
|
"test-helpers",
|
||||||
|
"translations",
|
||||||
|
"types"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,14 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
"*.d.ts",
|
||||||
"*.js",
|
"*.js",
|
||||||
"docs",
|
"docs",
|
||||||
"src",
|
"src",
|
||||||
"test",
|
"test",
|
||||||
"translations"
|
"test-helpers",
|
||||||
|
"translations",
|
||||||
|
"types"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
"prepublishOnly": "../../scripts/npm-prepublish.js",
|
||||||
|
|
|
||||||
8
tsconfig.build.types.json
Normal file
8
tsconfig.build.types.json
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"declaration": true,
|
||||||
|
"emitDeclarationOnly": true,
|
||||||
|
"noEmit": false
|
||||||
|
}
|
||||||
|
}
|
||||||
24
tsconfig.json
Normal file
24
tsconfig.json
Normal file
|
|
@ -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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
nodeResolve: true,
|
nodeResolve: true,
|
||||||
sessionStartTimeout: 30000,
|
sessionStartTimeout: 30000,
|
||||||
|
concurrency: 5,
|
||||||
coverageConfig: {
|
coverageConfig: {
|
||||||
threshold: {
|
threshold: {
|
||||||
statements: 80,
|
statements: 80,
|
||||||
|
|
|
||||||
51
yarn.lock
51
yarn.lock
|
|
@ -2413,11 +2413,16 @@
|
||||||
"@open-wc/semantic-dom-diff" "^0.13.16"
|
"@open-wc/semantic-dom-diff" "^0.13.16"
|
||||||
"@types/chai" "^4.1.7"
|
"@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"
|
version "1.2.17"
|
||||||
resolved "https://registry.yarnpkg.com/@open-wc/dedupe-mixin/-/dedupe-mixin-1.2.17.tgz#50fb903fc8785639487d7987caae45d7bba08ec7"
|
resolved "https://registry.yarnpkg.com/@open-wc/dedupe-mixin/-/dedupe-mixin-1.2.17.tgz#50fb903fc8785639487d7987caae45d7bba08ec7"
|
||||||
integrity sha512-9A3WohqNxEloJa4y1DuBL5zH12cNRNW1vsrkiaLMnOGuQdhibs2XY1oliudsKpvIeNjDXRVRPUdIIzn65BypCw==
|
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":
|
"@open-wc/demoing-storybook@^2.0.2":
|
||||||
version "2.3.11"
|
version "2.3.11"
|
||||||
resolved "https://registry.yarnpkg.com/@open-wc/demoing-storybook/-/demoing-storybook-2.3.11.tgz#4c4887760591c6e58fb25852c92f0357c3445246"
|
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-element "^2.2.1"
|
||||||
lit-html "^1.0.0"
|
lit-html "^1.0.0"
|
||||||
|
|
||||||
"@open-wc/testing@^2.5.0":
|
"@open-wc/testing@^2.5.18":
|
||||||
version "2.5.18"
|
version "2.5.18"
|
||||||
resolved "https://registry.yarnpkg.com/@open-wc/testing/-/testing-2.5.18.tgz#b05215535a22515fbd98183eface1c33fd804a5d"
|
resolved "https://registry.yarnpkg.com/@open-wc/testing/-/testing-2.5.18.tgz#b05215535a22515fbd98183eface1c33fd804a5d"
|
||||||
integrity sha512-poFIaGEsHseNEbAE/pGjzZGbSksxqsL4CRr+MSPUEotzhbVa3BzA3JzPHfn3FD1zVGlBcNEU0kFa0jj/Goc52w==
|
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"
|
resolved "https://registry.yarnpkg.com/@types/caniuse-api/-/caniuse-api-3.0.0.tgz#af31cc52062be0ab24583be072fd49b634dcc2fe"
|
||||||
integrity sha512-wT1VfnScjAftZsvLYaefu/UuwYJdYBwD2JDL2OQd01plGmuAoir5V6HnVHgrfh7zEwcasoiyO2wQ+W58sNh2sw==
|
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":
|
"@types/chai-dom@^0.0.9":
|
||||||
version "0.0.9"
|
version "0.0.9"
|
||||||
resolved "https://registry.yarnpkg.com/@types/chai-dom/-/chai-dom-0.0.9.tgz#77379354efec2568284ca355fff6a4f85f5a66f4"
|
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"
|
merge2 "^1.2.3"
|
||||||
micromatch "^3.1.10"
|
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:
|
fast-glob@^3.1.1:
|
||||||
version "3.2.2"
|
version "3.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.2.tgz#ade1a9d91148965d4bf7c51f72e1ca662d32e63d"
|
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"
|
resolved "https://registry.yarnpkg.com/try-to-catch/-/try-to-catch-1.1.1.tgz#770162dd13b9a0e55da04db5b7f888956072038a"
|
||||||
integrity sha512-ikUlS+/BcImLhNYyIgZcEmq4byc31QpC+46/6Jm5ECWkVFhf8SM2Fp/0pMVXPX6vk45SMCwrP4Taxucne8I0VA==
|
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:
|
tsconfig-paths@^3.9.0:
|
||||||
version "3.9.0"
|
version "3.9.0"
|
||||||
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b"
|
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"
|
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||||
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
|
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
|
||||||
|
|
||||||
typescript@^3.6.4:
|
typescript@^3.5.3, typescript@^3.8.3:
|
||||||
version "3.9.5"
|
version "3.9.6"
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.5.tgz#586f0dba300cde8be52dd1ac4f7e1009c1b13f36"
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.6.tgz#8f3e0198a34c3ae17091b35571d3afd31999365a"
|
||||||
integrity sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ==
|
integrity sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw==
|
||||||
|
|
||||||
typical@^4.0.0:
|
typical@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
|
|
@ -13629,6 +13658,16 @@ wcwidth@^1.0.0, wcwidth@^1.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
defaults "^1.0.3"
|
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:
|
web-namespaces@^1.0.0, web-namespaces@^1.1.2:
|
||||||
version "1.1.4"
|
version "1.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec"
|
resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue