Skip to content
This repository was archived by the owner on Oct 1, 2024. It is now read-only.

Commit d5e49f5

Browse files
author
George Treviranus
committed
Merge branch 'develop'
2 parents adbd1fc + 7f91367 commit d5e49f5

35 files changed

+808
-379
lines changed

.browserslistrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ chrome >= 49
44
safari >= 10
55
android >= 4.4
66
ios >= 9
7-
explorer 11
7+
not ie <= 11
88
not op_mini all

dist/undernet.bundle.esm.js

Lines changed: 94 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*!
22
* @license MIT (https://github.com/geotrev/undernet/blob/master/LICENSE)
3-
* Undernet v3.3.6 (https://undernet.io)
3+
* Undernet v3.3.7 (https://undernet.io)
44
* Copyright 2017-2019 George Treviranus
55
*/
66
const KeyCodes = {
@@ -21,6 +21,19 @@ const Events = {
2121
CLICK: "click",
2222
};
2323

24+
/**
25+
* Creates a string of element selector patterns using common elements.
26+
* @param {String} container - The enclosing container's class, attribute, etc.
27+
* @return {String}
28+
*/
29+
const getFocusableElements = container => {
30+
const focusables = Selectors.FOCUSABLE_TAGS.map(
31+
element => `${container} ${element}${Selectors.NOT_VISUALLY_HIDDEN}`
32+
).join(", ");
33+
34+
return document.querySelectorAll(focusables)
35+
};
36+
2437
/**
2538
* Utility methods for DOM traversal and focus trapping.
2639
* @module Utils
@@ -50,7 +63,7 @@ class Utils {
5063
*/
5164
captureFocus(container, options) {
5265
this._focusContainerSelector = container;
53-
this._focusableChildren = this.getFocusableElements(this._focusContainerSelector);
66+
this._focusableChildren = getFocusableElements(this._focusContainerSelector);
5467
this._focusableFirstChild = this._focusableChildren[0];
5568
this._focusableLastChild = this._focusableChildren[this._focusableChildren.length - 1];
5669

@@ -94,30 +107,6 @@ class Utils {
94107
}
95108
}
96109

97-
/**
98-
* Because IE does not recognize NodeList.forEach(),
99-
* we use a cross-browser solution for returning an array of DOM nodes every time.
100-
* @param {String} element - A DOM node's class, attribute, etc., to search the document.
101-
* @return {Array}
102-
*/
103-
getElements(element) {
104-
const nodeList = document.querySelectorAll(element);
105-
return Array.apply(null, nodeList)
106-
}
107-
108-
/**
109-
* Creates a string of element selector patterns using common elements.
110-
* @param {String} container - The enclosing container's class, attribute, etc.
111-
* @return {String}
112-
*/
113-
getFocusableElements(container) {
114-
const focusables = Selectors.FOCUSABLE_TAGS.map(
115-
element => `${container} ${element}${Selectors.NOT_VISUALLY_HIDDEN}`
116-
).join(", ");
117-
118-
return this.getElements(focusables)
119-
}
120-
121110
// private
122111

123112
/**
@@ -267,6 +256,8 @@ class Modal extends Utils {
267256
constructor() {
268257
super();
269258

259+
this._iosMobile = /(iphone|ipod)/i.test(navigator.userAgent);
260+
270261
// events
271262
this._render = this._render.bind(this);
272263
this._handleClose = this._handleClose.bind(this);
@@ -297,10 +288,10 @@ class Modal extends Utils {
297288
* Begin listening to elements with [data-modal-button]
298289
*/
299290
start() {
300-
this._modals = this.getElements(this._modalContainerAttr);
301-
this._modalButtons = this.getElements(`[${Selectors$1.DATA_MODAL_BUTTON}]`);
291+
this._modals = document.querySelectorAll(this._modalContainerAttr);
292+
this._modalButtons = document.querySelectorAll(`[${Selectors$1.DATA_MODAL_BUTTON}]`);
302293

303-
this.getFocusableElements(this._modalContainerAttr).forEach(element => {
294+
getFocusableElements(this._modalContainerAttr).forEach(element => {
304295
element.setAttribute(Selectors$1.TABINDEX, "-1");
305296
});
306297

@@ -351,12 +342,12 @@ class Modal extends Utils {
351342

352343
this._activeModalSelector = `${this._activeModalOverlayAttr} ${this._modalContainerAttr}`;
353344
this._activeModal = document.querySelector(this._activeModalSelector);
354-
this._activeModalCloseButtons = this.getElements(
345+
this._activeModalCloseButtons = document.querySelectorAll(
355346
`${this._activeModalOverlayAttr} [${Selectors$1.DATA_CLOSE}]`
356347
);
357348

358349
// allow focusable elements to be focused
359-
this.getFocusableElements(this._activeModalSelector).forEach(element => {
350+
getFocusableElements(this._activeModalSelector).forEach(element => {
360351
element.setAttribute(Selectors$1.TABINDEX, "0");
361352
});
362353

@@ -373,6 +364,11 @@ class Modal extends Utils {
373364
// offset slight scroll caused by this._activeModal.focus()
374365
this._activeModalOverlay.scrollTop = 0;
375366

367+
// on ios devices, let the modal close on overlay click
368+
if (this._iosMobile) {
369+
this._activeModalOverlay.style.cursor = "pointer";
370+
}
371+
376372
// begin listening to events
377373
document.addEventListener(Events$1.KEYDOWN, this._handleEscapeKeyPress);
378374
document.addEventListener(Events$1.CLICK, this._handleOverlayClick);
@@ -415,10 +411,14 @@ class Modal extends Utils {
415411
this._activeModalOverlay.setAttribute(Selectors$1.ARIA_HIDDEN, "true");
416412
this._activeModal.removeAttribute(Selectors$1.TABINDEX);
417413

418-
this.getFocusableElements(this._activeModalSelector).forEach(element => {
414+
getFocusableElements(this._activeModalSelector).forEach(element => {
419415
element.setAttribute(Selectors$1.TABINDEX, "-1");
420416
});
421417

418+
if (this._iosMobile) {
419+
this._activeModalOverlay.style.cursor = "auto";
420+
}
421+
422422
// stop listening to events
423423
document.removeEventListener(Events$1.KEYDOWN, this._handleEscapeKeyPress);
424424
document.removeEventListener(Events$1.CLICK, this._handleOverlayClick);
@@ -549,7 +549,7 @@ class Accordion extends Utils {
549549
const accordionButtonSelector = this._getPossibleAccordionButtonAttrs(
550550
`[${Selectors$2.DATA_ACCORDION}]`
551551
);
552-
this._accordionButtons = this.getElements(accordionButtonSelector);
552+
this._accordionButtons = document.querySelectorAll(accordionButtonSelector);
553553

554554
if (this._accordionButtons.length) {
555555
this._accordionButtons.forEach(button => {
@@ -570,6 +570,42 @@ class Accordion extends Utils {
570570

571571
// private
572572

573+
/**
574+
* Open accordion content associated with an accordion button.
575+
* @param {Object} event - The event object
576+
*/
577+
_render(event) {
578+
event.preventDefault();
579+
580+
this._activeButton = event.target;
581+
this._activeAccordionRowId = this._activeButton.getAttribute(Selectors$2.DATA_TARGET);
582+
583+
this._activeRowAttr = this._getAccordionRowAttr(this._activeAccordionRowId);
584+
this._activeRow = document.querySelector(this._activeRowAttr);
585+
586+
if (!this._activeButton.getAttribute(Selectors$2.DATA_PARENT)) {
587+
return console.error(Messages$1.NO_PARENT_ERROR(this._activeAccordionRowId))
588+
}
589+
590+
this._activeContainerId = this._activeButton.getAttribute(Selectors$2.DATA_PARENT);
591+
this._activeContainerAttr = `[${Selectors$2.DATA_ACCORDION}='${this._activeContainerId}']`;
592+
593+
if (!document.querySelector(this._activeContainerAttr)) {
594+
return console.error(Messages$1.NO_ACCORDION_ERROR(this._activeContainerId))
595+
}
596+
597+
this._activeContainer = document.querySelector(this._activeContainerAttr);
598+
this._activeContent = document.getElementById(this._activeAccordionRowId);
599+
600+
const accordionButtonState = this._activeRow.getAttribute(Selectors$2.DATA_VISIBLE);
601+
602+
this._activeButtonExpandState = accordionButtonState === "true" ? "false" : "true";
603+
this._activeContentHiddenState = this._activeButtonExpandState === "false" ? "true" : "false";
604+
605+
this._closeAllIfToggleable();
606+
this._toggleSelectedAccordion();
607+
}
608+
573609
/**
574610
* Add initial attributes to accordion elements.
575611
* @param {Element} button - A button element that triggers an accordion.
@@ -590,13 +626,13 @@ class Accordion extends Utils {
590626

591627
const accordionRow = document.querySelector(accordionRowAttr);
592628
const buttonHeaderAttr = this._getPossibleAccordionHeaderAttrs(accordionRowAttr);
593-
const buttonHeader = this.getElements(buttonHeaderAttr)[0];
629+
const buttonHeader = document.querySelectorAll(buttonHeaderAttr)[0];
594630

595631
if (!buttonHeader || !buttonHeader.id) {
596632
console.error(Messages$1.NO_HEADER_ID_ERROR(buttonId));
597633
}
598634

599-
const buttonContentChildren = this.getFocusableElements(`#${buttonContent.id}`);
635+
const buttonContentChildren = getFocusableElements(`#${buttonContent.id}`);
600636

601637
button.setAttribute(Selectors$2.ARIA_CONTROLS, buttonId);
602638
buttonContent.setAttribute(Selectors$2.ARIA_LABELLEDBY, buttonHeader.id);
@@ -653,42 +689,6 @@ class Accordion extends Utils {
653689
return `[${Selectors$2.DATA_ACCORDION_ROW}='${id}']`
654690
}
655691

656-
/**
657-
* Open accordion content associated with an accordion button.
658-
* @param {Object} event - The event object
659-
*/
660-
_render(event) {
661-
event.preventDefault();
662-
663-
this._activeButton = event.target;
664-
this._activeAccordionRowId = this._activeButton.getAttribute(Selectors$2.DATA_TARGET);
665-
666-
this._activeRowAttr = this._getAccordionRowAttr(this._activeAccordionRowId);
667-
this._activeRow = document.querySelector(this._activeRowAttr);
668-
669-
if (!this._activeButton.getAttribute(Selectors$2.DATA_PARENT)) {
670-
return console.error(Messages$1.NO_PARENT_ERROR(this._activeAccordionRowId))
671-
}
672-
673-
this._activeContainerId = this._activeButton.getAttribute(Selectors$2.DATA_PARENT);
674-
this._activeContainerAttr = `[${Selectors$2.DATA_ACCORDION}='${this._activeContainerId}']`;
675-
676-
if (!document.querySelector(this._activeContainerAttr)) {
677-
return console.error(Messages$1.NO_ACCORDION_ERROR(this._activeContainerId))
678-
}
679-
680-
this._activeContainer = document.querySelector(this._activeContainerAttr);
681-
this._activeContent = document.getElementById(this._activeAccordionRowId);
682-
683-
const accordionButtonState = this._activeRow.getAttribute(Selectors$2.DATA_VISIBLE);
684-
685-
this._activeButtonExpandState = accordionButtonState === "true" ? "false" : "true";
686-
this._activeContentHiddenState = this._activeButtonExpandState === "false" ? "true" : "false";
687-
688-
this._closeAllIfToggleable();
689-
this._toggleSelectedAccordion();
690-
}
691-
692692
/**
693693
* If toggling multiple rows at once isn't enabled, close all rows except the selected one.
694694
* This ensures the selected one can be closed if it's already open.
@@ -697,17 +697,19 @@ class Accordion extends Utils {
697697
if (this._activeContainer.hasAttribute(Selectors$2.DATA_TOGGLE_MULTIPLE)) return
698698

699699
const allContentAttr = `${this._activeContainerAttr} [${Selectors$2.ARIA_HIDDEN}]`;
700-
const allRows = this.getElements(`${this._activeContainerAttr} [${Selectors$2.DATA_VISIBLE}]`);
701-
const allContent = this.getElements(allContentAttr);
700+
const allRows = document.querySelectorAll(
701+
`${this._activeContainerAttr} [${Selectors$2.DATA_VISIBLE}]`
702+
);
703+
const allContent = document.querySelectorAll(allContentAttr);
702704

703705
const accordionButtonSelector = this._getPossibleAccordionButtonAttrs(this._activeContainerAttr);
704-
const allButtons = this.getElements(accordionButtonSelector);
706+
const allButtons = document.querySelectorAll(accordionButtonSelector);
705707

706708
allContent.forEach(content => {
707709
if (content !== this._activeContent) content.style.maxHeight = null;
708710
});
709711

710-
this.getFocusableElements(allContentAttr).forEach(element => {
712+
getFocusableElements(allContentAttr).forEach(element => {
711713
element.setAttribute(Selectors$2.TABINDEX, "-1");
712714
});
713715

@@ -725,7 +727,7 @@ class Accordion extends Utils {
725727
this._activeContent.setAttribute(Selectors$2.ARIA_HIDDEN, this._activeContentHiddenState);
726728

727729
const activeContentBlock = `#${this._activeAccordionRowId}`;
728-
this.getFocusableElements(activeContentBlock).forEach(element => {
730+
getFocusableElements(activeContentBlock).forEach(element => {
729731
const value = this._activeButtonExpandState === "true" ? "0" : "-1";
730732
element.setAttribute(Selectors$2.TABINDEX, value);
731733
});
@@ -780,7 +782,6 @@ const Selectors$3 = {
780782
const Events$3 = {
781783
KEYDOWN: "keydown",
782784
CLICK: "click",
783-
TOUCHEND: "touchend",
784785
};
785786

786787
const Messages$2 = {
@@ -798,6 +799,8 @@ class Dropdown extends Utils {
798799
constructor() {
799800
super();
800801

802+
this._iosMobile = /(iphone|ipod)/i.test(navigator.userAgent);
803+
801804
// events
802805
this._render = this._render.bind(this);
803806
this._handleFirstTabClose = this._handleFirstTabClose.bind(this);
@@ -833,8 +836,8 @@ class Dropdown extends Utils {
833836
* Begin listening to dropdowns for events.
834837
*/
835838
start() {
836-
this._dropdowns = this.getElements(`${this._dropdownContainerAttr}`);
837-
this._dropdownButtons = this.getElements(
839+
this._dropdowns = document.querySelectorAll(`${this._dropdownContainerAttr}`);
840+
this._dropdownButtons = document.querySelectorAll(
838841
`${this._dropdownContainerAttr} > ${this._dropdownTargetAttr}`
839842
);
840843

@@ -907,7 +910,11 @@ class Dropdown extends Utils {
907910

908911
document.addEventListener(Events$3.KEYDOWN, this._handleEscapeKeyPress);
909912
document.addEventListener(Events$3.CLICK, this._handleOffMenuClick);
910-
document.addEventListener(Events$3.TOUCHEND, this._handleOffMenuClick);
913+
914+
// make click events work on mobile iOS
915+
if (this._iosMobile) {
916+
document.body.style.cursor = "pointer";
917+
}
911918

912919
this._activeDropdownLinks = this._getDropdownLinks(this._activeDropdownAttr);
913920

@@ -988,8 +995,12 @@ class Dropdown extends Utils {
988995
this._activeDropdownButton.addEventListener(Events$3.CLICK, this._render);
989996

990997
document.removeEventListener(Events$3.KEYDOWN, this._handleEscapeKeyPress);
998+
999+
if (this._iosMobile) {
1000+
document.body.style.cursor = "auto";
1001+
}
1002+
9911003
document.removeEventListener(Events$3.CLICK, this._handleOffMenuClick);
992-
document.removeEventListener(Events$3.TOUCHEND, this._handleOffMenuClick);
9931004

9941005
if (this._allowFocusReturn) {
9951006
this._handleReturnFocus();
@@ -1033,7 +1044,7 @@ class Dropdown extends Utils {
10331044
* @return {String} - Selector for possible menu item links.
10341045
*/
10351046
_getDropdownLinks(attr) {
1036-
return this.getElements(`${attr} > ul > li > a, ${attr} > ul > li > button`)
1047+
return document.querySelectorAll(`${attr} > ul > li > a, ${attr} > ul > li > button`)
10371048
}
10381049

10391050
/**
@@ -1057,7 +1068,7 @@ class Dropdown extends Utils {
10571068
dropdownButton.setAttribute(Selectors$3.ARIA_EXPANDED, "false");
10581069
dropdownMenu.setAttribute(Selectors$3.ARIA_LABELLEDBY, dropdownButton.id);
10591070

1060-
const dropdownMenuItems = this.getElements(dropdownMenuItemsAttr);
1071+
const dropdownMenuItems = document.querySelectorAll(dropdownMenuItemsAttr);
10611072
dropdownMenuItems.forEach(item => item.setAttribute(Selectors$3.ROLE, "none"));
10621073

10631074
this._getDropdownLinks(dropdownIdAttr).forEach(link => {

0 commit comments

Comments
 (0)