From ee28eda9982d3d31e9c7d12b831de006369e6db9 Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Tue, 12 Oct 2021 12:56:18 -0500 Subject: [PATCH] Checkbox Example (Mixed-State): Restore Focus Appearance and Update Code Style (pull #1801) * Restored visual focus styling features that were mistakenly removed. * Added documentation of accessibility features. * Updated JS code to use current coding practices. * Changed from using event.keyCode to event.key in event handlers. * Change to use SVG graphics * Renamed example HTML file: checkbox-mixed.html . Co-authored-by: Matt King --- .../checkbox/checkbox-2/js/checkboxMixed.js | 151 --------------- .../checkbox-2/js/controlledCheckbox.js | 98 ---------- .../checkbox-2.html => checkbox-mixed.html} | 68 ++++--- examples/checkbox/css/checkbox-mixed.css | 62 +++++++ examples/checkbox/js/checkbox-mixed.js | 173 ++++++++++++++++++ examples/index.html | 6 +- ...eckbox-2.js => checkbox_checkbox-mixed.js} | 10 +- 7 files changed, 275 insertions(+), 293 deletions(-) delete mode 100644 examples/checkbox/checkbox-2/js/checkboxMixed.js delete mode 100644 examples/checkbox/checkbox-2/js/controlledCheckbox.js rename examples/checkbox/{checkbox-2/checkbox-2.html => checkbox-mixed.html} (74%) create mode 100644 examples/checkbox/css/checkbox-mixed.css create mode 100644 examples/checkbox/js/checkbox-mixed.js rename test/tests/{checkbox_checkbox-2.js => checkbox_checkbox-mixed.js} (96%) diff --git a/examples/checkbox/checkbox-2/js/checkboxMixed.js b/examples/checkbox/checkbox-2/js/checkboxMixed.js deleted file mode 100644 index 84698448be..0000000000 --- a/examples/checkbox/checkbox-2/js/checkboxMixed.js +++ /dev/null @@ -1,151 +0,0 @@ -/* - * This content is licensed according to the W3C Software License at - * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document - * - * File: CheckboxMixed.js - * - * Desc: CheckboxMixed widget that implements ARIA Authoring Practices - * for a menu of links - */ - -/* global ControlledCheckbox */ - -'use strict'; - -/* - * @constructor CheckboxMixed - * - * - */ -var CheckboxMixed = function (domNode) { - this.domNode = domNode; - - this.controlledCheckboxes = []; - - this.keyCode = Object.freeze({ - RETURN: 13, - SPACE: 32, - }); -}; - -CheckboxMixed.prototype.init = function () { - this.domNode.tabIndex = 0; - - var ids = this.domNode.getAttribute('aria-controls').split(' '); - - for (var i = 0; i < ids.length; i++) { - var node = document.getElementById(ids[i]); - var ccb = new ControlledCheckbox(node, this); - ccb.init(); - this.controlledCheckboxes.push(ccb); - } - - this.domNode.addEventListener('keydown', this.handleKeydown.bind(this)); - this.domNode.addEventListener('click', this.handleClick.bind(this)); - this.domNode.addEventListener('focus', this.handleFocus.bind(this)); - this.domNode.addEventListener('blur', this.handleBlur.bind(this)); - - this.updateCheckboxMixed(); -}; - -CheckboxMixed.prototype.updateCheckboxMixed = function () { - var count = 0; - - for (var i = 0; i < this.controlledCheckboxes.length; i++) { - if (this.controlledCheckboxes[i].isChecked()) { - count++; - } - } - - if (count === 0) { - this.domNode.setAttribute('aria-checked', 'false'); - } else { - if (count === this.controlledCheckboxes.length) { - this.domNode.setAttribute('aria-checked', 'true'); - } else { - this.domNode.setAttribute('aria-checked', 'mixed'); - this.updateControlledStates(); - } - } -}; - -CheckboxMixed.prototype.updateControlledStates = function () { - for (var i = 0; i < this.controlledCheckboxes.length; i++) { - this.controlledCheckboxes[i].lastState = this.controlledCheckboxes[ - i - ].isChecked(); - } -}; - -CheckboxMixed.prototype.anyLastChecked = function () { - var count = 0; - - for (var i = 0; i < this.controlledCheckboxes.length; i++) { - if (this.controlledCheckboxes[i].lastState) { - count++; - } - } - - return count > 0; -}; - -CheckboxMixed.prototype.setControlledCheckboxes = function (value) { - for (var i = 0; i < this.controlledCheckboxes.length; i++) { - this.controlledCheckboxes[i].setChecked(value); - } - - this.updateCheckboxMixed(); -}; - -CheckboxMixed.prototype.toggleCheckboxMixed = function () { - var state = this.domNode.getAttribute('aria-checked'); - - if (state === 'false') { - if (this.anyLastChecked()) { - this.setControlledCheckboxes('last'); - } else { - this.setControlledCheckboxes('true'); - } - } else { - if (state === 'mixed') { - this.setControlledCheckboxes('true'); - } else { - this.setControlledCheckboxes('false'); - } - } - - this.updateCheckboxMixed(); -}; - -/* EVENT HANDLERS */ - -CheckboxMixed.prototype.handleKeydown = function (event) { - var flag = false; - - switch (event.keyCode) { - case this.keyCode.SPACE: - this.toggleCheckboxMixed(); - flag = true; - break; - - default: - break; - } - - if (flag) { - event.stopPropagation(); - event.preventDefault(); - } -}; - -CheckboxMixed.prototype.handleClick = function () { - this.toggleCheckboxMixed(); -}; - -CheckboxMixed.prototype.handleFocus = function () { - this.domNode.classList.add('focus'); -}; - -CheckboxMixed.prototype.handleBlur = function () { - this.domNode.classList.remove('focus'); -}; diff --git a/examples/checkbox/checkbox-2/js/controlledCheckbox.js b/examples/checkbox/checkbox-2/js/controlledCheckbox.js deleted file mode 100644 index a8684309a5..0000000000 --- a/examples/checkbox/checkbox-2/js/controlledCheckbox.js +++ /dev/null @@ -1,98 +0,0 @@ -/* - * This content is licensed according to the W3C Software License at - * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document - * - * File: controlledCheckbox.js - * - * Desc: ControlledCheckbox widget that implements ARIA Authoring Practices - * for a mixed checkbox - */ - -'use strict'; - -/* - * @constructor ControlledCheckbox - * - * - */ -var ControlledCheckbox = function (domNode, controllerObj) { - this.domNode = domNode; - this.controller = controllerObj; - this.lastState = false; -}; - -ControlledCheckbox.prototype.init = function () { - this.lastState = this.isChecked(); - - this.domNode.addEventListener('change', this.handleChange.bind(this)); - - this.domNode.addEventListener('keydown', this.handleKeyup.bind(this), true); - this.domNode.addEventListener('click', this.handleClick.bind(this), true); -}; - -ControlledCheckbox.prototype.isChecked = function () { - // if standard input[type=checkbox] - if (typeof this.domNode.checked === 'boolean') { - return this.domNode.checked; - } - - // If ARIA checkbox widget - return this.domNode.getAttribute('aria-checked') === 'true'; -}; - -ControlledCheckbox.prototype.setChecked = function (value) { - // if standard input[type=checkbox] - if (typeof this.domNode.checked === 'boolean') { - switch (value) { - case 'true': - this.domNode.checked = true; - break; - - case 'false': - this.domNode.checked = false; - break; - - case 'last': - this.domNode.checked = this.lastState; - break; - - default: - break; - } - } - - // If ARIA checkbox widget - if (typeof this.domNode.getAttribute('aria-checked') === 'string') { - switch (value) { - case 'true': - case 'false': - this.domNode.setAttribute('aria-checked', value); - break; - - case 'last': - if (this.lastState) { - this.domNode.setAttribute('aria-checked', 'true'); - } else { - this.domNode.setAttribute('aria-checked', 'false'); - } - break; - - default: - break; - } - } -}; - -/* EVENT HANDLERS */ - -ControlledCheckbox.prototype.handleChange = function () { - this.controller.updateCheckboxMixed(); -}; - -ControlledCheckbox.prototype.handleKeyup = function () { - this.lastState = this.isChecked(); -}; - -ControlledCheckbox.prototype.handleClick = function () { - this.lastState = this.isChecked(); -}; diff --git a/examples/checkbox/checkbox-2/checkbox-2.html b/examples/checkbox/checkbox-mixed.html similarity index 74% rename from examples/checkbox/checkbox-2/checkbox-2.html rename to examples/checkbox/checkbox-mixed.html index 42681efcc2..925b12e5de 100644 --- a/examples/checkbox/checkbox-2/checkbox-2.html +++ b/examples/checkbox/checkbox-mixed.html @@ -6,14 +6,13 @@ - - - - + + + + - - - + +