summaryrefslogtreecommitdiff
path: root/js_tests
diff options
context:
space:
mode:
authorseanhelvey <sean.helvey@gmail.com>2024-12-13 11:56:53 -0800
committerJacob Walls <jacobtylerwalls@gmail.com>2026-01-22 21:12:23 -0500
commitb1ffa9a9d78b0c2c5ad6ed5a1d84e380d5cfd010 (patch)
tree0fcfd9b90c788e21e58cb9249f4119062b8bfc4e /js_tests
parent3851601b2e080df34fb9227fe5d2fd43af604263 (diff)
Fixed #13883 -- Rendered named choice groups with <optgroup> in FilteredSelectMultiple.
This patch adds support for <optgroup>s in FilteredSelectMultiple widgets. When a popup returns a new object, if the source field contains optgroup choices, the optgroup is now also included in the response data. Additionally, this adds error handling for invalid source_model parameters to prevent crashes and display user-friendly error messages instead. Co-authored-by: Michael McLarnon <mmclar@gmail.com>
Diffstat (limited to 'js_tests')
-rw-r--r--js_tests/admin/SelectBox.test.js160
1 files changed, 160 insertions, 0 deletions
diff --git a/js_tests/admin/SelectBox.test.js b/js_tests/admin/SelectBox.test.js
index 7d127b5d59..4915ba6b9b 100644
--- a/js_tests/admin/SelectBox.test.js
+++ b/js_tests/admin/SelectBox.test.js
@@ -45,3 +45,163 @@ QUnit.test('preserve scroll position', function(assert) {
assert.equal(toSelectBox.options.length, selectedOptions.length);
assert.notEqual(fromSelectBox.scrollTop, 0);
});
+
+QUnit.test('retain optgroups', function(assert) {
+ const $ = django.jQuery;
+ $('<select id="id"></select>').appendTo('#qunit-fixture');
+ const grp = $('<optgroup label="group one">').appendTo('#id');
+ $('<option value="0">A</option>').appendTo(grp);
+ $('</optgroup>').appendTo('#id');
+ $('<option value="1">B</option>').appendTo('#id');
+ SelectBox.init('id');
+ SelectBox.redisplay('id');
+ assert.equal($('#id option').length, 2);
+ assert.equal($('#id optgroup').length, 1);
+});
+
+QUnit.test('sort optgroups', function(assert) {
+ const $ = django.jQuery;
+ $('<select id="id"></select>').appendTo('#qunit-fixture');
+ // Add optgroups in non-alphabetical order
+ const grp2 = $('<optgroup label="Group B">').appendTo('#id');
+ $('<option value="3">Item 3</option>').appendTo(grp2);
+ $('<option value="4">Item 4</option>').appendTo(grp2);
+ const grp1 = $('<optgroup label="Group A">').appendTo('#id');
+ $('<option value="1">Item 1</option>').appendTo(grp1);
+ $('<option value="2">Item 2</option>').appendTo(grp1);
+
+ SelectBox.init('id');
+
+ // Verify cache is sorted by group then by item
+ assert.equal(SelectBox.cache.id.length, 4);
+ assert.equal(SelectBox.cache.id[0].group, 'Group A');
+ assert.equal(SelectBox.cache.id[0].text, 'Item 1');
+ assert.equal(SelectBox.cache.id[1].group, 'Group A');
+ assert.equal(SelectBox.cache.id[1].text, 'Item 2');
+ assert.equal(SelectBox.cache.id[2].group, 'Group B');
+ assert.equal(SelectBox.cache.id[2].text, 'Item 3');
+ assert.equal(SelectBox.cache.id[3].group, 'Group B');
+ assert.equal(SelectBox.cache.id[3].text, 'Item 4');
+});
+
+QUnit.test('do not sort when no optgroups', function(assert) {
+ const $ = django.jQuery;
+ $('<select id="id"></select>').appendTo('#qunit-fixture');
+ // Add options in non-alphabetical order
+ $('<option value="3">Zebra</option>').appendTo('#id');
+ $('<option value="1">Apple</option>').appendTo('#id');
+ $('<option value="2">Banana</option>').appendTo('#id');
+
+ SelectBox.init('id');
+
+ // Verify cache preserves original order (not sorted)
+ assert.equal(SelectBox.cache.id.length, 3);
+ assert.equal(SelectBox.cache.id[0].text, 'Zebra');
+ assert.equal(SelectBox.cache.id[1].text, 'Apple');
+ assert.equal(SelectBox.cache.id[2].text, 'Banana');
+});
+
+QUnit.test('move with optgroups sorts', function(assert) {
+ const $ = django.jQuery;
+ $('<select id="from_id"></select>').appendTo('#qunit-fixture');
+ $('<select id="to_id"></select>').appendTo('#qunit-fixture');
+
+ // Add options with optgroups to from_id in non-alphabetical order
+ const grp2 = $('<optgroup label="Group B">').appendTo('#from_id');
+ $('<option value="2">Item 2</option>').appendTo(grp2);
+ const grp1 = $('<optgroup label="Group A">').appendTo('#from_id');
+ $('<option value="1">Item 1</option>').appendTo(grp1);
+
+ SelectBox.init('from_id');
+ SelectBox.init('to_id');
+
+ // Select and move item
+ document.getElementById('from_id').options[0].selected = true;
+ SelectBox.move('from_id', 'to_id');
+
+ // Verify to_id cache is sorted (even though we only added one item)
+ assert.equal(SelectBox.cache.to_id.length, 1);
+ assert.equal(SelectBox.cache.to_id[0].group, 'Group B');
+ assert.equal(SelectBox.cache.to_id[0].text, 'Item 2');
+});
+
+QUnit.test('move without optgroups does not sort', function(assert) {
+ const $ = django.jQuery;
+ $('<select id="from_id"></select>').appendTo('#qunit-fixture');
+ $('<select id="to_id"></select>').appendTo('#qunit-fixture');
+
+ // Add options without optgroups in non-alphabetical order
+ $('<option value="3">Zebra</option>').appendTo('#from_id');
+ $('<option value="1">Apple</option>').appendTo('#from_id');
+
+ SelectBox.init('from_id');
+ SelectBox.init('to_id');
+
+ // Select and move first item (Zebra)
+ document.getElementById('from_id').options[0].selected = true;
+ SelectBox.move('from_id', 'to_id');
+
+ // Verify to_id cache preserves order (not sorted)
+ assert.equal(SelectBox.cache.to_id.length, 1);
+ assert.equal(SelectBox.cache.to_id[0].text, 'Zebra');
+
+ // Move second item (Apple)
+ document.getElementById('from_id').options[0].selected = true;
+ SelectBox.move('from_id', 'to_id');
+
+ // Verify items are in order they were added, not alphabetical
+ assert.equal(SelectBox.cache.to_id.length, 2);
+ assert.equal(SelectBox.cache.to_id[0].text, 'Zebra');
+ assert.equal(SelectBox.cache.to_id[1].text, 'Apple');
+});
+
+QUnit.test('move_all with optgroups sorts', function(assert) {
+ const $ = django.jQuery;
+ $('<select id="from_id"></select>').appendTo('#qunit-fixture');
+ $('<select id="to_id"></select>').appendTo('#qunit-fixture');
+
+ // Add options with optgroups in non-alphabetical order
+ const grp2 = $('<optgroup label="Group B">').appendTo('#from_id');
+ $('<option value="3">Zebra</option>').appendTo(grp2);
+ const grp1 = $('<optgroup label="Group A">').appendTo('#from_id');
+ $('<option value="1">Apple</option>').appendTo(grp1);
+ $('<option value="2">Banana</option>').appendTo(grp1);
+
+ SelectBox.init('from_id');
+ SelectBox.init('to_id');
+
+ // Move all items
+ SelectBox.move_all('from_id', 'to_id');
+
+ // Verify to_id cache is sorted by group
+ assert.equal(SelectBox.cache.to_id.length, 3);
+ assert.equal(SelectBox.cache.to_id[0].group, 'Group A');
+ assert.equal(SelectBox.cache.to_id[0].text, 'Apple');
+ assert.equal(SelectBox.cache.to_id[1].group, 'Group A');
+ assert.equal(SelectBox.cache.to_id[1].text, 'Banana');
+ assert.equal(SelectBox.cache.to_id[2].group, 'Group B');
+ assert.equal(SelectBox.cache.to_id[2].text, 'Zebra');
+});
+
+QUnit.test('move_all without optgroups does not sort', function(assert) {
+ const $ = django.jQuery;
+ $('<select id="from_id"></select>').appendTo('#qunit-fixture');
+ $('<select id="to_id"></select>').appendTo('#qunit-fixture');
+
+ // Add options without optgroups in non-alphabetical order
+ $('<option value="3">Zebra</option>').appendTo('#from_id');
+ $('<option value="1">Apple</option>').appendTo('#from_id');
+ $('<option value="2">Banana</option>').appendTo('#from_id');
+
+ SelectBox.init('from_id');
+ SelectBox.init('to_id');
+
+ // Move all items
+ SelectBox.move_all('from_id', 'to_id');
+
+ // Verify to_id cache preserves original order (not sorted)
+ assert.equal(SelectBox.cache.to_id.length, 3);
+ assert.equal(SelectBox.cache.to_id[0].text, 'Zebra');
+ assert.equal(SelectBox.cache.to_id[1].text, 'Apple');
+ assert.equal(SelectBox.cache.to_id[2].text, 'Banana');
+});