Sfoglia il codice sorgente

Added basic accessibility tests

This adds basic tests to ensure that the selection is accessible by
all users. This checks for common attributes, ensuring that the
container is always keyboard accessible and follows ARIA guidelines.

This also changes the base selection to create a common element so
we don't need to repeat attributes and such across multiple selection
containers.
Kevin Brown 10 anni fa
parent
commit
b43656c13c

+ 1 - 0
dist/js/i18n/cs.js

@@ -0,0 +1 @@
+window.$=window.$||{},function(){$&&$.fn&&$.fn.select2&&$.fn.select2.amd&&(define=$.fn.select2.amd.define,require=$.fn.select2.amd.require),define("select2/i18n/cs",[],function(){function e(e,t){switch(e){case 2:return t?"dva":"dvě";case 3:return"tři";case 4:return"čtyři"}return""}return{errorLoading:function(){return"Výsledky nemohly být načteny."},inputTooLong:function(t){var n=t.input.length-t.maximum;return n==1?"Prosím zadejte o jeden znak méně":n<=4?"Prosím zadejte o "+e(n,!0)+" znaky méně":"Prosím zadejte o "+n+" znaků méně"},inputTooShort:function(t){var n=t.minimum-t.input.length;return n==1?"Prosím zadejte ještě jeden znak":n<=4?"Prosím zadejte ještě další "+e(n,!0)+" znaky":"Prosím zadejte ještě dalších "+n+" znaků"},loadingMore:function(){return"Načítají se další výsledky…"},maximumSelected:function(t){var n=t.maximum;return n==1?"Můžete zvolit jen jednu položku":n<=4?"Můžete zvolit maximálně "+e(n,!1)+" položky":"Můžete zvolit maximálně "+n+" položek"},noResults:function(){return"Nenalezeny žádné položky"},searching:function(){return"Vyhledávání…"}}}),require("jquery.select2"),$.fn.select2.amd={define:define,require:require}}();

+ 38 - 41
dist/js/select2.amd.full.js

@@ -677,9 +677,10 @@ define('select2/keys',[
 });
 
 define('select2/selection/base',[
+  'jquery',
   '../utils',
   '../keys'
-], function (Utils, KEYS) {
+], function ($, Utils, KEYS) {
   function BaseSelection ($element, options) {
     this.$element = $element;
     this.options = options;
@@ -690,7 +691,17 @@ define('select2/selection/base',[
   Utils.Extend(BaseSelection, Utils.Observable);
 
   BaseSelection.prototype.render = function () {
-    throw new Error('The `render` method must be defined in child classes.');
+    var $selection = $(
+      '<span class="select2-selection" tabindex="0" role="combobox" ' +
+      'aria-autocomplete="list" aria-haspopup="true" aria-expanded="false">' +
+      '</span>'
+    );
+
+    $selection.attr('title', this.$element.attr('title'));
+
+    this.$selection = $selection;
+
+    return $selection;
   };
 
   BaseSelection.prototype.bind = function (container, $container) {
@@ -735,6 +746,14 @@ define('select2/selection/base',[
 
       self._detachCloseHandler(container);
     });
+
+    container.on('enable', function () {
+      self.$selection.attr('tabindex', '0');
+    });
+
+    container.on('disable', function () {
+      self.$selection.attr('tabindex', '-1');
+    });
   };
 
   BaseSelection.prototype._attachCloseHandler = function (container) {
@@ -787,10 +806,11 @@ define('select2/selection/base',[
 });
 
 define('select2/selection/single',[
+  'jquery',
   './base',
   '../utils',
   '../keys'
-], function (BaseSelection, Utils, KEYS) {
+], function ($, BaseSelection, Utils, KEYS) {
   function SingleSelection () {
     SingleSelection.__super__.constructor.apply(this, arguments);
   }
@@ -798,20 +818,16 @@ define('select2/selection/single',[
   Utils.Extend(SingleSelection, BaseSelection);
 
   SingleSelection.prototype.render = function () {
-    var $selection = $(
-      '<span class="select2-selection select2-selection--single" tabindex="0"' +
-        ' role="combobox" aria-autocomplete="list" aria-haspopup="true"' +
-        ' aria-expanded="false">' +
-        '<span class="select2-selection__rendered"></span>' +
-        '<span class="select2-selection__arrow" role="presentation">' +
-          '<b role="presentation"></b>' +
-        '</span>' +
-      '</span>'
-    );
+    var $selection = SingleSelection.__super__.render.call(this);
 
-    $selection.attr('title', this.$element.attr('title'));
+    $selection.addClass('select2-selection--single');
 
-    this.$selection = $selection;
+    $selection.html(
+      '<span class="select2-selection__rendered"></span>' +
+      '<span class="select2-selection__arrow" role="presentation">' +
+        '<b role="presentation"></b>' +
+      '</span>'
+    );
 
     return $selection;
   };
@@ -845,14 +861,6 @@ define('select2/selection/single',[
       // User exits the container
     });
 
-    container.on('enable', function () {
-      self.$selection.attr('tabindex', '0');
-    });
-
-    container.on('disable', function () {
-      self.$selection.attr('tabindex', '-1');
-    });
-
     container.on('selection:update', function (params) {
       self.update(params.data);
     });
@@ -889,9 +897,10 @@ define('select2/selection/single',[
 });
 
 define('select2/selection/multiple',[
+  'jquery',
   './base',
   '../utils'
-], function (BaseSelection, Utils) {
+], function ($, BaseSelection, Utils) {
   function MultipleSelection ($element, options) {
     MultipleSelection.__super__.constructor.apply(this, arguments);
   }
@@ -899,17 +908,13 @@ define('select2/selection/multiple',[
   Utils.Extend(MultipleSelection, BaseSelection);
 
   MultipleSelection.prototype.render = function () {
-    var $selection = $(
-      '<span class="select2-selection select2-selection--multiple"' +
-        ' tabindex="0" role="combobox" aria-autocomplete="list"' +
-        ' aria-haspopup="true" aria-expanded="false">' +
-        '<ul class="select2-selection__rendered"></ul>' +
-      '</span>'
-    );
+    var $selection = MultipleSelection.__super__.render.call(this);
 
-    $selection.attr('title', this.$element.attr('title'));
+    $selection.addClass('select2-selection--multiple');
 
-    this.$selection = $selection;
+    $selection.html(
+      '<ul class="select2-selection__rendered"></ul>'
+    );
 
     return $selection;
   };
@@ -937,14 +942,6 @@ define('select2/selection/multiple',[
         data: data
       });
     });
-
-    container.on('enable', function () {
-      self.$selection.attr('tabindex', '0');
-    });
-
-    container.on('disable', function () {
-      self.$selection.attr('tabindex', '-1');
-    });
   };
 
   MultipleSelection.prototype.clear = function () {

+ 38 - 41
dist/js/select2.amd.js

@@ -677,9 +677,10 @@ define('select2/keys',[
 });
 
 define('select2/selection/base',[
+  'jquery',
   '../utils',
   '../keys'
-], function (Utils, KEYS) {
+], function ($, Utils, KEYS) {
   function BaseSelection ($element, options) {
     this.$element = $element;
     this.options = options;
@@ -690,7 +691,17 @@ define('select2/selection/base',[
   Utils.Extend(BaseSelection, Utils.Observable);
 
   BaseSelection.prototype.render = function () {
-    throw new Error('The `render` method must be defined in child classes.');
+    var $selection = $(
+      '<span class="select2-selection" tabindex="0" role="combobox" ' +
+      'aria-autocomplete="list" aria-haspopup="true" aria-expanded="false">' +
+      '</span>'
+    );
+
+    $selection.attr('title', this.$element.attr('title'));
+
+    this.$selection = $selection;
+
+    return $selection;
   };
 
   BaseSelection.prototype.bind = function (container, $container) {
@@ -735,6 +746,14 @@ define('select2/selection/base',[
 
       self._detachCloseHandler(container);
     });
+
+    container.on('enable', function () {
+      self.$selection.attr('tabindex', '0');
+    });
+
+    container.on('disable', function () {
+      self.$selection.attr('tabindex', '-1');
+    });
   };
 
   BaseSelection.prototype._attachCloseHandler = function (container) {
@@ -787,10 +806,11 @@ define('select2/selection/base',[
 });
 
 define('select2/selection/single',[
+  'jquery',
   './base',
   '../utils',
   '../keys'
-], function (BaseSelection, Utils, KEYS) {
+], function ($, BaseSelection, Utils, KEYS) {
   function SingleSelection () {
     SingleSelection.__super__.constructor.apply(this, arguments);
   }
@@ -798,20 +818,16 @@ define('select2/selection/single',[
   Utils.Extend(SingleSelection, BaseSelection);
 
   SingleSelection.prototype.render = function () {
-    var $selection = $(
-      '<span class="select2-selection select2-selection--single" tabindex="0"' +
-        ' role="combobox" aria-autocomplete="list" aria-haspopup="true"' +
-        ' aria-expanded="false">' +
-        '<span class="select2-selection__rendered"></span>' +
-        '<span class="select2-selection__arrow" role="presentation">' +
-          '<b role="presentation"></b>' +
-        '</span>' +
-      '</span>'
-    );
+    var $selection = SingleSelection.__super__.render.call(this);
 
-    $selection.attr('title', this.$element.attr('title'));
+    $selection.addClass('select2-selection--single');
 
-    this.$selection = $selection;
+    $selection.html(
+      '<span class="select2-selection__rendered"></span>' +
+      '<span class="select2-selection__arrow" role="presentation">' +
+        '<b role="presentation"></b>' +
+      '</span>'
+    );
 
     return $selection;
   };
@@ -845,14 +861,6 @@ define('select2/selection/single',[
       // User exits the container
     });
 
-    container.on('enable', function () {
-      self.$selection.attr('tabindex', '0');
-    });
-
-    container.on('disable', function () {
-      self.$selection.attr('tabindex', '-1');
-    });
-
     container.on('selection:update', function (params) {
       self.update(params.data);
     });
@@ -889,9 +897,10 @@ define('select2/selection/single',[
 });
 
 define('select2/selection/multiple',[
+  'jquery',
   './base',
   '../utils'
-], function (BaseSelection, Utils) {
+], function ($, BaseSelection, Utils) {
   function MultipleSelection ($element, options) {
     MultipleSelection.__super__.constructor.apply(this, arguments);
   }
@@ -899,17 +908,13 @@ define('select2/selection/multiple',[
   Utils.Extend(MultipleSelection, BaseSelection);
 
   MultipleSelection.prototype.render = function () {
-    var $selection = $(
-      '<span class="select2-selection select2-selection--multiple"' +
-        ' tabindex="0" role="combobox" aria-autocomplete="list"' +
-        ' aria-haspopup="true" aria-expanded="false">' +
-        '<ul class="select2-selection__rendered"></ul>' +
-      '</span>'
-    );
+    var $selection = MultipleSelection.__super__.render.call(this);
 
-    $selection.attr('title', this.$element.attr('title'));
+    $selection.addClass('select2-selection--multiple');
 
-    this.$selection = $selection;
+    $selection.html(
+      '<ul class="select2-selection__rendered"></ul>'
+    );
 
     return $selection;
   };
@@ -937,14 +942,6 @@ define('select2/selection/multiple',[
         data: data
       });
     });
-
-    container.on('enable', function () {
-      self.$selection.attr('tabindex', '0');
-    });
-
-    container.on('disable', function () {
-      self.$selection.attr('tabindex', '-1');
-    });
   };
 
   MultipleSelection.prototype.clear = function () {

+ 38 - 41
dist/js/select2.full.js

@@ -10212,9 +10212,10 @@ define('select2/keys',[
 });
 
 define('select2/selection/base',[
+  'jquery',
   '../utils',
   '../keys'
-], function (Utils, KEYS) {
+], function ($, Utils, KEYS) {
   function BaseSelection ($element, options) {
     this.$element = $element;
     this.options = options;
@@ -10225,7 +10226,17 @@ define('select2/selection/base',[
   Utils.Extend(BaseSelection, Utils.Observable);
 
   BaseSelection.prototype.render = function () {
-    throw new Error('The `render` method must be defined in child classes.');
+    var $selection = $(
+      '<span class="select2-selection" tabindex="0" role="combobox" ' +
+      'aria-autocomplete="list" aria-haspopup="true" aria-expanded="false">' +
+      '</span>'
+    );
+
+    $selection.attr('title', this.$element.attr('title'));
+
+    this.$selection = $selection;
+
+    return $selection;
   };
 
   BaseSelection.prototype.bind = function (container, $container) {
@@ -10270,6 +10281,14 @@ define('select2/selection/base',[
 
       self._detachCloseHandler(container);
     });
+
+    container.on('enable', function () {
+      self.$selection.attr('tabindex', '0');
+    });
+
+    container.on('disable', function () {
+      self.$selection.attr('tabindex', '-1');
+    });
   };
 
   BaseSelection.prototype._attachCloseHandler = function (container) {
@@ -10322,10 +10341,11 @@ define('select2/selection/base',[
 });
 
 define('select2/selection/single',[
+  'jquery',
   './base',
   '../utils',
   '../keys'
-], function (BaseSelection, Utils, KEYS) {
+], function ($, BaseSelection, Utils, KEYS) {
   function SingleSelection () {
     SingleSelection.__super__.constructor.apply(this, arguments);
   }
@@ -10333,20 +10353,16 @@ define('select2/selection/single',[
   Utils.Extend(SingleSelection, BaseSelection);
 
   SingleSelection.prototype.render = function () {
-    var $selection = $(
-      '<span class="select2-selection select2-selection--single" tabindex="0"' +
-        ' role="combobox" aria-autocomplete="list" aria-haspopup="true"' +
-        ' aria-expanded="false">' +
-        '<span class="select2-selection__rendered"></span>' +
-        '<span class="select2-selection__arrow" role="presentation">' +
-          '<b role="presentation"></b>' +
-        '</span>' +
-      '</span>'
-    );
+    var $selection = SingleSelection.__super__.render.call(this);
 
-    $selection.attr('title', this.$element.attr('title'));
+    $selection.addClass('select2-selection--single');
 
-    this.$selection = $selection;
+    $selection.html(
+      '<span class="select2-selection__rendered"></span>' +
+      '<span class="select2-selection__arrow" role="presentation">' +
+        '<b role="presentation"></b>' +
+      '</span>'
+    );
 
     return $selection;
   };
@@ -10380,14 +10396,6 @@ define('select2/selection/single',[
       // User exits the container
     });
 
-    container.on('enable', function () {
-      self.$selection.attr('tabindex', '0');
-    });
-
-    container.on('disable', function () {
-      self.$selection.attr('tabindex', '-1');
-    });
-
     container.on('selection:update', function (params) {
       self.update(params.data);
     });
@@ -10424,9 +10432,10 @@ define('select2/selection/single',[
 });
 
 define('select2/selection/multiple',[
+  'jquery',
   './base',
   '../utils'
-], function (BaseSelection, Utils) {
+], function ($, BaseSelection, Utils) {
   function MultipleSelection ($element, options) {
     MultipleSelection.__super__.constructor.apply(this, arguments);
   }
@@ -10434,17 +10443,13 @@ define('select2/selection/multiple',[
   Utils.Extend(MultipleSelection, BaseSelection);
 
   MultipleSelection.prototype.render = function () {
-    var $selection = $(
-      '<span class="select2-selection select2-selection--multiple"' +
-        ' tabindex="0" role="combobox" aria-autocomplete="list"' +
-        ' aria-haspopup="true" aria-expanded="false">' +
-        '<ul class="select2-selection__rendered"></ul>' +
-      '</span>'
-    );
+    var $selection = MultipleSelection.__super__.render.call(this);
 
-    $selection.attr('title', this.$element.attr('title'));
+    $selection.addClass('select2-selection--multiple');
 
-    this.$selection = $selection;
+    $selection.html(
+      '<ul class="select2-selection__rendered"></ul>'
+    );
 
     return $selection;
   };
@@ -10472,14 +10477,6 @@ define('select2/selection/multiple',[
         data: data
       });
     });
-
-    container.on('enable', function () {
-      self.$selection.attr('tabindex', '0');
-    });
-
-    container.on('disable', function () {
-      self.$selection.attr('tabindex', '-1');
-    });
   };
 
   MultipleSelection.prototype.clear = function () {

File diff suppressed because it is too large
+ 0 - 0
dist/js/select2.full.min.js


+ 38 - 41
dist/js/select2.js

@@ -1105,9 +1105,10 @@ define('select2/keys',[
 });
 
 define('select2/selection/base',[
+  'jquery',
   '../utils',
   '../keys'
-], function (Utils, KEYS) {
+], function ($, Utils, KEYS) {
   function BaseSelection ($element, options) {
     this.$element = $element;
     this.options = options;
@@ -1118,7 +1119,17 @@ define('select2/selection/base',[
   Utils.Extend(BaseSelection, Utils.Observable);
 
   BaseSelection.prototype.render = function () {
-    throw new Error('The `render` method must be defined in child classes.');
+    var $selection = $(
+      '<span class="select2-selection" tabindex="0" role="combobox" ' +
+      'aria-autocomplete="list" aria-haspopup="true" aria-expanded="false">' +
+      '</span>'
+    );
+
+    $selection.attr('title', this.$element.attr('title'));
+
+    this.$selection = $selection;
+
+    return $selection;
   };
 
   BaseSelection.prototype.bind = function (container, $container) {
@@ -1163,6 +1174,14 @@ define('select2/selection/base',[
 
       self._detachCloseHandler(container);
     });
+
+    container.on('enable', function () {
+      self.$selection.attr('tabindex', '0');
+    });
+
+    container.on('disable', function () {
+      self.$selection.attr('tabindex', '-1');
+    });
   };
 
   BaseSelection.prototype._attachCloseHandler = function (container) {
@@ -1215,10 +1234,11 @@ define('select2/selection/base',[
 });
 
 define('select2/selection/single',[
+  'jquery',
   './base',
   '../utils',
   '../keys'
-], function (BaseSelection, Utils, KEYS) {
+], function ($, BaseSelection, Utils, KEYS) {
   function SingleSelection () {
     SingleSelection.__super__.constructor.apply(this, arguments);
   }
@@ -1226,20 +1246,16 @@ define('select2/selection/single',[
   Utils.Extend(SingleSelection, BaseSelection);
 
   SingleSelection.prototype.render = function () {
-    var $selection = $(
-      '<span class="select2-selection select2-selection--single" tabindex="0"' +
-        ' role="combobox" aria-autocomplete="list" aria-haspopup="true"' +
-        ' aria-expanded="false">' +
-        '<span class="select2-selection__rendered"></span>' +
-        '<span class="select2-selection__arrow" role="presentation">' +
-          '<b role="presentation"></b>' +
-        '</span>' +
-      '</span>'
-    );
+    var $selection = SingleSelection.__super__.render.call(this);
 
-    $selection.attr('title', this.$element.attr('title'));
+    $selection.addClass('select2-selection--single');
 
-    this.$selection = $selection;
+    $selection.html(
+      '<span class="select2-selection__rendered"></span>' +
+      '<span class="select2-selection__arrow" role="presentation">' +
+        '<b role="presentation"></b>' +
+      '</span>'
+    );
 
     return $selection;
   };
@@ -1273,14 +1289,6 @@ define('select2/selection/single',[
       // User exits the container
     });
 
-    container.on('enable', function () {
-      self.$selection.attr('tabindex', '0');
-    });
-
-    container.on('disable', function () {
-      self.$selection.attr('tabindex', '-1');
-    });
-
     container.on('selection:update', function (params) {
       self.update(params.data);
     });
@@ -1317,9 +1325,10 @@ define('select2/selection/single',[
 });
 
 define('select2/selection/multiple',[
+  'jquery',
   './base',
   '../utils'
-], function (BaseSelection, Utils) {
+], function ($, BaseSelection, Utils) {
   function MultipleSelection ($element, options) {
     MultipleSelection.__super__.constructor.apply(this, arguments);
   }
@@ -1327,17 +1336,13 @@ define('select2/selection/multiple',[
   Utils.Extend(MultipleSelection, BaseSelection);
 
   MultipleSelection.prototype.render = function () {
-    var $selection = $(
-      '<span class="select2-selection select2-selection--multiple"' +
-        ' tabindex="0" role="combobox" aria-autocomplete="list"' +
-        ' aria-haspopup="true" aria-expanded="false">' +
-        '<ul class="select2-selection__rendered"></ul>' +
-      '</span>'
-    );
+    var $selection = MultipleSelection.__super__.render.call(this);
 
-    $selection.attr('title', this.$element.attr('title'));
+    $selection.addClass('select2-selection--multiple');
 
-    this.$selection = $selection;
+    $selection.html(
+      '<ul class="select2-selection__rendered"></ul>'
+    );
 
     return $selection;
   };
@@ -1365,14 +1370,6 @@ define('select2/selection/multiple',[
         data: data
       });
     });
-
-    container.on('enable', function () {
-      self.$selection.attr('tabindex', '0');
-    });
-
-    container.on('disable', function () {
-      self.$selection.attr('tabindex', '-1');
-    });
   };
 
   MultipleSelection.prototype.clear = function () {

File diff suppressed because it is too large
+ 0 - 0
dist/js/select2.min.js


+ 21 - 2
src/js/select2/selection/base.js

@@ -1,7 +1,8 @@
 define([
+  'jquery',
   '../utils',
   '../keys'
-], function (Utils, KEYS) {
+], function ($, Utils, KEYS) {
   function BaseSelection ($element, options) {
     this.$element = $element;
     this.options = options;
@@ -12,7 +13,17 @@ define([
   Utils.Extend(BaseSelection, Utils.Observable);
 
   BaseSelection.prototype.render = function () {
-    throw new Error('The `render` method must be defined in child classes.');
+    var $selection = $(
+      '<span class="select2-selection" tabindex="0" role="combobox" ' +
+      'aria-autocomplete="list" aria-haspopup="true" aria-expanded="false">' +
+      '</span>'
+    );
+
+    $selection.attr('title', this.$element.attr('title'));
+
+    this.$selection = $selection;
+
+    return $selection;
   };
 
   BaseSelection.prototype.bind = function (container, $container) {
@@ -57,6 +68,14 @@ define([
 
       self._detachCloseHandler(container);
     });
+
+    container.on('enable', function () {
+      self.$selection.attr('tabindex', '0');
+    });
+
+    container.on('disable', function () {
+      self.$selection.attr('tabindex', '-1');
+    });
   };
 
   BaseSelection.prototype._attachCloseHandler = function (container) {

+ 7 - 18
src/js/select2/selection/multiple.js

@@ -1,7 +1,8 @@
 define([
+  'jquery',
   './base',
   '../utils'
-], function (BaseSelection, Utils) {
+], function ($, BaseSelection, Utils) {
   function MultipleSelection ($element, options) {
     MultipleSelection.__super__.constructor.apply(this, arguments);
   }
@@ -9,17 +10,13 @@ define([
   Utils.Extend(MultipleSelection, BaseSelection);
 
   MultipleSelection.prototype.render = function () {
-    var $selection = $(
-      '<span class="select2-selection select2-selection--multiple"' +
-        ' tabindex="0" role="combobox" aria-autocomplete="list"' +
-        ' aria-haspopup="true" aria-expanded="false">' +
-        '<ul class="select2-selection__rendered"></ul>' +
-      '</span>'
-    );
+    var $selection = MultipleSelection.__super__.render.call(this);
 
-    $selection.attr('title', this.$element.attr('title'));
+    $selection.addClass('select2-selection--multiple');
 
-    this.$selection = $selection;
+    $selection.html(
+      '<ul class="select2-selection__rendered"></ul>'
+    );
 
     return $selection;
   };
@@ -47,14 +44,6 @@ define([
         data: data
       });
     });
-
-    container.on('enable', function () {
-      self.$selection.attr('tabindex', '0');
-    });
-
-    container.on('disable', function () {
-      self.$selection.attr('tabindex', '-1');
-    });
   };
 
   MultipleSelection.prototype.clear = function () {

+ 10 - 21
src/js/select2/selection/single.js

@@ -1,8 +1,9 @@
 define([
+  'jquery',
   './base',
   '../utils',
   '../keys'
-], function (BaseSelection, Utils, KEYS) {
+], function ($, BaseSelection, Utils, KEYS) {
   function SingleSelection () {
     SingleSelection.__super__.constructor.apply(this, arguments);
   }
@@ -10,20 +11,16 @@ define([
   Utils.Extend(SingleSelection, BaseSelection);
 
   SingleSelection.prototype.render = function () {
-    var $selection = $(
-      '<span class="select2-selection select2-selection--single" tabindex="0"' +
-        ' role="combobox" aria-autocomplete="list" aria-haspopup="true"' +
-        ' aria-expanded="false">' +
-        '<span class="select2-selection__rendered"></span>' +
-        '<span class="select2-selection__arrow" role="presentation">' +
-          '<b role="presentation"></b>' +
-        '</span>' +
-      '</span>'
-    );
+    var $selection = SingleSelection.__super__.render.call(this);
 
-    $selection.attr('title', this.$element.attr('title'));
+    $selection.addClass('select2-selection--single');
 
-    this.$selection = $selection;
+    $selection.html(
+      '<span class="select2-selection__rendered"></span>' +
+      '<span class="select2-selection__arrow" role="presentation">' +
+        '<b role="presentation"></b>' +
+      '</span>'
+    );
 
     return $selection;
   };
@@ -57,14 +54,6 @@ define([
       // User exits the container
     });
 
-    container.on('enable', function () {
-      self.$selection.attr('tabindex', '0');
-    });
-
-    container.on('disable', function () {
-      self.$selection.attr('tabindex', '-1');
-    });
-
     container.on('selection:update', function (params) {
       self.update(params.data);
     });

+ 146 - 0
tests/a11y/selection-tests.js

@@ -0,0 +1,146 @@
+module('Accessibility - All');
+
+var BaseSelection = require('select2/selection/base');
+var SingleSelection = require('select2/selection/single');
+var MultipleSelection = require('select2/selection/multiple');
+
+var $ = require('jquery');
+
+var Options = require('select2/options');
+var options = new Options({});
+
+test('title is carried over from original element', function (assert) {
+  var $select = $('#qunit-fixture .single');
+
+  var selection = new BaseSelection($select, options);
+  var $selection = selection.render();
+
+  assert.equal(
+    $selection.attr('title'),
+    $select.attr('title'),
+    'The title should have been copied over from the original element'
+  );
+});
+
+test('aria-expanded reflects the state of the container', function (assert) {
+  var $select = $('#qunit-fixture .single');
+
+  var selection = new BaseSelection($select, options);
+  var $selection = selection.render();
+
+  var container = new MockContainer();
+
+  selection.bind(container, $('<span></span>'));
+
+  assert.equal(
+    $selection.attr('aria-expanded'),
+    'false',
+    'The container should not be expanded when it is closed'
+  );
+
+  container.trigger('open');
+
+  assert.equal(
+    $selection.attr('aria-expanded'),
+    'true',
+    'The container should be expanded when it is opened'
+  );
+});
+
+test('static aria attributes are present', function (assert) {
+  var $select = $('#qunit-fixture .single');
+
+  var selection = new BaseSelection($select, options);
+  var $selection = selection.render();
+
+  assert.equal(
+    $selection.attr('role'),
+    'combobox',
+    'The container should identify as a combobox'
+  );
+
+  assert.equal(
+    $selection.attr('aria-haspopup'),
+    'true',
+    'The dropdown is considered a popup of the container'
+  );
+
+  assert.equal(
+    $selection.attr('aria-autocomplete'),
+    'list',
+    'The results in the dropdown are the autocomplete list'
+  );
+});
+
+test('aria-activedescendant should be removed when closed', function (assert) {
+  var $select = $('#qunit-fixture .single');
+
+  var selection = new BaseSelection($select, options);
+  var $selection = selection.render();
+
+  var container = new MockContainer();
+  selection.bind(container, $('<span></span>'));
+
+  $selection.attr('aria-activedescendant', 'something');
+
+  container.trigger('close');
+
+  assert.ok(
+    !$selection.attr('aria-activedescendant'),
+    'There is no active descendant when the dropdown is closed'
+  );
+});
+
+test('the container should be in the tab order', function (assert) {
+  var $select = $('#qunit-fixture .single');
+
+  var selection = new BaseSelection($select, options);
+  var $selection = selection.render();
+
+  var container = new MockContainer();
+  selection.bind(container, $('<span></span>'));
+
+  assert.equal(
+    $selection.attr('tabindex'),
+    '0',
+    'The tab index should allow it to fit in the natural tab order'
+  );
+
+  container.trigger('disable');
+
+  assert.equal(
+    $selection.attr('tabindex'),
+    '-1',
+    'The selection should be dropped out of the tab order when disabled'
+  );
+
+  container.trigger('enable');
+
+  assert.equal(
+    $selection.attr('tabindex'),
+    '0',
+    'The tab index should be restored when re-enabled'
+  );
+});
+
+module('Accessibility - Single');
+
+test('aria-labelledby should match the rendered container', function (assert) {
+  var $select = $('#qunit-fixture .single');
+
+  var selection = new SingleSelection($select, options);
+  var $selection = selection.render();
+
+  var container = new MockContainer();
+  selection.bind(container, $('<span></span>'));
+
+  var $rendered = $selection.find('.select2-selection__rendered');
+
+  assert.equal(
+    $selection.attr('aria-labelledby'),
+    $rendered.attr('id'),
+    'The rendered selection should label the container'
+  );
+});
+
+module('Accessibility - Multiple');

+ 39 - 0
tests/a11y/selection.html

@@ -0,0 +1,39 @@
+<!doctype html>
+<html>
+  <head>
+    <link rel="stylesheet" href="../vendor/qunit-1.14.0.css" type="text/css" />
+    <link rel="stylesheet" href="../../dist/css/select2.css" type="text/css" />
+  </head>
+  <body>
+    <div id="qunit"></div>
+    <div id="qunit-fixture">
+      <select class="single" title="This is an example title">
+        <option value="default">Default</option>
+        <option value="1">One</option>
+        <option>2</option>
+      </select>
+
+      <select class="multiple" multiple="multiple" title="One more example title">
+        <option value="default">Default</option>
+        <option value="1">One</option>
+        <option>2</option>
+      </select>
+
+      <select class="groups">
+        <optgroup label="Test">
+          <option value="one">One</option>
+          <option value="two">Two</option>
+        </optgroup>
+        <optgroup label="Empty"></optgroup>
+      </select>
+    </div>
+
+    <script src="../vendor/qunit-1.14.0.js" type="text/javascript"></script>
+    <script src="../../vendor/almond-0.2.9.js" type="text/javascript"></script>
+    <script src="../../vendor/jquery-2.1.0.js" type="text/javascript"></script>
+    <script src="../../dist/js/select2.amd.js" type="text/javascript"></script>
+    <script src="../mock.js" type="text/javascript"></script>
+
+    <script src="selection-tests.js" type="text/javascript"></script>
+  </body>
+</html>

Some files were not shown because too many files changed in this diff