mirror of
https://github.com/jlengrand/open-wc.git
synced 2026-03-10 08:31:19 +00:00
feat: add types + linting & improve intellisense
This commit is contained in:
3
browser.d.ts
vendored
Normal file
3
browser.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
interface ImportMeta {
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
@@ -15,6 +15,7 @@ const sidebar = [
|
|||||||
['/developing/', 'Getting started'],
|
['/developing/', 'Getting started'],
|
||||||
'/developing/owc-dev-server',
|
'/developing/owc-dev-server',
|
||||||
['/developing/create', 'Generators'],
|
['/developing/create', 'Generators'],
|
||||||
|
'/developing/types',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
118
docs/developing/types.md
Normal file
118
docs/developing/types.md
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
# Types
|
||||||
|
|
||||||
|
## Types are good
|
||||||
|
|
||||||
|
> The following information is a quote from [Type Safe JavaScript with JSDoc](https://medium.com/@trukrs/type-safe-javascript-with-jsdoc-7a2a63209b76)
|
||||||
|
|
||||||
|
Types provide valuable information about the nature of code, and help identify typos, enable refactoring, etc. Most editors these days provide some kind of IntelliSense based on type information gleaned from the code. My favorite is Visual Studio Code. With proper type information it can give you symbol defintions on hover, code completion, symbol renaming across files, etc.
|
||||||
|
|
||||||
|
### Benefits of Types
|
||||||
|
1. Early detection of type errors
|
||||||
|
2. Better code analysis
|
||||||
|
3. Improved IDE support
|
||||||
|
4. Promotes dependable refactoring
|
||||||
|
5. Improves code readability
|
||||||
|
6. Provides useful IntelliSense while coding
|
||||||
|
7. These benefits are provided by using TypeScript, Flow and even JSDoc comments.
|
||||||
|
|
||||||
|
## Open Web Components Types
|
||||||
|
|
||||||
|
For most of our products we do offer types via JSDocs.
|
||||||
|
In order to utalize them you will need to add something to your setup.
|
||||||
|
|
||||||
|
### Setup for JavaScript
|
||||||
|
|
||||||
|
In order to get type linting in a JavaScript project using VS Code all you need is to add a `jsconfig.json`.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"lib": ["es2017", "dom"],
|
||||||
|
"checkJs": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"alwaysStrict": true,
|
||||||
|
"esModuleInterop": true
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Setup for TypeScript
|
||||||
|
|
||||||
|
If you wish to use our typings in TypeScript you will to do a little more.
|
||||||
|
|
||||||
|
You will need to add to this to your `tsconfig.json`.
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowJs": true,
|
||||||
|
"checkJs": true,
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"node_modules/@open-wc/**/*.js"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules/!(@open-wc)",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
e.g. we need to include the js files from @open-wc and you can not have it in an exclude.
|
||||||
|
|
||||||
|
However as `allowJs` prevents you from generating definition files for your own typescript files ([issue 7546](https://github.com/Microsoft/TypeScript/issues/7546)) you probably want to have an alternative config `tsconfig.build.json` for that.
|
||||||
|
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowJs": false,
|
||||||
|
"checkJs": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can then use it like so in you `package.json`
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"lint:types": "tsc",
|
||||||
|
"build": "tsc -p tsconfig.build.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
That way
|
||||||
|
- `tsconfig.json` will be used by the language server (in VS code)
|
||||||
|
- `tsconfig.build.json` will be used to build your typescript project (including definition files)
|
||||||
|
|
||||||
|
|
||||||
|
Example how a full config might look like
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "esnext",
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"lib": ["es2017", "dom"],
|
||||||
|
"allowJs": true,
|
||||||
|
"checkJs": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"strict": false,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"alwaysStrict": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"node_modules/@open-wc/**/*.js"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules/!(@open-wc)",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
"lint": "run-p lint:*",
|
"lint": "run-p lint:*",
|
||||||
"lint:eslint": "eslint --ext .js,.html .",
|
"lint:eslint": "eslint --ext .js,.html .",
|
||||||
"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",
|
||||||
"format": "npm run format:eslint && npm run format:prettier",
|
"format": "npm run format:eslint && npm run format:prettier",
|
||||||
"format:eslint": "eslint --ext .js,.html . --fix",
|
"format:eslint": "eslint --ext .js,.html . --fix",
|
||||||
"format:prettier": "prettier \"**/*.js\" --write",
|
"format:prettier": "prettier \"**/*.js\" --write",
|
||||||
@@ -35,6 +36,7 @@
|
|||||||
"@commitlint/config-lerna-scopes": "7.2.1",
|
"@commitlint/config-lerna-scopes": "7.2.1",
|
||||||
"@open-wc/testing-karma": "file:./packages/testing-karma",
|
"@open-wc/testing-karma": "file:./packages/testing-karma",
|
||||||
"@open-wc/testing-karma-bs": "file:./packages/testing-karma-bs",
|
"@open-wc/testing-karma-bs": "file:./packages/testing-karma-bs",
|
||||||
|
"@types/mocha": "^5.0.0",
|
||||||
"@vuepress/plugin-google-analytics": "^1.0.0-alpha.30",
|
"@vuepress/plugin-google-analytics": "^1.0.0-alpha.30",
|
||||||
"eslint": "^5.13.0",
|
"eslint": "^5.13.0",
|
||||||
"eslint-config-prettier": "^3.3.0",
|
"eslint-config-prettier": "^3.3.0",
|
||||||
@@ -43,6 +45,7 @@
|
|||||||
"lint-staged": "^8.1.0",
|
"lint-staged": "^8.1.0",
|
||||||
"npm-run-all": "4.1.3",
|
"npm-run-all": "4.1.3",
|
||||||
"prettier": "^1.15.0",
|
"prettier": "^1.15.0",
|
||||||
|
"typescript": "^3.3.3333",
|
||||||
"vuepress": "^1.0.0-alpha.30",
|
"vuepress": "^1.0.0-alpha.30",
|
||||||
"webpack-merge": "^4.1.5"
|
"webpack-merge": "^4.1.5"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* Changes a relative URL to an absolute URL.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* toAbsoluteURL('hoge.html') // http://example.com/hoge.html
|
||||||
|
*
|
||||||
|
* @param {string} url Relative URL
|
||||||
|
* @returns {string} Absolute URL
|
||||||
|
*/
|
||||||
function toAbsoluteURL(url) {
|
function toAbsoluteURL(url) {
|
||||||
const a = document.createElement('a');
|
const a = document.createElement('a');
|
||||||
a.setAttribute('href', url); // <a href="hoge.html">
|
a.setAttribute('href', url); // <a href="hoge.html">
|
||||||
|
// @ts-ignore
|
||||||
return a.cloneNode(false).href; // -> "http://example.com/hoge.html"
|
return a.cloneNode(false).href; // -> "http://example.com/hoge.html"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
window.importModule = function importModule(url) {
|
window.importModule = function importModule(url) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const vector = `$importModule$${Math.random()
|
const vector = `$importModule$${Math.random()
|
||||||
@@ -18,7 +29,7 @@ window.importModule = function importModule(url) {
|
|||||||
URL.revokeObjectURL(script.src);
|
URL.revokeObjectURL(script.src);
|
||||||
script.src = '';
|
script.src = '';
|
||||||
};
|
};
|
||||||
script.defer = 'defer';
|
script.defer = true;
|
||||||
script.type = 'module';
|
script.type = 'module';
|
||||||
script.onerror = () => {
|
script.onerror = () => {
|
||||||
reject(new Error(`Failed to import: ${url}`));
|
reject(new Error(`Failed to import: ${url}`));
|
||||||
|
|||||||
15
packages/building-webpack/demo/ts-babel/babel-demo-app.ts
Normal file
15
packages/building-webpack/demo/ts-babel/babel-demo-app.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
async function babelMessage(nr: number): Promise<string> {
|
||||||
|
return `Hello typescript! ${nr * 2}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
class BabelDemoApp extends HTMLElement {
|
||||||
|
async connectedCallback() {
|
||||||
|
const msg = await babelMessage(2);
|
||||||
|
|
||||||
|
this.innerHTML = `
|
||||||
|
<h1>${msg}</h1>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define('babel-demo-app', BabelDemoApp);
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
async function message(nr: number): Promise<string> {
|
|
||||||
return `Hello typescript! ${nr * 2}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
class DemoApp extends HTMLElement {
|
|
||||||
async connectedCallback() {
|
|
||||||
const msg = await message(2);
|
|
||||||
|
|
||||||
this.innerHTML = `
|
|
||||||
<h1>${msg}</h1>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define('demo-app', DemoApp);
|
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
<h1>Static content in index.html is preserved</h1>
|
<h1>Static content in index.html is preserved</h1>
|
||||||
|
|
||||||
<demo-app></demo-app>
|
<babel-demo-app></babel-demo-app>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ function createConfig(options, legacy) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
plugins: [
|
plugins: [
|
||||||
|
// @ts-ignore
|
||||||
!development && new CleanWebpackPlugin(),
|
!development && new CleanWebpackPlugin(),
|
||||||
|
|
||||||
new HtmlWebpackPlugin({
|
new HtmlWebpackPlugin({
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ module.exports = userOptions => {
|
|||||||
},
|
},
|
||||||
|
|
||||||
plugins: [
|
plugins: [
|
||||||
|
// @ts-ignore
|
||||||
!development && new CleanWebpackPlugin(),
|
!development && new CleanWebpackPlugin(),
|
||||||
|
|
||||||
new HtmlWebpackPlugin({
|
new HtmlWebpackPlugin({
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// @ts-nocheck
|
||||||
/* eslint-disable no-param-reassign */
|
/* eslint-disable no-param-reassign */
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
import * as Chai from 'chai'
|
|
||||||
export function getOuterHtml(el: Element): string;
|
|
||||||
export function getCleanedShadowDom(el: Element): string;
|
|
||||||
export function chaiDomEquals(chai: Chai, utils: any): undefined;
|
|
||||||
@@ -3,8 +3,12 @@ import { getDiffableSemanticHTML } from '@open-wc/semantic-dom-diff';
|
|||||||
/**
|
/**
|
||||||
* el.outerHTML is not polyfilled so we need to recreate the tag + attributes and
|
* el.outerHTML is not polyfilled so we need to recreate the tag + attributes and
|
||||||
* combine it with el.innerHTML.
|
* combine it with el.innerHTML.
|
||||||
|
*
|
||||||
|
* @param {Element} el Element you want to get the out Html from
|
||||||
|
* @returns {String} outer html
|
||||||
*/
|
*/
|
||||||
export const getOuterHtml = el => {
|
export const getOuterHtml = el => {
|
||||||
|
// @ts-ignore
|
||||||
if (window.ShadyCSS && window.ShadyCSS.nativeShadow === false) {
|
if (window.ShadyCSS && window.ShadyCSS.nativeShadow === false) {
|
||||||
const tagName = el.tagName.toLowerCase();
|
const tagName = el.tagName.toLowerCase();
|
||||||
let attributes = ' ';
|
let attributes = ' ';
|
||||||
@@ -21,8 +25,12 @@ export const getOuterHtml = el => {
|
|||||||
/**
|
/**
|
||||||
* For comparision we do not need the style scoping classes on polyfilled browsers
|
* For comparision we do not need the style scoping classes on polyfilled browsers
|
||||||
* Rather naive approach for now - probably need to improve once we have failing cases.
|
* Rather naive approach for now - probably need to improve once we have failing cases.
|
||||||
|
*
|
||||||
|
* @param {Element} el Element you want to get the cleaned shadow dom
|
||||||
|
* @returns {String} cleaned shadow dom
|
||||||
*/
|
*/
|
||||||
export const getCleanedShadowDom = el => {
|
export const getCleanedShadowDom = el => {
|
||||||
|
// @ts-ignore
|
||||||
if (window.ShadyCSS && window.ShadyCSS.nativeShadow === false) {
|
if (window.ShadyCSS && window.ShadyCSS.nativeShadow === false) {
|
||||||
const tagName = el.tagName.toLowerCase();
|
const tagName = el.tagName.toLowerCase();
|
||||||
const regexTagName = new RegExp(tagName, 'g');
|
const regexTagName = new RegExp(tagName, 'g');
|
||||||
@@ -36,16 +44,27 @@ export const getCleanedShadowDom = el => {
|
|||||||
return el.shadowRoot.innerHTML;
|
return el.shadowRoot.innerHTML;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup the
|
||||||
|
*
|
||||||
|
* Note: can not be an arrow function as it gets rebound
|
||||||
|
*
|
||||||
|
* @param {any} chai
|
||||||
|
* @param {any} utils
|
||||||
|
*/
|
||||||
export const chaiDomEquals = (chai, utils) => {
|
export const chaiDomEquals = (chai, utils) => {
|
||||||
// can not be an arrow function as it gets rebound
|
|
||||||
chai.Assertion.addProperty('dom', function dom() {
|
chai.Assertion.addProperty('dom', function dom() {
|
||||||
|
// @ts-ignore
|
||||||
new chai.Assertion(this._obj.nodeType).to.equal(1);
|
new chai.Assertion(this._obj.nodeType).to.equal(1);
|
||||||
|
// @ts-ignore
|
||||||
utils.flag(this, 'dom', true);
|
utils.flag(this, 'dom', true);
|
||||||
});
|
});
|
||||||
|
|
||||||
// can not be an arrow function as it gets rebound
|
// can not be an arrow function as it gets rebound
|
||||||
chai.Assertion.addProperty('shadowDom', function shadowDom() {
|
chai.Assertion.addProperty('shadowDom', function shadowDom() {
|
||||||
|
// @ts-ignore
|
||||||
new chai.Assertion(this._obj.nodeType).to.equal(1);
|
new chai.Assertion(this._obj.nodeType).to.equal(1);
|
||||||
|
// @ts-ignore
|
||||||
utils.flag(this, 'shadowDom', true);
|
utils.flag(this, 'shadowDom', true);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -53,15 +72,19 @@ export const chaiDomEquals = (chai, utils) => {
|
|||||||
// TODO: this is here for backwards compatibility, removal will be
|
// TODO: this is here for backwards compatibility, removal will be
|
||||||
// a breaking change
|
// a breaking change
|
||||||
chai.Assertion.addProperty('semantically', function shadowDom() {
|
chai.Assertion.addProperty('semantically', function shadowDom() {
|
||||||
|
// @ts-ignore
|
||||||
new chai.Assertion(this._obj.nodeType).to.equal(1);
|
new chai.Assertion(this._obj.nodeType).to.equal(1);
|
||||||
|
// @ts-ignore
|
||||||
utils.flag(this, 'semantically', true);
|
utils.flag(this, 'semantically', true);
|
||||||
});
|
});
|
||||||
|
|
||||||
// can not be an arrow function as it gets rebound
|
// can not be an arrow function as it gets rebound
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
const domEquals = _super => function handleDom(value, ...args) {
|
const domEquals = _super => function handleDom(value, ...args) {
|
||||||
|
// @ts-ignore
|
||||||
if (utils.flag(this, 'dom')) {
|
if (utils.flag(this, 'dom')) {
|
||||||
const expectedHTML = getDiffableSemanticHTML(value);
|
const expectedHTML = getDiffableSemanticHTML(value);
|
||||||
|
// @ts-ignore
|
||||||
const actualHTML = getDiffableSemanticHTML(getOuterHtml(this._obj));
|
const actualHTML = getDiffableSemanticHTML(getOuterHtml(this._obj));
|
||||||
|
|
||||||
// use chai's built-in string comparison, log the updated snapshot on error
|
// use chai's built-in string comparison, log the updated snapshot on error
|
||||||
@@ -76,8 +99,10 @@ export const chaiDomEquals = (chai, utils) => {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
} else if (utils.flag(this, 'shadowDom')) {
|
} else if (utils.flag(this, 'shadowDom')) {
|
||||||
const expectedHTML = getDiffableSemanticHTML(value);
|
const expectedHTML = getDiffableSemanticHTML(value);
|
||||||
|
// @ts-ignore
|
||||||
const actualHTML = getDiffableSemanticHTML(getCleanedShadowDom(this._obj));
|
const actualHTML = getDiffableSemanticHTML(getCleanedShadowDom(this._obj));
|
||||||
|
|
||||||
// use chai's built-in string comparison, log the updated snapshot on error
|
// use chai's built-in string comparison, log the updated snapshot on error
|
||||||
@@ -93,6 +118,7 @@ export const chaiDomEquals = (chai, utils) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
// @ts-ignore
|
||||||
_super.apply(this, [value, ...args]);
|
_super.apply(this, [value, ...args]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
1
packages/chai-dom-equals/index.d.ts
vendored
1
packages/chai-dom-equals/index.d.ts
vendored
@@ -1 +0,0 @@
|
|||||||
export { getOuterHtml, getCleanedShadowDom, chaiDomEquals } from './chai-dom-equals';
|
|
||||||
@@ -21,6 +21,8 @@ class Base extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: check how to allow extending with added properties with LitElement
|
||||||
|
// @ts-ignore
|
||||||
export class MyEl extends Base {
|
export class MyEl extends Base {
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return css`
|
return css`
|
||||||
@@ -47,6 +49,7 @@ export class MyEl extends Base {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
this.locked = false;
|
||||||
this.header = 'Default Header';
|
this.header = 'Default Header';
|
||||||
this.headerColor = '#ff0000';
|
this.headerColor = '#ff0000';
|
||||||
this.disabled = false;
|
this.disabled = false;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
} from '../../index.js';
|
} from '../../index.js';
|
||||||
|
|
||||||
import { MyEl } from '../my-el.js';
|
import { MyEl } from '../my-el.js';
|
||||||
|
// @ts-ignore
|
||||||
import readme from '../README.md';
|
import readme from '../README.md';
|
||||||
|
|
||||||
addParameters({
|
addParameters({
|
||||||
|
|||||||
23
packages/demoing-storybook/index.d.ts
vendored
23
packages/demoing-storybook/index.d.ts
vendored
@@ -1,23 +0,0 @@
|
|||||||
export { html } from 'lit-html';
|
|
||||||
|
|
||||||
// NB: @types/storybook__polymer doesn't yet exist
|
|
||||||
// export { storiesOf, addParameters } from '@storybook/polymer';
|
|
||||||
export { action } from '@storybook/addon-actions';
|
|
||||||
export { linkTo } from '@storybook/addon-links';
|
|
||||||
// NB: @types/storybook__addon-backgrounds is 2 major versions behind
|
|
||||||
// export { withBackgrounds } from '@storybook/addon-backgrounds';
|
|
||||||
export { withNotes } from '@storybook/addon-notes';
|
|
||||||
|
|
||||||
export {
|
|
||||||
withKnobs,
|
|
||||||
text,
|
|
||||||
button,
|
|
||||||
number,
|
|
||||||
select,
|
|
||||||
date,
|
|
||||||
color,
|
|
||||||
array,
|
|
||||||
boolean,
|
|
||||||
} from '@storybook/addon-knobs';
|
|
||||||
|
|
||||||
export { withClassPropertiesKnobs } from './withClassPropertiesKnobs.js';
|
|
||||||
@@ -37,7 +37,6 @@
|
|||||||
"@storybook/polymer": "^5.0.0",
|
"@storybook/polymer": "^5.0.0",
|
||||||
"@types/storybook__addon-actions": "^3.4.1",
|
"@types/storybook__addon-actions": "^3.4.1",
|
||||||
"@types/storybook__addon-backgrounds": "^3.2.1",
|
"@types/storybook__addon-backgrounds": "^3.2.1",
|
||||||
"@types/storybook__addon-knobs": "^3.4.1",
|
|
||||||
"@types/storybook__addon-links": "^3.3.3",
|
"@types/storybook__addon-links": "^3.3.3",
|
||||||
"@types/storybook__addon-notes": "^3.3.3",
|
"@types/storybook__addon-notes": "^3.3.3",
|
||||||
"@webcomponents/webcomponentsjs": "^2.2.0",
|
"@webcomponents/webcomponentsjs": "^2.2.0",
|
||||||
|
|||||||
@@ -2,6 +2,12 @@ import { text, number, date, object, array, boolean } from '@storybook/addon-kno
|
|||||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||||
import { render } from 'lit-html';
|
import { render } from 'lit-html';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} OptionsWithClassPropertiesKnobs
|
||||||
|
* @property {function} [overrides] Override knobs for specific properties
|
||||||
|
* @property {import('lit-html').TemplateResult} [template] Your template if you need properties or children set
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @example
|
* @example
|
||||||
* class MyEl extends LitElement { ... }
|
* class MyEl extends LitElement { ... }
|
||||||
@@ -18,6 +24,8 @@ import { render } from 'lit-html';
|
|||||||
* { key: 'locked', group: 'Security' }, // change group of an default Element property
|
* { key: 'locked', group: 'Security' }, // change group of an default Element property
|
||||||
* ]));
|
* ]));
|
||||||
* });
|
* });
|
||||||
|
* @param {any} Klass The class (not instance) you want the knobs for
|
||||||
|
* @param {OptionsWithClassPropertiesKnobs} Options Define overrides and a template if neeed
|
||||||
*/
|
*/
|
||||||
export function withClassPropertiesKnobs(Klass, { overrides: overrideFunction, template } = {}) {
|
export function withClassPropertiesKnobs(Klass, { overrides: overrideFunction, template } = {}) {
|
||||||
let el;
|
let el;
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ if ('help' in options) {
|
|||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
return;
|
process.exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
// open if the open option is given, even when there were no arguments
|
// open if the open option is given, even when there were no arguments
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
*
|
*
|
||||||
* Adapted to not load language polyfills and use dynamic imports
|
* Adapted to not load language polyfills and use dynamic imports
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function needsTemplatePolyfill() {
|
function needsTemplatePolyfill() {
|
||||||
// no real <template> because no `content` property (IE and older browsers)
|
// no real <template> because no `content` property (IE and older browsers)
|
||||||
const template = document.createElement('template');
|
const template = document.createElement('template');
|
||||||
@@ -20,14 +19,17 @@ function needsTemplatePolyfill() {
|
|||||||
template.content.appendChild(template2);
|
template.content.appendChild(template2);
|
||||||
const clone = template.cloneNode(true);
|
const clone = template.cloneNode(true);
|
||||||
return (
|
return (
|
||||||
|
// @ts-ignore
|
||||||
clone.content.childNodes.length === 0 ||
|
clone.content.childNodes.length === 0 ||
|
||||||
|
// @ts-ignore
|
||||||
clone.content.firstChild.content.childNodes.length === 0
|
clone.content.firstChild.content.childNodes.length === 0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads web component polyfills if needed
|
* Loads web component polyfills if needed
|
||||||
* @returns {Promise<void>} resolves when polyfills are loaded
|
*
|
||||||
|
* @returns {Promise} resolves when polyfills are loaded
|
||||||
*/
|
*/
|
||||||
export default function loadPolyfills() {
|
export default function loadPolyfills() {
|
||||||
const polyfills = [];
|
const polyfills = [];
|
||||||
@@ -35,12 +37,15 @@ export default function loadPolyfills() {
|
|||||||
const needsShadowDom =
|
const needsShadowDom =
|
||||||
!('attachShadow' in Element.prototype) ||
|
!('attachShadow' in Element.prototype) ||
|
||||||
!('getRootNode' in Element.prototype) ||
|
!('getRootNode' in Element.prototype) ||
|
||||||
|
// @ts-ignore
|
||||||
(window.ShadyDOM && window.ShadyDOM.force);
|
(window.ShadyDOM && window.ShadyDOM.force);
|
||||||
|
// @ts-ignore
|
||||||
const needsCustomElements = !window.customElements || window.customElements.forcePolyfill;
|
const needsCustomElements = !window.customElements || window.customElements.forcePolyfill;
|
||||||
|
|
||||||
// URL is required by webcomponents polyfill
|
// URL is required by webcomponents polyfill
|
||||||
// We can use URLSearchParams as a watermark for URL support
|
// We can use URLSearchParams as a watermark for URL support
|
||||||
if (!('URLSearchParams' in window)) {
|
if (!('URLSearchParams' in window)) {
|
||||||
|
// @ts-ignore
|
||||||
polyfills.push(import('@bundled-es-modules/url-polyfill'));
|
polyfills.push(import('@bundled-es-modules/url-polyfill'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
1
packages/semantic-dom-diff/index.d.ts
vendored
1
packages/semantic-dom-diff/index.d.ts
vendored
@@ -1 +0,0 @@
|
|||||||
export { getDiffableSemanticHTML, getAST } from './src/get-dom-diff';
|
|
||||||
@@ -1 +1 @@
|
|||||||
export { getDiffableSemanticHTML } from './src/get-dom-diff.js';
|
export { getDiffableSemanticHTML, getAST } from './src/get-dom-diff.js';
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
import { DocumentFragment } from 'parse5';
|
|
||||||
|
|
||||||
export function getAST(value, config): DocumentFragment
|
|
||||||
export function getDiffableSemanticHTML(html: string): string
|
|
||||||
@@ -4,6 +4,13 @@ import toDiffableHtml from '@bundled-es-modules/diffable-html';
|
|||||||
import { sanitizeHtmlString } from './sanitize-html-string.js';
|
import { sanitizeHtmlString } from './sanitize-html-string.js';
|
||||||
import { normalizeAST } from './normalize-ast.js';
|
import { normalizeAST } from './normalize-ast.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an ast for a given string
|
||||||
|
*
|
||||||
|
* @param {string} value String to convert to AST
|
||||||
|
* @param {Object} config Options
|
||||||
|
* @returns {import('parse5').DefaultTreeElement} Ast
|
||||||
|
*/
|
||||||
export function getAST(value, config = {}) {
|
export function getAST(value, config = {}) {
|
||||||
const ast = parseFragment(sanitizeHtmlString(value));
|
const ast = parseFragment(sanitizeHtmlString(value));
|
||||||
normalizeAST(ast, config.ignoredTags);
|
normalizeAST(ast, config.ignoredTags);
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ function sortAttributes(attrs) {
|
|||||||
/**
|
/**
|
||||||
* Normalized AST tree, normlaizing whitespace, attribute + class order etc. Not a pure function,
|
* Normalized AST tree, normlaizing whitespace, attribute + class order etc. Not a pure function,
|
||||||
* mutates input.
|
* mutates input.
|
||||||
* @param {ASTNode} node
|
* @param {import('parse5').DefaultTreeElement} node
|
||||||
* @param {string[]} ignoredTags
|
* @param {string[]} ignoredTags
|
||||||
*/
|
*/
|
||||||
export function normalizeAST(node, ignoredTags = []) {
|
export function normalizeAST(node, ignoredTags = []) {
|
||||||
@@ -53,6 +53,7 @@ export function normalizeAST(node, ignoredTags = []) {
|
|||||||
|
|
||||||
if (isParentNode(node)) {
|
if (isParentNode(node)) {
|
||||||
node.childNodes = node.childNodes.filter(child => filterNode(child, ignoredTags));
|
node.childNodes = node.childNodes.filter(child => filterNode(child, ignoredTags));
|
||||||
|
// @ts-ignore
|
||||||
node.childNodes.forEach(child => normalizeAST(child, ignoredTags));
|
node.childNodes.forEach(child => normalizeAST(child, ignoredTags));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,24 +4,28 @@ const isDefinedPromise = action => typeof action === 'object' && Promise.resolve
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Awaits for "update complete promises" of elements
|
* Awaits for "update complete promises" of elements
|
||||||
* - updateComplete [lit-element]
|
* - for [lit-element](https://github.com/polymer/lit-element) that is `el.updateComplete`;
|
||||||
* - componentOnReady() [stencil]
|
* - for [stencil](https://github.com/ionic-team/stencil/) that is `el.componentOnReady()`;
|
||||||
*
|
*
|
||||||
* If none of these is available we await the next frame.
|
* If none of those specfic Promise hooks are found, it will wait for one frame via
|
||||||
|
* `await nextFrame()`.
|
||||||
*
|
*
|
||||||
* Ensures that ShadyDOM finished its job if available.
|
* Ensures that ShadyDOM finished its job if available.
|
||||||
*
|
*
|
||||||
* @param {HTMLElement} el
|
* @template {Element} T
|
||||||
* @returns {Promise<Element>}
|
* @param {T} el
|
||||||
|
* @returns {Promise<T>}
|
||||||
*/
|
*/
|
||||||
export async function elementUpdated(el) {
|
export async function elementUpdated(el) {
|
||||||
let hasSpecificAwait = false;
|
let hasSpecificAwait = false;
|
||||||
|
// @ts-ignore
|
||||||
let update = el && el.updateComplete;
|
let update = el && el.updateComplete;
|
||||||
if (isDefinedPromise(update)) {
|
if (isDefinedPromise(update)) {
|
||||||
await update;
|
await update;
|
||||||
hasSpecificAwait = true;
|
hasSpecificAwait = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
update = el && el.componentOnReady ? el.componentOnReady() : false;
|
update = el && el.componentOnReady ? el.componentOnReady() : false;
|
||||||
if (isDefinedPromise(update)) {
|
if (isDefinedPromise(update)) {
|
||||||
await update;
|
await update;
|
||||||
@@ -32,7 +36,9 @@ export async function elementUpdated(el) {
|
|||||||
await nextFrame();
|
await nextFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
if (window.ShadyDOM && typeof window.ShadyDOM.flush === 'function') {
|
if (window.ShadyDOM && typeof window.ShadyDOM.flush === 'function') {
|
||||||
|
// @ts-ignore
|
||||||
window.ShadyDOM.flush();
|
window.ShadyDOM.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,14 @@ import { stringFixture, stringFixtureSync } from './stringFixture.js';
|
|||||||
import { litFixture, litFixtureSync } from './litFixture.js';
|
import { litFixture, litFixtureSync } from './litFixture.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setups an element synchronously from the provided string template and puts it in the DOM.
|
* Renders a string/TemplateResult and puts it in the DOM via a fixtureWrapper.
|
||||||
* Allows to specify properties via an object or a function taking the element as an argument.
|
|
||||||
*
|
*
|
||||||
* @param {string | TemplateResult} template
|
* @example
|
||||||
* @returns {Element}
|
* const el = fixtureSync('<my-el><span></span></my-el>');
|
||||||
|
*
|
||||||
|
* @template {Element} T
|
||||||
|
* @param {string | TemplateResult} template Either a string or lit-html TemplateResult
|
||||||
|
* @returns {T} First child of the rendered DOM
|
||||||
*/
|
*/
|
||||||
export function fixtureSync(template) {
|
export function fixtureSync(template) {
|
||||||
if (typeof template === 'string') {
|
if (typeof template === 'string') {
|
||||||
@@ -20,11 +23,24 @@ export function fixtureSync(template) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setups an element asynchronously from the provided string template and puts it in the DOM.
|
* Renders a string/TemplateResult and puts it in the DOM via a fixtureWrapper.
|
||||||
* Allows to specify properties via an object or a function taking the element as an argument.
|
* By default fixture awaits the elements "update complete" Promise.
|
||||||
|
* - for [lit-element](https://github.com/polymer/lit-element) that is `el.updateComplete`;
|
||||||
|
* - for [stencil](https://github.com/ionic-team/stencil/) that is `el.componentOnReady()`;
|
||||||
*
|
*
|
||||||
* @param {string | TemplateResult} template
|
* If none of those specfic Promise hooks are found, it will wait for one frame via
|
||||||
* @returns {Promise<Element>}
|
* `await nextFrame()`.
|
||||||
|
*
|
||||||
|
* **Note**: this does not guarantee that the element is done rendering -
|
||||||
|
* it just waits for the next JavaScript tick.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const el = await fixture('<my-el><span></span></my-el>');
|
||||||
|
* expect(el.fullyRendered).to.be.true;
|
||||||
|
*
|
||||||
|
* @template {Element} T
|
||||||
|
* @param {string | TemplateResult} template Either a string or lit-html TemplateResult
|
||||||
|
* @returns {Promise<T>} A Promise that will resolve to the first child of the rendered DOM
|
||||||
*/
|
*/
|
||||||
export async function fixture(template) {
|
export async function fixture(template) {
|
||||||
if (typeof template === 'string') {
|
if (typeof template === 'string') {
|
||||||
|
|||||||
5
packages/testing-helpers/fixture.d.ts
vendored
5
packages/testing-helpers/fixture.d.ts
vendored
@@ -1,5 +0,0 @@
|
|||||||
import { TemplateResult } from 'lit-html';
|
|
||||||
|
|
||||||
export function fixtureSync<T extends Element = Element>(template: string | TemplateResult): T;
|
|
||||||
export function fixture<T extends Element = Element>(template: string | TemplateResult): Promise<T>;
|
|
||||||
export function fixtureCleanup(): void;
|
|
||||||
2
packages/testing-helpers/fixtureWrapper.d.ts
vendored
2
packages/testing-helpers/fixtureWrapper.d.ts
vendored
@@ -1,2 +0,0 @@
|
|||||||
export const cachedWrappers: Array<Element>;
|
|
||||||
export function fixtureWrapper(): Element;
|
|
||||||
@@ -3,10 +3,9 @@ export const cachedWrappers = [];
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a wrapper as a direct child of `<body>` to put the tested element into.
|
* Creates a wrapper as a direct child of `<body>` to put the tested element into.
|
||||||
* Needed to run a `connectedCallback()` on a tested element.
|
* Need to be in the DOM to test for example `connectedCallback()` on elements.
|
||||||
*
|
*
|
||||||
* @returns {Element}
|
* @returns {Element}
|
||||||
* @private
|
|
||||||
*/
|
*/
|
||||||
export function fixtureWrapper() {
|
export function fixtureWrapper() {
|
||||||
const wrapper = document.createElement('div');
|
const wrapper = document.createElement('div');
|
||||||
@@ -16,7 +15,8 @@ export function fixtureWrapper() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cleans up all defined fixtures
|
* Cleans up all defined fixtures by removing the actual wrapper nodes.
|
||||||
|
* Common usecase is at the end of each test.
|
||||||
*/
|
*/
|
||||||
export function fixtureCleanup() {
|
export function fixtureCleanup() {
|
||||||
if (cachedWrappers) {
|
if (cachedWrappers) {
|
||||||
|
|||||||
10
packages/testing-helpers/helpers.d.ts
vendored
10
packages/testing-helpers/helpers.d.ts
vendored
@@ -1,10 +0,0 @@
|
|||||||
type Constructor<T = {}> = new (...args: any[]) => T;
|
|
||||||
|
|
||||||
export function defineCE<TBase extends Constructor>(klass: TBase): string;
|
|
||||||
export function isIE(): boolean
|
|
||||||
export function aTimeout(ms: number): Promise<void>
|
|
||||||
export function triggerBlurFor(element: HTMLElement): Promise<void>
|
|
||||||
export function triggerFocusFor(element: HTMLElement): Promise<void>
|
|
||||||
export function oneEvent(element: HTMLElement, eventName: string): Promise<Event>
|
|
||||||
export function nextFrame(): Promise<void>
|
|
||||||
export function flush(): Promise<void>
|
|
||||||
@@ -11,8 +11,8 @@ let defineCECounter = 0;
|
|||||||
* const el = fixture(`<${tag}></${tag}>`);
|
* const el = fixture(`<${tag}></${tag}>`);
|
||||||
* // test el
|
* // test el
|
||||||
*
|
*
|
||||||
* @param {function} klass
|
* @param {function} klass Class which extends HTMLElement
|
||||||
* @returns {string}
|
* @returns {string} Tag name of the registered element
|
||||||
*/
|
*/
|
||||||
export function defineCE(klass) {
|
export function defineCE(klass) {
|
||||||
const tag = `test-${defineCECounter}`;
|
const tag = `test-${defineCECounter}`;
|
||||||
@@ -31,10 +31,13 @@ export function isIE() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves after provided amount of time.
|
* Resolves after provided amount of miliseconds.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* await aTimeout(100);
|
||||||
*
|
*
|
||||||
* @param {number} ms Miliseconds.
|
* @param {number} ms Miliseconds.
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>} Promise to await until time is up
|
||||||
*/
|
*/
|
||||||
export function aTimeout(ms) {
|
export function aTimeout(ms) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
@@ -45,17 +48,25 @@ export function aTimeout(ms) {
|
|||||||
/**
|
/**
|
||||||
* Resolves after requestAnimationFrame.
|
* Resolves after requestAnimationFrame.
|
||||||
*
|
*
|
||||||
* @returns {Promise<void>}
|
* @example
|
||||||
|
* await nextFrame();
|
||||||
|
*
|
||||||
|
* @returns {Promise<void>} Promise that resolved after requestAnimationFrame
|
||||||
*/
|
*/
|
||||||
export function nextFrame() {
|
export function nextFrame() {
|
||||||
return new Promise(resolve => requestAnimationFrame(resolve));
|
return new Promise(resolve => requestAnimationFrame(() => resolve()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves after blur.
|
* Blurs the provided element and await time before and after it on IE.
|
||||||
*
|
*
|
||||||
* @param {HTMLElement} element
|
* @example
|
||||||
* @returns {Promise<void>}
|
* const el = await fixture('<input type="text" autofocus />');
|
||||||
|
* await triggerBlurFor(el);
|
||||||
|
* // el is no longer focused
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} element Element/Node to blur
|
||||||
|
* @returns {Promise<void>} Promise to await until blur is done (for IE)
|
||||||
*/
|
*/
|
||||||
export async function triggerBlurFor(element) {
|
export async function triggerBlurFor(element) {
|
||||||
if (isIE()) {
|
if (isIE()) {
|
||||||
@@ -70,12 +81,19 @@ export async function triggerBlurFor(element) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves after focus.
|
* Focuses the provided element and await time before and after it on IE.
|
||||||
* Adding an event and immediately trigger it in fails in IE.
|
*
|
||||||
|
* Background info:
|
||||||
|
* Adding an event and immediately trigger it fails in IE.
|
||||||
* Also before checking the effects of a trigger IE needs some time.
|
* Also before checking the effects of a trigger IE needs some time.
|
||||||
*
|
*
|
||||||
* @param {HTMLElement} element
|
* @example
|
||||||
* @returns {Promise<void>}
|
* const el = await fixture('<input type="text" />');
|
||||||
|
* await triggerFocusFor(el);
|
||||||
|
* // el is now focused
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} element Element/Node to focus
|
||||||
|
* @returns {Promise<void>} Promise to await until focus is done (for IE)
|
||||||
*/
|
*/
|
||||||
export async function triggerFocusFor(element) {
|
export async function triggerFocusFor(element) {
|
||||||
if (isIE()) {
|
if (isIE()) {
|
||||||
@@ -92,9 +110,14 @@ export async function triggerFocusFor(element) {
|
|||||||
/**
|
/**
|
||||||
* Listens for one event and resolves with this event object after it was fired.
|
* Listens for one event and resolves with this event object after it was fired.
|
||||||
*
|
*
|
||||||
* @param {HTMLElement} element
|
* @example
|
||||||
* @param {string} eventName
|
* setTimeout(() => el.fireDone());
|
||||||
* @returns {Promise<Event>}
|
* await oneEvent(el, 'done');
|
||||||
|
* expect(el.done).to.be.true;
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} element Element that is going to fire the event
|
||||||
|
* @param {string} eventName Name of the event
|
||||||
|
* @returns {Promise<CustomEvent>} Promise to await until the event has been fired
|
||||||
*/
|
*/
|
||||||
export function oneEvent(element, eventName) {
|
export function oneEvent(element, eventName) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
|
|||||||
13
packages/testing-helpers/index.d.ts
vendored
13
packages/testing-helpers/index.d.ts
vendored
@@ -1,13 +0,0 @@
|
|||||||
export { html, unsafeStatic } from './lit-html.js';
|
|
||||||
export {
|
|
||||||
triggerBlurFor,
|
|
||||||
triggerFocusFor,
|
|
||||||
oneEvent,
|
|
||||||
isIE,
|
|
||||||
defineCE,
|
|
||||||
aTimeout,
|
|
||||||
nextFrame,
|
|
||||||
} from './helpers.js';
|
|
||||||
export { litFixture, litFixtureSync } from './litFixture.js';
|
|
||||||
export { stringFixture, stringFixtureSync } from './stringFixture.js';
|
|
||||||
export { fixture, fixtureSync } from './fixture.js';
|
|
||||||
2
packages/testing-helpers/lit-html.d.ts
vendored
2
packages/testing-helpers/lit-html.d.ts
vendored
@@ -1,2 +0,0 @@
|
|||||||
export { render, html } from 'lit-html/lit-html';
|
|
||||||
export function unsafeStatic<TOptions>(options: TOptions): { d: TOptions }
|
|
||||||
@@ -18,6 +18,10 @@ export { render } from 'lit-html/lit-html.js';
|
|||||||
* html`<${tag} prop="${prop}"></${tag>`
|
* html`<${tag} prop="${prop}"></${tag>`
|
||||||
* // will in turn calls lit-html html function as:
|
* // will in turn calls lit-html html function as:
|
||||||
* html`<my-tag prop="${prop}"></my-tag>`
|
* html`<my-tag prop="${prop}"></my-tag>`
|
||||||
|
*
|
||||||
|
* @param {TemplateStringsArray} strings Static Parts
|
||||||
|
* @param {Array[any]} values Dynamic Parts
|
||||||
|
* @returns {import('lit-html').TemplateResult}
|
||||||
*/
|
*/
|
||||||
export function html(strings, ...values) {
|
export function html(strings, ...values) {
|
||||||
const newVal = []; // result values to be passed on to lit-html
|
const newVal = []; // result values to be passed on to lit-html
|
||||||
@@ -49,6 +53,9 @@ export function html(strings, ...values) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
// Return lit template
|
// Return lit template
|
||||||
|
// TODO: this is the reason it's not performant TemplateStringsArray needs to be always exactly
|
||||||
|
// the same => e.g. would require specific caching
|
||||||
|
// @ts-ignore
|
||||||
return litHtml(newStr, ...newVal);
|
return litHtml(newStr, ...newVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
4
packages/testing-helpers/litFixture.d.ts
vendored
4
packages/testing-helpers/litFixture.d.ts
vendored
@@ -1,4 +0,0 @@
|
|||||||
import { TemplateResult } from 'lit-html';
|
|
||||||
|
|
||||||
export function litFixtureSync<T extends Element = Element>(template: TemplateResult): T;
|
|
||||||
export function litFixture<T extends Element = Element>(template: TemplateResult): Promise<T>;
|
|
||||||
@@ -5,20 +5,22 @@ import { elementUpdated } from './elementUpdated.js';
|
|||||||
/**
|
/**
|
||||||
* Setups an element synchronously from the provided lit-html template and puts it in the DOM.
|
* Setups an element synchronously from the provided lit-html template and puts it in the DOM.
|
||||||
*
|
*
|
||||||
|
* @template {Element} T - Is an element or a node
|
||||||
* @param {import('lit-html').TemplateResult} template
|
* @param {import('lit-html').TemplateResult} template
|
||||||
* @returns {Element}
|
* @returns {T}
|
||||||
*/
|
*/
|
||||||
export function litFixtureSync(template) {
|
export function litFixtureSync(template) {
|
||||||
const wrapper = fixtureWrapper();
|
const wrapper = fixtureWrapper();
|
||||||
render(template, wrapper);
|
render(template, wrapper);
|
||||||
return wrapper.children[0];
|
return /** @type {T} */ (wrapper.children[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setups an element asynchronously from the provided lit-html template and puts it in the DOM.
|
* Setups an element asynchronously from the provided lit-html template and puts it in the DOM.
|
||||||
*
|
*
|
||||||
|
* @template {Element} T - Is an element or a node
|
||||||
* @param {import('lit-html').TemplateResult} template
|
* @param {import('lit-html').TemplateResult} template
|
||||||
* @returns {Promise<Element>}
|
* @returns {Promise<T>}
|
||||||
*/
|
*/
|
||||||
export async function litFixture(template) {
|
export async function litFixture(template) {
|
||||||
const el = litFixtureSync(template);
|
const el = litFixtureSync(template);
|
||||||
|
|||||||
@@ -5,21 +5,23 @@ import { elementUpdated } from './elementUpdated.js';
|
|||||||
* Setups an element synchronously from the provided string template and puts it in the DOM.
|
* Setups an element synchronously from the provided string template and puts it in the DOM.
|
||||||
* Allows to specify properties via an object or a function taking the element as an argument.
|
* Allows to specify properties via an object or a function taking the element as an argument.
|
||||||
*
|
*
|
||||||
|
* @template {Element} T - Is an element or a node
|
||||||
* @param {string} template
|
* @param {string} template
|
||||||
* @returns {Element}
|
* @returns {T}
|
||||||
*/
|
*/
|
||||||
export function stringFixtureSync(template) {
|
export function stringFixtureSync(template) {
|
||||||
const wrapper = fixtureWrapper();
|
const wrapper = fixtureWrapper();
|
||||||
wrapper.innerHTML = template;
|
wrapper.innerHTML = template;
|
||||||
return wrapper.children[0];
|
return /** @type {T} */ (wrapper.children[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setups an element asynchronously from the provided string template and puts it in the DOM.
|
* Setups an element asynchronously from the provided string template and puts it in the DOM.
|
||||||
* Allows to specify properties via an object or a function taking the element as an argument.
|
* Allows to specify properties via an object or a function taking the element as an argument.
|
||||||
*
|
*
|
||||||
|
* @template {Element} T - Is an element or a node
|
||||||
* @param {string} template
|
* @param {string} template
|
||||||
* @returns {Promise<Element>}
|
* @returns {Promise<T>}
|
||||||
*/
|
*/
|
||||||
export async function stringFixture(template) {
|
export async function stringFixture(template) {
|
||||||
const el = stringFixtureSync(template);
|
const el = stringFixtureSync(template);
|
||||||
|
|||||||
@@ -10,22 +10,22 @@ describe('elementUpdated', () => {
|
|||||||
counter += 1;
|
counter += 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
class TmpElement {}
|
class TmpElement extends HTMLElement {}
|
||||||
const el = new TmpElement();
|
const el = Object.create(TmpElement.prototype);
|
||||||
await elementUpdated(el);
|
await elementUpdated(el);
|
||||||
expect(counter).to.equal(1);
|
expect(counter).to.equal(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('will wait for lit-element to be updated via el.updateComplete', async () => {
|
it('will wait for lit-element to be updated via el.updateComplete', async () => {
|
||||||
let counter = 0;
|
let counter = 0;
|
||||||
class TmpElement {
|
class TmpElement extends HTMLElement {
|
||||||
get updateComplete() {
|
get updateComplete() {
|
||||||
return new Promise(resolve => requestAnimationFrame(resolve)).then(() => {
|
return new Promise(resolve => requestAnimationFrame(resolve)).then(() => {
|
||||||
counter += 1;
|
counter += 1;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const el = new TmpElement();
|
const el = Object.create(TmpElement.prototype);
|
||||||
|
|
||||||
await elementUpdated(el);
|
await elementUpdated(el);
|
||||||
expect(counter).to.equal(1);
|
expect(counter).to.equal(1);
|
||||||
@@ -33,14 +33,15 @@ describe('elementUpdated', () => {
|
|||||||
|
|
||||||
it('will wait for stencil to be updated via el.componentOnReady()', async () => {
|
it('will wait for stencil to be updated via el.componentOnReady()', async () => {
|
||||||
let counter = 0;
|
let counter = 0;
|
||||||
class TmpElement {
|
class TmpElement extends HTMLElement {
|
||||||
|
// @ts-ignore
|
||||||
componentOnReady() {
|
componentOnReady() {
|
||||||
return new Promise(resolve => requestAnimationFrame(resolve)).then(() => {
|
return new Promise(resolve => requestAnimationFrame(resolve)).then(() => {
|
||||||
counter += 1;
|
counter += 1;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const el = new TmpElement();
|
const el = Object.create(TmpElement.prototype);
|
||||||
|
|
||||||
await elementUpdated(el);
|
await elementUpdated(el);
|
||||||
expect(counter).to.equal(1);
|
expect(counter).to.equal(1);
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
// @ts-ignore
|
||||||
import sinon from 'sinon';
|
import sinon from 'sinon';
|
||||||
|
// @ts-ignore
|
||||||
import { expect } from '@bundled-es-modules/chai';
|
import { expect } from '@bundled-es-modules/chai';
|
||||||
import { cachedWrappers } from '../fixtureWrapper.js';
|
import { cachedWrappers } from '../fixtureWrapper.js';
|
||||||
import { defineCE } from '../helpers';
|
import { defineCE } from '../helpers';
|
||||||
@@ -18,7 +20,11 @@ describe('fixtureSync & fixture', () => {
|
|||||||
const myFunction = () => {};
|
const myFunction = () => {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Element} element
|
* @typedef {Object} TestDiv
|
||||||
|
* @property {number} propNumber Test property for number
|
||||||
|
* @property {function} propFunction Test property for function
|
||||||
|
*
|
||||||
|
* @param {TestDiv} element
|
||||||
*/
|
*/
|
||||||
function testElement(element) {
|
function testElement(element) {
|
||||||
expect(element.propNumber).to.equal(10);
|
expect(element.propNumber).to.equal(10);
|
||||||
@@ -68,16 +74,20 @@ describe('fixtureSync & fixture', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('ensures ShadyDOM finished its job', async () => {
|
it('ensures ShadyDOM finished its job', async () => {
|
||||||
|
// @ts-ignore
|
||||||
const originalShadyDOM = window.ShadyDOM;
|
const originalShadyDOM = window.ShadyDOM;
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
window.ShadyDOM = { flush: sinon.spy() };
|
window.ShadyDOM = { flush: sinon.spy() };
|
||||||
|
|
||||||
class Test extends HTMLElement {}
|
class Test extends HTMLElement {}
|
||||||
|
|
||||||
const tag = defineCE(Test);
|
const tag = defineCE(Test);
|
||||||
await fixture(`<${tag}></${tag}>`);
|
await fixture(`<${tag}></${tag}>`);
|
||||||
|
// @ts-ignore
|
||||||
expect(window.ShadyDOM.flush.callCount).to.equal(1);
|
expect(window.ShadyDOM.flush.callCount).to.equal(1);
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
window.ShadyDOM = originalShadyDOM;
|
window.ShadyDOM = originalShadyDOM;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,19 @@
|
|||||||
import { expect } from '@bundled-es-modules/chai';
|
import { expect } from '@bundled-es-modules/chai';
|
||||||
import { html, litFixture, unsafeStatic } from '../index.js';
|
import { html, litFixture, unsafeStatic } from '../index.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} ChildType
|
||||||
|
* @property {number} myNumber
|
||||||
|
*
|
||||||
|
* @typedef {Element & ChildType} TestDiv
|
||||||
|
*/
|
||||||
|
|
||||||
describe('html', () => {
|
describe('html', () => {
|
||||||
it('renders dynamic tags', async () => {
|
it('renders dynamic tags', async () => {
|
||||||
const tag = unsafeStatic('my-foo');
|
const tag = unsafeStatic('my-foo');
|
||||||
const el = await litFixture(html`
|
const el = /** @type {TestDiv} */ (await litFixture(html`
|
||||||
<${tag} .myNumber=${4} foo="bar"></${tag}>
|
<${tag} .myNumber=${4} foo="bar"></${tag}>
|
||||||
`);
|
`));
|
||||||
expect(el.tagName).to.equal('MY-FOO');
|
expect(el.tagName).to.equal('MY-FOO');
|
||||||
expect(el.myNumber).to.equal(4);
|
expect(el.myNumber).to.equal(4);
|
||||||
expect(el.getAttribute('foo')).to.equal('bar');
|
expect(el.getAttribute('foo')).to.equal('bar');
|
||||||
@@ -20,9 +27,9 @@ describe('html', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('renders static templates with properties, attributes', async () => {
|
it('renders static templates with properties, attributes', async () => {
|
||||||
const el = await litFixture(html`
|
const el = /** @type {TestDiv} */ (await litFixture(html`
|
||||||
<div .myNumber=${4} foo="bar"></div>
|
<div .myNumber=${4} foo="bar"></div>
|
||||||
`);
|
`));
|
||||||
expect(el.tagName).to.equal('DIV');
|
expect(el.tagName).to.equal('DIV');
|
||||||
expect(el.myNumber).to.equal(4);
|
expect(el.myNumber).to.equal(4);
|
||||||
expect(el.getAttribute('foo')).to.equal('bar');
|
expect(el.getAttribute('foo')).to.equal('bar');
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ describe('True Checking', () => {
|
|||||||
<get-result .success=${foo === 1}></get-result>
|
<get-result .success=${foo === 1}></get-result>
|
||||||
`,
|
`,
|
||||||
);
|
);
|
||||||
|
// @ts-ignore
|
||||||
expect(el.success).to.be.true;
|
expect(el.success).to.be.true;
|
||||||
expect(el).dom.to.equal('<get-result><p>YEAH</p></get-result>');
|
expect(el).dom.to.equal('<get-result><p>YEAH</p></get-result>');
|
||||||
});
|
});
|
||||||
|
|||||||
22
packages/testing/index.d.ts
vendored
22
packages/testing/index.d.ts
vendored
@@ -1,22 +0,0 @@
|
|||||||
export {
|
|
||||||
html,
|
|
||||||
unsafeStatic,
|
|
||||||
triggerBlurFor,
|
|
||||||
triggerFocusFor,
|
|
||||||
oneEvent,
|
|
||||||
isIE,
|
|
||||||
defineCE,
|
|
||||||
aTimeout,
|
|
||||||
litFixture,
|
|
||||||
litFixtureSync,
|
|
||||||
fixture,
|
|
||||||
fixtureSync,
|
|
||||||
} from '@open-wc/testing-helpers';
|
|
||||||
|
|
||||||
export {
|
|
||||||
expect,
|
|
||||||
should,
|
|
||||||
assert,
|
|
||||||
} from 'chai';
|
|
||||||
|
|
||||||
import 'chai';
|
|
||||||
@@ -1 +1 @@
|
|||||||
window.bar = new URL('./', import.meta.url);
|
export const bar = new URL('./', import.meta.url);
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
import './caseBsub/caseBsub';
|
import './caseBsub/caseBsub';
|
||||||
|
|
||||||
window.foo = new URL('./', import.meta.url);
|
export const foo = new URL('./', import.meta.url);
|
||||||
|
|||||||
@@ -41,11 +41,11 @@ describe('import-meta-url-loader', () => {
|
|||||||
'' +
|
'' +
|
||||||
"import './caseBsub/caseBsub';\n" +
|
"import './caseBsub/caseBsub';\n" +
|
||||||
'\n' +
|
'\n' +
|
||||||
"window.foo = new URL('./', ({ url: `${window.location.protocol}//${window.location.host}/caseB/index.js` }).url);\n",
|
"export const foo = new URL('./', ({ url: `${window.location.protocol}//${window.location.host}/caseB/index.js` }).url);\n",
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(caseBsub).to.equal(
|
expect(caseBsub).to.equal(
|
||||||
"window.bar = new URL('./', ({ url: `${window.location.protocol}//${window.location.host}/caseB/caseBsub/caseBsub.js` }).url);\n",
|
"export const bar = new URL('./', ({ url: `${window.location.protocol}//${window.location.host}/caseB/caseBsub/caseBsub.js` }).url);\n",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
|
// @ts-nocheck
|
||||||
const regex = /import\.meta/g;
|
const regex = /import\.meta/g;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
32
tsconfig.json
Normal file
32
tsconfig.json
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "esnext",
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"lib": ["es2017", "dom"],
|
||||||
|
"allowJs": true,
|
||||||
|
"checkJs": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"strict": false,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"alwaysStrict": true,
|
||||||
|
"types": ["node", "mocha"],
|
||||||
|
"esModuleInterop": true,
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"browser.d.ts"
|
||||||
|
],
|
||||||
|
"include": [
|
||||||
|
"packages"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"**/karma.conf.js",
|
||||||
|
"**/*.config.js",
|
||||||
|
"**/wallaby.js",
|
||||||
|
"node_modules",
|
||||||
|
"**/node_modules/*",
|
||||||
|
"**/coverage/*",
|
||||||
|
"packages/create/src/generators/*/templates/**/*",
|
||||||
|
"**/dist/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user