[//]: # 'AUTO INSERT HEADER PREPUBLISH' # Accordion `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 { LitElement } from 'lit-element'; import { html } from 'lit-html'; import './lion-accordion.js'; export default { title: 'Navigation/Accordion', }; ``` ```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.

`; ``` ## How to use ### Installation ```bash npm i --save @lion/accordion ``` ```js import { LionAccordion } from '@lion/accordion'; // or import '@lion/accordion/lion-accordion.js'; ``` ### Usage ```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.

``` > An accordion exists off a list of expandable headings (of the same level). To get this behavior you need to add a slot="invoker" to the heading and place a button as the content. ## Examples ### 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. This means you must locate your content before it's invoker. ```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.

`; ``` ### Distribute New Elements Below, we demonstrate on how you could dynamically add 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}

`)}
``` ## Rationale ### Contents are not focusable Focusable elements should have a means to interact with them. Contents themselves do not offer any interactiveness. If there is a button or a form inside the tab panel then these elements get focused directly.