Skip to content

Commit 4dbbd05

Browse files
committed
Selectmenu: implement new ARIA spec
1 parent 50c3083 commit 4dbbd05

File tree

3 files changed

+60
-47
lines changed

3 files changed

+60
-47
lines changed

tests/unit/selectmenu/selectmenu_core.js

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,26 @@ test("accessibility", function () {
1212
ul = menu.children("ul"),
1313
links = ul.find("li.ui-menu-item a");
1414

15-
expect(9 + links.length * 2);
15+
expect(13 + links.length * 2);
1616

17-
equals( "true", link.attr("aria-haspopup"), "button link aria-haspopup" );
18-
equals( "button", link.attr("role"), "button link role" );
19-
equals( ul.attr("id"), link.attr("aria-owns"), "button link aria-owns" );
20-
equals( 0, link.attr("tabindex"), "button link tabindex" );
17+
equals( link.attr("role"), "combobox", "button link role" );
18+
equals( link.attr("aria-haspopup"), "true", "button link aria-haspopup" );
19+
equals( link.attr("aria-expanded"), "false", "button link aria-expanded" );
20+
equals( link.attr("aria-autocomplete"), "list", "button link aria-autocomplete" );
21+
equals( link.attr("aria-activedescendant"), links.eq(element[0].selectedIndex).attr("id"), "button link aria-activedescendant" );
22+
equals( link.attr("aria-owns"), ul.attr("id"), "button link aria-owns" );
23+
equals( link.attr("tabindex"), 0, "button link tabindex" );
2124

22-
equals( "true", ul.attr("aria-hidden"), "menu aria-hidden" );
23-
equals( link.attr("id"), ul.attr("aria-labelledby"), "menu aria-labelledby" );
24-
equals( "menubox", ul.attr("role"), "menu role" );
25-
equals( 0, ul.attr("tabindex"), "menu tabindex" );
26-
equals( links.eq(element[0].selectedIndex).attr("id"), ul.attr("aria-activedescendant"), "menu aria-activedescendant" );
25+
equals( ul.attr("role"), "listbox", "menu role" );
26+
equals( ul.attr("aria-labelledby"), link.attr("id"), "menu aria-labelledby" );
27+
equals( ul.attr("aria-hidden"), "true", "menu aria-hidden" );
28+
equals( ul.attr("tabindex"), 0, "menu tabindex" );
29+
equals( ul.attr("aria-activedescendant"), links.eq(element[0].selectedIndex).attr("id"), "menu aria-activedescendant" );
2730
$.each( links, function(index){
28-
equals( "option", $(this).attr("role"), "menu link #" + index +" role" );
29-
equals( -1, $(this).attr("tabindex"), "menu link #" + index +" tabindex" );
31+
equals( $(this).attr("role"), "option", "menu link #" + index +" role" );
32+
equals( $(this).attr("tabindex"), -1, "menu link #" + index +" tabindex" );
3033
});
34+
equals( links.eq(element[0].selectedIndex).attr("aria-selected"), "true", "selected menu link aria-selected" );
3135
});
3236

3337

@@ -42,23 +46,29 @@ $.each([
4246
}
4347
], function( i, settings ) {
4448
test("state synchronization - " + settings.type, function () {
45-
expect(5);
49+
expect(10);
4650

4751
var element = $(settings.selector).selectmenu(),
4852
widget = element.selectmenu("widget"),
4953
button = widget.filter(".ui-selectmenu-button"),
5054
menu = widget.filter(".ui-selectmenu-menu"),
5155
link = button.find("a"),
56+
ul = menu.children("ul"),
57+
links = ul.find("li.ui-menu-item a"),
5258
selected = element.find("option:selected");
5359

54-
equals( button.text(), selected.text(), "inital button text" );
55-
5660
link.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } );
61+
equals( ul.attr("aria-activedescendant"), links.eq(element[0].selectedIndex).attr("id"), "after keydown menu aria-activedescendant" );
62+
equals( link.attr("aria-activedescendant"), links.eq(element[0].selectedIndex).attr("id"), "after keydown button link aria-activedescendant" );
63+
equals( links.eq(element[0].selectedIndex).attr("aria-selected"), "true", "after keydown selected menu link aria-selected" );
5764
equals( element.find("option:selected").val(), selected.next("option").val() , "after keydown original select state" );
5865
equals( button.text(), selected.next("option").text(), "after keydown button text" );
5966

6067
link.simulate( "click" );
6168
menu.find("a").last().simulate( "mouseover" ).trigger( "click" );
69+
equals( ul.attr("aria-activedescendant"), links.eq(element[0].selectedIndex).attr("id"), "after click menu aria-activedescendant" );
70+
equals( link.attr("aria-activedescendant"), links.eq(element[0].selectedIndex).attr("id"), "after click button link aria-activedescendant" );
71+
equals( links.eq(element[0].selectedIndex).attr("aria-selected"), "true", "after click selected menu link aria-selected" );
6272
equals( element.find("option:selected").val(), element.find("option").last().val(), "after click original select state" );
6373
equals( button.text(), element.find("option").last().text(), "after click button text" );
6474
});

tests/unit/selectmenu/selectmenu_methods.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ test( "open / close", function() {
2929

3030

3131
test("enable / disable", function () {
32-
expect(12);
32+
expect(14);
3333

3434
var element = $('#speed').selectmenu(),
3535
widget = element.selectmenu("widget"),
@@ -39,6 +39,7 @@ test("enable / disable", function () {
3939

4040
element.selectmenu("disable")
4141
ok( element.selectmenu("option", "disabled"), "disable: widget option" );
42+
equals( element.attr("disabled"), "disabled", "disable: native select disabled" );
4243
equals( button.attr("aria-disabled"), "true", "disable: button wrapper ARIA" );
4344
equals( link.attr("aria-disabled"), "true", "disable: button ARIA" );
4445
equals( link.attr("tabindex"), -1, "disable: button tabindex" );
@@ -47,6 +48,7 @@ test("enable / disable", function () {
4748

4849
element.selectmenu("enable")
4950
ok( !element.selectmenu("option", "disabled"), "enable: widget option" );
51+
equals( element.attr("disabled"), undefined, "enable: native select disabled" );
5052
equals( button.attr("aria-disabled"), "false", "enable: button wrapper ARIA" );
5153
equals( link.attr("aria-disabled"), "false", "enable: button ARIA" );
5254
equals( link.attr("tabindex"), 0, "enable: button tabindex" );

ui/jquery.ui.selectmenu.js

Lines changed: 32 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ $.widget( "ui.selectmenu", {
5454
this._bind( this.button, this._buttonEvents );
5555

5656
this._drawMenu();
57+
this.refresh();
5758

5859
if ( this.options.disabled ) {
5960
this.disable();
@@ -74,15 +75,18 @@ $.widget( "ui.selectmenu", {
7475
css: {
7576
width: this.element.outerWidth()
7677
},
78+
'aria-expanded': false,
79+
'aria-autocomplete': 'list',
7780
'aria-owns': this.ids.menu,
7881
'aria-haspopup': true
7982
})
8083
.button({
81-
label: this.element.find( "option:selected" ).text(),
8284
icons: {
8385
primary: ( this.options.dropdown ? 'ui-icon-triangle-1-s' : 'ui-icon-triangle-2-n-s' )
8486
}
85-
});
87+
})
88+
// change ARIA role
89+
.attr( 'role', 'combobox' );
8690

8791
// wrap and insert new button
8892
this.buttonWrap = $( '<span />' )
@@ -122,7 +126,9 @@ $.widget( "ui.selectmenu", {
122126
var item = ui.item.data( "item.selectmenu" ),
123127
oldIndex = that.element[0].selectedIndex;
124128

125-
that._setIndex( item.index );
129+
// change native select element
130+
that.element[0].selectedIndex = item.index;
131+
that._setSelected();
126132
that._trigger( "select", event, { item: item } );
127133

128134
if ( item.index != oldIndex ) {
@@ -145,7 +151,7 @@ $.widget( "ui.selectmenu", {
145151
}
146152
})
147153
// change ARIA role
148-
.attr( 'role', 'menubox' );
154+
.attr( 'role', 'listbox' );
149155

150156
// change menu styles?
151157
this._setOption( "dropdown", this.options.dropdown );
@@ -165,38 +171,27 @@ $.widget( "ui.selectmenu", {
165171

166172
this._readOptions();
167173
this._renderMenu( this.menu, this.items );
168-
174+
169175
this.menu.menu( "refresh" );
170-
// button option label wont work here
171-
this.button.children( '.ui-button-text' ).text( this.items[ this.element[0].selectedIndex ].label );
172176

173177
// adjust ARIA
174-
this.menu.find( "li" ).not( '.ui-selectmenu-optgroup' ).find( 'a' ).attr( 'role', 'option' );
175-
this.menu.attr( "aria-activedescendant" , this.menu.find( "li.ui-menu-item a" ).eq( this.element[0].selectedIndex ).attr( "id" ) );
178+
this._getItems().find( 'a' ).attr( 'role', 'option' );
179+
this._setSelected();
176180

177181
// set and transfer disabled state
178182
this._getCreateOptions();
179-
if ( this.options.disabled ) {
180-
this.disable();
181-
} else {
182-
this.enable()
183-
}
183+
this._setOption( "disabled", this.options.disabled );
184184
},
185185

186186
open: function( event ) {
187187
if ( !this.options.disabled ) {
188-
// init menu when initial opened
189-
if ( !this.wasOpen ) {
190-
this.refresh();
191-
this.wasOpen = true;
192-
}
193-
194188
var currentItem = this._getSelectedItem();
195189

196190
this._toggleButtonStyle();
197191

198192
this.menuWrap.addClass( 'ui-selectmenu-open' );
199193
this.menu.attr("aria-hidden", false);
194+
this.button.attr("aria-expanded", true);
200195
// needs to be fired after the document click event has closed all other Selectmenus
201196
// otherwise the current item is not indicated
202197
// TODO check if this should be handled by Menu
@@ -236,7 +231,8 @@ $.widget( "ui.selectmenu", {
236231
this._toggleButtonStyle();
237232

238233
this.menuWrap.removeClass( 'ui-selectmenu-open' );
239-
this.menu.attr("aria-hidden", true);
234+
this.menu.attr( "aria-hidden", true );
235+
this.button.attr( "aria-expanded", false );
240236
this.isOpen = false;
241237

242238
if ( focus ) {
@@ -282,14 +278,9 @@ $.widget( "ui.selectmenu", {
282278
},
283279

284280
_move: function( direction, event ) {
285-
// init menu when not done yet
286-
if ( !this.wasOpen ) {
287-
this.refresh();
288-
this.wasOpen = true;
289-
}
290281
if ( direction == "first" || direction == "last" ) {
291282
// set focus manually for first or last item
292-
this.menu.menu( "focus", event, this.menu.find( "li" ).not( '.ui-selectmenu-optgroup' )[ direction ]() );
283+
this.menu.menu( "focus", event, this._getItems()[ direction ]() );
293284
} else {
294285
// if menu is closed we need to focus the element first to indicate correct element
295286
if ( !this.isOpen ) {
@@ -306,7 +297,11 @@ $.widget( "ui.selectmenu", {
306297
},
307298

308299
_getSelectedItem: function() {
309-
return this.menu.find( "li" ).not( '.ui-selectmenu-optgroup' ).eq( this.element[0].selectedIndex );
300+
return this._getItems().eq( this.element[0].selectedIndex );
301+
},
302+
303+
_getItems: function() {
304+
return this.menu.find( "li" ).not( '.ui-selectmenu-optgroup' );
310305
},
311306

312307
_toggle: function( event ) {
@@ -360,6 +355,7 @@ $.widget( "ui.selectmenu", {
360355
break;
361356
case $.ui.keyCode.HOME:
362357
case $.ui.keyCode.PAGE_UP:
358+
console.log("test");
363359
this._move( "first", event );
364360
break;
365361
case $.ui.keyCode.END:
@@ -376,9 +372,14 @@ $.widget( "ui.selectmenu", {
376372
}
377373
},
378374

379-
_setIndex: function( index ) {
380-
this.element[0].selectedIndex = index;
381-
this.button.button( "option", "label", this.items[ index ].label );
375+
_setSelected: function() {
376+
var item = this._getSelectedItem().find("a");
377+
// update button text
378+
this.button.button( "option", "label", item.text() );
379+
// change ARIA attr
380+
this.button.add( this.menu ).attr( "aria-activedescendant" , item.attr( "id" ) );
381+
this._getItems().find("a").attr( "aria-selected", false );
382+
item.attr( "aria-selected", true );
382383
},
383384

384385
_setOption: function( key, value ) {

0 commit comments

Comments
 (0)