255 lines
7.2 KiB
Text
255 lines
7.2 KiB
Text
import { Story, Meta, html, Preview } from '@open-wc/demoing-storybook';
|
|
import { LitElement } from '@lion/core';
|
|
|
|
import '../lea-tabs.js';
|
|
import '../lea-tab.js';
|
|
import '../lea-tab-panel.js';
|
|
|
|
<Meta title="Intro/Tabs Example" parameters={{ component: 'lea-tabs' }} />
|
|
|
|
# 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.
|
|
|
|
<Preview>
|
|
<Story name="default">
|
|
{html`
|
|
<lea-tabs>
|
|
<lea-tab slot="tab">Info</lea-tab>
|
|
<lea-tab-panel slot="panel">
|
|
Info page with lots of information about us.
|
|
</lea-tab-panel>
|
|
<lea-tab slot="tab">Work</lea-tab>
|
|
<lea-tab-panel slot="panel">
|
|
Work page that showcases our work.
|
|
</lea-tab-panel>
|
|
</lea-tabs>
|
|
`}
|
|
</Story>
|
|
</Preview>
|
|
|
|
## How to use
|
|
|
|
### Installation
|
|
|
|
```sh
|
|
npm i --save @lion/tabs;
|
|
```
|
|
|
|
### Usage
|
|
|
|
```js
|
|
import '@lion/tabs/lea-tabs.js';
|
|
```
|
|
|
|
```html
|
|
<lea-tabs>
|
|
<lea-tab slot="tab">Info</lea-tab>
|
|
<lea-tab-panel slot="panel">
|
|
Info page with lots of information about us.
|
|
</lea-tab-panel>
|
|
<lea-tab slot="tab">Work</lea-tab>
|
|
<lea-tab-panel slot="panel">
|
|
Work page that showcases our work.
|
|
</lea-tab-panel>
|
|
</lea-tabs>
|
|
```
|
|
|
|
## Examples
|
|
|
|
### Selected Index
|
|
|
|
You can set the `selectedIndex` to select a certain tab.
|
|
|
|
<Preview>
|
|
<Story name="Selected index">
|
|
{html`
|
|
<lea-tabs .selectedIndex=${1}>
|
|
<lea-tab slot="tab">Info</lea-tab>
|
|
<lea-tab-panel slot="panel">
|
|
Info page with lots of information about us.
|
|
</lea-tab-panel>
|
|
<lea-tab slot="tab">Work</lea-tab>
|
|
<lea-tab-panel slot="panel">
|
|
Work page that showcases our work.
|
|
</lea-tab-panel>
|
|
</lea-tabs>
|
|
`}
|
|
</Story>
|
|
</Preview>
|
|
|
|
### 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.
|
|
|
|
<Preview>
|
|
<Story name="Slots order">
|
|
{html`
|
|
<lea-tabs>
|
|
<lea-tab slot="tab">Info</lea-tab>
|
|
<lea-tab slot="tab">Work</lea-tab>
|
|
<lea-tab-panel slot="panel">
|
|
Info page with lots of information about us.
|
|
</lea-tab-panel>
|
|
<lea-tab-panel slot="panel">
|
|
Work page that showcases our work.
|
|
</lea-tab-panel>
|
|
</lea-tabs>
|
|
`}
|
|
</Story>
|
|
</Preview>
|
|
|
|
### Distribute New Elements
|
|
|
|
Below, we demonstrate on how you could dynamically add new tab + panels.
|
|
|
|
<Story name="Distribute new elements">
|
|
{() => {
|
|
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`
|
|
<h3>Append</h3>
|
|
<button @click="${this.__handleAppendClick}">
|
|
Append
|
|
</button>
|
|
<lea-tabs id="appendTabs">
|
|
<lea-tab slot="tab">tab 1</lea-tab>
|
|
<lea-tab-panel slot="panel">panel 1</lea-tab-panel>
|
|
<lea-tab slot="tab">tab 2</lea-tab>
|
|
<lea-tab-panel slot="panel">panel 2</lea-tab-panel>
|
|
</lea-tabs>
|
|
<hr />
|
|
<h3>Push</h3>
|
|
<button @click="${this.__handlePushClick}">
|
|
Push
|
|
</button>
|
|
<lea-tabs id="pushTabs">
|
|
<lea-tab slot="tab">tab 1</lea-tab>
|
|
<lea-tab-panel slot="panel">panel 1</lea-tab-panel>
|
|
<lea-tab slot="tab">tab 2</lea-tab>
|
|
<lea-tab-panel slot="panel">panel 2</lea-tab-panel>
|
|
${this.__collection.map(
|
|
item => html`
|
|
<lea-tab slot="tab">${item.button}</lea-tab>
|
|
<lea-tab-panel slot="panel">${item.panel}</lea-tab-panel>
|
|
`,
|
|
)}
|
|
</lea-tabs>
|
|
`;
|
|
}
|
|
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`
|
|
<lea-tabs-experimental></lea-tabs-experimental>
|
|
`;
|
|
}}
|
|
</Story>
|
|
|
|
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
|
|
<lea-tabs id="pushTabs">
|
|
${myCollection.map(item => html`
|
|
<lea-tab slot="tab">${item.button}</lea-tab>
|
|
<lea-tab-panel slot="panel">${item.panel}</lea-tab-panel>
|
|
`)}
|
|
</lea-tabs>
|
|
```
|
|
|
|
## 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 <https://www.w3.org/TR/wai-aria-practices/#keyboard-interaction-19>
|
|
|
|
> 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.
|