diff --git a/src/web/HTMLOperation.mjs b/src/web/HTMLOperation.mjs
index 30cfd1d9..725f0b5f 100755
--- a/src/web/HTMLOperation.mjs
+++ b/src/web/HTMLOperation.mjs
@@ -43,17 +43,23 @@ class HTMLOperation {
/**
* Renders the operation in HTML as a stub operation with no ingredients.
*
+ * @param {boolean} removeIcon - show icon for removing operation
+ * @param {string} elementId - element ID for aria usage
* @returns {string}
*/
- toStubHtml(removeIcon) {
+ toStubHtml(removeIcon = false, elementId = null) {
let html = "
${titleFromWikiLink(this.infoURL)}` : "";
html += ` data-container='body' data-toggle='popover' data-placement='right'
data-content="${this.description}${infoLink}" data-html='true' data-trigger='hover'
- data-boundary='viewport'`;
+ data-boundary='viewport' role='button'`;
}
html += ">" + this.name;
diff --git a/src/web/html/index.html b/src/web/html/index.html
index 38bf7ccc..a81b6c28 100755
--- a/src/web/html/index.html
+++ b/src/web/html/index.html
@@ -173,7 +173,7 @@
Operations
-
+
diff --git a/src/web/waiters/OperationsWaiter.mjs b/src/web/waiters/OperationsWaiter.mjs
index 45a40c82..6c2de064 100755
--- a/src/web/waiters/OperationsWaiter.mjs
+++ b/src/web/waiters/OperationsWaiter.mjs
@@ -28,17 +28,16 @@ class OperationsWaiter {
this.removeIntent = false;
}
-
/**
* Handler for search events.
* Finds operations which match the given search term and displays them under the search box.
*
- * @param {event} e
+ * @param {KeyboardEvent | ClipboardEvent | Event} e
*/
searchOperations(e) {
let ops, selected;
- if (e.type === "search" || e.keyCode === 13) { // Search or Return
+ if ((e.type === "search" && e.target.value !== "") || e.keyCode === 13) { // Search (non-empty) or Return
e.preventDefault();
ops = document.querySelectorAll("#search-results li");
if (ops.length) {
@@ -49,27 +48,43 @@ class OperationsWaiter {
}
}
+ /**
+ * Sets up the operation element with the correct attributes when selected
+ * @param {HTMLElement} element
+ */
+ const _selectOperation = (element) => {
+ element.classList.add("selected-op");
+ element.scrollIntoView({block: "nearest"});
+ $(element).popover("show");
+ e.target.setAttribute("aria-activedescendant", element.id);
+ };
+
+ /**
+ * Sets up the operation element with the correct attributes when deselected
+ * @param {HTMLElement} element
+ */
+ const _deselectOperation = (element) => {
+ element.classList.remove("selected-op");
+ $(element).popover("hide");
+ };
+
if (e.keyCode === 40) { // Down
e.preventDefault();
ops = document.querySelectorAll("#search-results li");
if (ops.length) {
selected = this.getSelectedOp(ops);
- if (selected > -1) {
- ops[selected].classList.remove("selected-op");
- }
+ if (selected > -1) _deselectOperation(ops[selected]);
if (selected === ops.length-1) selected = -1;
- ops[selected+1].classList.add("selected-op");
+ _selectOperation(ops[selected+1]);
}
} else if (e.keyCode === 38) { // Up
e.preventDefault();
ops = document.querySelectorAll("#search-results li");
if (ops.length) {
selected = this.getSelectedOp(ops);
- if (selected > -1) {
- ops[selected].classList.remove("selected-op");
- }
+ if (selected > -1) _deselectOperation(ops[selected]);
if (selected === 0) selected = ops.length;
- ops[selected-1].classList.add("selected-op");
+ _selectOperation(ops[selected-1]);
}
} else {
const searchResultsEl = document.getElementById("search-results");
@@ -83,11 +98,13 @@ class OperationsWaiter {
searchResultsEl.removeChild(searchResultsEl.firstChild);
}
+ document.querySelector("#search").removeAttribute("aria-activedescendant");
+
$("#categories .show").collapse("hide");
if (str) {
const matchedOps = this.filterOperations(str, true);
const matchedOpsHtml = matchedOps
- .map(v => v.toStubHtml())
+ .map((operation, idx) => operation.toStubHtml(false, `search-result-${idx}`))
.join("");
searchResultsEl.innerHTML = matchedOpsHtml;
@@ -103,7 +120,7 @@ class OperationsWaiter {
* @param {string} searchStr
* @param {boolean} highlight - Whether or not to highlight the matching string in the operation
* name and description
- * @returns {string[]}
+ * @returns {HTMLOperation[]}
*/
filterOperations(inStr, highlight) {
const matchedOps = [];