Skip to content

Commit

Permalink
ARIA 1.0 Combobox Examples: Integrate 3 New Examples (pull #508)
Browse files Browse the repository at this point in the history
For issue #99, integrate contributions from @jongund and @mcking65.

Adds three ARIA 1.0 style combobox examples,
Each demonstrating a different type of autocomplete:
1. List with inline autocomplete
2. List with manual selection
3. No autocomplete.

Also includes:

* Update links from the design pattern.
* Updates to links from the ARIA 1.1 combobox example pages.
* Editorial revisions across all 5 combobox example pages for editorial consistency.
  • Loading branch information
mcking65 authored Oct 31, 2017
1 parent a27e757 commit dd56ad2
Show file tree
Hide file tree
Showing 13 changed files with 2,973 additions and 1,411 deletions.
6 changes: 4 additions & 2 deletions aria-practices.html
Original file line number Diff line number Diff line change
Expand Up @@ -636,9 +636,11 @@ <h3>Combo Box</h3>
<section class="notoc">
<h4>Examples</h4>
<ul>
<li><a href="examples/combobox/aria1.1pattern/listbox-combo.html">Examples of ARIA 1.1 Combobox with Listbox Popup</a>: Comboboxes that demonstrate the various forms of autocomplete behavior using a listbox popup.</li>
<li><a href="examples/combobox/aria1.1pattern/listbox-combo.html">Examples of ARIA 1.1 Combobox with Listbox Popup</a>: Comboboxes that demonstrate the various forms of autocomplete behavior using a listbox popup and use the ARIA 1.1 implementation pattern.</li>
<li><a href="examples/combobox/aria1.1pattern/grid-combo.html">Example of ARIA 1.1 Combobox with Grid Popup</a>: A combobox that presents suggestions in a grid, enabling users to navigate descriptive information about each suggestion.</li>
<li><a href="examples/combobox/aria1.0pattern/combobox-autocomplete-list.html">ARIA 1.0 Combobox with List Autocomplete</a></li>
<li><a href="examples/combobox/aria1.0pattern/combobox-autocomplete-both.html">ARIA 1.0 Combobox with Both List and Inline Autocomplete</a>: A combobox that demonstrates the autocomplete behavior known as <q>list with inline autocomplete</q> and uses the ARIA 1.0 implementation pattern.</li>
<li><a href="examples/combobox/aria1.0pattern/combobox-autocomplete-list.html">ARIA 1.0 Combobox with List Autocomplete</a>: A combobox that demonstrates the autocomplete behavior known as <q>list with manual selection</q> and uses the ARIA 1.0 implementation pattern.</li>
<li><a href="examples/combobox/aria1.0pattern/combobox-autocomplete-none.html">ARIA 1.0 Combobox Without Autocomplete</a>: A combo box that demonstrates the behavior associated with <code>aria-autocomplete=none</code> and uses the ARIA 1.0 implementation pattern.</li>
</ul>
</section>

Expand Down
608 changes: 438 additions & 170 deletions examples/combobox/aria1.0pattern/combobox-autocomplete-both.html

Large diffs are not rendered by default.

599 changes: 429 additions & 170 deletions examples/combobox/aria1.0pattern/combobox-autocomplete-list.html

Large diffs are not rendered by default.

544 changes: 376 additions & 168 deletions examples/combobox/aria1.0pattern/combobox-autocomplete-none.html

Large diffs are not rendered by default.

27 changes: 27 additions & 0 deletions examples/combobox/aria1.0pattern/css/combobox-1.0.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,30 @@
font-style: italic;
color: #366ED4;
}

.combobox-list {
position: relative;
}

.combobox-inline label,
.combobox-list label {
margin: 0;
padding: 0;
display: block;
}

.combobox-list .group input,
.combobox-list .group button {
display: inline;
}

.combobox-list .group button {
margin: 0;
padding: 0 0.125em 0 0.125em;
position: relative;
top: 1px;
left: -2px;
font-size: 85%;
background-color: #eee;
}

43 changes: 43 additions & 0 deletions examples/combobox/aria1.0pattern/css/listbox.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@

ul[role="listbox"] {
margin: 0;
padding: 0;
position: absolute;
top: 3em;
list-style: none;
background-color: #EEEEEE;
display: none;
border: thin #333 solid;
height: 12em;
width: 12em;
overflow: scroll;
}

ul[role="listbox"] li[role="option"]{
display: block;
margin: 0.25em;
padding: 0;
background-color: #EEEEEE;
font-size: 100%;
}

/* focus and hover styling */

button:focus,
button:hover,
input:focus,
input:hover {
outline: 2px solid black;
}

input:focus,
input:hover {
background-color: #EEEEEE;
}

ul[role="listbox"] li[role="option"].focus,
ul[role="listbox"] li[role="option"]:hover{
background-color: black;
color: white;
}

278 changes: 278 additions & 0 deletions examples/combobox/aria1.0pattern/js/combobox-1.0-list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
/*
* This content is licensed according to the W3C Software License at
* https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
*/
var ComboboxList = function (domNode) {

this.domNode = domNode;
this.listbox = false;
this.option = false;

this.hasFocus = false;
this.hasHover = false;
this.filter = '';

this.keyCode = Object.freeze({
'BACKSPACE': 8,
'TAB': 9,
'RETURN': 13,
'ESC': 27,
'SPACE': 32,
'PAGEUP': 33,
'PAGEDOWN': 34,
'END': 35,
'HOME': 36,
'LEFT': 37,
'UP': 38,
'RIGHT': 39,
'DOWN': 40
});
};

ComboboxList.prototype.init = function () {

this.domNode.setAttribute('aria-haspopup', 'true');

this.autocomplete = this.domNode.getAttribute('aria-autocomplete');

if (this.autocomplete) {
this.autocomplete = this.autocomplete.toLowerCase();
}

this.domNode.addEventListener('keydown', this.handleKeydown.bind(this));
this.domNode.addEventListener('keyup', this.handleKeyup.bind(this));
this.domNode.addEventListener('click', this.handleClick.bind(this));
this.domNode.addEventListener('focus', this.handleFocus.bind(this));
this.domNode.addEventListener('blur', this.handleBlur.bind(this));

// initialize pop up menus

var listbox = document.getElementById(this.domNode.getAttribute('aria-owns'));

if (listbox) {
this.listbox = new Listbox(listbox, this);
this.listbox.init();
}

// Open Button

var button = this.domNode.nextElementSibling;

if (button && button.tagName === 'BUTTON') {
button.addEventListener('click', this.handleButtonClick.bind(this));
}

};

ComboboxList.prototype.updateValue = function () {
if (this.autocomplete === 'both') {

if (this.filter.length && this.option && this.listbox.isOpen()) {
this.domNode.value = this.option.textContent;
this.domNode.setSelectionRange(this.filter.length,this.filter.length);
}
else {
this.domNode.value = this.filter;
}
}
};

ComboboxList.prototype.setValue = function (value) {
this.filter = value;
this.domNode.value = this.filter;
this.domNode.setSelectionRange(this.filter.length,this.filter.length);
if (this.autocomplete !== 'none') {
this.listbox.filterOptions(this.filter, this.option);
}
};

ComboboxList.prototype.setOption = function (option) {
if (option) {
this.option = option;
this.listbox.setFocusStyle(this.option);
this.updateValue();
}
};

/* Event Handlers */

ComboboxList.prototype.handleKeydown = function (event) {
var tgt = event.currentTarget,
flag = false,
char = event.key,
shiftKey = event.shiftKey,
ctrlKey = event.ctrlKey,
altKey = event.altKey;

switch (event.keyCode) {

case this.keyCode.RETURN:
if (this.option) {
this.setValue(this.option.textContent);
this.listbox.close(true);
}
flag = true;
break;

case this.keyCode.DOWN:

if (this.listbox.hasOptions()) {
if (this.listbox.isOpen()) {
this.setOption(this.listbox.getNextItem(this.option));
}
else {
this.listbox.open();
if (!altKey) {
this.setOption(this.listbox.getFirstItem());
}
}
}
flag = true;
break;

case this.keyCode.UP:
if (this.listbox.hasOptions()) {
if (this.listbox.isOpen()) {
this.setOption(this.listbox.getPreviousItem(this.option));
}
else {
this.listbox.open();
if (!altKey) {
this.setOption(this.listbox.getLastItem());
}
}
}
flag = true;
break;

case this.keyCode.HOME:
case this.keyCode.PAGEUP:
if (this.listbox.hasOptions()) {
if (this.listbox.isOpen()) {
this.setOption(this.listbox.getFirstItem());
}
}
flag = true;
break;

case this.keyCode.END:
case this.keyCode.PAGEDOWN:
if (this.listbox.hasOptions()) {
if (this.listbox.isOpen()) {
this.setOption(this.listbox.getLastItem());
}
}
flag = true;
break;

case this.keyCode.ESC:
if (this.listbox.isOpen()) {
this.listbox.close(true);
}
this.setValue(this.filter);
flag = true;
break;

default:
break;
}

if (flag) {
event.stopPropagation();
event.preventDefault();
}

};

ComboboxList.prototype.handleKeyup = function (event) {
var tgt = event.currentTarget,
flag = false,
option = false,
char = event.key;

function isPrintableCharacter (str) {
return str.length === 1 && str.match(/\S/);
}

this.filter = this.domNode.value.substring(0,this.domNode.selectionEnd);

if (this.autocomplete !== 'none') {
option = this.listbox.filterOptions(this.filter, this.option);
}

switch (event.keyCode) {

case this.keyCode.BACKSPACE:
if (this.autocomplete === 'both') {
this.setValue(this.filter);
}
flag = true;
break;

case this.keyCode.LEFT:
case this.keyCode.RIGHT:
flag = true;
break;

default:

if (isPrintableCharacter(char)) {
if (option) {
this.setOption(option);
}
else {
this.setValue(this.filter);
}
}
flag = true;
break;
}

if (flag) {
event.stopPropagation();
event.preventDefault();
}

};

ComboboxList.prototype.handleClick = function (event) {
if (this.listbox.isOpen()) {
this.listbox.close(true);
}
else {
this.listbox.open();
}
};

ComboboxList.prototype.handleFocus = function (event) {
this.listbox.hasFocus = true;
};

ComboboxList.prototype.handleBlur = function (event) {
this.listbox.hasFocus = false;
setTimeout(this.listbox.close.bind(this.listbox, false), 300);

};

ComboboxList.prototype.handleButtonClick = function (event) {
if (this.listbox.isOpen()) {
this.listbox.close(true);
}
else {
this.listbox.open();
}
};


// Initialize comboboxes

window.addEventListener('load', function () {

var comboboxes = document.querySelectorAll('.combobox-list [role="combobox"]');

for (var i = 0; i < comboboxes.length; i++) {
var combobox = new ComboboxList(comboboxes[i]);
combobox.init();
}

});
Loading

0 comments on commit dd56ad2

Please sign in to comment.