From eb04145a62a76c7d1ae4691e9668dc15632e87ea Mon Sep 17 00:00:00 2001 From: John L Date: Fri, 27 May 2022 17:06:29 +0100 Subject: [PATCH 1/7] ccessible user experience --- package.json | 2 +- src/web/HTMLOperation.mjs | 11 +++++++- src/web/Manager.mjs | 6 ++++ src/web/html/index.html | 7 +++++ src/web/index.js | 3 +- src/web/waiters/OperationsWaiter.mjs | 15 +++++++++- src/web/waiters/OptionsWaiter.mjs | 14 ++++++++++ src/web/waiters/RecipeWaiter.mjs | 41 ++++++++++++++++++++++++++-- tests/browser/nightwatch.js | 34 +++++++++++++++++++++++ 9 files changed, 127 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 02be5cbc4f..d99e4e5474 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "autoprefixer": "^10.4.4", "babel-loader": "^8.2.4", "babel-plugin-dynamic-import-node": "^2.3.3", - "chromedriver": "^99.0.0", + "chromedriver": "^101.0.0", "cli-progress": "^3.10.0", "colors": "^1.4.0", "copy-webpack-plugin": "^10.2.4", diff --git a/src/web/HTMLOperation.mjs b/src/web/HTMLOperation.mjs index 04e9b3ccad..67c077889a 100755 --- a/src/web/HTMLOperation.mjs +++ b/src/web/HTMLOperation.mjs @@ -46,7 +46,7 @@ class HTMLOperation { * @returns {string} */ toStubHtml(removeIcon) { - let html = "
  • ${titleFromWikiLink(this.infoURL)}` : ""; @@ -58,6 +58,12 @@ class HTMLOperation { html += ">" + this.name; + html += ` + + `; + if (removeIcon) { html += "delete"; } @@ -83,6 +89,9 @@ class HTMLOperation { html += `
    + arrow_downward + arrow_upward + delete pause not_interested
    diff --git a/src/web/Manager.mjs b/src/web/Manager.mjs index e1e07dfda2..779546c1bf 100755 --- a/src/web/Manager.mjs +++ b/src/web/Manager.mjs @@ -93,6 +93,7 @@ class Manager { this.bindings.updateKeybList(); this.background.registerChefWorker(); this.seasonal.load(); + this.options.load(); } @@ -126,6 +127,7 @@ class Manager { // Operations this.addMultiEventListener("#search", "keyup paste search", this.ops.searchOperations, this.ops); this.addDynamicListener(".op-list li.operation", "dblclick", this.ops.operationDblclick, this.ops); + this.addDynamicListener(".op-list li.operation button i", "click", this.ops.operationAdd, this.ops); document.getElementById("edit-favourites").addEventListener("click", this.ops.editFavouritesClick.bind(this.ops)); document.getElementById("save-favourites").addEventListener("click", this.ops.saveFavouritesClick.bind(this.ops)); document.getElementById("reset-favourites").addEventListener("click", this.ops.resetFavouritesClick.bind(this.ops)); @@ -137,6 +139,9 @@ class Manager { this.addDynamicListener(".arg[type=checkbox], .arg[type=radio], select.arg", "change", this.recipe.ingChange, this.recipe); this.addDynamicListener(".disable-icon", "click", this.recipe.disableClick, this.recipe); this.addDynamicListener(".breakpoint", "click", this.recipe.breakpointClick, this.recipe); + this.addDynamicListener(".remove-icon", "click", this.recipe.removeClick, this.recipe); + this.addDynamicListener(".move-down", "click", this.recipe.moveDownClick, this.recipe); + this.addDynamicListener(".move-up", "click", this.recipe.moveUpClick, this.recipe); this.addDynamicListener("#rec-list li.operation", "dblclick", this.recipe.operationDblclick, this.recipe); this.addDynamicListener("#rec-list li.operation > div", "dblclick", this.recipe.operationChildDblclick, this.recipe); this.addDynamicListener("#rec-list .dropdown-menu.toggle-dropdown a", "click", this.recipe.dropdownToggleClick, this.recipe); @@ -233,6 +238,7 @@ class Manager { document.getElementById("theme").addEventListener("change", this.options.themeChange.bind(this.options)); document.getElementById("logLevel").addEventListener("change", this.options.logLevelChange.bind(this.options)); document.getElementById("imagePreview").addEventListener("change", this.input.renderFileThumb.bind(this.input)); + document.getElementById("accessibleUX").addEventListener("change", this.options.uxChange.bind(this.options)); // Misc window.addEventListener("keydown", this.bindings.parseInput.bind(this.bindings)); diff --git a/src/web/html/index.html b/src/web/html/index.html index 8a7adac7cf..59a6b9b4ab 100755 --- a/src/web/html/index.html +++ b/src/web/html/index.html @@ -584,6 +584,13 @@ Keep the current tab in sync between the input and output + +
    + +
  • data + this.manager.recipe.addOperation($(li).data("opname")); + } - this.manager.recipe.addOperation(li.textContent); + /** + * Handler for operation add events. + * Adds the operation to the recipe and auto bakes. + * + * @param {event} e + */ + operationAdd(e) { + log.info("add"); + const li = e.target.parentNode.parentNode.parentNode; + // get operation name from
  • data + this.manager.recipe.addOperation($(li).data("opname")); } diff --git a/src/web/waiters/OptionsWaiter.mjs b/src/web/waiters/OptionsWaiter.mjs index 5ef517d4ad..eb46e7674f 100755 --- a/src/web/waiters/OptionsWaiter.mjs +++ b/src/web/waiters/OptionsWaiter.mjs @@ -36,6 +36,8 @@ class OptionsWaiter { for (i = 0; i < cboxes.length; i++) { cboxes[i].checked = this.app.options[cboxes[i].getAttribute("option")]; } + // init ux option - ux is last cbox + cboxes[i-1].dispatchEvent(new Event("change")); const nboxes = document.querySelectorAll("#options-body input[type=number]"); for (i = 0; i < nboxes.length; i++) { @@ -189,6 +191,18 @@ class OptionsWaiter { this.manager.worker.setLogLevel(); this.manager.input.setLogLevel(); } + + + /** + * Changes the UX using CSS change so that actions can be managed + * regardless of visibility + * + * @param {Event} e + */ + uxChange(e) { + const checked = $("#accessibleUX").is(":checked"); + $(".accessibleUX").css("display", checked ? "block" : "none"); + } } export default OptionsWaiter; diff --git a/src/web/waiters/RecipeWaiter.mjs b/src/web/waiters/RecipeWaiter.mjs index f4107e66b1..b15e3a03e2 100755 --- a/src/web/waiters/RecipeWaiter.mjs +++ b/src/web/waiters/RecipeWaiter.mjs @@ -261,6 +261,43 @@ class RecipeWaiter { window.dispatchEvent(this.manager.statechange); } + /** + * Handler for remove click events. + * Removes the operation from the recipe and auto bakes. + * + * @fires Manager#statechange + * @param {event} e + */ + removeClick(e) { + e.target.parentNode.parentNode.remove(); + this.opRemove(e); + } + + /** + * Handler for remove click events. + * Removes the operation from the recipe and auto bakes. + * + * @fires Manager#statechange + * @param {event} e + */ + moveUpClick(e) { + const li = $(e.target.parentNode.parentNode); + li.prev().before(li); + window.dispatchEvent(this.manager.statechange); + } + + /** + * Handler for down click events. + * Moves the operation in the recipe and auto bakes. + * + * @fires Manager#statechange + * @param {event} e + */ + moveDownClick(e) { + const li = $(e.target.parentNode.parentNode); + li.next().after(li); + window.dispatchEvent(this.manager.statechange); + } /** * Handler for operation doubleclick events. @@ -368,7 +405,7 @@ class RecipeWaiter { * @param {element} el - The operation stub element from the operations pane */ buildRecipeOperation(el) { - const opName = el.textContent; + const opName = $(el).data('opname'); const op = new HTMLOperation(opName, this.app.operations[opName], this.app, this.manager); el.innerHTML = op.toFullHtml(); @@ -395,7 +432,7 @@ class RecipeWaiter { const item = document.createElement("li"); item.classList.add("operation"); - item.innerHTML = name; + $(item).data("opname", name); this.buildRecipeOperation(item); document.getElementById("rec-list").appendChild(item); diff --git a/tests/browser/nightwatch.js b/tests/browser/nightwatch.js index 41aff9b29d..2373354a4e 100644 --- a/tests/browser/nightwatch.js +++ b/tests/browser/nightwatch.js @@ -56,6 +56,40 @@ module.exports = { browser.expect.element("//li[contains(@class, 'operation') and text()='Register']").to.be.present; }, + "Accessible user experience": browser => { + const addOp = "#catFavourites li.operation"; + const recOp = "#rec-list li:nth-child(1)"; + const down = " i.move-down.accessibleUX"; + const remove = " i.remove-icon.accessibleUX"; + + // Switch UX on + browser + .useCss() + .click("#options") + .waitForElementVisible("#options-modal", 1000) + .click("#reset-options") + .pause(500) + // Using label for checkbox click because nightwatch thinks #acessibleUX isn't visible + .click('label[for="accessibleUX"]') + .click("#options-modal .modal-footer button:last-child") + .waitForElementNotVisible("#options-modal") + .expect.element(addOp).to.be.visible; + + // add Operations & move them + browser + .useCss() + .click(addOp + ":nth-child(1) button") + .click(addOp + ":nth-child(2) button") + .click(recOp + down) + .expect.element(recOp).text.to.contain("From Base64"); + + // delete operations + browser + .click(recOp + remove) + .click(recOp + remove) + .waitForElementNotPresent(recOp); + }, + "Recipe can be run": browser => { const toHex = "//li[contains(@class, 'operation') and text()='To Hex']"; const op = "#rec-list .operation .op-title"; From 9b34626df96a6a5f3906043fe5c59c59fb09bd2c Mon Sep 17 00:00:00 2001 From: John L Date: Fri, 27 May 2022 17:22:04 +0100 Subject: [PATCH 2/7] lint wants double quotes --- src/web/waiters/RecipeWaiter.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web/waiters/RecipeWaiter.mjs b/src/web/waiters/RecipeWaiter.mjs index b15e3a03e2..bf9c757f88 100755 --- a/src/web/waiters/RecipeWaiter.mjs +++ b/src/web/waiters/RecipeWaiter.mjs @@ -405,7 +405,7 @@ class RecipeWaiter { * @param {element} el - The operation stub element from the operations pane */ buildRecipeOperation(el) { - const opName = $(el).data('opname'); + const opName = $(el).data("opname"); const op = new HTMLOperation(opName, this.app.operations[opName], this.app, this.manager); el.innerHTML = op.toFullHtml(); From f60f80be0b652ab5e22ecf4e241149bf970e87ae Mon Sep 17 00:00:00 2001 From: John L Date: Mon, 6 Jun 2022 18:04:18 +0100 Subject: [PATCH 3/7] Search testing too --- src/web/HTMLOperation.mjs | 28 +++++++++++++++------------- src/web/waiters/OperationsWaiter.mjs | 3 +-- tests/browser/nightwatch.js | 25 +++++++++++++------------ 3 files changed, 29 insertions(+), 27 deletions(-) diff --git a/src/web/HTMLOperation.mjs b/src/web/HTMLOperation.mjs index 67c077889a..6576307911 100755 --- a/src/web/HTMLOperation.mjs +++ b/src/web/HTMLOperation.mjs @@ -27,6 +27,7 @@ class HTMLOperation { this.manager = manager; this.name = name; + this.title = name; this.description = config.description; this.infoURL = config.infoURL; this.manualBake = config.manualBake || false; @@ -46,7 +47,7 @@ class HTMLOperation { * @returns {string} */ toStubHtml(removeIcon) { - let html = `
  • ${titleFromWikiLink(this.infoURL)}` : ""; @@ -56,24 +57,25 @@ class HTMLOperation { data-boundary='viewport'`; } - html += ">" + this.name; + html += ">" + this.title; - html += ` + // Ensure add button only appears in sidebar, not fav edit + if (removeIcon) { + // Remove button + html += "delete"; + } else { + // Add buttob + html += ` `; - - if (removeIcon) { - html += "delete"; } html += "
  • "; return html; } - - /** * Renders the operation in HTML as a full operation with ingredients. * @@ -109,18 +111,18 @@ class HTMLOperation { */ highlightSearchStrings(nameIdxs, descIdxs) { if (nameIdxs.length && typeof nameIdxs[0][0] === "number") { - let opName = "", + let title = "", pos = 0; nameIdxs.forEach(idxs => { const [start, length] = idxs; if (typeof start !== "number") return; - opName += this.name.slice(pos, start) + "" + - this.name.slice(start, start + length) + ""; + title += this.title.slice(pos, start) + "" + + this.title.slice(start, start + length) + ""; pos = start + length; }); - opName += this.name.slice(pos, this.name.length); - this.name = opName; + title += this.title.slice(pos, this.title.length); + this.title = title; } if (this.description && descIdxs.length && descIdxs[0][0] >= 0) { diff --git a/src/web/waiters/OperationsWaiter.mjs b/src/web/waiters/OperationsWaiter.mjs index f37ecfef22..afb69d85bf 100755 --- a/src/web/waiters/OperationsWaiter.mjs +++ b/src/web/waiters/OperationsWaiter.mjs @@ -44,7 +44,7 @@ class OperationsWaiter { if (ops.length) { selected = this.getSelectedOp(ops); if (selected > -1) { - this.manager.recipe.addOperation(ops[selected].innerHTML); + this.manager.recipe.addOperation($(ops[selected]).data("opname")); } } } @@ -216,7 +216,6 @@ class OperationsWaiter { * @param {event} e */ operationAdd(e) { - log.info("add"); const li = e.target.parentNode.parentNode.parentNode; // get operation name from
  • data this.manager.recipe.addOperation($(li).data("opname")); diff --git a/tests/browser/nightwatch.js b/tests/browser/nightwatch.js index 2373354a4e..5684944224 100644 --- a/tests/browser/nightwatch.js +++ b/tests/browser/nightwatch.js @@ -59,8 +59,10 @@ module.exports = { "Accessible user experience": browser => { const addOp = "#catFavourites li.operation"; const recOp = "#rec-list li:nth-child(1)"; + const searchOp = "#search-results"; const down = " i.move-down.accessibleUX"; const remove = " i.remove-icon.accessibleUX"; + const favCat = "#categories div:nth-child(1)" // Switch UX on browser @@ -68,8 +70,7 @@ module.exports = { .click("#options") .waitForElementVisible("#options-modal", 1000) .click("#reset-options") - .pause(500) - // Using label for checkbox click because nightwatch thinks #acessibleUX isn't visible + // Using label for checkbox click because nightwatch thinks #accessibleUX isn't visible .click('label[for="accessibleUX"]') .click("#options-modal .modal-footer button:last-child") .waitForElementNotVisible("#options-modal") @@ -88,6 +89,16 @@ module.exports = { .click(recOp + remove) .click(recOp + remove) .waitForElementNotPresent(recOp); + + // Search for an op + browser + .useCss() + .clearValue("#search") + .setValue("#search", "md5") + .click(searchOp + " button") + .click(recOp + remove) + .click(favCat) + .expect.element(searchOp).text.to.contain("MD5"); }, "Recipe can be run": browser => { @@ -245,16 +256,6 @@ module.exports = { browser.click("#clr-recipe"); }, - "Search": browser => { - // Search for an op - browser - .useCss() - .clearValue("#search") - .setValue("#search", "md5") - .useXpath() - .waitForElementVisible("//ul[@id='search-results']//b[text()='MD5']", 1000); - }, - after: browser => { browser.end(); } From 486c139afa34e9a8702711db357097dafbd13ba5 Mon Sep 17 00:00:00 2001 From: John L Date: Sat, 11 Jun 2022 16:19:27 +0100 Subject: [PATCH 4/7] lint fix --- tests/browser/nightwatch.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/browser/nightwatch.js b/tests/browser/nightwatch.js index 5684944224..c3328ac9cb 100644 --- a/tests/browser/nightwatch.js +++ b/tests/browser/nightwatch.js @@ -62,7 +62,7 @@ module.exports = { const searchOp = "#search-results"; const down = " i.move-down.accessibleUX"; const remove = " i.remove-icon.accessibleUX"; - const favCat = "#categories div:nth-child(1)" + const favCat = "#categories div:nth-child(1)"; // Switch UX on browser From bc0b782fac08e11f211d1d8e0cb1e69d345e2aba Mon Sep 17 00:00:00 2001 From: john19696 Date: Fri, 5 Aug 2022 16:40:36 +0100 Subject: [PATCH 5/7] vertically centre add_box --- src/web/stylesheets/components/_button.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/web/stylesheets/components/_button.css b/src/web/stylesheets/components/_button.css index 54dfc339cb..8638bbb21f 100755 --- a/src/web/stylesheets/components/_button.css +++ b/src/web/stylesheets/components/_button.css @@ -11,3 +11,7 @@ span.btn img { margin-right: 3px; margin-bottom: 1px; } + +button.btn.accessibleUX { + margin-top: -5px; +} \ No newline at end of file From 27c5b7c5924500a9ea6181e830f71d3923157d3f Mon Sep 17 00:00:00 2001 From: john19696 Date: Thu, 25 Aug 2022 12:40:55 +0100 Subject: [PATCH 6/7] fix option --- src/web/waiters/OptionsWaiter.mjs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/web/waiters/OptionsWaiter.mjs b/src/web/waiters/OptionsWaiter.mjs index eb46e7674f..731945196e 100755 --- a/src/web/waiters/OptionsWaiter.mjs +++ b/src/web/waiters/OptionsWaiter.mjs @@ -201,7 +201,8 @@ class OptionsWaiter { */ uxChange(e) { const checked = $("#accessibleUX").is(":checked"); - $(".accessibleUX").css("display", checked ? "block" : "none"); + const UXstyle = "display:" + (checked ? "block" : "none"); + $('html > head').append($('')); } } From 63799408afff814c1f4cdf90ddedb6ac655a88af Mon Sep 17 00:00:00 2001 From: john19696 Date: Thu, 25 Aug 2022 13:27:04 +0100 Subject: [PATCH 7/7] lint should have errored before --- src/web/waiters/OptionsWaiter.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web/waiters/OptionsWaiter.mjs b/src/web/waiters/OptionsWaiter.mjs index 731945196e..0ba97400f5 100755 --- a/src/web/waiters/OptionsWaiter.mjs +++ b/src/web/waiters/OptionsWaiter.mjs @@ -202,7 +202,7 @@ class OptionsWaiter { uxChange(e) { const checked = $("#accessibleUX").is(":checked"); const UXstyle = "display:" + (checked ? "block" : "none"); - $('html > head').append($('')); + $("html > head").append($("")); } }