chore: new website using rocket

Co-authored-by: Lars den Bakker <larsdenbakker@gmail.com>
This commit is contained in:
Thomas Allmer
2020-10-12 19:39:18 +02:00
parent b96501055f
commit 0a1b247513
217 changed files with 12776 additions and 5915 deletions

19
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,19 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
## Expected behavior
A clear and concise description of what you expected to happen.
## Actual Behavior
Describe what happens instead.
## Additional context
Which packages and versions are you using.

View File

@@ -0,0 +1,9 @@
---
name: Feature Request
about: You have an idea about an additional feature
title: '[Feature Request] please use the discussions tab'
labels: ''
assignees: ''
---
Please use [discussions](https://github.com/open-wc/open-wc/discussions/new) for new feature requests.

3
.github/pull_request_template.md vendored Normal file
View File

@@ -0,0 +1,3 @@
## What I did
1.

8
.gitignore vendored
View File

@@ -46,3 +46,11 @@ local.log
## generated codelabs
docs/.vuepress/public/codelabs
# Local Netlify folder
.netlify
## Rocket ignore files (need to be the full relative path to the folders)
docs/_merged_data/
docs/_merged_assets/
docs/_merged_includes/

2
.nvmrc
View File

@@ -1 +1 @@
12
14

View File

@@ -1,88 +0,0 @@
/* eslint-disable */
const path = require('path');
const copy = require('rollup-plugin-copy');
const { rollup } = require('rollup');
const { generateSW } = require('rollup-plugin-workbox');
const Eleventy = require('@11ty/eleventy');
const { createMpaConfig } = require('./docs/_building-rollup/createMpaConfig.js');
const elev = new Eleventy('./docs', './_site');
elev.setConfigPathOverride('./docs/.eleventy.js');
elev.setDryRun(true); // do not write to file system
/**
* @param {object} config
*/
async function buildAndWrite(config) {
const bundle = await rollup(config);
if (Array.isArray(config.output)) {
await bundle.write(config.output[0]);
await bundle.write(config.output[1]);
} else {
await bundle.write(config.output);
}
}
async function productionBuild(html) {
const mpaConfig = createMpaConfig({
outputDir: '_site',
legacyBuild: false,
html: { html },
injectServiceWorker: false,
workbox: false,
});
mpaConfig.plugins.push(
generateSW({
globIgnores: ['polyfills/*.js', 'legacy-*.js', 'nomodule-*.js'],
swDest: path.join(process.cwd(), '_site', 'service-worker.js'),
globDirectory: path.join(process.cwd(), '_site'),
globPatterns: ['**/*.{html,js,json,css,webmanifest,png,gif}'],
skipWaiting: true,
clientsClaim: true,
runtimeCaching: [
{
urlPattern: 'polyfills/*.js',
handler: 'CacheFirst',
},
],
}),
);
const dest = '_site/';
mpaConfig.plugins.push(
copy({
targets: [
{ src: 'docs/styles.css', dest },
{ src: 'docs/demoing/demo/custom-elements.json', dest },
{ src: 'docs/manifest.json', dest },
{ src: 'docs/**/*.{png,gif}', dest },
],
flatten: false,
}),
);
await buildAndWrite(mpaConfig);
}
async function main() {
const htmlFiles = [];
elev.config.filters['hook-for-rocket'] = (html, outputPath, inputPath) => {
htmlFiles.push({
html,
name: outputPath.substring(8),
rootDir: path.dirname(path.resolve(inputPath)),
});
return html;
};
await elev.init();
await elev.write();
await productionBuild(htmlFiles);
}
main();

View File

@@ -1,68 +0,0 @@
/* eslint-disable */
const { createConfig, startServer } = require('es-dev-server');
const Eleventy = require('@11ty/eleventy');
const elev = new Eleventy('./docs', './__site');
elev.setConfigPathOverride('./docs/.eleventy.js');
elev.setDryRun(true); // do not write to file system
async function run() {
await elev.init();
const config = {
nodeResolve: true,
watch: true,
open: './docs/README.md',
middlewares: [
async (ctx, next) => {
if (ctx.path.endsWith('index.html')) {
ctx.path = ctx.path.replace('index.html', 'README.md');
} else if (ctx.path.endsWith('.html')) {
ctx.path = ctx.path.replace('.html', '.md');
} else if (ctx.path.endsWith('/')) {
ctx.path += 'README.md';
}
return next();
},
],
plugins: [
{
async transform(context) {
if (context.path.endsWith('md')) {
const serverPath = context.path;
let { body } = 'File not found';
elev.config.filters['hook-for-rocket'] = (content, outputPath, inputPath) => {
if (inputPath === `.${serverPath}`) {
body = content;
}
return content;
};
await elev.write();
return {
body: body.replace(/href="\//g, 'href="/docs/').replace(/src="\//g, 'src="/docs/'),
};
}
return undefined;
},
resolveMimeType(context) {
if (context.path.endsWith('md')) {
return 'text/html';
}
return undefined;
},
},
],
};
startServer(createConfig(config));
['exit', 'SIGINT'].forEach(event => {
// @ts-ignore
process.on(event, () => {
process.exit(0);
});
});
}
run();

View File

@@ -1,27 +0,0 @@
const pluginMdjs = require('./_plugin-mdjs/index.js');
module.exports = function (eleventyConfig) {
eleventyConfig.addPlugin(pluginMdjs);
eleventyConfig.addPassthroughCopy('./styles.css');
eleventyConfig.addPassthroughCopy('./demoing/demo/custom-elements.json');
eleventyConfig.addPassthroughCopy('./manifest.json');
eleventyConfig.addPassthroughCopy('./**/*.{png,gif}');
eleventyConfig.addCollection('section', function (collection) {
// This works _because_ of our current content. Something like https://github.com/Polymer/lit-html/blob/master/docs/.eleventy.js#L37
// would be more robust, but there are likely other answers here.
return collection.getFilteredByTag('section').reverse();
});
// 11ty needs this as it apparently reads this config from multiple files
// and only if we provide this hook we can actually override later when we
// programmatically trigger 11ty
// @TODO: create an issue and find a nicer way to add this transformer
eleventyConfig.addTransform('hook-for-rocket');
return {
dir: { input: './', output: '../_site-dev' },
passthroughFileCopy: true,
};
};

8
docs/404.md Normal file
View File

@@ -0,0 +1,8 @@
---
title: Open Web Components 404 Page
permalink: 404.html
---
This page could not be found.
Return [home](/) to start over.

View File

@@ -1,30 +0,0 @@
---
permalink: 'index.html'
title: Open Web Components
layout: home.njk
home: true
actionText: Get Started →
actionLink: /guide/
features:
- title: Smart Defaults
details: Enjoy the peace of mind that comes from having a well-known default solution for almost everything. From linting to testing to demos to publishing - have the full experience.
- title: Awesome Generators
details: Get up and running quickly with opinionated generators, or add recommended tools to existing projects. Our comprehensive fleet of generators have got you covered.
- title: Open Source Love
details: Open Web Components is a community-effort, independent of any framework or company. We use mostly open-source tools and services.
footer: MIT Licensed | Copyright © 2018-2020 open-wc
---
<hr>
<p align="center" style="margin: 50px; color: #4e6e8e;">
<q><i>I used open-wc to get testing working, it was so helpful, works like a charm!</i></q>
<br><br> - Ikira
</p>
<hr>
<p align="center" style="margin: 50px; color: #4e6e8e;">
<q><i>Just now visiting open-wc.org site for the first time. You guys are killing it! Amazing stuff, and IMO totally on target. Many thanks.</i></q>
<br><br> - Petecarapetyan
</p>
<p align="center">
hosted by <a href="http://netlify.com/" rel="noopener" target="_blank">netlify</a>
</p>

9
docs/_assets/head.html Normal file
View File

@@ -0,0 +1,9 @@
<!-- <link
href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;700&display=swap"
rel="stylesheet"
/>
<link
href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;700&display=swap"
rel="stylesheet"
/> -->
<meta name="twitter:creator" content="@open-wc" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="700.000000pt" height="700.000000pt" viewBox="0 0 700.000000 700.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.11, written by Peter Selinger 2001-2013
</metadata>
<g transform="translate(0.000000,700.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M3238 6965 c-2 -2 -35 -7 -73 -10 -75 -6 -131 -12 -174 -20 -14 -2
-42 -7 -61 -10 -69 -11 -196 -37 -255 -52 -33 -8 -67 -16 -75 -18 -8 -2 -61
-18 -118 -35 -290 -87 -643 -254 -897 -423 -781 -522 -1315 -1321 -1494 -2232
-20 -106 -47 -304 -56 -420 -6 -80 -6 -422 0 -475 2 -19 6 -69 10 -110 11
-136 47 -344 90 -520 22 -93 96 -323 106 -335 5 -5 9 -15 9 -23 0 -39 166
-388 261 -551 45 -76 179 -276 214 -320 8 -9 37 -45 65 -81 96 -121 236 -270
376 -400 66 -61 249 -210 292 -237 15 -10 29 -20 32 -24 8 -10 198 -131 275
-176 261 -151 598 -289 860 -353 129 -31 306 -68 365 -75 30 -4 62 -9 70 -10
8 -2 51 -6 95 -10 44 -3 96 -8 115 -10 92 -11 462 -4 590 10 665 73 1288 329
1805 740 156 124 333 293 452 431 l52 61 -41 33 c-22 19 -46 39 -52 45 -6 5
-40 34 -76 63 -79 65 -71 58 -255 211 -82 69 -152 128 -155 131 -3 3 -70 59
-150 125 -80 66 -156 129 -169 141 l-23 21 -137 -136 c-442 -442 -998 -675
-1611 -675 -236 0 -412 26 -646 94 -674 197 -1232 719 -1479 1384 -40 110
-101 338 -110 413 -3 27 -7 59 -10 71 -9 51 -19 194 -19 297 0 105 12 303 19
315 1 3 6 25 9 50 18 133 82 369 127 466 10 24 19 45 19 48 0 3 20 48 45 101
159 334 414 640 720 862 333 241 728 387 1140 420 602 48 1183 -138 1645 -525
95 -79 256 -241 286 -288 9 -13 20 -24 24 -24 7 0 293 226 570 450 19 15 109
87 200 160 91 72 166 134 168 138 4 13 -173 211 -291 326 -185 180 -395 341
-622 479 -225 136 -491 257 -740 337 -91 29 -304 84 -365 95 -14 2 -68 11
-120 20 -52 9 -120 19 -150 21 -30 3 -66 8 -80 11 -29 7 -596 14 -602 8z"/>
<path d="M6240 5255 c-96 -63 -229 -148 -295 -190 -445 -282 -537 -345 -527
-358 11 -14 64 -106 89 -154 9 -18 21 -33 26 -33 5 0 250 120 543 267 l534
266 -44 81 c-60 112 -136 236 -144 235 -4 0 -86 -51 -182 -114z"/>
<path d="M6645 4681 c-55 -21 -282 -106 -505 -190 -223 -83 -412 -155 -420
-158 -8 -3 -17 -7 -20 -7 -3 -1 -20 -8 -38 -15 -38 -16 -38 -5 5 -148 23 -75
31 -92 45 -87 9 2 25 6 35 8 10 2 88 22 173 44 234 62 464 121 565 147 252 64
369 95 373 100 8 8 -97 345 -107 344 -3 0 -51 -17 -106 -38z"/>
<path d="M6880 4002 c-27 -3 -342 -51 -376 -57 -12 -2 -48 -7 -80 -10 -33 -4
-68 -9 -79 -11 -38 -8 -70 -13 -120 -19 -27 -3 -75 -10 -105 -15 -30 -5 -75
-12 -100 -15 -76 -10 -234 -35 -257 -40 -13 -3 -20 -11 -18 -22 3 -21 17 -157
20 -193 l1 -25 110 1 c107 1 379 10 604 20 184 8 319 13 409 15 l84 2 -7 101
c-3 55 -8 110 -10 121 -2 11 -7 50 -10 87 -8 71 -5 68 -66 60z"/>
<path d="M5759 3328 c-1 -26 -3 -54 -14 -153 -4 -38 -5 -67 -1 -64 4 4 25 3
47 -2 21 -5 56 -12 77 -15 32 -4 82 -13 177 -28 14 -3 33 -7 43 -10 9 -2 37
-7 62 -10 25 -3 54 -8 65 -10 11 -2 40 -7 65 -11 25 -4 54 -9 65 -11 35 -7
309 -53 350 -59 22 -4 47 -8 55 -10 8 -2 35 -7 60 -10 25 -3 60 -10 77 -15 18
-4 35 -5 37 -2 2 4 7 31 11 61 4 30 8 62 10 72 6 28 18 150 19 189 1 32 -1 35
-29 36 -28 1 -94 6 -295 19 -110 8 -203 13 -310 20 -52 3 -120 7 -150 10 -68
5 -195 13 -322 20 -97 6 -98 6 -99 -17z"/>
<path d="M5649 2780 l-37 -111 37 -15 c20 -9 77 -31 126 -51 557 -219 876
-346 903 -358 18 -8 38 -15 45 -15 10 0 70 144 82 198 2 9 13 45 24 79 l19 63
-41 11 c-23 5 -55 15 -72 20 -27 8 -547 153 -735 204 -8 2 -46 13 -85 24 -81
23 -223 61 -227 61 -2 0 -19 -50 -39 -110z"/>
<path d="M5454 2353 c-27 -48 -50 -90 -52 -93 -1 -4 55 -44 125 -91 70 -46
292 -192 493 -324 201 -133 370 -244 375 -247 10 -6 41 42 132 197 51 88 51
90 -4 118 -27 13 -73 38 -103 54 -30 17 -127 67 -215 113 -88 45 -173 90 -190
100 -28 16 -97 53 -382 201 -62 33 -116 59 -121 59 -5 0 -31 -39 -58 -87z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

1
docs/_assets/logo.svg Normal file
View File

@@ -0,0 +1 @@
<?xml version="1.0" encoding="utf-8"?><svg viewBox="0 0 244 244" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" alt="open-wc" class="logo"><defs><linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-1"><stop stop-color="#9B00FF" offset="0%"></stop><stop stop-color="#0077FF" offset="100%"></stop></linearGradient><g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" id="logo"><path d="M205.639259,176.936244 C207.430887,174.217233 209.093339,171.405629 210.617884,168.510161 M215.112174,158.724316 C216.385153,155.50304 217.495621,152.199852 218.433474,148.824851 M220.655293,138.874185 C221.231935,135.482212 221.637704,132.03207 221.863435,128.532919 M222,118.131039 C221.860539,114.466419 221.523806,110.85231 221.000113,107.299021 M218.885321,96.8583653 C218.001583,93.4468963 216.942225,90.1061026 215.717466,86.8461994 M211.549484,77.3039459 C209.957339,74.1238901 208.200597,71.0404957 206.290425,68.0649233 M200.180513,59.5598295 C181.848457,36.6639805 153.655709,22 122.036748,22 C66.7879774,22 22,66.771525 22,122 C22,177.228475 66.7879774,222 122.036748,222 C152.914668,222 180.52509,208.015313 198.875424,186.036326" stroke="url(#linearGradient-1)" stroke-width="42.0804674"></path></g></defs><use xlink:href="#logo"></use></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

130
docs/_assets/style.css Normal file
View File

@@ -0,0 +1,130 @@
body[layout='home'] .markdown-body .call-to-action:nth-of-type(2) {
--primary-color: #222;
--primary-color-lighter: #333;
--primary-color-darker: #000;
}
rocket-navigation {
font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;
}
header {
font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;
}
/* TODO: should be in rocket? */
#sidebar-nav .logo-link {
text-decoration: none;
}
@media screen and (min-width: 1024px) {
body[layout='home'][home-layout='background'] .page-background {
top: -111px;
right: -360px;
}
}
body,
#sidebar-nav {
background: var(--page-background);
}
footer {
background-color: var(--footer-background);
}
#newsletter {
box-shadow: 0 0 8px rgba(0, 0, 0, 0.101562);
padding: 20px;
text-align: center;
margin-top: 20px;
background-color: var(--blog-ba) #fafafa;
border-top: 1px solid #eaeaea;
border-bottom: 1px solid #eaeaea;
}
#newsletter h4 {
margin: 0;
}
header {
background: var(--header-color);
}
.call-to-action {
background: var(--button-one)!important;
text-shadow: none!important;
border-radius: 5px!important;
padding-top: 15px!important;
padding-bottom: 15px!important;
border: none!important;
}
.call-to-action:hover,
.call-to-action:focus,
.call-to-action:active {
background: var(--button-one-hover)!important;
}
.call-to-action:nth-child(2) {
background: var(--button-two)!important;
}
.call-to-action:nth-child(2):hover,
.call-to-action:nth-child(2):focus,
.call-to-action:nth-child(2):active {
background: var(--button-two-hover)!important;
}
/* Guides styles: */
#sidebar-nav > rocket-navigation > ul > li.menu-item.current > a {
color: var(--owc-active-color)!important;
}
#sidebar-nav > rocket-navigation > ul > li.menu-item.current > ul > li.menu-item.anchor.current a {
color: var(--owc-active-color)!important;
}
/* Docs styles: */
#sidebar-nav > rocket-navigation > ul > li.menu-item.active > a {
color: var(--owc-active-color)!important;
}
#sidebar-nav > rocket-navigation > ul > li.menu-item.active > ul > li.menu-item.current > a {
color: var(--owc-active-color)!important;
}
#sidebar-nav > rocket-navigation > ul > li.menu-item.active > ul > li.menu-item.current > ul > li.menu-item.anchor.current:before {
background: var(--owc-active-color)!important;
}
#sidebar-nav > rocket-navigation > ul > li.menu-item.active > ul > li.menu-item.current > ul > li.menu-item.anchor.current a {
color: var(--owc-active-color)!important;
}
footer {
border-top: 0px!important;
}
rocket-navigation > ul > li > a {
text-transform: none!important;
}
@media (prefers-color-scheme: dark) {
.markdown-body {
color: var(--primary-text-color);
}
.sidebar-tags .tag {
background: var(--footer-background);
}
}
@media screen and (min-width: 1024px) {
#sidebar {
max-width: 280px;
}
}

140
docs/_assets/variables.css Normal file
View File

@@ -0,0 +1,140 @@
html {
--page-background: white;
--footer-background: rgba(0, 0, 0, 0.02);
--primary-color: rgb(44, 62, 80);
--primary-color-lighter: #449ad7;
--primary-color-darker: #1a5285;
--primary-color-accent: #cee5f6;
--contrast-color-light: #fff;
--contrast-color-dark: #1d3557;
--primary-text-color: #2c3e50;
--primary-lines-color: #ccc;
--markdown-link-color: #2758ff;
--header-color: white;
--owc-active-color: #2758ff;
--owc-hover-color: #436eff;
--button-one: #2758ff;
--button-one-hover: #436eff;
--button-two: black;
--button-two-hover: #444444;
}
/* TODO: markdown Vars */
/* SEE: https://github.com/daKmoR/rocket/pull/18 */
@media (prefers-color-scheme: dark) {
html {
--header-color: #2f3136;
--footer-background: rgba(255, 255, 255, 0.1);
--page-background: #36393e;
--primary-text-color: #eee;
--primary-color: white;
--primary-color-lighter: #449ad7;
--primary-color-darker: #1a5285;
--primary-color-accent: #cee5f6;
--contrast-color-light: #fff;
--contrast-color-dark: #1d3557;
--primary-lines-color: #333;
--owc-active-color: #41ffb0;
--owc-hover-color: #6dffc2;
--button-one: #9b03fe;
--button-one-hover: #a724ff;
--button-two: black;
--button-two-hover: rgb(36, 36, 36);
/* Markdown */
--markdown-octicon-link: var(--primary-text-color);
--markdown-body: #24292e;
--markdown-link-color: #41ffb0;
--markdown-divider-color: #e1e4e8;
--markdown-blockquote-border-color: #dfe2e5;
--markdown-blockquote-color: #90aac7;
--markdown-kbd-background-color: #fafbfc;
--markdown-kbd-border-color: #c6cbd1;
--markdown-kbd-border-bottom-color: #959da5;
--markdown-kbd-color: #444d56;
--markdown-heading-color-6: #6a737d;
--markdown-table-background-color: #fff;
--markdown-table-border-color: #c6cbd1;
--markdown-table-row-odd-background-color: #f6f8fa;
--markdown-code-background-color: rgba(27, 31, 35, 0.05);
--markdown-pre-background-color: rgb(49, 49, 49);
/* syntax */
--markdown-syntax-color: #f8f8f2;
--markdown-syntax-background-color: #2e3440;
--markdown-syntax-atrule-color: #88c0d0;
--markdown-syntax-attr-name-color: #a3be8c;
--markdown-syntax-attr-value-color: #88c0d0;
--markdown-syntax-builtin-color: #a3be8c;
--markdown-syntax-boolean-color: #81a1c1;
--markdown-syntax-class-name-color: #88c0d0;
--markdown-syntax-constant-color: #81a1c1;
--markdown-syntax-char-color: #a3be8c;
--markdown-syntax-deleted-color: #81a1c1;
--markdown-syntax-entity-color: #81a1c1;
--markdown-syntax-function-color: #88c0d0;
--markdown-syntax-inserted-color: #a3be8c;
--markdown-syntax-keyword-color: #81a1c1;
--markdown-syntax-number-color: #b48ead;
--markdown-syntax-operator-color: #81a1c1;
--markdown-syntax-property-color: #81a1c1;
--markdown-syntax-punctuation-color: #81a1c1;
--markdown-syntax-regex-color: #81a1c1;
--markdown-syntax-important-color: #81a1c1;
--markdown-syntax-selector-color: #a3be8c;
--markdown-syntax-symbol-color: #81a1c1;
--markdown-syntax-string-color: #a3be8c;
--markdown-syntax-tag-color: #81a1c1;
--markdown-syntax-url-color: #81a1c1;
--markdown-syntax-variable-color: #81a1c1;
--markdown-syntax-hotkey-selector-color: #d73a49;
--markdown-syntax-keyword-color: #22863a;
--markdown-table-background-color: #212121;
--markdown-table-row-odd-background-color: #515151;
--markdown-table-border-color: #8e8e8e;
--markdown-syntax-background-color: rgb(27, 29, 35);
--markdown-syntax-atrule-color: rgb(198, 120, 221);
--markdown-syntax-attr-name-color: rgb(198, 120, 221);
--markdown-syntax-boolean-color: rgb(209, 154, 102);
--markdown-syntax-class-name-color: rgb(97, 175, 239);
--markdown-syntax-constant-color: rgb(220, 220, 170);
--markdown-syntax-entity-color: rgb(220, 220, 170);
--markdown-syntax-function-color: rgb(97, 175, 239);
--markdown-syntax-inserted-color: rgb(220, 220, 170);
--markdown-syntax-keyword-color: rgb(198, 120, 221);
--markdown-syntax-number-color: rgb(220, 220, 170);
--markdown-syntax-operator-color: rgb(220, 220, 170);
--markdown-syntax-property-color: rgb(220, 220, 170);
--markdown-syntax-punctuation-color: white;
--markdown-syntax-regex-color: rgb(209, 154, 102);
--markdown-syntax-selector-color: rgb(86, 156, 214);
--markdown-syntax-symbol-color: rgb(220, 220, 170);
--markdown-syntax-tag-color: rgb(86, 156, 214);
--markdown-syntax-url-color: rgb(220, 220, 170);
--markdown-syntax-variable-color: rgb(220, 220, 170);
}
.string {
color: rgb(152, 195, 121);
}
.comment {
color: #7d7d7d;
}
.language-css {
--markdown-syntax-string-color: #81a1c1;
}
}

View File

@@ -1,20 +0,0 @@
/* eslint-disable */
/** @typedef {import('./types').SpaOptions} SpaOptions */
const merge = require('deepmerge');
const { createSpaConfig } = require('@open-wc/building-rollup');
/**
* @param {SpaOptions} options
*/
function createMpaConfig(options) {
const userOptions = merge(
{
html: { flatten: false },
},
options,
);
return createSpaConfig(userOptions);
}
module.exports = { createMpaConfig };

View File

@@ -0,0 +1,90 @@
/* eslint-disable import/no-extraneous-dependencies */
const fetch = require('node-fetch');
const baseLibraries = [
{
name: 'lit-element',
package: 'lit-element',
description: 'A simple base class for creating fast, lightweight web components',
url: 'https://lit-element.polymer-project.org/',
},
{
name: 'FAST',
package: '@microsoft/fast-element',
description: 'The adaptive interface system for modern web experiences',
url: 'https://www.fast.design/',
},
{
package: 'atomico',
description:
'Atomico a micro-library for creating webcomponents using only functions, hooks and virtual-dom.',
},
{
package: 'haunted',
description: "React's Hooks API but for standard web components and lit-html or hyperHTML",
},
{
package: 'hybrids',
description: 'The simplest way to create web components with plain objects and pure functions!',
},
{
package: 'hyperhtml-element',
description: 'An extensible class to define hyperHTML based Custom Elements',
},
{
name: 'Joist',
package: '@joist/component',
description:
'A small (~2kb) library to help with the creation of web components and web component based applications',
},
{
name: 'Lightning Web Components',
package: 'lwc',
description: 'A Blazing Fast, Enterprise-Grade Web Components Foundation',
},
{
name: 'preact-custom-element',
package: 'preact-custom-element',
description: 'Generate/register a custom element from a preact component',
},
{
package: 'skatejs',
description:
'Skate is a library built on top of the W3C web component specs that enables you to write functional and performant web components with a very small footprint.',
url: 'https://skatejs.netlify.com/',
},
{
package: 'slimjs',
description:
'a lightweight web component authoring library that provides extended capabilities for components (such as data binding) using es6 native class inheritance',
},
{
package: 'solid-js',
description: 'The Deceptively Simple User Interface Library',
},
{
package: 'stencil',
description: 'web component with typescript and JSX (requires a build step)',
url: 'https://stenciljs.com/',
},
];
module.exports = async function getBaseLibraries() {
const result = await Promise.all(
baseLibraries.map(async lib => {
const pkg = encodeURIComponent(lib.package);
const response = await fetch(`https://api.npmjs.org/downloads/point/last-week/${pkg}`);
const { downloads } = await response.json();
return {
...lib,
downloads,
downloadsFormatted: new Intl.NumberFormat('de-DE').format(downloads),
name: lib.name || lib.package,
url: lib.url || `https://www.npmjs.com/package/${pkg}`,
};
}),
);
return result.sort((a, b) => b.downloads - a.downloads);
};

View File

@@ -0,0 +1,156 @@
const componentLibraries = [
{
name: 'aybolit',
url: 'https://web-padawan.github.io/aybolit/',
description:
'A growing family of elements with default styling for Bootstrap, Bulma, and Material, as well as a solid white-label basis for extending the underlying functionality with your own custom designs.',
},
{
name: 'Auro',
url: 'https://auro.alaskaair.com',
description:
"The Auro Design System and by extension, Auro Web Components are the core components for a new cross-team, multi-platform development strategy to ensure consistency of UI and experience for all of Alaska's customers. Auro is an eco-system of design, core utilities, and web components designed to reduce design and development efforts.",
},
{
name: 'Bolt',
url: 'https://boltdesignsystem.com/',
description:
'A family of web components built with first-class participation in the Twig templating system for PHP in mind. This set is backed by an expansive catalog of usage variants.',
},
{
name: 'Carbon Design System',
url: 'https://github.com/carbon-design-system/carbon-custom-elements',
description:
'Carbon is an open-source design system built by IBM. With the IBM Design Language as its foundation, the system consists of working code, design tools and resources, human interface guidelines, and a vibrant community of contributors.',
},
{
name: 'Clever Components',
url: 'https://github.com/CleverCloud/clever-components',
description:
'A collection of low-level (atoms) and high-level (domain specific) Web Components made by Clever Cloud for its different Web UIs (public and internal).',
},
{
name: 'Elix',
url: 'https://component.kitchen/elix',
description:
'The Elix project aims to create a universal library of all general-purpose user interface patterns commonly found in desktop and mobile UIs, where each pattern is implemented as a well-designed, attractive, high-quality, performant, accessible, localizable, and extensively customizable web component.',
},
{
name: 'FAST',
url: 'https://fast.design',
description:
'FAST is a web component library built by Microsoft. The core, `fast-element`, is a lightweight means to easily build performant and standards-compliant Web Components. `fast-foundation` is a library of Web Component classes, templates, and other utilities built on fast-element intended to be composed into registered web components by design systems.',
},
{
name: 'HelixUI',
url: 'https://helixdesignsystem.github.io/helix-ui/',
description:
'The HelixUI library provides front-end developers a set of reusable CSS classes and HTML Custom Elements that adhere to Helix design standards, as outlined by Rackspace.',
},
{
name: 'Ink Components',
url: 'https://components.ink/',
description:
'Web components for interactive scientific writing, reactive documents and explorable explanations. The Ink Components library can bring your math and science documents to the next level by breathing life into charts, equations, and variables that can be used throughout your application or content.',
},
{
name: 'Io GUI',
url: 'https://io-gui.dev/#page=elements',
description:
'Io is a UI framework for web applications and custom elements. It supports virtual DOM, reactive rendering and data binding. It includes a design system composed of UI elements ranging from simple input fields, to menu systems and responsive layouts. Its unique feature is the ability to create visually complex, yet fast and GPU-optimized elements using WebGL shaders.',
},
{
name: 'Ionic UI Components',
url: 'https://ionicframework.com/docs/components',
description:
'Take advantage of the component system that powers Ionic applications with a large ecosystem to choose from and in-depth usage instructions, no matter the framework you use.',
},
{
name: 'iooxa.dev',
url: 'https://iooxa.dev/',
description:
'The goal of [iooxa.dev](https://iooxa.dev/) is to provide open source tools to promote and enable interactive scientific writing, reactive documents and explorable explanations.',
},
{
name: 'Kor UI',
url: 'https://kor-ui.com/',
description:
'A Design System and complete UI component library built to facilitate the design and development of intuitive, coherent and pleasing applications based on Web technologies (HTML5). It contains 35+ components which are compatible with any framework, form factor, input type and modern browser.',
},
{
name: 'Lightning Web Components by SalesForce',
url: 'https://developer.salesforce.com/docs/component-library/overview/components',
description:
'Lightning Web Components are now the go-to standard for developing applications in the SalesForce ecosystem, and with the power of web components, they can be the basis of applications outside of their ecosystem, too!',
},
{
name: 'Lion Web Components by ING Bank',
url: 'https://github.com/ing-bank/lion',
description:
'Lion web components is a set of highly performant, accessible and flexible Web Components. They provide an unopinionated, white label layer that can be extended to your own layer of components.',
},
{
name: 'Material Web Components',
url:
'https://material-components.github.io/material-components-web-components/demos/index.html',
description:
"Material Design Components from Material Design team themselves. Stay as close as possible to the changing specification with these components from Google's own Material Design team.",
},
{
name: 'Morningstar',
url: 'http://designsystem.morningstar.com/components/component-status.html',
description:
'The Morningstar Design System combines vanilla HTML/CSS with web components in just the right proportions to empower the design and development of wide reaching content and functionality.',
},
{
name: 'Shoelace',
url: 'https://shoelace.style/',
description:
'Shoelace provides a collection of professionally designed, every day UI components built on a framework-agnostic technology. Every web component is built with accessibility and developer experience in mind.',
},
{
name: 'Smart HTML Elements',
url: 'https://www.htmlelements.com',
description:
'With the goal of helping to deliver modern responsive websites and applications that work on any device and are pleasure to use, the Smart HTML Elements collection is one the most feature rich and comprehensive javascript user interface frameworks.',
},
{
name: 'Spectrum Web Components',
url: 'https://opensource.adobe.com/spectrum-web-components/',
description:
"The Spectrum Web Components project is an implementation of Spectrum, Adobes design system. It's designed to work with any web framework — or even without one.",
},
{
name: 'UI5 Web Components',
url: 'https://sap.github.io/ui5-webcomponents/',
description:
"Get the power and flexibility of SAP's UI5 rendering stack with the ergonomics and ease of use of web components, and bring enterprise-grade features, Fiori UX and themeability home to your application.",
},
{
name: 'Vaadin',
url: 'https://vaadin.com/components',
description:
'Vaadin has a comprehensive set of beautifully crafted, performant, and adaptable UI components for modern mobile-first Web apps. They are the ideal building blocks for Progressive Web Applications.',
},
{
name: 'Weightless',
url: 'https://weightless.dev/',
description:
'A lightweight component library featuring a wide array of design concepts. It surfaces a good amount of customizability via comprehensive component and CSS Custom Property APIs with a small footprint, just short of 30KB.',
},
{
name: 'Wired Elements',
url: 'https://wiredjs.com/',
description:
'A set of common UI elements with a hand-drawn, sketchy look. These can be used for wireframes, mockups, or just the fun hand-drawn look.',
},
{
name: 'Zooplus Web Components',
url: 'https://zooplus.github.io/zoo-web-components/',
description: 'A set of web components that implement Z+ shop style guide.',
},
];
module.exports = async function getComponentLibraries() {
return componentLibraries.sort((a, b) => a.name.localeCompare(b.name));
};

49
docs/_data/footer.json Normal file
View File

@@ -0,0 +1,49 @@
[
{
"name": "Discover",
"children": [
{
"text": "Blog",
"href": "/blog/"
},
{
"text": "About",
"href": "/discover/about/"
},
{
"text": "Help and Feedback",
"href": "https://github.com/open-wc/open-wc/issues"
}
]
},
{
"name": "Follow",
"children": [
{
"text": "Github",
"href": "https://github.com/open-wc/open-wc"
},
{
"text": "Twitter",
"href": "https://twitter.com/openwc"
},
{
"text": "Slack",
"href": "/discover/slack/"
}
]
},
{
"name": "Support",
"children": [
{
"text": "Sponsor",
"href": "https://opencollective.com/modern-web"
},
{
"text": "Contribute",
"href": "https://github.com/open-wc/open-wc/blob/master/CONTRIBUTING.md"
}
]
}
]

View File

@@ -1 +0,0 @@
module.exports = 'layout.njk';

View File

@@ -0,0 +1,4 @@
{
"homeLayout": "home",
"newsletter": false
}

View File

@@ -1,22 +1,22 @@
let url = '/';
/* eslint-disable */
switch (process.env.CONTEXT) {
case 'production':
url = process.env.URL;
break;
case 'deploy-preview':
url = process.env.DEPLOY_URL;
break;
case 'branch-deploy':
url = process.env.DEPLOY_PRIME_URL;
break;
default:
break;
}
module.exports = {
name: 'Open Web Components',
shortDesc:
'Open Web Components provides a set of defaults, recommendations and tools to help facilitate your web component project. Our recommendations include: developing, linting, testing, building, tooling, demoing, publishing and automating.',
url,
module.exports = async function () {
return {
name: 'Open Web Components',
description:
'Open Web Components provides a set of defaults, recommendations and tools to help facilitate your Web Component.',
socialLinks: [
{
name: 'GitHub',
url: 'https://github.com/open-wc/open-wc',
},
],
helpUrl: 'https://github.com/open-wc/open-wc/issues',
logoAlt: 'O with occluded right edge to appear also as a C',
iconColorMaskIcon: '#3f93ce',
iconColorMsapplicationTileColor: '#1d3557',
iconColorThemeColor: '#1d3557',
analytics: 'UA-131782693-2',
socialMediaImage: '/_assets/social-media-image.jpg',
};
};

View File

@@ -1,92 +0,0 @@
<!doctype html>
<html lang="en">
<head>
{% include 'partials/meta.njk' %}
<link rel="stylesheet" href="/styles.css">
</head>
<body data-gr-c-s-loaded="true">
<div id="app">
<div class="theme-container no-sidebar">
{% include 'partials/header.njk' %}
<div class="sidebar-mask"></div>
<aside class="sidebar">
<nav class="nav-links">
<div class="nav-item">
<a href="/" class="nav-link router-link-exact-active router-link-active">
Home
</a>
</div>
<div class="nav-item">
<a href="/guide/" class="nav-link">
Guide
</a>
</div>
<div class="nav-item">
<a href="/faq/" class="nav-link">
FAQ
</a>
</div>
<div class="nav-item">
<a href="/about/" class="nav-link">
About
</a>
</div>
<a href="https://github.com/open-wc/open-wc" target="_blank" rel="noopener noreferrer" class="repo-link">
GitHub
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15" class="icon outbound">
<path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path>
<polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon>
</svg>
</a>
</nav>
<!---->
</aside>
<main aria-labelledby="main-title" class="home">
<header class="hero">
<svg viewbox="0 0 244 244" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" alt="open-wc" class="logo">
<use xlink:href="#logo"></use>
</svg>
<h1 id="main-title">open-wc</h1>
<p class="description">
Open Web Component Recommendations
</p>
<clipboard-copy for="homepage-npm-init-command" tabindex="0" role="button">
<code id="homepage-npm-init-command" class="language-bash">npm init @open-wc</code>
</clipboard-copy>
<p class="action">
<a href="/guide/" class="nav-link action-button">
Get Started →
</a>
</p>
</header>
<div class="features">
<div class="feature">
<h2>Smart Defaults</h2>
<p>Enjoy the peace of mind that comes from having a well-known default solution for almost everything. From linting to testing to demos to publishing - have the full experience.</p>
</div>
<div class="feature">
<h2>Awesome Generators</h2>
<p>Get up and running quickly with opinionated generators, or add recommended tools to existing projects. Our comprehensive fleet of generators have got you covered.</p>
</div>
<div class="feature">
<h2>Open Source Love</h2>
<p>Open Web Components is a community-effort, independent of any framework or company. We use mostly open-source tools and services.</p>
</div>
</div>
<div class="theme-default-content custom content__default">
{{ content.html | safe }}
<div class="footer">
MIT Licensed | Copyright © 2018-2020 open-wc
</div>
</main>
</div>
<div class="global-ui"></div>
</div>
{% include "partials/sidebar-script.njk" %}
{% include 'partials/service-worker.njk' %}
</body>
</html>

View File

@@ -1,33 +0,0 @@
<!doctype html>
<html lang="en">
<head>
{% include 'partials/meta.njk' %}
<link rel="stylesheet" href="/styles.css">
</head>
<body data-gr-c-s-loaded="true">
<div id="app">
<div class="theme-container">
{% include 'partials/header.njk' %}
{% include 'partials/sidebar.njk' %}
<main class="page">
<div class="theme-default-content content__default">
{{ content.html | safe }}
</div>
</main>
</div>
<div class="global-ui"></div>
</div>
{% include "partials/sidebar-script.njk" %}
{% if content.jsCode %}
<script type="module">
{{ content.jsCode | safe }}
</script>
{% endif %}
{% include 'partials/service-worker.njk' %}
</body>
</html>

View File

@@ -0,0 +1,6 @@
<div class="content-footer">
<p>
Caught a mistake or want to contribute to the documentation?
<a href="https://github.com/open-wc/open-wc/edit/chore/newSiteWithRocket/{{ page.inputPath }}">Edit this page on GitHub!</a>
</p>
</div>

View File

@@ -1,44 +0,0 @@
<header class="navbar">
<div class="sidebar-button">
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewbox="0 0 448 512" class="icon">
<path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path>
</svg>
</div>
<a href="/index.html" class="home-link router-link-active" aria-label="Open Web Components">
<svg viewbox="0 0 244 244" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" alt="open-wc" class="logo">
<defs>
<lineargradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-1">
<stop stop-color="#9B00FF" offset="0%"></stop>
<stop stop-color="#0077FF" offset="100%"></stop>
</lineargradient>
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" id="logo">
<path d="M205.639259,176.936244 C207.430887,174.217233 209.093339,171.405629 210.617884,168.510161 M215.112174,158.724316 C216.385153,155.50304 217.495621,152.199852 218.433474,148.824851 M220.655293,138.874185 C221.231935,135.482212 221.637704,132.03207 221.863435,128.532919 M222,118.131039 C221.860539,114.466419 221.523806,110.85231 221.000113,107.299021 M218.885321,96.8583653 C218.001583,93.4468963 216.942225,90.1061026 215.717466,86.8461994 M211.549484,77.3039459 C209.957339,74.1238901 208.200597,71.0404957 206.290425,68.0649233 M200.180513,59.5598295 C181.848457,36.6639805 153.655709,22 122.036748,22 C66.7879774,22 22,66.771525 22,122 C22,177.228475 66.7879774,222 122.036748,222 C152.914668,222 180.52509,208.015313 198.875424,186.036326" stroke="url(#linearGradient-1)" stroke-width="42.0804674"></path>
</g>
</defs>
<use xlink:href="#logo"></use>
</svg>
<span class="site-name can-hide">open-wc</span></a>
<div class="links" style="max-width: 1532px;">
<nav class="nav-links can-hide">
<div class="nav-item">
<a href="/index.html" class="nav-link{% if permalink == 'index.html' %} router-link-active{% endif %}">
Home
</a>
</div>
{% for navItem in collections.section %}
<div class="nav-item">
<a href="/{{ navItem.data.permalink }}" class="nav-link{% if section == navItem.data.section %} router-link-active{% endif %}">
{{ navItem.data.title }}
</a>
</div>
{% endfor %}
<a href="https://github.com/open-wc/open-wc" target="_blank" rel="noopener noreferrer" class="repo-link">
GitHub
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15" class="icon outbound">
<path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path>
<polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon>
</svg>
</a>
</nav>
</div>
</header>

View File

@@ -1,34 +0,0 @@
{% set pageTitle = title %}
{% if title != site.name %}
{% set pageTitle = title + ': ' + site.name %}
{% endif %}
{% set currentUrl = site.url + page.url %}
{% set metaDesc = site.shortDesc %}
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ pageTitle }}</title>
<meta name="generator" content="11ty 0.10.0">
<link rel="manifest" href="/manifest.json">
<link rel="icon" type="image/png" href="/favicon.png"/>
<link rel="apple-touch-icon" href="/icons/icon-128x128.png"/>
<meta name="theme-color" content="#127afb"/>
{% if site.url !== '/' %}
<link rel="canonical" href="{{ currentUrl }}"/>
{% endif %}
<meta property="og:site_name" content="{{ site.name }}"/>
<meta property="og:title" content="{{ pageTitle }}"/>
<meta property="og:type" content="website"/>
<meta property="og:image" content="/logo.png">
<meta property="og:url" content="{{ currentUrl }}"/>
<meta name="twitter:creator" content="@OpenWc"/>
<meta name="twitter:card" content="summary">
{% if metaDesc %}
<meta name="description" content="{{ metaDesc }}"/>
<meta name="twitter:description" content="{{ metaDesc }}"/>
<meta property="og:description" content="{{ metaDesc }}"/>
{% endif %}

View File

@@ -1,42 +0,0 @@
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker
.register(`${window.location.pathname.split('/').length - 1 > 1 ? '../service-worker.js' : './service-worker.js' }`)
.then(function() {
console.log('ServiceWorker registered.');
})
.catch(function(err) {
console.log('ServiceWorker registration failed: ', err);
});
});
}
if (sessionStorage.getItem('openItems')) {
const openItems = JSON.parse(sessionStorage.getItem('openItems'));
const menuItems = [...document.querySelectorAll('section > p.sidebar-heading')];
if (openItems.length === menuItems.length) {
menuItems.forEach((item, i) => {
if (openItems[i]) {
item.click();
}
});
sessionStorage.removeItem('openItems');
}
}
let refreshing;
navigator.serviceWorker.addEventListener('controllerchange',
function() {
if (refreshing) return;
refreshing = true;
const openItems = [...document.querySelectorAll('section > p.sidebar-heading')]
.map(item => item.classList.contains('open'));
sessionStorage.setItem('openItems', JSON.stringify(openItems));
window.location.reload();
}
);
</script>

View File

@@ -1,14 +0,0 @@
<ul class="sidebar-links">
<li>
<a href="/about/" class="{% if permalink == 'about/index.html' %}active {% endif %}sidebar-link">About</a>
</li>
<li>
<a href="/about/contact.html" class="{% if permalink == 'about/contact.html' %}active {% endif %}sidebar-link">Contact</a>
</li>
<li>
<a href="/about/rationales.html" class="{% if permalink == 'about/rationales.html' %}active {% endif %}sidebar-link">Rationales</a>
</li>
<li>
<a href="/about/blog.html" class="{% if permalink == 'about/blog.html' %}active {% endif %} sidebar-link">Blog</a>
</li>
</ul>

View File

@@ -1,30 +0,0 @@
<ul class="sidebar-links">
<li>
<a href="/faq/index.html" class="{% if permalink == 'faq/index.html' %}active {% endif %}sidebar-link">Faq</a>
</li>
<li>
<section class="sidebar-group depth-0">
<p class="sidebar-heading open">
<span>Deep dives</span>
<!---->
</p>
<ul class="sidebar-links sidebar-group-items">
<li>
<a href="/faq/events.html" class="{% if permalink == 'faq/events.html' %}active {% endif %}sidebar-link">Managing events in your custom elements</a>
</li>
<li>
<a href="/faq/rerender.html" class="{% if permalink == 'faq/rerender.html' %}active {% endif %}sidebar-link">Rerender not triggered</a>
</li>
<li>
<a href="/faq/unit-testing-custom-events.html" class="{% if permalink == 'faq/unit-testing-custom-events.html' %}active {% endif %}sidebar-link">Unit Testing Events</a>
</li>
<li>
<a href="/faq/unit-testing-init-error.html" class="{% if permalink == 'faq/unit-testing-init-error.html' %}active {% endif %}sidebar-link">Testing for errors thrown in initialization</a>
</li>
<li>
<a href="/faq/lit-element-lifecycle.html" class="{% if permalink == 'faq/lit-element-lifecycle.html' %}active {% endif %}sidebar-link">LitElement lifecycle</a>
</li>
</ul>
</section>
</li>
</ul>

View File

@@ -1,233 +0,0 @@
<ul class="sidebar-links">
<li>
<a href="/guide/" class="{% if page.url == '/guide/' %}active {% endif %}sidebar-link">Introduction</a>
</li>
<li>
<section class="sidebar-group collapsable depth-0">
<p class="sidebar-heading">
<span>Guides &amp; Docs</span>
<span class="arrow right"></span></p>
<ul class="sidebar-links sidebar-group-items" style="">
{% for item in collections.guide %}
<li>
<a href="{{ item.url }}" class="{% if page.url == item.url %}active {% endif %}sidebar-link">{{ item.data.title }}</a>
</li>
{% endfor %}
</ul>
</section>
</li>
<li>
<section class="sidebar-group collapsable depth-0">
<p class="sidebar-heading">
<span>Configs &amp; Recommendations</span>
<span class="arrow right"></span></p>
<ul class="sidebar-links sidebar-group-items" style="">
<li>
<section class="sidebar-group is-sub-group depth-1">
<p class="sidebar-heading open">
<a href="/linting/" class="{% if permalink == 'linting/index.html' %}active {% endif %} sidebar-heading-link">Linting</a>
<!---->
</p>
<ul class="sidebar-links sidebar-group-items">
<li>
<a href="/linting/linting-eslint.html" class="{% if permalink == 'linting/linting-eslint.html' %}active {% endif %}sidebar-link">ESLint</a>
</li>
</ul>
</section>
</li>
<li>
<section class="sidebar-group is-sub-group depth-1">
<p class="sidebar-heading">
<a href="/developing/" class="{% if permalink == 'developing/index.html' %}active {% endif %} sidebar-heading-link">Developing</a>
<!---->
</p>
</section>
</li>
<li>
<section class="sidebar-group is-sub-group depth-1">
<p class="sidebar-heading">
<a href="/testing/" class="{% if permalink == 'testing/index.html' %}active {% endif %} sidebar-heading-link">Testing</a>
<!---->
</p>
<ul class="sidebar-links sidebar-group-items">
<li>
<a href="/testing/testing.html" class="{% if permalink == 'testing/testing.html' %}active {% endif %}sidebar-link">Testing library</a>
</li>
</ul>
</section>
</li>
<li>
<section class="sidebar-group is-sub-group depth-1">
<p class="sidebar-heading">
<a href="/publishing/" class="{% if page.url == '/publishing/' %}active {% endif %}sidebar-heading-link">Publishing Web Components</a>
</p>
</section>
</li>
<li>
<section class="sidebar-group is-sub-group depth-1">
<p class="sidebar-heading">
<a href="/building/" class="{% if permalink == 'building/index.html' %}active {% endif %} sidebar-heading-link">Building</a>
<!---->
</p>
<ul class="sidebar-links sidebar-group-items">
<li>
<a href="/building/building-rollup.html" class="{% if permalink == 'building/building-rollup.html' %}active {% endif %}sidebar-link">Rollup</a>
</li>
</ul>
</section>
</li>
<li>
<section class="sidebar-group is-sub-group depth-1">
<p class="sidebar-heading">
<a href="/deploying/" class="{% if permalink == 'deploying/index.html' %}active {% endif %} sidebar-heading-link">Deploying apps</a>
<!---->
</p>
</section>
</li>
<li>
<section class="sidebar-group is-sub-group depth-1">
<p class="sidebar-heading">
<a href="/demoing/" class="{% if permalink == 'demoing/index.html' %}active {% endif %} sidebar-heading-link">Demoing</a>
<!---->
</p>
</section>
</li>
</ul>
</section>
</li>
<li>
<section class="sidebar-group collapsable depth-0">
<p class="sidebar-heading">
<span>Tools &amp; Libraries</span>
<span class="arrow right"></span></p>
<ul class="sidebar-links sidebar-group-items" style="">
<li>
<section class="sidebar-group is-sub-group depth-1">
<p class="sidebar-heading open">
<span>Developing</span>
</p>
<ul class="sidebar-links sidebar-group-items">
<li>
<a href="/developing/es-dev-server.html" class="{% if permalink == 'developing/es-dev-server.html' %}active {% endif %}sidebar-link">ES dev server</a>
</li>
<li>
<a href="/init/" class="{% if permalink == 'init/index.html' %}active {% endif %}sidebar-link">Generators</a>
</li>
<li>
<a href="/developing/lit-helpers.html" class="{% if permalink == 'developing/lit-helpers.html' %}active {% endif %}sidebar-link">Lit Helpers</a>
</li>
<li>
<a href="/guide/dedupe-mixin.html" class="{% if permalink == 'guide/dedupe-mixin.html' %}active {% endif %}sidebar-link">Dedupe Mixin</a>
</li>
<li>
<a href="/scoped-elements/" class="{% if permalink == 'scoped-elements/index.html' %}active {% endif %}sidebar-link">Scoped Elements</a>
</li>
</ul>
</section>
</li>
<li>
<section class="sidebar-group is-sub-group depth-1">
<p class="sidebar-heading">
<span>Testing</span>
</p>
<ul class="sidebar-links sidebar-group-items">
<li>
<a href="/testing/testing-helpers.html" class="{% if permalink == 'testing/testing-helpers.html' %}active {% endif %}sidebar-link">Testing Helpers</a>
</li>
<li>
<a href="/testing/testing-chai-a11y-axe.html" class="{% if permalink == 'testing/testing-chai-a11y-axe.html' %}active {% endif %}sidebar-link">Chai A11y aXe</a>
</li>
<li>
<a href="/testing/semantic-dom-diff.html" class="{% if permalink == 'testing/semantic-dom-diff.html' %}active {% endif %}sidebar-link">Semantic Dom Diff</a>
</li>
</ul>
</section>
</li>
<li>
<section class="sidebar-group is-sub-group depth-1">
<p class="sidebar-heading">
<span>Building</span>
</p>
<ul class="sidebar-links sidebar-group-items">
<li>
<a href="/building/rollup-plugin-html.html" class="{% if permalink == 'building/rollup-plugin-html.html' %}active {% endif %}sidebar-link">Rollup Plugin HTML</a>
</li>
<li>
<a href="/building/rollup-plugin-polyfills-loader.html" class="{% if permalink == 'building/rollup-plugin-polyfills-loader.html' %}active {% endif %}sidebar-link">Rollup Plugin Polyfills Loader</a>
</li>
<li>
<a href="/building/polyfills-loader.html" class="{% if permalink == 'building/polyfills-loader.html' %}active {% endif %}sidebar-link">Polyfills loader</a>
</li>
</ul>
</section>
</li>
<li>
<section class="sidebar-group is-sub-group depth-1">
<p class="sidebar-heading">
<span>Demoing</span>
</p>
<ul class="sidebar-links sidebar-group-items">
<li>
<a href="/demoing/storybook-addon-markdown-docs.html" class="{% if permalink == 'demoing/storybook-addon-markdown-docs.html' %}active {% endif %}sidebar-link">Storybook Addon: Markdown Docs</a>
</li>
</ul>
</section>
</li>
</ul>
</section>
</li>
<li>
<section class="sidebar-group collapsable depth-0">
<p class="sidebar-heading">
<span>Experiments</span>
<span class="arrow right"></span></p>
<ul class="sidebar-links sidebar-group-items" style="">
<li>
<a href="/mdjs/" class="{% if permalink == 'mdjs/index.html' %}active {% endif %}sidebar-link">Markdown JavaScript (mdjs) Format</a>
</li>
</ul>
</section>
</li>
</ul>
<script>
const groups = [...document.querySelectorAll('.sidebar-group:not(.is-sub-group)')];
groups.map(group => {
group
.querySelector('.sidebar-heading')
.addEventListener('click', e => {
e
.currentTarget
.classList
.toggle('open');
const arrow = e
.currentTarget
.querySelector('.arrow');
arrow
.classList
.toggle('down');
arrow
.classList
.toggle('right');
e
.currentTarget
.nextElementSibling
.classList
.toggle('hidden');
});
const active = group.querySelector('.active');
if (active) {
const arrow = group.querySelector('.arrow');
arrow
.classList
.toggle('down');
arrow
.classList
.toggle('right');
} else {
group
.querySelector('.sidebar-group-items')
.classList
.toggle('hidden');
}
})
</script>

View File

@@ -1,18 +0,0 @@
<script>
document
.querySelector('.sidebar-button')
.addEventListener('click', () => {
document
.querySelector('.theme-container')
.classList
.toggle('sidebar-open');
});
document
.querySelector('.sidebar-mask')
.addEventListener('click', () => {
document
.querySelector('.theme-container')
.classList
.toggle('sidebar-open');
});
</script>

View File

@@ -1,41 +0,0 @@
<div class="sidebar-mask"></div>
<aside class="sidebar">
<nav class="nav-links">
<div class="nav-item">
<a href="/" class="nav-link">
Home
</a>
</div>
<div class="nav-item">
<a href="/guide/index.html" class="nav-link">
Guide
</a>
</div>
<div class="nav-item">
<a href="/faq/index.html" class="nav-link active">
FAQ
</a>
</div>
<div class="nav-item">
<a href="/about/index.html" class="nav-link">
About
</a>
</div>
<a href="https://github.com/open-wc/open-wc" target="_blank" rel="noopener noreferrer" class="repo-link">
GitHub
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15" class="icon outbound">
<path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path>
<polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon>
</svg>
</a>
</nav>
{% if section == 'about' %}
{% include 'partials/sidebar-about.njk' %}
{% endif %}
{% if section == 'faq' %}
{% include 'partials/sidebar-faq.njk' %}
{% endif %}
{% if section == 'guides' %}
{% include 'partials/sidebar-guide.njk' %}
{% endif %}
</aside>

View File

@@ -1,46 +0,0 @@
/* eslint-disable import/no-extraneous-dependencies */
const { mdjsProcess, mdjsProcessPlugins } = require('@mdjs/core');
const plugins = mdjsProcessPlugins.map(pluginObj => {
if (pluginObj.name === 'htmlHeading') {
return {
...pluginObj,
options: {
properties: {
className: ['header-anchor'],
},
content: [{ type: 'text', value: '#' }],
},
};
}
return pluginObj;
});
function eleventyUnified() {
return {
set: () => {},
render: async str => {
const result = await mdjsProcess(str, {
plugins,
});
return result;
},
};
}
const defaultEleventyUnifiedOptions = {
plugins: [],
};
const _eleventy = {
initArguments: {},
configFunction: (eleventyConfig, pluginOptions = {}) => {
const options = {
...defaultEleventyUnifiedOptions,
...pluginOptions,
};
eleventyConfig.setLibrary('md', eleventyUnified(options));
},
};
module.exports = _eleventy;

42
docs/_redirects Normal file
View File

@@ -0,0 +1,42 @@
/guide/ /guides/
/guide/component-libraries.html /guides/community/component-libraries/
/developing/code-examples.html /guides/developing-components/code-examples/
/developing/ /guides/developing-components/getting-started/
/developing/es-dev-server.html /docs/legacy/legacy-projects/
/init/ /guides/developing-components/getting-started/
/codelabs/ /guides/developing-components/codelabs/
/testing/ /guides/developing-components/testing/
/guide/index.html /guides/
/testing/testing-helpers.html /docs/testing/helpers/
/demoing/ /docs/demoing/storybook/
/developing/best-practices.html /guides/knowledge/lit-element/lifecycle/
/building/building-rollup.html /docs/building/rollup/
/index.html /
/faq/events.html /guides/knowledge/events/
/building/ /docs/building/
/mdjs/ /docs/experimental/mdjs/
/scoped-elements/ /docs/development/scoped-elements/
/testing/testing.html /guides/developing-components/testing/
/testing/testing-karma.html /docs/legacy/legacy-projects/
/developing/lit-html.html /guides/developing-components/getting-started/
/publishing/ /guides/developing-components/publishing/
/developing/lit-helpers.html /docs/development/lit-helpers/
/developing/ide.html /guides/tools/ide/
/building/rollup-plugin-html.html /docs/legacy/legacy-projects/
/faq/rerender.html /guides/knowledge/lit-element/rendering/
/faq/unit-testing-custom-events.html /guides/knowledge/events/
/guide/dedupe-mixin.html /docs/development/dedupe-mixin/
/faq/index.html /guides/knowledge/lit-element/lifecycle/
/testing/semantic-dom-diff.html /docs/testing/semantic-dom-diff/
/linting/ /guides/tools/linting-and-formatting/
/building/polyfills-loader.html /docs/legacy/legacy-projects/
/about/index.html /discover/about/
/demoing/storybook-addon-markdown-docs.html /docs/legacy/legacy-projects/
/testing/testing-chai-a11y-axe.html /docs/testing/chai-a11y-axe/
/linting/linting-eslint.html /guides/tools/linting-and-formatting/
/testing/karma-esm.html /docs/legacy/legacy-projects/
/faq/lit-element-lifecycle.html /guides/knowledge/lit-element/lifecycle/
/about/ /discover/about/
/faq/ /guides/knowledge/lit-element/lifecycle/
/building/rollup-plugin-polyfills-loader.html /docs/legacy/legacy-projects/
/testing/testing-karma-bs.html /docs/legacy/legacy-projects/

View File

@@ -1,33 +0,0 @@
---
permalink: 'about/blog.html'
title: Blog
section: about
tags:
- about
---
# Blog
## [Announcing Open Web Components](https://dev.to/thepassle/announcing-open-web-components-5h7)
by [open-wc](https://github.com/open-wc)
## [Cross Browser Testing your Stencil Web Component like a boss](https://medium.com/vincent-ogloblinsky/cross-browser-testing-your-stencil-web-component-like-a-boss-93c1b154bfd9)
by [Vincent Ogloblinsky](https://github.com/vogloblinsky)
## [npm Convos: open-wc](https://blog.npmjs.org/post/182917093835/npm-convos-open-wc?utm_campaign=newsletter20190228&utm_source=newsletter_mailer&utm_medium=email&utm_source=npm+Weekly&utm_campaign=72f3f1c998-npm_weekly_186_2019_02_26_08_58&utm_medium=email&utm_term=0_e17fe5d778-72f3f1c998-303236901)
by [npm](https://npmjs.com)
## [Not Another To Do App](https://medium.com/@westbrook/not-another-to-do-app-169c14bb7ef9)
by [Westbrook Johnson](https://github.com/westbrook)
## [Web Components: spoils of the browser wars](https://www.andrewzigler.com/blog/2019/02/28/web-components-spoils-of-the-browser-wars/)
by [Andrew Zigler](https://github.com/azigler)
## [Shared Behaviors best practices with Mocha](https://dev.to/open-wc/shared-behaviors-best-practices-with-mocha-519d)
by [Noël Macé](https://github.com/noelmace)

View File

@@ -1,15 +0,0 @@
---
permalink: 'about/contact.html'
title: Contact
section: about
tags:
- about
---
# Contact
Feel free to reach out to us on [twitter](https://twitter.com/OpenWc) or create [a github issue](https://github.com/open-wc/open-wc/issues/new) for any feedback or questions you might have.
You can also find us on the Polymer slack in the [#open-wc](https://slack.com/share/IUQNUPWUF/awabyN8iYH4dXX6aGpu16ES9/enQtOTc2Nzc2ODEyOTY3LWM5ZGExNGFiMmM4NDY2YWI2MzYwOGY5ZTNlZjk4OGU4NTFhMGJjNmVhNGI4MzVlNTMwNGRiNGIxNjc4MGJhNDg) channel.
You can join the Polymer slack by visiting [this link](https://www.polymer-project.org/slack-invite).

View File

@@ -1,75 +0,0 @@
---
permalink: 'about/rationales.html'
title: Rationales
section: about
tags:
- about
---
# Rationales
Our recommendations must fulfill certain criteria before we publish them.
1. Language or platform features/APIs must be released without a flag in the stable version of at least one browser
1. Libraries must offer an ES module version
**Note**: <a id="bare-specifiers"></a>We currently have ONE exception to this rule and that is 'bare modules'.
This is such a powerful and widely-used pattern in the current JavaScript ecosystem, that if you don't use it you basically need to implement everything yourself.
We want to help you build your apps as easily and efficiently as possible, so for now we've adopted this practice as our only exception to the above rules.
The [import maps](https://github.com/WICG/import-maps) proposal aims to bring bare modules to the web browser. You can follow that repository to stay up to date.
## Workflows
We recommend implementing the following three workflows for specific tasks while developing your component or application.
You are encouraged to freely switch between them depending on what you are working on.
### Development Workflow
The ideal development environment uses no tools, just an up-to-date browser and a simple HTTP server.
<div class="custom-block warning"><p class="custom-block-title">WARNING</p> <p>Unfortunately we are not fully there yet, because of the <a href="#bare-specifiers">bare modules exception</a> you will still need to have a server that at least supports them.
We recommend our <a href="../developing/es-dev-server.html" class="">ES Dev Server</a> as it does nothing more/nothing less.</p></div>
When would you choose this workflow:
- When developing new features
Why would you choose this:
- Simple to setup
- As fast as it gets
### Bundling Workflow
Chances are that the web components you're building will need to run in more than just the latest browser.
In these cases it's time to open your toolbox and make sure everything works in all supported browsers.
When would you choose this workflow:
- To verify everything works in all supported browsers
- To debug legacy browsers
We recommend our [Open Web Components Building Setup](../building).
Why would you choose it:
- As good as it gets when you need to work with legacy browsers
- Auto-bundling/reloading
### Production Workflow
Once you're happy with your web components, it's time to put them somewhere more useful.
Most likely a publicly available web server.
Before you do that let's apply all the optimizations magic we can cook up.
When would you use this:
- Optimize your build
- Publishing to production
We recommend our [Open Web Components Building Setup](../building).
Why would you choose it:
- Multiple bundles for differential serving
- Conditional auto-loader for those bundles (without a special server)

View File

@@ -1,47 +0,0 @@
---
permalink: 'automating/index.html'
title: Automating
section: guides
tags:
- guides
---
# Automating
Having continuous integration in your project can provide valuable insights, and we consider it an essential in your projects.
## Circle ci
If you use the default generator you will already have CircleCi setup with a .circleci folder.
It also contains a config that takes care of linting and testing.
<div class="custom-block tip"><p class="custom-block-title">Info</p> <p>This is part of the default <a href="https://open-wc.org/" target="_blank" rel="noopener noreferrer">open-wc<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a> recommendation.</p></div>
## Setup
```bash
npm init @open-wc automating
```
### Manual Setup
- copy [.circleci/config.yml](https://github.com/open-wc/open-wc/blob/master/packages/create/src/generators/tools-circleci/templates/static/.circleci/config.yml) to `.circleci/config.yml`
## Usage
- Register via [https://circleci.com/signup/](https://circleci.com/signup/)
- Use Sign Up with Github and select your user
- If you already have an account, simply login
- In the sidebar click "Add Projects"
- Click "Set up Project"
- Config is already present so you can simply click "Start building"
## Run Tests via Browserstack
- If you do not have an account yet, please create a Browserstack account
- For open source projects you can request a sponsorhip (like we have for open-wc) => You only need to add the logo + link to browserstack and write an E-Mail.
- Go to [https://www.browserstack.com/accounts/settings](https://www.browserstack.com/accounts/settings)
- Look for "Automate", and write down the "Access Key" and "Username"
- Open your [circleci App](https://circleci.com/dashboard) or direclty via https://circleci.com/gh/{groupname}/{reponame}/edit
- Go to the project settings -> Environment Variables
- Add Variable: BROWSER_STACK_USERNAME + \${username from url above}
- Add Variable: BROWSER_STACK_ACCESS_KEY + \${key from url above}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -0,0 +1,176 @@
---
title: 'Announcing Open Web Components'
pageTitle: 'Announcing Open Web Components'
date: 2019-02-11
published: true
description: 'Open Web Components provides a set of defaults, recommendations and tools to help facilitate your Web Component'
tags: [webcomponents, lithtml, litelement, javascript]
canonical_url: https://open-wc.org/blog/announcing-open-web-components/
cover_image: /blog/announcing-open-web-components/images/blog-header.jpg
socialMediaImage: /blog/announcing-open-web-components/images/social-media-image.jpg
---
Hi all! 👋
We are `open-wc`, a collective of open source and web component enthusiasts. We consider it our goal to empower everyone with a powerful and battle-tested setup for creating and sharing open source web components.
Many web developers have experienced the dreaded "Javascript Fatigue". With our recommendations, we hope you'll enjoy the peace of mind that comes from having a well-known default solution for almost everything. From IDE to CI, open-wc has got you covered.
We want web component development to be accessible and approachable for everyone, regardless of your background or previous experience. Therefore, our recommendations aim to be easy to use, be ready to use, and provide that "it just works" developer experience we all crave for the various aspects of web component development.
We strongly believe that staying close to browser standards will be the best long term investment for your code. It is the basis for all our recommendations, and it means that sometimes we will not recommend a popular feature or functionality. It also means we can be faster to adopt and recommend new browser standards.
## Why Web Components
In the last several years, the component-based model for web application development was popularized, and the JavaScript community blossomed with a wide variety of libraries and approaches. Work on standardizing the webs native component model began at Google in 2012, and after several years of open development, was successfully implemented across all major browsers in 2019. At the time of writing, over 10% of all page views contain web components.
We believe web components provide a standards-based solution to problems like reusability, interopability, and encapsulation. Furthermore, we believe using the browser's native component model will increase the longevity of your application. The web has an extremely strong tradition of backwards-compatibility, as standards bodies have consistently gone out of their way to maintain legacy APIs.
At `open-wc` you'll find anything you might need to get started developing web components.
## Developing
In our [developing](https://open-wc.org/developing/) section you'll find anything you need to know about writing your code; from practical code demos, to accessibility, to mixins, to tutorials and blog posts.
We have a wide range of interactive demo's available to help you get started writing code quickly:
[![demos](https://i.imgur.com/CHs2d9a.png)](https://open-wc-lit-demos.stackblitz.io/)
If you want to learn more about web components first, we recommend the following blog posts to give you a solid basis to start developing web components:
- [Lets Build Web Components!](https://dev.to/bennypowers/lets-build-web-components-part-1-the-standards-3e85) by Benny Powers
- [Web Components: from Zero to Hero](https://dev.to/thepassle/web-components-from-zero-to-hero-4n4m) by Pascal Schilp
## Testing
Not only do we provide a [testing setup](https://open-wc.org/testing/testing-karma.html) with Karma, Browserstack, and Wallaby, we also provide a set of testing helpers that help you:
### Make fixtures:
![fixtures](https://i.imgur.com/ettoUME.png)
```js
CAPTION: code snippet
import { html, fixture } from '@open-wc/testing-helpers';
it('can instantiate an element with properties', async () => {
const el = await fixture(html`<my-el .foo=${'bar'}></my-el>`);
expect(el.foo).to.equal('bar');
}
```
### Compare DOM:
![compare-dom](https://i.imgur.com/pjGezjL.png)
```js
CAPTION: code snippet
import { html, fixture } from '@open-wc/testing-helpers';
it('has the following dom', async () => {
const el = await fixture(`<div><!-- comment --><h1>${'Hey'} </h1> </div>`);
expect(el).dom.to.equal('<div><h1>Hey</h1></div>');
});
```
### Manage timings:
![timing](https://i.imgur.com/iyE0IKf.png)
```js
CAPTION: code snippet
import { nextFrame, aTimeout, html, fixture } from '@open-wc/testing-helpers';
const el = await fixture(html`<my-el .foo=${'bar'}></my-el>`);
el.foo = 'baz';
await nextFrame();
expect(el.shadowRoot.querySelector('#foo').innerText).to.equal('baz');
```
### Define multiple custom elements:
![definece](https://i.imgur.com/lHUO7BO.png)
```js
CAPTION: code snippet
import { fixture, defineCE } from '@open-wc/testing-helpers';
const tag = defineCE(class extends MyMixin(HTMLElement) {
constructor() {
super();
this.foo = true;
}
});
const el = await fixture(`<${tag}></${tag}>`);
expect(el.foo).to.be.true;
```
## App Starter
Many developers have experienced what has often been described as "JavaScript Fatigue", the overwhelming feeling of having to keep up with every new technology in the JavaScript ecosystem. JavaScript tooling can have an intimidating learning curve and can often be frustrating to configure. What are the right tools? Which tools should I be using? How do these tools work?
Our recommendations aim to relieve you some of that painful setup so you can skip right to the fun part; development. So if you want to get straight to developing, with a powerful setup that leverages the best of browser standards in no time, our [open-wc-app-starter](https://github.com/open-wc/open-wc-starter-app) might be for you!
[![app-starter](https://i.imgur.com/HMiq3b4.png)](https://github.com/open-wc/open-wc-starter-app)
Live demo [here](https://open-wc-starter-app.netlify.com/).
Our open-wc-app-starter will set you up with a full configuration, with the following features:
- **Module resolution**
- **Automatic module type selection**
- **HTML, JS and CSS minifications**
- **es2015 and es5 output** - Using [webpack-babel-multi-target-plugin](https://www.npmjs.com/package/webpack-babel-multi-target-plugin), our build outputs an es5 and es2015 version of your app. Using the [nomodule trick](https://jakearchibald.com/2017/es-modules-in-browsers/), we can serve es2015 code on modern browsers and es5 on older browsers (IE11 specifically). This significantly reduces the size of your app on modern browsers.
- **No regenerator runtime / transform**
- **Polyfills by usage** - Language polyfills are added based on browser support and usage. This leads to a significantly smaller initial bundle of your app.
- **Syntax and javascript APIs** - Our config only supports standard javascript syntax and browser APIs. We support stage 3 proposals when they add significant value and are easy to support without major performance penalties. Some of the proposals we support are: - Dynamic import - import.meta.url
- **Testing suite with Karma**
- **Linting with ESLint, Prettier and commitlint**
You can find more documentation on our `open-wc-app-starter` [here](https://github.com/open-wc/open-wc-starter-app). We try to provide the best, user friendly set up available and your feedback is extremely valuable to us, so if you feel like anything is missing or you have any kind of feedback, please feel free to create an issue on our repo.
## And much, much more
Other recommendations include anything betwixt and between: [linting](https://open-wc.org/linting/), [demoing](https://open-wc.org/demoing/), [building](https://open-wc.org/building/), [publishing](https://open-wc.org/publishing/) and [automating](https://open-wc.org/automating/). We also have a fleet of [generators](https://open-wc.org/developing/generator) to plug and play any of our setups in your current project.
If you're interested in learning more about our philosophy and the rationale for our recommendations, you can do so [here](https://open-wc.org/about/).
It is our goal to help you get set up as quickly, and effortlessly as possible. If you feel like our recommendations are missing something, feel free to contact us. Please note that our recommendations and best practices are subject to change and may evolve over time.
## Join the conversation!
We'd love to hear any feedback or questions you might have. You can reach us at:
- Feel free to open an issue on our [Github](https://github.com/open-wc/open-wc) if you have a question or feedback.
- You can also find us on the Polymer slack in the [#open-wc](https://polymer.slack.com/messages/CE6D9DN05) channel.
You can join the Polymer slack by visiting [this link](https://join.slack.com/t/polymer/shared_invite/enQtNTAzNzg3NjU4ODM4LTkzZGVlOGIxMmNiMjMzZDM1YzYyMzdiYTk0YjQyOWZhZTMwN2RlNjM5ZDFmZjMxZWRjMWViMDA1MjNiYWFhZWM).
- You can find our recommendations and documentation over at: [open-wc](https://open-wc.org).
You can also find some of us on twitter: [BennyP](https://twitter.com/PowersBenny), [daKmoR](https://twitter.com/daKmoR), [passle](https://twitter.com/passle_)
<br>
<p align="center">
🚽 Made with love by <a href="https://github.com/open-wc/open-wc">open-wc</a>. </p>

19
docs/blog/index.md Normal file
View File

@@ -0,0 +1,19 @@
---
title: Open Web Components Blog
pageTitle: Open Web Components Blog
layout: blog-overview.njk
eleventyNavigation:
key: Blog
order: 30
pagination:
data: collections.blog
size: 10
reverse: true
alias: posts
---
Discover articles from the core team and contributors about Open Web Components, tips, and tricks included!
Do you want to write a blog post, or want us to write about a specific topic? [Suggest it!](https://github.com/open-wc/open-wc/issues/new?title=[blog%20post]%20Write%20about%20...&labels=blog%20post&body=I%20would%20like%20to%20write%20about...)
_Note: blog posts may age while our guides & docs section will always be up to date_

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -0,0 +1,971 @@
---
title: 'Testing Workflow for Web Components'
pageTitle: 'Testing Workflow for Web Components'
published: true
description: 'Learn to test and debug your web component with open-wc tools so you can deploy or share your production-ready code with confidence.'
tags: [webcomponents, javascript, testing, karma]
date: 2019-05-09
canonical_url: https://open-wc.org/blog/testing-workflow-for-web-components/
cover_image: /blog/testing-workflow-for-web-components/images/blog-header.jpg
socialMediaImage: /blog/testing-workflow-for-web-components/images/social-media-image.jpg
---
Whenever you ship something to be used by others, you take on a responsibility to deliver safe and stable code. One way to address this is by testing your code.
No matter how small - no matter how simple your project, there should always ideally be tests.
> Yes, I know reality hits hard and there will be many cases where that doesn't happen - but you should always strive to have tests
### Disclaimer
In this tutorial, we're going to make a simple version of an input element. By the end of it, you'll gain the skills and knowledge to put open-wc testing tools to practice; and build a solid, accessible, and well-tested input component.
### Warning
This is an in-depth tutorial showing a few pitfalls and tough cases when working with web components. This is definitely for more advanced users. You should have a basic knowledge about [LitElement](https://lit-element.polymer-project.org/) and [JSDoc Types](https://dev.to/dakmor/type-safe-web-components-with-jsdoc-4icf). Having an idea what [Mocha](https://mochajs.org/), [Chai BDD](https://www.chaijs.com/api/bdd/), [Karma](https://karma-runner.github.io/latest/index.html) is might help a little as well.
> We are thinking about posting an easier digestible version of this so if that is something you would like to see - let us know in the comments.
If you want to play along - all the code is on [github](https://github.com/daKmoR/testing-workflow-for-web-components).
## Let's Get Started!
Run in your console
```bash
$ npm init @open-wc
# Results in this flow
✔ What would you like to do today? Scaffold a new project
✔ What would you like to scaffold? Web Component
# Select with space! "Testing" => just enter will move one with no selection
✔ What would you like to add? Testing
✔ Would you like to scaffold examples files for? Testing
✔ What is the tag name of your application/web component? … a11y-input
✔ Do you want to write this file structure to disk? Yes
Writing..... done
✔ Do you want to install dependencies? No
```
For more details please see [https://open-wc.org/testing/](https://open-wc.org/testing/).
Delete `src/A11yInput.js`
Modify `src/a11y-input.js` to:
```js
import { LitElement, html, css } from 'lit-element';
export class A11yInput extends LitElement {}
customElements.define('a11y-input', A11yInput);
```
and `test/a11y-input.test.js` to:
```js
/* eslint-disable no-unused-expressions */
import { html, fixture, expect } from '@open-wc/testing';
import '../src/a11y-input.js';
/**
* @typedef {import('../src/a11y-input.js').A11yInput} A11yInput
*/
describe('a11y input', () => {
it('has by default an empty string as label', async () => {
const el = /** @type {A11yInput} */ (await fixture('<a11y-input></a11y-input>'));
expect(el.label).to.equal('');
});
});
```
Our tests so far consist of a single feature (the `label` property) and a single assertion (`expect`). We're using karma and chai's <abbr title="behaviour/business-driven-development">BDD</abbr> syntax, so we group sets of tests (`it`) under the features or APIs they relate to (`describe`).
Let's see if everything works correctly by running: `npm run test`.
```
SUMMARY:
✔ 0 tests completed
✖ 1 test failed
FAILED TESTS:
a11y input
✖ has by default an empty string as label
HeadlessChrome 73.0.3683 (Windows 10.0.0)
AssertionError: expected undefined to equal ''
+ expected - actual
-[undefined]
+""
```
Awesome - just as expected (🥁), we have a failing test :)
Let's switch into watch mode, which will run the tests continuously whenever you make changes to your code.
`npm run test:watch`
![01-watch-mode-intro](./images/01-watch-mode-intro.gif)
The following code was added in the video above to `src/a11y-input.js`:
```js
static get properties() {
return {
label: { type: String },
};
}
constructor() {
super();
this.label = '';
}
```
So far, so good? Still with us? Great! Let's up the game a little...
### Adding a Test for Shadow DOM
Let's add an assertion to test the contents of our element's shadow root.
If we want to be sure that our element behaves/looks the same we should make sure its dom structure remains the same.
So let's compare the actual shadow dom to what we want it to be.
```js
it('has a static shadowDom', async () => {
const el = /** @type {A11yInput} */ (await fixture(html` <a11y-input></a11y-input> `));
expect(el.shadowRoot.innerHTML).to.equal(`
<slot name="label"></slot>
<slot name="input"></slot>
`);
});
```
As expected, we get:
```
✖ has a static shadowDom
AssertionError: expected '' to equal '\n <slot name="label"></slot>\n <slot name="input"></slot>\n '
+ expected - actual
+
+ <slot name="label"></slot>
+ <slot name="input"></slot>
+
```
So let's implement that in our element.
```js
render() {
return html`
<slot name="label"></slot>
<slot name="input"></slot>
`;
}
```
Interesting, the test should be green... but it's not :thinking: Let's take a look.
```
✖ has a static shadowDom
AssertionError: expected '<!---->\n <slot name="label"></slot>\n <slot name="input"></slot>\n <!---->' to equal '\n <slot name="label"></slot>\n <slot name="input"></slot>\n '
+ expected - actual
-<!---->
- <slot name="label"></slot>
- <slot name="input"></slot>
- <!---->
+
+ <slot name="label"></slot>
+ <slot name="input"></slot>
+
```
You may have noticed those weird empty comment `<!---->` tags. They are markers that `lit-html` uses to remember where dynamic parts are, so it can be updated efficiently. For testing, however, this can be a little annoying to deal with.
If we use `innerHTML` to compare the DOM, we'd have to rely on simple string equality. Under those circumstances, we'd have to exactly match the generated DOM's whitespace, comments, etc; in other words: it will need to be a perfect match. Really all we need to test is that the elements we want to render are rendered. We want to test the _semantic_ contents of the shadow root.
Fortunately, we've got you covered. If you're using `@open-wc/testing` then it automatically loads the `@open-wc/semantic-dom-diff` chai plugin for us to use.
So let's try it out :muscle:
```js
// old:
expect(el.shadowRoot.innerHTML).to.equal(`...`);
// new:
expect(el).shadowDom.to.equal(`
<slot name="label"></slot>
<slot name="input"></slot>
`);
```
Bam :tada:
```
a11y input
✔ has by default an empty string as a label
✔ has a static shadowDom
```
### How does shadowDom.to.equal() work?
1. It gets the `innerHTML` of the shadow root
2. Parses it (actually, the browser parses it - no library needed)
3. Normalizes it (potentially every tag/property on its own line)
4. Parses and normalizes the expected HTML string
5. Passes both normalized DOM strings on to chai's default compare function
6. In case of failure, groups, and displays any differences in a clear manner
If you want to know more, please check out the documentation of [semantic-dom-diff](https://open-wc.org/testing/semantic-dom-diff.html).
### Testing the "Light" DOM
We can do exactly the same thing with the light DOM. (The DOM which will be provided by our user or our defaults, i.e. the element's `children`).
```js
it('has 1 input and 1 label in light-dom', async () => {
const el = /** @type {A11yInput} */ (await fixture(html`
<a11y-input .label=${'foo'}></a11y-input>
`));
expect(el).lightDom.to.equal(`
<label slot="label">foo</label>
<input slot="input">
`);
});
```
And let's implement it.
```js
connectedCallback() {
super.connectedCallback();
this.labelEl = document.createElement('label');
this.labelEl.innerText = this.label;
this.labelEl.setAttribute('slot', 'label');
this.appendChild(this.labelEl);
this.inputEl = document.createElement('input');
this.inputEl.setAttribute('slot', 'input');
this.appendChild(this.inputEl);
}
```
So we tested our light and shadow dom :muscle: and our tests run clean :tada:
> Note: Using the DOM API in a lit-element's lifecycle is an anti-pattern, however to allow for <abbr title="accessibility">a11y</abbr> it might be a real use case - anyways it's great for illustration purposes
### Using Our Element in an App
So now that we have a basic a11y input let's use it - and test it - in our application.
Again we start with a skeleton `src/my-app.js`
```js
/* eslint-disable class-methods-use-this */
import { LitElement, html, css } from 'lit-element';
export class MyApp extends LitElement {}
customElements.define('my-app', MyApp);
```
And our test in `test/my-app.test.js`;
```js
/* eslint-disable no-unused-expressions */
import { html, fixture, expect } from '@open-wc/testing';
import '../src/my-app.js';
/**
* @typedef {import('../src/my-app.js').MyApp} MyApp
*/
describe('My Filter App', () => {
it('has a heading and a search field', async () => {
const el = /** @type {MyApp} */ (await fixture(html` <my-app .label=${'foo'}></my-app> `));
expect(el).shadowDom.to.equal(`
<h1>My Filter App</h1>
<a11y-input></a11y-input>
`);
});
});
```
Run the test => fails and then we add the implementation to `src/a11y-input.js`
```js
render() {
return html`
<h1>My Filter App</h1>
<a11y-input></a11y-input>
`;
}
```
But oh no! That should be green now...
```
SUMMARY:
✔ 3 tests completed
✖ 1 test failed
FAILED TESTS:
My Filter App
✖ has a heading and a search field
AssertionError: expected '<h1>\n My Filter App\n</h1>\n<a11y-input>\n <label slot="label">\n </label>\n <input slot="input">\n</a11y-input>\n' to equal '<h1>\n My Filter App\n</h1>\n<a11y-input>\n</a11y-input>\n'
+ expected - actual
<h1>
My Filter App
</h1>
<a11y-input>
- <label slot="label">
- </label>
- <input slot="input">
</a11y-input>
```
What is happening?
Do you remember that we had a specific test to ensure the light-dom of a11y-input?
So even if the users only puts `<a11y-input></a11y-input>` in his code - what actually comes out is
```html
<a11y-input>
<label slot="label"></label>
<input slot="input" />
</a11y-input>
```
e.g. `a11y-input` is actually creating nodes inside of your `my-app` shadow dom. Preposterous! For our example here we say that's what we want.
So how can we still test it?
Luckily `.shadowDom` has another ace up its sleeve; it allows us to ignore parts of dom.
```js
expect(el).shadowDom.to.equal(
`
<h1>My Filter App</h1>
<a11y-input></a11y-input>
`,
{ ignoreChildren: ['a11y-input'] },
);
```
We can even specify the following properties as well:
- `ignoreChildren`
- `ignoreTags`
- `ignoreAttributes` (globally or for specific tags)
For more details please see [semantic-dom-diff](https://open-wc.org/testing/semantic-dom-diff.html).
#### Snapshot testing
If you have a lot of big dom trees writing/maintaining all those manually written expects is going to be tough.
To help you with that there are semi/automatic snapshots.
So if we change our code
```js
// from
expect(el).shadowDom.to.equal(`
<slot name="label"></slot>
<slot name="input"></slot>
`);
// to
expect(el).shadowDom.to.equalSnapshot();
```
If we now execute `npm run test` it will create a file `__snapshots__/a11y input.md` and fill it with something like this
````
## `a11y input`
#### `has a static shadowDom`
```html
<slot name="label">
</slot>
<slot name="input">
</slot>
````
````
What we wrote before by hand can now be auto-generated on init or forcefully via `npm run test:update-snapshots`.
If the file `__snapshots__/a11y input.md` already exists it will compare it with the output and you will get errors if your html output changed.
```
FAILED TESTS:
a11y input
✖ has a static shadowDom
HeadlessChrome 73.0.3683 (Windows 10.0.0)
AssertionError: Received value does not match stored snapshot 0
+ expected - actual
-<slot name="label-wrong">
+<slot name="label">
</slot>
<slot name="input">
-</slot>
+</slot>
```
For more details please see [semantic-dom-diff](https://open-wc.org/testing/semantic-dom-diff.html).
I think that's now enough about comparing dom trees...
It's time for a change :hugs:
### Code coverage
Another useful metric we get when testing with the open-wc setup is code coverage.
So what does it mean and how can we get it? [Code coverage](https://www.wikiwand.com/en/Code_coverage) is a measure of *how much* of our code is checked by tests. If there's a line, statement, function, or branch (e.g. `if`/`else` statement) that our tests don't cover our coverage score will be affected.
A simple `npm run test` is all we need and you will get the following:
```
=============================== Coverage summary ===============================
Statements : 100% ( 15/15 )
Branches : 100% ( 0/0 )
Functions : 100% ( 5/5 )
Lines : 100% ( 15/15 )
================================================================================
```
Which means that 100% of our code's statements, branches, functions, and lines are covered by tests. Pretty neat!
So let's go the other way and add code to `src/a11y-input.js` before adding a test. Let's say we want to access the value of our input directly via our custom element and whenever its value is 'cat' we want to log something.
```js
get value() {
return this.inputEl.value;
}
set value(newValue) {
if (newValue === 'cat') {
console.log('We like cats too :)');
}
this.inputEl.value = newValue;
}
```
It's a vastly different result
```
SUMMARY:
✔ 4 tests completed
TOTAL: 4 SUCCESS
=============================== Coverage summary ===============================
Statements : 81.82% ( 18/22 )
Branches : 0% ( 0/2 )
Functions : 75% ( 6/8 )
Lines : 81.82% ( 18/22 )
================================================================================
06 04 2019 10:40:45.380:ERROR [reporter.coverage-istanbul]: Coverage for statements (81.82%) does not meet global threshold (90%)
06 04 2019 10:40:45.381:ERROR [reporter.coverage-istanbul]: Coverage for lines (81.82%) does not meet global threshold (90%)
06 04 2019 10:40:45.381:ERROR [reporter.coverage-istanbul]: Coverage for branches (0%) does not meet global threshold (90%)
06 04 2019 10:40:45.381:ERROR [reporter.coverage-istanbul]: Coverage for functions (75%) does not meet global threshold (90%)
```
Our coverage is way lower than before. Our test command even fails, even though all the tests run successfully.
This is because by default open-wc's config sets a 90% threshold for code coverage.
If we want to improve coverage we need to add tests - so let's do it
```js
it('can set/get the input value directly via the custom element', async () => {
const el = /** @type {A11yInput} */ (await fixture(html`
<a11y-input .value=${'foo'}></a11y-input>
`));
expect(el.value).to.equal('foo');
});
```
uh oh :scream: we wanted to improve coverage but now we need fix an actual bug first :disappointed:
```
FAILED TESTS:
a11y input
✖ can set/get the input value directly via the custom element
TypeError: Cannot set property 'value' of null at HTMLElement.set value [as value]
// ... => long error stack
```
That was unexpected... on first glance, I don't really know what that means... better to check some actual nodes and inspect them in the browser.
### Debugging in the Browser
When we run our test with watch, karma sets up a persistent browser environment to run tests in.
- Be sure you started with `npm run test:watch`
- visit [http://localhost:9876/debug.html](http://localhost:9876/debug.html)
You should see something like this
![02-debugging-in-browser](./images/02-debugging-in-browser.png)
You can click on the circled play button to only run one individual test.
So let's open the Chrome Dev Tools (F12) and put a debugger in the test code.
```js
it('can set/get the input value directly via the custom element', async () => {
const el = /** @type {A11yInput} */ (await fixture(html`
<a11y-input .value=${'foo'}></a11y-input>
`));
debugger;
expect(el.value).to.equal('foo');
});
```
Dang.. the error happens even before that point...
"Fatal" errors like this are a little tougher as they are not failing tests but sort of a complete meltdown of your full component.
Ok, let's put some code in the `setter` directly.
```js
set value(newValue) {
debugger;
```
Alright, that worked so our chrome console we write `console.log(this)` let's see what we have here
```js
<a11y-input>
#shadow-root (open)
</a11y-input>
```
Ahh there we have it - the shadow dom is not yet rendered when the setter is called.
So let's be safe and add a check before
```js
set value(newValue) {
if (newValue === 'cat') {
console.log('We like cats too :)');
}
if (this.inputEl) {
this.inputEl.value = newValue;
}
}
```
Fatel error is gone :tada:
But we now have a failing test :sob:
```
✖ can set/get the input value directly via the custom element
AssertionError: expected '' to equal 'foo'
```
We may need a change of tactic :thinking:
We can add it as a separate `value` property and sync when needed.
```js
static get properties() {
return {
label: { type: String },
value: { type: String },
};
}
constructor() {
super();
this.label = '';
this.value = '';
// ...
}
update(changedProperties) {
super.update(changedProperties);
if (changedProperties.has('value')) {
if (this.value === 'cat') {
console.log('We like cats too :)');
}
this.inputEl.value = this.value;
}
}
```
And we're finally back in business! :tada:
ok bug fixed - can we please get back to coverage? thank you :pray:
### Back to coverage
With this added test we made some progress.
```
=============================== Coverage summary ===============================
Statements : 95.83% ( 23/24 )
Branches : 50% ( 2/4 )
Functions : 100% ( 7/7 )
Lines : 95.83% ( 23/24 )
================================================================================
06 04 2019 13:18:54.902:ERROR [reporter.coverage-istanbul]: Coverage for branches (50%) does not meet global threshold (90%)
```
However we are still not fully there - the question is why?
To find out open `coverage/index.html` in your browser. No web server needed just open the file in your browser - on a mac you can do that from the command line with `open coverage/index.html`
You will see something like this
![03-coverage-overview](./images/03-coverage-overview.png)
Once you click on `a11y-input.js` you get a line by line info how often they got executed.
So we can immediately see which lines are not executed yet by our tests.
![04-coverage-line-by-line](./images/04-coverage-line-by-line.png)
So let's add a test for that
```js
it('logs "We like cats too :)" if the value is "cat"', async () => {
const el = /** @type {A11yInput} */ (await fixture(html`
<a11y-input .value=${'cat'}></a11y-input>
`));
// somehow check that console.log was called
});
```
```
=============================== Coverage summary ===============================
Statements : 100% ( 24/24 )
Branches : 75% ( 3/4 )
Functions : 100% ( 7/7 )
Lines : 100% ( 24/24 )
================================================================================
```
With that, we are back at 100% on statements but we still have something missing on branches.
Let's see why?
![05-coverage-line-by-line-else](./images/05-coverage-line-by-line-else.png)
This `E` means `else path not taken`.
So whenever the function `update` gets called there is always a property `value` in the changedProperties.
We have `label` as well so it's a good idea to test it. :+1:
```js
it('can update its label', async () => {
const el = /** @type {A11yInput} */ (await fixture('<a11y-input label="foo"></a11y-input>'));
expect(el.label).to.equal('foo');
el.label = 'bar';
expect(el.label).to.equal('bar');
});
```
boom 100% :muscle: we win :1st_place_medal:
```
=============================== Coverage summary ===============================
Statements : 100% ( 24/24 )
Branches : 100% ( 4/4 )
Functions : 100% ( 7/7 )
Lines : 100% ( 24/24 )
================================================================================
```
But wait we didn't even finish the test above - the code is still
```js
// somehow check that console.log was called
```
#### How come we have 100% test coverage?
Lets first try to understand how code coverage works :thinking:
The way code coverage gets measured is by applying a form of `instrumentation`. In short, before our code is executed it gets changed (`instrumented`) and it behaves something like this:
**Note:** This is a super simplified version for illustration purposes.
```js
if (this.value === 'cat') {
console.log('We like cats too :)');
}
// becomes something like this (psoido code)
__instrumented['functionUpdate'] += 1;
if (this.value === 'cat') {
__instrumented['functionUpdateBranch1yes'] += 1;
console.log('We like cats too :)');
} else {
__instrumented['functionUpdateBranch1no'] += 1;
}
```
Basically, your code gets littered with many many flags. Based on which flags get trigger a statistic gets created.
So 100% test coverage only means that every line you have in your code was executed at least once after all your tests finished. It does *not* mean that you tested everything, or if your tests make the correct assertions.
So even though we already have 100% code coverage we are still going to improve our log test.
You should, therefore, see code coverage as a tool that only gives you guidance and help on spotting some missing tests, rather than a hard guarantee of code quality.
### Spying on Code
If you want to check how often or with which parameters a function gets called, that's called spying.
open-wc recommends the venerable [sinon](https://sinonjs.org/) package, which provides many tools for spying and other related tasks.
```bash
npm i -D sinon
```
So you create a spy on a specific object and then you can check how often it gets called.
```js
import sinon from 'sinon';
it('outputs "We like cats too :)" if the value is set to "cat"', async () => {
const logSpy = sinon.spy(console, 'log');
const el = /** @type {A11yInput} */ (await fixture(html`
<a11y-input></a11y-input>
`));
el.value = 'cat';
expect(logSpy.callCount).to.equal(1);
});
```
Uh oh... the test fails:
```
AssertionError: expected 0 to equal 1
```
Messing with global objects like `console` might have [side effects](https://gyandeeps.com/console-stubbing/) so let's better refactor using a dedicated log function.
```js
update(changedProperties) {
super.update(changedProperties);
if (changedProperties.has('value')) {
if (this.value === 'cat') {
this.log('We like cats too :)');
}
this.inputEl.value = this.value;
}
}
log(msg) {
console.log(msg);
}
```
This result in no global object in our test code - sweet :hugs:
```js
it('logs "We like cats too :)" if the value is set to "cat"', async () => {
const el = /** @type {A11yInput} */ (await fixture(html`
<a11y-input></a11y-input>
`));
const logSpy = sinon.spy(el, 'log');
el.value = 'cat';
expect(logSpy.callCount).to.equal(1);
});
```
However, we still get the same error. Let's debug... boohoo apparently `update` is not sync - a wrong assumption I made :see_no_evil: I am saying *assumptions are dangerous* quite often - still I fall for it from time to time :cry:.
So what can we do? Sadly there seems to be no public api to do some sync actions triggered by an property update.
Let's create an issue for it https://github.com/Polymer/lit-element/issues/643.
For now apparently, the only way is to rely on a *private* api. :see_no_evil:
Also, we needed to move the value sync to `updated` so it gets executed after every dom render.
```js
_requestUpdate(name, oldValue) {
super._requestUpdate(name, oldValue);
if (name === 'value') {
if (this.value === 'cat') {
this.log('We like cats too :)');
}
}
}
updated(changedProperties) {
super.updated(changedProperties);
if (changedProperties.has('value')) {
this.inputEl.value = this.value;
}
}
```
and here is the updated test for the logging
```js
it('logs "We like cats too :)" if the value is set to "cat"', async () => {
const el = /** @type {A11yInput} */ (await fixture(html`
<a11y-input></a11y-input>
`));
const logSpy = sinon.spy(el, 'log');
el.value = 'cat';
expect(logSpy.callCount).to.equal(1);
expect(logSpy.calledWith('We like cats too :)')).to.be.true;
// different values do NOT log
el.value = 'foo';
expect(logSpy.callCount).to.equal(1);
el.value = 'cat';
expect(logSpy.callCount).to.equal(2);
});
```
wow, that was a little tougher than expected but we did it :muscle:
```
SUMMARY:
✔ 7 tests completed
TOTAL: 7 SUCCESS
```
### Running Tests Without Karma Framework
The Karma framework is powerful and feature-rich, but sometimes we might want to pare-down our testing regiment. The nice thing with everything we proposed so far is that we only used browser-standard es modules with no need for transpilation, [with the one exception of bare modules specifiers](https://open-wc.org/about/rationales.html#workflows).
So just by creating a `test/index.html`.
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link href="../node_modules/mocha/mocha.css" rel="stylesheet" />
<script src="../node_modules/mocha/mocha.js"></script>
<script src="../node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js"></script>
</head>
<body>
<div id="mocha"></div>
<script>
mocha.setup('bdd');
</script>
<script type="module">
import './a11y-input.test.js';
import './my-app.test.js';
mocha.checkLeaks();
mocha.run();
</script>
</body>
</html>
```
and opening it via `owc-dev-server` in chrome, it will work perfectly fine.
We got everything up and running without `webpack` or `karma` - sweet :hugs:
### Do the Cross-Browser Thing
We now feel pretty comfortable with our web component. It's tested and covered; there's just one more step - we want to make sure it runs and is tested in all browsers.
Open WC recommends [Browserstack](https://www.browserstack.com/) for cross-browser testing. If you haven't set it up yet, you can do it now - here is the link again - [https://open-wc.org/testing/](https://open-wc.org/testing/).
So let's just run it
```
npm run test:bs
SUMMARY:
✔ 42 tests completed
TOTAL: 42 SUCCESS
```
Yeah, that works nicely! :hugs:
If there are failing tests it will output them in the summary with the specific browser where it failed.
```
SUMMARY:
✔ 40 tests completed
✖ 2 tests failed
FAILED TESTS:
a11y input
✖ has a static shadowDom
Firefox 64.0.0 (Windows 10.0.0)
Safari 12.0.0 (Mac OS X 10.14.0)
expected '<slot name="label">\n</slot>\n<slot name="input">\n</slot>\n<style>\n</style>\n' to equal '<slot name="label">\n</slot>\n<slot name="input">\n</slot>\n'
+ expected - actual
<slot name="label">
</slot>
<slot name="input">
</slot>
-<style>
-</style>
```
If you need to debug a particular browser:
- `npm run test:legacy:watch`
- visit [http://localhost:9876/debug.html](http://localhost:9876/debug.html) with that browser (either it locally or via browserstack)
- select a specific test (or use `it.only()` in code)
- start debugging
Also if you want to adjust the browser that gets tested you can adjust your `karma.bs.config.js`.
For example, if you want to add the `Firefox ESR` to your list.
```js
module.exports = config => {
config.set(
merge(bsSettings(config), createBaseConfig(config), {
browserStack: {
project: 'testing-workflow-for-web-components',
},
browsers: [
'bs_win10_firefox_ESR',
],
// define browsers
// https://www.browserstack.com/automate/capabilities
customLaunchers: {
bs_win10_firefox_ESR: {
base: 'BrowserStack',
browser: 'Firefox',
browser_version: '60',
os: 'Windows',
os_version: '10',
},
},
}),
);
return config;
};
```
Or maybe you want to test only 2 specific browsers?
```js
merge.strategy({
browsers: 'replace',
})(bsSettings(config), createBaseConfig(config), {
browserStack: {
project: 'testing-workflow-for-web-components',
},
browsers: [
'bs_win10_ie_11',
'bs_win10_firefox_ESR',
],
}),
```
**Note:** This uses the [webpack merge strategies](https://github.com/survivejs/webpack-merge#merging-with-strategies) replace.
## Quick Recap
- Testing is important for every project. Be sure to write as many as you can.
- Try to keep your code coverage high, but remember it's not a magic guarantee, so it doesn't always need to be 100%.
- Debug in the browser via `npm run test:watch`. For legacy browsers, use `npm run test:legacy.watch`.
## What's Next?
- Run the tests in your CI (works perfectly well together with browserstack). See our recommendations at [automating](https://open-wc.org/automating/).
Follow us on [Twitter](https://twitter.com/openwc), or follow me on my personal [Twitter](https://twitter.com/dakmor).
Make sure to check out our other tools and recommendations at [open-wc.org](https://open-wc.org).
Thanks to [Pascal](https://dev.to/thepassle) and [Benny](https://dev.to/bennypowers) for feedback and helping turn my scribbles to a followable story.
````

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -0,0 +1,83 @@
---
title: 'The all new Open Web Components'
pageTitle: 'The all new Open Web Components'
date: 2020-10-29
published: true
description: 'A lot has happend in Open Web Components - new website, repo cleanup, change of setup and we joined Modern Web'
tags: [webcomponents, open-wc, javascript]
canonical_url: https://open-wc.org/blog/the-all-new-open-web-components/
cover_image: /blog/the-all-new-open-web-components/images/blog-header.jpg
socialMediaImage: /blog/the-all-new-open-web-components/images/social-media-image.jpg
---
It has been an incredible busy year for Open Web Components. Lots has happened behind the scenes and there is still more to come.
Let's talk about the obvious first.
## The all-new Open Web Components website
As you may have noticed we completely restructured all our content and there is now a clear distinction between Guides and Documentation.
In [Guides](../../guides/index.md) we focus more on followable step by step explanations where as [Documentation](../../docs/index.md) is meant as a reference book if you need to look up which options are available or what they are doing.
In Guides you can find our current best sellers like [Codelabs](../../guides/developing-components/codelabs.md), [Code Examples](../../guides/developing-components/code-examples.md) or [Publishing](../../guides/developing-components/publishing.md). However we also added a complete new [Community](../../guides/community/getting-started.md) section which shows communities you can join or Base Libraries and Component Libraries you should check out.
Additionally we now share web components knowledge like how [attributes and properties](../../guides/knowledge/attributes-and-properties.md) or [events](../../guides/knowledge/events.md) work.
Technically the new website is using [eleventy](11ty.dev), [rollup](https://rollupjs.org/) and our own tools like Web Dev Server and Mdjs.
## Cleanup our repo
Over the last years we have created different projects and recommendations. During this time certain projects have become deprecated as we moved on to different tools or approaches.
This doesn't mean that we've completely dropped support for these projects. While we don't feature them on the main website, we still maintain and support these projects. We don't develop any new features or functionalities, but we will continue to support bugfixes and in some cases update along with the dependent tooling.
The documentation for our legacy projects is maintained in the github readmes:
### Legacy projects
- [es-dev-server](https://github.com/open-wc/es-dev-server) is now replaced by [web-dev-server](https://modern-web.dev/docs/dev-server/overview/)
- [testing-karma](https://github.com/open-wc/legacy/tree/master/packages/testing-karma) and [karma-esm](https://github.com/open-wc/legacy/tree/master/packages/karma-esm) we now recommend [web-test-runner](https://modern-web.dev/docs/test-runner/overview/)
- [testing-karma-bs](https://github.com/open-wc/legacy/tree/master/packages/testing-karma-bs) we now recommend [web-test-runner](https://modern-web.dev/docs/test-runner/overview/) & [@web/test-runner-browserstack](https://modern-web.dev/docs/test-runner/browser-launchers/browserstack/)
- [rollup-plugin-index-html](https://github.com/open-wc/legacy/tree/master/packages/rollup-plugin-index-html) we now recommend `@web/rollup-plugin-html`
- [webpack-import-meta-loader](https://github.com/open-wc/legacy/tree/master/packages/webpack-import-meta-loader) we now recommend [babel-plugin-bundled-import-meta](https://www.npmjs.com/package/babel-plugin-bundled-import-meta)
- [building-webpack](https://github.com/open-wc/legacy/tree/master/packages/building-webpack) we now recommend rollup over webpack
- [webpack-index-html-plugin](https://github.com/open-wc/legacy/tree/master/packages/webpack-index-html-plugin) we now recommend rollup over webpack
- [storybook-addon-web-components-knobs](https://github.com/open-wc/legacy/tree/master/packages/storybook-addon-web-components-knobs) storybook v6 has a new better knobs system
We also move out our [create](https://github.com/open-wc/create) Generators into a dedicated repository - which is only our first step as we will later automate updating it's dependencies via a bot so you can always be sure you get the latest versions.
## Change our setup
On top of moving out all "dusty" code we additionally change our setup.
We are now using [changesets](https://github.com/atlassian/changesets) to gives us more control about what gets released and how.
With [github actions](https://github.com/features/actions) we run our tests on multiple node versions (12 & 14) and windows at the same time so we can make sure our tools don't break.
Additionally our web testing is now performed by [Web Test Runner](https://modern-web.dev/docs/test-runner/overview/) which runs web tests in all evergreen browsers within a github action.
## Return focus to Web Components
With all those general web development packages moved to [Modern Web](https://modern-web.dev) and all those legacy packages move out of the repo we can bring our focus back to web components.
You will see more web component specific guides and tools coming up.
One of those is our just recently released [eslint-plugin-lit-a11y](../../docs/linting/eslint-plugin-lit-a11y/overview.md). It features more then 20 rules that will help you write more accessible lit-html templates.
## Issues and discussions
We do have a fair share of open issues which makes it sometimes hard to see/understand what are actual bugs/issues and what are feature request or questions. Additionally with all these package some issues probably are not relevant anymore. We plan to clean this up in the upcoming weeks by
1. Moving issues to the appropriate repository (if it's code got moved)
2. Moving feature requests into [github discussions](https://github.com/open-wc/open-wc/discussions)
3. This will leave only actual bugs in our [issue list](https://github.com/open-wc/open-wc/issues) 💪
We do hope this will make navigating out Github Page easier.
## Joining Modern Web
This now also makes it more apparent that we are part of the [Modern Web Family](https://modern-web.dev/discover/about/).
So be sure to follow Modern Web on [Twitter](https://twitter.com/modern_web_dev) and if you like what you see please consider sponsoring the project on [Open Collective](https://opencollective.com/modern-web).
Written with ♥️ &nbsp; by the Open Web Components Core Team

9
docs/browserconfig.xml Normal file
View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/mstile-150x150.png"/>
<TileColor>#9b03fe</TileColor>
</tile>
</msapplication>
</browserconfig>

View File

@@ -1,41 +0,0 @@
---
permalink: 'building/index.html'
title: Building
section: guides
tags:
- guides
---
# Building
Building is a necessary optimization when shipping apps to production. By using a build you reduce the total size and amount of files transferred to the end-user, and you ensure your code runs on all supported browsers.
We recommend doing most of the building only in projects which deploy the final result to production, such as apps or websites. This is where you can make the best decisions about supported browsers and optimizations.
## Build types
Depending on the type of project, we recommend different approaches to building.
### Building single page apps (SPA)
If you are building a single page application we recommend [rollup](https://rollupjs.org/guide/en/) to build your app.
Take a look at our dedicated [building-rollup](/building/building-rollup.html) page which explains how to set up rollup. We ship a default config that you can use to set up your project, or you can use it as inspiration for your custom config.
### Building reusable components and libraries
If you are building a reusable component or library we recommend publishing code that runs without modifications on the latest browsers. You should not bundle in any dependencies or polyfills, or build to a very old version of JavaScript such as ES5. This way, consuming projects can decide which polyfills to load and which JavaScript version to target based on browser support.
In practical terms, this means publishing standard ES modules and standard JavaScript features implemented in modern browsers, like Chrome, Safari, Firefox, and Edge. We recommend [buildless development](/developing/), so unless you are using very cutting edge features, you can actually just publish your source code as is. See [this blog post](https://justinfagnani.com/2019/11/01/how-to-publish-web-components-to-npm/) for a general guideline.
If you are using very new or non-standard features, such as TypeScript, you will need to set up a lightweight build. For this, we recommend tools such as [babel](https://babeljs.io/) with the [preset-env](https://babeljs.io/docs/en/babel-preset-env) plugin or the [TypeScript compiler](https://www.typescriptlang.org/). Make sure to set the target to modern browsers, and publish ES modules.
### Building websites or multi page apps (MPA)
Single page apps are great for a snappy user experience when you have highly dynamic content, but a lot of content on the web does not fall into this category. It still makes sense to build websites or apps consisting of multiple pages. This also requires a different approach to your build system.
We are still in the process of investigating and documenting our recommendations for this. In the meantime both [building-rollup](/building/building-rollup.html) and [@open-wc/rollup-plugin-html](https://open-wc.org/building/rollup-plugin-html.html) have tips for these types of projects.
## Webpack
We recommend [rollup](https://rollupjs.org/guide/en/) as a build tool. It is designed specifically for standard es modules and it's very easy to use. But sometimes you are not in control of choosing the tools to use on a project, and you need to make things work with other tools. For webpack, we have a legacy [standard config](https://github.com/open-wc/legacy/tree/master/packages/building-webpack) which can be used to build apps with web components.

View File

@@ -1 +0,0 @@
../../packages/building-rollup/README.md

View File

@@ -1 +0,0 @@
../../packages/polyfills-loader/README.md

View File

@@ -1 +0,0 @@
../../packages/rollup-plugin-html/README.md

View File

@@ -1 +0,0 @@
../../packages/rollup-plugin-polyfills-loader/README.md

View File

@@ -1,59 +0,0 @@
---
permalink: 'developing/index.html'
title: Developing
section: guides
tags:
- guides
---
# Developing
## Going buildless
Browsers have improved a lot over the past years. It's now possible to do web development without requiring any build tools, using the native module loader of the browser. We think this is a great fit for web components, and we recommend this as a general starting point.
Build tools can quickly add a lot of complexity to your code, and make your code reliant on a specific build setup. We think it's best to avoid them during development, or only add them for light transformations if you know what you're doing.
Read [this article](https://dev.to/open-wc/developing-without-a-build-1-introduction-26ao) to learn more about this approach.
## Development server
We created [es-dev-server](https://open-wc.org/developing/es-dev-server.html) to help developing without build tools.
## Web component libraries
You can write web components using just the basic web component APIs. This can be a great choice when you're looking to keep dependencies low. But generally, we recommend using lightweight libraries to help improve the developer experience and reduce boilerplate.
We recommend [lit-html](https://www.npmjs.com/package/lit-html) with the [lit-element](https://www.npmjs.com/package/lit-element) base class as a general-purpose library for building web components. `lit-html` is feature complete, extremely lightweight and offers a great development experience. Check out the [lit-html page](/developing/lit-html.html) for code examples and more information.
In the code snippets throughout our documentation we use `lit-html` and `lit-element`, but our recommendations and tools are not specific to them. You should be able to use them with any web component library that follows browser standards. If you do run into issues, or have any questions, let us know about it!
### Alternative libraries
Besides `lit-html`, there are other great options available:
- [haunted](https://www.npmjs.com/package/haunted) functional-style web components, with react-like hooks
- [hybrids](https://www.npmjs.com/package/hybrids) another functional web component library
- [SkateJS](https://skatejs.netlify.com/) wraps libraries like react, preact or lit-html in a web component
- [slim.js](https://slimjs.com/) declarative web components
- [stencil](https://stenciljs.com/) web component with typescript and JSX (requires a build step)
## Codelabs
See the [codelabs](/codelabs/) page for step by step tutorials for development.
## Code examples
Check out the [code examples](/developing/code-examples.html) page for a collection of best practices and design patterns.
## Testing
Check out our [testing documentation](/testing/) for help with setting up testing.
## Building
When you are ready to ship your app to production, or when you need to test your app on older browsers, take a look at our [building documentation](/building/) to get you started.
## Further reading
See: [awesome lit-html](https://github.com/web-padawan/awesome-lit-html) for a great collection of resources.

View File

@@ -1,26 +0,0 @@
---
permalink: 'developing/best-practices.html'
title: Best Practices
section: guides
tags:
- guides
- guide
---
# Best Practices
If you're using our [linting setup](/linting), you may already follow some Web Component best practices, but unfortunately, linters do not cover everything. On this page, you can find our recommended best practices.
In general, we recommend you read [Custom Element Best Practices](https://developers.google.com/web/fundamentals/web-components/best-practices), and [Guidelines for creating web platform compatible components](https://w3ctag.github.io/webcomponents-design-guidelines/).
<div class="custom-block tip"><p class="custom-block-title">Looking for contributors!</p> <p>We're actively looking to extend this page with common best practices. If you have anything to contribute, please make an issue or a pull request on our <a href="https://github.com/open-wc/" target="_blank" rel="noopener noreferrer">github repository<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a>.</p></div>
## Upwards Data
> ⬇️ Properties/attributes down
>
> ⬆️ Events up
Every developer at some point will run into the problem of moving some data upwards to a parent element. While there are many ways to manage state in your application like Redux, or passing down a bound function to a child element to mutate state in the parent, our default recommendation for moving data upwards is simply using events. The benefits of using events are that it's close to the platform, and will always have the same expected behavior across frameworks. You can find a demo of using `CustomEvents` [here](https://stackblitz.com/edit/open-wc-lit-demos?file=01-basic%2F12-firing-events.js).
If you find yourself moving a lot of data around, it may be time to look into some state management libraries like [Redux](https://redux.js.org/) or consider some more advanced composition techniques with [slotting](https://webcomponents.dev/edit/collection/fOta0aCFgRQqMtyXJjXT/pNsN9JVAiNHOAHpvkL6c).

View File

@@ -1,942 +0,0 @@
---
permalink: 'developing/es-dev-server.html'
title: ES dev server
section: guides
tags:
- guides
---
> **Notice**
>
> Development of es-dev-server continues under a new name: [web dev server](https://modern-web.dev/docs/dev-server/overview/). We recommend using it for new projects, and upgrading for existing projects.
>
> We will continue to support fixing bugs for es-dev-server.
# es dev server
A web server for development without bundling.
```bash
npx es-dev-server --node-resolve --watch
```
**Quick overview**
- efficient browser caching for fast reloads
- [transform code on older browsers for compatibility](#compatibility-mode)
- [resolve bare module imports for use in the browser](#node-resolve) (`--node-resolve`)
- auto-reload on file changes with the (`--watch`)
- history API fallback for SPA routing (`--app-index index.html`)
- [plugin API for extensions](#plugins)
[See all commands](#command-line-flags-and-configuration)
## Getting started
We recommend [following this guide](https://dev.to/open-wc/developing-without-a-build-2-es-dev-server-1cf5) for a step by step overview of different workflows with `es-dev-server`.
## Setup
```bash
npm i --save-dev es-dev-server
```
Add scripts to your `package.json`, modify the flags as needed:
```json
{
"scripts": {
"start": "es-dev-server --app-index index.html --node-resolve --watch --open"
}
}
```
Run the server:
```bash
npm run start
```
## Node version
es-dev-server requires node v10 or higher
## Command-line flags and Configuration
### Server configuration
| name | type | description |
| --------- | -------------- | ----------------------------------------------------------------------- |
| port | number | The port to use, uses a random free port if not set. |
| hostname | string | The hostname to use. Default: localhost |
| open | boolean/string | Opens the browser on app-index, root dir or a custom path |
| app-index | string | The app's index.html file, sets up history API fallback for SPA routing |
| root-dir | string | The root directory to serve files from. Default: working directory |
| base-path | string | Base path the app is served on. Example: /my-app |
| config | string | The file to read configuration from (JS or JSON) |
| cors | boolean | Enable CORS |
| help | none | See all options |
### Development help
| name | type | description |
| ----- | ------- | ------------------------------------------------------------------- |
| watch | boolean | Reload the browser when files are edited |
| http2 | boolean | Serve files over HTTP2. Sets up HTTPS with self-signed certificates |
### Code transformation
| name | type | description |
| -------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| compatibility | string | Compatibility mode for older browsers. Can be: `auto`, `always`, `min`, `max` or `none` Default `auto` |
| node-resolve | boolean | Resolve bare import imports using node resolve |
| dedupe | boolean/array | Deduplicates all modules, or modules from specified packages if the value is an array |
| preserve-symlinks | boolean | Preserve symlinks when resolving modules. Set to true, if using tools that rely on symlinks, such as `npm link`. Default false. |
| module-dirs | string/array | Directories to resolve modules from. Used by node-resolve |
| babel | boolean | Transform served code through babel. Requires .babelrc |
| file-extensions | string/array | Extra file extensions to use when transforming code. |
| babel-exclude | number/array | Patterns of files to exclude from babel compilation. |
| babel-modern-exclude | number/array | Patterns of files to exclude from babel compilation on modern browsers. |
| babel-module-exclude | number/array | Patterns of files to exclude from babel compilation for modules only. |
| event-stream | boolean | Whether to inject event stream script. Defaults to true. |
Most commands have an alias/shorthand. You can view them by using `--help`.
### Configuration files
We pick up an `es-dev-server.config.js` file automatically if it is present in the current working directory. You can specify a custom config path using the `config` flag.
Configuration options are the same as command line flags, using their camelCased names. Example:
```javascript
module.exports = {
port: 8080,
watch: true,
nodeResolve: true,
appIndex: 'demo/index.html',
plugins: [],
moduleDirs: ['node_modules', 'web_modules'],
};
```
In addition to the command-line flags, the configuration file accepts these additional options:
| name | type | description |
| --------------- | ------- | --------------------------------------------------------- |
| middlewares | array | Koa middlewares to add to the server. (read more below) |
| plugins | array | Plugins to add to the server. (read more below) |
| babelConfig | object | Babel config to run with the server. |
| polyfillsLoader | object | Configuration for the polyfills loader. (read more below) |
| debug | boolean | Whether to turn on debug mode on the server. |
## Serving files
es-dev-server is a static web server. When a request is made from the browser for `/foo/bar.js` it will try and find this file from the root directory. It cannot serve any files outside of your root directory because the browser has no way to request them, and the path on the file system must always be reflected in the path of the browser.
### index.html in the root
The simplest setup, making sure that all files are accessible, is to place your index.html at the root of your project
<details>
<summary>Read more</summary>
Consider this example directory structure:
```
node_modules/...
src/...
index.html
```
If you run the `es-dev-server` command from the root of the project, you can access your app at `/` or `/index.html` in the browser.
</details>
### index.html in a folder
If you move your `index.html` outside the root of your project, you have some different options.
<details>
<summary>Read more</summary>
Use the `--open` parameter for when you'd like to keep you index.html in a subfolder.
```
node_modules/...
src/...
src/index.html
```
You can access your app in the browser at `/src/` or `/src/index.html`. You can tell es-dev-server to explicitly open at this path:
```bash
# with app-index flag
es-dev-server --app-index src/index.html --open
# without app-index flag
es-dev-server --open src/
```
You can also change the root directory of the dev server:
```bash
es-dev-server --root-dir src --open
```
Now your `index.html` is accessible at `/` or `/index.html`. However, the dev server cannot serve any files outside of the root directory. So if your app uses any node modules, they will no longer because accessible.
If you want your index in a subfolder without this being visible in the browser URL, you can set up a file rewrite rule. [Read more here](#rewriting-request-urls)
</details>
### Monorepos
If you are using es-dev-server in a monorepo, your node modules are in two different locations. In a package's folder and at the repository root. You need to make sure that es-dev-server can serve from both directories.
<details>
<summary>Read more</summary>
For example, a typical monorepo setup looks like this:
```
node_modules/...
packages/my-package/node_modules/...
packages/my-package/index.html
```
You will need to make sure the root node_modules folder is accessible to the dev server.
If your working directory is `packages/my-package` you can use this command:
```bash
# with app-index (for SPA)
es-dev-server --root-dir ../../ --app-index packages/my-package/index.html --open
# without app-index
es-dev-server --root-dir ../../ --open packages/my-package/index.html
```
If your working directory is the root of the repository you can use this command:
```bash
# with app index (for SPA)
es-dev-server --app-index packages/my-package/index.html --open
# without app index
es-dev-server --open packages/my-package/index.html
```
This is the same approach as serving an index.html in a subdirectory, so the section above applies here as well.
</details>
### Base Element
<details>
<summary>Read more</summary>
If you are building a single page application with client-side routing, we recommend adding a base element to set the base URL of your document.
The base URL of the document can be accessed through `document.baseURI` and is used by the browser to resolve relative paths (anchors, images, links, scripts, etc.). By default, it is set to the browser's URL.
You can add `<base href="">` element to modify how files are resolved relatively to your index.html. This can be very useful when your index.html is not at the root of your project.
[Read more about this on MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base)
</details>
## Node resolve
"Bare imports" are imports which don't specify a full path to a file:
```js
import foo from 'bar';
```
The browser doesn't know where to find this file called `bar`. The `--node-resolve` flag resolves this bare import to the actual file path before serving it to the browser:
```js
import foo from './node_modules/bar/bar.js';
```
Because we use [es-module-lexer](https://github.com/guybedford/es-module-lexer) for blazing fast analysis to find the imports in a file without booting up a full-blown parser like babel, we can do this without a noticeable impact on performance.
For the actual resolve logic, we internally use [@rollup/plugin-node-resolve](https://github.com/rollup/plugins/tree/master/packages/node-resolve) so that you can keep the resolve logic in sync between development and production. When using a config file, the `nodeResolve` can also be an object which accepts the same options as the rollup plugin. options.
<details>
<summary>Example config</summary>
See [the rollup docs](https://github.com/rollup/plugins/tree/master/packages/node-resolve) for all options and what they do.
Some options like `dedupe`, `fileExtensions`, `preserveSymlinks` and `moduleDirs` are mapped to options for `nodeResolve` internally. You can overwrite them with your custom config.
```js
module.exports = {
nodeResolve: {
jsnext: true,
browser: true,
// set default to false because es-dev-server always
// runs in the browser
preferBuiltins: true,
// will overwrite es-dev-server's fileExtensions option
extensions: ['.mjs', '.js'],
// will overwrite es-dev-server's dedupe option
dedupe: ['lit-html'],
customResolveOptions: {
// will overwrite es-dev-server's moduleDirs option
moduleDirectory: ['node_modules'],
preserveSymlinks: true,
},
},
};
```
</details>
In the future, we are hoping that [import maps](https://github.com/WICG/import-maps) will make this step unnecessary.
## Middleware
You can add your own middleware to es-dev-server using the `middlewares` property. The middleware should be a standard koa middleware. [Read more about koa here.](https://koajs.com/)
You can use middleware to modify respond to any request from the browser, for example to rewrite a URL or proxy to another server. For serving or manipulating files it's recommended to use plugins.
### Proxying requests
<details>
<summary>Read more</summary>
```javascript
const proxy = require('koa-proxies');
module.exports = {
port: 9000,
middlewares: [
proxy('/api', {
target: 'http://localhost:9001',
}),
],
};
```
</details>
### Rewriting request urls
You can rewrite certain file requests using a simple middleware. This can be useful for example to serve your `index.html` from a different file location or to alias a module.
<details>
<summary>Read more</summary>
Serve `/index.html` from `/src/index.html`:
```javascript
module.exports = {
middlewares: [
function rewriteIndex(context, next) {
if (context.url === '/' || context.url === '/index.html') {
context.url = '/src/index.html';
}
return next();
},
],
};
```
</details>
## Plugins
Plugins are objects with lifecycle hooks called by es-dev-server as it serves files to the browser. They can be used to serve virtual files, transform files, or resolve module imports.
### Adding plugins
A plugin is just an object that you add to the `plugins` array in your configuration file. You can add an object directly, or create one from a function somewhere:
<details>
<summary>Read more</summary>
```js
const awesomePlugin = require('awesome-plugin');
module.exports = {
plugins: [
// use a plugin
awesomePlugin({ someOption: 'someProperty' }),
// create an inline plugin
{
transform(context) {
if (context.response.is('html')) {
return { body: context.body.replace(/<base href=".*">/, '<base href="/foo/">') };
}
},
},
],
};
```
</details>
See the full type interface for all options:
<details>
<summary>Read more</summary>
```ts
import Koa, { Context } from 'koa';
import { FSWatcher } from 'chokidar';
import { Server } from 'net';
import { ParsedConfig } from './config';
type ServeResult = void | { body: string; type?: string; headers?: Record<string, string> };
type TransformResult = void | { body?: string; headers?: Record<string, string> };
type ResolveResult = void | string | Promise<void> | Promise<string>;
interface ServerArgs {
config: ParsedConfig;
app: Koa;
server: Server;
fileWatcher: FSWatcher;
}
export interface Plugin {
serverStart?(args: ServerArgs): void | Promise<void>;
serve?(context: Context): ServeResult | Promise<ServeResult>;
transform?(context: Context): TransformResult | Promise<TransformResult>;
resolveImport?(args: { source: string; context: Context }): ResolveResult;
resolveMimeType?(context: Context): undefined | string | Promise<undefined | string>;
}
```
</details>
### Hook: serve
The serve hook can be used to serve virtual files from the server. The first plugin to respond with a body is used. It can return a Promise.
<details>
<summary>Read more</summary>
Serve an auto generated `index.html`:
```js
const indexHTML = generateIndexHTML();
module.exports = {
plugins: [
{
serve(context) {
if (context.path === '/index.html') {
return { body: indexHTML };
}
},
},
],
};
```
Serve a virtual module:
```js
const indexHTML = generateIndexHTML();
module.exports = {
plugins: [
{
serve(context) {
if (context.path === '/messages.js') {
return { body: 'export default "Hello world";' };
}
},
},
],
};
```
The file extension is used to infer the mime type to respond with. If you are using a non-standard file extension you can use the `type` property to set it explicitly:
```js
module.exports = {
plugins: [
{
serve(context) {
if (context.path === '/foo.xyz') {
return { body: 'console.log("foo bar");', type: 'js' };
}
},
},
],
};
```
</details>
### Hook: resolveMimeType
Browsers don't use file extensions to know how to interpret files. Instead, they use [media or MIME type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types) which is set using the `content-type` header.
es-dev-server guesses the MIME type based on the file extension. When serving virtual files with non-standard file extensions, you can set the MIME type in the returned result (see the examples above). If you are transforming code from one format to another, you need to use the `resolveMimeType` hook.
<details>
<summary>Read more</summary>
The returned MIME type can be a file extension, this will be used to set the corresponding default MIME type. For example `js` resolves to `application/javascript` and `css` to `text/css`.
```js
module.exports = {
plugins: [
{
resolveMimeType(context) {
// change all MD files to HTML
if (context.response.is('md')) {
return 'html';
}
},
},
{
resolveMimeType(context) {
// change all CSS files to JS, except for a specific file
if (context.response.is('css') && context.path !== '/global.css') {
return 'js';
}
},
},
],
};
```
It is also possible to set the full mime type directly:
```js
module.exports = {
plugins: [
{
resolveMimeType(context) {
if (context.response.is('md')) {
return 'text/html';
}
},
},
],
};
```
</details>
### Hook: transform
The transform hook is called for each file and can be used to transform a file. Multiple plugins can transform a single file. It can return a Promise.
This hook is useful for small modifications, such as injecting environment variables, or for compiling files to JS before serving them to the browser.
If you are transforming non-standard file types, you may also need to include a `resolveMimeType` hook.
<details>
<summary>Read more</summary>
Rewrite the base path of your application for local development;
```js
module.exports = {
plugins: [
{
transform(context) {
if (context.path === '/index.html') {
const transformedBody = context.body.replace(/<base href=".*">/, '<base href="/foo/">');
return { body: transformedBody };
}
},
},
],
};
```
Inject a script to set global variables during local development:
```js
module.exports = {
plugins: [
{
transform(context) {
if (context.path === '/index.html') {
const transformedBody = context.body.replace(
'</head>',
'<script>window.process = { env: { NODE_ENV: "development" } }</script></head>',
);
return { body: transformedBody };
}
},
},
],
};
```
Inject environment variables into a JS module:
```js
const packageJson = require('./package.json');
module.exports = {
plugins: [
{
transform(context) {
if (context.path === '/src/environment.js') {
return { body: `export const version = '${packageJson.version}';` };
}
},
},
],
};
```
Transform markdown to HTML:
```js
const markdownToHTML = require('markdown-to-html-library');
module.exports = {
plugins: [
{
resolveMimeType(context) {
// this ensures the browser interprets .md files as .html
if (context.path.endsWith('.md')) {
return 'html';
}
},
async transform(context) {
// this will transform all MD files. if you only want to transform certain MD files
// you can check context.path
if (context.path.endsWith('.md')) {
const html = await markdownToHTML(body);
return { body: html };
}
},
},
],
};
```
Polyfill CSS modules in JS:
```js
module.exports = {
plugins: [
{
resolveMimeType(context) {
if (context.path.endsWith('.css')) {
return 'js';
}
},
async transform(context) {
if (context.path.endsWith('.css')) {
const stylesheet = `
const stylesheet = new CSSStyleSheet();
stylesheet.replaceSync(${JSON.stringify(body)});
export default stylesheet;
`;
return { body: stylesheet };
}
},
},
],
};
```
</details>
### Hook: resolveImport
The `resolveImport` hook is called for each module import. It can be used to resolve module imports before they reach the browser.
<details>
<summary>Read more</summary>
es-dev-server already resolves module imports when the `--node-resolve` flag is turned on. You can do the resolving yourself, or overwrite it for some files.
The hook receives the import string and should return the string to replace it with. This should be a browser-compatible path, not a file path.
```js
module.exports = {
plugins: [
{
async resolveImport({ source, context }) {
const resolvedImport = fancyResolveLibrary(source);
return resolvedImport;
},
},
],
};
```
</details>
### Hook: serverStart
The `serverStart` hook is called when the server starts. It is the ideal location to boot up other servers you will proxy to. It receives the server config, which you can use if plugins need access to general information such as the `rootDir` or `appIndex`. It also receives the HTTP server, Koa app, and `chokidar` file watcher instance. These can be used for more advanced plugins. This hook can be async, and it awaited before actually booting the server and opening the browser.
<details>
<summary>Read more</summary>
Accessing the serverStart parameters:
```js
function myFancyPlugin() {
let rootDir;
return {
serverStart({ config, app, server, fileWatcher }) {
// take the rootDir to access it later
rootDir = config.rootDir;
// register a koa middleware directly
app.use((context, next) => {
console.log(context.path);
return next();
});
// register a file to be watched
fileWatcher.add('/foo.md');
},
};
}
module.exports = {
plugins: [myFancyPlugin()],
};
```
Boot up another server for proxying in serverStart:
```js
const proxy = require('koa-proxies');
module.exports = {
plugins: [
{
async serverStart({ app }) {
// set up a proxy for certain requests
app.use(
proxy('/api', {
target: 'http://localhost:9001',
}),
);
// boot up the other server, because it is awaited es-dev-server will also wait for it
await startOtherServer({ port: 9001 });
},
},
],
};
```
</details>
### Koa Context
The plugin hooks simply receive the Koa `Context` object. This contains information about the server's request and response. Check the [Koa documentation](https://koajs.com/) to learn more about this.
To transform specific kinds of files we don't recommend relying on file extensions. Other plugins may be using non-standard file extensions. Instead, you should use the server's MIME type or content-type header. You can easily check this using the `context.response.is()` function. This is used a lot in the examples above.
Because files can be requested with query parameters and hashes, we recommend using `context.path` for reading the path segment of the URL only. If you do need to access search parameters, we recommend using `context.URL.searchParams.get('my-parameter')`.
## Order of execution
The order of execution for the es-dev-server when a file request is received:
1. User middleware: before "next"
2. Serve
- Plugins: serve
- es-dev-server: static file middleware (if no plugin match)
3. Plugins: resolveMimeType
4. Plugins: transform
5. Resolve module imports
- Plugins: resolveModuleImport
- es-dev-server: node-resolve (if no plugin resolve)
6. es-dev-server: babel + compatibility transforms
7. es-dev-server: response cache (caches all JS files served, including plugin transforms)
8. User middleware: after "next"
## Typescript support
Because es-dev-server doesn't do any bundling, it's easy to integrate it with typescript and doesn't require any extra tooling or plugins. Just run `tsc` on your code, and serve the compiled output with es-dev-server. You can run both `tsc` and es-dev-server in watch mode, changes will be picked up automatically.
Make sure to configure `tsc` to output real ES modules.
## Compatibility mode
Compatibility mode enables bundle-free development using modern browsers features on older browsers. Automatic compatibility mode is enabled by default.
<details>
<summary>Read more</summary>
Compatibility mode can be configured using the `--compatibility` flag. The possible options are `auto`, `min`, `max`, and `none`. The default is mode is `auto`.
**auto**
`auto` compatibility looks at the current browser to determine the level of compatibility to enable. On the latest 2 versions of the major browsers, it doesn't do any work. This keeps the server as fast as possible in the general case.
On older browsers, the server uses the browser's user agent and [@babel/preset-env](https://babeljs.io/docs/en/babel-preset-env) to do a targeted transformation for that specific browser and version. `@babel/preset-env` only works with stage 4 javascript features, they should become an official standard before they can be used.
If the browser does not support es module scripts, dynamic imports or `import.meta.url` es modules are transformed to [system-js](https://github.com/systemjs/systemjs).
This works down to at least IE11. Depending on what browser features you are using, it might work with an earlier version too but this is not tested.
**always**
`always` compatibility is the same as `auto`, except that it doesn't skip compiling on the latest 2 versions of the major browsers. This makes it a bit slower on modern browsers but allows you to use new features before they are implemented in the browser.
**min**
`min` compatibility forces the same level of compatibility on all browsers. It makes code compatible with the latest two versions of the major browsers and does not transform es modules.
**max**
`max` compatibility forces the same level of compatibility on all browsers. It compiles everything to es5 and [system-js](https://github.com/systemjs/systemjs).
**none**
`none` disables compatibility mode entirely.
</details>
### Polyfills loader
When compatibility mode is enabled, polyfills are loaded using [polyfills-loader](https://github.com/open-wc/open-wc/tree/master/packages/polyfills-loader).
<details>
<summary>Read more</summary>
You can customize the polyfill loader configuration from your configuration file. Check the docs for the [polyfills-loader](https://github.com/open-wc/open-wc/tree/master/packages/polyfills-loader) for all possible options.
```js
module.exports = {
polyfillsLoader: {
polyfills: {
fetch: false,
custom: [
{
name: 'my-feature-polyfill',
path: require.resolve('my-feature-polyfill'),
test: "!('myFeature' in window)",
},
],
},
},
};
```
By default, es-dev-server wraps all scripts and are deferred until polyfills are loaded. Loading order of scripts are preserved, but this can create problems if you rely on a script being executed before HTML is parsed. You can configure `es-dev-server` to exclude certain types of scripts:
```js
module.exports = {
polyfillsLoader: {
exclude: {
jsModules: true,
inlineJsModules: true,
jsScripts: true,
inlineJsScripts: true,
},
},
};
```
</details>
## Using es-dev-server programmatically
You can use different components from `es-dev-server` as a library and integrate it with other tools:
<details>
<summary>Read more</summary>
### createConfig
When using the server from javascript you are going to need a config object to tell the server what options to turn on and off. It's best to use `createConfig` for this as this converts the public API to an internal config structure and sets up default values.
By default, all options besides static file serving are turned off, so it's easy to configure based on your requirements.
The config structure is the same as the configuration explained in the [configuration files section](#configuration-files)
```javascript
import { createConfig } from 'es-dev-server';
const config = createConfig({
http2: true,
babel: true,
open: true,
});
```
### createMiddlewares
`createMiddlewares` creates the dev server's middlewares based on your configuration. You can use this to hook them up to your koa server.
Returns an array of koa middleware functions.
```javascript
import Koa from 'koa';
import { createConfig, createMiddlewares } from 'es-dev-server';
const config = createConfig({});
const middlewares = createMiddlewares(config);
const app = new Koa();
middlewares.forEach(middleware => {
app.use(middleware);
});
```
### createServer
`createServer` creates an instance of the dev server including all middlewares, but without starting the server. This is useful if you want to be in control of starting the server yourself.
Returns the koa app and a node http or http2 server.
```javascript
import Koa from 'koa';
import { createConfig, createServer } from 'es-dev-server';
const config = createConfig({ ... });
const { app, server } = createServer(config);
server.listen(3000);
```
### watch mode
`createMiddlewares` and `createServer` requires a chokidar fileWatcher if watch mode is enabled. You need to pass this separately because the watcher nees-dev-server to be killed explicitly when the server closes.
```javascript
import Koa from 'koa';
import chokidar from 'chokidar';
import { createConfig, createMiddlewares, createServer } from 'es-dev-server';
const config = createConfig({ ... });
const fileWatcher = chokidar.watch([]);
// if using createMiddlewares
createMiddlewares(config, fileWatcher);
// if using createServer
createServer(config, fileWatcher);
// close filewatcher when no longer necessary
fileWatcher.close();
```
### startServer
`startServer` asynchronously creates and starts the server, listening on the configured port. It opens the browser if configured and logs a startup message.
Returns the koa app and a node http or http2 server.
```javascript
import Koa from 'koa';
import { createConfig, startServer } from 'es-dev-server';
async function main() {
const config = createConfig({ ... });
const { app, server } = await startServer(config, fileWatcher);
}
main();
```
</details>

View File

@@ -1,74 +0,0 @@
---
permalink: 'developing/ide.html'
title: IDE
section: guides
tags:
- guides
- guide
---
# IDE
Your IDE is your primary tool while working with code, we recommend the following tools and plugins to make developing Web Components easier.
## Visual Studio Code
We recommend [VSCode](https://code.visualstudio.com/). For setup please visit the instructions on the Visual Studio Code [homepage](https://code.visualstudio.com/).
### Configuration
We recommend the following user settings:
```json
{
"files.autoSave": "onWindowChange",
"editor.tabSize": 2,
"files.trimTrailingWhitespace": true,
"[markdown]": {
"files.trimTrailingWhitespace": false
}
}
```
**How to set up**:
1. File > Preferences > Settings
1. click on "..." > Open settings.json
![VSCodeSettings](/ide-vscode-settings.gif)
### Plugins
We recommend the following plugins:
- [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)
Get ESLint feedback directly in your IDE => more details under [Linting](/linting/)
- [lit-plugin](https://marketplace.visualstudio.com/items?itemName=runem.lit-plugin)
Syntax highlighting, type checking and code completion for lit-html
- [prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) Code formatter.
## Atom
An alternative to VSCode is [Atom](https://atom.io/), an IDE created by Github. It provides near-native support for working with web components and has great support for template literals.
### Recommended plugins
- [prettier-atom](https://atom.io/packages/prettier-atom) - Template literal highlighting and formatting
## [Intellij IDEA](https://www.jetbrains.com/idea/) and other Jetbrains variants
Another possible alternative for development is IntelliJ IDEA. IntelliJ is a Java integrated development environment (IDE) for developing computer software. It is developed by JetBrains (formerly known as IntelliJ), and is available as an Apache 2 Licensed community edition and in a proprietary commercial edition.
Syntax highlighting from html and css in template literals should be supported out of the box. Generic web components related functionalitites such as Custom Elements support and completion is also available. You can read more about it [here](https://blog.jetbrains.com/phpstorm/2013/10/phpstorm-7-web-toolkit-javascript-templates-web-components-support/).
![intellij-syntax0-highlighting](/intellij-syntax-highlighting.png)
Due to the support available directly in the IDE, the ecosystem for plugins is very limited and we do not recommend any.
## [Sublime Text 3](https://www.sublimetext.com/3)
Officially called a text editor Sublime Text features plugins which give it a lot of the possibilities of the IDE's listed above. It is available for Windows, Linux and OSX and can be evaluated for free.
### Recommended plugins
- [Lit Element Syntax Highlighting](https://packagecontrol.io/packages/LitElement%20Syntax%20Highlighting) - Syntax highlighting

View File

@@ -1 +0,0 @@
../../packages/lit-helpers/README.md

View File

@@ -1,26 +0,0 @@
---
permalink: 'developing/lit-html.html'
title: lit-html
section: guides
tags:
- guides
- guide
---
# lit-html
We recommend [lit-html](https://www.npmjs.com/package/lit-html) with the [lit-element](https://www.npmjs.com/package/lit-element) base class as a general-purpose library for building web components. `lit-html` is feature complete, extremely lightweight and offers a great development experience.
Under the hood new Web Component APIs such as [Constructable Stylesheets](https://developers.google.com/web/updates/2019/02/constructable-stylesheets) are used when they are available. As browser support improves, you get performance improvements for free.
## Getting started
To get started, we recommend using our project scaffolding:
```bash
npm init @open-wc
```
## Examples
Check out the [code examples](/developing/code-examples.html) page for a collection of best practices and design patterns.

View File

@@ -1,106 +0,0 @@
---
permalink: 'developing/types.html'
title: Types
section: guides
tags:
- guides
- guide
---
# 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 utilize types, you will need to set them up for your project.
### Why types?
Types can help improve your developer experience by giving you:
- Awesome intellisense for @open-wc tools/helpers
- Ability to jump directly to the source of @open-wc code via F12 (in vs code)
Here is a small gif showing how it can help.
![TypesExample](/types.gif)
### Setup for JavaScript
In order to get type linting in a JavaScript project using VS Code all you need is to add a `tsconfig.json`.
```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": ["**/*.js", "node_modules/@open-wc/**/*.js"],
"exclude": ["node_modules/!(@open-wc)"]
}
```
### Setup for TypeScript
If you wish to use our typings in TypeScript you will need to do a little more.
You will need to add to this to your `tsconfig.json`.
```json
{
"compilerOptions": {
"allowJs": 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.
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": ["**/*.js", "node_modules/@open-wc/**/*.js"],
"exclude": ["node_modules/!(@open-wc)"]
}
```

View File

@@ -1,13 +1,4 @@
---
permalink: 'about/index.html'
title: 'About'
section: about
tags:
- about
- section
---
# About Us
# About Open Web Components
## Why `open-wc`
@@ -15,7 +6,7 @@ We want to provide the community with well-known and experience-tested recommend
### Our Philosophy
The goal of Open Web Components is to empower developers with powerful and battle-tested tools for creating and sharing open source web components.
The goal of Open Web Components is to empower developers with powerful and battle-tested tools for creating and sharing open-source web components.
Many web developers have experienced the dreaded "Javascript Fatigue". With our recommendations, we hope you'll enjoy the peace of mind that comes from having a well-known default solution for almost everything. From IDE to CI, `open-wc` has got you covered.
@@ -31,7 +22,7 @@ We believe that web components provide a standards-based solution to problems li
The World Wide Web was originally just text documents connected by hyperlinks. As technologies like JavaScript and techniques like <abbr title="Asynchronous JavaScript and XML">AJAX</abbr> and <abbr title="Representational State Transfer">REST</abbr> entered the picture, the web slowly developed into a platform for full-fledged application development.
At first, JavaScript libraries like jQuery were absolutely required. jQuery gave developers a common set of tools across browsers with extremely different feature sets. Eventually jQuery features became standard browser features. The pattern of libraries innovating and browsers subsequently standardizing continued. As the "[browser wars](https://www.wikiwand.com/en/Browser_wars)" died down, the pace of standardization increased, and many new features came to the web.
At first, JavaScript libraries like jQuery were absolutely required. jQuery gave developers a common set of tools across browsers with extremely different feature sets. Eventually, jQuery features became standard browser features. The pattern of libraries innovating and browsers subsequently standardizing continued. As the "[browser wars](https://www.wikiwand.com/en/Browser_wars)" died down, the pace of standardization increased, and many new features came to the web.
In the last several years, a component-based model for web application development was popularized, and the JavaScript community blossomed with a wide variety of libraries and approaches. Work on standardizing the webs native component model began at Google in 2012, and after several years of open development, was successfully implemented across all major browsers in 2019.
@@ -55,6 +46,13 @@ Using the browser's standards-based component model will increase the longevity
The web components standards are fairly low-level, letting library authors build upon them with innovative APIs. By using such libraries you can build your app in the style you want while still leveraging built-in browser features.
## Modern Web Family
Open-wc is part of the Modern Web family, which consists of:
- modern-web.dev with the `@web` npm namespace
- open-wc.org with the `@open-wc` npm namespace
## Contribute
Feel free to critique, ask questions and join the conversation, we'd love to hear your opinions and feedback. You can reach us by creating an issue on our [github](https://github.com/open-wc) or on the [#open-wc](hhttps://slack.com/share/IUQNUPWUF/awabyN8iYH4dXX6aGpu16ES9/enQtOTc2Nzc2ODEyOTY3LWM5ZGExNGFiMmM4NDY2YWI2MzYwOGY5ZTNlZjk4OGU4NTFhMGJjNmVhNGI4MzVlNTMwNGRiNGIxNjc4MGJhNDg) channel in the [Polymer slack](https://www.polymer-project.org/slack-invite).
Feel free to critique, ask questions, and join the conversation, we'd love to hear your opinions and feedback. You can reach us by creating an issue on our [github](https://github.com/open-wc) or on the [#open-wc](hhttps://slack.com/share/IUQNUPWUF/awabyN8iYH4dXX6aGpu16ES9/enQtOTc2Nzc2ODEyOTY3LWM5ZGExNGFiMmM4NDY2YWI2MzYwOGY5ZTNlZjk4OGU4NTFhMGJjNmVhNGI4MzVlNTMwNGRiNGIxNjc4MGJhNDg) channel in the [Polymer slack](https://www.polymer-project.org/slack-invite).

5
docs/discover/slack.md Normal file
View File

@@ -0,0 +1,5 @@
# Slack
You can also find us on the Polymer slack in the [#open-wc](https://slack.com/share/IUQNUPWUF/awabyN8iYH4dXX6aGpu16ES9/enQtOTc2Nzc2ODEyOTY3LWM5ZGExNGFiMmM4NDY2YWI2MzYwOGY5ZTNlZjk4OGU4NTFhMGJjNmVhNGI4MzVlNTMwNGRiNGIxNjc4MGJhNDg) channel.
You can join the Polymer slack by visiting [https://www.polymer-project.org/slack-invite](https://www.polymer-project.org/slack-invite).

View File

@@ -0,0 +1,3 @@
# Building ||40
Please see a sub page

View File

@@ -0,0 +1,5 @@
# Building >> Overview ||10
Building is a necessary optimization when shipping apps to production. By using a build you reduce the total size and amount of files transferred to the end-user, and you ensure your code runs on all supported browsers.
We recommend doing most of the building only in projects which deploy the final result to production, such as apps or websites. This is where you can make the best decisions about supported browsers and optimizations.

View File

@@ -0,0 +1,458 @@
# Building >> Rollup ||20
Rollup configuration to help you get started building modern web applications.
You write modern javascript using the latest browser features, rollup will optimize your code for production and ensure it runs on all supported browsers.
## Features
- Set HTML or JS as input and/or output
- Optimized for browsers which support modules
- Optional separate build for legacy browsers
- Loads polyfills using feature detection
- Generates a service worker
- Minifies JS
- Minifies lit-html templates
## Automatic setup
We recommend the open-wc [project generator](https://open-wc.org/init/) for automated setup, for new projects or to upgrade existing projects.
## Manual setup
Install the required dependencies:
<details>
<summary>View</summary>
```bash
npm i -D rollup @open-wc/building-rollup rimraf deepmerge es-dev-server
```
</details>
Create a `rollup.config.js` file:
<details>
<summary>View</summary>
```js
import merge from 'deepmerge';
// use createSpaConfig for bundling a Single Page App
import { createSpaConfig } from '@open-wc/building-rollup';
// use createBasicConfig to do regular JS to JS bundling
// import { createBasicConfig } from '@open-wc/building-rollup';
const baseConfig = createSpaConfig({
// use the outputdir option to modify where files are output
// outputDir: 'dist',
// if you need to support older browsers, such as IE11, set the legacyBuild
// option to generate an additional build just for this browser
// legacyBuild: true,
// development mode creates a non-minified build for debugging or development
developmentMode: process.env.ROLLUP_WATCH === 'true',
// set to true to inject the service worker registration into your index.html
injectServiceWorker: false,
});
export default merge(baseConfig, {
// if you use createSpaConfig, you can use your index.html as entrypoint,
// any <script type="module"> inside will be bundled by rollup
input: './index.html',
// alternatively, you can use your JS as entrypoint for rollup and
// optionally set a HTML template manually
// input: './app.js',
});
```
</details>
Add the following NPM scripts to your `package.json`:
<details>
<summary>View</summary>
```json
{
"scripts": {
"build": "rimraf dist && rollup -c rollup.config.js",
"start:build": "npm run build && es-dev-server --root-dir dist --app-index index.html --compatibility none --open"
}
}
```
</details>
### SPA Projects
If you have a SPA (Single Page App) project you can use `createSpaConfig` to generate your config. This will inject the rollup output into a single index.html file, generate a service worker and load polyfills using feature detection.
#### HTML as input
You can set an index.html file as rollup input. Any modules inside will be bundled by rollup and the final output will be injected into the final HTML file.
<details>
<summary>View example</summary>
```js
import merge from 'deepmerge';
import { createBasicConfig } from '@open-wc/building-rollup';
const baseConfig = createBasicConfig();
export default merge(baseConfig, {
input: './index.html',
});
```
</details>
#### Custom HTML template
You can also provide the HTML as a template, as a string or read from a file. See the [html plugin docs](https://github.com/open-wc/open-wc/tree/master/packages/rollup-plugin-html#readme) for all possible options.
<details>
<summary>View example</summary>
```js
import merge from 'deepmerge';
import { createBasicConfig } from '@open-wc/building-rollup';
const baseConfig = createBasicConfig({
html: {
template: /* your template goes here */,
},
});
export default merge(baseConfig, {
input: './src/app.js',
});
```
</details>
### Non-SPA projects
If you are not building a single page app you can use `createBasicConfig` to set up regular JS to JS bundling.
From there on it's easy to extend further for different use cases. For example, to bundle and generate multiple HTML files you can take a look at the documentation of [@open-wc/rollup-plugin-html](https://github.com/open-wc/open-wc/tree/master/packages/rollup-plugin-html#readme). See below how to add plugins to the rollup config.
## Injecting a service worker
When you use `createSpaConfig` a service worker is generated automatically with Workbox. The service worker registration is not injected into your `index.html` by default to prevent unexpected results. You can turn this on by enabling the `injectServiceWorker` option:
```js
const baseConfig = createSpaConfig({
injectServiceWorker: true,
});
```
If you overwrite the workbox configuration and the `swDest` property of the workbox config, `injectServiceWorker` will automatically use the value of `swDest` in the service worker registration. Example:
```js
const baseConfig = createSpaConfig({
injectServiceWorker: true,
workbox: {
swDest: './my-sw.js',
},
});
```
Will result in:
```js
navigator.serviceWorker.register('./my-sw.js');
```
## Supporting older browsers
The default build output works only on browsers that support modules. If you need to support older browsers, such as IE11 or the old Edge, you can set the `legacyBuild` option when you use the create config function.
This will create a separate rollup build output for legacy browsers and makes sure the correct version is loaded. This has minimal impact on users with modern browsers.
```js
const baseConfig = createSpaConfig({
legacyBuild: true,
});
```
## Customizations
Our config generator sets you up with good defaults for most projects. It's easy to extend and customize this config further for your requirements.
If you find yourself disabling a lot of the default functionality we recommend forking from the default config and taking control yourself. Rollup is relatively easy to configure compared to other build tools, it's better to be in full control if you know what you're doing.
### Customizing the babel config
You can define custom babel plugins to be loaded by adding a `.babelrc` or `babel.config.js` to your project. See [babeljs config](https://babeljs.io/docs/en/configuration) for more information.
For example to add support for class properties:
```json
{
"plugins": ["@babel/plugin-proposal-class-properties"]
}
```
### Customizing default plugins
Our config creators install a number of rollup plugins by default:
Basic and SPA config plugins:
- [nodeResolve](https://github.com/rollup/plugins/tree/master/packages/node-resolve#readme)
- [babel](https://github.com/rollup/plugins/tree/master/packages/babel#readme)
- [terser](https://github.com/TrySound/rollup-plugin-terser#readme)
SPA config plugins:
- [html](https://github.com/open-wc/open-wc/tree/master/packages/rollup-plugin-html#readme)
- [polyfillsLoader](https://github.com/open-wc/open-wc/tree/master/packages/polyfills-loader#readme)
- [workbox](https://www.npmjs.com/package/rollup-plugin-workbox)
You can customize options for these plugins, or turn them off completely by setting them to false. Check the documentation for the respective plugins to learn which options can be configured.
<details>
<summary>View example</summary>
Each plugin can be either "true", "false" or an object. If it's an object, this is used as a configuration for the plugin.
```js
const baseConfig = createSpaConfig({
nodeResolve: { browser: true, dedupe: ['lit-html'] },
babel: true,
terser: { exclude: ['node_modules*'] },
html: false,
polyfillsLoader: false,
workbox: false,
});
```
</details>
Examples:
### Customize HTML
[@open-wc/rollup-plugin-html](https://github.com/open-wc/open-wc/tree/master/packages/rollup-plugin-html#readme) powers a lot of what our rollup config does. It has a lot of options available, for example, to transform the HTML output or set a different template.
We recommend looking into the documentation to get an overview of all available options.
<details>
<summary>View example</summary>
```js
import packageJson from './package.json';
const baseConfig = createSpaConfig({
html: {
transform: [
// inject lang attribute
html => html.replace('<html>', '<html lang="en-GB">'),
// inject app version
html =>
html.replace(
'</body>',
`<script>window.APP_VERSION = "${packageJson.version}"</script></body>`,
),
],
},
});
```
</details>
### Customize polyfills
[@open-wc/rollup-plugin-polyills-loader](https://github.com/open-wc/open-wc/tree/master/packages/rollup-plugin-polyfills-loader#readme) loads polyfills only when necessary based on feature detection.
You can prevent certain polyfills from being loaded or add your own polyfills.
<details>
<summary>View example</summary>
```js
const baseConfig = createSpaConfig({
polyfillsLoader: {
polyfills: {
webcomponents: false,
intersectionObserver: true,
resizeObserver: true,
custom: [
{
name: 'my-feature-polyfill',
path: require.resolve('my-feature-polyfill'),
test: "!('myFeature' in window)",
minify: true,
},
],
},
},
});
```
</details>
### Customize built-in babel plugins
We add some babel plugins by default. These can be overwritten with a different configuration from the config. For example to change the html template minification, or add other modules to be minified:
<details>
<summary>View example</summary>
```js
const baseConfig = createSpaConfig({
babel: {
plugins: [
[
require.resolve('babel-plugin-template-html-minifier'),
{
modules: {
'cool-html': ['html'],
},
htmlMinifier: {
removeComments: false,
},
},
],
],
},
});
```
</details>
## Extending the rollup config
A rollup config is just a plain object. It's easy to extend it using javascript. We recommend using the `deepmerge` library because it is an easy way to merge objects and arrays:
<details>
<summary>View example</summary>
```javascript
import merge from 'deepmerge';
import { createSpaConfig } from '@open-wc/building-rollup';
const baseConfig = createSpaConfig();
export default merge(baseConfig, {
// add your own rollup configuration here
input: './index.html',
output: {
sourcemap: false,
},
plugins: [
// add new plugins
myPlugin(),
],
});
```
</details>
If you have enabled the legacy build option, the `output` option is an array. In that case, you cannot use deepmerge if you need to make changes to the `output` option.
<details>
<summary>View example</summary>
```javascript
import merge from 'deepmerge';
import { createSpaConfig } from '@open-wc/building-rollup';
const baseConfig = createSpaConfig({
legacyBuild: true,
});
// set the sourcemap option on both outputs
baseConfig.output[0].sourcemap = true;
baseConfig.output[1].sourcemap = true;
export default merge(baseConfig, {
input: './index.html',
plugins: [
// add new plugins
myPlugin(),
],
});
```
</details>
#### Copying assets
To copy over assets, such as images, css or json files, we recommend using [rollup-plugin-copy](https://www.npmjs.com/package/rollup-plugin-copy)
<details>
<summary>View example</summary>
```js
import merge from 'deepmerge';
import { createSpaConfig } from '@open-wc/building-rollup';
import copy from 'rollup-plugin-copy';
const baseConfig = createSpaConfig();
export default merge(baseConfig, {
input: './index.html',
plugins: [
copy({
targets: [{ src: 'assets/**/*', dest: '/dist' }],
// set flatten to false to preserve folder structure
flatten: false,
}),
],
});
```
</details>
#### Support for CommonJs modules
Rollup only supports standard es modules (using `import` and `export`). A lot of projects don't use this syntax yet, and instead use the CommonJs module format. This format uses `require` and `module.exports` statements, and is intended for NodeJs.
To support this in Rollup, you can add the [@rollup/plugin-commonjs](https://github.com/rollup/plugins/tree/master/packages/commonjs) plugin.
<details>
<summary>View example</summary>
```js
import merge from 'deepmerge';
import { createSpaConfig } from '@open-wc/building-rollup';
import commonjs from '@rollup/plugin-commonjs';
const baseConfig = createSpaConfig();
export default merge(baseConfig, {
input: './index.html',
plugins: [commonjs()],
});
```
</details>
#### Support for Typescript
<details>
<summary>View example</summary>
To support Typescript in rollup you have multiple options. You can run `tsc`, and then run rollup on the generated JS files. This is useful when you are already running `tsc` for use in other tools, such as a dev server. You can also use the [@rollup/plugin-typescript](https://github.com/rollup/plugins/tree/master/packages/typescript) plugin to integrate with rollup more directly. View their documentation for more information.
```js
import merge from 'deepmerge';
import { createSpaConfig } from '@open-wc/building-rollup';
import typescript from '@rollup/plugin-typescript';
const baseConfig = createSpaConfig();
export default merge(baseConfig, {
input: './index.html',
plugins: [typescript()],
});
```
</details>

View File

@@ -0,0 +1,3 @@
# Demoing ||30
Please see a sub page

View File

@@ -0,0 +1,19 @@
---
title: 'Storybook Addon: Markdown Docs'
eleventyNavigation:
key: 'Storybook Addon: Markdown Docs'
order: 30
parent: Tools
---
[//]: # 'AUTO INSERT HEADER PREPUBLISH'
This extension for [Storybook](https://storybook.js.org/) enables you to use [Markdown JavaScript (mdjs) Format](https://open-wc.org/mdjs/).
## Installation
Please check out [@open-wc/demoing-storybook](https://open-wc.org/demoing/) for a fully integrated setup.
## How it works
This extension converts the mdjs output directly to JavaScript (in CSF format) which can be displayed by storybook docs.

View File

@@ -0,0 +1,321 @@
# Demoing >> Storybook ||10
For demoing, documenting and showcasing different states of your Web Component, we recommend using [storybook](https://storybook.js.org/).
[//]: # 'AUTO INSERT HEADER PREPUBLISH'
# Features
- Create API documentation/playground
- Use Storybook docs mode to showcase your elements within the normal text flow
- Works down to IE11
- Prebuilt storybook UI (for a fast startup)
- Uses es-dev-server (serve modern code while developing)
- Completely separate storybook UI from your code
## Demo
```js script
import '@d4kmor/launch/inline-notification/inline-notification.js';
```
<inline-notification type="tip">
<p>Don't take our word for it but look at <a href="https://open-wc.org/demoing-storybook/?path=/docs/demo-card-docs--simple" target="_blank" rel="noopener noreferrer">the documentation of a demo card<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a> and <a href="https://open-wc.org/demoing-storybook/?path=/docs/decorators-withwebcomponentknobs--example-output" target="_blank" rel="noopener noreferrer">the documentation of the knobs decorator<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a>.</p>
</inline-notification>
## Setup
```bash
npm init @open-wc
# Upgrade > Demoing
```
### Manual
- `yarn add @open-wc/demoing-storybook --dev`
- Copy at minimum the [.storybook](https://github.com/open-wc/open-wc/tree/master/packages/create/src/generators/demoing-storybook/templates/static/.storybook) folder to `.storybook`
- If you want to bring along the examples, you may also copy the `stories` folder.
- Be sure you have a [custom-elements.json](#custom-elementsjson) file.
- Add the following scripts to your package.json
```json
"scripts": {
"storybook": "start-storybook --node-resolve --watch --open",
"storybook:build": "build-storybook"
},
```
## Usage
```bash
npm run storybook
```
### CLI configuration
#### Dev server
The storybook server is based on [es-dev-server](https://open-wc.org/developing/es-dev-server.html) and accepts the same command line args. Check the docs for all available options.
#### Storybook specific
| name | type | description |
| ---------- | ------ | ------------------------------------------------------------- |
| config-dir | string | Where the storybook config files are. Default: `./.storybook` |
| output-dir | string | Rollup build output directory. Default: `./static-storybook` |
### Configuration file
By default, storybook looks for a config file called `main.js` in your config dir (default `.storybook`). In this file you can configure storybook itself, `es-dev-server` and the `rollup` build configuration.
```js
module.exports = {
// Globs of all the stories in your project
stories: ['../stories/*.stories.{js,mdx}'],
// Addons to be loaded, note that you need to import
// them from storybook-prebuilt
addons: [
'storybook-prebuilt/addon-actions/register.js',
'storybook-prebuilt/addon-knobs/register.js',
'storybook-prebuilt/addon-a11y/register.js',
'storybook-prebuilt/addon-docs/register.js',
],
// Configuration for es-dev-server (start-storybook only)
esDevServer: {
nodeResolve: true,
open: true,
},
// Rollup build output directory (build-storybook only)
outputDir: '../dist',
// Configuration for rollup (build-storybook only)
rollup: config => {
return config;
},
};
```
### Create documentation (mdjs)
Create a `*.stories.md` (for example `card.stories.md`) file within the `stories` folder.
This uses the [Markdown JavaScript (mdjs) Format](https://open-wc.org/mdjs/) via [storybook-addon-markdown-docs](https://open-wc.org/demoing/storybook-addon-markdown-docs.html).
````md
```js script
import '../demo-wc-card.js';
export default {
title: 'Demo Card/Docs (markdown)',
parameters: { component: 'demo-wc-card' },
};
```
# Demo Web Component Card
A component meant to display small information with additional data on the back.
// [...] use markdown to format your text
// the following demo is inline
```js story
export const Simple = () => html` <demo-wc-card>Hello World</demo-wc-card> `;
```
## Variations
Show demo with a frame and a "show code" button.
```js preview-story
export const Simple = () => html` <demo-wc-card>Hello World</demo-wc-card> `;
```
## API
The api table will show the data of "demo-wc-card" in your `custom-elements.json`.
<sb-props of="demo-wc-card"></sb-props>
// [...]
````
### Create documentation (mdx)
Create a `*.stories.mdx` (for example `card.stories.mdx`) file within the `stories` folder.
```md
import { Story, Preview, Meta, Props } from '@open-wc/demoing-storybook';
import { html } from 'lit-html';
import '../demo-wc-card.js';
<Meta title="Card|Docs" />
# Demo Web Component Card
A component meant to display small information with additional data on the back.
// [...] use markdown to format your text
<Preview withToolbar>
<Story name="Simple" height="220px">
{html`
<demo-wc-card>Hello World</demo-wc-card>
`}
</Story>
</Preview>
## API
The api table will show the data of "demo-wc-card" in your `custom-elements.json`.
<Props of="demo-wc-card" />
// [...]
```
### Create stories in CSF (Component story format)
Create a `*.stories.js` (for example `card-variations.stories.js`) file within the `stories` folder.
```js
export default {
title: 'Card|Variations',
component: 'demo-wc-card',
};
export const singleComponent = () => html` <demo-wc-card></demo-wc-card> `;
```
For more details see the [official storybook docs](https://storybook.js.org/docs/formats/component-story-format/).
You can import these templates into any other place if needed.
For example in tests:
```js
import { expect, fixture } from '@open-wc/testing';
import { singleComponent } from '../stories/card-variations.stories.js';
it('has a header', async () => {
const el = await fixture(singleComponent);
expect(el.header).to.equal('Your Message');
});
```
### Create API playground
<inline-notification type="tip">
You can find a more interactive version of this in the [withWebComponentsKnobs docs](/demoing-storybook/?path=/docs/decorators-withwebcomponentknobs--example-output).
</inline-notification>
Based on the data in [custom-elements.json](./#custom-elementsjson), we can automatically generate knobs for your stories.
To enable this feature you will need to add an additional decorator.
**MDX**
```md
import { withKnobs, withWebComponentsKnobs } from '@open-wc/demoing-storybook';
<Meta
title="WithWebComponentsKnobs|Docs"
decorators={[withKnobs, withWebComponentsKnobs]}
parameters={{ component: 'demo-wc-card', options: { selectedPanel: 'storybookjs/knobs/panel' } }}
/>
<Story name="Custom Header" height="220px">
{html`
<demo-wc-card header="Harry Potter">A character that is part of a book series...</demo-wc-card>
`}
</Story>
```
**CSF**
```js
import { html } from 'lit-html';
import { withKnobs, withWebComponentsKnobs } from '@open-wc/demoing-storybook';
import '../demo-wc-card.js';
export default {
title: 'Card|Playground',
component: 'demo-wc-card',
decorators: [withKnobs, withWebComponentsKnobs],
parameters: { options: { selectedPanel: 'storybookjs/knobs/panel' } },
};
export const singleComponent = () => html` <demo-wc-card></demo-wc-card> `;
```
For additional features like
- define which components to show knobs for
- showing knobs for multiple different components
- syncing components states to knobs
- Filtering properties and debugging states
please see the official [documentation of the knobs for web components decorator](/demoing-storybook/?path=/docs/decorators-withwebcomponentknobs--example-output).
### custom-elements.json
In order to get documentation for web-components you will need to have a [custom-elements.json](https://github.com/webcomponents/custom-elementsjson) file.
You can handwrite it or better generate it. Depending on the web components sugar you are choosing your mileage may vary.
Please note that the details of the file are still being discussed so we may adopt to changes in `custom-elements.json` without a breaking release.
Known analyzers that output `custom-elements.json`:
- [web-component-analyzer](https://github.com/runem/web-component-analyzer)
- Supports LitElement, Polymer, Vanilla, (Stencil)
- [stenciljs](https://stenciljs.com/)
- Supports Stencil (but does not have all metadata)
It basically looks like this:
```json
{
"version": 2,
"tags": [
{
"name": "demo-wc-card",
"properties": [
{
"name": "header",
"type": "String",
"description": "Shown at the top of the card"
}
],
"events": [],
"slots": [],
"cssProperties": []
}
]
}
```
For a full example see the [./demo/custom-elements.json](./demo/custom-elements.json).
### Additional middleware config like an api proxy
As we are using [es-dev-server](https://open-wc.org/developing/es-dev-server.html) under the hood you can use all it's power. You can use the regular command line flags, or provide your own config via `start storybook -c /path/to/config.js`.
To set up a proxy, you can set up a koa middleware. [Read more about koa here.](https://koajs.com/)
```javascript
const proxy = require('koa-proxies');
module.exports = {
esDevServer: {
port: 9000,
middlewares: [
proxy('/api', {
target: 'http://localhost:9001',
}),
],
},
};
```

View File

@@ -0,0 +1,205 @@
# Development >> Dedupe Mixin ||30
Automatically Deduplicate JavaScript Class Mixins
[//]: # 'AUTO INSERT HEADER PREPUBLISH'
## Features
- Small
- Fast
- Typed
## Usage
Apply it to each mixin in the chain to make sure they are not applied more than once to the final class.
```js
import { dedupeMixin } from '@open-wc/dedupe-mixin';
export const MyMixin = dedupeMixin(
superclass =>
class MyMixin extends superclass {
// your mixin code goes here
},
);
```
## What is a Mixin?
> A mixin is an abstract subclass; i.e. a subclass definition that may be applied to different superclasses to create a related family of modified classes.
>
> - Gilad Bracha and William Cook, [Mixin-based Inheritance](http://www.bracha.org/oopsla90.pdf)
Let's take for example Logging. Imagine you have 3 Pages
- Red
- Green
- Blue
```
+----------+
| Page |
+----------+
| | |
+----------+ | +-----------+
| | |
+---------+ +-----------+ +----------+
| PageRed | | PageGreen | | PageBlue |
+----+----+ +-----------+ +----------+
```
```js
class Page {}
class PageRed extends Page {}
class PageGreen extends Page {}
class PageBlue extends Page {}
```
Now we want to log whenever someone goes on Page Red.
To archive that we extend Page Red and make a Logged Page Red.
```
+----------+
| Page |
+-+--+--+--+
| | |
+----------+ | +-----------+
| | |
+----+----+ +-----+-----+ +-----+----+
| PageRed | | PageGreen | | PageBlue |
+----+----+ +-----------+ +----------+
|
+----+----+
| Logged |
| PageRed |
+---------+
```
```js
class Page {}
class PageRed extends Page {}
class PageGreen extends Page {}
class PageBlue extends Page {}
class LoggedPagRed extends PageRed {}
```
If we want to start logging for PageGreen we have an issue:
- we can't put the logic in `Page` as Blue should not be logged
- we can't reuse the logic in `Logged PageGreen` as we can not extend from 2 source (even if we could it would mean conflicting info in Red and Green)
What we can do is put it in an "external" place and write it so it can be "mixed in".
```
+----------+ +----------+
| Page | | Logging* |
+-+--+--+--+ +----------+
| | |
+----------+ | +-----------+
| | |
+-----+----+ +-----+-----+ +-----+----+
| PageRed | | PageGreen | | PageBlue |
| with | | with | +----------+
| Logging* | | Logging* |
+----------+ +-----------+
```
```js
// defining the Mixin
export const LoggingMixin = superclass =>
class LoggingMixin extends superclass {
// logging logic
};
class Page {}
// applying a Mixin
class PageRed extends LoggingMixin(Page) {}
class PageGreen extends LoggingMixin(Page) {}
class PageBlue extends Page {}
```
With that approach we can extract logic into a separate code pieces we can use where needed.
For a more in depth technical explanation please read [Real Mixins with JavaScript Classes](https://justinfagnani.com/2015/12/21/real-mixins-with-javascript-classes/).
## Why is Deduping of Mixins Necessary?
We now want all logging to the Red, Green, and Blue pages.
Easy enough - as we can now apply the LoggingMixin on the Page itself.
```
+----------+ +----------+
| Page | | Logging* |
| with | +----------+
| Logging* |
+-+--+--+--+
| | |
+----------+ | +-----------+
| | |
+-----+----+ +-----+-----+ +-----+----+
| PageRed | | PageGreen | | PageBlue |
+----------+ | with | +----------+
| Logging* |
+-----------+
```
However, Team Green were eager to launch, so they already applied `LoggingMixin` to their Page class. When we apply it to the base `Page` class, Mixin is now applied twice 😱
Suddenly, the Green page will print each log twice - not what we originally had in mind.
What we need to do is make sure that each Mixin is attached only once even if we try to apply it multiple times.
Generally the more generic a mixin is, the higher the chance becomes that is gets applied more than once. As a mixin author you can't control how it is used, and can't always predict it. So as a safety measure it is always recommended to create deduping mixins.
```js
import { dedupeMixin } from '@open-wc/dedupe-mixin';
export const MyMixin = dedupeMixin(
superclass =>
class MyMixin extends superclass {
// your mixin code goes here
},
);
```
You can see exactly this situation in the demo.
By applying dedupeMixin to the mixin function, before we export it, we can be sure that our mixin class will only take effect once, even if it is mixed in to multiple base classes in the inheritance chain.
- [no-dedupe](/dedupe-mixin/demo/no-dedupe/) "fails" by logging Green two times
- [with-dedupe](/dedupe-mixin/demo/with-dedupe/) "succeeds" by logging Green one time as well
You can check the source code for both on [github](https://github.com/open-wc/open-wc/tree/master/packages/dedupe-mixin/demo-typed).
### Nested examples
You may think that the above example is too simple and can be solved by aligning on when to do changes.
However in most real live scenarios the situation is much more complicated 🙈
Mixins can be extended and just because you import a class it does not meant that this class has some Mixins pre applied.
Consider this example:
```
+----------+ +----------+ +----------+
| Page | | Logging* | | Feature |
| with | +----+-----+ | with |
| Logging* | | | Metrics* |
+-+--+--+--+ +----+-----+ +----+--+--+
| | | | Metrics* | | |
+----------+ | +-----------+ +----------+ | +------
| | | |
+-----+----+ +-----+-----+ +-----+----+ +------+-------+
| PageRed | | PageGreen | | PageBlue | | WaterFeature |
+----------+ +-----------+ | with | +--------------+
| Metrics* |
+----------+
```
- Pages generally only need Logging
- There is however also more advanced Metrics System which extends Logging
- Metrics was separately developed for Features
- When we now want to get the same Metrics on Page Blue we get duplicate logging without consciously applying logging even once (we do `class PageBlue extends MetricsMixin(Page) {}`)
- Only deduping can help in these scenarios
_Ascii Graphics made with [AsciiFlow](http://asciiflow.com/)_

View File

@@ -1,25 +1,13 @@
---
permalink: 'init/index.html'
title: Create Open Web Components
section: guides
tags:
- guides
---
# Create Open Web Components
# Development >> Generator ||15
Web component project scaffolding.
[//]: # 'AUTO INSERT HEADER PREPUBLISH'
## Usage
```bash
npm init @open-wc
```
<div class="custom-block warning"><p class="custom-block-title">WARNING</p> <p><code>npm init</code> requires node 10 &amp; npm 6 or higher</p></div>
This will kickstart a menu guiding you through all available actions.
```
@@ -87,7 +75,7 @@ npm init @open-wc
<br/>
- `Testing`<br>
This generator adds a complete testing setup with Web Test Runner.
This generator adds a complete testing setup with Karma.
<br/>
- `Demoing`<br>

View File

@@ -0,0 +1,3 @@
# Development ||10
Please see a sub page

View File

@@ -0,0 +1,196 @@
# Development >> Lit Helpers ||20
A library with helpers functions for working with [lit-html](https://lit-html.polymer-project.org/) and [lit-element](https://lit-element.polymer-project.org/)
[//]: # 'AUTO INSERT HEADER PREPUBLISH'
## Installation
```bash
npm i --save @open-wc/lit-helpers
```
## Spread directives
Spread directives can be used to set an arbitrary collection of properties, attributes or event listeners on an element without knowing all the keys in advance.
The spread directives work by taking in an object of data to spread. Because of `lit-html` syntax, we need one attribute to apply the directive to. It doesn't actually matter what the name of this attribute is, we recommend sticking to the convention of using `...` as the attribute name.
### Regular spread
The regular `spread` directive can be used to set properties, attribute or event listeners. It uses the same syntax as `lit-html` for distinguishing different types of bindings:
```js
import { html, render } from 'lit-html';
import { spread } from '@open-wc/lit-helpers';
render(
html`
<div
...=${spread({
'my-attribute': 'foo',
'?my-boolean-attribute': true
'.myProperty': { foo: 'bar' },
'@my-event': () => console.log('my-event fired'),
})}
></div>
`,
document.body,
);
```
### Property spread
Because spreading properties is a common use case, you can use the `spreadProps` directive so that you don't need prefix each property with a `.`. This is especially useful when the data comes from external sources.
```js
import { html, render } from 'lit-html';
import { spreadProps } from '@open-wc/lit-helpers';
render(html` <div ...="${spreadProps({ propertyA: 'a', propertyB: 'b' })}"></div> `, document.body);
```
### Attribute spread
Since `spread` already spreads attribute as the default case, we do not need a separate `spreadAttributes` directive.
### Re-rendering
When re-rendering, values are updated if they changed from the previous value. We use the same strict equality check (`!==`) as `lit-html` for this.
Unlike `lit-html`, when spreading attributes we remove the attribute if it is set to `null` or `undefined`.
If an entry in the spread data is removed, the property is set to undefined, the attribute is removed or the event listener is deregistered.
Example:
```js
function renderSpread(data) {
render(html` <div ...="${spread(data)}"></div> `, document.body);
}
// result: <div foo="bar">
renderSpread({ foo: 'bar' });
// result: <div foo="buz">
renderSpread({ foo: 'buz' });
// result: <div foo="buz" lorem="ipsum">
renderSpread({ foo: 'buz', lorem: 'ipsum' });
// result: <div foo="buz">
renderSpread({ foo: 'buz' });
// result: <div>
renderSpread({ foo: 'undefined' });
```
## Live binding
For efficiency, lit-html does not set properties or attributes if they did not change since the previous render. This can cause problems when the properties are changed outside of lit-html's control. The `live` directive can be used to dirty check the element's live value, and set the property or attribute if it changed.
A great example for this, is the DOM element's `scrollTop` property which changes without lit-html knowing about it when the user scrolls.
By using the `live` directive, you can make sure it is always in sync with the value rendered by `lit-html`:
```js
html` <my-element .scrollTop=${live(scrollTop)}></my-element> `;
```
## Privately Settable Read-Only Properties
`ReadOnlyPropertiesMixin` provides a way for based on `LitElement` (or it's parent class `UpdatingElement`) to define properties by adding `readOnly: true` to their property declaration. Those properties are read-only from the outside, but can be updated internally with the `setReadOnlyProperties` method.
```js
import { ReadOnlyPropertiesMixin } from '@open-wc/lit-helpers';
import { LitElement } from 'lit-element';
class SettableElement extends ReadOnlyPropertiesMixin(LitElement) {
static get properties() {
return {
timestamp: { type: Number, readOnly: true },
};
}
constructor() {
super();
this.timestamp = Date.now();
}
updateTime() {
this.setReadOnlyProperties({ timestamp: Date.now() });
}
render() {
return html`
<button @click="${this.updateTime}">Update Time</button>
<time>${this.timestamp}</time>
`;
}
}
```
The mixin also supports the `@property` decorator.
```js
import { ReadOnlyPropertiesMixin } from '@open-wc/lit-helpers';
import { LitElement, property } from 'lit-element';
class SettableElement extends ReadOnlyPropertiesMixin(LitElement) {
@property({ type: Number, readOnly: true }) timestamp = Date.now();
updateTime() {
this.setReadOnlyProperties({ timestamp: Date.now() });
}
render() {
return html`
<button @click="${this.updateTime}">Update Time</button>
<time>${this.timestamp}</time>
`;
}
}
```
### Known Limitations
#### Order of Application Matters
Currently, this mixin only works properly when applied to LitElement (or UpdatingElement) directly. In other words, if you have a component which inherits like the example below, then `ReadOnlyPropertiesMixin` must be applied to `LitElement` only.
```js
// Bad
class Lowest extends LitElement {
@property({ readOnly: true }) lowestProperty = undefined;
}
class Highest extends ReadOnlyPropertiesMixin(Lowest) {
// will not work as expected
@property({ readOnly: true }) highestProperty = undefined;
}
```
```js
// Good
class Lowest extends ReadOnlyPropertiesMixin(LitElement) {
@property({ readOnly: true }) lowestProperty = undefined;
}
class Highest extends Lowest {
@property({ readOnly: true }) highestProperty = undefined;
}
```
#### Properties Must be Initialized
Read only properties must be initialized with some value, even if the value is `undefined`. This is because the mixin allows one free setting, to support class field initialization.
```js
// Bad
class Uninitialized extends ReadOnlyPropertiesMixin(LitElement) {
@property({ readOnly: true }) uninitialized;
}
// Good
class Initialized extends ReadOnlyPropertiesMixin(LitElement) {
@property({ readOnly: true }) initialized = undefined;
}
```

View File

@@ -0,0 +1,323 @@
# Development >> Scoped Elements ||40
Scope element tag names avoiding naming collision and allowing to use different versions of the same web component in your code.
## Installation
```bash
npm i --save @open-wc/scoped-elements
```
## Usage
1. Import `ScopedElementsMixin` from `@open-wc/scoped-elements`.
```js
import { ScopedElementsMixin } from '@open-wc/scoped-elements';
```
2. Import the classes of the components you want to use.
```js
import { MyButton } from './MyButton.js';
import { MyPanel } from './MyPanel.js';
```
3. Apply `ScopedElementsMixin` and define the tags you want to use for your components.
```js
class MyElement extends ScopedElementsMixin(LitElement) {
static get scopedElements() {
return {
'my-button': MyButton,
'my-panel': MyPanel,
};
}
}
```
> WARNING: If you are going to use elements that are globally defined you have to declare them in `scopedElements` as well. This is required because we are trying to work as close as possible to the future Scoped Custom Element Registries feature and, by the moment, there is not going ot be inheritance between registries.
>
> You can declare them like in the following example:
>
> ```js
> static get scopedElements() {
> return {
> 'old-button': customElements.get('old-button'),
> 'my-panel': MyPanel,
> };
> }
> ```
>
> If you try to register the same element globally AND locally with the exact same name AND class instance it will reuse the global tag name and NOT scope it.
4. Use your components in your html.
```js
render() {
return html`
<my-panel class="panel">
<my-button>${this.text}</my-button>
</my-panel>
`;
}
```
### Complete example
```js
import { css, LitElement } from 'lit-element';
import { ScopedElementsMixin } from '@open-wc/scoped-elements';
import { MyButton } from './MyButton.js';
import { MyPanel } from './MyPanel.js';
export class MyElement extends ScopedElementsMixin(LitElement) {
static get scopedElements() {
return {
'my-button': MyButton,
'my-panel': MyPanel,
};
}
static get styles() {
return css`
.panel {
padding: 10px;
background-color: grey;
}
`;
}
static get properties() {
return {
text: String,
};
}
render() {
return html`
<my-panel class="panel">
<my-button>${this.text}</my-button>
</my-panel}>
`;
}
}
```
### Lazy scoped components
In some situations may happen that you want to use a component in your templates that is not already loaded at the moment of defining the scoped elements map. The `ScopedElementsMixin` provides the `defineScopedElement` method to define scoped elements at any time.
```js
import { LitElement } from 'lit-element';
import { ScopedElementsMixin } from '@open-wc/scoped-elements';
import { MyPanel } from './MyPanel.js';
export class MyElement extends ScopedElementsMixin(LitElement) {
static get scopedElements() {
return {
'my-panel': MyPanel,
};
}
constructor() {
super();
import('./MyButton.js').then(({ MyButton }) => this.defineScopedElement('my-button', MyButton));
}
render() {
return html`
<my-panel class="panel">
<my-button>${this.text}</my-button>
</my-panel>
`;
}
}
```
### Obtaining a scoped tag name
Maybe you want to create a scoped element programmatically and don't know which one is the scoped tag name? No problem, there is a static method called `getScopedTagName` that would help you for that.
```js
import { LitElement } from 'lit-element';
import { ScopedElementsMixin } from '@open-wc/scoped-elements';
import { MyButton } from './MyButton.js';
import { MyPanel } from './MyPanel.js';
export class MyElement extends ScopedElementsMixin(LitElement) {
static get scopedElements() {
return {
'my-panel': MyPanel,
'my-button': MyButton,
};
}
constructor() {
super();
const scopedTagName = this.constructor.getScopedTagName('my-button');
// do whatever you need with the scopedTagName
}
// ...
}
```
## Motivation
Complex Web Component applications are often developed by several teams across organizations. In that scenario it is common that shared component libraries are used by teams to create a homogeneous look and feel or just to avoid creating the same components multiple times, but as those libraries evolve problems between different versions of the same library may appear, as teams may not be able to evolve and update their code at the same velocity. This causes bottlenecks in software delivery that should be managed by the teams and complex build systems, to try to alleviate the problem.
[Scoped Custom Element Registries](https://github.com/w3c/webcomponents/issues/716) is a proposal that will solve this problem, but until it is ready, or a polyfill becomes available, we have to _scope_ custom element tag names if we want to use different versions of those custom elements in our code. This package allows you to forget about how custom elements are defined, registered and scopes their tag names if it is necessary, and avoids the name collision problem.
## Use case and demos
Consider the following setup
- **Team Blue** owns **Page A**
- **Team Green** owns **Page B**
- **Team Black** owns **Feature A & B**
1. Everything is good and [your app is live](https://open-wc.org/scoped-elements/demo/before-nesting/) [[code](https://github.com/open-wc/open-wc/tree/master/packages/scoped-elements/demo/before-nesting)] with both pages.
2. **Team Black** releases a new version (**2.x**) of **Feature B** which unfortunately needs to be breaking in order to support new use-cases.
3. **Team Blue** (on **Page A**) does not use any of those new use cases, and they have a tight deadline to meet, so they cannot update right now.
4. **Team Green** (on **Page B**) has to deliver an important functionality to your end users, but they need to upgrade to **Feature B 2.x** since it can only be solved with this new version.
5. Since **Feature A 1.x & 2.x** are both used in the same app, this will lead to nested dependencies, which then will lead to [catastrophic failure, and errors](https://open-wc.org/scoped-elements/demo/no-scope/) [[code](https://github.com/open-wc/open-wc/tree/master/packages/scoped-elements/demo/no-scope)].
Two possible solutions come to mind:
1. Temporarily (!) allow shipping similar source code (most breaking releases are not a total rewrite) and scope them via `@open-wc/scoped-elements`; see the "fixed" example [with-scope](https://open-wc.org/scoped-elements/demo/with-scope/) [[code](https://github.com/open-wc/open-wc/tree/master/packages/scoped-elements/demo/with-scope)] running with nested dependencies.
2. Synchronizing updates of shared dependencies - e.g. make sure **Team Blue** & **Team Green** always use the same version when releasing. This can be a viable solution however it comes with a high organizational overhead and is hard to scale up (for 10+ teams)
#### Technical explanation of scenario
The simplified app has the following dependencies
- app
- page-a
- feature-a 1.x
- feature-b 1.x
- page-b
- feature-a 2.x
- feature-b 1.x
which leads to the following node_modules tree
```
├── node_modules
│ ├── feature-a
│ ├── feature-b
│ ├── page-a
│ └── page-b
│ └── node_modules
│ └── feature-a
├── demo-app.js
└── index.html
```
To demonstrate, we made three demos:
1. [before-nesting](https://open-wc.org/scoped-elements/demo/before-nesting/) [[code](https://github.com/open-wc/open-wc/tree/master/packages/scoped-elements/demo/before-nesting)] In this demo, everything works fine as **Page A and B** both are using the same version of **Feature A**
2. [no-scope](https://open-wc.org/scoped-elements/demo/no-scope/) [[code](https://github.com/open-wc/open-wc/tree/master/packages/scoped-elements/demo/no-scope)] **Feature A** version **1.x** and **2.x** are imported via self registering entry points which leads to the following error message, because the `feature-a` component tries to register multiple times:
```
Uncaught DOMException: Failed to execute 'define' on 'CustomElementRegistry': the name "feature-a" has already been used with this registry
at [...]/node_modules/page-b/node_modules/feature-a/feature-a.js:3:16
```
3. [with-scope](https://open-wc.org/scoped-elements/demo/with-scope/) [[code](https://github.com/open-wc/open-wc/tree/master/packages/scoped-elements/demo/with-scope)] This example successfully fixes the problem by using `ScopedElementsMixin` on both **Page A** and **Page B**.
## How it works
`ScopedElementsMixin` is mixed into your LitElement and via `static get scopedElements()` you define the tags and classes you wanna use in your elements template.
Under the hood it changes your template so `<my-button>${this.text}</my-button>` becomes `<my-button-2748 data-tag-name="my-button">${this.text}</my-button-2748>`.
Every auto-defined scoped elements gets a random\* 4 digits number suffix. This suffix changes every time to make sure developers are not inclined to use it the generated tag name as a styling hook. Additionally the suffix allows scoped-elements and traditional self-defined elements to coexist, avoiding name collision.
\* it is actually a global counter that gets initialized with a random starting number on load
## Limitations
1. Components imported via npm **SHOULD NOT** be self registering components. If a shared component (installed from npm) does not offer an export to the class alone, without the registration side effect, then this component may not be used. E.g. every component that calls `customElement.define`.
```js
export class MyEl { ... }
customElement.define('my-el', MyEl);
```
Or uses the `customElement` typescript decorator
```ts
@customElement('my-el')
export class MyEl { ... }
```
Only side effects free class exports may be used
```js
export class MyEl { ... }
```
2. Every component that uses sub components should use `scoped-elements`. Any import to a self registering component can potentially result in a browser exception - completely breaking the whole application.
3. Imported elements should be fully side effect free (not only element registration)
4. Currently, only `lit-element` is supported (though other elements/rendering engines could be incorporated in the future).
5. You cannot use tag selectors in css, but you could use an id, a class name or even a property instead.
```css
🚫 my-panel {
width: 300px;
}
✅ .panel {
width: 300px;
}
```
6. You cannot use tag names using javascript querySelectors, but you could use an id, a class name or even a property instead.
```js
🚫 this.shadowRoot.querySelector('my-panel');
✅ this.shadowRoot.querySelector('.panel');
```
7. Using `scoped-elements` may result in a performance degradation of up to 8%.
8. Loading of duplicate/similar source code (most breaking releases are not a total rewrite) should always be a temporary solution.
9. Often, temporary solutions tend to become more permanent. Be sure to focus on keeping the lifecycle of nested dependencies short.
## Performance
We are using [Tachometer](https://github.com/Polymer/tachometer) to measure the performance penalty of using the scoped elements feature. The chosen test application is a slight variation of the [Polymer Shop Application](https://shop.polymer-project.org).
This is an example of the results obtained running the performance test.
```
⠋ Auto-sample 560 (timeout in 16m27s)
┌─────────────┬───────────────┐
│ Version │ <none> │
├─────────────┼───────────────┤
│ Browser │ chrome │
│ │ 80.0.3987.106 │
├─────────────┼───────────────┤
│ Sample size │ 610 │
└─────────────┴───────────────┘
┌─────────────────────────────┬────────────┬─────────────────────┬─────────────────┬──────────────────────────┐
│ Benchmark │ Bytes │ Avg time │ vs lit-element │ vs scoped-elements-mixin │
├─────────────────────────────┼────────────┼─────────────────────┼─────────────────┼──────────────────────────┤
│ lit-element │ 281.24 KiB │ 285.72ms - 286.69ms │ │ faster │
│ │ │ │ - │ 2% - 2% │
│ │ │ │ │ 5.68ms - 7.1ms │
├─────────────────────────────┼────────────┼─────────────────────┼─────────────────┼──────────────────────────┤
│ scoped-elements-mixin │ 283.21 KiB │ 292.08ms - 293.11ms │ slower │ │
│ │ │ │ 2% - 2% │ - │
│ │ │ │ 5.68ms - 7.10ms │ │
└─────────────────────────────┴────────────┴─────────────────────┴─────────────────┴──────────────────────────┘
```
## Special thanks
This package was initially inspired by [carehtml](https://github.com/bashmish/carehtml) and we would like to thank [@bashmish](https://github.com/bashmish) for his work on it.

View File

@@ -0,0 +1,3 @@
# Experimental ||60
Please see a sub page

View File

@@ -1,14 +1,6 @@
---
permalink: 'mdjs/index.html'
title: Markdown JavaScript (mdjs) Format
section: guides
tags:
- guides
---
# Experimental >> Markdown JavaScript || 10
# Markdown JavaScript (mdjs) Format
Mdjs is a format that allows you to use JavaScript with Markdown, to create interactive demos. It does so by "annotating" JavaScript that should be executed in Markdown.
Markdown JavaScript (Mdjs) is a format that allows you to use JavaScript with Markdown, to create interactive demos. It does so by "annotating" JavaScript that should be executed in Markdown.
To annotate we use a code block with `js script`.

10
docs/docs/index.md Normal file
View File

@@ -0,0 +1,10 @@
---
title: Documentation
eleventyNavigation:
key: Docs
order: 20
---
Our documentation is hand crafted and optimized to serve as a dictionary to look up details once the need arises.
For a more guided learning experience please visit our [Guides](../guides/).

View File

@@ -0,0 +1,3 @@
# Legacy ||50
Please see a sub page

View File

@@ -0,0 +1,18 @@
# Legacy >> Legacy Projects || 10
Over the last years we have created different projects and recommendations. During this time certain projects have become deprecated as we moved on to different tools or approaches.
This doesn't mean that we've completely dropped support for these projects. While we don't feature them on the main website, we still maintain and support these projects. We don't develop any new features or functionalities, but we will continue to support bugfixes and in some cases update along with the dependent tooling.
The documentation for our legacy projects is maintained in the github readmes:
### Legacy projects
- [es-dev-server](https://github.com/open-wc/es-dev-server) is now replaced by [web-dev-server](https://modern-web.dev/docs/dev-server/overview/)
- [testing-karma](https://github.com/open-wc/legacy/tree/master/packages/testing-karma) and [karma-esm](https://github.com/open-wc/legacy/tree/master/packages/karma-esm) we now recommend [web-test-runner](https://modern-web.dev/docs/test-runner/overview/)
- [testing-karma-bs](https://github.com/open-wc/legacy/tree/master/packages/testing-karma-bs) we now recommend [web-test-runner](https://modern-web.dev/docs/test-runner/overview/) & [@web/test-runner-browserstack](https://modern-web.dev/docs/test-runner/browser-launchers/browserstack/)
- [rollup-plugin-index-html](https://github.com/open-wc/legacy/tree/master/packages/rollup-plugin-index-html) we now recommend `@web/rollup-plugin-html`
- [webpack-import-meta-loader](https://github.com/open-wc/legacy/tree/master/packages/webpack-import-meta-loader) we now recommend [babel-plugin-bundled-import-meta](https://www.npmjs.com/package/babel-plugin-bundled-import-meta)
- [building-webpack](https://github.com/open-wc/legacy/tree/master/packages/building-webpack) we now recommend rollup over webpack
- [webpack-index-html-plugin](https://github.com/open-wc/legacy/tree/master/packages/webpack-index-html-plugin) we now recommend rollup over webpack
- [storybook-addon-web-components-knobs](https://github.com/open-wc/legacy/tree/master/packages/storybook-addon-web-components-knobs) storybook v6 has a new better knobs system

View File

@@ -0,0 +1,3 @@
# Linting >> EsLint Plugin Lit A11y || 10
sse

View File

@@ -0,0 +1,106 @@
# Linting >> EsLint Plugin Lit A11y >> Overview || -5
Accessibility linting plugin for lit-html.
Most of the rules are ported from [eslint-plugin-jsx-a11y](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y), and made to work with [lit-html](https://lit-html.polymer-project.org/) templates and custom elements.
## Installation
You'll first need to install [ESLint](http://eslint.org):
```
$ npm i eslint --save-dev
```
Next, install `eslint-plugin-lit-a11y`:
```
$ npm install eslint-plugin-lit-a11y --save-dev
```
**Note:** If you installed ESLint globally (using the `-g` flag) then you must also install `eslint-plugin-lit-a11y` globally.
## Usage
Add `lit-a11y` to the plugins section of your `.eslintrc` configuration file. You can omit the `eslint-plugin-` prefix:
```json
{
"plugins": ["lit-a11y"]
}
```
Then configure the rules you want to use under the rules section.
```json
{
"rules": {
"lit-a11y/rule-name": 2
}
}
```
## Configuration
You may also extend the recommended configuration like so:
```json
{
"extends": ["plugin:lit-a11y/recommended"]
}
```
By default, any tagged template literal that starts with `html` is linted. Example:
```js
html`<img />`;
```
It could be the case, however, that you're using multiple rendering libraries in a project, like for example [`htm`](https://github.com/developit/htm), which also uses a `html` tagged template literal, but has a slightly different syntax than lit-html. In this case you can specify the following option, to make sure only lit-html tagged template literals are linted:
```json
{
"settings": {
"litHtmlSources": true
}
}
```
This will cause the plugin to lint _only_ `html` tagged template literals that are imported from either `'lit-html'` or `'lit-element'`.
If you're importing lit-html from a package that re-exports lit-html, like for example `@apollo-elements/lit-apollo`, you can specify `@apollo-elements/lit-apollo` as a valid litHtmlSource like so:
```json
{
"settings": {
"litHtmlSources": ["@apollo-elements/lit-apollo"]
}
}
```
## Supported Rules
- [lit-a11y/accessible-emoji](./rules/accessible-emoji.md)
- [lit-a11y/alt-text](./rules/alt-text.md)
- [lit-a11y/anchor-has-content](./rules/anchor-has-content.md)
- [lit-a11y/anchor-is-valid](./rules/anchor-is-valid.md)
- [lit-a11y/aria-activedescendant-has-tabindex](./rules/aria-activedescendant-has-tabindex.md)
- [lit-a11y/aria-attr-valid-value](./rules/aria-attr-valid-value.md)
- [lit-a11y/aria-attrs](./rules/aria-attrs.md)
- [lit-a11y/aria-role](./rules/aria-role.md)
- [lit-a11y/aria-unsupported-elements](./rules/aria-unsupported-elements.md)
- [lit-a11y/autocomplete-valid](./rules/autocomplete-valid.md)
- [lit-a11y/click-events-have-key-events](./rules/click-events-have-key-events.md)
- [lit-a11y/heading-has-content](./rules/heading-has-content.md)
- [lit-a11y/iframe-title](./rules/iframe-title.md)
- [lit-a11y/img-redundant-alt](./rules/img-redundant-alt.md)
- [lit-a11y/mouse-events-have-key-events](./rules/mouse-events-have-key-events.md)
- [lit-a11y/no-access-key](./rules/no-access-key.md)
- [lit-a11y/no-autofocus](./rules/no-autofocus.md)
- [lit-a11y/no-distracting-elements](./rules/no-distracting-elements.md)
- [lit-a11y/no-invalid-change-handler](./rules/no-invalid-change-handler.md)
- [lit-a11y/no-redundant-role](./rules/no-redundant-role.md)
- [lit-a11y/role-has-required-aria-attrs](./rules/role-has-required-aria-attrs.md)
- [lit-a11y/role-supports-aria-attr](./rules/role-supports-aria-attr.md)
- [lit-a11y/scope](./rules/scope.md)
- [lit-a11y/tabindex-no-positive](./rules/tabindex-no-positive.md)

View File

@@ -0,0 +1,50 @@
# Linting >> EsLint Plugin Lit A11y >> accessible-emoji (DEPRECATED)
Enforce emojis are wrapped in `<span>` and provide screenreader access.
> Emoji help us communicate complex ideas very easily. When used in native apps and applications, emoji are reasonably accessible to screen readers, but on the web we need to do a little more to make sure everyone can understand emoji.
- [Léonie Watson](https://tink.uk/accessible-emoji/)
While many modern user agents are able to announce emoji to screen reader users, this rule is still useful for apps targetting older user agents.
## Rule Details
This rule aims to prevent inaccessible use of emoji in lit-html templates.
Examples of **incorrect** code for this rule:
```js
html`
<span>🐼</span>
<i role="img" aria-label="Panda face">🐼</i>
`;
```
Examples of **correct** code for this rule:
```js
html` <span role="img" aria-label="Panda face">🐼</span> `;
```
```js
html` <span role="img" alt="Panda face">🐼</span> `;
```
```js
html`
<span role="img" aria-label="Panda face">🐼</span>
<span role="img" alt="Panda face">🐼</span>
<label id="label">Clown</label>
<span role="img" aria-labelledby="label">🤡</span>
`;
```
## When Not To Use It
If you exclusively target modern user agents which explicitly support emoji in plain text, or if you do not use emoji in your lit-html templates.
## Further Reading
[Accessible emoji by Léonie Watson](https://tink.uk/accessible-emoji/)

View File

@@ -0,0 +1,51 @@
# Linting >> EsLint Plugin Lit A11y >> alt-text
Enforce that all elements that require alternative text have meaningful information to relay back to the end user. This is a critical component of accessibility for screen reader users in order for them to understand the content's purpose on the page. By default, this rule checks for alternative text on `<img>` elements.
This rule also permits images which are completely removed from the <acronym>AOM (Accessibility Object Model)</acronym>:
> **Sometimes there is non-text content that really is not meant to be seen or understood by the user.** Transparent images used to move text over on a page; an invisible image that is used to track usage statistics; and a swirl in the corner that conveys no information but just fills up a blank space to create an aesthetic effect are all examples of this. Putting alternative text on such items just distracts people using screen readers from the content on the page. Not marking the content in any way, though, leaves users guessing what the non-text content is and what information they may have missed (even though they have not missed anything in reality). This type of non-text content, therefore, is marked or implemented in a way that assistive technologies (AT) will ignore it and not present anything to the user.
- [WCAG 1.1.1](https://www.w3.org/WAI/WCAG21/Understanding/non-text-content.html#examples)
## Rule Details
Examples of **incorrect** code for this rule:
```js
html`
<img src="${src}" />
<div role="img"></div>
`;
```
Examples of **correct** code for this rule:
```js
html`
<img src="${src}" alt="" />
<img src="${src}" aria-hidden="true" />
<img src="${src}" alt="foo" />
<img src="${src}" aria-label="foo" />
<label id="label">foo</label>
<img src="${src}" aria-labelledby="label" />
<div role="img" aria-label="foo"></div>
<div role="img" aria-labelledBy="label"></div>
<div role="img" aria-hidden="true"></div>
`;
```
## Further Reading
- [WCAG 1.1.1](https://www.w3.org/WAI/WCAG21/Understanding/non-text-content.html)
- [axe-core, object-alt](https://dequeuniversity.com/rules/axe/3.2/object-alt)
- [axe-core, image-alt](https://dequeuniversity.com/rules/axe/3.2/image-alt)
- [axe-core, input-image-alt](https://dequeuniversity.com/rules/axe/3.2/input-image-alt)
- [axe-core, area-alt](https://dequeuniversity.com/rules/axe/3.2/area-alt)

View File

@@ -0,0 +1,31 @@
# Linting >> EsLint Plugin Lit A11y >> anchor-has-content
Enforce that anchors have content and that the content is accessible to screen readers. Accessible means that it is not hidden using the `aria-hidden` attribute. Refer to the references to learn about why this is important.
## Rule Details
This rule aims to...
Examples of **incorrect** code for this rule:
```js
html`
<a></a>
<a><some-text-bearing-component aria-hidden></some-text-bearing-component></a>
`;
```
Examples of **correct** code for this rule:
```js
html`
<a>Anchor Content!</a>
<a><some-text-bearing-component></some-text-bearing-component></a>
`;
```
## Further Reading
- [WCAG 2.4.4](https://www.w3.org/WAI/WCAG21/Understanding/link-purpose-in-context)
- [WCAG 4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value)
- [axe-core, link-name](https://dequeuniversity.com/rules/axe/3.2/link-name)

View File

@@ -0,0 +1,235 @@
# Linting >> EsLint Plugin Lit A11y >> anchor-is-valid
Performs validity check on anchor hrefs. Warns when anchors are used as buttons.
The HTML `<a>` element, with a valid `href` attribute, is formally defined as representing a **hyperlink**. That is, a link between one HTML document and another, or between one location inside an HTML document and another location inside the same document.
In fact, the interactive, underlined `<a>` element has become so synonymous with web navigation that this expectation has become entrenched inside browsers, assistive technologies such as screen readers and in how people generally expect the internet to behave. In short, anchors should navigate.
The use of JavaScript frameworks and libraries has made it very easy to add or subtract functionality from the standard HTML elements. This has led to _anchors_ often being used in applications based on how they look and function instead of what they represent.
Whilst it is possible, for example, to turn the `<a>` element into a fully functional `<button>` element with ARIA, the native user agent implementations of HTML elements are to be preferred over custom ARIA solutions.
## How do I resolve this error?
### Case: I want to perform an action and need a clickable UI element
The native user agent implementations of the `<a>` and `<button>` elements not only differ in how they look and how they act when activated, but also in how the user is expected to interact with them. Both are perfectly clickable when using a mouse, but keyboard users expect `<a>` to activate on `enter` only and `<button>` to activate on _both_ `enter` and `space`.
This is exacerbated by the expectation sighted users have of how _buttons_ and _anchors_ work based on their appearance. Therefore we find that using _anchors_ as _buttons_ can easily create confusion without a relatively complicated ARIA and CSS implementation that only serves to create an element HTML already offers and browsers already implement fully accessibly.
We are aware that sometimes _anchors_ are used instead of _buttons_ to achieve a specific visual design. When using the `<button>` element this can still be achieved with styling but, due to the meaning many people attach to the standard underlined `<a>` due its appearance, please reconsider this in the design.
Consider the following:
```js
html`
<a href="javascript:void(0)" @click=${foo}>Perform action</a>
<a href="#" @click=${foo}>Perform action</a>
<a @click=${foo}>Perform action</a>
`;
```
All these _anchor_ implementations indicate that the element is only used to execute JavaScript code. All the above should be replaced with:
```js
html` <button @click=${foo}>Perform action</button> `;
```
### Case: I want navigable links
An `<a>` element without an `href` attribute no longer functions as a hyperlink. That means that it can no longer accept keyboard focus or be clicked on. The documentation for [no-noninteractive-tabindex](no-noninteractive-tabindex.md) explores this further. Preferably use another element (such as `div` or `span`) for display of text.
To properly function as a hyperlink, the `href` attribute should be present and also contain a valid _URL_. _JavaScript_ strings, empty values or using only **#** are not considered valid `href` values.
Valid `href` attributes values are:
```js
html`
<a href="/some/valid/uri">Navigate to page</a>
<a href="/some/valid/uri#top">Navigate to page and location</a>
<a href="#top">Navigate to internal page location</a>
`;
```
### Case: I need the HTML to be interactive, don't I need to use an `a` tag for that?
An `<a>` tag is not inherently interactive. Without an href attribute, it really is no different to a `<div>`.
Let's look at an example that is not accessible by all users:
```js
html` <a class="thing" @mouseenter=${() => (this.showSomething = true)}> ${label} </a> `;
```
If you need to create an interface element that the user can click on, consider using a button:
```js
html`
<button class="thing"
@click={() => this.showSomething = true}>
${label}
</button>
`;
```
If you want to navigate while providing the user with extra functionality, for example in the `@mouseenter` event, use an anchor with an `href` attribute containing a URL or path as its value.
```js
html`
<a class="thing" href=${someValidPath} @mouseenter=${() => (this.showSomething = true)}>
${label}
</a>
`;
```
If you need to create an interface element that the user can mouse over or mouse out of, consider using a div element. In this case, you may need to apply a role of presentation or an interactive role. Interactive ARIA roles include `button`, `link`, `checkbox`, `menuitem`, `menuitemcheckbox`, `menuitemradio`, `option`, `radio`, `searchbox`, `switch` and `textbox`.
```js
html`
<div class="thing" role="menuitem" @click=${() => (this.showSomething = true)}>
@mouseenter=${() => (this.showSomething = true)}> ${label}
</div>
`;
```
In the example immediately above an `@click` event handler was added to provide the same experience mouse users enjoy to keyboard-only and touch-screen users. Never fully rely on mouse events alone to expose functionality.
### Case: I understand the previous cases but still need an element resembling a link that is purely clickable
We recommend, without reserve, that elements resembling anchors should navigate. This will provide a superior user experience to a larger group of users out there.
However, we understand that developers are not always in total control of the visual design of web applications. In cases where it is imperative to provide an element resembling an anchor that purely acts as a click target with no navigation as result, we would like to recommend a compromise.
Again change the element to a `<button>`:
```js
html`
<button class="link-button" type="button" @click=${() => (this.showSomething = true)}>
Press me, I look like a link
</button>
`;
```
Then use styling to change its appearance to that of a link:
```css
.link-button {
background-color: transparent;
border: none;
cursor: pointer;
text-decoration: underline;
display: inline;
margin: 0;
padding: 0;
}
.link-button:hover,
.link-button:focus {
text-decoration: none;
}
```
This button element can now also be used inline in text.
Once again we stress that this is an inferior implementation and some users will encounter difficulty to use your website, however, it will allow a larger group of people to interact with your website than the alternative of ignoring the rule's warning.
## Rule Details
This rule takes one optional object argument of type object:
```json
{
"rules": {
"lit-a11y/anchor-is-valid": [
"error",
{
"allowHash": true,
"aspects": ["noHref", "invalidHref", "preferButton"]
}
]
}
}
```
For the `allowHash` option (default `true`), if set to `false`, the empty hash or "scroll-to-top" link will be considered an error.
Examples of **incorrect** code for this rule with `"allowHash": false`:
```js
html` <a href="#"></a> `;
```
Examples of **correct** code for this rule with `"allowHash": false`:
```js
html` <a href="#top"></a> `;
```
For the `aspects` option, these strings determine which sub-rules are run. This allows omission of certain error types in restrictive environments.
- `noHref`: Checks whether an anchor contains an `href` attribute.
- `invalidHref`: Checks if a given `href` value is valid.
- `preferButton`: Checks if anchors have been used as buttons.
If omitted, all sub-rule aspects will be run by default. This is the recommended configuration for all cases except where the rule becomes unusable due to well founded restrictions.
The option must contain at least one `aspect`.
Examples of **incorrect** code for this rule:
Anchors should be a button:
```js
html`
<a @click=${foo}></a>
<a href="javascript:void(0)" @click=${foo}></a>
<a href=${'javascript:void(0)'} @click=${foo}></a>
<a href=${`javascript:void(0)`} @click=${foo}></a>
`;
```
Missing `href` attribute:
```js
html`
<a></a>
<a href="{undefined}"></a>
<a href="{null}"></a>
`;
```
Invalid `href` attribute:
```js
html`
<a href="javascript:void(0)"></a>
<a href={"javascript:void(0)"}></a>
<a href={`javascript:void(0)`}></a>
`;
```
Examples of **correct** code for this rule:
```js
html`
<a href="https://github.com"></a>
<a href="#"></a>
<a href="#section"></a>
<a href="foo"></a>
<a href="/foo/bar"></a>
<a href=${someValidPath}></a>
<a href="https://github.com" @click=${foo}></a>
<a href="#section" @click=${foo}></a>
<a href="foo" @click=${foo}></a>
<a href="/foo/bar" @click=${foo}></a>
<a href=${someValidPath} @click=${foo}></a>
`;
```
## Further Reading
- [WCAG 2.1.1](https://www.w3.org/WAI/WCAG21/Understanding/keyboard)
- [WebAIM - Introduction to Links and Hypertext](http://webaim.org/techniques/hypertext/)
- [Links vs. Buttons in Modern Web Applications](https://marcysutton.com/links-vs-buttons-in-modern-web-applications/)
- [Using ARIA - Notes on ARIA use in HTML](https://www.w3.org/TR/using-aria/#NOTES)

View File

@@ -0,0 +1,35 @@
# Linting >> EsLint Plugin Lit A11y >> aria-activedescendant-has-tabindex
`aria-activedescendant` is used to manage focus within a [composite widget](https://www.w3.org/TR/wai-aria/#composite).
The element with the attribute `aria-activedescendant` retains the active document
focus; it indicates which of its child elements has secondary focus by assigning
the ID of that element to the value of `aria-activedescendant`. This pattern is
used to build a widget like a search typeahead select list. The search input box
retains document focus so that the user can type in the input. If the down arrow
key is pressed and a search suggestion is highlighted, the ID of the suggestion
element will be applied as the value of `aria-activedescendant` on the input
element.
Because an element with `aria-activedescendant` must be tabbable, it must either
have an inherent `tabIndex` of zero or declare a `tabIndex` of zero with the `tabIndex`
attribute.
## Rule Details
This rule aims to...
Examples of **incorrect** code for this rule:
```js
html` <div aria-activedescendant=${someID}></div> `;
```
Examples of **correct** code for this rule:
```js
html` <div aria-activedescendant="foo" tabindex="0"></div> `;
```
## Further Reading
- [MDN, Using the aria-activedescendant attribute](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-activedescendant_attribute)

Some files were not shown because too many files have changed in this diff Show More