diff --git a/examples/index.html b/examples/index.html index 719a72a5e6..3e419a723f 100644 --- a/examples/index.html +++ b/examples/index.html @@ -134,6 +134,7 @@

Examples by Role

  • Auto-Rotating Image Carousel with Buttons for Slide Control
  • Checkbox (Two State)
  • Editor Menubar
  • +
  • Color Viewer Slider with Mobile Support
  • Date Picker Spin Button
  • Navigation Treeview Using Computed Properties
  • Navigation Treeview Using Declared Properties
  • @@ -287,6 +288,7 @@

    Examples by Role

  • Horizontal Multi-Thumb Slider
  • Horizontal Slider
  • Slider with aria-orientation and aria-valuetext
  • +
  • Color Viewer Slider with Mobile Support
  • @@ -563,6 +565,7 @@

    Examples By Properties and States

  • Navigation Menubar
  • Radio Group Using aria-activedescendant
  • Radio Group Using Roving tabindex
  • +
  • Color Viewer Slider with Mobile Support
  • Date Picker Spin Button
  • Table
  • Tabs with Automatic Activation
  • @@ -599,6 +602,7 @@

    Examples By Properties and States

  • Navigation Menu Button
  • Radio Group Using aria-activedescendant
  • Radio Group Using Roving tabindex
  • +
  • Color Viewer Slider with Mobile Support
  • Date Picker Spin Button
  • Tabs with Automatic Activation
  • Tabs with Manual Activation
  • @@ -743,6 +747,7 @@

    Examples By Properties and States

  • Horizontal Multi-Thumb Slider
  • Horizontal Slider
  • Slider with aria-orientation and aria-valuetext
  • +
  • Color Viewer Slider with Mobile Support
  • Date Picker Spin Button
  • @@ -755,6 +760,7 @@

    Examples By Properties and States

  • Horizontal Multi-Thumb Slider
  • Horizontal Slider
  • Slider with aria-orientation and aria-valuetext
  • +
  • Color Viewer Slider with Mobile Support
  • Date Picker Spin Button
  • Toolbar
  • @@ -768,6 +774,7 @@

    Examples By Properties and States

  • Horizontal Multi-Thumb Slider
  • Horizontal Slider
  • Slider with aria-orientation and aria-valuetext
  • +
  • Color Viewer Slider with Mobile Support
  • Date Picker Spin Button
  • Toolbar
  • diff --git a/examples/slider/css/slider-color-viewer.css b/examples/slider/css/slider-color-viewer.css new file mode 100644 index 0000000000..178bda5206 --- /dev/null +++ b/examples/slider/css/slider-color-viewer.css @@ -0,0 +1,161 @@ +/* CSS Document */ + +.color-viewer-sliders label { + display: block; +} + +.color-box-sliders .label { + margin-top: 0; + margin-bottom: 1 em; +} + +.color-viewer-sliders .color-box { + width: 200px; + height: 200px; + border: black solid medium; + text-align: center; + padding: 0.25em; + forced-color-adjust: none; +} + +.color-viewer-sliders .color-info { + padding-top: 5px; +} + +.color-viewer-sliders .color-info label { + margin-top: 0.25em; + display: block; +} + +.color-group { + padding: 4px; + color: #005a9c; + width: 490px; +} + +.color-group > button, +.color-group > div { + display: inline; +} + +.color-group .color-slider .value { + fill: currentColor; +} + +.color-group.focus .color-slider .value, +.color-group .color-slider:focus .value, +.color-group .color-slider:hover .value { + font-weight: bold; +} + +.color-group .color-slider { + margin: 0; + padding: 0; +} + +.color-group .color-slider .valueBackground { + fill: white; + stroke-width: 0; +} + +.color-group .color-slider .rail { + fill: #bbb; + stroke: transparent; + stroke-width: 2px; + opacity: 0.5; +} + +.color-group .color-slider .fill { + stroke-width: 0; + opacity: 0.5; + pointer-events: none; +} + +.color-group.green .color-slider .fill { + fill: green; +} + +.color-group.red .color-slider .fill { + fill: red; +} + +.color-group.blue .color-slider .fill { + fill: blue; +} + +.color-group .color-slider .thumb { + fill: #666; + stroke: transparent; + stroke-width: 2px; +} + +.color-group .color-slider .focus { + fill: transparent; + stroke: transparent; + stroke-width: 2px; +} + +.color-group.focus .color-slider .fill, +.color-group .color-slider:focus .fill, +.color-group .color-slider:hover .fill { + opacity: 1; +} + +.color-group.focus .color-slider .rail, +.color-group .color-slider:focus .rail, +.color-group .color-slider:hover .rail { + fill: transparent; + stroke: currentColor; + opacity: 1; +} + +.color-group .color-slider:focus .thumb, +.color-group .color-slider:hover .thumb { + fill: #ddd; + stroke: currentColor; + opacity: 1; +} + +.color-group .color-slider:focus .focus, +.color-group .color-slider:hover .focus { + stroke: currentColor; +} + +.color-group button.dec10, +.color-group button.dec, +.color-group button.inc, +.color-group button.inc10 { + padding: 0; + margin: 0; + border: none; + background: none; + color: currentColor; +} + +.color-group button .focus { + stroke: transparent; + stroke-width: 2px; + fill: transparent; +} + +.color-group button line, +.color-group button .background { + fill: transparent; + stroke: currentColor; + stroke-width: 2px; +} + +.color-group button line { + stroke: currentColor; + stroke-width: 2px; +} + +.color-group button:focus, +.color-group .color-slider:focus { + outline: none; +} + +.color-group button:focus .focus, +.color-group button:hover .focus { + stroke: currentColor; +} diff --git a/examples/slider/js/slider-color-viewer.js b/examples/slider/js/slider-color-viewer.js new file mode 100644 index 0000000000..f141853a56 --- /dev/null +++ b/examples/slider/js/slider-color-viewer.js @@ -0,0 +1,566 @@ +'use strict'; +/* + * This content is licensed according to the W3C Software License at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document + * + * File: slider-color-viewer.js + * + * Desc: ColorViewerSliders widget that implements ARIA Authoring Practices + */ + +// Create ColorViewerSliders that contains value, valuemin, valuemax, and valuenow +class ColorViewerSliders { + constructor(domNode) { + this.domNode = domNode; + + this.sliders = {}; + + this.svgWidth = 310; + this.svgHeight = 50; + this.borderWidth = 2; + + this.valueY = 22; + + this.railX = 15; + this.railY = 26; + this.railWidth = 275; + this.railHeight = 14; + + this.thumbHeight = this.railHeight; + this.thumbWidth = this.thumbHeight; + this.rectRadius = this.railHeight / 4; + + this.focusY = this.borderWidth; + this.focusWidth = 36; + this.focusHeight = 48; + + this.initSliderRefs(this.sliders, 'red'); + this.initSliderRefs(this.sliders, 'green'); + this.initSliderRefs(this.sliders, 'blue'); + + this.colorBoxNode = domNode.querySelector('.color-box'); + this.colorValueHexNode = domNode.querySelector('input.color-value-hex'); + this.colorValueRGBNode = domNode.querySelector('input.color-value-rgb'); + } + + initSliderRefs(sliderRef, color) { + sliderRef[color] = {}; + var n = this.domNode.querySelector('.color-group.' + color); + sliderRef[color].groupNode = n; + sliderRef[color].sliderNode = n.querySelector('.color-slider'); + + sliderRef[color].svgNode = n.querySelector('.color-slider svg'); + sliderRef[color].svgNode.setAttribute('width', this.svgWidth); + sliderRef[color].svgNode.setAttribute('height', this.svgHeight); + sliderRef[color].svgPoint = sliderRef[color].svgNode.createSVGPoint(); + + sliderRef[color].valueNode = n.querySelector('.color-slider .value'); + sliderRef[color].valueNode.setAttribute('y', this.valueY); + + sliderRef[color].thumbNode = n.querySelector('.color-slider .thumb'); + sliderRef[color].thumbNode.setAttribute('width', this.thumbWidth); + sliderRef[color].thumbNode.setAttribute('height', this.thumbHeight); + sliderRef[color].thumbNode.setAttribute('y', this.railY); + sliderRef[color].thumbNode.setAttribute('rx', this.rectRadius); + + sliderRef[color].focusNode = n.querySelector('.color-slider .focus'); + sliderRef[color].focusNode.setAttribute( + 'width', + this.focusWidth - this.borderWidth + ); + sliderRef[color].focusNode.setAttribute( + 'height', + this.focusHeight - this.borderWidth + ); + sliderRef[color].focusNode.setAttribute('y', this.focusY); + sliderRef[color].focusNode.setAttribute('rx', this.rectRadius); + + sliderRef[color].railNode = n.querySelector('.color-slider .rail'); + sliderRef[color].railNode.setAttribute('x', this.railX); + sliderRef[color].railNode.setAttribute('y', this.railY); + sliderRef[color].railNode.setAttribute('width', this.railWidth); + sliderRef[color].railNode.setAttribute('height', this.railHeight); + sliderRef[color].railNode.setAttribute('rx', this.rectRadius); + + sliderRef[color].fillNode = n.querySelector('.color-slider .fill'); + sliderRef[color].fillNode.setAttribute('x', this.railX); + sliderRef[color].fillNode.setAttribute('y', this.railY); + sliderRef[color].fillNode.setAttribute('width', this.railWidth); + sliderRef[color].fillNode.setAttribute('height', this.railHeight); + sliderRef[color].fillNode.setAttribute('rx', this.rectRadius); + + // Increment and decrement buttons are optional for + // mobile support + sliderRef[color].dec10Node = n.querySelector('.dec10'); + if (sliderRef[color].dec10Node) { + this.renderButton(sliderRef[color].dec10Node, 'dec10'); + } + + sliderRef[color].decNode = n.querySelector('.dec'); + if (sliderRef[color].decNode) { + this.renderButton(sliderRef[color].decNode, 'dec'); + } + + sliderRef[color].incNode = n.querySelector('.inc'); + if (sliderRef[color].incNode) { + this.renderButton(sliderRef[color].incNode, 'inc'); + } + + sliderRef[color].inc10Node = n.querySelector('.inc10'); + if (sliderRef[color].inc10Node) { + this.renderButton(sliderRef[color].inc10Node, 'inc10'); + } + } + + renderButton(node, option) { + let x, y; + let buttonSVGWidth = 39; + let buttonSVGHeight = 39; + + let buttonCircleX = 19; + let buttonCircleY = 21; + let buttonCircleR = 12; + + let lineLen = (2 * buttonCircleR) / 3; + + let svgNode = node.querySelector('svg'); + svgNode.setAttribute('width', buttonSVGWidth); + svgNode.setAttribute('height', buttonSVGHeight); + + let backgroundNode = svgNode.querySelector('.background'); + backgroundNode.setAttribute('cx', buttonCircleX); + backgroundNode.setAttribute('cy', buttonCircleY); + backgroundNode.setAttribute('r', buttonCircleR); + + let hcBackgroundNode = svgNode.querySelector('.focus'); + hcBackgroundNode.setAttribute('cx', buttonCircleX); + hcBackgroundNode.setAttribute('cy', buttonCircleY); + hcBackgroundNode.setAttribute('r', buttonCircleR + 2 * this.borderWidth); + + var lines = svgNode.querySelectorAll('line'); + if (option.indexOf('10') > 0) { + x = buttonCircleX - lineLen - 1; + lines[0].setAttribute('x1', x); + lines[0].setAttribute('y1', buttonCircleY); + x = x + lineLen; + lines[0].setAttribute('x2', x); + lines[0].setAttribute('y2', buttonCircleY); + x = x + 2; + lines[1].setAttribute('x1', x); + lines[1].setAttribute('y1', buttonCircleY); + x = x + lineLen; + lines[1].setAttribute('x2', x); + lines[1].setAttribute('y2', buttonCircleY); + } else { + x = buttonCircleX - lineLen / 2; + lines[0].setAttribute('x1', x); + lines[0].setAttribute('y1', buttonCircleY); + x = x + lineLen; + lines[0].setAttribute('x2', x); + lines[0].setAttribute('y2', buttonCircleY); + } + + if (option.indexOf('inc') >= 0) { + if (option.indexOf('10') > 0) { + x = buttonCircleX - lineLen / 2 - 1; + y = buttonCircleY - lineLen / 2; + lines[2].setAttribute('x1', x); + lines[2].setAttribute('y1', y); + y = y + lineLen; + lines[2].setAttribute('x2', x); + lines[2].setAttribute('y2', y); + + x = buttonCircleX + lineLen / 2 + 1; + y = buttonCircleY - lineLen / 2; + lines[3].setAttribute('x1', x); + lines[3].setAttribute('y1', y); + y = y + lineLen; + lines[3].setAttribute('x2', x); + lines[3].setAttribute('y2', y); + } else { + y = buttonCircleY - lineLen / 2; + lines[1].setAttribute('x1', buttonCircleX); + lines[1].setAttribute('y1', y); + y = y + lineLen; + lines[1].setAttribute('x2', buttonCircleX); + lines[1].setAttribute('y2', y); + } + } + } + + // Initialize slider + init() { + for (var slider in this.sliders) { + if (this.sliders[slider].sliderNode.tabIndex != 0) { + this.sliders[slider].sliderNode.tabIndex = 0; + } + + this.sliders[slider].railNode.addEventListener( + 'click', + this.onRailClick.bind(this) + ); + + this.sliders[slider].sliderNode.addEventListener( + 'keydown', + this.onSliderKeyDown.bind(this) + ); + + this.sliders[slider].sliderNode.addEventListener( + 'mousedown', + this.onThumbMouseDown.bind(this) + ); + this.sliders[slider].sliderNode.addEventListener( + 'focus', + this.onFocus.bind(this) + ); + this.sliders[slider].sliderNode.addEventListener( + 'blur', + this.onBlur.bind(this) + ); + + // Increment and decrement buttons are optional for + // mobile support + if (this.sliders[slider].dec10Node) { + this.sliders[slider].dec10Node.addEventListener( + 'click', + this.onDec10Click.bind(this) + ); + this.sliders[slider].dec10Node.addEventListener( + 'focus', + this.onFocus.bind(this) + ); + this.sliders[slider].dec10Node.addEventListener( + 'blur', + this.onBlur.bind(this) + ); + } + + if (this.sliders[slider].decNode) { + this.sliders[slider].decNode.addEventListener( + 'click', + this.onDecClick.bind(this) + ); + this.sliders[slider].decNode.addEventListener( + 'focus', + this.onFocus.bind(this) + ); + this.sliders[slider].decNode.addEventListener( + 'blur', + this.onBlur.bind(this) + ); + } + + if (this.sliders[slider].incNode) { + this.sliders[slider].incNode.addEventListener( + 'click', + this.onIncClick.bind(this) + ); + this.sliders[slider].incNode.addEventListener( + 'focus', + this.onFocus.bind(this) + ); + this.sliders[slider].incNode.addEventListener( + 'blur', + this.onBlur.bind(this) + ); + } + + if (this.sliders[slider].inc10Node) { + this.sliders[slider].inc10Node.addEventListener( + 'click', + this.onInc10Click.bind(this) + ); + this.sliders[slider].inc10Node.addEventListener( + 'focus', + this.onFocus.bind(this) + ); + this.sliders[slider].inc10Node.addEventListener( + 'blur', + this.onBlur.bind(this) + ); + } + + this.moveSliderTo( + this.sliders[slider], + this.getValueNow(this.sliders[slider]) + ); + } + } + + // Get point in global SVG space + getSVGPoint(slider, event) { + slider.svgPoint.x = event.clientX; + slider.svgPoint.y = event.clientY; + return slider.svgPoint.matrixTransform( + slider.svgNode.getScreenCTM().inverse() + ); + } + + getSlider(domNode) { + if (!domNode.classList.contains('color-slider')) { + if (domNode.tagName.toLowerCase() === 'rect') { + domNode = domNode.parentNode.parentNode; + } else { + domNode = domNode.parentNode.querySelector('.color-slider'); + } + } + + if (this.sliders.red.sliderNode === domNode) { + return this.sliders.red; + } + + if (this.sliders.green.sliderNode === domNode) { + return this.sliders.green; + } + + return this.sliders.blue; + } + + getValueMin(slider) { + return parseInt(slider.sliderNode.getAttribute('aria-valuemin')); + } + + getValueNow(slider) { + return parseInt(slider.sliderNode.getAttribute('aria-valuenow')); + } + + getValueMax(slider) { + return parseInt(slider.sliderNode.getAttribute('aria-valuemax')); + } + + moveSliderTo(slider, value) { + var pos, offsetX, valueWidth; + var valueMin = this.getValueMin(slider); + var valueNow = this.getValueNow(slider); + var valueMax = this.getValueMax(slider); + + if (value > valueMax) { + value = valueMax; + } + + if (value < valueMin) { + value = valueMin; + } + + valueNow = value; + slider.sliderNode.setAttribute('aria-valuenow', value); + + offsetX = Math.round( + (valueNow * (this.railWidth - this.thumbWidth)) / (valueMax - valueMin) + ); + + pos = this.railX + offsetX; + + slider.thumbNode.setAttribute('x', pos); + slider.fillNode.setAttribute('width', offsetX + this.rectRadius); + + slider.valueNode.textContent = valueNow; + valueWidth = slider.valueNode.getBBox().width; + + pos = this.railX + offsetX - (valueWidth - this.thumbWidth) / 2; + slider.valueNode.setAttribute('x', pos); + + pos = this.railX + offsetX - (this.focusWidth - this.thumbWidth) / 2; + slider.focusNode.setAttribute('x', pos); + + this.updateColorBox(); + } + + onSliderKeyDown(event) { + var flag = false; + + var slider = this.getSlider(event.currentTarget); + + var valueMin = this.getValueMin(slider); + var valueNow = this.getValueNow(slider); + var valueMax = this.getValueMax(slider); + + switch (event.key) { + case 'Left': + case 'ArrowLeft': + case 'Down': + case 'ArrowDown': + this.moveSliderTo(slider, valueNow - 1); + flag = true; + break; + + case 'Right': + case 'ArrowRight': + case 'Up': + case 'ArrowUp': + this.moveSliderTo(slider, valueNow + 1); + flag = true; + break; + + case 'PageDown': + this.moveSliderTo(slider, valueNow - 10); + flag = true; + break; + + case 'PageUp': + this.moveSliderTo(slider, valueNow + 10); + flag = true; + break; + + case 'Home': + this.moveSliderTo(slider, valueMin); + flag = true; + break; + + case 'End': + this.moveSliderTo(slider, valueMax); + flag = true; + break; + + default: + break; + } + + if (flag) { + event.preventDefault(); + event.stopPropagation(); + } + } + + onThumbMouseDown(event) { + let slider = this.getSlider(event.currentTarget); + + var onMouseMove = function (event) { + let x = this.getSVGPoint(slider, event).x; + let min = this.getValueMin(slider); + let max = this.getValueMax(slider); + let diffX = x - this.railX; + let value = Math.round((diffX * (max - min)) / this.railWidth); + this.moveSliderTo(slider, value); + + event.preventDefault(); + event.stopPropagation(); + }.bind(this); + + var onMouseUp = function () { + document.removeEventListener('mousemove', onMouseMove); + document.removeEventListener('mouseup', onMouseUp); + }; + + // bind a mousemove event handler to move pointer + document.addEventListener('mousemove', onMouseMove); + + // bind a mouseup event handler to stop tracking mouse movements + document.addEventListener('mouseup', onMouseUp); + + event.preventDefault(); + event.stopPropagation(); + + // Set focus to the clicked on + slider.sliderNode.focus(); + } + + // handleMouseMove has the same functionality as we need for handleMouseClick on the rail + onRailClick(event) { + var slider = this.getSlider(event.currentTarget); + + var x = this.getSVGPoint(slider, event).x; + var min = this.getValueMin(slider); + var max = this.getValueMax(slider); + var diffX = x - this.railX; + var value = Math.round((diffX * (max - min)) / this.railWidth); + this.moveSliderTo(slider, value); + + event.preventDefault(); + event.stopPropagation(); + + // Set focus to the clicked handle + slider.sliderNode.focus(); + } + + onChangeClick(event, n) { + var slider = this.getSlider(event.currentTarget); + + var valueNow = this.getValueNow(slider); + + valueNow = valueNow + n; + this.moveSliderTo(slider, valueNow); + event.preventDefault(); + event.stopPropagation(); + } + + onDec10Click(event) { + this.onChangeClick(event, -10); + } + + onDecClick(event) { + this.onChangeClick(event, -1); + } + + onIncClick(event) { + this.onChangeClick(event, 1); + } + + onInc10Click(event) { + this.onChangeClick(event, 10); + } + + onFocus(event) { + let slider = this.getSlider(event.currentTarget); + slider.groupNode.classList.add('focus'); + } + + onBlur(event) { + let slider = this.getSlider(event.currentTarget); + slider.groupNode.classList.remove('focus'); + } + + getColorHex() { + var r = parseInt( + this.sliders.red.sliderNode.getAttribute('aria-valuenow') + ).toString(16); + var g = parseInt( + this.sliders.green.sliderNode.getAttribute('aria-valuenow') + ).toString(16); + var b = parseInt( + this.sliders.blue.sliderNode.getAttribute('aria-valuenow') + ).toString(16); + + if (r.length === 1) { + r = '0' + r; + } + if (g.length === 1) { + g = '0' + g; + } + if (b.length === 1) { + b = '0' + b; + } + + return '#' + r + g + b; + } + + getColorRGB() { + var r = this.sliders.red.sliderNode.getAttribute('aria-valuenow'); + var g = this.sliders.green.sliderNode.getAttribute('aria-valuenow'); + var b = this.sliders.blue.sliderNode.getAttribute('aria-valuenow'); + + return r + ', ' + g + ', ' + b; + } + + updateColorBox() { + if (this.colorBoxNode) { + this.colorBoxNode.style.backgroundColor = this.getColorHex(); + } + + if (this.colorValueHexNode) { + this.colorValueHexNode.value = this.getColorHex(); + } + + if (this.colorValueRGBNode) { + this.colorValueRGBNode.value = this.getColorRGB(); + } + } +} +// Initialize ColorViewerSliders on the page +window.addEventListener('load', function () { + var cps = document.querySelectorAll('.color-viewer-sliders'); + for (let i = 0; i < cps.length; i++) { + let s = new ColorViewerSliders(cps[i]); + s.init(); + } +}); diff --git a/examples/slider/slider-color-viewer-mobile.html b/examples/slider/slider-color-viewer-mobile.html new file mode 100644 index 0000000000..507c4169bd --- /dev/null +++ b/examples/slider/slider-color-viewer-mobile.html @@ -0,0 +1,497 @@ + + + + + + + Color Viewer Slider Example with Mobile Support| WAI-ARIA Authoring Practices 1.2 + + + + + + + + + + + + + + +
    +

    Color Viewer Slider Example with Mobile Support

    +

    + Following is an example of a color viewer that demonstrates the + slider design pattern. + Change the background of the color view box by adjusting the sliders for red, green, and blue values. + The HEX and RGB values of the chosen color are displayed by the color view box. +

    +

    Similar examples include:

    + +
    +
    +

    Example

    +
    + +
    +
    +

    Color Viewer

    +
    Red
    +
    + + + +
    + + 128 + + + + + +
    + + +
    + +
    Green
    +
    + + + +
    + + 128 + + + + + +
    + + +
    + +
    Blue
    +
    + + + +
    + + 128 + + + + + +
    + + +
    + +

    Color View Box

    +
    +
    + + +
    +
    +
    + +
    + +
    +

    Accessibility Features

    + +
    + +
    +

    Keyboard Support

    + +

    Slider

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    KeyFunction
    Right ArrowIncreases slider value one step.
    Up ArrowIncreases slider value one step.
    Left ArrowDecreases slider value one step.
    Down ArrowDecreases slider value one step.
    Page UpIncreases slider value multiple steps. In this slider, jumps ten steps.
    Page DownDecreases slider value multiple steps. In this slider, jumps ten steps.
    HomeSets slider to its minimum value.
    EndSets slider to its maximum value.
    + +

    Increase and Decrease Buttons

    + + + + + + + + + + + + + +
    KeyFunction
    Space, EnterChange the value of the slider by the value associated with the button.
    + +
    + +
    +

    Role, Property, State, and Tabindex Attributes

    +

    Slider

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    RoleAttributeElementUsage
    groupdiv +
      +
    • Identifies the div as a group of sliders.
    • +
    • The group provides a means to inform assistive technology users that the three sliders are all related to the single purpose of choosing a color.
    • +
    +
    aria-labelledby="IDREF"div +
      +
    • Contains the ID that reference the div element that provide the accessible name for the group.
    • +
    • One ID refers to the visible label Color Viewer.
    • +
    +
    + slider + + div + +
      +
    • Identifies the element as a slider.
    • +
    • Set on the div that represents as the movable thumb because it is the operable element that represents the slider value.
    • +
    +
    + tabindex=0 + + div + Includes the slider thumb in the page tab sequence.
    + aria-valuemax=255 + + div + Specifies the maximum value of the slider.
    + aria-valuemin=0 + + div + Specifies the minimum value of the slider.
    + aria-valuenow=NUMBER + + div + Indicates the current value of the slider.
    + aria-labelledby=IDREF + + div + Refers to the element containing the name of the slider.
    aria-label="NAME_STRING"buttonDefines the accessible name for each increase and decrease button (-10, -1, +1, and +10).
    tabindex="-1"buttonRemoves the decrease and increase buttons from the page Tab sequence while keeping them focusable so they can be accessed with touch-based assistive technologies.
    + +

    Increase and Decrease Buttons

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    RoleAttributeElementUsage
    tabindex=-1button +
      +
    • Removes the button from the tab sequence of the page.
    • +
    • The buttons are for touch screen support for changing the slider values by mobile assistive technologies and including the buttons in the tab order of the page is redundant with slider keyboard support.
    • +
    +
    aria-label=namebutton +
      +
    • Provide an accessible name for each button.
    • +
    • Accessible name includes the color and the size of the change to the slider value.
    • +
    +
    focusable=falsesvg + focusable=false fixes an Internet Explorer 11 issue that makes SVG images within button elements focusable=true by default. +
    + +
    + +
    +

    Javascript and CSS Source Code

    + +
    + +
    +

    HTML Source Code

    + +
    + + +
    + +
    + + + diff --git a/test/tests/slider_slider-color-viewer-mobile.js b/test/tests/slider_slider-color-viewer-mobile.js new file mode 100644 index 0000000000..2b8fcdbb93 --- /dev/null +++ b/test/tests/slider_slider-color-viewer-mobile.js @@ -0,0 +1,1383 @@ +const { ariaTest } = require('..'); +const { By, Key } = require('selenium-webdriver'); +const assertAttributeValues = require('../util/assertAttributeValues'); +const assertAriaLabelExists = require('../util/assertAriaLabelExists'); +const assertAriaLabelledby = require('../util/assertAriaLabelledby'); +const assertAriaRoles = require('../util/assertAriaRoles'); + +const exampleFile = 'slider/slider-color-viewer-mobile.html'; + +const ex = { + groupSelector: '#ex1 [role="group"]', + sliderSelector: '#ex1 [role="slider"]', + buttonSelector: '#ex1 .change', + buttonSVGSelector: '#ex1 .change svg', + redButtonSelectors: [ + '#ex1 .red .dec10', + '#ex1 .red .dec', + '#ex1 .red .inc', + '#ex1 .red .inc10', + ], + greenButtonSelectors: [ + '#ex1 .green .dec10', + '#ex1 .green .dec', + '#ex1 .green .inc', + '#ex1 .green .inc10', + ], + blueButtonSelectors: [ + '#ex1 .blue .dec10', + '#ex1 .blue .dec', + '#ex1 .blue .inc', + '#ex1 .blue .inc10', + ], + hexTextInput: '#ex1 .color-info .color-value-hex', + rgbTextInput: '#ex1 .color-info .color-value-rgb', + colorBox: '#ex1 .color-info .color-box', +}; + +const testDisplayMatchesValue = async function (t, rgbString) { + const rgbValue = await t.context.session + .findElement(By.css(ex.rgbTextInput)) + .getAttribute('value'); + const hexValue = await t.context.session + .findElement(By.css(ex.hexTextInput)) + .getAttribute('value'); + const boxColor = await t.context.session + .findElement(By.css(ex.colorBox)) + .getCssValue('background-color'); + + if (rgbValue !== rgbString) { + return ( + ex.rgbTextInput + + ' was not update, value is ' + + rgbValue + + ' but expected ' + + rgbString + ); + } + + if (boxColor !== 'rgb(' + rgbString + ')') { + return ( + 'Box color was not update, background-color is ' + + boxColor + + ' but expected ' + + 'rgb(' + + rgbString + + ')' + ); + } + + const rbgFromHexString = hexValue + .match(/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i) + .slice(1, 4) + .map((x) => parseInt(x, 16)) + .join(', '); + + if (rbgFromHexString !== rgbString) { + return ( + ex.hexTextInput + + ' was not update, value is ' + + rbgFromHexString + + ' but expected ' + + rgbString + ); + } + + return true; +}; + +const sendAllSlidersToEnd = async function (t) { + const sliders = await t.context.queryElements(t, ex.sliderSelector); + + for (let slider of sliders) { + await slider.sendKeys(Key.END); + } +}; + +const sendAllSlidersToBeginning = async function (t) { + const sliders = await t.context.queryElements(t, ex.sliderSelector); + + for (let slider of sliders) { + await slider.sendKeys(Key.HOME); + } +}; + +// Attributes + +ariaTest( + 'role="group" on div element', + exampleFile, + 'group-role', + async (t) => { + await assertAriaRoles(t, 'ex1', 'group', '1', 'div'); + } +); + +ariaTest( + '"aria-labelledby" set on group', + exampleFile, + 'aria-labelledby', + async (t) => { + await assertAriaLabelledby(t, ex.groupSelector); + } +); + +ariaTest( + 'role="slider" on div element', + exampleFile, + 'slider-role', + async (t) => { + await assertAriaRoles(t, 'ex1', 'slider', '3', 'div'); + } +); + +ariaTest( + '"tabindex" set to "0" on sliders', + exampleFile, + 'tabindex', + async (t) => { + await assertAttributeValues(t, ex.sliderSelector, 'tabindex', '0'); + } +); + +ariaTest( + '"aria-valuemax" set to "255" on sliders', + exampleFile, + 'aria-valuemax', + async (t) => { + await assertAttributeValues(t, ex.sliderSelector, 'aria-valuemax', '255'); + } +); + +ariaTest( + '"aria-valuemin" set to "0" on sliders', + exampleFile, + 'aria-valuemin', + async (t) => { + await assertAttributeValues(t, ex.sliderSelector, 'aria-valuemin', '0'); + } +); + +ariaTest( + '"aria-valuenow" reflects slider value', + exampleFile, + 'aria-valuenow', + async (t) => { + await assertAttributeValues(t, ex.sliderSelector, 'aria-valuenow', '128'); + } +); + +ariaTest( + '"aria-labelledby" set on sliders', + exampleFile, + 'aria-labelledby', + async (t) => { + await assertAriaLabelledby(t, ex.sliderSelector); + } +); + +ariaTest( + '"tabindex" set to "-1" on buttons', + exampleFile, + 'button-tabindex', + async (t) => { + await assertAttributeValues(t, ex.buttonSelector, 'tabindex', '-1'); + } +); + +ariaTest( + 'Test aria-label on buttons', + exampleFile, + 'button-aria-label', + async (t) => { + await assertAriaLabelExists(t, ex.buttonSelector); + } +); + +ariaTest( + '"focusable" set to "false" on svg image in buttons', + exampleFile, + 'button-svg-focusable', + async (t) => { + await assertAttributeValues(t, ex.buttonSVGSelector, 'focusable', 'false'); + } +); + +// Keys + +ariaTest( + 'Right arrow increases slider value by 1', + exampleFile, + 'key-right-arrow', + async (t) => { + const sliders = await t.context.queryElements(t, ex.sliderSelector); + + // Send 1 key to red slider + const redSlider = sliders[0]; + await redSlider.sendKeys(Key.ARROW_RIGHT); + + t.is( + await redSlider.getAttribute('aria-valuenow'), + '129', + 'After sending 1 arrow right key to the red slider, the value of the red slider should be 129' + ); + t.true( + await testDisplayMatchesValue(t, '129, 128, 128'), + 'Display should match rgb(129, 128, 128)' + ); + + // Send more than 255 keys to red slider + for (let i = 0; i < 260; i++) { + await redSlider.sendKeys(Key.ARROW_RIGHT); + } + + t.is( + await redSlider.getAttribute('aria-valuenow'), + '255', + 'After sending 260 arrow right key, the value of the red slider should be 255' + ); + t.true( + await testDisplayMatchesValue(t, '255, 128, 128'), + 'Display should match rgb(255, 128, 128)' + ); + + // Send 1 key to green slider + const greenSlider = sliders[1]; + await greenSlider.sendKeys(Key.ARROW_RIGHT); + + t.is( + await greenSlider.getAttribute('aria-valuenow'), + '129', + 'After sending 1 arrow right key to the green slider, the value of the green slider should be 129' + ); + t.true( + await testDisplayMatchesValue(t, '255, 129, 128'), + 'Display should match rgb(255, 129, 128)' + ); + + // Send more than 255 keys to green slider + for (let i = 0; i < 260; i++) { + await greenSlider.sendKeys(Key.ARROW_RIGHT); + } + + t.is( + await greenSlider.getAttribute('aria-valuenow'), + '255', + 'After sending 260 arrow right key, the value of the green slider should be 255' + ); + t.true( + await testDisplayMatchesValue(t, '255, 255, 128'), + 'Display should match rgb(255, 255, 128)' + ); + + // Send 1 key to blue slider + const blueSlider = sliders[2]; + await blueSlider.sendKeys(Key.ARROW_RIGHT); + + t.is( + await blueSlider.getAttribute('aria-valuenow'), + '129', + 'After sending 1 arrow right key to the blue slider, the value of the blue slider should be 129' + ); + t.true( + await testDisplayMatchesValue(t, '255, 255, 129'), + 'Display should match rgb(255, 255, 129)' + ); + + // Send more than 255 keys to blue slider + for (let i = 0; i < 260; i++) { + await blueSlider.sendKeys(Key.ARROW_RIGHT); + } + + t.is( + await blueSlider.getAttribute('aria-valuenow'), + '255', + 'After sending 260 arrow right key, the value of the blue slider should be 255' + ); + t.true( + await testDisplayMatchesValue(t, '255, 255, 255'), + 'Display should match rgb(255, 255, 255)' + ); + } +); + +ariaTest( + 'up arrow increases slider value by 1', + exampleFile, + 'key-up-arrow', + async (t) => { + const sliders = await t.context.queryElements(t, ex.sliderSelector); + + // Send 1 key to red slider + const redSlider = sliders[0]; + await redSlider.sendKeys(Key.ARROW_UP); + + t.is( + await redSlider.getAttribute('aria-valuenow'), + '129', + 'After sending 1 arrow up key to the red slider, the value of the red slider should be 129' + ); + t.true( + await testDisplayMatchesValue(t, '129, 128, 128'), + 'Display should match rgb(129, 128, 128)' + ); + + // Sen over 255 arrow up keys to the red slider + for (let i = 0; i < 260; i++) { + await redSlider.sendKeys(Key.ARROW_UP); + } + + t.is( + await redSlider.getAttribute('aria-valuenow'), + '255', + 'After sending 260 arrow up key, the value of the red slider should be 255' + ); + t.true( + await testDisplayMatchesValue(t, '255, 128, 128'), + 'Display should match rgb(255, 128, 128)' + ); + + // Send 1 key to green slider + const greenSlider = sliders[1]; + await greenSlider.sendKeys(Key.ARROW_UP); + + t.is( + await greenSlider.getAttribute('aria-valuenow'), + '129', + 'After sending 1 arrow up key to the blue slider, the value of the green slider should be 129' + ); + t.true( + await testDisplayMatchesValue(t, '255, 129, 128'), + 'Display should match rgb(255, 129, 128)' + ); + + // Send more than 255 keys to green slider + for (let i = 0; i < 260; i++) { + await greenSlider.sendKeys(Key.ARROW_UP); + } + + t.is( + await greenSlider.getAttribute('aria-valuenow'), + '255', + 'After sending 260 arrow up key, the value of the green slider should be 255' + ); + t.true( + await testDisplayMatchesValue(t, '255, 255, 128'), + 'Display should match rgb(255, 255, 128)' + ); + + // Send 1 key to blue slider + const blueSlider = sliders[2]; + await blueSlider.sendKeys(Key.ARROW_UP); + + t.is( + await blueSlider.getAttribute('aria-valuenow'), + '129', + 'After sending 1 arrow up key to the blue slider, the value of the blue slider should be 129' + ); + t.true( + await testDisplayMatchesValue(t, '255, 255, 129'), + 'Display should match rgb(255, 255, 129)' + ); + + // Send more than 255 keys to blue slider + for (let i = 0; i < 260; i++) { + await blueSlider.sendKeys(Key.ARROW_UP); + } + + t.is( + await blueSlider.getAttribute('aria-valuenow'), + '255', + 'After sending 260 arrow up key, the value of the blue slider should be 255' + ); + t.true( + await testDisplayMatchesValue(t, '255, 255, 255'), + 'Display should match rgb(255, 255, 255)' + ); + } +); + +ariaTest( + 'page up increases slider value by 10', + exampleFile, + 'key-page-up', + async (t) => { + const sliders = await t.context.queryElements(t, ex.sliderSelector); + + // Send 1 key to red slider + const redSlider = sliders[0]; + await redSlider.sendKeys(Key.PAGE_UP); + + t.is( + await redSlider.getAttribute('aria-valuenow'), + '138', + 'After sending 1 page up key to the red slider, the value of the red slider should be 138' + ); + t.true( + await testDisplayMatchesValue(t, '138, 128, 128'), + 'Display should match rgb(138, 128, 128)' + ); + + // Send over 26 page up keys to the red slider + for (let i = 0; i < 26; i++) { + await redSlider.sendKeys(Key.PAGE_UP); + } + + t.is( + await redSlider.getAttribute('aria-valuenow'), + '255', + 'After sending 26 page up key, the value of the red slider should be 255' + ); + t.true( + await testDisplayMatchesValue(t, '255, 128, 128'), + 'Display should match rgb(255, 128, 128)' + ); + + // Send 1 key to green slider + const greenSlider = sliders[1]; + await greenSlider.sendKeys(Key.PAGE_UP); + + t.is( + await greenSlider.getAttribute('aria-valuenow'), + '138', + 'After sending 1 page up key to the blue slider, the value of the green slider should be 138' + ); + t.true( + await testDisplayMatchesValue(t, '255, 138, 128'), + 'Display should match rgb(255, 138, 128)' + ); + + // Send more than 26 keys to green slider + for (let i = 0; i < 26; i++) { + await greenSlider.sendKeys(Key.PAGE_UP); + } + + t.is( + await greenSlider.getAttribute('aria-valuenow'), + '255', + 'After sending 260 page up key, the value of the green slider should be 255' + ); + t.true( + await testDisplayMatchesValue(t, '255, 255, 128'), + 'Display should match rgb(255, 255, 128)' + ); + + // Send 1 key to blue slider + const blueSlider = sliders[2]; + await blueSlider.sendKeys(Key.PAGE_UP); + + t.is( + await blueSlider.getAttribute('aria-valuenow'), + '138', + 'After sending 1 page up key to the blue slider, the value of the blue slider should be 138' + ); + t.true( + await testDisplayMatchesValue(t, '255, 255, 138'), + 'Display should match rgb(255, 255, 138)' + ); + + // Send more than 26 keys to blue slider + for (let i = 0; i < 26; i++) { + await blueSlider.sendKeys(Key.PAGE_UP); + } + + t.is( + await blueSlider.getAttribute('aria-valuenow'), + '255', + 'After sending 26 page up key, the value of the blue slider should be 255' + ); + t.true( + await testDisplayMatchesValue(t, '255, 255, 255'), + 'Display should match rgb(255, 255, 255)' + ); + } +); + +ariaTest( + 'key end set slider at max value', + exampleFile, + 'key-end', + async (t) => { + sendAllSlidersToBeginning(t); + + const sliders = await t.context.queryElements(t, ex.sliderSelector); + + // Send key end to red slider + const redSlider = sliders[0]; + await redSlider.sendKeys(Key.END); + + t.is( + await await redSlider.getAttribute('aria-valuenow'), + '255', + 'After sending 1 end key to the red slider, the value of the red slider should be 255' + ); + t.true( + await testDisplayMatchesValue(t, '255, 0, 0'), + 'Display should match rgb(255, 0, 0)' + ); + + // Send key end to green slider + const greenSlider = sliders[1]; + await greenSlider.sendKeys(Key.END); + + t.is( + await greenSlider.getAttribute('aria-valuenow'), + '255', + 'After sending 1 end key to the blue slider, the value of the green slider should be 255' + ); + t.true( + await testDisplayMatchesValue(t, '255, 255, 0'), + 'Display should match rgb(255, 255, 0)' + ); + + // Send key end to blue slider + const blueSlider = sliders[2]; + await blueSlider.sendKeys(Key.END); + + t.is( + await blueSlider.getAttribute('aria-valuenow'), + '255', + 'After sending 1 end key to the blue slider, the value of the blue slider should be 255' + ); + t.true( + await testDisplayMatchesValue(t, '255, 255, 255'), + 'Display should match rgb(255, 255, 255)' + ); + } +); + +ariaTest( + 'left arrow decreases slider value by 1', + exampleFile, + 'key-left-arrow', + async (t) => { + await sendAllSlidersToEnd(t); + + const sliders = await t.context.queryElements(t, ex.sliderSelector); + + // Send 1 key to red slider + const redSlider = sliders[0]; + await redSlider.sendKeys(Key.ARROW_LEFT); + + t.is( + await redSlider.getAttribute('aria-valuenow'), + '254', + 'After sending 1 arrow left key to the red slider, the value of the red slider should be 254' + ); + t.true( + await testDisplayMatchesValue(t, '254, 255, 255'), + 'Display should match rgb(254, 255, 255)' + ); + + // Send more than 255 keys to red slider + for (let i = 0; i < 260; i++) { + await redSlider.sendKeys(Key.ARROW_LEFT); + } + + t.is( + await redSlider.getAttribute('aria-valuenow'), + '0', + 'After sending 260 arrow left key, the value of the red slider should be 0' + ); + t.true( + await testDisplayMatchesValue(t, '0, 255, 255'), + 'Display should match rgb(0, 255, 255)' + ); + + // Send 1 key to green slider + const greenSlider = sliders[1]; + await greenSlider.sendKeys(Key.ARROW_LEFT); + + t.is( + await greenSlider.getAttribute('aria-valuenow'), + '254', + 'After sending 1 arrow left key to the blue slider, the value of the green slider should be 254' + ); + t.true( + await testDisplayMatchesValue(t, '0, 254, 255'), + 'Display should match rgb(0, 254, 255)' + ); + + // Send more than 255 keys to green slider + for (let i = 0; i < 260; i++) { + await greenSlider.sendKeys(Key.ARROW_LEFT); + } + + t.is( + await greenSlider.getAttribute('aria-valuenow'), + '0', + 'After sending 260 arrow left key, the value of the green slider should be 0' + ); + t.true( + await testDisplayMatchesValue(t, '0, 0, 255'), + 'Display should match rgb(0, 0, 255)' + ); + + // Send 1 key to blue slider + const blueSlider = sliders[2]; + await blueSlider.sendKeys(Key.ARROW_LEFT); + + t.is( + await blueSlider.getAttribute('aria-valuenow'), + '254', + 'After sending 1 arrow left key to the blue slider, the value of the blue slider should be 254' + ); + t.true( + await testDisplayMatchesValue(t, '0, 0, 254'), + 'Display should match rgb(0, 0, 254)' + ); + + // Send more than 255 keys to blue slider + for (let i = 0; i < 260; i++) { + await blueSlider.sendKeys(Key.ARROW_LEFT); + } + + t.is( + await blueSlider.getAttribute('aria-valuenow'), + '0', + 'After sending 260 arrow left key, the value of the blue slider should be 0' + ); + t.true( + await testDisplayMatchesValue(t, '0, 0, 0'), + 'Display should match rgb(0, 0, 0)' + ); + } +); + +ariaTest( + 'down arrow decreases slider value by 1', + exampleFile, + 'key-down-arrow', + async (t) => { + await sendAllSlidersToEnd(t); + + const sliders = await t.context.queryElements(t, ex.sliderSelector); + + // Send 1 key to red slider + const redSlider = sliders[0]; + await redSlider.sendKeys(Key.ARROW_DOWN); + + t.is( + await redSlider.getAttribute('aria-valuenow'), + '254', + 'After sending 1 arrow down key to the red slider, the value of the red slider should be 254' + ); + t.true( + await testDisplayMatchesValue(t, '254, 255, 255'), + 'Display should match rgb(254, 255, 255)' + ); + + // Send more than 255 keys to red slider + for (let i = 0; i < 260; i++) { + await redSlider.sendKeys(Key.ARROW_DOWN); + } + + t.is( + await redSlider.getAttribute('aria-valuenow'), + '0', + 'After sending 260 arrow down key, the value of the red slider should be 0' + ); + t.true( + await testDisplayMatchesValue(t, '0, 255, 255'), + 'Display should match rgb(0, 255, 255)' + ); + + // Send 1 key to green slider + const greenSlider = sliders[1]; + await greenSlider.sendKeys(Key.ARROW_DOWN); + + t.is( + await greenSlider.getAttribute('aria-valuenow'), + '254', + 'After sending 1 arrow down key to the blue slider, the value of the green slider should be 254' + ); + t.true( + await testDisplayMatchesValue(t, '0, 254, 255'), + 'Display should match rgb(0, 254, 255)' + ); + + // Send more than 255 keys to green slider + for (let i = 0; i < 260; i++) { + await greenSlider.sendKeys(Key.ARROW_DOWN); + } + + t.is( + await greenSlider.getAttribute('aria-valuenow'), + '0', + 'After sending 260 arrow down key, the value of the green slider should be 0' + ); + t.true( + await testDisplayMatchesValue(t, '0, 0, 255'), + 'Display should match rgb(0, 0, 255)' + ); + + // Send 1 key to blue slider + const blueSlider = sliders[2]; + await blueSlider.sendKeys(Key.ARROW_DOWN); + + t.is( + await blueSlider.getAttribute('aria-valuenow'), + '254', + 'After sending 1 arrow down key to the blue slider, the value of the blue slider should be 254' + ); + t.true( + await testDisplayMatchesValue(t, '0, 0, 254'), + 'Display should match rgb(0, 0, 254)' + ); + + // Send more than 255 keys to blue slider + for (let i = 0; i < 260; i++) { + await blueSlider.sendKeys(Key.ARROW_DOWN); + } + + t.is( + await blueSlider.getAttribute('aria-valuenow'), + '0', + 'After sending 260 arrow down key, the value of the blue slider should be 0' + ); + t.true( + await testDisplayMatchesValue(t, '0, 0, 0'), + 'Display should match rgb(0, 0, 0)' + ); + } +); + +ariaTest( + 'page down decreases slider value by 10', + exampleFile, + 'key-page-down', + async (t) => { + await sendAllSlidersToEnd(t); + + const sliders = await t.context.queryElements(t, ex.sliderSelector); + + // Send 1 key to red slider + const redSlider = sliders[0]; + await redSlider.sendKeys(Key.PAGE_DOWN); + + t.is( + await redSlider.getAttribute('aria-valuenow'), + '245', + 'After sending 1 page down key to the red slider, the value of the red slider should be 245' + ); + t.true( + await testDisplayMatchesValue(t, '245, 255, 255'), + 'Display should match rgb(245, 255, 255)' + ); + + // Send more than 25 keys to red slider + for (let i = 0; i < 26; i++) { + await redSlider.sendKeys(Key.PAGE_DOWN); + } + + t.is( + await redSlider.getAttribute('aria-valuenow'), + '0', + 'After sending 26 page down key, the value of the red slider should be 0' + ); + t.true( + await testDisplayMatchesValue(t, '0, 255, 255'), + 'Display should match rgb(0, 255, 255)' + ); + + // Send 1 key to green slider + const greenSlider = sliders[1]; + await greenSlider.sendKeys(Key.PAGE_DOWN); + + t.is( + await greenSlider.getAttribute('aria-valuenow'), + '245', + 'After sending 1 page down key to the blue slider, the value of the green slider should be 245' + ); + t.true( + await testDisplayMatchesValue(t, '0, 245, 255'), + 'Display should match rgb(0, 245, 255)' + ); + + // Send more than 25 keys to green slider + for (let i = 0; i < 26; i++) { + await greenSlider.sendKeys(Key.PAGE_DOWN); + } + + t.is( + await greenSlider.getAttribute('aria-valuenow'), + '0', + 'After sending 26 page down key, the value of the green slider should be 0' + ); + t.true( + await testDisplayMatchesValue(t, '0, 0, 255'), + 'Display should match rgb(0, 0, 255)' + ); + + // Send 1 key to blue slider + const blueSlider = sliders[2]; + await blueSlider.sendKeys(Key.PAGE_DOWN); + } +); + +// Test decrease by 10 buttons +ariaTest( + 'decrease 10 button changes slider value by 10', + exampleFile, + 'button-space-enter', + async (t) => { + await sendAllSlidersToEnd(t); + + const sliders = await t.context.queryElements(t, ex.sliderSelector); + const redDec10Button = await t.context.queryElement( + t, + ex.redButtonSelectors[0] + ); + const greenDec10Button = await t.context.queryElement( + t, + ex.greenButtonSelectors[0] + ); + const blueDec10Button = await t.context.queryElement( + t, + ex.blueButtonSelectors[0] + ); + + // Click on the red dec 10 button + const redSlider = sliders[0]; + await redDec10Button.click(); + + t.is( + await redSlider.getAttribute('aria-valuenow'), + '245', + 'After clicking on dec 10 button, the value of the red slider should be 245' + ); + t.true( + await testDisplayMatchesValue(t, '245, 255, 255'), + 'Display should match rgb(245, 255, 255)' + ); + + // Send more than 25 clicks to red dec 10 button + for (let i = 0; i < 26; i++) { + await redDec10Button.click(); + } + + t.is( + await redSlider.getAttribute('aria-valuenow'), + '0', + 'After sending 26 clicks, the value of the red slider should be 0' + ); + t.true( + await testDisplayMatchesValue(t, '0, 255, 255'), + 'Display should match rgb(0, 255, 255)' + ); + + // Send 1 click to green dec 10 button + const greenSlider = sliders[1]; + await greenDec10Button.click(); + + t.is( + await greenSlider.getAttribute('aria-valuenow'), + '245', + 'After sending 1 click to the blue dec 10 button, the value of the green slider should be 245' + ); + t.true( + await testDisplayMatchesValue(t, '0, 245, 255'), + 'Display should match rgb(0, 245, 255)' + ); + + // Send more than 25 click to green dec 10 button + for (let i = 0; i < 26; i++) { + await greenDec10Button.click(); + } + + t.is( + await greenSlider.getAttribute('aria-valuenow'), + '0', + 'After sending 26 clicks, the value of the green slider should be 0' + ); + t.true( + await testDisplayMatchesValue(t, '0, 0, 255'), + 'Display should match rgb(0, 0, 255)' + ); + + // Click on the red dec 10 button + const blueSlider = sliders[2]; + await blueDec10Button.click(); + + t.is( + await blueSlider.getAttribute('aria-valuenow'), + '245', + 'After clicking on dec 10 button, the value of the blue slider should be 245' + ); + t.true( + await testDisplayMatchesValue(t, '0, 0, 245'), + 'Display should match rgb(0, 0, 245)' + ); + + // Send more than 25 clicks to blue dec 10 button + for (let i = 0; i < 26; i++) { + await blueDec10Button.click(); + } + + t.is( + await blueSlider.getAttribute('aria-valuenow'), + '0', + 'After sending 26 clicks, the value of the blue slider should be 0' + ); + t.true( + await testDisplayMatchesValue(t, '0, 0, 0'), + 'Display should match rgb(0, 0, 0)' + ); + } +); + +// Test decrease by one buttons +ariaTest( + 'decrease by one button changes slider value by 1', + exampleFile, + 'button-space-enter', + async (t) => { + const sliders = await t.context.queryElements(t, ex.sliderSelector); + const redDecButton = await t.context.queryElement( + t, + ex.redButtonSelectors[1] + ); + const greenDecButton = await t.context.queryElement( + t, + ex.greenButtonSelectors[1] + ); + const blueDecButton = await t.context.queryElement( + t, + ex.blueButtonSelectors[1] + ); + + // Click on the red dec one button + const redSlider = sliders[0]; + await redDecButton.click(); + + t.is( + await redSlider.getAttribute('aria-valuenow'), + '127', + 'After clicking on dec one button, the value of the red slider should be 127' + ); + t.true( + await testDisplayMatchesValue(t, '127, 128, 128'), + 'Display should match rgb(127, 128, 128)' + ); + + // Send 1 click to green dec one button + const greenSlider = sliders[1]; + await greenDecButton.click(); + + t.is( + await greenSlider.getAttribute('aria-valuenow'), + '127', + 'After sending 1 click to the blue dec one button, the value of the green slider should be 127' + ); + t.true( + await testDisplayMatchesValue(t, '127, 127, 128'), + 'Display should match rgb(127, 127, 128)' + ); + + // Click on the red dec one button + const blueSlider = sliders[2]; + await blueDecButton.click(); + + t.is( + await blueSlider.getAttribute('aria-valuenow'), + '127', + 'After clicking on dec one button, the value of the blue slider should be 127' + ); + t.true( + await testDisplayMatchesValue(t, '127, 127, 127'), + 'Display should match rgb(127, 127, 127)' + ); + + await sendAllSlidersToEnd(t); + + // Click on the red dec one button + await redDecButton.click(); + + t.is( + await redSlider.getAttribute('aria-valuenow'), + '254', + 'After clicking on dec one button, the value of the red slider should be 254' + ); + t.true( + await testDisplayMatchesValue(t, '254, 255, 255'), + 'Display should match rgb(254, 255, 255)' + ); + + // Send 1 click to green dec one button + await greenDecButton.click(); + + t.is( + await greenSlider.getAttribute('aria-valuenow'), + '254', + 'After sending 1 click to the blue dec one button, the value of the green slider should be 254' + ); + t.true( + await testDisplayMatchesValue(t, '254, 254, 255'), + 'Display should match rgb(254, 254, 255)' + ); + + // Click on the red dec one button + await blueDecButton.click(); + + t.is( + await blueSlider.getAttribute('aria-valuenow'), + '254', + 'After clicking on dec one button, the value of the blue slider should be 254' + ); + t.true( + await testDisplayMatchesValue(t, '254, 254, 254'), + 'Display should match rgb(254, 254, 254)' + ); + + await sendAllSlidersToBeginning(t); + + // Click on the red dec one button + await redDecButton.click(); + + t.is( + await redSlider.getAttribute('aria-valuenow'), + '0', + 'After clicking on dec one button, the value of the red slider should be 0' + ); + t.true( + await testDisplayMatchesValue(t, '0, 0, 0'), + 'Display should match rgb(0, 0, 0)' + ); + + // Send 1 click to green dec one button + await greenDecButton.click(); + + t.is( + await greenSlider.getAttribute('aria-valuenow'), + '0', + 'After sending 1 click to the blue dec one button, the value of the green slider should be 0' + ); + t.true( + await testDisplayMatchesValue(t, '0, 0, 0'), + 'Display should match rgb(0, 0, 0)' + ); + + // Click on the red dec one button + await blueDecButton.click(); + + t.is( + await blueSlider.getAttribute('aria-valuenow'), + '0', + 'After clicking on dec one button, the value of the blue slider should be 0' + ); + t.true( + await testDisplayMatchesValue(t, '0, 0, 0'), + 'Display should match rgb(0, 0, 0)' + ); + } +); + +// Test increase by one buttons +ariaTest( + 'increase by one button changes slider value by 1', + exampleFile, + 'button-space-enter', + async (t) => { + const sliders = await t.context.queryElements(t, ex.sliderSelector); + const redIncButton = await t.context.queryElement( + t, + ex.redButtonSelectors[2] + ); + const greenIncButton = await t.context.queryElement( + t, + ex.greenButtonSelectors[2] + ); + const blueIncButton = await t.context.queryElement( + t, + ex.blueButtonSelectors[2] + ); + + // Click on the red inc one button + const redSlider = sliders[0]; + await redIncButton.click(); + + t.is( + await redSlider.getAttribute('aria-valuenow'), + '129', + 'After clicking on inc one button, the value of the red slider should be 129' + ); + t.true( + await testDisplayMatchesValue(t, '129, 128, 128'), + 'Display should match rgb(129, 128, 128)' + ); + + // Send 1 click to green inc one button + const greenSlider = sliders[1]; + await greenIncButton.click(); + + t.is( + await greenSlider.getAttribute('aria-valuenow'), + '129', + 'After sending 1 click to the blue inc one button, the value of the green slider should be 129' + ); + t.true( + await testDisplayMatchesValue(t, '129, 129, 128'), + 'Display should match rgb(129, 129, 128)' + ); + + // Click on the red inc one button + const blueSlider = sliders[2]; + await blueIncButton.click(); + + t.is( + await blueSlider.getAttribute('aria-valuenow'), + '129', + 'After clicking on inc one button, the value of the blue slider should be 129' + ); + t.true( + await testDisplayMatchesValue(t, '129, 129, 129'), + 'Display should match rgb(129, 129, 129)' + ); + + await sendAllSlidersToBeginning(t); + + // Click on the red inc one button + await redIncButton.click(); + + t.is( + await redSlider.getAttribute('aria-valuenow'), + '1', + 'After clicking on inc one button, the value of the red slider should be 1' + ); + t.true( + await testDisplayMatchesValue(t, '1, 0, 0'), + 'Display should match rgb(1, 0, 0)' + ); + + // Send 1 click to green dec one button + await greenIncButton.click(); + + t.is( + await greenSlider.getAttribute('aria-valuenow'), + '1', + 'After sending 1 click to the blue inc one button, the value of the green slider should be 1' + ); + t.true( + await testDisplayMatchesValue(t, '1, 1, 0'), + 'Display should match rgb(1, 1, 0)' + ); + + // Click on the red dec 10 button + await blueIncButton.click(); + + t.is( + await blueSlider.getAttribute('aria-valuenow'), + '1', + 'After clicking on inc one button, the value of the blue slider should be 1' + ); + t.true( + await testDisplayMatchesValue(t, '1, 1, 1'), + 'Display should match rgb(1, 1, 1)' + ); + + await sendAllSlidersToEnd(t); + + // Click on the red inc one button + await redIncButton.click(); + + t.is( + await redSlider.getAttribute('aria-valuenow'), + '255', + 'After clicking on inc one button, the value of the red slider should be 255' + ); + t.true( + await testDisplayMatchesValue(t, '255, 255, 255'), + 'Display should match rgb(255, 255, 255)' + ); + + // Send 1 click to green dec one button + await greenIncButton.click(); + + t.is( + await greenSlider.getAttribute('aria-valuenow'), + '255', + 'After sending 1 click to the blue inc one button, the value of the green slider should be 255' + ); + t.true( + await testDisplayMatchesValue(t, '255, 255, 255'), + 'Display should match rgb(255, 255, 255)' + ); + + // Click on the red dec 10 button + await blueIncButton.click(); + + t.is( + await blueSlider.getAttribute('aria-valuenow'), + '255', + 'After clicking on inc one button, the value of the blue slider should be 255' + ); + t.true( + await testDisplayMatchesValue(t, '255, 255, 255'), + 'Display should match rgb(255, 255, 255)' + ); + } +); + +// Test increase by 10 buttons +ariaTest( + 'increase 10 button changes slider value by 10', + exampleFile, + 'button-space-enter', + async (t) => { + await sendAllSlidersToBeginning(t); + + const sliders = await t.context.queryElements(t, ex.sliderSelector); + const redInc10Button = await t.context.queryElement( + t, + ex.redButtonSelectors[3] + ); + const greenInc10Button = await t.context.queryElement( + t, + ex.greenButtonSelectors[3] + ); + const blueInc10Button = await t.context.queryElement( + t, + ex.blueButtonSelectors[3] + ); + + // Click on the red dec 10 button + const redSlider = sliders[0]; + await redInc10Button.click(); + + t.is( + await redSlider.getAttribute('aria-valuenow'), + '10', + 'After clicking on inc 10 button, the value of the red slider should be 10' + ); + t.true( + await testDisplayMatchesValue(t, '10, 0, 0'), + 'Display should match rgb(10, 0, 0)' + ); + + // Send more than 25 clicks to red inc 10 button + for (let i = 0; i < 26; i++) { + await redInc10Button.click(); + } + + t.is( + await redSlider.getAttribute('aria-valuenow'), + '255', + 'After sending 26 clicks, the value of the red slider should be 255' + ); + t.true( + await testDisplayMatchesValue(t, '255, 0, 0'), + 'Display should match rgb(255, 0, 0)' + ); + + // Send 1 click to green inc 10 button + const greenSlider = sliders[1]; + await greenInc10Button.click(); + + t.is( + await greenSlider.getAttribute('aria-valuenow'), + '10', + 'After sending 1 click to the blue inc 10 button, the value of the green slider should be 10' + ); + t.true( + await testDisplayMatchesValue(t, '255, 10, 0'), + 'Display should match rgb(255, 10, 0)' + ); + + // Send more than 25 click to green inc 10 button + for (let i = 0; i < 26; i++) { + await greenInc10Button.click(); + } + + t.is( + await greenSlider.getAttribute('aria-valuenow'), + '255', + 'After sending 26 clicks, the value of the green slider should be 255' + ); + t.true( + await testDisplayMatchesValue(t, '255, 255, 0'), + 'Display should match rgb(255, 255, 0)' + ); + + // Click on the red inc 10 button + const blueSlider = sliders[2]; + await blueInc10Button.click(); + + t.is( + await blueSlider.getAttribute('aria-valuenow'), + '10', + 'After clicking on inc 10 button, the value of the blue slider should be 10' + ); + t.true( + await testDisplayMatchesValue(t, '255, 255, 10'), + 'Display should match rgb(255, 255, 10)' + ); + + // Send more than 25 clicks to blue dec 10 button + for (let i = 0; i < 26; i++) { + await blueInc10Button.click(); + } + + t.is( + await blueSlider.getAttribute('aria-valuenow'), + '255', + 'After sending 26 clicks, the value of the blue slider should be 255' + ); + t.true( + await testDisplayMatchesValue(t, '255, 255, 255'), + 'Display should match rgb(255, 255, 255)' + ); + } +); + +ariaTest( + 'home set slider value to minimum', + exampleFile, + 'key-home', + async (t) => { + const sliders = await t.context.queryElements(t, ex.sliderSelector); + + await sendAllSlidersToEnd(t); + + // Send key end to red slider + const redSlider = sliders[0]; + await redSlider.sendKeys(Key.HOME); + + t.is( + await redSlider.getAttribute('aria-valuenow'), + '0', + 'After sending 1 home key to the red slider, the value of the red slider should be 0' + ); + t.true( + await testDisplayMatchesValue(t, '0, 255, 255'), + 'Display should match rgb(0, 255, 255)' + ); + + // Send key home to green slider + const greenSlider = sliders[1]; + await greenSlider.sendKeys(Key.HOME); + + t.is( + await greenSlider.getAttribute('aria-valuenow'), + '0', + 'After sending 1 home key to the blue slider, the value of the green slider should be 0' + ); + t.true( + await testDisplayMatchesValue(t, '0, 0, 255'), + 'Display should match rgb(0, 0, 255)' + ); + + // Send key home to blue slider + const blueSlider = sliders[2]; + await blueSlider.sendKeys(Key.HOME); + + t.is( + await blueSlider.getAttribute('aria-valuenow'), + '0', + 'After sending 1 home key to the blue slider, the value of the blue slider should be 0' + ); + t.true( + await testDisplayMatchesValue(t, '0, 0, 0'), + 'Display should match rgb(0, 0, 0)' + ); + } +);