/** * 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; }
/** * 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; }