diff --git a/core/components/menu/menu.js b/core/components/menu/menu.js index 00fd13e4e..4386f47ac 100644 --- a/core/components/menu/menu.js +++ b/core/components/menu/menu.js @@ -92,7 +92,7 @@ Blockly.Menu.prototype.blur = function() { /** * Set the menu accessibility role. - * @param {!Blockly.utils.aria.Role|string} roleName role name. + * @param {!Blockly.utils.aria.Role} roleName role name. * @package */ Blockly.Menu.prototype.setRole = function(roleName) { diff --git a/core/components/menu/menuitem.js b/core/components/menu/menuitem.js index 7e529279d..9639aab43 100644 --- a/core/components/menu/menuitem.js +++ b/core/components/menu/menuitem.js @@ -95,8 +95,8 @@ Blockly.MenuItem.prototype.createDom = function() { Blockly.utils.aria.setRole(element, this.roleName_ || (this.checkable_ ? Blockly.utils.aria.Role.MENUITEMCHECKBOX : Blockly.utils.aria.Role.MENUITEM)); - Blockly.utils.aria.setState(element, - Blockly.utils.aria.State.SELECTED, (this.checkable_ && this.checked_) || false); + Blockly.utils.aria.setState(element, Blockly.utils.aria.State.SELECTED, + (this.checkable_ && this.checked_) || false); }; /** @@ -164,7 +164,7 @@ Blockly.MenuItem.prototype.getValue = function() { /** * Set the menu accessibility role. - * @param {!Blockly.utils.aria.Role|string} roleName role name. + * @param {!Blockly.utils.aria.Role} roleName Role name. * @package */ Blockly.MenuItem.prototype.setRole = function(roleName) { diff --git a/core/components/tree/basenode.js b/core/components/tree/basenode.js index 9127dbe32..40130fb71 100644 --- a/core/components/tree/basenode.js +++ b/core/components/tree/basenode.js @@ -188,10 +188,8 @@ Blockly.tree.BaseNode.prototype.initAccessibility = function() { label.id = this.getId() + '.label'; } - Blockly.utils.aria.setRole(el, - Blockly.utils.aria.Role.TREEITEM); - Blockly.utils.aria.setState(el, - Blockly.utils.aria.State.SELECTED, false); + Blockly.utils.aria.setRole(el, Blockly.utils.aria.Role.TREEITEM); + Blockly.utils.aria.setState(el, Blockly.utils.aria.State.SELECTED, false); Blockly.utils.aria.setState(el, Blockly.utils.aria.State.LEVEL, this.getDepth()); if (label) { diff --git a/core/field_colour.js b/core/field_colour.js index 2e6913a68..6878977e6 100644 --- a/core/field_colour.js +++ b/core/field_colour.js @@ -557,13 +557,12 @@ Blockly.FieldColour.prototype.dropdownCreate_ = function() { table.className = 'blocklyColourTable'; table.tabIndex = 0; table.dir = 'ltr'; - Blockly.utils.aria.setRole(table, - Blockly.utils.aria.Role.GRID); - Blockly.utils.aria.setState(table, - Blockly.utils.aria.State.EXPANDED, true); - Blockly.utils.aria.setState(table, 'rowcount', + Blockly.utils.aria.setRole(table, Blockly.utils.aria.Role.GRID); + Blockly.utils.aria.setState(table, Blockly.utils.aria.State.EXPANDED, true); + Blockly.utils.aria.setState(table, Blockly.utils.aria.State.ROWCOUNT, Math.floor(colours.length / columns)); - Blockly.utils.aria.setState(table, 'colcount', columns); + Blockly.utils.aria.setState(table, Blockly.utils.aria.State.COLCOUNT, + columns); var row; for (var i = 0; i < colours.length; i++) { if (i % columns == 0) { diff --git a/core/field_dropdown.js b/core/field_dropdown.js index d01a89c1a..3f38b3a6d 100644 --- a/core/field_dropdown.js +++ b/core/field_dropdown.js @@ -33,6 +33,7 @@ goog.require('Blockly.Menu'); goog.require('Blockly.MenuItem'); goog.require('Blockly.navigation'); goog.require('Blockly.utils'); +goog.require('Blockly.utils.aria'); goog.require('Blockly.utils.dom'); goog.require('Blockly.utils.object'); goog.require('Blockly.utils.Size'); @@ -251,7 +252,7 @@ Blockly.FieldDropdown.prototype.showEditor_ = function() { Blockly.FieldDropdown.prototype.dropdownCreate_ = function() { var menu = new Blockly.Menu(); menu.setRightToLeft(this.sourceBlock_.RTL); - menu.setRole('listbox'); + menu.setRole(Blockly.utils.aria.Role.LISTBOX); var options = this.getOptions(false); this.selectedMenuItem_ = null; @@ -266,7 +267,7 @@ Blockly.FieldDropdown.prototype.dropdownCreate_ = function() { content = image; } var menuItem = new Blockly.MenuItem(content); - menuItem.setRole('option'); + menuItem.setRole(Blockly.utils.aria.Role.OPTION); menuItem.setRightToLeft(this.sourceBlock_.RTL); menuItem.setValue(value); menuItem.setCheckable(true); diff --git a/core/field_multilineinput.js b/core/field_multilineinput.js index 349afc37e..41e16b0fd 100644 --- a/core/field_multilineinput.js +++ b/core/field_multilineinput.js @@ -29,6 +29,7 @@ goog.require('Blockly.Css'); goog.require('Blockly.DropDownDiv'); goog.require('Blockly.FieldTextInput'); goog.require('Blockly.utils'); +goog.require('Blockly.utils.aria'); goog.require('Blockly.utils.Coordinate'); goog.require('Blockly.utils.dom'); goog.require('Blockly.utils.KeyCodes'); @@ -178,10 +179,12 @@ Blockly.FieldMultilineInput.prototype.render_ = function() { var htmlInput = /** @type {!HTMLElement} */(this.htmlInput_); if (!this.isTextValid_) { Blockly.utils.dom.addClass(htmlInput, 'blocklyInvalidInput'); - Blockly.utils.aria.setState(htmlInput, 'invalid', true); + Blockly.utils.aria.setState(htmlInput, + Blockly.utils.aria.State.INVALID, true); } else { Blockly.utils.dom.removeClass(htmlInput, 'blocklyInvalidInput'); - Blockly.utils.aria.setState(htmlInput, 'invalid', false); + Blockly.utils.aria.setState(htmlInput, + Blockly.utils.aria.State.INVALID, false); } } }; diff --git a/core/field_number.js b/core/field_number.js index be5ac735d..9cdd99439 100644 --- a/core/field_number.js +++ b/core/field_number.js @@ -25,6 +25,7 @@ goog.provide('Blockly.FieldNumber'); goog.require('Blockly.fieldRegistry'); goog.require('Blockly.FieldTextInput'); +goog.require('Blockly.utils.aria'); goog.require('Blockly.utils.object'); diff --git a/core/field_textinput.js b/core/field_textinput.js index f6ebb0a22..2d7d3b762 100644 --- a/core/field_textinput.js +++ b/core/field_textinput.js @@ -77,7 +77,7 @@ Blockly.FieldTextInput = function(opt_value, opt_validator, opt_config) { * @private */ this.onKeyDownWrapper_ = null; - + /** * Key input event data. * @type {?Blockly.EventData} @@ -206,10 +206,12 @@ Blockly.FieldTextInput.prototype.render_ = function() { var htmlInput = /** @type {!HTMLElement} */(this.htmlInput_); if (!this.isTextValid_) { Blockly.utils.dom.addClass(htmlInput, 'blocklyInvalidInput'); - Blockly.utils.aria.setState(htmlInput, 'invalid', true); + Blockly.utils.aria.setState(htmlInput, + Blockly.utils.aria.State.INVALID, true); } else { Blockly.utils.dom.removeClass(htmlInput, 'blocklyInvalidInput'); - Blockly.utils.aria.setState(htmlInput, 'invalid', false); + Blockly.utils.aria.setState(htmlInput, + Blockly.utils.aria.State.INVALID, false); } } }; diff --git a/core/utils/aria.js b/core/utils/aria.js index 14f31beb2..3ba1eac11 100644 --- a/core/utils/aria.js +++ b/core/utils/aria.js @@ -44,55 +44,6 @@ Blockly.utils.aria.ROLE_ATTRIBUTE_ = 'role'; * @enum {string} */ Blockly.utils.aria.Role = { - // ARIA role for an alert element that doesn't need to be explicitly closed. - ALERT: 'alert', - - // ARIA role for an alert dialog element that takes focus and must be closed. - ALERTDIALOG: 'alertdialog', - - // ARIA role for an application that implements its own keyboard navigation. - APPLICATION: 'application', - - // ARIA role for an article. - ARTICLE: 'article', - - // ARIA role for a banner containing mostly site content, not page content. - BANNER: 'banner', - - // ARIA role for a button element. - BUTTON: 'button', - - // ARIA role for a checkbox button element; use with the CHECKED state. - CHECKBOX: 'checkbox', - - // ARIA role for a column header of a table or grid. - COLUMNHEADER: 'columnheader', - - // ARIA role for a combo box element. - COMBOBOX: 'combobox', - - // ARIA role for a supporting section of the document. - COMPLEMENTARY: 'complementary', - - // ARIA role for a large perceivable region that contains information - // about the parent document. - CONTENTINFO: 'contentinfo', - - // ARIA role for a definition of a term or concept. - DEFINITION: 'definition', - - // ARIA role for a dialog, some descendant must take initial focus. - DIALOG: 'dialog', - - // ARIA role for a directory, like a table of contents. - DIRECTORY: 'directory', - - // ARIA role for a part of a page that's a document, not a web application. - DOCUMENT: 'document', - - // ARIA role for a landmark region logically considered one form. - FORM: 'form', - // ARIA role for an interactive control of tabular data. GRID: 'grid', @@ -102,57 +53,18 @@ Blockly.utils.aria.Role = { // ARIA role for a group of related elements like tree item siblings. GROUP: 'group', - // ARIA role for a heading element. - HEADING: 'heading', - - // ARIA role for a container of elements that together comprise one image. - IMG: 'img', - - // ARIA role for a link. - LINK: 'link', - - // ARIA role for a list of non-interactive list items. - LIST: 'list', - // ARIA role for a listbox. LISTBOX: 'listbox', - // ARIA role for a list item. - LISTITEM: 'listitem', - - // ARIA role for a live region where new information is added. - LOG: 'log', - - // ARIA landmark role for the main content in a document. Use only once. - MAIN: 'main', - - // ARIA role for a live region of non-essential information that changes. - MARQUEE: 'marquee', - - // ARIA role for a mathematical expression. - MATH: 'math', - // ARIA role for a popup menu. MENU: 'menu', - // ARIA role for a menubar element containing menu elements. - MENUBAR: 'menubar', - // ARIA role for menu item elements. MENUITEM: 'menuitem', // ARIA role for a checkbox box element inside a menu. MENUITEMCHECKBOX: 'menuitemcheckbox', - // ARIA role for a radio button element inside a menu. - MENUITEMRADIO: 'menuitemradio', - - // ARIA landmark role for a collection of navigation links. - NAVIGATION: 'navigation', - - // ARIA role for a section ancillary to the main content. - NOTE: 'note', - // ARIA role for option items that are children of combobox, listbox, menu, // radiogroup, or tree elements. OPTION: 'option', @@ -160,78 +72,12 @@ Blockly.utils.aria.Role = { // ARIA role for ignorable cosmetic elements with no semantic significance. PRESENTATION: 'presentation', - // ARIA role for a progress bar element. - PROGRESSBAR: 'progressbar', - - // ARIA role for a radio button element. - RADIO: 'radio', - - // ARIA role for a group of connected radio button elements. - RADIOGROUP: 'radiogroup', - - // ARIA role for an important region of the page. - REGION: 'region', - // ARIA role for a row of cells in a grid. ROW: 'row', - // ARIA role for a group of one or more rows in a grid. - ROWGROUP: 'rowgroup', - - // ARIA role for a row header of a table or grid. - ROWHEADER: 'rowheader', - - // ARIA role for a scrollbar element. - SCROLLBAR: 'scrollbar', - - // ARIA landmark role for a part of the page providing search functionality. - SEARCH: 'search', - - // ARIA role for a menu separator. - SEPARATOR: 'separator', - - // ARIA role for a slider. - SLIDER: 'slider', - - // ARIA role for a spin button. - SPINBUTTON: 'spinbutton', - - // ARIA role for a live region with advisory info less severe than an alert. - STATUS: 'status', - - // ARIA role for a tab button. - TAB: 'tab', - - // ARIA role for a table. - TABLE: 'table', - - // ARIA role for a tab bar (i.e. a list of tab buttons). - TABLIST: 'tablist', - - // ARIA role for a tab page (i.e. the element holding tab contents). - TABPANEL: 'tabpanel', - - // ARIA role for a textbox element. - TEXTBOX: 'textbox', - - // ARIA role for a textinfo element. - TEXTINFO: 'textinfo', - - // ARIA role for an element displaying elapsed time or time remaining. - TIMER: 'timer', - - // ARIA role for a toolbar element. - TOOLBAR: 'toolbar', - - // ARIA role for a tooltip element. - TOOLTIP: 'tooltip', - // ARIA role for a tree. TREE: 'tree', - // ARIA role for a grid whose rows can be expanded and collapsed like a tree. - TREEGRID: 'treegrid', - // ARIA role for a tree item that sometimes may be expanded or collapsed. TREEITEM: 'treeitem' }; @@ -246,65 +92,15 @@ Blockly.utils.aria.State = { // for example the selected item in a list box. Value: ID of an element. ACTIVEDESCENDANT: 'activedescendant', - // ARIA property that, if true, indicates that all of a changed region should - // be presented, instead of only parts. Value: one of {true, false}. - ATOMIC: 'atomic', - - // ARIA property to specify that input completion is provided. Value: - // one of {'inline', 'list', 'both', 'none'}. - AUTOCOMPLETE: 'autocomplete', - - // ARIA state to indicate that an element and its subtree are being updated. - // Value: one of {true, false}. - BUSY: 'busy', - - // ARIA state for a checked item. Value: one of {'true', 'false', 'mixed', - // undefined}. - CHECKED: 'checked', - - // ARIA state that defines an element's column index or position with respect - // to the total number of columns within a table, grid, or treegrid. - // Value: number. - COLINDEX: 'colindex', - - // ARIA property that identifies the element or elements whose contents or - // presence are controlled by this element. - // Value: space-separated IDs of other elements. - CONTROLS: 'controls', - - // ARIA property that identifies the element or elements that describe - // this element. Value: space-separated IDs of other elements. - DESCRIBEDBY: 'describedby', - - // ARIA state for a disabled item. Value: one of {true, false}. - DISABLED: 'disabled', - - // ARIA property that indicates what functions can be performed when a - // dragged object is released on the drop target. Value: one of - // {'copy', 'move', 'link', 'execute', 'popup', 'none'}. - DROPEFFECT: 'dropeffect', + // ARIA property defines the total number of columns in a table, grid, or + // treegrid. + // Value: integer. + COLCOUNT: 'colcount', // ARIA state for setting whether the element like a tree node is expanded. // Value: one of {true, false, undefined}. EXPANDED: 'expanded', - // ARIA property that identifies the next element (or elements) in the - // recommended reading order of content. Value: space-separated ids of - // elements to flow to. - FLOWTO: 'flowto', - - // ARIA state that indicates an element's "grabbed" state in drag-and-drop. - // Value: one of {true, false, undefined}. - GRABBED: 'grabbed', - - // ARIA property indicating whether the element has a popup. - // Value: one of {true, false}. - HASPOPUP: 'haspopup', - - // ARIA state indicating that the element is not visible or perceivable - // to any user. Value: one of {true, false}. - HIDDEN: 'hidden', - // ARIA state indicating that the entered value does not conform. Value: // one of {false, true, 'grammar', 'spelling'} INVALID: 'invalid', @@ -321,54 +117,18 @@ Blockly.utils.aria.State = { // Value: integer. LEVEL: 'level', - // ARIA property indicating that an element will be updated, and - // describes the types of updates the user agents, assistive technologies, - // and user can expect from the live region. Value: one of {'off', 'polite', - // 'assertive'}. - LIVE: 'live', - - // ARIA property indicating whether a text box can accept multiline input. - // Value: one of {true, false}. - MULTILINE: 'multiline', - - // ARIA property indicating if the user may select more than one item. - // Value: one of {true, false}. - MULTISELECTABLE: 'multiselectable', - // ARIA property indicating if the element is horizontal or vertical. // Value: one of {'vertical', 'horizontal'}. ORIENTATION: 'orientation', - // ARIA property creating a visual, functional, or contextual parent/child - // relationship when the DOM hierarchy can't be used to represent it. - // Value: Space-separated IDs of elements. - OWNS: 'owns', - // ARIA property that defines an element's number of position in a list. // Value: integer. POSINSET: 'posinset', - // ARIA state for a pressed item. - // Value: one of {true, false, undefined, 'mixed'}. - PRESSED: 'pressed', - - // ARIA property indicating that an element is not editable. - // Value: one of {true, false}. - READONLY: 'readonly', - - // ARIA property indicating that change notifications within this subtree - // of a live region should be announced. Value: one of {'additions', - // 'removals', 'text', 'all', 'additions text'}. - RELEVANT: 'relevant', - - // ARIA property indicating that user input is required on this element - // before a form may be submitted. Value: one of {true, false}. - REQUIRED: 'required', - - // ARIA state that defines an element's row index or position with respect - // to the total number of rows within a table, grid, or treegrid. - // Value: number. - ROWINDEX: 'rowindex', + // ARIA property defines the total number of rows in a table, grid, or + // treegrid. + // Value: integer. + ROWCOUNT: 'rowcount', // ARIA state for setting the currently selected item in the list. // Value: one of {true, false, undefined}. @@ -377,72 +137,30 @@ Blockly.utils.aria.State = { // ARIA property defining the number of items in a list. Value: integer. SETSIZE: 'setsize', - // ARIA property indicating if items are sorted. Value: one of {'ascending', - // 'descending', 'none', 'other'}. - SORT: 'sort', - // ARIA property for slider maximum value. Value: number. VALUEMAX: 'valuemax', // ARIA property for slider minimum value. Value: number. - VALUEMIN: 'valuemin', - - // ARIA property for slider active value. Value: number. - VALUENOW: 'valuenow', - - // ARIA property for slider active value represented as text. - // Value: string. - VALUETEXT: 'valuetext' + VALUEMIN: 'valuemin' }; /** - * Sets the role of an element. If the roleName is - * empty string or null, the role for the element is removed. - * We encourage clients to call the goog.a11y.aria.removeRole - * method instead of setting null and empty string values. - * Special handling for this case is added to ensure - * backword compatibility with existing code. + * Sets the role of an element. * * Similar to Closure's goog.a11y.aria * * @param {!Element} element DOM node to set role of. - * @param {!Blockly.utils.aria.Role|string} roleName role name(s). + * @param {!Blockly.utils.aria.Role} roleName Role name. */ Blockly.utils.aria.setRole = function(element, roleName) { - if (!roleName) { - // Setting the ARIA role to empty string is not allowed - // by the ARIA standard. - Blockly.utils.aria.removeRole(element); - } else { - element.setAttribute(Blockly.utils.aria.ROLE_ATTRIBUTE_, roleName); - } -}; - -/** - * Gets role of an element. - * Copied from Closure's goog.a11y.aria - * @param {!Element} element DOM element to get role of. - * @return {?Blockly.utils.aria.Role} ARIA Role name. - */ -Blockly.utils.aria.getRole = function(element) { - var role = element.getAttribute(Blockly.utils.aria.ROLE_ATTRIBUTE_); - return /** @type {Blockly.utils.aria.Role} */ (role) || null; -}; - -/** - * Removes role of an element. - * Copied from Closure's goog.a11y.aria - * @param {!Element} element DOM element to remove the role from. - */ -Blockly.utils.aria.removeRole = function(element) { - element.removeAttribute(Blockly.utils.aria.ROLE_ATTRIBUTE_); + element.setAttribute(Blockly.utils.aria.ROLE_ATTRIBUTE_, roleName); }; /** * Sets the state or property of an element. * Copied from Closure's goog.a11y.aria * @param {!Element} element DOM node where we set state. - * @param {!(Blockly.utils.aria.State|string)} stateName State attribute being set. + * @param {!Blockly.utils.aria.State} stateName State attribute being set. * Automatically adds prefix 'aria-' to the state name if the attribute is * not an extra attribute. * @param {string|boolean|number|!Array.} value Value @@ -452,18 +170,6 @@ Blockly.utils.aria.setState = function(element, stateName, value) { if (Array.isArray(value)) { value = value.join(' '); } - var attrStateName = Blockly.utils.aria.getAriaAttributeName_(stateName); + var attrStateName = Blockly.utils.aria.ARIA_PREFIX_ + stateName; element.setAttribute(attrStateName, value); }; - -/** - * Adds the 'aria-' prefix to ariaName. - * Copied from Closure's goog.a11y.aria - * @param {string} ariaName ARIA state/property name. - * @private - * @return {string} The ARIA attribute name with added 'aria-' prefix. - * @throws {Error} If no such attribute exists. - */ -Blockly.utils.aria.getAriaAttributeName_ = function(ariaName) { - return Blockly.utils.aria.ARIA_PREFIX_ + ariaName; -};