'
);
@@ -29,14 +29,18 @@ define([
Search.prototype.bind = function (decorated, container, $container) {
var self = this;
+ var resultsId = container.id + '-results';
+
decorated.call(this, container, $container);
container.on('open', function () {
+ self.$search.attr('aria-controls', resultsId);
self.$search.trigger('focus');
});
container.on('close', function () {
self.$search.val('');
+ self.$search.removeAttr('aria-controls');
self.$search.removeAttr('aria-activedescendant');
self.$search.trigger('focus');
});
@@ -56,7 +60,11 @@ define([
});
container.on('results:focus', function (params) {
- self.$search.attr('aria-activedescendant', params.id);
+ if (params.data._resultId) {
+ self.$search.attr('aria-activedescendant', params.data._resultId);
+ } else {
+ self.$search.removeAttr('aria-activedescendant');
+ }
});
this.$selection.on('focusin', '.select2-search--inline', function (evt) {
diff --git a/tests/a11y/search-tests.js b/tests/a11y/search-tests.js
deleted file mode 100644
index 58e56492..00000000
--- a/tests/a11y/search-tests.js
+++ /dev/null
@@ -1,51 +0,0 @@
-module('Accessibility - Search');
-
-var MultipleSelection = require('select2/selection/multiple');
-var InlineSearch = require('select2/selection/search');
-
-var $ = require('jquery');
-
-var Utils = require('select2/utils');
-var Options = require('select2/options');
-var options = new Options({});
-
-test('aria-autocomplete attribute is present', function (assert) {
- var $select = $('#qunit-fixture .multiple');
-
- var CustomSelection = Utils.Decorate(MultipleSelection, InlineSearch);
- var selection = new CustomSelection($select, options);
- var $selection = selection.render();
-
- // Update the selection so the search is rendered
- selection.update([]);
-
- assert.equal(
- $selection.find('input').attr('aria-autocomplete'),
- 'list',
- 'The search box is marked as autocomplete'
- );
-});
-
-test('aria-activedescendant should be removed when closed', function (assert) {
- var $select = $('#qunit-fixture .multiple');
-
- var CustomSelection = Utils.Decorate(MultipleSelection, InlineSearch);
- var selection = new CustomSelection($select, options);
- var $selection = selection.render();
-
- var container = new MockContainer();
- selection.bind(container, $(''));
-
- // Update the selection so the search is rendered
- selection.update([]);
-
- var $search = $selection.find('input');
- $search.attr('aria-activedescendant', 'something');
-
- container.trigger('close');
-
- assert.ok(
- !$search.attr('aria-activedescendant'),
- 'There is no active descendant when the dropdown is closed'
- );
-});
diff --git a/tests/dropdown/search-a11y-tests.js b/tests/dropdown/search-a11y-tests.js
new file mode 100644
index 00000000..0876de9f
--- /dev/null
+++ b/tests/dropdown/search-a11y-tests.js
@@ -0,0 +1,185 @@
+module('Dropdown - Search - Accessibility');
+
+var Utils = require('select2/utils');
+
+var Dropdown = require('select2/dropdown');
+var DropdownSearch = Utils.Decorate(
+ Dropdown,
+ require('select2/dropdown/search')
+);
+
+var $ = require('jquery');
+
+var Options = require('select2/options');
+var options = new Options({});
+
+test('role attribute is set to searchbox', function (assert) {
+ var $select = $('#qunit-fixture .single');
+
+ var dropdown = new DropdownSearch($select, options);
+ var $dropdown = dropdown.render();
+
+ var container = new MockContainer();
+ dropdown.bind(container, $(''));
+
+ assert.equal(
+ $dropdown.find('input').attr('role'),
+ 'searchbox',
+ 'The search box is marked as a search box'
+ );
+});
+
+test('aria-autocomplete attribute is present', function (assert) {
+ var $select = $('#qunit-fixture .single');
+
+ var dropdown = new DropdownSearch($select, options);
+ var $dropdown = dropdown.render();
+
+ var container = new MockContainer();
+ dropdown.bind(container, $(''));
+
+ assert.equal(
+ $dropdown.find('input').attr('aria-autocomplete'),
+ 'list',
+ 'The search box is marked as autocomplete'
+ );
+});
+
+test('aria-activedescendant should not be set initiailly', function (assert) {
+ var $select = $('#qunit-fixture .single');
+
+ var dropdown = new DropdownSearch($select, options);
+ var $dropdown = dropdown.render();
+
+ var container = new MockContainer();
+ dropdown.bind(container, $(''));
+
+ var $search = $dropdown.find('input');
+
+ assert.ok(
+ !$search.attr('aria-activedescendant'),
+ 'The search box should not point to anything when it is first rendered'
+ );
+});
+
+test('aria-activedescendant should be set after highlight', function (assert) {
+ var $select = $('#qunit-fixture .single');
+
+ var dropdown = new DropdownSearch($select, options);
+ var $dropdown = dropdown.render();
+
+ var container = new MockContainer();
+ dropdown.bind(container, $(''));
+
+ container.trigger('results:focus', {
+ data: {
+ _resultId: 'test'
+ }
+ });
+
+ var $search = $dropdown.find('input');
+
+ assert.equal(
+ $search.attr('aria-activedescendant'),
+ 'test',
+ 'The search is pointing to the focused result'
+ );
+});
+
+test('activedescendant should remove if there is no ID', function (assert) {
+ var $select = $('#qunit-fixture .single');
+
+ var dropdown = new DropdownSearch($select, options);
+ var $dropdown = dropdown.render();
+
+ var container = new MockContainer();
+ dropdown.bind(container, $(''));
+
+ var $search = $dropdown.find('input');
+ $search.attr('aria-activedescendant', 'test');
+
+ container.trigger('results:focus', {
+ data: {}
+ });
+
+ assert.ok(
+ !$search.attr('aria-activedescendant'),
+ 'There is no result for the search to be pointing to'
+ );
+});
+
+test('aria-activedescendant should be removed when closed', function (assert) {
+ var $select = $('#qunit-fixture .single');
+
+ var dropdown = new DropdownSearch($select, options);
+ var $dropdown = dropdown.render();
+
+ var container = new MockContainer();
+ dropdown.bind(container, $(''));
+
+ var $search = $dropdown.find('input');
+ $search.attr('aria-activedescendant', 'something');
+
+ container.trigger('close');
+
+ assert.ok(
+ !$search.attr('aria-activedescendant'),
+ 'There is no active descendant when the dropdown is closed'
+ );
+});
+
+test('aria-controls should not be set initiailly', function (assert) {
+ var $select = $('#qunit-fixture .single');
+
+ var dropdown = new DropdownSearch($select, options);
+ var $dropdown = dropdown.render();
+
+ var container = new MockContainer();
+ dropdown.bind(container, $(''));
+
+ var $search = $dropdown.find('input');
+
+ assert.ok(
+ !$search.attr('aria-controls'),
+ 'The search box should not point to the results when it is first rendered'
+ );
+});
+
+test('aria-controls should be set when opened', function (assert) {
+ var $select = $('#qunit-fixture .single');
+
+ var dropdown = new DropdownSearch($select, options);
+ var $dropdown = dropdown.render();
+
+ var container = new MockContainer();
+ dropdown.bind(container, $(''));
+
+ var $search = $dropdown.find('input');
+
+ container.trigger('open');
+
+ assert.ok(
+ $search.attr('aria-controls'),
+ 'The search should point to the results when it is opened'
+ );
+});
+
+test('aria-controls should be removed when closed', function (assert) {
+ var $select = $('#qunit-fixture .single');
+
+ var dropdown = new DropdownSearch($select, options);
+ var $dropdown = dropdown.render();
+
+ var container = new MockContainer();
+ dropdown.bind(container, $(''));
+
+ var $search = $dropdown.find('input');
+ $search.attr('aria-controls', 'something');
+
+ container.trigger('close');
+
+ assert.ok(
+ !$search.attr('aria-controls'),
+ 'There are no results for the search box to point to when it is closed'
+ );
+});
diff --git a/tests/results/a11y-tests.js b/tests/results/a11y-tests.js
new file mode 100644
index 00000000..c5293265
--- /dev/null
+++ b/tests/results/a11y-tests.js
@@ -0,0 +1,25 @@
+module('Results - Accessibility');
+
+var $ = require('jquery');
+
+var Options = require('select2/options');
+
+var Results = require('select2/results');
+
+test('role of results should be a listbox', function (assert) {
+ var results = new Results($(''), new Options({}));
+
+ var $results = results.render();
+
+ assert.equal($results.attr('role'), 'listbox');
+});
+
+test('multiple select should have aria-multiselectable', function (assert) {
+ var results = new Results($(''), new Options({
+ multiple: true
+ }));
+
+ var $results = results.render();
+
+ assert.equal($results.attr('aria-multiselectable'), 'true');
+});
diff --git a/tests/results/option-tests.js b/tests/results/option-tests.js
index f98937e7..96570501 100644
--- a/tests/results/option-tests.js
+++ b/tests/results/option-tests.js
@@ -60,4 +60,57 @@ test('option in disabled optgroup is disabled', function (assert) {
});
assert.equal(option.getAttribute('aria-disabled'), 'true');
-});
\ No newline at end of file
+});
+
+test('options are not selected by default', function (assert) {
+ var results = new Results($(''), new Options({}));
+
+ var $option = $('');
+ var option = results.option({
+ id: 'test',
+ element: $option[0]
+ });
+
+ assert.equal(option.getAttribute('aria-selected'), 'false');
+});
+
+test('options with children are given the group role', function(assert) {
+ var results = new Results($(''), new Options({}));
+
+ var $option = $('');
+ var option = results.option({
+ children: [{
+ id: 'test'
+ }],
+ element: $option[0]
+ });
+
+ assert.equal(option.getAttribute('role'), 'group');
+});
+
+test('options with children have the aria-label set', function (assert) {
+ var results = new Results($(''), new Options({}));
+
+ var $option = $('');
+ var option = results.option({
+ children: [{
+ id: 'test'
+ }],
+ element: $option[0],
+ text: 'test'
+ });
+
+ assert.equal(option.getAttribute('aria-label'), 'test');
+});
+
+test('non-group options are given the option role', function (assert) {
+ var results = new Results($(''), new Options({}));
+
+ var $option = $('');
+ var option = results.option({
+ id: 'test',
+ element: $option[0]
+ });
+
+ assert.equal(option.getAttribute('role'), 'option');
+});
diff --git a/tests/selection/search-a11y-tests.js b/tests/selection/search-a11y-tests.js
new file mode 100644
index 00000000..b2628df5
--- /dev/null
+++ b/tests/selection/search-a11y-tests.js
@@ -0,0 +1,217 @@
+module('Selection containers - Inline search - Accessibility');
+
+var MultipleSelection = require('select2/selection/multiple');
+var InlineSearch = require('select2/selection/search');
+
+var $ = require('jquery');
+
+var Utils = require('select2/utils');
+var Options = require('select2/options');
+var options = new Options({});
+
+test('role attribute is set to searchbox', function (assert) {
+ var $select = $('#qunit-fixture .multiple');
+
+ var CustomSelection = Utils.Decorate(MultipleSelection, InlineSearch);
+ var selection = new CustomSelection($select, options);
+ var $selection = selection.render();
+
+ var container = new MockContainer();
+ selection.bind(container, $(''));
+
+ // Update the selection so the search is rendered
+ selection.update([]);
+
+ assert.equal(
+ $selection.find('input').attr('role'),
+ 'searchbox',
+ 'The search box is marked as a search box'
+ );
+});
+
+test('aria-autocomplete attribute is present', function (assert) {
+ var $select = $('#qunit-fixture .multiple');
+
+ var CustomSelection = Utils.Decorate(MultipleSelection, InlineSearch);
+ var selection = new CustomSelection($select, options);
+ var $selection = selection.render();
+
+ var container = new MockContainer();
+ selection.bind(container, $(''));
+
+ // Update the selection so the search is rendered
+ selection.update([]);
+
+ assert.equal(
+ $selection.find('input').attr('aria-autocomplete'),
+ 'list',
+ 'The search box is marked as autocomplete'
+ );
+});
+
+test('aria-activedescendant should not be set initiailly', function (assert) {
+ var $select = $('#qunit-fixture .multiple');
+
+ var CustomSelection = Utils.Decorate(MultipleSelection, InlineSearch);
+ var selection = new CustomSelection($select, options);
+ var $selection = selection.render();
+
+ var container = new MockContainer();
+ selection.bind(container, $(''));
+
+ // Update the selection so the search is rendered
+ selection.update([]);
+
+ var $search = $selection.find('input');
+
+ assert.ok(
+ !$search.attr('aria-activedescendant'),
+ 'The search box should not point to anything when it is first rendered'
+ );
+});
+
+test('aria-activedescendant should be set after highlight', function (assert) {
+ var $select = $('#qunit-fixture .multiple');
+
+ var CustomSelection = Utils.Decorate(MultipleSelection, InlineSearch);
+ var selection = new CustomSelection($select, options);
+ var $selection = selection.render();
+
+ var container = new MockContainer();
+ selection.bind(container, $(''));
+
+ // Update the selection so the search is rendered
+ selection.update([]);
+
+ container.trigger('results:focus', {
+ data: {
+ _resultId: 'test'
+ }
+ });
+
+ var $search = $selection.find('input');
+
+ assert.equal(
+ $search.attr('aria-activedescendant'),
+ 'test',
+ 'The search is pointing to the focused result'
+ );
+});
+
+test('activedescendant should remove if there is no ID', function (assert) {
+ var $select = $('#qunit-fixture .multiple');
+
+ var CustomSelection = Utils.Decorate(MultipleSelection, InlineSearch);
+ var selection = new CustomSelection($select, options);
+ var $selection = selection.render();
+
+ var container = new MockContainer();
+ selection.bind(container, $(''));
+
+ // Update the selection so the search is rendered
+ selection.update([]);
+
+ var $search = $selection.find('input');
+ $search.attr('aria-activedescendant', 'test');
+
+ container.trigger('results:focus', {
+ data: {}
+ });
+
+ assert.ok(
+ !$search.attr('aria-activedescendant'),
+ 'There is no result for the search to be pointing to'
+ );
+});
+
+test('aria-activedescendant should be removed when closed', function (assert) {
+ var $select = $('#qunit-fixture .multiple');
+
+ var CustomSelection = Utils.Decorate(MultipleSelection, InlineSearch);
+ var selection = new CustomSelection($select, options);
+ var $selection = selection.render();
+
+ var container = new MockContainer();
+ selection.bind(container, $(''));
+
+ // Update the selection so the search is rendered
+ selection.update([]);
+
+ var $search = $selection.find('input');
+ $search.attr('aria-activedescendant', 'something');
+
+ container.trigger('close');
+
+ assert.ok(
+ !$search.attr('aria-activedescendant'),
+ 'There is no active descendant when the dropdown is closed'
+ );
+});
+
+test('aria-controls should not be set initiailly', function (assert) {
+ var $select = $('#qunit-fixture .multiple');
+
+ var CustomSelection = Utils.Decorate(MultipleSelection, InlineSearch);
+ var selection = new CustomSelection($select, options);
+ var $selection = selection.render();
+
+ var container = new MockContainer();
+ selection.bind(container, $(''));
+
+ // Update the selection so the search is rendered
+ selection.update([]);
+
+ var $search = $selection.find('input');
+
+ assert.ok(
+ !$search.attr('aria-controls'),
+ 'The search box should not point to the results when it is first rendered'
+ );
+});
+
+test('aria-controls should be set when opened', function (assert) {
+ var $select = $('#qunit-fixture .multiple');
+
+ var CustomSelection = Utils.Decorate(MultipleSelection, InlineSearch);
+ var selection = new CustomSelection($select, options);
+ var $selection = selection.render();
+
+ var container = new MockContainer();
+ selection.bind(container, $(''));
+
+ // Update the selection so the search is rendered
+ selection.update([]);
+
+ var $search = $selection.find('input');
+
+ container.trigger('open');
+
+ assert.ok(
+ $search.attr('aria-controls'),
+ 'The search should point to the results when it is opened'
+ );
+});
+
+test('aria-controls should be removed when closed', function (assert) {
+ var $select = $('#qunit-fixture .multiple');
+
+ var CustomSelection = Utils.Decorate(MultipleSelection, InlineSearch);
+ var selection = new CustomSelection($select, options);
+ var $selection = selection.render();
+
+ var container = new MockContainer();
+ selection.bind(container, $(''));
+
+ // Update the selection so the search is rendered
+ selection.update([]);
+
+ var $search = $selection.find('input');
+ $search.attr('aria-controls', 'something');
+
+ container.trigger('close');
+
+ assert.ok(
+ !$search.attr('aria-controls'),
+ 'There are no results for the search box to point to when it is closed'
+ );
+});
diff --git a/tests/unit-jq1.html b/tests/unit-jq1.html
index bdbe1e04..dc2ca21e 100644
--- a/tests/unit-jq1.html
+++ b/tests/unit-jq1.html
@@ -57,7 +57,6 @@
-
@@ -72,6 +71,7 @@
+
@@ -81,6 +81,7 @@
+
@@ -91,6 +92,7 @@
+
diff --git a/tests/unit-jq2.html b/tests/unit-jq2.html
index e3f23023..52500510 100644
--- a/tests/unit-jq2.html
+++ b/tests/unit-jq2.html
@@ -57,7 +57,6 @@
-
@@ -72,6 +71,7 @@
+
@@ -81,6 +81,7 @@
+
@@ -91,6 +92,7 @@
+
diff --git a/tests/unit-jq3.html b/tests/unit-jq3.html
index d19a88df..d038818a 100644
--- a/tests/unit-jq3.html
+++ b/tests/unit-jq3.html
@@ -57,7 +57,6 @@
-
@@ -72,6 +71,7 @@
+
@@ -81,6 +81,7 @@
+
@@ -91,6 +92,7 @@
+