EC System

Accordions

Why and how to use this component

If you need to present a lot of content on a page, it can be divided into sub-sections in a structured way.

The accordion component is a list of headers that can be clicked to hide or reveal additional content. It is also possible to add an icon to the headers.

When to use this component

  • when you have a large amount of information
  • on FAQ pages

Do not use this component

  • as a rule avoid hiding information as much as possible

Resources

{#
  Parameters:
    - "panels" (array) (default: ""): format [
        {
          "id" (string) (default: '')
          "isExpanded" (boolean) (default: false)
          "heading": {
            "level" (string) (default: '')
            "title" (string) (default: '')
            "icon" (string) (default: '')
          },
          "body" (string) (default: '')
        },
        ...
      ]
    - "extra_classes" (string) (default: '')
    - "extra_attributes" (array) (default: []): format: [
        {
          'name': 'name_of_the_attribute',
          'value': 'value_of_the_attribute'
        },
        ...
      ]
#}

{% include '@ecl/generic-component-accordion' %}
{
  "_demo": {
    "scripts": "document.addEventListener('DOMContentLoaded', function () {\n      ECL.accordions();\n    });"
  },
  "panels": [
    {
      "id": "pan1",
      "heading": {
        "title": "Jobs, Growth, Investment and Competitiveness with an additional quite long string",
        "icon": "growth",
        "level": 2
      },
      "body": "<p class=\"ecl-paragraph\">Bullfights. Bull hockey. Do you like this? The bull is stabbed, prodded, beaten. The bull is wounded. The bull is tired before the matador ever steps into the ring. Now, is that victory? Of course it is. Wanna know the secret to winning? Creative sportsmanship. In other words, one has to rig the game.</p>"
    },
    {
      "id": "pan2",
      "heading": {
        "title": "Energy Union",
        "icon": "energy",
        "level": 2
      },
      "body": "<p class=\"ecl-paragraph\">Bullfights. Bull hockey. Do you like this? The bull is stabbed, prodded, beaten. The bull is wounded. The bull is tired before the matador ever steps into the ring. Now, is that victory? Of course it is. Wanna know the secret to winning? Creative sportsmanship. In other words, one has to rig the game.</p>"
    }
  ]
}
<dl class="ecl-accordion" role="presentation">
  <dt role="heading" aria-level="2">
    <button id="ecl-accordion-header-pan1" class="ecl-accordion__header" aria-controls="ecl-accordion-panel-pan1" aria-expanded="false">
      <span class="ecl-accordion__header-icon ecl-icon ecl-icon--rounded ecl-u-bg-secondary ecl-icon--growth"></span>
      Jobs, Growth, Investment and Competitiveness with an additional quite long string
    </button>
  </dt>
  <dd id="ecl-accordion-panel-pan1" class="ecl-accordion__panel" role="region" aria-labelledby="ecl-accordion-header-pan1" aria-hidden="true">
    <p class="ecl-paragraph">Bullfights. Bull hockey. Do you like this? The bull is stabbed, prodded, beaten. The bull is wounded. The bull is tired before the matador ever steps into the ring. Now, is that victory? Of course it is. Wanna know the secret to winning? Creative sportsmanship. In other words, one has to rig the game.</p>
  </dd>
  <dt role="heading" aria-level="2">
    <button id="ecl-accordion-header-pan2" class="ecl-accordion__header" aria-controls="ecl-accordion-panel-pan2" aria-expanded="false">
      <span class="ecl-accordion__header-icon ecl-icon ecl-icon--rounded ecl-u-bg-secondary ecl-icon--energy"></span>
      Energy Union
    </button>
  </dt>
  <dd id="ecl-accordion-panel-pan2" class="ecl-accordion__panel" role="region" aria-labelledby="ecl-accordion-header-pan2" aria-hidden="true">
    <p class="ecl-paragraph">Bullfights. Bull hockey. Do you like this? The bull is stabbed, prodded, beaten. The bull is wounded. The bull is tired before the matador ever steps into the ring. Now, is that victory? Of course it is. Wanna know the secret to winning? Creative sportsmanship. In other words, one has to rig the game.</p>
  </dd>
</dl>
  • Content:
    // Heavily inspired by the accordion component from https://github.com/frend/frend.co
    
    import { queryAll } from '@ecl/ec-base/helpers/dom';
    
    /**
     * @param {object} options Object containing configuration overrides
     */
    export const accordions = ({
      selector: selector = '.ecl-accordion',
      headerSelector: headerSelector = '.ecl-accordion__header',
    } = {}) => {
      // SUPPORTS
      if (
        !('querySelector' in document) ||
        !('addEventListener' in window) ||
        !document.documentElement.classList
      )
        return null;
    
      // SETUP
      // set accordion element NodeLists
      const accordionContainers = queryAll(selector);
    
      // ACTIONS
      function hidePanel(target) {
        // get panel
        const activePanel = document.getElementById(
          target.getAttribute('aria-controls')
        );
    
        target.setAttribute('aria-expanded', 'false');
    
        // toggle aria-hidden
        activePanel.setAttribute('aria-hidden', 'true');
      }
    
      function showPanel(target) {
        // get panel
        const activePanel = document.getElementById(
          target.getAttribute('aria-controls')
        );
    
        // set attributes on header
        target.setAttribute('tabindex', 0);
        target.setAttribute('aria-expanded', 'true');
    
        // toggle aria-hidden and set height on panel
        activePanel.setAttribute('aria-hidden', 'false');
      }
    
      function togglePanel(target) {
        // close target panel if already active
        if (target.getAttribute('aria-expanded') === 'true') {
          hidePanel(target);
          return;
        }
    
        showPanel(target);
      }
    
      function giveHeaderFocus(headerSet, i) {
        // set active focus
        headerSet[i].focus();
      }
    
      // EVENTS
      function eventHeaderClick(e) {
        togglePanel(e.currentTarget);
      }
    
      function eventHeaderKeydown(e) {
        // collect header targets, and their prev/next
        const currentHeader = e.currentTarget;
        const isModifierKey = e.metaKey || e.altKey;
        // get context of accordion container and its children
        const thisContainer = currentHeader.parentNode.parentNode;
        const theseHeaders = queryAll(headerSelector, thisContainer);
        const currentHeaderIndex = [].indexOf.call(theseHeaders, currentHeader);
    
        // don't catch key events when ⌘ or Alt modifier is present
        if (isModifierKey) return;
    
        // catch enter/space, left/right and up/down arrow key events
        // if new panel show it, if next/prev move focus
        switch (e.keyCode) {
          case 13:
          case 32:
            togglePanel(currentHeader);
            e.preventDefault();
            break;
          case 37:
          case 38: {
            const previousHeaderIndex =
              currentHeaderIndex === 0
                ? theseHeaders.length - 1
                : currentHeaderIndex - 1;
            giveHeaderFocus(theseHeaders, previousHeaderIndex);
            e.preventDefault();
            break;
          }
          case 39:
          case 40: {
            const nextHeaderIndex =
              currentHeaderIndex < theseHeaders.length - 1
                ? currentHeaderIndex + 1
                : 0;
            giveHeaderFocus(theseHeaders, nextHeaderIndex);
            e.preventDefault();
            break;
          }
          default:
            break;
        }
      }
    
      // BIND EVENTS
      function bindAccordionEvents(accordionContainer) {
        const accordionHeaders = queryAll(headerSelector, accordionContainer);
        // bind all accordion header click and keydown events
        accordionHeaders.forEach(accordionHeader => {
          accordionHeader.addEventListener('click', eventHeaderClick);
          accordionHeader.addEventListener('keydown', eventHeaderKeydown);
        });
      }
    
      // UNBIND EVENTS
      function unbindAccordionEvents(accordionContainer) {
        const accordionHeaders = queryAll(headerSelector, accordionContainer);
        // unbind all accordion header click and keydown events
        accordionHeaders.forEach(accordionHeader => {
          accordionHeader.removeEventListener('click', eventHeaderClick);
          accordionHeader.removeEventListener('keydown', eventHeaderKeydown);
        });
      }
    
      // DESTROY
      function destroy() {
        accordionContainers.forEach(accordionContainer => {
          unbindAccordionEvents(accordionContainer);
        });
      }
    
      // INIT
      function init() {
        if (accordionContainers.length) {
          accordionContainers.forEach(accordionContainer => {
            bindAccordionEvents(accordionContainer);
          });
        }
      }
    
      init();
    
      // REVEAL API
      return {
        init,
        destroy,
      };
    };
    
    // module exports
    export default accordions;
    
  • URL: /components/raw/ec-component-accordion/ec-component-accordion.js
  • Filesystem Path: ../../src/systems/ec/ec-component/ec-component-accordion/ec-component-accordion.js
  • Size: 4.4 KB
  • Content:
    /**
     * Accordions
     * @define accordion
     */
    
    // Import base and generic
    @import '@ecl/ec-base/ec-base';
    @import '@ecl/generic-component-accordion/generic-component-accordion';
    
    // Check if overridden dependencies are already loaded, if needed
    @include check-imports(('ec-style-icon'));
    
    // Use generic mixin
    @include exports('ec-component-accordion') {
      @include ecl-accordion();
    }
    
  • URL: /components/raw/ec-component-accordion/ec-component-accordion.scss
  • Filesystem Path: ../../src/systems/ec/ec-component/ec-component-accordion/ec-component-accordion.scss
  • Size: 382 Bytes
  • Handle: @ecl/ec-component-accordion
  • Tags: organism
  • Preview:
  • Filesystem Path: ../../src/systems/ec/ec-component/ec-component-accordion/ec-component-accordion.twig