lion/packages/tabs
CircleCI 94b87c80f5 chore: release new versions
- @lion/ajax@0.4.0
 - babel-plugin-extend-docs@0.2.0
 - @lion/button@0.7.0
 - @lion/calendar@0.9.0
 - @lion/checkbox-group@0.10.0
 - @lion/dialog@0.7.0
 - @lion/fieldset@0.13.0
 - @lion/form-core@0.1.0
 - @lion/form-integrations@0.1.0
 - @lion/form@0.6.0
 - @lion/icon@0.6.0
 - @lion/input-amount@0.7.0
 - @lion/input-date@0.7.0
 - @lion/input-datepicker@0.14.0
 - @lion/input-email@0.8.0
 - @lion/input-iban@0.9.0
 - @lion/input-range@0.4.0
 - @lion/input@0.7.0
 - @lion/localize@0.11.0
 - @lion/overlays@0.16.0
 - @lion/radio-group@0.10.0
 - @lion/select-rich@0.18.0
 - @lion/select@0.7.0
 - @lion/steps@0.5.0
 - @lion/switch@0.10.0
 - @lion/tabs@0.4.0
 - @lion/textarea@0.7.0
 - @lion/tooltip@0.11.0
 - @lion/validate-messages@0.1.0
2020-05-29 15:03:48 +00:00
..
src fix(tabs): do not focus tabs when selectedIndex is set (#729) 2020-05-27 16:44:39 +02:00
test fix(tabs): do not focus tabs when selectedIndex is set (#729) 2020-05-27 16:44:39 +02:00
CHANGELOG.md chore: release new versions 2020-05-29 15:03:48 +00:00
index.js feat(tabs): create tabs component 2019-10-31 10:50:45 +01:00
lion-tabs.js feat(tabs): create tabs component 2019-10-31 10:50:45 +01:00
package.json chore: release new versions 2020-05-29 15:03:48 +00:00
README.md chore: updated dep versions and made compatible 2020-05-29 17:01:15 +02:00

Tabs

lion-tabs implements tabs view to allow users to quickly move between a small number of equally important views.

import { LitElement } from 'lit-element';
import { html } from 'lit-html';
import './lion-tabs.js';

export default {
  title: 'Navigation/Tabs',
};
export const main = () => html`
  <lion-tabs>
    <button slot="tab">Info</button>
    <p slot="panel">
      Info page with lots of information about us.
    </p>
    <button slot="tab">Work</button>
    <p slot="panel">
      Work page that showcases our work.
    </p>
  </lion-tabs>
`;

Live Demo/Documentation

See our storybook for a live demo and API documentation

How to use

Installation

npm i --save @lion/tabs;

Usage

import { LiontTabs } from '@lion/tabs';
// or
import '@lion/tabs/lion-tabs.js';
<lion-tabs>
  <button slot="tab">Info</button>
  <p slot="panel">
    Info page with lots of information about us.
  </p>
  <button slot="tab">Work</button>
  <p slot="panel">
    Work page that showcases our work.
  </p>
</lion-tabs>

Examples

Selected Index

You can set the selectedIndex to select a certain tab.

export const selectedIndex = () => html`
  <lion-tabs .selectedIndex=${1}>
    <button slot="tab">Info</button>
    <p slot="panel">
      Info page with lots of information about us.
    </p>
    <button slot="tab">Work</button>
    <p slot="panel">
      Work page that showcases our work.
    </p>
  </lion-tabs>
`;

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.

export const slotsOrder = () => html`
  <lion-tabs>
    <button slot="tab">Info</button>
    <button slot="tab">Work</button>
    <p slot="panel">
      Info page with lots of information about us.
    </p>
    <p slot="panel">
      Work page that showcases our work.
    </p>
  </lion-tabs>
`;

Distribute New Elements

Below, we demonstrate on how you could dynamically add new tab + panels.

export const distributeNewElement = () => {
  const tagName = 'lion-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>
            <lion-tabs id="appendTabs">
              <button slot="tab">tab 1</button>
              <p slot="panel">panel 1</p>
              <button slot="tab">tab 2</button>
              <p slot="panel">panel 2</p>
            </lion-tabs>
            <hr />
            <h3>Push</h3>
            <button @click="${this.__handlePushClick}">
              Push
            </button>
            <lion-tabs id="pushTabs">
              <button slot="tab">tab 1</button>
              <p slot="panel">panel 1</p>
              <button slot="tab">tab 2</button>
              <p slot="panel">panel 2</p>
              ${this.__collection.map(
                item => html`
                  <button slot="tab">${item.button}</button>
                  <p slot="panel">${item.panel}</p>
                `,
              )}
            </lion-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('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` <lion-tabs-experimental></lion-tabs-experimental> `;
};

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:

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

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

<lion-tabs id="pushTabs">
  ${myCollection.map(item => html`
  <button slot="tab">${item.button}</button>
  <p slot="panel">${item.panel}</p>
  `)}
</lion-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.