diff --git a/.changeset/early-dolphins-build.md b/.changeset/early-dolphins-build.md new file mode 100644 index 000000000..9ec0dde9a --- /dev/null +++ b/.changeset/early-dolphins-build.md @@ -0,0 +1,5 @@ +--- +'babel-plugin-extend-docs': patch +--- + +Still replace tags if templates uses `.foo=${{ key: 'value' }}` diff --git a/.changeset/famous-bears-confess.md b/.changeset/famous-bears-confess.md new file mode 100644 index 000000000..f76dc859f --- /dev/null +++ b/.changeset/famous-bears-confess.md @@ -0,0 +1,23 @@ +--- +'remark-extend': minor +--- + +BREAKING CHANGE: Changing approach from overwriting extending files to using an import-based approach. + +Removed features: + +- `::replaceFrom` +- `::replaceBetween` +- `::addMdAfter` +- `::removeFrom` +- `::removeBetween` + +Added Features: + +- `::import` +- `::importBlock` +- `::importBlockContent` +- `::importSmallBlock` +- `::importSmallBlockContent` + +See the updated documentation for how to use this new approach. diff --git a/.changeset/odd-beds-clean.md b/.changeset/odd-beds-clean.md new file mode 100644 index 000000000..867fe5e5a --- /dev/null +++ b/.changeset/odd-beds-clean.md @@ -0,0 +1,5 @@ +--- +'providence-analytics': minor +--- + +BREAKING: Align exports fields. If you want to import from CLI instead of main entrypoint (`import { ... } from 'providence-analytics';`) using export maps, you can now do so with `import { ... } from 'providence-analytics/src/cli';` instead of `import { ... } from 'providence-analytics/src/cli/index.js';`. diff --git a/.changeset/silly-ants-melt.md b/.changeset/silly-ants-melt.md new file mode 100644 index 000000000..abd080871 --- /dev/null +++ b/.changeset/silly-ants-melt.md @@ -0,0 +1,40 @@ +--- +'@lion/accordion': minor +'@lion/button': minor +'@lion/calendar': minor +'@lion/checkbox-group': minor +'@lion/collapsible': minor +'@lion/combobox': minor +'@lion/core': minor +'@lion/dialog': minor +'@lion/fieldset': minor +'@lion/form': minor +'@lion/form-core': minor +'@lion/form-integrations': minor +'@lion/helpers': minor +'@lion/icon': minor +'@lion/input': minor +'@lion/input-amount': minor +'@lion/input-date': minor +'@lion/input-datepicker': minor +'@lion/input-email': minor +'@lion/input-iban': minor +'@lion/input-range': minor +'@lion/input-stepper': minor +'@lion/listbox': minor +'@lion/localize': minor +'@lion/overlays': minor +'@lion/pagination': minor +'@lion/progress-indicator': minor +'@lion/radio-group': minor +'@lion/select': minor +'@lion/select-rich': minor +'@lion/steps': minor +'@lion/switch': minor +'@lion/tabs': minor +'@lion/textarea': minor +'@lion/tooltip': minor +'@lion/validate-messages': minor +--- + +BREAKING: Align exports fields. This means no more wildcards, meaning you always import with bare import specifiers, extensionless. Import components where customElements.define side effect is executed by importing from '@lion/package/define'. For multi-component packages this defines all components (e.g. radio-group + radio). If you want to only import a single one, do '@lion/package/define-radio' for example for just lion-radio. diff --git a/.eleventyignore b/.eleventyignore new file mode 100644 index 000000000..3dc0e48c6 --- /dev/null +++ b/.eleventyignore @@ -0,0 +1,4 @@ +node_modules/** +/docs/_assets +/docs/_includes +/docs/_data diff --git a/.eslintignore b/.eslintignore index cdffae36b..9a1a5c81d 100644 --- a/.eslintignore +++ b/.eslintignore @@ -3,3 +3,5 @@ coverage/ bundlesize/ .history/ *.d.ts +_site-dev +_site diff --git a/.eslintrc.js b/.eslintrc.js index 7ffa5bd87..dc4b294f7 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -5,6 +5,8 @@ module.exports = { files: ['**/*.js'], rules: { 'wc/guard-super-call': 'off', // types will prevent you from calling the super if it's not in the base class, making the guard unnecessary + 'no-await-in-loop': 'off', + 'import/no-unresolved': 'off', // eslint not smart enough atm to understand package exports maps }, }, { diff --git a/.gitignore b/.gitignore index 7e8d30fea..09f5a7753 100644 --- a/.gitignore +++ b/.gitignore @@ -42,6 +42,15 @@ local.log ## browserstack browserstack.err +## Rocket ignore files (need to be the full relative path to the folders) +docs/_merged_data/ +docs/_merged_assets/ +docs/_merged_includes/ + debug.log -!packages/singleton-manager/demo/**/node_modules +_site +_site-dev + +## generated test fiels +__output diff --git a/.markdownlint.json b/.markdownlint.json index beda11a36..da2c8aa29 100644 --- a/.markdownlint.json +++ b/.markdownlint.json @@ -2,6 +2,9 @@ "line-length": { "line_length": -1 }, + "fenced-code-language": false, + "no-inline-html": false, + "first-line-h1": false, "no-trailing-punctuation": { "punctuation": ".,;。,;:!" } diff --git a/.prettierignore b/.prettierignore index 85b847f95..63ecf735b 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,5 @@ coverage/ CHANGELOG.md bundlesize/ +_site +_site-dev diff --git a/.storybook/main.js b/.storybook/main.js deleted file mode 100644 index 81b7fdd96..000000000 --- a/.storybook/main.js +++ /dev/null @@ -1,81 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -module.exports = { - stories: [ - '../{packages,packages-node}/*/README.md', - '../{packages,packages-node}/*/docs/*.md', - '../{packages,packages-node}/*/docs/!(assets)**/*.md', - '../packages/helpers/*/README.md', - '../docs/README.md', - '../docs/**/*.md', - '../README.md', - '../demo/README.md', - '../demo/docs/*.md', - ], - addons: [ - // order of tabs in addons panel - 'storybook-prebuilt/addon-actions/register.js', - 'storybook-prebuilt/addon-knobs/register.js', - 'storybook-prebuilt/addon-a11y/register.js', - 'storybook-prebuilt/addon-docs/register.js', - // no tab in addons panel (e.g. load order does not matter here) - 'storybook-prebuilt/addon-backgrounds/register.js', - 'storybook-prebuilt/addon-links/register.js', - 'storybook-prebuilt/addon-viewport/register.js', - ], - addons: [ - 'storybook-prebuilt/addon-docs/register.js', - 'storybook-prebuilt/addon-actions/register.js', - 'storybook-prebuilt/addon-knobs/register.js', - 'storybook-prebuilt/addon-a11y/register.js', - 'storybook-prebuilt/addon-backgrounds/register.js', - 'storybook-prebuilt/addon-links/register.js', - 'storybook-prebuilt/addon-viewport/register.js', - ], - esDevServer: { - nodeResolve: true, - watch: true, - open: true, - }, - rollup: config => { - // temporarily hard copy all needed global files as all tested rollup plugins flatten the - // directory structure - // `rollup-plugin-copy` might work if issue 37 is resolved - // https://github.com/vladshcherbin/rollup-plugin-copy/issues/37 - config.plugins.push({ - generateBundle() { - this.emitFile({ - type: 'asset', - fileName: 'packages/form-integrations/dev-assets/FormatMixinDiagram-1.svg', - source: fs.readFileSync( - path.join( - __dirname, - '../packages/form-integrations/dev-assets/FormatMixinDiagram-1.svg', - ), - ), - }); - this.emitFile({ - type: 'asset', - fileName: 'packages/form-integrations/dev-assets/FormatMixinDiagram-2.svg', - source: fs.readFileSync( - path.join( - __dirname, - '../packages/form-integrations/dev-assets/FormatMixinDiagram-2.svg', - ), - ), - }); - this.emitFile({ - type: 'asset', - fileName: 'packages/form-integrations/dev-assets/FormatMixinDiagram-3.svg', - source: fs.readFileSync( - path.join( - __dirname, - '../packages/form-integrations/dev-assets/FormatMixinDiagram-3.svg', - ), - ), - }); - }, - }); - }, -}; diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html deleted file mode 100644 index dcd7618be..000000000 --- a/.storybook/preview-head.html +++ /dev/null @@ -1,27 +0,0 @@ - diff --git a/.storybook/preview.js b/.storybook/preview.js deleted file mode 100755 index aa4ddac3a..000000000 --- a/.storybook/preview.js +++ /dev/null @@ -1,49 +0,0 @@ -import { - addDecorator, - addParameters, - setCustomElements, - withA11y, -} from '@open-wc/demoing-storybook'; -import { sortEachDepth } from '../packages/helpers/index.js'; - -async function run() { - // const customElements = await ( - // await fetch(new URL('../custom-elements.json', import.meta.url)) - // ).json(); - setCustomElements({}); - - addDecorator(withA11y); - - addParameters({ - a11y: { - config: {}, - options: { - checks: { 'color-contrast': { options: { noScroll: true } } }, - restoreScroll: true, - }, - }, - docs: { - iframeHeight: '200px', - }, - options: { - showRoots: true, - storySort: sortEachDepth([ - [ - 'Intro', - 'Forms', - 'Buttons', - 'Overlays', - 'Navigation', - 'Localize', - 'Icons', - 'Others', - '...', - ], - ['Intro', 'Features Overview', '...', 'Validation', 'System'], - ['Overview', '...', '_internals'], - ]), - }, - }); -} - -run(); diff --git a/README.md b/README.md index 7c110f021..77c2b9e4f 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,60 @@ -# Lion Web Components +

+ Lion +

-```js script -export default { - title: 'Intro/Lion Web Components', -}; -``` +

+ GitHub Actions workflow status + GitHub Actions workflow status + Todos +

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

+ Website + · + Guides + · + Components + · + Documentation + · + Blog +

-For some more details see the [announcement blog post](https://medium.com/ing-blog/ing-open-sources-lion-a-library-for-performant-accessible-flexible-web-components-22ad165b1d3d). +

-[![TODOs](https://badgen.net/https/api.tickgit.com/badgen/github.comgithub.com/ing-bank/lion)](https://www.tickgit.com/browse?repo=github.com/ing-bank/lion) +**Lion is a set of highly performant, accessible and flexible Web Components.!** -## Demos +They provide an unopinionated, white-label layer that can be extended to your own layer of components. -We do have a [live Storybook](http://lion-web-components.netlify.com) which shows all our components. +- **High Performance:** Focused on great performance in all relevant browsers with a minimal number of dependencies. +- **Accessibility:** Aimed at compliance with the WCAG 2.1 AA standard to create components that are accessible for everybody. +- **Flexibility:** Provides solutions through Web Components and JavaScript classes which can be used, adopted and extended to fit all needs. +- **Modern Code:** Lion is distributes as pure es modules. +- **Exposes functions/classes and Web Components:** Ships a functionality in it's most appropriate form. -**Please note:** This project uses Yarn [Workspaces](https://classic.yarnpkg.com/en/docs/workspaces). If you want to run all demos locally you need to get [Yarn](https://classic.yarnpkg.com/en/docs/install) and install all dependencies by executing `yarn install`. - -The code examples make use of [Javascript tagged template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) which are a key component of the [lit-html engine](https://lit-html.polymer-project.org/) used in Lion. -Additionally imports like `import '@lion/form/lion-form.js'` need to be transformed somehow, for example by [es-dev-server](https://open-wc.org/developing/es-dev-server.html#node-resolve). - -## Features - -- High Performance - Focused on great performance in all relevant browsers with a minimal number of dependencies -- Accessibility - Aimed at compliance with the WCAG 2.1 AA standard to create components that are accessible for everybody -- Flexibility - Provides solutions through Web Components and JavaScript classes which can be used, adopted and extended to fit all needs -- Pure ES Modules -- Exposes functions/classes and Web Components - -> Note: These demos may look a little bland but that is on purpose. They only come with functional stylings. +> Note: Our demos may look a little bland but that is on purpose. They only come with functional stylings. > This makes sense as the main use case is to extend those components and if you do you do not want to override existing stylings. +

+ Explore the Lion Guides  ▶ +

+ ## How to install ```bash @@ -82,7 +104,7 @@ You can also use the lion elements directly, although this is likely not a commo ```html @@ -150,18 +172,17 @@ The accessibility column indicates whether the functionality is accessible in it ## Technologies -Lion Web Components aims to be future proof and use well-supported proven technology. The stack we have chosen should reflect this. +Lion Web Components aims to be future-proof and use well-supported proven technology. The stack we have chosen should reflect this. - [lit-html](https://lit-html.polymer-project.org) and [lit-element](https://lit-element.polymer-project.org) - [npm](http://npmjs.com) - [yarn](https://yarnpkg.com) -- [open-wc](https://open-wc.org) -- [Karma](https://karma-runner.github.io) +- [Open Web Components](https://open-wc.org) +- [Modern Web](https://modern-web.dev) - [Mocha](https://mochajs.org) - [Chai](https://www.chaijs.com) - [ESLint](https://eslint.org) - [prettier](https://prettier.io) -- [Storybook](https://storybook.js.org) - [ES modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) - Lots and lots of tests @@ -180,6 +201,8 @@ Check out our [coding guidelines](https://lion-web-components.netlify.app/?path= ## How to contribute +**Please note:** This project uses Yarn [Workspaces](https://classic.yarnpkg.com/en/docs/workspaces). If you want to run all demos locally you need to get [Yarn](https://classic.yarnpkg.com/en/docs/install) and install all dependencies by executing `yarn install`. + Lion Web Components are only as good as its contributions. Read our [contribution guide](https://github.com/ing-bank/lion/blob/master/CONTRIBUTING.md) and feel free to enhance/improve Lion. We keep feature requests closed while we're not working on them. diff --git a/assets/logo.png b/assets/logo.png new file mode 100644 index 000000000..b4528d72d Binary files /dev/null and b/assets/logo.png differ diff --git a/demo/docs/20-lea-tabs.md b/demo/docs/20-lea-tabs.md deleted file mode 100644 index 5c3154b10..000000000 --- a/demo/docs/20-lea-tabs.md +++ /dev/null @@ -1,231 +0,0 @@ -# lea Tabs - -> This is not a real implementation! -> -> It is an example of how you can reuse the functionality of Lion to create your own Style System - -`lea-tabs` implements tabs view to allow users to quickly move between a small number of equally important views. - -```js script -import { html } from '@lion/core'; -import { LitElement } from '@lion/core'; - -import '../lea-tabs.js'; -import '../lea-tab.js'; -import '../lea-tab-panel.js'; - -export default { - title: 'Intro/Tabs Example', -}; -``` - -```js preview-story -export const main = () => html` - - Info - Info page with lots of information about us. - Work - Work page that showcases our work. - -`; -``` - -## How to use - -### Installation - -```sh -npm i --save @lion/tabs; -``` - -### Usage - -```js -import '@lion/tabs/lea-tabs.js'; -``` - -```html - - Info - Info page with lots of information about us. - Work - Work page that showcases our work. - -``` - -## Examples - -### Selected Index - -You can set the `selectedIndex` to select a certain tab. - -```js preview-story -export const selectedIndex = () => html` - - Info - Info page with lots of information about us. - Work - Work page that showcases our work. - -`; -``` - -### Slots Order - -The tab and panel slots are ordered by DOM order. - -This means you can switch the grouping in your `lea-tabs` from tab + panel to all tabs first or all panels first. - -```js preview-story -export const slotsOrder = () => html` - - Info - Work - Info page with lots of information about us. - Work page that showcases our work. - -`; -``` - -### Distribute New Elements - -Below, we demonstrate on how you could dynamically add new tab + panels. - -```js preview-story -export const distributeNewElements = () => { - const tagName = 'lea-tabs-experimental'; - if (!customElements.get(tagName)) { - customElements.define( - tagName, - class extends LitElement { - static get properties() { - return { - __collection: { type: Array }, - }; - } - render() { - return html` -

Append

- - - tab 1 - panel 1 - tab 2 - panel 2 - -
-

Push

- - - tab 1 - panel 1 - tab 2 - panel 2 - ${this.__collection.map( - item => html` - ${item.button} - ${item.panel} - `, - )} - - `; - } - constructor() { - super(); - this.__collection = []; - } - __handleAppendClick() { - const tabsElement = this.shadowRoot.querySelector('#appendTabs'); - const c = 2; - const n = Math.floor(tabsElement.children.length / 2); - for (let i = n + 1; i < n + c; i += 1) { - const tab = document.createElement('lea-tab'); - tab.setAttribute('slot', 'tab'); - tab.innerText = `tab ${i}`; - const panel = document.createElement('lea-tab-panel'); - panel.setAttribute('slot', 'panel'); - panel.innerText = `panel ${i}`; - tabsElement.append(tab); - tabsElement.append(panel); - } - } - __handlePushClick() { - const tabsElement = this.shadowRoot.querySelector('#pushTabs'); - const i = Math.floor(tabsElement.children.length / 2) + 1; - this.__collection = [ - ...this.__collection, - { - button: `tab ${i}`, - panel: `panel ${i}`, - }, - ]; - } - }, - ); - } - return html` `; -}; -``` - -One way is by creating the DOM elements and appending them as needed. - -Inside your `lea-tabs` extension, an example for appending nodes on a certain button click: - -```js -__handleAppendClick() { - const tabsAmount = this.children.length / 2; - const tab = document.createElement('lea-tab'); - tab.setAttribute('slot', 'tab'); - tab.innerText = `tab ${tabsAmount + 1}`; - const panel = document.createElement('lea-tab-panel'); - panel.setAttribute('slot', 'panel'); - panel.innerText = `panel ${tabsAmount + 1}`; - this.append(tab); - this.append(panel); -} -``` - -The other way is by adding data to a Lit property where you loop over this property in your template. -You then need to ensure this causes a re-render. - -```js -__handlePushClick() { - const tabsAmount = this.children.length; - myCollection = [ - ...myCollection, - { - button: `tab ${tabsAmount + 1}`, - panel: `panel ${tabsAmount + 1}`, - }, - ]; - renderMyCollection(); -} -``` - -Make sure your template re-renders when myCollection is updated. - -```html - - ${myCollection.map(item => html` - ${item.button} - ${item.panel} - `)} - -``` - -## Rationale - -### No separate active/focus state when using keyboard - -We will immediately switch content as all our content comes from light dom (e.g. no latency) - -See Note at - -> It is recommended that tabs activate automatically when they receive focus as long as their -> associated tab panels are displayed without noticeable latency. This typically requires tab -> panel content to be preloaded. - -### Panels are not focusable - -Focusable elements should have a means to interact with them. Tab panels themselves do not offer any interactiveness. -If there is a button or a form inside the tab panel then these elements get focused directly. diff --git a/demo/lea-tab-panel.js b/demo/lea-tab-panel.js deleted file mode 100644 index c9b048d53..000000000 --- a/demo/lea-tab-panel.js +++ /dev/null @@ -1,3 +0,0 @@ -import { LeaTabPanel } from './src/LeaTabPanel.js'; - -customElements.define('lea-tab-panel', LeaTabPanel); diff --git a/demo/lea-tab.js b/demo/lea-tab.js deleted file mode 100644 index 5fb7621ab..000000000 --- a/demo/lea-tab.js +++ /dev/null @@ -1,3 +0,0 @@ -import { LeaTab } from './src/LeaTab.js'; - -customElements.define('lea-tab', LeaTab); diff --git a/demo/lea-tabs.js b/demo/lea-tabs.js deleted file mode 100644 index 29092c758..000000000 --- a/demo/lea-tabs.js +++ /dev/null @@ -1,3 +0,0 @@ -import { LeaTabs } from './src/LeaTabs.js'; - -customElements.define('lea-tabs', LeaTabs); diff --git a/docs/404.md b/docs/404.md new file mode 100644 index 000000000..9bdb1a891 --- /dev/null +++ b/docs/404.md @@ -0,0 +1,4 @@ +--- +permalink: 404.html +layout: layout-404 +--- diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index 74610c7a8..000000000 --- a/docs/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Coding Guidelines - -```js script -export default { - title: 'Guidelines/Intro', -}; -``` - -First be sure to understand our [definitions](?path=/docs/guidelines-definitions--page). - -- [Guidelines for Styling](?path=/docs/guidelines-styling--page) -- [Guidelines for Scoped Elements](?path=/docs/guidelines-scoped-elements--page) -- [Guidelines for SubClasser APIs](?path=/docs/guidelines-subclasser-apis--page) diff --git a/docs/_assets/_static/icons/android-chrome-192x192.png b/docs/_assets/_static/icons/android-chrome-192x192.png new file mode 100644 index 000000000..3de4ce367 Binary files /dev/null and b/docs/_assets/_static/icons/android-chrome-192x192.png differ diff --git a/docs/_assets/_static/icons/android-chrome-512x512.png b/docs/_assets/_static/icons/android-chrome-512x512.png new file mode 100644 index 000000000..2f4b2547d Binary files /dev/null and b/docs/_assets/_static/icons/android-chrome-512x512.png differ diff --git a/docs/_assets/_static/icons/apple-touch-icon.png b/docs/_assets/_static/icons/apple-touch-icon.png new file mode 100644 index 000000000..bc556fc6f Binary files /dev/null and b/docs/_assets/_static/icons/apple-touch-icon.png differ diff --git a/docs/_assets/_static/icons/favicon-16x16.png b/docs/_assets/_static/icons/favicon-16x16.png new file mode 100644 index 000000000..da06d9604 Binary files /dev/null and b/docs/_assets/_static/icons/favicon-16x16.png differ diff --git a/docs/_assets/_static/icons/favicon-32x32.png b/docs/_assets/_static/icons/favicon-32x32.png new file mode 100644 index 000000000..31c2edecd Binary files /dev/null and b/docs/_assets/_static/icons/favicon-32x32.png differ diff --git a/docs/_assets/_static/icons/mstile-150x150.png b/docs/_assets/_static/icons/mstile-150x150.png new file mode 100644 index 000000000..58eec7e9a Binary files /dev/null and b/docs/_assets/_static/icons/mstile-150x150.png differ diff --git a/docs/_assets/_static/icons/safari-pinned-tab.svg b/docs/_assets/_static/icons/safari-pinned-tab.svg new file mode 100644 index 000000000..78295723e --- /dev/null +++ b/docs/_assets/_static/icons/safari-pinned-tab.svg @@ -0,0 +1,39 @@ + + + + +Created by potrace 1.11, written by Peter Selinger 2001-2013 + + + + + diff --git a/docs/_assets/logo.svg b/docs/_assets/logo.svg new file mode 100644 index 000000000..dec159884 --- /dev/null +++ b/docs/_assets/logo.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/_assets/style.css b/docs/_assets/style.css new file mode 100644 index 000000000..04e56e465 --- /dev/null +++ b/docs/_assets/style.css @@ -0,0 +1,23 @@ +body[layout='layout-home'] .markdown-body .call-to-action:nth-of-type(2) { + --primary-color: #222; + --primary-color-lighter: #333; + --primary-color-darker: #000; +} + +.markdown-body img { + width: 100%; +} + +mdjs-preview { + margin-bottom: 20px; +} + +#main-header a[href="/blog/"] { + display: none; +} + +@media screen and (min-width: 1024px) { + #main-header a[href="/blog/"] { + display: block; + } +} diff --git a/docs/_assets/variables.css b/docs/_assets/variables.css new file mode 100644 index 000000000..181a4b183 --- /dev/null +++ b/docs/_assets/variables.css @@ -0,0 +1,41 @@ +html { + --primary-color: #a07f23; + --primary-color-lighter: #d1a62f; + --primary-color-darker: #705918; + --primary-color-accent: #cee5f6; + --primary-text-color: #2c3e50; + --primary-lines-color: #ccc; + + /* Contrast colors */ + --contrast-color-light: #fff; + --contrast-color-dark: #1d3557; + + /* background-colors */ + --page-background: white; + --footer-background: rgba(0, 0, 0, 0.1); + + --text-color: black; +} + +html.dark { + --primary-color: #e63946; + --primary-color-lighter: #e25761; + --primary-color-darker: #a22831; + --primary-color-accent: #cee5f6; + --primary-text-color: #eee; + + /* Contrast colors */ + --contrast-color-light: #fff; + --contrast-color-dark: #1d3557; + + /* background-colors */ + --page-background: #333; + --footer-background: #4f4f4f; + + --text-color: white; + + --markdown-octicon-link: white; + --markdown-syntax-background-color: #a0a0a0; + --markdown-link-color: #fb7881; + --markdown-blockquote-color: #c9e3ff; +} diff --git a/docs/_data/footer.json b/docs/_data/footer.json new file mode 100644 index 000000000..c5053c70b --- /dev/null +++ b/docs/_data/footer.json @@ -0,0 +1,41 @@ +[ + { + "name": "Discover", + "children": [ + { + "text": "Blog", + "href": "/blog/" + }, + { + "text": "Help and Feedback", + "href": "https://github.com/ing-bank/lion/issues" + } + ] + }, + { + "name": "Follow", + "children": [ + { + "text": "Github", + "href": "https://github.com/ing-bank/lion" + }, + { + "text": "Twitter", + "href": "https://twitter.com/daKmoR" + }, + { + "text": "Slack", + "href": "/about/slack/" + } + ] + }, + { + "name": "Support", + "children": [ + { + "text": "Contribute", + "href": "https://github.com/ing-bank/lion/blob/master/CONTRIBUTING.md" + } + ] + } +] diff --git a/docs/_data/site.cjs b/docs/_data/site.cjs new file mode 100644 index 000000000..7cf118bc8 --- /dev/null +++ b/docs/_data/site.cjs @@ -0,0 +1,22 @@ +module.exports = async function () { + return { + dir: 'ltr', + lang: 'en', + name: 'Lion', + description: 'Fundamental white label web components for building your design system', + socialLinks: [ + { + name: 'GitHub', + url: 'https://github.com/ing-bank/lion', + }, + ], + gitSiteUrl: 'https://github.com/ing-bank/lion', + gitBranch: 'master', + helpUrl: 'https://github.com/ing-bank/lion/issues', + logoAlt: 'Lion Logo', + iconColorMaskIcon: '#3f93ce', + iconColorMsapplicationTileColor: '#1d3557', + iconColorThemeColor: '#1d3557', + analytics: 'G-151V7YV71K', + }; +}; diff --git a/docs/about/slack.md b/docs/about/slack.md new file mode 100644 index 000000000..90b4a8b8d --- /dev/null +++ b/docs/about/slack.md @@ -0,0 +1,5 @@ +# Slack + +You can also find us on the Polymer Slack in the [#lion](https://polymer.slack.com/archives/CJGFWJN9J) channel. + +You can join the Polymer Slack by visiting [https://www.polymer-project.org/slack-invite](https://www.polymer-project.org/slack-invite). diff --git a/docs/blog/controlling-exports.md b/docs/blog/controlling-exports.md new file mode 100644 index 000000000..1500627fc --- /dev/null +++ b/docs/blog/controlling-exports.md @@ -0,0 +1,173 @@ +--- +title: Controlling exports +published: true +description: Maintainer can now define their public api of a package itself. +date: 2021-03-09 +tags: [javascript, exports] +cover_image: /blog/images/controlling-exports-cover-image.jpg +--- + +When publishing npm packages it can often be hard to understand what users are actually using. + +Basically, JavaScript allows you to write imports like this + +```js +import { addLeadingZero } '@lion/localize/src/date/utils/addLeadingZero.js'; +``` + +We as the maintainers of that package however consider this internal code, so any changes to it will not result in a new breaking change update. +So if you depend on this directly then your code may break with any minor or patch update. + +So why would we even "allow" such imports? Because so far there was no way to actually define and enforce what a maintainer considers to be the public API of the package. Now, with the introduction of node's [Package Entry Points](https://nodejs.org/api/packages.html#packages_package_entry_points) and the adoption of it in [@rollup/plugin-node-resolve](https://github.com/rollup/plugins/tree/master/packages/node-resolve#package-entrypoints) it can now be used in node, [@web/dev-server](https://modern-web.dev/docs/dev-server/overview/) and [rollup](https://rollupjs.org/). + +How can you use those `Package Entry Points`? + +Let's assume you have these two files in your `src` directory. + +```js +// src/index.js +export { foo } from './public.js'; + +// src/public.js +export const foo = 'public foo'; + +// src/internal.js +export const bar = 'internal bar'; +``` + +If you publish the package "normally" then users will be able to write imports like this + +```js +import { foo } from 'my-pkg'; +import { foo } from 'my-pkg/src/public.js'; +import { bar } from 'my-pkg/src/internal.js'; +``` + +This has multiple issues, described in use cases: + +1. Case 1: For maintenance purposes, we want to split `public.js` in `featureA.js` and `helpers.js`. Now all imports that use `import { foo } from 'my-pkg/src/public.js';` will break. +2. Case 2: We found a package that solved what we did in `internal.js` in a more generic way. We don't treat it as public API, so we actually go ahead and get rid of this file. Now all imports for `import { bar } from 'my-pkg/src/internal.js';` will break. + +Instead, what we actually want is all our consumers using the intended public API, which is + +```js +import { foo } from 'my-pkg'; +``` + +This way, above cases 1 and 2 just don't have any effect and we can freely refactor our codebase without introducing breaking changes. This means we can keep improving our code without disturbing our users. It's a win-win situation 🎉 + +Now, if someone tries to use a not defined export, like + +```js +import { bar } from 'my-pkg/src/internal.js'; +``` + +Then an error will be thrown + +``` +Could not resolve import "my-pkg/src/internal.js" +``` + +If a users needs access to `bar` then a GitHub Issue/Discussion should be opened to request it. +Maintainers can then have a discussion if they want to make this part of the public API or not. + +## Using consumer import in your own code + +An additional benefit of using Package Entry Points is that you can write imports in the same way as your consumers. + +So instead of writing demos or tests like + +```js +import { LionInput } from '../src/LionInput.js'; +``` + +we can now write + +```js +import { LionInput } from '@lion/input'; +``` + +This has the following benefits: + +- We can make sure everything we are demoing/testing is actually part of the public API +- Users can read / copy our demo code and it just works +- We can move files around without needing to adjust our demos/docs/tests + +## Exports for a single web component + +Usage: + +```js +// only the classes +import { MyElement } from 'my-element'; + +// OR + +// execute customElements.define +import 'my-element/define'; +``` + +Package Entry Points: + +```json +"exports": { + ".": "./src/index.js", + "define": "./src/my-element.js", +} +``` + +## Exports for multiple web components + +Usage: + +```js +// only the classes +import { MyElement, SubElement } from 'my-element'; + +// OR + +// execute customElements.define for all elements +import 'my-element/define'; + +// execute customElements.define for a single element +import 'my-element/define-my-element'; +import 'my-element/define-sub-element'; +``` + +Package Entry Points: + +```json +"exports": { + ".": "./src/index.js", + "define": "./src/define.js", + "define-my-element": "./src/my-element.js", + "define-sub-element": "./src/sub-element.js", +} +``` + +in this case, the `src/define.js` should not contain any `customElements.define`, but instead it just imports the other define files + +```js +import 'my-element/define-my-element'; +import 'my-element/define-sub-element'; +``` + +## What does it mean for Lion? + +Imports that worked before will need be be adjusted as they will no longer work. +This is a breaking change. + +```js +// no longer works +import '@lion/input/lion-input'; +import '@lion/input/lion-input.js'; +import { LionInput } from '@lion/input/src/LionInput.js'; + +// works +import '@lion/input/define'; +import { LionInput } from '@lion/input'; +``` + +--- + +Photo by Curology on Unsplash diff --git a/docs/extending-documentation.md b/docs/blog/extending-documentation.md similarity index 88% rename from docs/extending-documentation.md rename to docs/blog/extending-documentation.md index 29f4b4e00..e8caad715 100644 --- a/docs/extending-documentation.md +++ b/docs/blog/extending-documentation.md @@ -1,12 +1,13 @@ -# Extending Lion Documentation +--- +title: Extending Lion Documentation +published: true +description: Extending a component library and its documentation for a speedy design system +date: 2020-12-01 +tags: javascript, documentation, demos +cover_image: https://miro.medium.com/max/2000/1*NZ6tdtJHHJjxtPmIFxWpGw.jpeg +--- -```js script -export default { - title: 'Guidelines/Extending documentation', -}; -``` - -If you extend [Lion](https://lion-web-components.netlify.app/) components, you don't only want to reuse the components, but you probably want to reuse the documentation (Storybook demos) as well. Wouldn't it be nice to just take it all from lion, but replace it with your own design system extension, so you don't have the extra maintenance of essentially copying the docs from `Lion` for your own design system implementation? +If you extend [Lion](https://lion-web.netlify.app/) components, you don't only want to reuse the components, but you probably want to reuse the documentation (Storybook demos) as well. Wouldn't it be nice to just take it all from lion, but replace it with your own design system extension, so you don't have the extra maintenance of essentially copying the docs from `Lion` for your own design system implementation? In this blog we will explain how `Lion` supports this use case, and allows you to extend not just the components, but also the documentation. @@ -29,13 +30,13 @@ For step 1, we can use `@open-wc/demoing-storybook` version 2 or higher, which u Install it manually (and see the docs for configuring): -```sh +```bash npm i @open-wc/demoing-storybook --save-dev ``` Or scaffold it with basic configuration by doing -```sh +```bash npm init @open-wc ``` @@ -66,11 +67,11 @@ This step alone should already give you the `LionTabs` docs inside your own Stor Potentially the hardest part is to analyse your extension `LeaTabs`, and to figure out how we should transform the import paths for `LionTabs` to new paths to your `LeaTabs`. -To do this we make use of [Providence](https://lion-web-components.netlify.app/?path=/docs/tools-providence-main--run-providence). This tool has a command that creates a full map of all the import paths of a reference project (`Lion`) and can replace them with the correct paths of a target project (`Lea`). +To do this we make use of [Providence](https://lion-web.netlify.app/?path=/docs/tools-providence-main--run-providence). This tool has a command that creates a full map of all the import paths of a reference project (`Lion`) and can replace them with the correct paths of a target project (`Lea`). So lets install it: -```sh +```bash npm i providence-analytics --save-dev ``` @@ -86,7 +87,7 @@ The `--prefix-from` and `--prefix-to` are the prefixes of the project you extend If you know you only use a single component from lion, you can reduce the time the tool needs for analysis, by specifying this package `-r 'node_modules/@lion/tabs'`. -Running the script will create a `providence-extend-docs-data.json` file, with all from/to information. You can change the name / location of the output file, refer to [Providence Documentation](https://lion-web-components.netlify.app/?path=/docs/tools-providence-main--run-providence) for this. +Running the script will create a `providence-extend-docs-data.json` file, with all from/to information. You can change the name / location of the output file, refer to [Providence Documentation](https://lion-web.netlify.app/?path=/docs/tools-providence-main--run-providence) for this. #### Running it automatically when upgrading lion dependency @@ -103,13 +104,13 @@ Inside ING, our design system also makes use of this providence tool to create t Now that we have a JSON file with all the information we need to know about to replace import paths and tagnames inside templates, we can start transforming the `LionTabs` documentation to `LeaTabs` documentation. -For this, we created a `babel-plugin` called [babel-plugin-extend-docs](https://lion-web-components.netlify.app/?path=/docs/tools-babelpluginextenddocs--page). +For this, we created a `babel-plugin` called [babel-plugin-extend-docs](https://lion-web.netlify.app/?path=/docs/tools-babelpluginextenddocs--page). This will analyse the JavaScript script and story content inside the markdown files, which uses [MDJS](https://open-wc.org/mdjs/) syntax, and transform it on the fly in `es-dev-server`, as well as on rollup build for production. So all you need to do is to install this plugin: -```sh +```bash npm i babel-plugin-extend-docs --save-dev ``` @@ -164,7 +165,7 @@ In some cases you don't want to show all examples of how to use a component. Som In our example, we will show you have to remove the `Rationale` section that you would normally inherit from the `Lion` documentation. -For this step we make use of a remark plugin for the MD content, similar to how you would use a babel plugin for JS content. It is called [Remark extend](https://lion-web-components.netlify.app/?path=/docs/tools-remark-extend--page). +For this step we make use of a remark plugin for the MD content, similar to how you would use a babel plugin for JS content. It is called [Remark extend](https://lion-web.netlify.app/?path=/docs/tools-remark-extend--page). It will let you add, remove or replace sections or specific words. First of all we need to add the plugin to the `.storybook/main.js`: @@ -256,7 +257,7 @@ Or you can add an extra paragraph below the content. Create a fenced codeblock: ::removeMdAfter(':scope:last-child') ``` -> See [Remark extend](https://lion-web-components.netlify.app/?path=/docs/tools-remark-extend--page) for more information +> See [Remark extend](https://lion-web.netlify.app/?path=/docs/tools-remark-extend--page) for more information ### Lea Tabs Special Feature diff --git a/docs/blog/extending-lions-website.md b/docs/blog/extending-lions-website.md new file mode 100644 index 000000000..00a770745 --- /dev/null +++ b/docs/blog/extending-lions-website.md @@ -0,0 +1,124 @@ +--- +title: Extending lions website +published: false +description: A static website with docs and demos for lion +date: 2021-03-10 +tags: [javascript, rocket, documentation] +cover_image: /blog/images/introducing-lions-website-cover-image.jpg +--- + +After a month of preparations we can finally present to you our new website. With it we are enabled to give more context to each of our components. +Right now it's more or less a port for our existing demos from storybook. But we can organize it in a nicer way by splitting it into components, docs, guides and blog sections. + +## Importing Content into Markdown files + +We now use a system to import content from one markdown file into another. +So let's say you find the documentation of `input-amount` useful and you want to present it on your page as well. +Anywhere in your documentation you can now write. + +👉 `docs/input-amount.md` + +````md +# Input Amount + +```js ::import('@lion/input-amount/docs/overview.md', 'heading[depth=1] ~ *') + +``` +```` + +So when you now go to `https://domain.com/input-amount/` you will actually see the content from `@lion/input-amount/docs/overview.md`. + +Why is it a `js code block`? + +- You can define a start and end for what should be imported (using [unist-util-select](https://github.com/syntax-tree/unist-util-select#support)) +- You can add adjustments to the content as a function +- As links like `[go there](@lion/input-amount/docs/overview.md)` would not work anyways, having one syntax that allows for additional features is enough + +### Importing Partial Content + +Quite often you probably don't want to get the full file, so there is a special helper for that. + +Let's assume you have the following source file + +```md +# First Headline + +Content of first headline + +## Second Headline + +Content of second headline + +## Third Headline + +Content of Third headline +``` + +and you only want to get the 2nd "block" e.g. 2nd headline + text. + +With `importBlock` you can get exactly that. + +A block starts with a headline and ends when the next headline of an equal level starts. + +Note: importBlock is a faster way of writing imports for headlines + +```md +::importBlock('./path/to/file.md', '## red') +// is the same as +::import('./path/to/file.md', 'heading[depth=2]:has([value=red])', 'heading[depth=2]:has([value=red]) ~heading[depth=2]') +``` + +If you want to know more please look at the documentation for [remark-extend](../docs/node-tools/remark-extend/overview.md). + +## Upgrading Documentation + +Unfortunately this is quite a different concept then what we used with storybook before. Luckily the content is very much the same. + +Let's convert an example page + +### FROM + +````md +# Calendar + +`lion-calendar` is a reusable and accessible calendar view. It depends on [calendar](?path=/docs/calendar--default-story). + +```js script +import { html, css } from '@lion/core'; +import './lion-calendar.js'; + +export default { + title: 'Others/Calendar', +}; +``` + +... +```` + +### TO + +````md +# Component >> Calendar ||20 + +`lion-calendar` is a reusable and accessible calendar view. It depends on [calendar](../../path/to/calendar.md). + +```js script +import { html, css } from '@lion/core'; +import '@lion/calendar/define'; +``` + +... +```` + +So what you need to do: + +1. Remove the js default export and put the title / navigation into the headline +2. Replace all imports with the Package Entry Points +3. Adjust all link to be relative links to actual files (instead of storybook specific urls) + +## Upgrading Extending Documentation + +If you are currently extending our storybook documentation then this will no longer work. +For now we will need to ask you to stay on the current version and NOT upgrade. + +We will release the necessary tools to extend our Lion Website in the upcoming weeks 🤗 diff --git a/docs/blog/images/controlling-exports-cover-image.jpg b/docs/blog/images/controlling-exports-cover-image.jpg new file mode 100644 index 000000000..ed90497ba Binary files /dev/null and b/docs/blog/images/controlling-exports-cover-image.jpg differ diff --git a/demo/docs/assets/side-by-side.png b/docs/blog/images/ing-open-sources-lion-side-by-side.png similarity index 100% rename from demo/docs/assets/side-by-side.png rename to docs/blog/images/ing-open-sources-lion-side-by-side.png diff --git a/docs/blog/images/introducing-lions-website-cover-image.jpg b/docs/blog/images/introducing-lions-website-cover-image.jpg new file mode 100644 index 000000000..82cadc685 Binary files /dev/null and b/docs/blog/images/introducing-lions-website-cover-image.jpg differ diff --git a/docs/blog/index.md b/docs/blog/index.md new file mode 100644 index 000000000..48d22b3e7 --- /dev/null +++ b/docs/blog/index.md @@ -0,0 +1,14 @@ +--- +title: Lion Blog +layout: layout-blog-overview +eleventyNavigation: + key: Blog + order: 40 +pagination: + data: collections.blog + size: 10 + reverse: true + alias: posts +--- + +Discover articles from the core team and contributors about Lion, tips and tricks included! diff --git a/demo/README.md b/docs/blog/ing-open-sources-lion.md similarity index 92% rename from demo/README.md rename to docs/blog/ing-open-sources-lion.md index fb6f52b71..fe9197ee0 100644 --- a/demo/README.md +++ b/docs/blog/ing-open-sources-lion.md @@ -1,10 +1,11 @@ -# ING open sources Lion: A library of performant, accessible & flexible Web Components - -```js script -export default { - title: 'Intro/Announcement', -}; -``` +--- +title: ING open sources Lion +published: true +description: A library of performant, accessible & flexible Web Components +date: 2020-08-10 +tags: [javascript, tests, demos] +cover_image: https://miro.medium.com/max/1400/1*FT8CF6G-1IVa88G04nkf5w.png +--- **TL;DR:** Web development can be hard, whether you're making your own components, implementing Design Systems, support many different browsers, incorporating accessibility, or adding third party dependencies. Lion aims to make your life a little bit easier, by taking the groundwork of feature complete, accessible, performant, and framework agnostic components out of your hands! Check out the repo at [ing-bank/lion](https://github.com/ing-bank/lion). @@ -79,9 +80,9 @@ Lion is a core package of white label Web Components. What this means is that th And this is exactly what we at ING do as well. Our very own ing-web components extend the Lion components and apply our own ING visual identity which mostly is a thin layer on top of Lion. -Check out the [Lion demo's](http://lion-web-components.netlify.com/). Looks plain, doesn't it? And now compare Lion to ing-web: +Check out the [Lion demo's](http://lion-web.netlify.app/). Looks plain, doesn't it? And now compare Lion to ing-web: -![Lion - ing-web side by side](https://raw.githubusercontent.com/ing-bank/lion/master/demo/docs/assets/side-by-side.png) +![Lion - ing-web side by side](./images/ing-open-sources-lion-side-by-side.png) ### 🎯 Focus @@ -149,7 +150,7 @@ npm i lit-element @lion/tabs Create a `lea-tabs` component by reusing the functionality of Lion. This gives Lea all the functionality and accessible core that she needs for his custom tabs component. ```js -import { css } from '@lion/core'; +import { css } from 'lit-element'; import { LionTabs } from '@lion/tabs'; export class LeaTabs extends LionTabs { @@ -178,7 +179,7 @@ customElements.define('lea-tabs', LeaTabs); Lea also wants to be able to style the tab and tab-panel according to Betatechs visual identity. In order to do so, she creates a `lea-tab-panel` component and a `lea-tab` component, which she can then fully style however she desires, and eventually place inside the `lea-tabs` component. You can see how Lea achieved this in the example down below. ```js -import { LitElement, html, css } from '@lion/core'; +import { LitElement, html, css } from 'lit-element'; export class LeaTab extends LitElement { static get styles() { @@ -209,7 +210,7 @@ Excellent! Lea can now use the tabs component like so: ``` -There we go, Lea's component is already done, so let's write some documentation. See the [live Lea tabs documentation page](?path=/docs/intro-tabs-example--default-story). You can see the full code of `lea-tabs` [on github](https://github.com/ing-bank/lion/tree/master/demo/). +There we go, Lea's component is already done, so let's write some documentation. See the [live Lea tabs documentation page](../components/content/tabs/examples.md). You can see the full code of `lea-tabs` [on github](https://github.com/ing-bank/lion/tree/master/demo/). P.S.: Do note that Lea is now responsible for keeping the documentation of `lea-tabs` up to date herself, and improvements on Lion's documentation will not automatically be reflected on Lea's documentation. @@ -223,7 +224,7 @@ Component libraries are in huge demand. By open-sourcing our extendable componen Building applications is hard, and sometimes, you need a little bit more than just the right component alone, but also things like: Validation, Forms, Overlays, Localization, etc. But fear not; Lion has got you covered! -You can check them out in our [documentation](http://lion-web-components.netlify.com/), and we'll go into more depth about Lion's additional systems in future blog posts. +You can check them out in our [documentation](http://lion-web.netlify.app/), and we'll go into more depth about Lion's additional systems in future blog posts. ## Thanks diff --git a/docs/blog/introducing-lions-website.md b/docs/blog/introducing-lions-website.md new file mode 100644 index 000000000..b78bd3865 --- /dev/null +++ b/docs/blog/introducing-lions-website.md @@ -0,0 +1,110 @@ +--- +title: Introducing lions website +published: true +description: A static website with docs and demos for lion +date: 2021-03-10 +tags: [javascript, rocket, documentation] +cover_image: /blog/images/introducing-lions-website-cover-image.jpg +--- + +After a month of preparations, we can finally present to you our new website. With it, we are enabled to give more context to each of our components. +Right now it's more or less a port for our existing demos from storybook. But we can organize it in a nicer way by splitting it into components, docs, guides and blog sections. + +## Meet the new sections + +1. [Guides](../guides/index.md)
+ A dedicated section where we will teach you about how to get started with lion. This section is completely new and will grow over time. +2. [Components](../components/index.md)
+ Here you will find our documentation for each of our components. Each is split into two pages namely Overview and Features. We also plan to add an API page soonish. +3. [Docs](../docs/index.md)
+ This is the home for general documentation which includes the fundamental systems all our components are build upon and various tools we use in the frontend or backend. +4. [Blog](./index.md)
+ We now have a dedicated section about all our blog posts. Take a peek and follow our story. + +## Upgrading Our Documentation + +As this page is now a static website and no longer storybook we needed to convert all our documentation. +Luckily the format is very similar. + +Let's convert an example page + +### FROM + +````md +# Calendar + +`lion-calendar` is a reusable and accessible calendar view. It depends on [calendar](?path=/docs/calendar--default-story). + +```js script +import { html, css } from '@lion/core'; +import './lion-calendar.js'; + +export default { + title: 'Others/Calendar', +}; +``` + +... +```` + +### TO + +````md +# Component >> Calendar ||20 + +`lion-calendar` is a reusable and accessible calendar view. It depends on [calendar](../../path/to/calendar.md). + +```js script +import { html, css } from '@lion/core'; +import '@lion/calendar/define'; +``` + +... +```` + +So what we needed to do: + +1. Remove the js default export and put the title / navigation into the headline +2. Replace all imports with the Package Entry Points +3. Adjust all link to be relative links to actual files (instead of storybook specific urls) + +## Handling Mono Repo Documentation + +All of our documentation now resides in the root `docs` folder. To publish them with our components we added a tool that can copy content and files into our published package. + +Let's say you have this file in your documentation. + +👉 `docs/components/accordion/overview.md` + +``` +# Accordion + +Is a very useful... +``` + +Now you want to make sure it gets published with your accordion package so you replace you Readme file with this + +👉 `packages/accordion/README.md` + +``` +# Accordion + +[=> See Source <=](../../docs/components/content/accordion/overview.md) +``` + +Now by calling `publish-docs` within the `prepublishOnly` step your Readme file will contain the content of its source. + +The benefits of this approach are + +- Readme still makes sense on GitHub (e.g. you can navigate to the package and with one click you are at the docs) +- The published readme contains all the documentation you need +- All links are version safe (e.g. if you look at v0.5 it will link to the GitHub Pages at this time) + +If you want to know more please look at the documentation for [publish-docs](../docs/node-tools/publish-docs/overview.md). + +## Upgrading Extending Documentation + +If you are currently extending our storybook documentation then this will no longer work. +For now, we will need to ask you to stay on the current version and NOT upgrade. + +We will release the necessary tools to extend our Lion Website in the upcoming weeks 🤗 diff --git a/docs/browserconfig.xml b/docs/browserconfig.xml new file mode 100644 index 000000000..549601dcd --- /dev/null +++ b/docs/browserconfig.xml @@ -0,0 +1,9 @@ + + + + + + #1d3557 + + + diff --git a/docs/components/content/accordion/features.md b/docs/components/content/accordion/features.md new file mode 100644 index 000000000..a4d7535e2 --- /dev/null +++ b/docs/components/content/accordion/features.md @@ -0,0 +1,214 @@ +# Content >> Accordion >> Features ||20 + +```js script +import { LitElement, html } from '@lion/core'; + +import '@lion/accordion/define'; +``` + +## Expanded + +You can set `expanded` to pre-expand a certain invoker. + +```js preview-story +export const expanded = () => html` + +

+ +

+

Lorem ipsum dolor sit, amet consectetur adipisicing elit.

+

+ +

+

+ Laboriosam sequi odit cumque, enim aut assumenda itaque quis voluptas est quos fugiat unde + labore reiciendis saepe, iure, optio officiis obcaecati quibusdam. +

+
+`; +``` + +## Slots Order + +The invoker and content slots are ordered by DOM order. + +```js preview-story +export const slotsOrder = () => html` + +

+ +

+

Lorem ipsum dolor sit, amet consectetur adipisicing elit.

+

+ +

+

+ Laboriosam sequi odit cumque, enim aut assumenda itaque quis voluptas est quos fugiat unde + labore reiciendis saepe, iure, optio officiis obcaecati quibusdam. +

+
+`; +``` + +## Multiline header + +A header can be multiline. + +```js preview-story +export const multilineHeader = () => html` + +

+ +

+

content 1

+

+ +

+

content 2

+
+`; +``` + +## Distribute New Elements + +Below, we demonstrate how you could dynamically add a new invoker + content. + +```js preview-story +export const distributeNewElement = () => { + const tagName = 'demo-accordion-add-dynamically'; + if (!customElements.get(tagName)) { + customElements.define( + tagName, + class extends LitElement { + static get properties() { + return { + __collection: { type: Array }, + }; + } + render() { + return html` +

Append

+ +

+ +

+

content 1

+

+ +

+

content 2

+
+ +
+

Push

+ +

+ +

+

content 1

+

+ +

+

content 2

+ ${this.__collection.map( + item => html` +

+

${item.content}

+ `, + )} +
+ + `; + } + constructor() { + super(); + this.__collection = []; + } + __handleAppendClick() { + const accordionElement = this.shadowRoot.querySelector('#appendAccordion'); + const c = 2; + const n = Math.floor(accordionElement.children.length / 2); + for (let i = n + 1; i < n + c; i += 1) { + const invoker = document.createElement('h4'); + const button = document.createElement('button'); + button.innerText = `header ${i}`; + invoker.setAttribute('slot', 'invoker'); + invoker.appendChild(button); + const content = document.createElement('p'); + content.setAttribute('slot', 'content'); + content.innerText = `content ${i}`; + accordionElement.append(invoker); + accordionElement.append(content); + } + } + __handlePushClick() { + const accordionElement = this.shadowRoot.querySelector('#pushTabs'); + const i = Math.floor(accordionElement.children.length / 2) + 1; + this.__collection = [ + ...this.__collection, + { + invoker: `header ${i}`, + content: `content ${i}`, + }, + ]; + } + }, + ); + } + return html` `; +}; +``` + +One way is by creating the DOM elements and appending them as needed. + +Inside your `lion-accordion` extension, an example for appending nodes on a certain button click: + +```js +__handleAppendClick() { + const accordionAmount = this.children.length / 2; + const invoker = document.createElement('h4'); + const button = document.createElement('button'); + button.innerText = `header ${accordionAmount + 1}`; + invoker.setAttribute('slot', 'invoker'); + invoker.appendChild(button); + const content = document.createElement('p'); + content.setAttribute('slot', 'content'); + content.innerText = `content ${accordionAmount + 1}`; + this.append(invoker); + this.append(content); +} +``` + +The other way is by adding data to a Lit property where you loop over this property in your template. +You then need to ensure this causes a re-render. + +```js +__handlePushClick() { + const accordionAmount = this.children.length; + myCollection = [ + ...myCollection, + { + invoker: `header ${accordionAmount + 1}`, + content: `content ${accordionAmount + 1}`, + }, + ]; + renderMyCollection(); +} +``` + +Make sure your template re-renders when myCollection is updated. + +```html + + ${myCollection.map(item => html` +

+ +

+

${item.content}

+ `)} +
+``` diff --git a/docs/components/content/accordion/index.md b/docs/components/content/accordion/index.md new file mode 100644 index 000000000..cf0241b22 --- /dev/null +++ b/docs/components/content/accordion/index.md @@ -0,0 +1,3 @@ +# Content >> Accordion + +-> go to Overview diff --git a/docs/components/content/accordion/overview.md b/docs/components/content/accordion/overview.md new file mode 100644 index 000000000..e280f4a99 --- /dev/null +++ b/docs/components/content/accordion/overview.md @@ -0,0 +1,52 @@ +# Content >> Accordion >> Overview ||10 + +`lion-accordion` is a component used to toggle the display of sections of content. +Its purpose is to reduce the need to scroll when presenting multiple sections of content on a single page. Accordions often allow users to get the big picture before focusing on details. + +```js script +import { html } from '@lion/core'; +import '@lion/accordion/define'; +``` + +```js preview-story +export const main = () => html` + +

+ +

+

Lorem ipsum dolor sit, amet consectetur adipisicing elit.

+

+ +

+

+ Laboriosam sequi odit cumque, enim aut assumenda itaque quis voluptas est quos fugiat unde + labore reiciendis saepe, iure, optio officiis obcaecati quibusdam. +

+
+`; +``` + +## Features + +- content gets provided by users (slotted in) +- handles accessibility +- support navigation via keyboard + +## Installation + +```bash +npm i --save @lion/accordion +``` + +```js +import { LionAccordion } from '@lion/accordion'; +// or +import '@lion/accordion/define'; +``` + +## Rationale + +### Contents are not focusable + +Focusable elements should be interactive. Contents themselves do not offer any interactivity. +If there is a button or a form inside the tab panel then these elements get focused directly. diff --git a/packages/collapsible/demo/CustomCollapsible.js b/docs/components/content/collapsible/assets/CustomCollapsible.js similarity index 88% rename from packages/collapsible/demo/CustomCollapsible.js rename to docs/components/content/collapsible/assets/CustomCollapsible.js index dbfcde7d7..09df418b6 100644 --- a/packages/collapsible/demo/CustomCollapsible.js +++ b/docs/components/content/collapsible/assets/CustomCollapsible.js @@ -1,4 +1,4 @@ -import { LionCollapsible } from '../index.js'; +import { LionCollapsible } from '@lion/collapsible'; const EVENT = { TRANSITION_END: 'transitionend', @@ -8,8 +8,8 @@ const EVENT = { * `CustomCollapsible` is a class for custom collapsible element (`` web component). * @customElement custom-collapsible */ +// @ts-expect-error false positive for incompatible static get properties. Lit-element merges super properties already for you. export class CustomCollapsible extends LionCollapsible { - /** @type {any} */ static get properties() { return { transitioning: { @@ -56,9 +56,7 @@ export class CustomCollapsible extends LionCollapsible { contentNode.style.setProperty('opacity', '1'); contentNode.style.setProperty('padding', '12px 0'); contentNode.style.setProperty('max-height', '0px'); - await /** @type {Promise} */ (new Promise(resolve => - requestAnimationFrame(() => resolve()), - )); + await new Promise(resolve => requestAnimationFrame(() => resolve())); contentNode.style.setProperty('max-height', expectedHeight); await this._waitForTransition({ contentNode }); } @@ -107,9 +105,9 @@ export class CustomCollapsible extends LionCollapsible { */ async __calculateHeight(contentNode) { contentNode.style.setProperty('max-height', ''); - await /** @type {Promise} */ (new Promise(resolve => - requestAnimationFrame(() => resolve()), - )); + await new Promise(resolve => requestAnimationFrame(() => resolve())); return this._contentHeight; // Expected height i.e. actual size once collapsed after animation } } + +customElements.define('custom-collapsible', CustomCollapsible); diff --git a/packages/collapsible/demo/applyDemoCollapsibleStyles.js b/docs/components/content/collapsible/assets/applyDemoCollapsibleStyles.js similarity index 100% rename from packages/collapsible/demo/applyDemoCollapsibleStyles.js rename to docs/components/content/collapsible/assets/applyDemoCollapsibleStyles.js diff --git a/docs/components/content/collapsible/examples.md b/docs/components/content/collapsible/examples.md new file mode 100644 index 000000000..ef5708c9d --- /dev/null +++ b/docs/components/content/collapsible/examples.md @@ -0,0 +1,83 @@ +# Content >> Collapsible >> Examples ||30 + +```js script +import { html } from '@lion/core'; +import '@lion/collapsible/define'; +import '@lion/button/define'; +import './assets/CustomCollapsible.js'; +import './assets/applyDemoCollapsibleStyles.js'; +``` + +## Custom Invoker Template + +A custom template can be specified to the `invoker` slot. It can be any button or custom component which mimics the button behavior for better accessibility support. In the below example, `lion-button` and native `button` with styling is used as a collapsible invoker. + +```js preview-story +export const customInvokerTemplate = () => html` + + More about cars +
+ Most definitions of cars say that they run primarily on roads, seat one to eight people, have + four tires, and mainly transport people rather than goods. +
+
+`; +``` + +## Extended collapsible with animation + +`LionCollapsible` can easily be extended to add more features in the component, like animation for example. + +```js preview-story +export const customAnimation = () => html` +
+
+ A motorcycle, often called a motorbike, bike, or cycle, is a two- or three-wheeled motor + vehicle. +
+ + +
+ Motorcycle design varies greatly to suit a range of different purposes: long distance + travel, commuting, cruising, sport including racing, and off-road riding. Motorcycling is + riding a motorcycle and related social activity such as joining a motorcycle club and + attending motorcycle rallies. +
+
+
+
+
+ A car (or automobile) is a wheeled motor vehicle used for transportation. +
+ + +
+ Most definitions of cars say that they run primarily on roads, seat one to eight people, + have four tires, and mainly transport people rather than goods. +
+
+
+`; +``` + +Use `_showAnimation()` and `_hideAnimation()` methods to customize open and close behavior. Check the code for a full example of a `custom-collapsible`. + +```js +_showAnimation({ contentNode }) { + const expectedHeight = await this.__calculateHeight(contentNode); + contentNode.style.setProperty('opacity', '1'); + contentNode.style.setProperty('padding', '12px 0'); + contentNode.style.setProperty('max-height', '0px'); + await new Promise(resolve => requestAnimationFrame(() => resolve())); + contentNode.style.setProperty('max-height', expectedHeight); + await this._waitForTransition({ contentNode }); +} + +_hideAnimation({ contentNode }) { + if (this._contentHeight === '0px') { + return; + } + ['opacity', 'padding', 'max-height'].map(prop => contentNode.style.setProperty(prop, 0)); + await this._waitForTransition({ contentNode }); +} +``` diff --git a/docs/components/content/collapsible/features.md b/docs/components/content/collapsible/features.md new file mode 100644 index 000000000..b8f569000 --- /dev/null +++ b/docs/components/content/collapsible/features.md @@ -0,0 +1,79 @@ +# Content >> Collapsible >> Features ||20 + +```js script +import { html } from '@lion/core'; +import '@lion/collapsible/define'; +``` + +## Default open + +Add the `opened` attribute to keep the component default open. + +```js preview-story +export const defaultOpen = () => html` + + +
+ Most definitions of cars say that they run primarily on roads, seat one to eight people, have + four tires, and mainly transport people rather than goods. +
+
+`; +``` + +## Methods + +There are the following methods available to control the extra content for the collapsible. + +- `toggle()`: toggle the extra content +- `show()`: show the extra content +- `hide()`: hide the extra content + +```js preview-story +export const methods = ({ shadowRoot }) => html` + + +
+ Most definitions of cars say that they run primarily on roads, seat one to eight people, have + four tires, and mainly transport people rather than goods. +
+
+
+ + + +
+`; +``` + +## Events + +`lion-collapsible` fires an event on `invoker` click to notify the component's current state. It is useful for analytics purposes or to perform some actions while expanding and collapsing the component. + +- `@opened-changed`: triggers when collapsible either gets opened or closed + +```js preview-story +export const events = ({ shadowRoot }) => html` +
+ +
+ { + const collapsibleState = shadowRoot.getElementById('collapsible-state'); + collapsibleState.innerText = `Opened: ${ev.target.opened}`; + }} + > + +
+ Most definitions of cars say that they run primarily on roads, seat one to eight people, have + four tires, and mainly transport people rather than goods. +
+
+`; +``` diff --git a/docs/components/content/collapsible/index.md b/docs/components/content/collapsible/index.md new file mode 100644 index 000000000..f15486238 --- /dev/null +++ b/docs/components/content/collapsible/index.md @@ -0,0 +1,3 @@ +# Content >> Collapsible + +-> go to Overview diff --git a/docs/components/content/collapsible/overview.md b/docs/components/content/collapsible/overview.md new file mode 100644 index 000000000..802359176 --- /dev/null +++ b/docs/components/content/collapsible/overview.md @@ -0,0 +1,39 @@ +# Content >> Collapsible >> Overview ||10 + +`lion-collapsible` is a combination of a button (the invoker), a chunk of 'extra content', and can be extended with an animation to disclose the extra content. There are two slots available respectively; `invoker` to specify the collapsible's invoker and `content` for the extra content of the collapsible. + +```js script +import { html } from '@lion/core'; +import '@lion/collapsible/define'; +``` + +```js preview-story +export const main = () => html` + + +
+ Most definitions of cars say that they run primarily on roads, seat one to eight people, have + four tires, and mainly transport people rather than goods. +
+
+`; +``` + +## Features + +- Use `opened` attribute or `toggle()` method to render default open +- `invoker` slot can be custom template e.g. `lion-button` or native `button` with custom styling +- Observe the state with the help of `@opened-changed` event +- `show()` and `hide()` are helper methods to hide or show the content from outside + +## Installation + +```bash +npm i --save @lion/collapsible +``` + +```js +import { LionCollapsible } from '@lion/collapsible'; +// or +import '@lion/collapsible/define'; +``` diff --git a/docs/components/content/index.md b/docs/components/content/index.md new file mode 100644 index 000000000..deae6f052 --- /dev/null +++ b/docs/components/content/index.md @@ -0,0 +1 @@ +# Content ||10 diff --git a/packages/progress-indicator/demo/CustomProgressIndicator.js b/docs/components/content/progress-indicator/assets/custom-progress-indicator.js similarity index 65% rename from packages/progress-indicator/demo/CustomProgressIndicator.js rename to docs/components/content/progress-indicator/assets/custom-progress-indicator.js index 85fc4d1c5..da27c83ec 100644 --- a/packages/progress-indicator/demo/CustomProgressIndicator.js +++ b/docs/components/content/progress-indicator/assets/custom-progress-indicator.js @@ -1,26 +1,26 @@ -import { svg, css } from '@lion/core'; -import { LionProgressIndicator } from '../index.js'; +import { html, css } from '@lion/core'; +import { LionProgressIndicator } from '@lion/progress-indicator'; export class CustomProgressIndicator extends LionProgressIndicator { static get styles() { return [ css` :host { - display: inline-block; + display: block; } - svg { - animation: spinner-rotate 2s linear infinite; + .progress--icon { display: inline-block; - height: 48px; width: 48px; + height: 48px; + animation: spinner-rotate 2s linear infinite; } - circle { + .progress--icon--circle { animation: spinner-dash 1.35s ease-in-out infinite; fill: none; - stroke-width: 3.6; - stroke: firebrick; + stroke-width: 6px; + stroke: var(--primary-color); stroke-dasharray: 100, 28; /* This is a fallback for IE11 */ } @@ -49,8 +49,12 @@ export class CustomProgressIndicator extends LionProgressIndicator { } _graphicTemplate() { - return svg` - - `; + return html` + + + + `; } } + +customElements.define('custom-progress-indicator', CustomProgressIndicator); diff --git a/docs/components/content/progress-indicator/examples.md b/docs/components/content/progress-indicator/examples.md new file mode 100644 index 000000000..3baa49193 --- /dev/null +++ b/docs/components/content/progress-indicator/examples.md @@ -0,0 +1,76 @@ +# Content >> Progress Indicator >> Examples ||30 + +```js script +import { html } from '@lion/core'; +import './assets/custom-progress-indicator.js'; +``` + +## Extended indicator with a custom visual + +`LionProgressIndicator` is designed to be extended to add visuals. Implement the `_graphicTemplate()` method to set the rendered content and apply styles normally. + +### Example extension + +```js +class CustomProgressIndicator extends LionProgressIndicator { + static get styles() { + return [ + css` + :host { + display: block; + } + + .progress--icon { + display: inline-block; + width: 48px; + height: 48px; + animation: spinner-rotate 2s linear infinite; + } + + .progress--icon--circle { + animation: spinner-dash 1.35s ease-in-out infinite; + fill: none; + stroke-width: 6px; + stroke: var(--primary-color); + stroke-dasharray: 100, 28; /* This is a fallback for IE11 */ + } + + @keyframes spinner-rotate { + to { + transform: rotate(360deg); + } + } + + @keyframes spinner-dash { + 0% { + stroke-dasharray: 6, 122; + stroke-dashoffset: 0; + } + 50% { + stroke-dasharray: 100, 28; + stroke-dashoffset: -16; + } + 100% { + stroke-dasharray: 6, 122; + stroke-dashoffset: -127; + } + } + `, + ]; + } + + _graphicTemplate() { + return html` + + + + `; + } +} +``` + +### Result + +```js preview-story +export const main = () => html` `; +``` diff --git a/docs/components/content/progress-indicator/index.md b/docs/components/content/progress-indicator/index.md new file mode 100644 index 000000000..3180953cb --- /dev/null +++ b/docs/components/content/progress-indicator/index.md @@ -0,0 +1,3 @@ +# Content >> Progress Indicator + +-> go to Overview diff --git a/docs/components/content/progress-indicator/overview.md b/docs/components/content/progress-indicator/overview.md new file mode 100644 index 000000000..7ad6689ad --- /dev/null +++ b/docs/components/content/progress-indicator/overview.md @@ -0,0 +1,29 @@ +# Content >> Progress Indicator >> Overview ||10 + +`lion-progress-indicator` implements accessibility requirements for progress indicators. + +```html + +``` + +Note: You don't see a live demo here as it would be empty, since there is no styling. Check out the [examples](./examples.md) if you want to see a possible implementation. + +## Features + +`LionProgressIndicator` is designed to be extended to add visuals. + +- Accessibility compliant +- Localized "Loading" label +- Implementation independent of visuals + +## Installation + +```bash +npm i --save @lion/progress-indicator +``` + +```js +import { LionProgressIndicator } from '@lion/progress-indicator'; +// or +import '@lion/progress-indicator/define'; +``` diff --git a/docs/components/content/tabs/examples.md b/docs/components/content/tabs/examples.md new file mode 100644 index 000000000..f9c46d068 --- /dev/null +++ b/docs/components/content/tabs/examples.md @@ -0,0 +1,23 @@ +# Content >> Tabs >> Examples ||30 + +```js script +import { LitElement, html } from '@lion/core'; +import './src/lea-tabs.js'; +import './src/lea-tab.js'; +import './src/lea-tab-panel.js'; +``` + +## Lea Tabs + +Learn how to create the `lea tabs` in our ["introducing lion" blogpost](../../../blog/ing-open-sources-lion.md). + +```js preview-story +export const main = () => html` + + Info + Info page with lots of information about us. + Work + Work page that showcases our work. + +`; +``` diff --git a/docs/components/content/tabs/features.md b/docs/components/content/tabs/features.md new file mode 100644 index 000000000..79b42ea5d --- /dev/null +++ b/docs/components/content/tabs/features.md @@ -0,0 +1,194 @@ +# Content >> Tabs >> Features ||20 + +```js script +import { LitElement, html } from '@lion/core'; + +import '@lion/tabs/define'; +``` + +## Selected Index + +You can set the `selectedIndex` to select a certain tab. + +```js preview-story +export const selectedIndex = () => html` + + +

Info page with lots of information about us.

+ +

Work page that showcases our work.

+
+`; +``` + +## Slots Order + +The tab and panel slots are ordered by DOM order. + +This means you can switch the grouping in your `lion-tabs` from tab + panel to all tabs first or all panels first. + +```js preview-story +export const slotsOrder = () => html` + + + +

Info page with lots of information about us.

+

Work page that showcases our work.

+
+`; +``` + +## Nesting tabs + +You can include tabs within tabs + +```js preview-story +export const nestedTabs = () => html` + + + +
+

Find some more info about our favorite movies:

+ + + +

+ Cars is a 2006 American computer-animated comedy film produced by Pixar Animation Studios + and released by Walt Disney Pictures. +

+

+ The feature film directorial debut of John Lasseter, it was the first entirely + computer-animated feature film, as well as the first feature film from Pixar. +

+
+
+

Work page that showcases our work.

+
+`; +``` + +## Distribute New Elements + +Below, we demonstrate on how you could dynamically add new tab + panels. + +```js preview-story +export const distributeNewElement = () => { + const tagName = 'demo-tabs-add-dynamically'; + if (!customElements.get(tagName)) { + customElements.define( + tagName, + class extends LitElement { + static get properties() { + return { + __collection: { type: Array }, + }; + } + render() { + return html` +

Append

+ + +

panel 1

+ +

panel 2

+
+ +
+

Push

+ + +

panel 1

+ +

panel 2

+ ${this.__collection.map( + item => html` + +

${item.panel}

+ `, + )} +
+ + `; + } + constructor() { + super(); + this.__collection = []; + } + __handleAppendClick() { + const tabsElement = this.shadowRoot.querySelector('#appendTabs'); + const c = 2; + const n = Math.floor(tabsElement.children.length / 2); + for (let i = n + 1; i < n + c; i += 1) { + const tab = document.createElement('button'); + tab.setAttribute('slot', 'tab'); + tab.innerText = `tab ${i}`; + const panel = document.createElement('p'); + panel.setAttribute('slot', 'panel'); + panel.innerText = `panel ${i}`; + tabsElement.append(tab); + tabsElement.append(panel); + } + } + __handlePushClick() { + const tabsElement = this.shadowRoot.querySelector('#pushTabs'); + const i = Math.floor(tabsElement.children.length / 2) + 1; + this.__collection = [ + ...this.__collection, + { + button: `tab ${i}`, + panel: `panel ${i}`, + }, + ]; + } + }, + ); + } + return html` `; +}; +``` + +One way is by creating the DOM elements and appending them as needed. + +Inside your `lion-tabs` extension, an example for appending nodes on a certain button click: + +```js +__handleAppendClick() { + const tabsAmount = this.children.length / 2; + const tab = document.createElement('button'); + tab.setAttribute('slot', 'tab'); + tab.innerText = `tab ${tabsAmount + 1}`; + const panel = document.createElement('p'); + panel.setAttribute('slot', 'panel'); + panel.innerText = `panel ${tabsAmount + 1}`; + this.append(tab); + this.append(panel); +} +``` + +The other way is by adding data to a Lit property where you loop over this property in your template. +You then need to ensure this causes a re-render. + +```js +__handlePushClick() { + const tabsAmount = this.children.length; + myCollection = [ + ...myCollection, + { + button: `tab ${tabsAmount + 1}`, + panel: `panel ${tabsAmount + 1}`, + }, + ]; + renderMyCollection(); +} +``` + +Make sure your template re-renders when myCollection is updated. + +```html + + ${myCollection.map(item => html` + +

${item.panel}

+ `)} +
+``` diff --git a/docs/components/content/tabs/index.md b/docs/components/content/tabs/index.md new file mode 100644 index 000000000..3018ec15e --- /dev/null +++ b/docs/components/content/tabs/index.md @@ -0,0 +1,3 @@ +# Content >> Tabs + +-> go to Overview diff --git a/docs/components/content/tabs/overview.md b/docs/components/content/tabs/overview.md new file mode 100644 index 000000000..747013b01 --- /dev/null +++ b/docs/components/content/tabs/overview.md @@ -0,0 +1,48 @@ +# Content >> Tabs >> Overview ||10 + +A component to allow users to quickly move between a small number of equally important views. + +```js script +import { LitElement, html } from '@lion/core'; +import '@lion/tabs/define'; +``` + +```js preview-story +export const main = () => html` + + +

Info page with lots of information about us.

+ +

Work page that showcases our work.

+
+`; +``` + +## Installation + +```bash +npm i --save @lion/tabs; +``` + +```js +import { LionTabs } from '@lion/tabs'; +// or +import '@lion/tabs/define'; +``` + +## Rationale + +### No separate active/focus state when using keyboard + +We will immediately switch content as all our content comes from light dom (e.g. no latency) + +See Note at + +> It is recommended that tabs activate automatically when they receive focus as long as their +> associated tab panels are displayed without noticeable latency. This typically requires tab +> panel content to be preloaded. + +### Panels are not focusable + +Focusable elements should have a means to interact with them. Tab panels themselves do not offer any interactivity. +If there is a button or a form inside the tab panel then these elements get focused directly. diff --git a/demo/src/LeaTabPanel.js b/docs/components/content/tabs/src/lea-tab-panel.js similarity index 89% rename from demo/src/LeaTabPanel.js rename to docs/components/content/tabs/src/lea-tab-panel.js index 993461a19..3584d24a5 100644 --- a/demo/src/LeaTabPanel.js +++ b/docs/components/content/tabs/src/lea-tab-panel.js @@ -20,3 +20,5 @@ export class LeaTabPanel extends LitElement { `; } } + +customElements.define('lea-tab-panel', LeaTabPanel); diff --git a/demo/src/LeaTab.js b/docs/components/content/tabs/src/lea-tab.js similarity index 96% rename from demo/src/LeaTab.js rename to docs/components/content/tabs/src/lea-tab.js index b43d5944f..64b6940c7 100644 --- a/demo/src/LeaTab.js +++ b/docs/components/content/tabs/src/lea-tab.js @@ -41,3 +41,5 @@ export class LeaTab extends LitElement { return html``; } } + +customElements.define('lea-tab', LeaTab); diff --git a/demo/src/LeaTabs.js b/docs/components/content/tabs/src/lea-tabs.js similarity index 90% rename from demo/src/LeaTabs.js rename to docs/components/content/tabs/src/lea-tabs.js index aa81386a7..3c3f385a0 100644 --- a/demo/src/LeaTabs.js +++ b/docs/components/content/tabs/src/lea-tabs.js @@ -24,3 +24,5 @@ export class LeaTabs extends LionTabs { // being awesome } } + +customElements.define('lea-tabs', LeaTabs); diff --git a/packages/icon/docs/icons/bugs/bug01.svg.js b/docs/components/icons/icon/assets/bugs/bug01.svg.js similarity index 100% rename from packages/icon/docs/icons/bugs/bug01.svg.js rename to docs/components/icons/icon/assets/bugs/bug01.svg.js diff --git a/packages/icon/docs/icons/bugs/bug02.svg.js b/docs/components/icons/icon/assets/bugs/bug02.svg.js similarity index 100% rename from packages/icon/docs/icons/bugs/bug02.svg.js rename to docs/components/icons/icon/assets/bugs/bug02.svg.js diff --git a/packages/icon/docs/icons/bugs/bug05.svg.js b/docs/components/icons/icon/assets/bugs/bug05.svg.js similarity index 100% rename from packages/icon/docs/icons/bugs/bug05.svg.js rename to docs/components/icons/icon/assets/bugs/bug05.svg.js diff --git a/packages/icon/docs/icons/bugs/bug06.svg.js b/docs/components/icons/icon/assets/bugs/bug06.svg.js similarity index 100% rename from packages/icon/docs/icons/bugs/bug06.svg.js rename to docs/components/icons/icon/assets/bugs/bug06.svg.js diff --git a/packages/icon/docs/icons/bugs/bug08.svg.js b/docs/components/icons/icon/assets/bugs/bug08.svg.js similarity index 100% rename from packages/icon/docs/icons/bugs/bug08.svg.js rename to docs/components/icons/icon/assets/bugs/bug08.svg.js diff --git a/packages/icon/docs/icons/bugs/bug12.svg.js b/docs/components/icons/icon/assets/bugs/bug12.svg.js similarity index 100% rename from packages/icon/docs/icons/bugs/bug12.svg.js rename to docs/components/icons/icon/assets/bugs/bug12.svg.js diff --git a/packages/icon/docs/icons/bugs/bug19.svg.js b/docs/components/icons/icon/assets/bugs/bug19.svg.js similarity index 100% rename from packages/icon/docs/icons/bugs/bug19.svg.js rename to docs/components/icons/icon/assets/bugs/bug19.svg.js diff --git a/packages/icon/docs/icons/bugs/bug23.svg.js b/docs/components/icons/icon/assets/bugs/bug23.svg.js similarity index 100% rename from packages/icon/docs/icons/bugs/bug23.svg.js rename to docs/components/icons/icon/assets/bugs/bug23.svg.js diff --git a/packages/icon/docs/icons/bugs/bug24.svg.js b/docs/components/icons/icon/assets/bugs/bug24.svg.js similarity index 100% rename from packages/icon/docs/icons/bugs/bug24.svg.js rename to docs/components/icons/icon/assets/bugs/bug24.svg.js diff --git a/packages/icon/docs/icons/iconset-bugs.js b/docs/components/icons/icon/assets/iconset-bugs.js similarity index 100% rename from packages/icon/docs/icons/iconset-bugs.js rename to docs/components/icons/icon/assets/iconset-bugs.js diff --git a/packages/icon/docs/icons/iconset-misc.js b/docs/components/icons/icon/assets/iconset-misc.js similarity index 100% rename from packages/icon/docs/icons/iconset-misc.js rename to docs/components/icons/icon/assets/iconset-misc.js diff --git a/packages/icon/docs/icons/iconset-space.js b/docs/components/icons/icon/assets/iconset-space.js similarity index 100% rename from packages/icon/docs/icons/iconset-space.js rename to docs/components/icons/icon/assets/iconset-space.js diff --git a/packages/icon/docs/icons/misc/arrowLeft.svg.js b/docs/components/icons/icon/assets/misc/arrowLeft.svg.js similarity index 100% rename from packages/icon/docs/icons/misc/arrowLeft.svg.js rename to docs/components/icons/icon/assets/misc/arrowLeft.svg.js diff --git a/packages/icon/docs/icons/space/aliens-spaceship.svg.js b/docs/components/icons/icon/assets/space/aliens-spaceship.svg.js similarity index 100% rename from packages/icon/docs/icons/space/aliens-spaceship.svg.js rename to docs/components/icons/icon/assets/space/aliens-spaceship.svg.js diff --git a/packages/icon/docs/icons/space/meteor.svg.js b/docs/components/icons/icon/assets/space/meteor.svg.js similarity index 100% rename from packages/icon/docs/icons/space/meteor.svg.js rename to docs/components/icons/icon/assets/space/meteor.svg.js diff --git a/packages/icon/docs/icons/space/moon-flag.svg.js b/docs/components/icons/icon/assets/space/moon-flag.svg.js similarity index 100% rename from packages/icon/docs/icons/space/moon-flag.svg.js rename to docs/components/icons/icon/assets/space/moon-flag.svg.js diff --git a/packages/icon/docs/icons/space/moon.svg.js b/docs/components/icons/icon/assets/space/moon.svg.js similarity index 100% rename from packages/icon/docs/icons/space/moon.svg.js rename to docs/components/icons/icon/assets/space/moon.svg.js diff --git a/packages/icon/docs/icons/space/night.svg.js b/docs/components/icons/icon/assets/space/night.svg.js similarity index 100% rename from packages/icon/docs/icons/space/night.svg.js rename to docs/components/icons/icon/assets/space/night.svg.js diff --git a/packages/icon/docs/icons/space/orbit.svg.js b/docs/components/icons/icon/assets/space/orbit.svg.js similarity index 100% rename from packages/icon/docs/icons/space/orbit.svg.js rename to docs/components/icons/icon/assets/space/orbit.svg.js diff --git a/packages/icon/docs/icons/space/planet.svg.js b/docs/components/icons/icon/assets/space/planet.svg.js similarity index 100% rename from packages/icon/docs/icons/space/planet.svg.js rename to docs/components/icons/icon/assets/space/planet.svg.js diff --git a/packages/icon/docs/icons/space/robot.svg.js b/docs/components/icons/icon/assets/space/robot.svg.js similarity index 100% rename from packages/icon/docs/icons/space/robot.svg.js rename to docs/components/icons/icon/assets/space/robot.svg.js diff --git a/packages/icon/docs/icons/space/rocket.svg.js b/docs/components/icons/icon/assets/space/rocket.svg.js similarity index 100% rename from packages/icon/docs/icons/space/rocket.svg.js rename to docs/components/icons/icon/assets/space/rocket.svg.js diff --git a/packages/icon/docs/icons/space/satellite.svg.js b/docs/components/icons/icon/assets/space/satellite.svg.js similarity index 100% rename from packages/icon/docs/icons/space/satellite.svg.js rename to docs/components/icons/icon/assets/space/satellite.svg.js diff --git a/packages/icon/docs/icons/space/signal.svg.js b/docs/components/icons/icon/assets/space/signal.svg.js similarity index 100% rename from packages/icon/docs/icons/space/signal.svg.js rename to docs/components/icons/icon/assets/space/signal.svg.js diff --git a/packages/icon/docs/icons/space/space-helmet.svg.js b/docs/components/icons/icon/assets/space/space-helmet.svg.js similarity index 100% rename from packages/icon/docs/icons/space/space-helmet.svg.js rename to docs/components/icons/icon/assets/space/space-helmet.svg.js diff --git a/packages/icon/docs/icons/space/sun.svg.js b/docs/components/icons/icon/assets/space/sun.svg.js similarity index 100% rename from packages/icon/docs/icons/space/sun.svg.js rename to docs/components/icons/icon/assets/space/sun.svg.js diff --git a/packages/icon/docs/icons/space/telescope.svg.js b/docs/components/icons/icon/assets/space/telescope.svg.js similarity index 100% rename from packages/icon/docs/icons/space/telescope.svg.js rename to docs/components/icons/icon/assets/space/telescope.svg.js diff --git a/docs/components/icons/icon/features.md b/docs/components/icons/icon/features.md new file mode 100644 index 000000000..1bf59957d --- /dev/null +++ b/docs/components/icons/icon/features.md @@ -0,0 +1,93 @@ +# Icons >> Icon >> Features ||20 + +```js script +import { html } from '@lion/core'; +import { icons } from '@lion/icon'; +import './assets/iconset-bugs.js'; +import './assets/iconset-misc.js'; +import * as spaceSet from './assets/iconset-space.js'; + +import '@lion/icon/define'; + +icons.addIconResolver('lion', (iconset, name) => { + switch (iconset) { + case 'bugs': + return import('./assets/iconset-bugs.js').then(module => module[name]); + case 'space': + return import('./assets/iconset-space.js').then(module => module[name]); + case 'misc': + return import('./assets/iconset-misc.js').then(module => module[name]); + default: + throw new Error(`Unknown iconset ${iconset}`); + } +}); +``` + +## Icon sets + +Icons are displayed using icon sets. These are collections of icons, lazily loaded on demand for performance. +See the [system documentation](../../../docs/systems/icon/overview.md) to learn more about icon sets. + +```js preview-story +export const iconSets = () => html` + ${Object.keys(spaceSet).map( + name => html` + +
+ + ${name} +
+ `, + )} +`; +``` + +If for some reason you don't want to lazy load icons, you can still import and use them +synchronously. + +## Accessibility + +It is recommended to add an `aria-label` to provide information to visually impaired users: + +A `lion-icon` without an `aria-label` attribute will be automatically given an `aria-hidden` attribute. + +```js preview-story +export const accessibleLabel = () => html` + +`; +``` + +## Styling + +By default, a `lion-icon` will be `1em` × `1em` (the current line-height). + +`lion-icon` uses SVGs and may be styled with CSS, including using CSS properties such as `fill`: + +```js preview-story +export const Styling = () => html` + + +`; +``` + +See [SVG and CSS](https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/SVG_and_CSS) on MDN web docs for more information. diff --git a/docs/components/icons/icon/index.md b/docs/components/icons/icon/index.md new file mode 100644 index 000000000..0642be966 --- /dev/null +++ b/docs/components/icons/icon/index.md @@ -0,0 +1,3 @@ +# Icons >> Icon + +-> go to Overview diff --git a/docs/components/icons/icon/overview.md b/docs/components/icons/icon/overview.md new file mode 100644 index 000000000..48a4902ab --- /dev/null +++ b/docs/components/icons/icon/overview.md @@ -0,0 +1,40 @@ +# Icons >> Icon >> Overview ||10 + +A web component for displaying icons. + +```js script +import { html } from '@lion/core'; +import { icons } from '@lion/icon'; +import '@lion/icon/define'; + +icons.addIconResolver('lion', (iconset, name) => { + switch (iconset) { + case 'bugs': + return import('./assets/iconset-bugs.js').then(module => module[name]); + case 'space': + return import('./assets/iconset-space.js').then(module => module[name]); + case 'misc': + return import('./assets/iconset-misc.js').then(module => module[name]); + default: + throw new Error(`Unknown iconset ${iconset}`); + } +}); +``` + +```js preview-story +export const main = () => html` + +`; +``` + +## Installation + +```bash +npm i --save @lion/icon +``` + +```js +import { LionIcon } from '@lion/icon'; +// or +import '@lion/icon/define'; +``` diff --git a/docs/components/icons/index.md b/docs/components/icons/index.md new file mode 100644 index 000000000..71654445e --- /dev/null +++ b/docs/components/icons/index.md @@ -0,0 +1 @@ +# Icons ||30 diff --git a/docs/components/index.md b/docs/components/index.md new file mode 100644 index 000000000..a2a9287a0 --- /dev/null +++ b/docs/components/index.md @@ -0,0 +1,16 @@ +# Components ||20 + +We offer many web components for your application needs. For each, you will find an overview page to highlight its capabilities and how to start using it. Additionally, there will be a features page that showcases multiple use cases in action. Some even have a dedicated examples page so you can see a possible styled implementation. + +For organizational purposes, we split them into the following groups. + +1. [Content](./content/accordion/overview.md)
+ Everything to help you organize your content. +2. [Icons](./icons/icon/overview.md)
+ Loading and displaying icons. +3. [Inputs](./inputs/overview.md)
+ Input components that lets you make complex forms with ease, including validation. +4. [Interaction](./interaction/button/overview.md)
+ A set of interactive components. +5. [Navigation](./navigation/pagination/overview.md)
+ The reasoning behind some of our decisions. diff --git a/docs/components/inputs/calendar/features.md b/docs/components/inputs/calendar/features.md new file mode 100644 index 000000000..470c2edf4 --- /dev/null +++ b/docs/components/inputs/calendar/features.md @@ -0,0 +1,192 @@ +# Inputs >> Calendar >> Features ||20 + +```js script +import { html, css } from '@lion/core'; +import '@lion/calendar/define'; +``` + +## Selected date + +The `selectedDate` is the date which is currently marked as selected. +You usually select a date by clicking on it with the mouse or hitting Enter on the keyboard. + +The `selectedDate` might not be within the dates in the current month view. + +```js preview-story +export const selectedDate = () => html` + + +`; +``` + +## Central Date + +The `centralDate` defines which day will be focused when keyboard moves the focus to the current month grid. +By default it is set to today, or the enabled day of the current month view that is closest to today's date. + +The next and previous months' buttons work by changing the `centralDate` with plus or minus one month. +Changing the `centralDate` may mean a different view will be displayed to your users if it is in a different month. +Usually if you change only the day, "nothing" happens as it's already currently in view. + +The `centralDate` can be different from `selectedDate` as you can have today as actively selected but still look at date that is years ago. +When the `selectedDate` changes, it will sync its value to the `centralDate`. + +```js preview-story +export const centralDate = () => { + const today = new Date(); + const centralDate = new Date(today.getFullYear(), today.getMonth() + 1, today.getDate()); + return html` + + + `; +}; +``` + +## Controlling focus + +You can control the focus by calling the following methods + +- `focusCentralDate()` +- `focusSelectedDate()` +- `focusDate(dateInstanceToFocus)` + +> Be aware that the central date changes when a new date is focused. + +```js preview-story +export const controllingFocus = () => { + const today = new Date(); + const selectedDate = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1); + const centralDate = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 5); + return html` + + + + + + `; +}; +``` + +## Limiting selectable values + +### Providing a lower limit + +To give a lower limit you can bind a date to the `minDate` property. + +```js preview-story +export const providingLowerLimit = () => { + const minDate = new Date(); + return html` + + + `; +}; +``` + +### Provide a higher limit + +To give a higher limit you can bind a date to the `maxDate` property. In this example, we show how to create an offset of + 2 days. + +```js preview-story +export const providingHigherLimit = () => { + const today = new Date(); + const maxDate = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 2); + return html` + + + `; +}; +``` + +### Provide a list of disabled dates + +In some cases a specific date or day of the week needs to be disabled, supply those days to the `disableDates` property. + +```js preview-story +export const disabledDates = () => html` + + day.getDay() === 6 || day.getDay() === 0} + > +`; +``` + +### Combined disable dates + +To limit the scope of possible dates further, combine the methods mentioned above. + +```js preview-story +export const combinedDisabledDates = () => { + const today = new Date(); + const maxDate = new Date(today.getFullYear(), today.getMonth() + 2, today.getDate()); + return html` + + day.getDay() === 6 || day.getDay() === 0} + .minDate="${new Date()}" + .maxDate="${maxDate}" + > + `; +}; +``` diff --git a/docs/components/inputs/calendar/index.md b/docs/components/inputs/calendar/index.md new file mode 100644 index 000000000..1e2b998ea --- /dev/null +++ b/docs/components/inputs/calendar/index.md @@ -0,0 +1,3 @@ +# Inputs >> Calendar ||20 + +-> go to Overview diff --git a/docs/components/inputs/calendar/overview.md b/docs/components/inputs/calendar/overview.md new file mode 100644 index 000000000..cdec4e2ba --- /dev/null +++ b/docs/components/inputs/calendar/overview.md @@ -0,0 +1,49 @@ +# Inputs >> Calendar >> Overview ||10 + +`lion-calendar` is a reusable and accessible calendar view. + +```js script +import { html, css } from '@lion/core'; +import '@lion/calendar/define'; +``` + +```js story +export const main = () => { + return html` + + + `; +}; +``` + +## Features + +- fully accessible keyboard navigation (Arrow Keys, PgUp, PgDn, ALT+PgUp, ALT+PgDn) +- **minDate**: disables all dates before a given date +- **maxDate**: disables all dates after a given date +- **disableDates**: disables some dates within an available range +- **selectedDate**: currently selected date +- **centralDate**: date that determines the currently visible month and that will be focused when keyboard moves the focus to the month grid +- **focusedDate**: (getter only) currently focused date (if there is any with real focus) +- **focusDate(date)**: focus on a certain date +- **focusSelectedDate()**: focus on the current selected date +- **focusCentralDate()**: focus on the current central date +- **firstDayOfWeek**: typically Sunday (default) or Monday +- **weekdayHeaderNotation**: long/short/narrow for the current locale (e.g. Thursday/Thu/T) +- **locale**: different locale for the current component only + +## Installation + +```bash +npm i --save @lion/calendar +``` + +```js +import '@lion/calendar/define'; +``` diff --git a/docs/components/inputs/checkbox-group/features.md b/docs/components/inputs/checkbox-group/features.md new file mode 100644 index 000000000..6274421a2 --- /dev/null +++ b/docs/components/inputs/checkbox-group/features.md @@ -0,0 +1,205 @@ +# Inputs >> Checkbox Group >> Features ||20 + +```js script +import { html } from '@lion/core'; +import '@lion/checkbox-group/define'; +import '@lion/checkbox-group/define'; +import '@lion/checkbox-group/define'; +``` + +## Model value + +The `modelValue` of a `lion-checkbox-group` is an array containing the `choiceValues` of the `lion-checkbox` elements that have been checked. + +Given the scientists example above, say that we were to select the first and last options +(Archimedes & Marie Curie). + +Then the `modelValue` of the `lion-checkbox-group` will look as follows: + +```js +const groupElement = [parent].querySelector('lion-checkbox-group'); +groupElement.modelValue; + => ["Archimedes", "Marie Curie"]; +``` + +## The `name` attribute + +The `name` attribute of a `lion-checkbox-group` automatically gets assigned to its `lion-checkbox` children. You can also specify names for the `lion-checkbox` elements, but if this name is different from the name assigned to `lion-checkbox-group`, then an exception will be thrown. + +Our recommendation would be to set the `name` attribute only on the `lion-checkbox-group` and not on the `lion-checkbox` elements. + +## Example + +```html + + + + + +``` + +## Pre-select + +You can pre-select options by targeting the `modelValue` object of the option and setting the `checked` property to `true`. + +```js preview-story +export const preselect = () => html` + + + + + +`; +``` + +## Disabled + +You can disable the entire group by setting the `disabled` attribute on the ``. + +```js preview-story +export const disabled = () => html` + + + + + +`; +``` + +## Help text + +You can add help text on each checkbox with `help-text` attribute on the ``. + +```js preview-story +export const helpText = () => html` + + + + + +`; +``` + +## Event + +You can listen to the `model-value-changed` event whenever the value of the checkbox group is changed. + +```js preview-story +export const event = ({ shadowRoot }) => html` + + (shadowRoot.getElementById('selectedDinosaur').innerText = JSON.stringify( + ev.target.modelValue, + null, + 4, + ))} + > + + + + +
+ Selected scientists: N/A +`; +``` + +## Indeterminate + +```js preview-story +export const indeterminate = () => html` + + + + + + + +`; +``` + +```js preview-story +export const indeterminateSiblings = () => html` + + + + + + + + + + + +`; +``` + +```js preview-story +export const indeterminateChildren = () => html` + + + + + + + + + + + +`; +``` diff --git a/docs/components/inputs/checkbox-group/index.md b/docs/components/inputs/checkbox-group/index.md new file mode 100644 index 000000000..a5862f14b --- /dev/null +++ b/docs/components/inputs/checkbox-group/index.md @@ -0,0 +1,3 @@ +# Inputs >> Checkbox Group ||20 + +-> go to Overview diff --git a/docs/components/inputs/checkbox-group/overview.md b/docs/components/inputs/checkbox-group/overview.md new file mode 100644 index 000000000..2f3c3d8df --- /dev/null +++ b/docs/components/inputs/checkbox-group/overview.md @@ -0,0 +1,38 @@ +# Inputs >> Checkbox Group >> Overview ||10 + +`lion-checkbox-group` component enhances the functionality of the native `` element. +Its purpose is to provide a way for users to check **multiple** options amongst a set of choices, or to function as a single toggle. + +> You should use `` elements as the children of the ``. + +```js script +import { html } from '@lion/core'; +import '@lion/checkbox-group/define'; +``` + +```js story +export const main = () => html` + + + + + +`; +``` + +> Make sure that the checkbox-group also has a name attribute, this is necessary for the [lion-form](../form/overview.md)'s serialization result. + +## Features + +Since it extends from [lion-fieldset](../fieldset/overview.md), +it has all the features a fieldset has. + +## Installation + +```bash +npm i --save @lion/checkbox-group +``` + +```js +import '@lion/checkbox-group/define'; +``` diff --git a/docs/components/inputs/combobox/examples.md b/docs/components/inputs/combobox/examples.md new file mode 100644 index 000000000..85d119706 --- /dev/null +++ b/docs/components/inputs/combobox/examples.md @@ -0,0 +1,77 @@ +# Inputs >> Combobox >> Examples ||30 + +```js script +import { html } from '@lion/core'; +import './src/md-combobox/md-combobox.js'; +import './src/gh-combobox/gh-combobox.js'; +import './src/wa-combobox/wa-combobox.js'; +``` + +```js preview-story +export const MaterialDesign = () => html` + + Apple + Artichoke + Asparagus + Banana + Beets + +`; +``` + +```js preview-story +export const Github = () => html` + + master + develop + release + feat/abc + feat/xyz123 + +`; +``` + +```js preview-story +export const Whatsapp = () => html` + + + + + + + +`; +``` diff --git a/docs/components/inputs/combobox/features.md b/docs/components/inputs/combobox/features.md new file mode 100644 index 000000000..801d06bef --- /dev/null +++ b/docs/components/inputs/combobox/features.md @@ -0,0 +1,253 @@ +# Inputs >> Combobox >> Features ||20 + +A combobox is a widget made up of the combination of two distinct elements: + +- a single-line textbox +- an associated listbox overlay + +Based on the combobox configuration and entered textbox value, options in the listbox will be +filtered, checked, focused and the textbox value may be autocompleted. +Optionally the combobox contains a graphical button adjacent to the textbox, indicating the +availability of the popup. + +> Fore more information, consult [Combobox wai-aria design pattern](https://www.w3.org/TR/wai-aria-practices/#combobox) + +```js script +import { html } from '@lion/core'; +import { listboxData } from '../../../../packages/listbox/docs/listboxData.js'; +import '@lion/listbox/define'; +import '@lion/combobox/define'; +import './src/demo-selection-display.js'; +import { lazyRender } from './src/lazyRender.js'; +import levenshtein from './src/levenshtein.js'; +``` + +## Autocomplete + +Below you will find an overview of all possible `autocomplete` behaviors and how they correspond +to the configurable values `none`, `list`, `inline` and `both`. + +| | list | filter | focus | check | complete | +| -----: | :--: | :----: | :---: | :---: | :------: | +| none | ✓ | | | | | +| list | ✓ | ✓ | ✓ | ✓ | | +| inline | ✓ | | ✓ | ✓ | ✓ | +| both | ✓ | ✓ | ✓ | ✓ | ✓ | + +- **list** shows a list on keydown character press +- **filter** filters list of potential matches according to `matchmode` or provided `matchCondition` +- **focus** automatically focuses closest match (makes it the activedescendant) +- **check** automatically checks/selects closest match when `selection-follows-focus` is enabled (this is the default configuration) +- **complete** completes the textbox value inline (the 'missing characters' will be added as selected text) + +When `autocomplete="none"` is configured, the suggested options in the overlay are not filtered +based on the characters typed in the textbox. +Selection will happen manually by the user. + +```js preview-story +export const autocompleteNone = () => html` + + ${lazyRender( + listboxData.map(entry => html` ${entry} `), + )} + +`; +``` + +When `autocomplete="list"` is configured, it will filter listbox suggestions based on textbox value. + +```js preview-story +export const autocompleteList = () => html` + + ${lazyRender( + listboxData.map(entry => html` ${entry} `), + )} + +`; +``` + +When `autocomplete="inline"` is configured, it will present a value completion prediction inside the text input itself. +It does NOT filter list of potential matches. + +```js preview-story +export const autocompleteInline = () => html` + + ${lazyRender( + listboxData.map(entry => html` ${entry} `), + )} + +`; +``` + +When `autocomplete="both"` is configured, it combines the filtered list from `'list'` with the text input value completion prediction from `'inline'`. +This is the default value for `autocomplete`. + +```js preview-story +export const autocompleteBoth = () => html` + + ${lazyRender( + listboxData.map(entry => html` ${entry} `), + )} + +`; +``` + +## Match Mode + +When `match-mode="begin"` is applied, the entered text in the textbox only filters +options whose values begin with the entered text. For instance, the entered text 'ch' will match +with value 'Chard', but not with 'Artichoke'. +By default `match-mode="all"` is applied. This will also match parts of a word. +So 'ch' will both match 'Chard' and 'Artichoke'. + +```js preview-story +export const matchModeBegin = () => html` + + ${lazyRender( + listboxData.map(entry => html` ${entry} `), + )} + +`; +``` + +```js preview-story +export const matchModeAll = () => html` + + ${lazyRender( + listboxData.map(entry => html` ${entry} `), + )} + +`; +``` + +When the preconfigurable `match-mode` conditions are not sufficient, +one can define a custom matching function. +The example below matches when the Levenshtein distance is below 3 (including some other conditions). + +```js preview-story +export const customMatchCondition = () => html` + + ${lazyRender( + listboxData.map(entry => html` ${entry} `), + )} + +`; +``` + +## Options + +```js preview-story +export const showAllOnEmpty = () => html` + + ${lazyRender( + listboxData.map(entry => html` ${entry} `), + )} + +`; +``` + +### Changing defaults + +By default `selection-follows-focus` will be true (aligned with the +wai-aria examples and the native ``). +It is possible to disable this behavior, so the active/focused and checked/selected values +will be kept track of independently. + +> Note that, (just like in a listbox), selection-follows-focus will never be applicable for +> multiselect comboboxes. + +```js preview-story +export const noSelectionFollowsFocus = () => html` + + ${lazyRender( + listboxData.map(entry => html` ${entry} `), + )} + +`; +``` + +By default `rotate-keyboard-navigation` will be true (aligned with the +wai-aria examples and the natve ``). +It is possible to disable this behavior, see example below + +```js preview-story +export const noRotateKeyboardNavigation = () => html` + + ${lazyRender( + listboxData.map(entry => html` ${entry} `), + )} + +`; +``` + +## Multiple choice + +Add `multiple-choice` flag to allow multiple values to be selected. +This will: + +- keep the listbox overlay open on click of an option +- display a list of selected option representations next to the text box +- make the value of type `Array` instead of `String` + +> Please note that the lion-combobox-selection-display below is not exposed and only serves +> as an example. The selection part of a multiselect combobox is not yet accessible. Please keep +> in mind that for now, as a Subclasser, you would have to take care of this part yourself. + +```js preview-story +export const multipleChoice = () => html` + + + ${lazyRender( + listboxData.map( + (entry, i) => + html` ${entry} `, + ), + )} + +`; +``` + +## Invoker button + +```js preview-story +export const invokerButton = () => html` + + + ${lazyRender( + listboxData.map(entry => html` ${entry} `), + )} + +`; +``` + +## Listbox compatibility + +All configurations that can be applied to `lion-listbox`, can be applied to `lion-combobox` as well. +See the [listbox documentation](../listbox/overview.md) for more information. diff --git a/docs/components/inputs/combobox/index.md b/docs/components/inputs/combobox/index.md new file mode 100644 index 000000000..be43f2d35 --- /dev/null +++ b/docs/components/inputs/combobox/index.md @@ -0,0 +1,3 @@ +# Inputs >> Combobox ||20 + +-> go to Overview diff --git a/docs/components/inputs/combobox/overview.md b/docs/components/inputs/combobox/overview.md new file mode 100644 index 000000000..47766ec3e --- /dev/null +++ b/docs/components/inputs/combobox/overview.md @@ -0,0 +1,47 @@ +# Inputs >> Combobox >> Overview ||10 + +A combobox is a widget made up of the combination of two distinct elements: + +- a single-line textbox +- an associated listbox overlay + +Based on the combobox configuration and entered textbox value, options in the listbox will be +filtered, checked, focused and the textbox value may be autocompleted. +Optionally, the combobox contains a graphical button adjacent to the textbox, indicating the +availability of the pop-up. + +> Fore more information, consult [Combobox wai-aria design pattern](https://www.w3.org/TR/wai-aria-practices/#combobox) + +```js script +import { html } from '@lion/core'; +import { listboxData } from '../../../../packages/listbox/docs/listboxData.js'; +import '@lion/listbox/define'; +import '@lion/combobox/define'; +import { lazyRender } from './src/lazyRender.js'; +``` + +```js preview-story +export const main = () => html` + + ${lazyRender( + listboxData.map(entry => html` ${entry} `), + )} + +`; +``` + +[...show more](./examples.md) + +## Features + +> tbd + +## Installation + +```bash +npm i --save @lion/combobox +``` + +```js +import '@lion/combobox/define'; +``` diff --git a/packages/combobox/docs/LinkMixin.js b/docs/components/inputs/combobox/src/LinkMixin.js similarity index 90% rename from packages/combobox/docs/LinkMixin.js rename to docs/components/inputs/combobox/src/LinkMixin.js index 2f87c7321..305abc5b5 100644 --- a/packages/combobox/docs/LinkMixin.js +++ b/docs/components/inputs/combobox/src/LinkMixin.js @@ -1,7 +1,7 @@ import { dedupeMixin } from '@lion/core'; /** - * @typedef {import('@lion/core').PropertyValues } changedProperties + * @typedef {import('lit-element').PropertyValues } changedProperties */ /** @@ -51,9 +51,6 @@ const LinkMixinImplementation = superclass => if (changedProperties.has('target')) { this._nativeAnchor.target = this.target; } - if (changedProperties.has('rel')) { - this._nativeAnchor.rel = this.rel; - } } __navigate() { diff --git a/packages/combobox/docs/demo-selection-display.js b/docs/components/inputs/combobox/src/demo-selection-display.js similarity index 97% rename from packages/combobox/docs/demo-selection-display.js rename to docs/components/inputs/combobox/src/demo-selection-display.js index b7e60b153..1ab2c09e8 100644 --- a/packages/combobox/docs/demo-selection-display.js +++ b/docs/components/inputs/combobox/src/demo-selection-display.js @@ -94,7 +94,7 @@ export class DemoSelectionDisplay extends LitElement { } /** - * @param {import('@lion/core').PropertyValues } changedProperties + * @param {import('lit-element').PropertyValues } changedProperties */ firstUpdated(changedProperties) { super.firstUpdated(changedProperties); @@ -106,7 +106,7 @@ export class DemoSelectionDisplay extends LitElement { } /** - * @param {import('@lion/core').PropertyValues } changedProperties + * @param {import('lit-element').PropertyValues } changedProperties */ onComboboxElementUpdated(changedProperties) { if (changedProperties.has('modelValue')) { diff --git a/packages/combobox/docs/gh-combobox/gh-button.js b/docs/components/inputs/combobox/src/gh-combobox/gh-button.js similarity index 96% rename from packages/combobox/docs/gh-combobox/gh-button.js rename to docs/components/inputs/combobox/src/gh-combobox/gh-button.js index e6512c164..c90190b61 100644 --- a/packages/combobox/docs/gh-combobox/gh-button.js +++ b/docs/components/inputs/combobox/src/gh-combobox/gh-button.js @@ -65,10 +65,5 @@ export class GhButton extends LionButton { `; } - - constructor() { - super(); - this.value = ''; - } } customElements.define('gh-button', GhButton); diff --git a/packages/combobox/docs/gh-combobox/gh-combobox.js b/docs/components/inputs/combobox/src/gh-combobox/gh-combobox.js similarity index 99% rename from packages/combobox/docs/gh-combobox/gh-combobox.js rename to docs/components/inputs/combobox/src/gh-combobox/gh-combobox.js index 8e6c6ce33..7c6880ca8 100644 --- a/packages/combobox/docs/gh-combobox/gh-combobox.js +++ b/docs/components/inputs/combobox/src/gh-combobox/gh-combobox.js @@ -2,7 +2,7 @@ import { css, html } from '@lion/core'; import { LionOption } from '@lion/listbox'; import { renderLitAsNode } from '@lion/helpers'; // import { withModalDialogConfig } from '@lion/overlays'; -import { LionCombobox } from '../../src/LionCombobox.js'; +import { LionCombobox } from '@lion/combobox'; import './gh-button.js'; export class GhOption extends LionOption { diff --git a/packages/combobox/docs/lazyRender.js b/docs/components/inputs/combobox/src/lazyRender.js similarity index 100% rename from packages/combobox/docs/lazyRender.js rename to docs/components/inputs/combobox/src/lazyRender.js diff --git a/packages/combobox/docs/levenshtein.js b/docs/components/inputs/combobox/src/levenshtein.js similarity index 100% rename from packages/combobox/docs/levenshtein.js rename to docs/components/inputs/combobox/src/levenshtein.js diff --git a/packages/combobox/docs/md-combobox/MdFieldMixin.js b/docs/components/inputs/combobox/src/md-combobox/MdFieldMixin.js similarity index 100% rename from packages/combobox/docs/md-combobox/MdFieldMixin.js rename to docs/components/inputs/combobox/src/md-combobox/MdFieldMixin.js diff --git a/packages/combobox/docs/md-combobox/md-combobox.js b/docs/components/inputs/combobox/src/md-combobox/md-combobox.js similarity index 97% rename from packages/combobox/docs/md-combobox/md-combobox.js rename to docs/components/inputs/combobox/src/md-combobox/md-combobox.js index 39469d6e4..db579b814 100644 --- a/packages/combobox/docs/md-combobox/md-combobox.js +++ b/docs/components/inputs/combobox/src/md-combobox/md-combobox.js @@ -1,6 +1,6 @@ import { css, html } from '@lion/core'; import { LionOption } from '@lion/listbox'; -import { LionCombobox } from '../../src/LionCombobox.js'; +import { LionCombobox } from '@lion/combobox'; import { MdFieldMixin } from './MdFieldMixin.js'; import './style/md-ripple.js'; import './style/load-roboto.js'; diff --git a/packages/combobox/docs/md-combobox/md-input.js b/docs/components/inputs/combobox/src/md-combobox/md-input.js similarity index 100% rename from packages/combobox/docs/md-combobox/md-input.js rename to docs/components/inputs/combobox/src/md-combobox/md-input.js diff --git a/packages/combobox/docs/md-combobox/style/load-roboto.js b/docs/components/inputs/combobox/src/md-combobox/style/load-roboto.js similarity index 100% rename from packages/combobox/docs/md-combobox/style/load-roboto.js rename to docs/components/inputs/combobox/src/md-combobox/style/load-roboto.js diff --git a/packages/combobox/docs/md-combobox/style/md-ripple.js b/docs/components/inputs/combobox/src/md-combobox/style/md-ripple.js similarity index 100% rename from packages/combobox/docs/md-combobox/style/md-ripple.js rename to docs/components/inputs/combobox/src/md-combobox/style/md-ripple.js diff --git a/packages/combobox/docs/wa-combobox/wa-combobox.js b/docs/components/inputs/combobox/src/wa-combobox/wa-combobox.js similarity index 84% rename from packages/combobox/docs/wa-combobox/wa-combobox.js rename to docs/components/inputs/combobox/src/wa-combobox/wa-combobox.js index 9d4162b0b..c786ba8a7 100644 --- a/packages/combobox/docs/wa-combobox/wa-combobox.js +++ b/docs/components/inputs/combobox/src/wa-combobox/wa-combobox.js @@ -1,7 +1,7 @@ import { html, css } from '@lion/core'; import { renderLitAsNode } from '@lion/helpers'; import { LionOption } from '@lion/listbox'; -import { LionCombobox } from '../../src/LionCombobox.js'; +import { LionCombobox } from '@lion/combobox'; class WaOption extends LionOption { static get properties() { @@ -167,9 +167,9 @@ class WaOption extends LionOption { :host([is-user-text-read]) .wa-option__content-row2-text-inner-icon { color: lightblue; } - .wa-selected { - color: #009688; - } + /* + .wa-option__content-row2-menu { + } */ `, ]; } @@ -178,7 +178,13 @@ class WaOption extends LionOption { return html`