Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions elements/howto-slider/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
## Summary {: #summary }

A `<howto-slider>` represents a slider in a form. The slider allows user
to select a value from a range of value.

The element attempts to self apply the attributes `role="slider"` and
`tabindex="0"` when it is first created. The `role` attribute helps assistive
technology like a screen reader tell the user what kind of control this is.
The `tabindex` attribute opts the element into the tab order, making it keyboard
focusable and operable. To learn more about these two topics, check out
[What can ARIA do?][what-aria] and [Using tabindex][using-tabindex].

When the slider is moved, it sets `value` attribute. In addition,
the element sets an `aria-valuenow` attribute to corresponding value. Clicking on
the slider will change its value based on the distance in which it is clicked.
Also, on pressing `right` / `up` arrow key the value is increased by `1`.
On pressing `left` / `down` arrow key the value is decreased by `1` respectively.
You can also press `pageUp` / `pageDown` to increase / decrease the value by `10`
respectively. This also gives an option to press `home` / `end` button to go to
`minimum` and `maximum` values respectively.

Warning: Just because you _can_ build a custom element slider, doesn't
necessarily mean that you _should_. As this example shows, you will need to add
your own keyboard, labeling, and ARIA support. It's also important to note that
the native `<form>` element will NOT submit values from a custom element. You
will need to wire that up yourself using AJAX or a hidden `<input>` field.

## Reference {: #reference }

- [Checkbox Pattern in ARIA Authoring Practices 1.1][checkbox-pattern]
- [What can ARIA Do?][what-aria]
- [Using tabindex][using-tabindex]

[checkbox-pattern]: https://www.w3.org/TR/wai-aria-practices/examples/slider/slider-1.html
[what-aria]: https://developers.google.com/web/fundamentals/accessibility/semantics-aria/#what_can_aria_do
[using-tabindex]: https://developers.google.com/web/fundamentals/accessibility/focus/using-tabindex
14 changes: 14 additions & 0 deletions elements/howto-slider/demo.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!--
Copyright 2017 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

<howto-slider></howto-slider>
194 changes: 194 additions & 0 deletions elements/howto-slider/howto-slider.e2etest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
/**
* Copyright 2017 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint max-len: ["off"] */

const helper = require('../../tools/selenium-helper.js');
const expect = require('chai').expect;
const {Key} = require('selenium-webdriver');

describe('howto-slider', function() {
let success;
let value;
const findSlider = _ => {
window.expectedSlider = document.querySelector('[role=slider]');
return window.expectedSlider;
};

const initialize = value => {
window.expectedSlider.setAttribute('aria-valuenow', `${value}`);
window.expectedSlider.value = value;
return window.expectedSlider.getAttribute('aria-valuenow') === `${value}`;
};

const isInitialized = _ => {
let isAriaValuenow = window.expectedSlider.getAttribute('aria-valuenow') === '0';
return isAriaValuenow && window.expectedSlider.value === 0;
};

const isChanged = value => {
let isAriaValuenow = window.expectedSlider.getAttribute('aria-valuenow') === `${value}`;
return isAriaValuenow && window.expectedSlider.value === value;
};

beforeEach(function() {
value = 0;
return this.driver.get(`${this.address}/howto-slider/demo.html`)
.then(_ => helper.waitForElement(this.driver, 'howto-slider'));
});

it('should increase the value by 1 on [arrow-up]', async function() {
await this.driver.executeScript(findSlider);
success = await helper.pressKeyUntil(this.driver, Key.TAB, _ => document.activeElement === window.expectedSlider);
expect(success).to.be.true;
success = await this.driver.executeScript(isInitialized);
expect(success).to.be.true;
await this.driver.actions().sendKeys(Key.ARROW_UP).perform();
success = await this.driver.executeScript(isChanged, value+1);
expect(success).to.be.true;
});

it('should increase the value by 1 on [arrow-right]', async function() {
await this.driver.executeScript(findSlider);
success = await helper.pressKeyUntil(this.driver, Key.TAB, _ => document.activeElement === window.expectedSlider);
expect(success).to.be.true;
success = await this.driver.executeScript(isInitialized);
expect(success).to.be.true;
await this.driver.actions().sendKeys(Key.ARROW_RIGHT).perform();
success = await this.driver.executeScript(isChanged, value+1);
expect(success).to.be.true;
});

it('should increase the value by 10 on [page-up]', async function() {
await this.driver.executeScript(findSlider);
success = await helper.pressKeyUntil(this.driver, Key.TAB, _ => document.activeElement === window.expectedSlider);
expect(success).to.be.true;
success = await this.driver.executeScript(isInitialized);
expect(success).to.be.true;
await this.driver.actions().sendKeys(Key.PAGE_UP).perform();
success = await this.driver.executeScript(isChanged, value+10);
expect(success).to.be.true;
});

it('should decrease the value by 1 on [arrow-left]', async function() {
await this.driver.executeScript(findSlider);
success = await helper.pressKeyUntil(this.driver, Key.TAB, _ => document.activeElement === window.expectedSlider);
expect(success).to.be.true;
value = 10;
success = await this.driver.executeScript(initialize, value);
expect(success).to.be.true;
await this.driver.actions().sendKeys(Key.ARROW_LEFT).perform();
success = await this.driver.executeScript(isChanged, value-1);
expect(success).to.be.true;
});

it('should decrease the value by 1 on [arrow-down]', async function() {
await this.driver.executeScript(findSlider);
success = await helper.pressKeyUntil(this.driver, Key.TAB, _ => document.activeElement === window.expectedSlider);
expect(success).to.be.true;
value = 10;
success = await this.driver.executeScript(initialize, value);
expect(success).to.be.true;
await this.driver.actions().sendKeys(Key.ARROW_DOWN).perform();
success = await this.driver.executeScript(isChanged, value-1);
expect(success).to.be.true;
});

it('should decrease the value by 10 on [page-down]', async function() {
await this.driver.executeScript(findSlider);
success = await helper.pressKeyUntil(this.driver, Key.TAB, _ => document.activeElement === window.expectedSlider);
expect(success).to.be.true;
value = 50;
success = await this.driver.executeScript(initialize, value);
expect(success).to.be.true;
await this.driver.actions().sendKeys(Key.PAGE_DOWN).perform();
success = await this.driver.executeScript(isChanged, value-10);
expect(success).to.be.true;
});

it('should set the value to minimum on [home]', async function() {
await this.driver.executeScript(findSlider);
success = await helper.pressKeyUntil(this.driver, Key.TAB, _ => document.activeElement === window.expectedSlider);
expect(success).to.be.true;
value = 99;
success = await this.driver.executeScript(initialize, value);
expect(success).to.be.true;
await this.driver.actions().sendKeys(Key.HOME).perform();
success = await this.driver.executeScript(isChanged, 0);
expect(success).to.be.true;
});

it('should set the value to maximum on [end]', async function() {
await this.driver.executeScript(findSlider);
success = await helper.pressKeyUntil(this.driver, Key.TAB, _ => document.activeElement === window.expectedSlider);
expect(success).to.be.true;
value = 10;
success = await this.driver.executeScript(initialize, value);
expect(success).to.be.true;
await this.driver.actions().sendKeys(Key.END).perform();
success = await this.driver.executeScript(isChanged, 100);
expect(success).to.be.true;
});

it('should set the value on mousemove', async function() {
const slider = await this.driver.executeScript(findSlider);
success = await helper.pressKeyUntil(this.driver, Key.TAB, _ => document.activeElement === window.expectedSlider);
expect(success).to.be.true;
value = 10;
success = await this.driver.executeScript(initialize, value);
expect(success).to.be.true;
await this.driver.actions().mouseDown(slider).perform();
await this.driver.actions().mouseMove(slider, {x: 100, y: 0}).perform();
await this.driver.actions().mouseUp(slider).perform();
success = await this.driver.executeScript(isChanged, 100);
expect(success).to.be.true;
});
});

describe('howto-slider pre-upgrade', function() {
let success;

beforeEach(function() {
return this.driver.get(`${this.address}/howto-slider/demo.html?nojs`);
});

it('should handle attributes set before upgrade', async function() {
await this.driver.executeScript(_ => window.expectedSlider = document.querySelector('howto-slider'));
await this.driver.executeScript(_ => window.expectedSlider.setAttribute('value', 0));

await this.driver.executeScript(_ => _loadJavaScript());
await helper.waitForElement(this.driver, 'howto-slider');
success = await this.driver.executeScript(_ =>
window.expectedSlider.value === 0 &&
window.expectedSlider.getAttribute('aria-valuenow') === '0'
);
expect(success).to.be.true;
});

it('should handle instance properties set before upgrade', async function() {
await this.driver.executeScript(_ => window.expectedSlider = document.querySelector('howto-slider'));
await this.driver.executeScript(_ => window.expectedSlider.value = 0);

await this.driver.executeScript(_ => _loadJavaScript());
await helper.waitForElement(this.driver, 'howto-slider');
success = await this.driver.executeScript(_ =>
window.expectedSlider.hasAttribute('value') &&
window.expectedSlider.hasAttribute('aria-valuenow') &&
window.expectedSlider.getAttribute('aria-valuenow') === '0'
);
expect(success).to.be.true;
});
});

Loading