Fix generated options not receiving result IDs (#5586)
In order to enable the ability to uniquely identify a result by an ID in the DOM, we generate a new ID for the result based on a combination of things, including the container ID prefix that is generated and used elsewhere in Select2. This has worked fairly well for use cases including attaching Select2 to an existing `<select>` and loading in options from a remote data set. Unfortunately, because this process relied on the container ID being used as a prefix, this failed for options which were automatically generated on initialization using the `data:` option to Select2. These were not being generated with an ID because at the time that they were being generated, the data adapter was not aware of the container it was being used in. This broke some accessibility features because we had a mix of options in the results list with IDs, and some without, so we fixed the ordering to make this work. Option generation no longer happens when the data adapter is first initialized, which is where it was previously happening, and instead it now occurs when the data adapter is bound to the container. This allows us to ensure that the data adapter is always aware of the container it is being associated with, so now it will be able to generate the result IDs. This also fixes the tests for the array adapter as well as the legacy `<input />` adapter so they properly bind to a container during the test. This was causing test failures becuase the options which would previously be generated during initialization were no longer appearing. Fixes #4350
This commit is contained in:
parent
2fce8ae6c4
commit
1f3eceba5a
10
src/js/select2/data/array.js
vendored
10
src/js/select2/data/array.js
vendored
@ -4,15 +4,19 @@ define([
|
|||||||
'jquery'
|
'jquery'
|
||||||
], function (SelectAdapter, Utils, $) {
|
], function (SelectAdapter, Utils, $) {
|
||||||
function ArrayAdapter ($element, options) {
|
function ArrayAdapter ($element, options) {
|
||||||
var data = options.get('data') || [];
|
this._dataToConvert = options.get('data') || [];
|
||||||
|
|
||||||
ArrayAdapter.__super__.constructor.call(this, $element, options);
|
ArrayAdapter.__super__.constructor.call(this, $element, options);
|
||||||
|
|
||||||
this.addOptions(this.convertToOptions(data));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils.Extend(ArrayAdapter, SelectAdapter);
|
Utils.Extend(ArrayAdapter, SelectAdapter);
|
||||||
|
|
||||||
|
ArrayAdapter.prototype.bind = function (container, $container) {
|
||||||
|
ArrayAdapter.__super__.bind.call(this, container, $container);
|
||||||
|
|
||||||
|
this.addOptions(this.convertToOptions(this._dataToConvert));
|
||||||
|
};
|
||||||
|
|
||||||
ArrayAdapter.prototype.select = function (data) {
|
ArrayAdapter.prototype.select = function (data) {
|
||||||
var $option = this.$element.find('option').filter(function (i, elm) {
|
var $option = this.$element.find('option').filter(function (i, elm) {
|
||||||
return elm.value == data.id.toString();
|
return elm.value == data.id.toString();
|
||||||
|
@ -71,6 +71,9 @@ test('current gets default for single', function (assert) {
|
|||||||
|
|
||||||
var data = new ArrayData($select, arrayOptions);
|
var data = new ArrayData($select, arrayOptions);
|
||||||
|
|
||||||
|
var container = new MockContainer();
|
||||||
|
data.bind(container, $('<div></div>'));
|
||||||
|
|
||||||
data.current(function (val) {
|
data.current(function (val) {
|
||||||
assert.equal(
|
assert.equal(
|
||||||
val.length,
|
val.length,
|
||||||
@ -93,6 +96,9 @@ test('current gets default for multiple', function (assert) {
|
|||||||
|
|
||||||
var data = new ArrayData($select, arrayOptions);
|
var data = new ArrayData($select, arrayOptions);
|
||||||
|
|
||||||
|
var container = new MockContainer();
|
||||||
|
data.bind(container, $('<div></div>'));
|
||||||
|
|
||||||
data.current(function (val) {
|
data.current(function (val) {
|
||||||
assert.equal(
|
assert.equal(
|
||||||
val.length,
|
val.length,
|
||||||
@ -107,6 +113,9 @@ test('current works with existing selections', function (assert) {
|
|||||||
|
|
||||||
var data = new ArrayData($select, arrayOptions);
|
var data = new ArrayData($select, arrayOptions);
|
||||||
|
|
||||||
|
var container = new MockContainer();
|
||||||
|
data.bind(container, $('<div></div>'));
|
||||||
|
|
||||||
$select.val(['One']);
|
$select.val(['One']);
|
||||||
|
|
||||||
data.current(function (val) {
|
data.current(function (val) {
|
||||||
@ -137,6 +146,9 @@ test('current works with selected data', function (assert) {
|
|||||||
|
|
||||||
var data = new ArrayData($select, arrayOptions);
|
var data = new ArrayData($select, arrayOptions);
|
||||||
|
|
||||||
|
var container = new MockContainer();
|
||||||
|
data.bind(container, $('<div></div>'));
|
||||||
|
|
||||||
data.select({
|
data.select({
|
||||||
id: '2',
|
id: '2',
|
||||||
text: '2'
|
text: '2'
|
||||||
@ -170,6 +182,9 @@ test('select works for single', function (assert) {
|
|||||||
|
|
||||||
var data = new ArrayData($select, arrayOptions);
|
var data = new ArrayData($select, arrayOptions);
|
||||||
|
|
||||||
|
var container = new MockContainer();
|
||||||
|
data.bind(container, $('<div></div>'));
|
||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
$select.val(),
|
$select.val(),
|
||||||
'default',
|
'default',
|
||||||
@ -193,6 +208,9 @@ test('multiple sets the value', function (assert) {
|
|||||||
|
|
||||||
var data = new ArrayData($select, arrayOptions);
|
var data = new ArrayData($select, arrayOptions);
|
||||||
|
|
||||||
|
var container = new MockContainer();
|
||||||
|
data.bind(container, $('<div></div>'));
|
||||||
|
|
||||||
assert.ok(
|
assert.ok(
|
||||||
$select.val() == null || $select.val().length == 0,
|
$select.val() == null || $select.val().length == 0,
|
||||||
'nothing should be selected'
|
'nothing should be selected'
|
||||||
@ -211,6 +229,9 @@ test('multiple adds to the old value', function (assert) {
|
|||||||
|
|
||||||
var data = new ArrayData($select, arrayOptions);
|
var data = new ArrayData($select, arrayOptions);
|
||||||
|
|
||||||
|
var container = new MockContainer();
|
||||||
|
data.bind(container, $('<div></div>'));
|
||||||
|
|
||||||
$select.val(['One']);
|
$select.val(['One']);
|
||||||
|
|
||||||
assert.deepEqual($select.val(), ['One']);
|
assert.deepEqual($select.val(), ['One']);
|
||||||
@ -228,6 +249,9 @@ test('option tags are automatically generated', function (assert) {
|
|||||||
|
|
||||||
var data = new ArrayData($select, arrayOptions);
|
var data = new ArrayData($select, arrayOptions);
|
||||||
|
|
||||||
|
var container = new MockContainer();
|
||||||
|
data.bind(container, $('<div></div>'));
|
||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
$select.find('option').length,
|
$select.find('option').length,
|
||||||
4,
|
4,
|
||||||
@ -235,11 +259,32 @@ test('option tags are automatically generated', function (assert) {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('automatically generated option tags have a result id', function (assert) {
|
||||||
|
var $select = $('#qunit-fixture .single-empty');
|
||||||
|
|
||||||
|
var data = new ArrayData($select, arrayOptions);
|
||||||
|
|
||||||
|
var container = new MockContainer();
|
||||||
|
data.bind(container, $('<div></div>'));
|
||||||
|
|
||||||
|
data.select({
|
||||||
|
id: 'default'
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
Utils.GetData($select.find(':selected')[0], 'data')._resultId,
|
||||||
|
'<option> default should have a result ID assigned'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test('option tags can receive new data', function(assert) {
|
test('option tags can receive new data', function(assert) {
|
||||||
var $select = $('#qunit-fixture .single');
|
var $select = $('#qunit-fixture .single');
|
||||||
|
|
||||||
var data = new ArrayData($select, extraOptions);
|
var data = new ArrayData($select, extraOptions);
|
||||||
|
|
||||||
|
var container = new MockContainer();
|
||||||
|
data.bind(container, $('<div></div>'));
|
||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
$select.find('option').length,
|
$select.find('option').length,
|
||||||
2,
|
2,
|
||||||
@ -270,6 +315,9 @@ test('optgroup tags can also be generated', function (assert) {
|
|||||||
|
|
||||||
var data = new ArrayData($select, nestedOptions);
|
var data = new ArrayData($select, nestedOptions);
|
||||||
|
|
||||||
|
var container = new MockContainer();
|
||||||
|
data.bind(container, $('<div></div>'));
|
||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
$select.find('option').length,
|
$select.find('option').length,
|
||||||
1,
|
1,
|
||||||
@ -288,6 +336,9 @@ test('optgroup tags have the right properties', function (assert) {
|
|||||||
|
|
||||||
var data = new ArrayData($select, nestedOptions);
|
var data = new ArrayData($select, nestedOptions);
|
||||||
|
|
||||||
|
var container = new MockContainer();
|
||||||
|
data.bind(container, $('<div></div>'));
|
||||||
|
|
||||||
var $group = $select.children('optgroup');
|
var $group = $select.children('optgroup');
|
||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
@ -328,5 +379,8 @@ test('existing selections are respected on initialization', function (assert) {
|
|||||||
|
|
||||||
var data = new ArrayData($select, options);
|
var data = new ArrayData($select, options);
|
||||||
|
|
||||||
|
var container = new MockContainer();
|
||||||
|
data.bind(container, $('<div></div>'));
|
||||||
|
|
||||||
assert.equal($select.val(), 'Second');
|
assert.equal($select.val(), 'Second');
|
||||||
});
|
});
|
@ -23,6 +23,9 @@ test('test that options can be selected', function (assert) {
|
|||||||
|
|
||||||
var adapter = new InputAdapter($element, options);
|
var adapter = new InputAdapter($element, options);
|
||||||
|
|
||||||
|
var container = new MockContainer();
|
||||||
|
adapter.bind(container, $('<div></div>'));
|
||||||
|
|
||||||
adapter.select({
|
adapter.select({
|
||||||
id: 'test'
|
id: 'test'
|
||||||
});
|
});
|
||||||
@ -48,6 +51,9 @@ test('unselect the single selected option clears the value', function (assert) {
|
|||||||
|
|
||||||
var adapter = new InputAdapter($element, options);
|
var adapter = new InputAdapter($element, options);
|
||||||
|
|
||||||
|
var container = new MockContainer();
|
||||||
|
adapter.bind(container, $('<div></div>'));
|
||||||
|
|
||||||
adapter.unselect({
|
adapter.unselect({
|
||||||
id: 'test'
|
id: 'test'
|
||||||
});
|
});
|
||||||
@ -81,6 +87,9 @@ test('options can be unselected individually', function (assert) {
|
|||||||
|
|
||||||
var adapter = new InputAdapter($element, options);
|
var adapter = new InputAdapter($element, options);
|
||||||
|
|
||||||
|
var container = new MockContainer();
|
||||||
|
adapter.bind(container, $('<div></div>'));
|
||||||
|
|
||||||
adapter.unselect({
|
adapter.unselect({
|
||||||
id: 'test2'
|
id: 'test2'
|
||||||
});
|
});
|
||||||
@ -107,6 +116,9 @@ test('default values can be set', function (assert) {
|
|||||||
|
|
||||||
var adapter = new InputAdapter($element, options);
|
var adapter = new InputAdapter($element, options);
|
||||||
|
|
||||||
|
var container = new MockContainer();
|
||||||
|
adapter.bind(container, $('<div></div>'));
|
||||||
|
|
||||||
adapter.current(function (data) {
|
adapter.current(function (data) {
|
||||||
assert.equal(
|
assert.equal(
|
||||||
data.length,
|
data.length,
|
||||||
@ -142,6 +154,9 @@ test('no default value', function (assert) {
|
|||||||
|
|
||||||
var adapter = new InputAdapter($element, options);
|
var adapter = new InputAdapter($element, options);
|
||||||
|
|
||||||
|
var container = new MockContainer();
|
||||||
|
adapter.bind(container, $('<div></div>'));
|
||||||
|
|
||||||
adapter.current(function (data) {
|
adapter.current(function (data) {
|
||||||
assert.equal(
|
assert.equal(
|
||||||
data.length,
|
data.length,
|
||||||
|
Loading…
Reference in New Issue
Block a user