From b1ffa9a9d78b0c2c5ad6ed5a1d84e380d5cfd010 Mon Sep 17 00:00:00 2001 From: seanhelvey Date: Fri, 13 Dec 2024 11:56:53 -0800 Subject: Fixed #13883 -- Rendered named choice groups with in FilteredSelectMultiple. This patch adds support for 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 --- js_tests/admin/SelectBox.test.js | 160 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) (limited to 'js_tests') 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; + $('').appendTo('#qunit-fixture'); + const grp = $('').appendTo('#id'); + $('').appendTo(grp); + $('').appendTo('#id'); + $('').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; + $('').appendTo('#qunit-fixture'); + // Add optgroups in non-alphabetical order + const grp2 = $('').appendTo('#id'); + $('').appendTo(grp2); + $('').appendTo(grp2); + const grp1 = $('').appendTo('#id'); + $('').appendTo(grp1); + $('').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; + $('').appendTo('#qunit-fixture'); + // Add options in non-alphabetical order + $('').appendTo('#id'); + $('').appendTo('#id'); + $('').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; + $('').appendTo('#qunit-fixture'); + $('').appendTo('#qunit-fixture'); + + // Add options with optgroups to from_id in non-alphabetical order + const grp2 = $('').appendTo('#from_id'); + $('').appendTo(grp2); + const grp1 = $('').appendTo('#from_id'); + $('').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; + $('').appendTo('#qunit-fixture'); + $('').appendTo('#qunit-fixture'); + + // Add options without optgroups in non-alphabetical order + $('').appendTo('#from_id'); + $('').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; + $('').appendTo('#qunit-fixture'); + $('').appendTo('#qunit-fixture'); + + // Add options with optgroups in non-alphabetical order + const grp2 = $('').appendTo('#from_id'); + $('').appendTo(grp2); + const grp1 = $('').appendTo('#from_id'); + $('').appendTo(grp1); + $('').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; + $('').appendTo('#qunit-fixture'); + $('').appendTo('#qunit-fixture'); + + // Add options without optgroups in non-alphabetical order + $('').appendTo('#from_id'); + $('').appendTo('#from_id'); + $('').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'); +}); -- cgit v1.3