Skip to content
Snippets Groups Projects
accordion-item.tsx 6.35 KiB
Newer Older
Tuukka Turu's avatar
Tuukka Turu committed
import { Component, Prop, h } from '@stencil/core';

@Component({
  tag: 'hy-accordion-item',
  styleUrl: 'accordion-item.scss',
  shadow: false
})
export class AccordionItem {
  @Prop() accordiontitle?: string;

  render() {
    document.querySelectorAll('.hy-accordion-container').forEach(function (accordion) {
      console.log(accordion);
      // Allow for multiple accordion sections to be expanded at the same time
      var allowMultiple = accordion.hasAttribute('data-allow-multiple');
      // Allow for each toggle to both open and close individually
      var allowToggle = (allowMultiple) ? allowMultiple : accordion.hasAttribute('data-allow-toggle');
    
      // Create the array of toggle elements for the accordion group
      var triggers = Array.prototype.slice.call(accordion.querySelectorAll('.hy-accordion__button'));
      var panels = Array.prototype.slice.call(accordion.querySelectorAll('.hy-accordion__content'));
    
    
      accordion.addEventListener('click', function (event) {
        var target = event.target as HTMLTextAreaElement;
    
        if (target && target.classList.contains('hy-accordion__button')) {
          // Check if the current toggle is expanded.
          var isExpanded = target.getAttribute('aria-expanded') == 'true';
          var active = accordion.querySelector('[aria-expanded="true"]');
    
          // without allowMultiple, close the open accordion
          if (!allowMultiple && active && active !== target) {
            // Set the expanded state on the triggering element
            active.setAttribute('aria-expanded', 'false');
            // Hide the accordion sections, using aria-controls to specify the desired section
            document.getElementById(active.getAttribute('aria-controls')).setAttribute('hidden', '');
    
            // When toggling is not allowed, clean up disabled state
            if (!allowToggle) {
              active.removeAttribute('aria-disabled');
            }
          }
    
          if (!isExpanded) {
            // Set the expanded state on the triggering element
            target.setAttribute('aria-expanded', 'true');
            // Hide the accordion sections, using aria-controls to specify the desired section
            document.getElementById(target.getAttribute('aria-controls')).removeAttribute('hidden');
    
            // If toggling is not allowed, set disabled state on trigger
            if (!allowToggle) {
              target.setAttribute('aria-disabled', 'true');
            }
          }
          else if (allowToggle && isExpanded) {
            // Set the expanded state on the triggering element
            target.setAttribute('aria-expanded', 'false');
            // Hide the accordion sections, using aria-controls to specify the desired section
            document.getElementById(target.getAttribute('aria-controls')).setAttribute('hidden', '');
          }
    
          event.preventDefault();
        }
      });
    
      // Bind keyboard behaviors on the main accordion container
      accordion.addEventListener('keydown', function (event) {
        var target = event.target as HTMLTextAreaElement;
        var key = event.which.toString() as HTMLTextAreaElement;
    
        var isExpanded = target.getAttribute('aria-expanded') == 'true';
        var allowToggle = (allowMultiple) ? allowMultiple : accordion.hasAttribute('data-allow-toggle');
    
        // 33 = Page Up, 34 = Page Down
        var ctrlModifier = (event.ctrlKey && key.match(/33|34/));
    
        // Is this coming from an accordion header?
        if (target && target.classList.contains('hy-accordion__button')) {
          // Up/ Down arrow and Control + Page Up/ Page Down keyboard operations
          // 38 = Up, 40 = Down
          if (key.match(/38|40/) || ctrlModifier) {
            var index = triggers.indexOf(target);
            var direction = (key.match(/34|40/)) ? 1 : -1;
            var length = triggers.length;
            var newIndex = (index + length + direction) % length;
    
            triggers[newIndex].focus();
    
            event.preventDefault();
          }
          else if (key.match(/35|36/)) {
            // 35 = End, 36 = Home keyboard operations
            switch (key) {
              // Go to first accordion
              case '36':
                triggers[0].focus();
                break;
                // Go to last accordion
              case '35':
                triggers[triggers.length - 1].focus();
                break;
            }
            event.preventDefault();
    
          }
    
        }
      });
    
      // These are used to style the accordion when one of the buttons has focus
      accordion.querySelectorAll('.hy-accordion__button').forEach(function (trigger) {
    
        trigger.addEventListener('focus', function (event) {
          accordion.classList.add('focus');
        });
    
        trigger.addEventListener('blur', function (event) {
          accordion.classList.remove('focus');
        });
    
      });
    
      // Minor setup: will set disabled state, via aria-disabled, to an
      // expanded/ active accordion which is not allowed to be toggled close
      if (!allowToggle) {
        // Get the first expanded/ active accordion
        var expanded = accordion.querySelector('[aria-expanded="true"]');
    
        // If an expanded/ active accordion is found, disable
        if (expanded) {
          expanded.setAttribute('aria-disabled', 'true');
        }
      }
    
    });
    
    const classAttributes = ["hy-accordion__item", "hy-accordion__item__is-open"];
    const titleAsId = this.accordiontitle.toLowerCase().replace(/\W/g,'-');
    return (
      <div class={classAttributes.join(" ")}>
        <hy-heading heading="h3" class="hy-accordion--heading">
          <button
            aria-expanded="false"
            aria-controls={`${titleAsId}--content`}
            class="hy-accordion__button"
            id={`${titleAsId}--title`}
          >
            <span class="hy-accordion--heading__icon">
              <hy-icon icon={'hy-icon-caret-down'} size={16} />
            </span>
            { this.accordiontitle }
          </button>
        </hy-heading>
        <div
          aria-labelledBy={`${titleAsId}--title`}
          class="hy-accordion__content"
          id={`${titleAsId}--content`}
          role="region"
          hidden
        >
          <slot></slot>
        </div>
      </div>
    );
  }

}