ccessible user experience

This commit is contained in:
John L 2022-05-27 17:06:29 +01:00
parent 7c66dacc40
commit eb04145a62
9 changed files with 127 additions and 6 deletions

View file

@ -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",

View file

@ -46,7 +46,7 @@ class HTMLOperation {
* @returns {string}
*/
toStubHtml(removeIcon) {
let html = "<li class='operation'";
let html = `<li class='operation' data-opName="${this.name}"`;
if (this.description) {
const infoLink = this.infoURL ? `<hr>${titleFromWikiLink(this.infoURL)}` : "";
@ -58,6 +58,12 @@ class HTMLOperation {
html += ">" + this.name;
html += `<span class='float-right'>
<button type="button" class="btn btn-primary bmd-btn-icon accessibleUX" data-toggle="tooltip" data-original-title="Add to recipe">
<i class='material-icons'>add_box</i>
</button>
</span>`;
if (removeIcon) {
html += "<i class='material-icons remove-icon op-icon'>delete</i>";
}
@ -83,6 +89,9 @@ class HTMLOperation {
html += `</div>
<div class="recip-icons">
<i class="material-icons move-down accessibleUX" title="Move down">arrow_downward</i>
<i class="material-icons move-up accessibleUX">arrow_upward</i>
<i class="material-icons remove-icon accessibleUX" title="Delete operation">delete</i>
<i class="material-icons breakpoint" title="Set breakpoint" break="false">pause</i>
<i class="material-icons disable-icon" title="Disable operation" disabled="false">not_interested</i>
</div>

View file

@ -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));

View file

@ -584,6 +584,13 @@
Keep the current tab in sync between the input and output
</label>
</div>
<div class="checkbox option-item">
<label for="accessibleUX">
<input type="checkbox" option="accessibleUX" id="accessibleUX">
Accessible user experience, buttons for drag-and-drop operations
</label>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" id="reset-options">Reset options to default</button>

View file

@ -54,7 +54,8 @@ function main() {
autoMagic: true,
imagePreview: true,
syncTabs: true,
preserveCR: "entropy"
preserveCR: "entropy",
accessibleUX: false
};
document.removeEventListener("DOMContentLoaded", main, false);

View file

@ -205,8 +205,21 @@ class OperationsWaiter {
*/
operationDblclick(e) {
const li = e.target;
// get operation name from <li> 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 <li> data
this.manager.recipe.addOperation($(li).data("opname"));
}

View file

@ -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;

View file

@ -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);

View file

@ -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";