@ecl/ec-component-accordion
Advanced tools
Comparing version 1.2.0 to 2.1.0
@@ -1,162 +0,87 @@ | ||
// Heavily inspired by the accordion component from https://github.com/frend/frend.co | ||
import { queryOne, queryAll } from '@ecl/ec-base/helpers/dom'; | ||
import { queryAll } from '@ecl/ec-base/helpers/dom'; | ||
class Accordion { | ||
constructor( | ||
element, | ||
{ | ||
toggleSelector: toggleSelector = '[data-ecl-accordion-toggle]', | ||
attachClickListener: attachClickListener = true, | ||
} = {} | ||
) { | ||
// Check element | ||
if (!element || element.nodeType !== Node.ELEMENT_NODE) { | ||
throw new TypeError( | ||
'DOM element should be given to initialize this widget.' | ||
); | ||
} | ||
/** | ||
* @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; | ||
this.element = element; | ||
// SETUP | ||
// set accordion element NodeLists | ||
const accordionContainers = queryAll(selector); | ||
// Options | ||
this.toggleSelector = toggleSelector; | ||
this.attachClickListener = attachClickListener; | ||
// ACTIONS | ||
function hidePanel(target) { | ||
// get panel | ||
const activePanel = document.getElementById( | ||
target.getAttribute('aria-controls') | ||
); | ||
// Private variables | ||
this.toggles = null; | ||
this.forceClose = false; | ||
this.target = null; | ||
this.label = null; | ||
target.setAttribute('aria-expanded', 'false'); | ||
// toggle aria-hidden | ||
activePanel.setAttribute('aria-hidden', 'true'); | ||
// Bind `this` for use in callbacks | ||
this.handleClickOnToggle = this.handleClickOnToggle.bind(this); | ||
} | ||
function showPanel(target) { | ||
// get panel | ||
const activePanel = document.getElementById( | ||
target.getAttribute('aria-controls') | ||
); | ||
init() { | ||
this.toggles = queryAll(this.toggleSelector, this.element); | ||
// set attributes on header | ||
target.setAttribute('tabindex', 0); | ||
target.setAttribute('aria-expanded', 'true'); | ||
// Get label, if any | ||
this.label = queryOne(this.labelSelector, this.element); | ||
// toggle aria-hidden and set height on panel | ||
activePanel.setAttribute('aria-hidden', 'false'); | ||
// Bind click event on toggles | ||
if (this.attachClickListener && this.toggles) { | ||
this.toggles.forEach(toggle => { | ||
toggle.addEventListener( | ||
'click', | ||
this.handleClickOnToggle.bind(this, toggle) | ||
); | ||
}); | ||
} | ||
} | ||
function togglePanel(target) { | ||
// close target panel if already active | ||
if (target.getAttribute('aria-expanded') === 'true') { | ||
hidePanel(target); | ||
return; | ||
destroy() { | ||
if (this.attachClickListener && this.toggle) { | ||
this.toggles.forEach(toggle => { | ||
toggle.removeEventListener('click', this.handleClickOnToggle); | ||
}); | ||
} | ||
showPanel(target); | ||
} | ||
function giveHeaderFocus(headerSet, i) { | ||
// set active focus | ||
headerSet[i].focus(); | ||
} | ||
handleClickOnToggle(toggle) { | ||
// Get target element | ||
const target = queryOne(`#${toggle.getAttribute('aria-controls')}`); | ||
// EVENTS | ||
function eventHeaderClick(e) { | ||
togglePanel(e.currentTarget); | ||
} | ||
// Exit if no target found | ||
if (!target) { | ||
throw new TypeError( | ||
'Target has to be provided for accordion (aria-controls)' | ||
); | ||
} | ||
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); | ||
// Get current status | ||
const isExpanded = | ||
this.forceClose === true || | ||
toggle.getAttribute('aria-expanded') === 'true'; | ||
// 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; | ||
// Toggle the expandable/collapsible | ||
toggle.setAttribute('aria-expanded', !isExpanded); | ||
if (isExpanded) { | ||
target.setAttribute('hidden', true); | ||
} else { | ||
target.removeAttribute('hidden'); | ||
} | ||
} | ||
// 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); | ||
}); | ||
return this; | ||
} | ||
} | ||
// 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; | ||
export default Accordion; |
@@ -5,12 +5,14 @@ { | ||
"license": "EUPL-1.1", | ||
"version": "1.2.0", | ||
"description": "ECL Accordions", | ||
"version": "2.1.0", | ||
"description": "ECL EC Accordion", | ||
"main": "ec-component-accordion.js", | ||
"module": "ec-component-accordion.js", | ||
"style": "ec-component-accordion.scss", | ||
"sass": "ec-component-accordion.scss", | ||
"dependencies": { | ||
"@ecl/ec-base": "^1.0.0", | ||
"@ecl/ec-style-icon": "^1.2.0", | ||
"@ecl/ec-utility-colorize": "^1.0.0", | ||
"@ecl/generic-component-accordion": "^1.2.0" | ||
"@ecl/ec-base": "^2.1.0" | ||
}, | ||
"devDependencies": { | ||
"@ecl/ec-specs-accordion": "^2.1.0" | ||
}, | ||
"publishConfig": { | ||
@@ -32,4 +34,3 @@ "access": "public" | ||
], | ||
"main": "ec-component-accordion.js", | ||
"sass": "ec-component-accordion.scss" | ||
"gitHead": "6068cca85e387741556239826792ca27520fcf69" | ||
} |
@@ -1,36 +0,1 @@ | ||
# 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 | ||
* WAI-ARIA Authoring Practices | ||
* Latest Version (14 December 2016) | ||
* [https://www.w3.org/TR/2016/WD-wai-aria-practices-1.1-20161214/#accordion](https://www.w3.org/TR/2016/WD-wai-aria-practices-1.1-20161214/#accordion) | ||
* [https://www.w3.org/TR/2016/WD-wai-aria-practices-1.1-20161214/examples/accordion/accordion1.html](https://www.w3.org/TR/2016/WD-wai-aria-practices-1.1-20161214/examples/accordion/accordion1.html) | ||
* Current draft (as seen on 21 June 2017) | ||
* [https://w3c.github.io/aria-practices/#accordion](https://w3c.github.io/aria-practices/#accordion) | ||
* [https://w3c.github.io/aria-practices/examples/accordion/accordion.html](https://w3c.github.io/aria-practices/examples/accordion/accordion.html) | ||
* Existing libraries | ||
* [https://frend.co/components/accordion/](https://frend.co/components/accordion/) | ||
# Accordion |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
18201
1
1
5
72
2
1
+ Added@ecl/ec-base@2.39.0(transitive)
- Removed@ecl/ec-style-icon@^1.2.0
- Removed@ecl/ec-utility-colorize@^1.0.0
- Removed@ecl/ec-base@1.2.0(transitive)
- Removed@ecl/ec-style-icon@1.8.1(transitive)
- Removed@ecl/ec-utility-colorize@1.2.0(transitive)
- Removed@ecl/generic-base@1.2.0(transitive)
- Removed@ecl/generic-component-accordion@1.8.0(transitive)
- Removed@ecl/generic-style-icon@1.6.1(transitive)
- Removed@ecl/generic-utility-colorize@1.2.0(transitive)
Updated@ecl/ec-base@^2.1.0