diff --git a/examples/toolbar/css/toolbar.css b/examples/toolbar/css/toolbar.css index 924c454f7f..4f070bcd55 100644 --- a/examples/toolbar/css/toolbar.css +++ b/examples/toolbar/css/toolbar.css @@ -42,6 +42,54 @@ margin-right: 0.25em; } +[role="toolbar"] button.popup { + position: relative; +} + +[role="toolbar"] button .popup-label { + display: block; + width: initial; + border: 1px solid white; + padding: 2px 4px; + border-radius: 5px; + position: absolute; + top: -30000em; + background-color: black; + color: white; + font-weight: normal; +} + +[role="toolbar"] button .popup-label.show { + text-align: center; + top: -2.5em; +} + +[role="toolbar"] button .popup-label::after, +[role="toolbar"] button .popup-label::before { + top: 100%; + left: 50%; + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; +} + +[role="toolbar"] button .popup-label::after { + border-color: rgba(0, 0, 0, 0); + border-top-color: #000; + border-width: 10px; + margin-left: -10px; +} + +[role="toolbar"] button .popup-label::before { + border-color: rgba(255, 255, 255, 0); + border-top-color: #fff; + border-width: 12px; + margin-left: -12px; +} + [role="toolbar"] button[aria-pressed="true"], [role="toolbar"] [role="radio"][aria-checked="true"] { border-color: #555; diff --git a/examples/toolbar/js/FontMenuButton.js b/examples/toolbar/js/FontMenuButton.js index 7c5d114003..25d5078219 100644 --- a/examples/toolbar/js/FontMenuButton.js +++ b/examples/toolbar/js/FontMenuButton.js @@ -17,7 +17,7 @@ FontMenuButton = function (node, toolbar, toolbarItem) { this.keyCode = Object.freeze({ 'TAB': 9, - 'RETURN': 13, + 'ENTER': 13, 'ESC': 27, 'SPACE': 32, 'UP': 38, @@ -46,7 +46,7 @@ FontMenuButton.prototype.handleKeyDown = function (event) { switch (event.keyCode) { case this.keyCode.SPACE: - case this.keyCode.RETURN: + case this.keyCode.ENTER: case this.keyCode.DOWN: case this.keyCode.UP: this.fontMenu.open(); diff --git a/examples/toolbar/js/FontMenuItem.js b/examples/toolbar/js/FontMenuItem.js index ca26b5226c..2cf062ba0b 100644 --- a/examples/toolbar/js/FontMenuItem.js +++ b/examples/toolbar/js/FontMenuItem.js @@ -29,7 +29,7 @@ var FontMenuItem = function (domNode, fontMenu) { this.keyCode = Object.freeze({ 'TAB': 9, - 'RETURN': 13, + 'ENTER': 13, 'ESC': 27, 'SPACE': 32, 'PAGEUP': 33, @@ -86,7 +86,7 @@ FontMenuItem.prototype.handleKeydown = function (event) { switch (event.keyCode) { case this.keyCode.SPACE: - case this.keyCode.RETURN: + case this.keyCode.ENTER: this.handleClick(event); flag = true; break; diff --git a/examples/toolbar/js/FormatToolbar.js b/examples/toolbar/js/FormatToolbar.js index 8e888207e2..7f93b0012a 100644 --- a/examples/toolbar/js/FormatToolbar.js +++ b/examples/toolbar/js/FormatToolbar.js @@ -321,6 +321,11 @@ FormatToolbar.prototype.setFocusToLast = function (currentItem) { this.setFocusItem(this.lastItem); }; +FormatToolbar.prototype.hidePopupLabels = function () { + var tps = this.domNode.querySelectorAll('button .popup-label'); + tps.forEach(function (tp) {tp.classList.remove('show');}); +}; + // Initialize toolbars diff --git a/examples/toolbar/js/FormatToolbarItem.js b/examples/toolbar/js/FormatToolbarItem.js index f3e82c4fe9..0eda0cecc1 100644 --- a/examples/toolbar/js/FormatToolbarItem.js +++ b/examples/toolbar/js/FormatToolbarItem.js @@ -10,11 +10,14 @@ FormatToolbarItem = function (domNode, toolbar) { this.toolbar = toolbar; this.buttonAction = ''; this.value = ''; + this.popupLabelNode = null; + this.hasHover = false; + this.popupLabelDelay = 800; this.keyCode = Object.freeze({ 'TAB': 9, - 'RETURN': 13, + 'ENTER': 13, 'ESC': 27, 'SPACE': 32, 'PAGEUP': 33, @@ -33,6 +36,11 @@ FormatToolbarItem.prototype.init = function () { 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.domNode.addEventListener('mouseover', this.handleMouseOver.bind(this)); + this.domNode.addEventListener('mouseleave', this.handleMouseLeave.bind(this)); + + document.body.addEventListener('keydown', this.handleHideAllPopupLabels.bind(this)); + if (this.domNode.classList.contains('bold')) { this.buttonAction = 'bold'; @@ -81,6 +89,15 @@ FormatToolbarItem.prototype.init = function () { if (this.domNode.classList.contains('spinbutton')) { this.buttonAction = 'changeFontSize'; } + // Initialize any popup label + + this.popupLabelNode = this.domNode.querySelector('.popup-label'); + if (this.popupLabelNode) { + var width = 8 * this.popupLabelNode.textContent.length; + this.popupLabelNode.style.width = width + 'px'; + this.popupLabelNode.style.left = -1 * ((width - this.domNode.offsetWidth) / 2) - 5 + 'px'; + } + }; FormatToolbarItem.prototype.isPressed = function () { @@ -115,14 +132,45 @@ FormatToolbarItem.prototype.enable = function () { this.domNode.removeAttribute('aria-disabled'); }; +FormatToolbarItem.prototype.showPopupLabel = function () { + if (this.popupLabelNode) { + this.toolbar.hidePopupLabels(); + this.popupLabelNode.classList.add('show'); + } +}; + +FormatToolbarItem.prototype.hidePopupLabel = function () { + if (this.popupLabelNode && !this.hasHover) { + this.popupLabelNode.classList.remove('show'); + } +}; + + // Events +FormatToolbarItem.prototype.handleHideAllPopupLabels = function (event) { + + switch (event.keyCode) { + + case this.keyCode.ESC: + this.toolbar.hidePopupLabels(); + break; + + default: + break; + } + + +}; + + FormatToolbarItem.prototype.handleBlur = function (event) { this.toolbar.domNode.classList.remove('focus'); if (this.domNode.classList.contains('nightmode')) { this.domNode.parentNode.classList.remove('focus'); } + this.hidePopupLabel(); }; FormatToolbarItem.prototype.handleFocus = function (event) { @@ -131,7 +179,17 @@ FormatToolbarItem.prototype.handleFocus = function (event) { if (this.domNode.classList.contains('nightmode')) { this.domNode.parentNode.classList.add('focus'); } + this.showPopupLabel(); +}; + +FormatToolbarItem.prototype.handleMouseLeave = function (event) { + this.hasHover = false; + setTimeout(this.hidePopupLabel.bind(this), this.popupLabelDelay); +}; +FormatToolbarItem.prototype.handleMouseOver = function (event) { + this.showPopupLabel(); + this.hasHover = true; }; FormatToolbarItem.prototype.handleKeyDown = function (event) { @@ -139,7 +197,7 @@ FormatToolbarItem.prototype.handleKeyDown = function (event) { switch (event.keyCode) { - case this.keyCode.RETURN: + case this.keyCode.ENTER: case this.keyCode.SPACE: if ((this.buttonAction !== '') && (this.buttonAction !== 'bold') && diff --git a/examples/toolbar/toolbar.html b/examples/toolbar/toolbar.html index c54e49aaf3..826718a722 100644 --- a/examples/toolbar/toolbar.html +++ b/examples/toolbar/toolbar.html @@ -53,28 +53,28 @@
The bold, italic, underline, and text align buttons have popup labels that implement the requirements of WCAG Success Criterion 1.4.13 Content on Hover or Focus:
+aria-label="LABEL_STRING"
button
aria-pressed="true"
button
aria-hidden="true"
span
radio
button
button
element as a radio
element.aria-label="ALIGNMENT_NAME_STRING"
button
button
element as a radio
button.button
element.aria-hidden="true"
span
Note: In addition to the ARIA attributes, the accessible design includes the HTML title
attribute for providing accessible descriptions of button actions.