Example #1
0
/**
 * Collapse a collapsible element
 *
 * @param {HTMLElement} wrapper - The element wrapper
 * @param {HTMLElement} contentElem - The element containing the collapsible content.
 * @param {HTMLElement} toggleElem - The clickable element used for toggling.
 * @param {String} labelExpand - The label used to describe the toggle button
 *    when the element is collapsed and it is used expanding.
 * @param {String} labelCollapse - The label used to describe the toggle button
 *    when the element is expanded and it is used collapsing.
 * @param {String} collapsedClass - The label to attach to the wrapper when it is collapsed.
 * @param {String} expandedClass - The label to attach to the wrapper when it is expanded.
 * @param {String} bpsSelector - A selector for the element to which the active
 *    breakpoint data is attached. See
 *    [htz-parse-bps-state](https://github.com/haaretz/htz-parse-bps-state)
 * @param {Boolean} [isExpanded] - Indicates if the collapsible is currently collapsed (`false`) or
 *   expanded (true`).
 *
 * @fires module:htz-htz-collapsibles/changeState~collapse#htz-collapsibles:collapse-before
 * @fires module:htz-htz-collapsibles/changeState~collapse#htz-collapsibles:collapse-after
 *
 * @return {Boolean} Returns `false` to indicate the element has been collapsed.
 */
function collapse(
  wrapper,
  contentElem,
  toggleElem,
  labelExpand,
  labelCollapse,
  collapsedClass,
  expandedClass,
  bpsSelector,
  isExpanded
) {
  const expandedClassRegex = new RegExp(`(${expandedClass}[^'"\\s]*)`, 'gi');


  if ((isExpanded !== undefined && isExpanded) || !isCollapsed(contentElem)) {
    const bpsState = parseBpsState(bpsSelector);

    // Only collapse elements if collapsing is allowed at current breakpoint.
    if (isCollapsible(wrapper, bpsState)) {
      /**
      * A custom event fired before a collapsible is collapsed
      * Stops execution if any of its handlers calls `event.preventDefault`.
      *
      * @event module:htz-htz-collapsibles/changeState~collapse#htz-collapsibles:collapse-before
      * @type {Object}
      * @prop {Object} detail
      * @prop {HTMLElement} detail.wrapper - The element wrapping
      * @prop {HTMLElement} detail.contentElem - The element containing the collapsible content.
      * @prop {HTMLElement} detail.toggleElem - The clickable element used for toggling.
      * @prop {Function} detail.isCollapsed - Retuns a `Boolean` indicating if the
      *     element is collapsed.
      */
      const allowed = dispatchEvent(
        wrapper,
        'htz-collapsibles:expand-before',
        { wrapper, contentElem, toggleElem, isCollapsed }
      );

      if (allowed) {
        toggleElem.focus();
        wrapper.classList.add(collapsedClass);
        /* eslint-disable no-param-reassign */
        wrapper.className = wrapper.className.replace(expandedClassRegex, '');
        /* eslint-enable no-param-reassign */
        toggleElem.setAttribute('aria-label', labelExpand);
        toggleElem.setAttribute('aria-expanded', false);

        /**
        * A custom event fired after a collapsible is collapsed
        *
        * @event module:htz-htz-collapsibles/changeState~collapse#htz-collapsibles:collapse-after
        * @type {Object}
        * @prop {Object} detail
        * @prop {HTMLElement} detail.wrapper - The element wrapping
        * @prop {HTMLElement} detail.contentElem - The element containing the collapsible content.
        * @prop {HTMLElement} detail.toggleElem - The clickable element used for toggling.
        * @prop {Function} detail.isCollapsed - Retuns a `Boolean` indicating if the
        *     element is collapsed.
        */
        dispatchEvent(
          wrapper,
          'htz-collapsibles:collapse-after',
          { wrapper, contentElem, toggleElem, isCollapsed }
        );
      }
    }
  }

  return false;
}
Example #2
0
/**
 * Evaluate a collapsible element and set its
 * state to the correct one in current conditions.
 *
 * @param {HTMLElement} element - An element to evaluate.
 * @param {HTMLElement} contentElem - The element containing the collapsible content.
 * @param {HTMLElement} toggleElem - The clickable element used for toggling.
 * @param {String} labelExpand - The label used to describe the toggle button
 *    when the element is collapsed and the button is used for expanding.
 * @param {String} labelCollapse - The label used to describe the toggle button
 *    when the element is expanded and the button is used for collapsing.
 * @param {String} collapsedClass - The label to attach to the wrapper when it is collapsed.
 * @param {String} expandedClass - The label to attach to the wrapper when it is expanded.
 * @param {String} bpsSelector - A selector for the element to which the active
 *    breakpoint data is attached. See
 *    [htz-parse-bps-state](https://github.com/haaretz/htz-parse-bps-state)
 * @param {Boolean} [isExpanded] - Indicates if the collapsible is collapsed (`false`) or
 *   expanded (true`).
 *
 * @return {Boolean} - The evaluated element's state - `false` if collapsed,
 *     `true` if expanded.
 */
export default function evaluate(
  element,
  contentElem,
  toggleElem,
  labelExpand,
  labelCollapse,
  collapsedClass,
  expandedClass,
  bpsSelector,
  isExpanded
) {
  const bpsState = parseBpsState(bpsSelector);
  const collapsedClassRegex = new RegExp(`(${collapsedClass}[^'"\\s]*)`, 'gi');

  const elIsCollapsible = isCollapsible(element, bpsState);

  if (elIsCollapsible) {
    if (!contentElem.getAttribute('tabindex')) {
      // Make content element programmatically focusable,
      // but keep it outside the tab cycle
      contentElem.setAttribute('tabindex', '-1');
    }

    if (isExpanded === undefined) {
      if (isCollapsed(contentElem)) {
        toggleElem.setAttribute('aria-label', labelCollapse);
        toggleElem.setAttribute('aria-expanded', true);

        return undefined;
      }

      toggleElem.setAttribute('aria-label', labelExpand);
      toggleElem.setAttribute('aria-expanded', false);

      return undefined;
    }

    return isExpanded ?
      expand(
        element,
        contentElem,
        toggleElem,
        labelExpand,
        labelCollapse,
        collapsedClass,
        expandedClass,
        bpsSelector
      ) :
      collapse(
        element,
        contentElem,
        toggleElem,
        labelExpand,
        labelCollapse,
        collapsedClass,
        expandedClass,
        bpsSelector
      );
  }

  toggleElem.removeAttribute('aria-controls');
  toggleElem.removeAttribute('aria-label');
  toggleElem.removeAttribute('aria-expanded');
  element.classList.add(expandedClass);
  /* eslint-disable no-param-reassign */
  element.className = element.className.replace(collapsedClassRegex, '');
  /* eslint-enable no-param-reassign */

  return undefined;
}