Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Selectmenu: implement new ARIA spec

  • Loading branch information...
commit 4dbbd0598a58277816289441f3278a65025515d3 1 parent 50c3083
Felix Nagel fnagel authored
40 tests/unit/selectmenu/selectmenu_core.js
View
@@ -12,22 +12,26 @@ test("accessibility", function () {
ul = menu.children("ul"),
links = ul.find("li.ui-menu-item a");
- expect(9 + links.length * 2);
+ expect(13 + links.length * 2);
- equals( "true", link.attr("aria-haspopup"), "button link aria-haspopup" );
- equals( "button", link.attr("role"), "button link role" );
- equals( ul.attr("id"), link.attr("aria-owns"), "button link aria-owns" );
- equals( 0, link.attr("tabindex"), "button link tabindex" );
+ equals( link.attr("role"), "combobox", "button link role" );
+ equals( link.attr("aria-haspopup"), "true", "button link aria-haspopup" );
+ equals( link.attr("aria-expanded"), "false", "button link aria-expanded" );
+ equals( link.attr("aria-autocomplete"), "list", "button link aria-autocomplete" );
+ equals( link.attr("aria-activedescendant"), links.eq(element[0].selectedIndex).attr("id"), "button link aria-activedescendant" );
+ equals( link.attr("aria-owns"), ul.attr("id"), "button link aria-owns" );
+ equals( link.attr("tabindex"), 0, "button link tabindex" );
- equals( "true", ul.attr("aria-hidden"), "menu aria-hidden" );
- equals( link.attr("id"), ul.attr("aria-labelledby"), "menu aria-labelledby" );
- equals( "menubox", ul.attr("role"), "menu role" );
- equals( 0, ul.attr("tabindex"), "menu tabindex" );
- equals( links.eq(element[0].selectedIndex).attr("id"), ul.attr("aria-activedescendant"), "menu aria-activedescendant" );
+ equals( ul.attr("role"), "listbox", "menu role" );
+ equals( ul.attr("aria-labelledby"), link.attr("id"), "menu aria-labelledby" );
+ equals( ul.attr("aria-hidden"), "true", "menu aria-hidden" );
+ equals( ul.attr("tabindex"), 0, "menu tabindex" );
+ equals( ul.attr("aria-activedescendant"), links.eq(element[0].selectedIndex).attr("id"), "menu aria-activedescendant" );
$.each( links, function(index){
- equals( "option", $(this).attr("role"), "menu link #" + index +" role" );
- equals( -1, $(this).attr("tabindex"), "menu link #" + index +" tabindex" );
+ equals( $(this).attr("role"), "option", "menu link #" + index +" role" );
+ equals( $(this).attr("tabindex"), -1, "menu link #" + index +" tabindex" );
});
+ equals( links.eq(element[0].selectedIndex).attr("aria-selected"), "true", "selected menu link aria-selected" );
});
@@ -42,23 +46,29 @@ $.each([
}
], function( i, settings ) {
test("state synchronization - " + settings.type, function () {
- expect(5);
+ expect(10);
var element = $(settings.selector).selectmenu(),
widget = element.selectmenu("widget"),
button = widget.filter(".ui-selectmenu-button"),
menu = widget.filter(".ui-selectmenu-menu"),
link = button.find("a"),
+ ul = menu.children("ul"),
+ links = ul.find("li.ui-menu-item a"),
selected = element.find("option:selected");
- equals( button.text(), selected.text(), "inital button text" );
-
link.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } );
+ equals( ul.attr("aria-activedescendant"), links.eq(element[0].selectedIndex).attr("id"), "after keydown menu aria-activedescendant" );
+ equals( link.attr("aria-activedescendant"), links.eq(element[0].selectedIndex).attr("id"), "after keydown button link aria-activedescendant" );
+ equals( links.eq(element[0].selectedIndex).attr("aria-selected"), "true", "after keydown selected menu link aria-selected" );
equals( element.find("option:selected").val(), selected.next("option").val() , "after keydown original select state" );
equals( button.text(), selected.next("option").text(), "after keydown button text" );
link.simulate( "click" );
menu.find("a").last().simulate( "mouseover" ).trigger( "click" );
+ equals( ul.attr("aria-activedescendant"), links.eq(element[0].selectedIndex).attr("id"), "after click menu aria-activedescendant" );
+ equals( link.attr("aria-activedescendant"), links.eq(element[0].selectedIndex).attr("id"), "after click button link aria-activedescendant" );
+ equals( links.eq(element[0].selectedIndex).attr("aria-selected"), "true", "after click selected menu link aria-selected" );
equals( element.find("option:selected").val(), element.find("option").last().val(), "after click original select state" );
equals( button.text(), element.find("option").last().text(), "after click button text" );
});
4 tests/unit/selectmenu/selectmenu_methods.js
View
@@ -29,7 +29,7 @@ test( "open / close", function() {
test("enable / disable", function () {
- expect(12);
+ expect(14);
var element = $('#speed').selectmenu(),
widget = element.selectmenu("widget"),
@@ -39,6 +39,7 @@ test("enable / disable", function () {
element.selectmenu("disable")
ok( element.selectmenu("option", "disabled"), "disable: widget option" );
+ equals( element.attr("disabled"), "disabled", "disable: native select disabled" );
equals( button.attr("aria-disabled"), "true", "disable: button wrapper ARIA" );
equals( link.attr("aria-disabled"), "true", "disable: button ARIA" );
equals( link.attr("tabindex"), -1, "disable: button tabindex" );
@@ -47,6 +48,7 @@ test("enable / disable", function () {
element.selectmenu("enable")
ok( !element.selectmenu("option", "disabled"), "enable: widget option" );
+ equals( element.attr("disabled"), undefined, "enable: native select disabled" );
equals( button.attr("aria-disabled"), "false", "enable: button wrapper ARIA" );
equals( link.attr("aria-disabled"), "false", "enable: button ARIA" );
equals( link.attr("tabindex"), 0, "enable: button tabindex" );
63 ui/jquery.ui.selectmenu.js
View
@@ -54,6 +54,7 @@ $.widget( "ui.selectmenu", {
this._bind( this.button, this._buttonEvents );
this._drawMenu();
+ this.refresh();
if ( this.options.disabled ) {
this.disable();
@@ -74,15 +75,18 @@ $.widget( "ui.selectmenu", {
css: {
width: this.element.outerWidth()
},
+ 'aria-expanded': false,
+ 'aria-autocomplete': 'list',
'aria-owns': this.ids.menu,
'aria-haspopup': true
})
.button({
- label: this.element.find( "option:selected" ).text(),
icons: {
primary: ( this.options.dropdown ? 'ui-icon-triangle-1-s' : 'ui-icon-triangle-2-n-s' )
}
- });
+ })
+ // change ARIA role
+ .attr( 'role', 'combobox' );
// wrap and insert new button
this.buttonWrap = $( '<span />' )
@@ -122,7 +126,9 @@ $.widget( "ui.selectmenu", {
var item = ui.item.data( "item.selectmenu" ),
oldIndex = that.element[0].selectedIndex;
- that._setIndex( item.index );
+ // change native select element
+ that.element[0].selectedIndex = item.index;
+ that._setSelected();
that._trigger( "select", event, { item: item } );
if ( item.index != oldIndex ) {
@@ -145,7 +151,7 @@ $.widget( "ui.selectmenu", {
}
})
// change ARIA role
- .attr( 'role', 'menubox' );
+ .attr( 'role', 'listbox' );
// change menu styles?
this._setOption( "dropdown", this.options.dropdown );
@@ -165,38 +171,27 @@ $.widget( "ui.selectmenu", {
this._readOptions();
this._renderMenu( this.menu, this.items );
-
+
this.menu.menu( "refresh" );
- // button option label wont work here
- this.button.children( '.ui-button-text' ).text( this.items[ this.element[0].selectedIndex ].label );
// adjust ARIA
- this.menu.find( "li" ).not( '.ui-selectmenu-optgroup' ).find( 'a' ).attr( 'role', 'option' );
- this.menu.attr( "aria-activedescendant" , this.menu.find( "li.ui-menu-item a" ).eq( this.element[0].selectedIndex ).attr( "id" ) );
+ this._getItems().find( 'a' ).attr( 'role', 'option' );
+ this._setSelected();
// set and transfer disabled state
this._getCreateOptions();
- if ( this.options.disabled ) {
- this.disable();
- } else {
- this.enable()
- }
+ this._setOption( "disabled", this.options.disabled );
},
open: function( event ) {
if ( !this.options.disabled ) {
- // init menu when initial opened
- if ( !this.wasOpen ) {
- this.refresh();
- this.wasOpen = true;
- }
-
var currentItem = this._getSelectedItem();
this._toggleButtonStyle();
this.menuWrap.addClass( 'ui-selectmenu-open' );
this.menu.attr("aria-hidden", false);
+ this.button.attr("aria-expanded", true);
// needs to be fired after the document click event has closed all other Selectmenus
// otherwise the current item is not indicated
// TODO check if this should be handled by Menu
@@ -236,7 +231,8 @@ $.widget( "ui.selectmenu", {
this._toggleButtonStyle();
this.menuWrap.removeClass( 'ui-selectmenu-open' );
- this.menu.attr("aria-hidden", true);
+ this.menu.attr( "aria-hidden", true );
+ this.button.attr( "aria-expanded", false );
this.isOpen = false;
if ( focus ) {
@@ -282,14 +278,9 @@ $.widget( "ui.selectmenu", {
},
_move: function( direction, event ) {
- // init menu when not done yet
- if ( !this.wasOpen ) {
- this.refresh();
- this.wasOpen = true;
- }
if ( direction == "first" || direction == "last" ) {
// set focus manually for first or last item
- this.menu.menu( "focus", event, this.menu.find( "li" ).not( '.ui-selectmenu-optgroup' )[ direction ]() );
+ this.menu.menu( "focus", event, this._getItems()[ direction ]() );
} else {
// if menu is closed we need to focus the element first to indicate correct element
if ( !this.isOpen ) {
@@ -306,7 +297,11 @@ $.widget( "ui.selectmenu", {
},
_getSelectedItem: function() {
- return this.menu.find( "li" ).not( '.ui-selectmenu-optgroup' ).eq( this.element[0].selectedIndex );
+ return this._getItems().eq( this.element[0].selectedIndex );
+ },
+
+ _getItems: function() {
+ return this.menu.find( "li" ).not( '.ui-selectmenu-optgroup' );
},
_toggle: function( event ) {
@@ -360,6 +355,7 @@ $.widget( "ui.selectmenu", {
break;
case $.ui.keyCode.HOME:
case $.ui.keyCode.PAGE_UP:
+ console.log("test");
this._move( "first", event );
break;
case $.ui.keyCode.END:
@@ -376,9 +372,14 @@ $.widget( "ui.selectmenu", {
}
},
- _setIndex: function( index ) {
- this.element[0].selectedIndex = index;
- this.button.button( "option", "label", this.items[ index ].label );
+ _setSelected: function() {
+ var item = this._getSelectedItem().find("a");
+ // update button text
+ this.button.button( "option", "label", item.text() );
+ // change ARIA attr
+ this.button.add( this.menu ).attr( "aria-activedescendant" , item.attr( "id" ) );
+ this._getItems().find("a").attr( "aria-selected", false );
+ item.attr( "aria-selected", true );
},
_setOption: function( key, value ) {

1 comment on commit 4dbbd05

Felix Nagel
Collaborator

In order to fulfill the new ARIA specs I needed to remove lazy (menu widget) refresh (on first usage) because otherwise I have no ID attributes to use with aria-activedescendant.

Please sign in to comment.
Something went wrong with that request. Please try again.