diff --git a/lib/dragAndDropHandler.js b/lib/dragAndDropHandler.js index daa92fa1..e581d3ba 100644 --- a/lib/dragAndDropHandler.js +++ b/lib/dragAndDropHandler.js @@ -212,6 +212,9 @@ var DragAndDropHandler = /*#__PURE__*/function () { while (low < high) { var mid = low + high >> 1; var area = this.hitAreas[mid]; + if (!area) { + return null; + } if (y < area.top) { high = mid; } else if (y > area.bottom) { @@ -364,10 +367,16 @@ var VisibleNodeIterator = /*#__PURE__*/function () { if (mustIterateInside) { var childrenLength = node.children.length; node.children.forEach(function (_, i) { - if (i === childrenLength - 1) { - _iterateNode(node.children[i], null); - } else { - _iterateNode(node.children[i], node.children[i + 1]); + var child = node.children[i]; + if (child) { + if (i === childrenLength - 1) { + _iterateNode(child, null); + } else { + var nextChild = node.children[i + 1]; + if (nextChild) { + _iterateNode(child, nextChild); + } + } } }); if (node.is_open && $element) { @@ -526,12 +535,14 @@ var HitAreasGenerator = /*#__PURE__*/function (_VisibleNodeIterator) { var i = 0; while (i < positionCount) { var position = positionsInGroup[i]; - hitAreas.push({ - top: areaTop, - bottom: areaTop + areaHeight, - node: position.node, - position: position.position - }); + if (position) { + hitAreas.push({ + top: areaTop, + bottom: areaTop + areaHeight, + node: position.node, + position: position.position + }); + } areaTop += areaHeight; i += 1; } diff --git a/lib/elementsRenderer.js b/lib/elementsRenderer.js index 0e80ddf7..a45f05bc 100644 --- a/lib/elementsRenderer.js +++ b/lib/elementsRenderer.js @@ -39,7 +39,9 @@ var ElementsRenderer = /*#__PURE__*/function () { value: function renderFromRoot() { var $element = this.treeWidget.element; $element.empty(); - this.createDomElements($element[0], this.treeWidget.tree.children, true, 1); + if ($element[0]) { + this.createDomElements($element[0], this.treeWidget.tree.children, true, 1); + } } }, { key: "renderFromNode", @@ -125,6 +127,14 @@ var ElementsRenderer = /*#__PURE__*/function () { } return li; } + }, { + key: "setTreeItemAriaAttributes", + value: function setTreeItemAriaAttributes(li, node, level, isSelected) { + li.setAttribute("aria-label", node.name); + li.setAttribute("aria-level", "".concat(level)); + li.setAttribute("aria-selected", (0, _util.getBoolString)(isSelected)); + li.setAttribute("role", "treeitem"); + } }, { key: "createFolderLi", value: function createFolderLi(node, level, isSelected) { @@ -135,26 +145,28 @@ var ElementsRenderer = /*#__PURE__*/function () { // li var li = document.createElement("li"); li.className = "jqtree_common ".concat(folderClasses); - li.setAttribute("role", "presentation"); + this.setTreeItemAriaAttributes(li, node, level, isSelected); + li.setAttribute("aria-expanded", (0, _util.getBoolString)(node.is_open)); // div var div = document.createElement("div"); div.className = "jqtree-element jqtree_common"; - div.setAttribute("role", "presentation"); + div.setAttribute("role", "none"); li.appendChild(div); // button link var buttonLink = document.createElement("a"); buttonLink.className = buttonClasses; - buttonLink.appendChild(iconElement.cloneNode(true)); - buttonLink.setAttribute("role", "presentation"); buttonLink.setAttribute("aria-hidden", "true"); + if (iconElement) { + buttonLink.appendChild(iconElement.cloneNode(true)); + } if (this.treeWidget.options.buttonLeft) { div.appendChild(buttonLink); } // title span - div.appendChild(this.createTitleSpan(node.name, level, isSelected, node.is_open, true)); + div.appendChild(this.createTitleSpan(node.name, isSelected, true)); if (!this.treeWidget.options.buttonLeft) { div.appendChild(buttonLink); } @@ -172,21 +184,21 @@ var ElementsRenderer = /*#__PURE__*/function () { // li var li = document.createElement("li"); li.className = classString; - li.setAttribute("role", "presentation"); + this.setTreeItemAriaAttributes(li, node, level, isSelected); // div var div = document.createElement("div"); div.className = "jqtree-element jqtree_common"; - div.setAttribute("role", "presentation"); + div.setAttribute("role", "none"); li.appendChild(div); // title span - div.appendChild(this.createTitleSpan(node.name, level, isSelected, node.is_open, false)); + div.appendChild(this.createTitleSpan(node.name, isSelected, false)); return li; } }, { key: "createTitleSpan", - value: function createTitleSpan(nodeName, level, isSelected, isOpen, isFolder) { + value: function createTitleSpan(nodeName, isSelected, isFolder) { var titleSpan = document.createElement("span"); var classes = "jqtree-title jqtree_common"; if (isFolder) { @@ -194,10 +206,6 @@ var ElementsRenderer = /*#__PURE__*/function () { } classes += " jqtree-title-button-".concat(this.treeWidget.options.buttonLeft ? "left" : "right"); titleSpan.className = classes; - titleSpan.setAttribute("role", "treeitem"); - titleSpan.setAttribute("aria-level", "".concat(level)); - titleSpan.setAttribute("aria-selected", (0, _util.getBoolString)(isSelected)); - titleSpan.setAttribute("aria-expanded", (0, _util.getBoolString)(isOpen)); if (isSelected) { var tabIndex = this.treeWidget.options.tabIndex; if (tabIndex !== undefined) { diff --git a/lib/mouse.widget.js b/lib/mouse.widget.js index 88838de9..98ef968b 100644 --- a/lib/mouse.widget.js +++ b/lib/mouse.widget.js @@ -76,6 +76,9 @@ var MouseWidget = /*#__PURE__*/function (_SimpleWidget) { return; } var touch = e.changedTouches[0]; + if (!touch) { + return; + } _this.handleMouseDown(getPositionInfoFromTouch(touch, e)); }); _defineProperty(_assertThisInitialized(_this), "touchMove", function (e) { @@ -86,6 +89,9 @@ var MouseWidget = /*#__PURE__*/function (_SimpleWidget) { return; } var touch = e.changedTouches[0]; + if (!touch) { + return; + } _this.handleMouseMove(e, getPositionInfoFromTouch(touch, e)); }); _defineProperty(_assertThisInitialized(_this), "touchEnd", function (e) { @@ -96,6 +102,9 @@ var MouseWidget = /*#__PURE__*/function (_SimpleWidget) { return; } var touch = e.changedTouches[0]; + if (!touch) { + return; + } _this.handleMouseUp(getPositionInfoFromTouch(touch, e)); }); return _this; diff --git a/lib/node.js b/lib/node.js index 908b3f0c..99184920 100644 --- a/lib/node.js +++ b/lib/node.js @@ -513,7 +513,7 @@ var Node = /*#__PURE__*/function () { } else { var previousIndex = this.parent.getChildIndex(this) - 1; if (previousIndex >= 0) { - return this.parent.children[previousIndex]; + return this.parent.children[previousIndex] || null; } else { return null; } @@ -527,7 +527,7 @@ var Node = /*#__PURE__*/function () { } else { var nextIndex = this.parent.getChildIndex(this) + 1; if (nextIndex < this.parent.children.length) { - return this.parent.children[nextIndex]; + return this.parent.children[nextIndex] || null; } else { return null; } @@ -557,7 +557,7 @@ var Node = /*#__PURE__*/function () { value: function getNextNode() { var includeChildren = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; if (includeChildren && this.hasChildren()) { - return this.children[0]; + return this.children[0] || null; } else if (!this.parent) { return null; } else { @@ -574,7 +574,7 @@ var Node = /*#__PURE__*/function () { value: function getNextVisibleNode() { if (this.hasChildren() && this.is_open) { // First child - return this.children[0]; + return this.children[0] || null; } else { if (!this.parent) { return null; @@ -644,10 +644,13 @@ var Node = /*#__PURE__*/function () { return null; } else { var lastChild = this.children[this.children.length - 1]; + if (!lastChild) { + return null; + } if (!(lastChild.hasChildren() && lastChild.is_open)) { return lastChild; } else { - return lastChild.getLastChild(); + return lastChild === null || lastChild === void 0 ? void 0 : lastChild.getLastChild(); } } } diff --git a/lib/nodeElement.js b/lib/nodeElement.js index 57b306dd..48404c74 100644 --- a/lib/nodeElement.js +++ b/lib/nodeElement.js @@ -69,10 +69,10 @@ var NodeElement = /*#__PURE__*/function () { value: function deselect() { var $li = this.getLi(); $li.removeClass("jqtree-selected"); - $li.attr("aria-selected", "false"); + $li.attr("aria-selected", "true"); var $span = this.getSpan(); $span.removeAttr("tabindex"); - $span.blur(); + $span.trigger("blur"); } }, { key: "getUl", @@ -120,14 +120,16 @@ var FolderElement = /*#__PURE__*/function (_NodeElement) { $button.html(""); var buttonEl = $button.get(0); if (buttonEl) { - var icon = this.treeWidget.renderer.openedIconElement.cloneNode(true); - buttonEl.appendChild(icon); + var openedIconElement = this.treeWidget.renderer.openedIconElement; + if (openedIconElement) { + var icon = openedIconElement.cloneNode(true); + buttonEl.appendChild(icon); + } } var doOpen = function doOpen() { var $li = _this.getLi(); $li.removeClass("jqtree-closed"); - var $span = _this.getSpan(); - $span.attr("aria-expanded", "true"); + $li.attr("aria-expanded", "true"); if (onFinished) { onFinished(_this.node); } @@ -157,14 +159,16 @@ var FolderElement = /*#__PURE__*/function (_NodeElement) { $button.html(""); var buttonEl = $button.get(0); if (buttonEl) { - var icon = this.treeWidget.renderer.closedIconElement.cloneNode(true); - buttonEl.appendChild(icon); + var closedIconElement = this.treeWidget.renderer.closedIconElement; + if (closedIconElement) { + var icon = closedIconElement.cloneNode(true); + buttonEl.appendChild(icon); + } } var doClose = function doClose() { var $li = _this2.getLi(); $li.addClass("jqtree-closed"); - var $span = _this2.getSpan(); - $span.attr("aria-expanded", "false"); + $li.attr("aria-expanded", "false"); _this2.treeWidget._triggerEvent("tree.close", { node: _this2.node }); @@ -254,7 +258,8 @@ var GhostDropHint = /*#__PURE__*/function () { }, { key: "moveInsideOpenFolder", value: function moveInsideOpenFolder() { - var childElement = this.node.children[0].element; + var _this$node$children$; + var childElement = (_this$node$children$ = this.node.children[0]) === null || _this$node$children$ === void 0 ? void 0 : _this$node$children$.element; if (childElement) { jQuery(childElement).before(this.$ghost); } diff --git a/lib/saveStateHandler.js b/lib/saveStateHandler.js index b566e502..ab125f01 100644 --- a/lib/saveStateHandler.js +++ b/lib/saveStateHandler.js @@ -107,7 +107,7 @@ var SaveStateHandler = /*#__PURE__*/function () { value: function getNodeIdToBeSelected() { var state = this.getStateFromStorage(); if (state && state.selected_node) { - return state.selected_node[0]; + return state.selected_node[0] || null; } else { return null; } diff --git a/lib/scrollHandler.js b/lib/scrollHandler.js index f37bff7a..4b2edad1 100644 --- a/lib/scrollHandler.js +++ b/lib/scrollHandler.js @@ -37,7 +37,7 @@ var ScrollHandler = /*#__PURE__*/function () { key: "scrollToY", value: function scrollToY(top) { this.ensureInit(); - if (this.$scrollParent) { + if (this.$scrollParent && this.$scrollParent[0]) { this.$scrollParent[0].scrollTop = top; } else { var offset = this.treeWidget.$el.offset(); @@ -83,7 +83,8 @@ var ScrollHandler = /*#__PURE__*/function () { }, { key: "initScrollParent", value: function initScrollParent() { - var _this = this; + var _this = this, + _$scrollParent$; var getParentWithOverflow = function getParentWithOverflow() { var cssAttributes = ["overflow", "overflow-y"]; var hasOverFlow = function hasOverFlow($el) { @@ -124,7 +125,7 @@ var ScrollHandler = /*#__PURE__*/function () { setDocumentAsScrollParent(); } var $scrollParent = getParentWithOverflow(); - if ($scrollParent && $scrollParent.length && $scrollParent[0].tagName !== "HTML") { + if ($scrollParent && $scrollParent.length && ((_$scrollParent$ = $scrollParent[0]) === null || _$scrollParent$ === void 0 ? void 0 : _$scrollParent$.tagName) !== "HTML") { this.$scrollParent = $scrollParent; var offset = this.$scrollParent.offset(); this.scrollParentTop = offset ? offset.top : 0; @@ -210,6 +211,9 @@ var ScrollHandler = /*#__PURE__*/function () { return; } var scrollParent = $scrollParent[0]; + if (!scrollParent) { + return; + } var canScrollRight = scrollParent.scrollLeft + scrollParent.clientWidth < scrollParent.scrollWidth; var canScrollLeft = scrollParent.scrollLeft > 0; var rightEdge = scrollParentOffset.left + scrollParent.clientWidth; diff --git a/lib/selectNodeHandler.js b/lib/selectNodeHandler.js index 3a71e9b0..03cda631 100644 --- a/lib/selectNodeHandler.js +++ b/lib/selectNodeHandler.js @@ -26,7 +26,7 @@ var SelectNodeHandler = /*#__PURE__*/function () { value: function getSelectedNode() { var selectedNodes = this.getSelectedNodes(); if (selectedNodes.length) { - return selectedNodes[0]; + return selectedNodes[0] || false; } else { return false; } diff --git a/lib/test/jqTree/accessibility.test.js b/lib/test/jqTree/accessibility.test.js new file mode 100644 index 00000000..adf7d45d --- /dev/null +++ b/lib/test/jqTree/accessibility.test.js @@ -0,0 +1,41 @@ +"use strict"; + +function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } +var _jestAxe = require("jest-axe"); +require("../../tree.jquery"); +var _exampleData = _interopRequireDefault(require("../support/exampleData")); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } +function _regeneratorRuntime() { "use strict"; /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ _regeneratorRuntime = function _regeneratorRuntime() { return exports; }; var exports = {}, Op = Object.prototype, hasOwn = Op.hasOwnProperty, defineProperty = Object.defineProperty || function (obj, key, desc) { obj[key] = desc.value; }, $Symbol = "function" == typeof Symbol ? Symbol : {}, iteratorSymbol = $Symbol.iterator || "@@iterator", asyncIteratorSymbol = $Symbol.asyncIterator || "@@asyncIterator", toStringTagSymbol = $Symbol.toStringTag || "@@toStringTag"; function define(obj, key, value) { return Object.defineProperty(obj, key, { value: value, enumerable: !0, configurable: !0, writable: !0 }), obj[key]; } try { define({}, ""); } catch (err) { define = function define(obj, key, value) { return obj[key] = value; }; } function wrap(innerFn, outerFn, self, tryLocsList) { var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator, generator = Object.create(protoGenerator.prototype), context = new Context(tryLocsList || []); return defineProperty(generator, "_invoke", { value: makeInvokeMethod(innerFn, self, context) }), generator; } function tryCatch(fn, obj, arg) { try { return { type: "normal", arg: fn.call(obj, arg) }; } catch (err) { return { type: "throw", arg: err }; } } exports.wrap = wrap; var ContinueSentinel = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} var IteratorPrototype = {}; define(IteratorPrototype, iteratorSymbol, function () { return this; }); var getProto = Object.getPrototypeOf, NativeIteratorPrototype = getProto && getProto(getProto(values([]))); NativeIteratorPrototype && NativeIteratorPrototype !== Op && hasOwn.call(NativeIteratorPrototype, iteratorSymbol) && (IteratorPrototype = NativeIteratorPrototype); var Gp = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(IteratorPrototype); function defineIteratorMethods(prototype) { ["next", "throw", "return"].forEach(function (method) { define(prototype, method, function (arg) { return this._invoke(method, arg); }); }); } function AsyncIterator(generator, PromiseImpl) { function invoke(method, arg, resolve, reject) { var record = tryCatch(generator[method], generator, arg); if ("throw" !== record.type) { var result = record.arg, value = result.value; return value && "object" == _typeof(value) && hasOwn.call(value, "__await") ? PromiseImpl.resolve(value.__await).then(function (value) { invoke("next", value, resolve, reject); }, function (err) { invoke("throw", err, resolve, reject); }) : PromiseImpl.resolve(value).then(function (unwrapped) { result.value = unwrapped, resolve(result); }, function (error) { return invoke("throw", error, resolve, reject); }); } reject(record.arg); } var previousPromise; defineProperty(this, "_invoke", { value: function value(method, arg) { function callInvokeWithMethodAndArg() { return new PromiseImpl(function (resolve, reject) { invoke(method, arg, resolve, reject); }); } return previousPromise = previousPromise ? previousPromise.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); } }); } function makeInvokeMethod(innerFn, self, context) { var state = "suspendedStart"; return function (method, arg) { if ("executing" === state) throw new Error("Generator is already running"); if ("completed" === state) { if ("throw" === method) throw arg; return doneResult(); } for (context.method = method, context.arg = arg;;) { var delegate = context.delegate; if (delegate) { var delegateResult = maybeInvokeDelegate(delegate, context); if (delegateResult) { if (delegateResult === ContinueSentinel) continue; return delegateResult; } } if ("next" === context.method) context.sent = context._sent = context.arg;else if ("throw" === context.method) { if ("suspendedStart" === state) throw state = "completed", context.arg; context.dispatchException(context.arg); } else "return" === context.method && context.abrupt("return", context.arg); state = "executing"; var record = tryCatch(innerFn, self, context); if ("normal" === record.type) { if (state = context.done ? "completed" : "suspendedYield", record.arg === ContinueSentinel) continue; return { value: record.arg, done: context.done }; } "throw" === record.type && (state = "completed", context.method = "throw", context.arg = record.arg); } }; } function maybeInvokeDelegate(delegate, context) { var methodName = context.method, method = delegate.iterator[methodName]; if (undefined === method) return context.delegate = null, "throw" === methodName && delegate.iterator["return"] && (context.method = "return", context.arg = undefined, maybeInvokeDelegate(delegate, context), "throw" === context.method) || "return" !== methodName && (context.method = "throw", context.arg = new TypeError("The iterator does not provide a '" + methodName + "' method")), ContinueSentinel; var record = tryCatch(method, delegate.iterator, context.arg); if ("throw" === record.type) return context.method = "throw", context.arg = record.arg, context.delegate = null, ContinueSentinel; var info = record.arg; return info ? info.done ? (context[delegate.resultName] = info.value, context.next = delegate.nextLoc, "return" !== context.method && (context.method = "next", context.arg = undefined), context.delegate = null, ContinueSentinel) : info : (context.method = "throw", context.arg = new TypeError("iterator result is not an object"), context.delegate = null, ContinueSentinel); } function pushTryEntry(locs) { var entry = { tryLoc: locs[0] }; 1 in locs && (entry.catchLoc = locs[1]), 2 in locs && (entry.finallyLoc = locs[2], entry.afterLoc = locs[3]), this.tryEntries.push(entry); } function resetTryEntry(entry) { var record = entry.completion || {}; record.type = "normal", delete record.arg, entry.completion = record; } function Context(tryLocsList) { this.tryEntries = [{ tryLoc: "root" }], tryLocsList.forEach(pushTryEntry, this), this.reset(!0); } function values(iterable) { if (iterable) { var iteratorMethod = iterable[iteratorSymbol]; if (iteratorMethod) return iteratorMethod.call(iterable); if ("function" == typeof iterable.next) return iterable; if (!isNaN(iterable.length)) { var i = -1, next = function next() { for (; ++i < iterable.length;) if (hasOwn.call(iterable, i)) return next.value = iterable[i], next.done = !1, next; return next.value = undefined, next.done = !0, next; }; return next.next = next; } } return { next: doneResult }; } function doneResult() { return { value: undefined, done: !0 }; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, defineProperty(Gp, "constructor", { value: GeneratorFunctionPrototype, configurable: !0 }), defineProperty(GeneratorFunctionPrototype, "constructor", { value: GeneratorFunction, configurable: !0 }), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, toStringTagSymbol, "GeneratorFunction"), exports.isGeneratorFunction = function (genFun) { var ctor = "function" == typeof genFun && genFun.constructor; return !!ctor && (ctor === GeneratorFunction || "GeneratorFunction" === (ctor.displayName || ctor.name)); }, exports.mark = function (genFun) { return Object.setPrototypeOf ? Object.setPrototypeOf(genFun, GeneratorFunctionPrototype) : (genFun.__proto__ = GeneratorFunctionPrototype, define(genFun, toStringTagSymbol, "GeneratorFunction")), genFun.prototype = Object.create(Gp), genFun; }, exports.awrap = function (arg) { return { __await: arg }; }, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, asyncIteratorSymbol, function () { return this; }), exports.AsyncIterator = AsyncIterator, exports.async = function (innerFn, outerFn, self, tryLocsList, PromiseImpl) { void 0 === PromiseImpl && (PromiseImpl = Promise); var iter = new AsyncIterator(wrap(innerFn, outerFn, self, tryLocsList), PromiseImpl); return exports.isGeneratorFunction(outerFn) ? iter : iter.next().then(function (result) { return result.done ? result.value : iter.next(); }); }, defineIteratorMethods(Gp), define(Gp, toStringTagSymbol, "Generator"), define(Gp, iteratorSymbol, function () { return this; }), define(Gp, "toString", function () { return "[object Generator]"; }), exports.keys = function (val) { var object = Object(val), keys = []; for (var key in object) keys.push(key); return keys.reverse(), function next() { for (; keys.length;) { var key = keys.pop(); if (key in object) return next.value = key, next.done = !1, next; } return next.done = !0, next; }; }, exports.values = values, Context.prototype = { constructor: Context, reset: function reset(skipTempReset) { if (this.prev = 0, this.next = 0, this.sent = this._sent = undefined, this.done = !1, this.delegate = null, this.method = "next", this.arg = undefined, this.tryEntries.forEach(resetTryEntry), !skipTempReset) for (var name in this) "t" === name.charAt(0) && hasOwn.call(this, name) && !isNaN(+name.slice(1)) && (this[name] = undefined); }, stop: function stop() { this.done = !0; var rootRecord = this.tryEntries[0].completion; if ("throw" === rootRecord.type) throw rootRecord.arg; return this.rval; }, dispatchException: function dispatchException(exception) { if (this.done) throw exception; var context = this; function handle(loc, caught) { return record.type = "throw", record.arg = exception, context.next = loc, caught && (context.method = "next", context.arg = undefined), !!caught; } for (var i = this.tryEntries.length - 1; i >= 0; --i) { var entry = this.tryEntries[i], record = entry.completion; if ("root" === entry.tryLoc) return handle("end"); if (entry.tryLoc <= this.prev) { var hasCatch = hasOwn.call(entry, "catchLoc"), hasFinally = hasOwn.call(entry, "finallyLoc"); if (hasCatch && hasFinally) { if (this.prev < entry.catchLoc) return handle(entry.catchLoc, !0); if (this.prev < entry.finallyLoc) return handle(entry.finallyLoc); } else if (hasCatch) { if (this.prev < entry.catchLoc) return handle(entry.catchLoc, !0); } else { if (!hasFinally) throw new Error("try statement without catch or finally"); if (this.prev < entry.finallyLoc) return handle(entry.finallyLoc); } } } }, abrupt: function abrupt(type, arg) { for (var i = this.tryEntries.length - 1; i >= 0; --i) { var entry = this.tryEntries[i]; if (entry.tryLoc <= this.prev && hasOwn.call(entry, "finallyLoc") && this.prev < entry.finallyLoc) { var finallyEntry = entry; break; } } finallyEntry && ("break" === type || "continue" === type) && finallyEntry.tryLoc <= arg && arg <= finallyEntry.finallyLoc && (finallyEntry = null); var record = finallyEntry ? finallyEntry.completion : {}; return record.type = type, record.arg = arg, finallyEntry ? (this.method = "next", this.next = finallyEntry.finallyLoc, ContinueSentinel) : this.complete(record); }, complete: function complete(record, afterLoc) { if ("throw" === record.type) throw record.arg; return "break" === record.type || "continue" === record.type ? this.next = record.arg : "return" === record.type ? (this.rval = this.arg = record.arg, this.method = "return", this.next = "end") : "normal" === record.type && afterLoc && (this.next = afterLoc), ContinueSentinel; }, finish: function finish(finallyLoc) { for (var i = this.tryEntries.length - 1; i >= 0; --i) { var entry = this.tryEntries[i]; if (entry.finallyLoc === finallyLoc) return this.complete(entry.completion, entry.afterLoc), resetTryEntry(entry), ContinueSentinel; } }, "catch": function _catch(tryLoc) { for (var i = this.tryEntries.length - 1; i >= 0; --i) { var entry = this.tryEntries[i]; if (entry.tryLoc === tryLoc) { var record = entry.completion; if ("throw" === record.type) { var thrown = record.arg; resetTryEntry(entry); } return thrown; } } throw new Error("illegal catch attempt"); }, delegateYield: function delegateYield(iterable, resultName, nextLoc) { return this.delegate = { iterator: values(iterable), resultName: resultName, nextLoc: nextLoc }, "next" === this.method && (this.arg = undefined), ContinueSentinel; } }, exports; } +function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } +function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } +expect.extend(_jestAxe.toHaveNoViolations); +beforeEach(function () { + $("body").append('
'); +}); +afterEach(function () { + var $tree = $("#tree1"); + $tree.tree("destroy"); + $tree.remove(); +}); +it("has an accessible ui", /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee() { + var $tree, element; + return _regeneratorRuntime().wrap(function _callee$(_context) { + while (1) switch (_context.prev = _context.next) { + case 0: + $tree = $("#tree1"); + $tree.tree({ + data: _exampleData["default"] + }); + element = $tree.get()[0]; + _context.t0 = expect; + _context.next = 6; + return (0, _jestAxe.axe)(element); + case 6: + _context.t1 = _context.sent; + (0, _context.t0)(_context.t1).toHaveNoViolations(); + case 8: + case "end": + return _context.stop(); + } + }, _callee); +}))); \ No newline at end of file diff --git a/lib/test/node.test.js b/lib/test/node.test.js index 4d8c23bf..993885f1 100644 --- a/lib/test/node.test.js +++ b/lib/test/node.test.js @@ -151,7 +151,8 @@ describe("addChild", function () { })])); }); it("sets the parent of the child", function () { - expect(given.node.children[0].parent).toEqual(given.node); + var _given$node$children$; + expect((_given$node$children$ = given.node.children[0]) === null || _given$node$children$ === void 0 ? void 0 : _given$node$children$.parent).toEqual(given.node); }); }); describe("addChildAtPosition", function () { @@ -486,13 +487,15 @@ describe("getLastChild", function () { context("when its last child is open and has children", function () { beforeEach(function () { var child2 = given.node.children[1]; - child2.append("child2a"); - child2.append("child2b"); + child2 === null || child2 === void 0 ? void 0 : child2.append("child2a"); + child2 === null || child2 === void 0 ? void 0 : child2.append("child2b"); }); context("and the last child is open", function () { beforeEach(function () { var child2 = given.node.children[1]; - child2.is_open = true; + if (child2) { + child2.is_open = true; + } }); it("returns the last child of that child", function () { expect(given.node.getLastChild()).toMatchObject({ diff --git a/tree.jquery.debug.js b/tree.jquery.debug.js index d008475a..e9ad063c 100644 --- a/tree.jquery.debug.js +++ b/tree.jquery.debug.js @@ -790,7 +790,7 @@ var jqtree = (function (exports) { } else { var previousIndex = this.parent.getChildIndex(this) - 1; if (previousIndex >= 0) { - return this.parent.children[previousIndex]; + return this.parent.children[previousIndex] || null; } else { return null; } @@ -804,7 +804,7 @@ var jqtree = (function (exports) { } else { var nextIndex = this.parent.getChildIndex(this) + 1; if (nextIndex < this.parent.children.length) { - return this.parent.children[nextIndex]; + return this.parent.children[nextIndex] || null; } else { return null; } @@ -834,7 +834,7 @@ var jqtree = (function (exports) { value: function getNextNode() { var includeChildren = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; if (includeChildren && this.hasChildren()) { - return this.children[0]; + return this.children[0] || null; } else if (!this.parent) { return null; } else { @@ -851,7 +851,7 @@ var jqtree = (function (exports) { value: function getNextVisibleNode() { if (this.hasChildren() && this.is_open) { // First child - return this.children[0]; + return this.children[0] || null; } else { if (!this.parent) { return null; @@ -921,10 +921,13 @@ var jqtree = (function (exports) { return null; } else { var lastChild = this.children[this.children.length - 1]; + if (!lastChild) { + return null; + } if (!(lastChild.hasChildren() && lastChild.is_open)) { return lastChild; } else { - return lastChild.getLastChild(); + return lastChild === null || lastChild === void 0 ? void 0 : lastChild.getLastChild(); } } } @@ -1179,6 +1182,9 @@ var jqtree = (function (exports) { while (low < high) { var mid = low + high >> 1; var area = this.hitAreas[mid]; + if (!area) { + return null; + } if (y < area.top) { high = mid; } else if (y > area.bottom) { @@ -1330,10 +1336,16 @@ var jqtree = (function (exports) { if (mustIterateInside) { var childrenLength = node.children.length; node.children.forEach(function (_, i) { - if (i === childrenLength - 1) { - _iterateNode(node.children[i], null); - } else { - _iterateNode(node.children[i], node.children[i + 1]); + var child = node.children[i]; + if (child) { + if (i === childrenLength - 1) { + _iterateNode(child, null); + } else { + var nextChild = node.children[i + 1]; + if (nextChild) { + _iterateNode(child, nextChild); + } + } } }); if (node.is_open && $element) { @@ -1492,12 +1504,14 @@ var jqtree = (function (exports) { var i = 0; while (i < positionCount) { var position = positionsInGroup[i]; - hitAreas.push({ - top: areaTop, - bottom: areaTop + areaHeight, - node: position.node, - position: position.position - }); + if (position) { + hitAreas.push({ + top: areaTop, + bottom: areaTop + areaHeight, + node: position.node, + position: position.position + }); + } areaTop += areaHeight; i += 1; } @@ -1573,7 +1587,9 @@ var jqtree = (function (exports) { value: function renderFromRoot() { var $element = this.treeWidget.element; $element.empty(); - this.createDomElements($element[0], this.treeWidget.tree.children, true, 1); + if ($element[0]) { + this.createDomElements($element[0], this.treeWidget.tree.children, true, 1); + } } }, { key: "renderFromNode", @@ -1659,6 +1675,14 @@ var jqtree = (function (exports) { } return li; } + }, { + key: "setTreeItemAriaAttributes", + value: function setTreeItemAriaAttributes(li, node, level, isSelected) { + li.setAttribute("aria-label", node.name); + li.setAttribute("aria-level", "".concat(level)); + li.setAttribute("aria-selected", getBoolString(isSelected)); + li.setAttribute("role", "treeitem"); + } }, { key: "createFolderLi", value: function createFolderLi(node, level, isSelected) { @@ -1669,26 +1693,28 @@ var jqtree = (function (exports) { // li var li = document.createElement("li"); li.className = "jqtree_common ".concat(folderClasses); - li.setAttribute("role", "presentation"); + this.setTreeItemAriaAttributes(li, node, level, isSelected); + li.setAttribute("aria-expanded", getBoolString(node.is_open)); // div var div = document.createElement("div"); div.className = "jqtree-element jqtree_common"; - div.setAttribute("role", "presentation"); + div.setAttribute("role", "none"); li.appendChild(div); // button link var buttonLink = document.createElement("a"); buttonLink.className = buttonClasses; - buttonLink.appendChild(iconElement.cloneNode(true)); - buttonLink.setAttribute("role", "presentation"); buttonLink.setAttribute("aria-hidden", "true"); + if (iconElement) { + buttonLink.appendChild(iconElement.cloneNode(true)); + } if (this.treeWidget.options.buttonLeft) { div.appendChild(buttonLink); } // title span - div.appendChild(this.createTitleSpan(node.name, level, isSelected, node.is_open, true)); + div.appendChild(this.createTitleSpan(node.name, isSelected, true)); if (!this.treeWidget.options.buttonLeft) { div.appendChild(buttonLink); } @@ -1706,21 +1732,21 @@ var jqtree = (function (exports) { // li var li = document.createElement("li"); li.className = classString; - li.setAttribute("role", "presentation"); + this.setTreeItemAriaAttributes(li, node, level, isSelected); // div var div = document.createElement("div"); div.className = "jqtree-element jqtree_common"; - div.setAttribute("role", "presentation"); + div.setAttribute("role", "none"); li.appendChild(div); // title span - div.appendChild(this.createTitleSpan(node.name, level, isSelected, node.is_open, false)); + div.appendChild(this.createTitleSpan(node.name, isSelected, false)); return li; } }, { key: "createTitleSpan", - value: function createTitleSpan(nodeName, level, isSelected, isOpen, isFolder) { + value: function createTitleSpan(nodeName, isSelected, isFolder) { var titleSpan = document.createElement("span"); var classes = "jqtree-title jqtree_common"; if (isFolder) { @@ -1728,10 +1754,6 @@ var jqtree = (function (exports) { } classes += " jqtree-title-button-".concat(this.treeWidget.options.buttonLeft ? "left" : "right"); titleSpan.className = classes; - titleSpan.setAttribute("role", "treeitem"); - titleSpan.setAttribute("aria-level", "".concat(level)); - titleSpan.setAttribute("aria-selected", getBoolString(isSelected)); - titleSpan.setAttribute("aria-expanded", getBoolString(isOpen)); if (isSelected) { var tabIndex = this.treeWidget.options.tabIndex; if (tabIndex !== undefined) { @@ -2194,6 +2216,9 @@ var jqtree = (function (exports) { return; } var touch = e.changedTouches[0]; + if (!touch) { + return; + } _this.handleMouseDown(getPositionInfoFromTouch(touch, e)); }); _defineProperty(_assertThisInitialized(_this), "touchMove", function (e) { @@ -2204,6 +2229,9 @@ var jqtree = (function (exports) { return; } var touch = e.changedTouches[0]; + if (!touch) { + return; + } _this.handleMouseMove(e, getPositionInfoFromTouch(touch, e)); }); _defineProperty(_assertThisInitialized(_this), "touchEnd", function (e) { @@ -2214,6 +2242,9 @@ var jqtree = (function (exports) { return; } var touch = e.changedTouches[0]; + if (!touch) { + return; + } _this.handleMouseUp(getPositionInfoFromTouch(touch, e)); }); return _this; @@ -2454,7 +2485,7 @@ var jqtree = (function (exports) { value: function getNodeIdToBeSelected() { var state = this.getStateFromStorage(); if (state && state.selected_node) { - return state.selected_node[0]; + return state.selected_node[0] || null; } else { return null; } @@ -2646,7 +2677,7 @@ var jqtree = (function (exports) { key: "scrollToY", value: function scrollToY(top) { this.ensureInit(); - if (this.$scrollParent) { + if (this.$scrollParent && this.$scrollParent[0]) { this.$scrollParent[0].scrollTop = top; } else { var offset = this.treeWidget.$el.offset(); @@ -2692,7 +2723,8 @@ var jqtree = (function (exports) { }, { key: "initScrollParent", value: function initScrollParent() { - var _this = this; + var _this = this, + _$scrollParent$; var getParentWithOverflow = function getParentWithOverflow() { var cssAttributes = ["overflow", "overflow-y"]; var hasOverFlow = function hasOverFlow($el) { @@ -2733,7 +2765,7 @@ var jqtree = (function (exports) { setDocumentAsScrollParent(); } var $scrollParent = getParentWithOverflow(); - if ($scrollParent && $scrollParent.length && $scrollParent[0].tagName !== "HTML") { + if ($scrollParent && $scrollParent.length && ((_$scrollParent$ = $scrollParent[0]) === null || _$scrollParent$ === void 0 ? void 0 : _$scrollParent$.tagName) !== "HTML") { this.$scrollParent = $scrollParent; var offset = this.$scrollParent.offset(); this.scrollParentTop = offset ? offset.top : 0; @@ -2819,6 +2851,9 @@ var jqtree = (function (exports) { return; } var scrollParent = $scrollParent[0]; + if (!scrollParent) { + return; + } var canScrollRight = scrollParent.scrollLeft + scrollParent.clientWidth < scrollParent.scrollWidth; var canScrollLeft = scrollParent.scrollLeft > 0; var rightEdge = scrollParentOffset.left + scrollParent.clientWidth; @@ -2868,7 +2903,7 @@ var jqtree = (function (exports) { value: function getSelectedNode() { var selectedNodes = this.getSelectedNodes(); if (selectedNodes.length) { - return selectedNodes[0]; + return selectedNodes[0] || false; } else { return false; } @@ -3019,10 +3054,10 @@ var jqtree = (function (exports) { value: function deselect() { var $li = this.getLi(); $li.removeClass("jqtree-selected"); - $li.attr("aria-selected", "false"); + $li.attr("aria-selected", "true"); var $span = this.getSpan(); $span.removeAttr("tabindex"); - $span.blur(); + $span.trigger("blur"); } }, { key: "getUl", @@ -3069,14 +3104,16 @@ var jqtree = (function (exports) { $button.html(""); var buttonEl = $button.get(0); if (buttonEl) { - var icon = this.treeWidget.renderer.openedIconElement.cloneNode(true); - buttonEl.appendChild(icon); + var openedIconElement = this.treeWidget.renderer.openedIconElement; + if (openedIconElement) { + var icon = openedIconElement.cloneNode(true); + buttonEl.appendChild(icon); + } } var doOpen = function doOpen() { var $li = _this.getLi(); $li.removeClass("jqtree-closed"); - var $span = _this.getSpan(); - $span.attr("aria-expanded", "true"); + $li.attr("aria-expanded", "true"); if (onFinished) { onFinished(_this.node); } @@ -3106,14 +3143,16 @@ var jqtree = (function (exports) { $button.html(""); var buttonEl = $button.get(0); if (buttonEl) { - var icon = this.treeWidget.renderer.closedIconElement.cloneNode(true); - buttonEl.appendChild(icon); + var closedIconElement = this.treeWidget.renderer.closedIconElement; + if (closedIconElement) { + var icon = closedIconElement.cloneNode(true); + buttonEl.appendChild(icon); + } } var doClose = function doClose() { var $li = _this2.getLi(); $li.addClass("jqtree-closed"); - var $span = _this2.getSpan(); - $span.attr("aria-expanded", "false"); + $li.attr("aria-expanded", "false"); _this2.treeWidget._triggerEvent("tree.close", { node: _this2.node }); @@ -3201,7 +3240,8 @@ var jqtree = (function (exports) { }, { key: "moveInsideOpenFolder", value: function moveInsideOpenFolder() { - var childElement = this.node.children[0].element; + var _this$node$children$; + var childElement = (_this$node$children$ = this.node.children[0]) === null || _this$node$children$ === void 0 ? void 0 : _this$node$children$.element; if (childElement) { jQuery(childElement).before(this.$ghost); } diff --git a/tree.jquery.debug.js.map b/tree.jquery.debug.js.map index c2705f1b..6c496273 100644 --- a/tree.jquery.debug.js.map +++ b/tree.jquery.debug.js.map @@ -1 +1 @@ -{"version":3,"file":"tree.jquery.debug.js","sources":["src/version.ts","src/node.ts","src/dragAndDropHandler.ts","src/util.ts","src/elementsRenderer.ts","src/dataLoader.ts","src/keyHandler.ts","src/simple.widget.ts","src/mouse.widget.ts","src/saveStateHandler.ts","src/scrollHandler.ts","src/selectNodeHandler.ts","src/nodeElement.ts","src/tree.jquery.ts"],"sourcesContent":["const version = \"1.7.0\";\n\nexport default version;\n","interface NodeRecordWithChildren extends NodeRecord {\n children: NodeData[];\n}\n\nexport enum Position {\n Before = 1,\n After,\n Inside,\n None,\n}\n\nconst positionNames: Record