Skip to content

Commit 1ea69fb

Browse files
authored
Accordion: update items without losing their states (T1190407, T1298282) (DevExpress#30268)
1 parent 0e0aac8 commit 1ea69fb

File tree

2 files changed

+80
-46
lines changed

2 files changed

+80
-46
lines changed

packages/devextreme/js/__internal/ui/m_accordion.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,8 +414,8 @@ class Accordion extends CollectionWidget<AccordionProperties> {
414414
const { repaintChangesOnly } = this.option();
415415

416416
if (repaintChangesOnly === true && args.fullName === 'items') {
417-
this._updateItemHeightsWrapper(true);
418417
this._renderSelection(this._getSelectedItemIndices(), []);
418+
this._updateItemHeightsWrapper(true);
419419
}
420420
break;
421421
}

packages/devextreme/testing/tests/DevExpress.ui.widgets/accordion.tests.js

Lines changed: 79 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ QUnit.module('widget rendering', moduleSetup, () => {
100100

101101
instance.expandItem(1);
102102

103-
assert.ok(!$items.eq(0).hasClass(ACCORDION_ITEM_OPENED_CLASS), 'closed item has no \'item opened\' class');
104-
assert.ok($items.eq(1).hasClass(ACCORDION_ITEM_OPENED_CLASS), 'opened item has \'item opened\' class');
103+
assert.ok(!$items.eq(0).hasClass(ACCORDION_ITEM_OPENED_CLASS), 'closed item has no "item opened" class');
104+
assert.ok($items.eq(1).hasClass(ACCORDION_ITEM_OPENED_CLASS), 'opened item has "item opened" class');
105105
});
106106

107107
QUnit.test('height should be correctly updated on dxshown event', function(assert) {
@@ -127,7 +127,7 @@ QUnit.module('widget rendering', moduleSetup, () => {
127127
}
128128
});
129129

130-
QUnit.test('animation shouldn\'t change transform property (T354912)', function(assert) {
130+
QUnit.test('animation should not change transform property (T354912)', function(assert) {
131131
const origAnimate = fx.animate;
132132

133133
const $element = $('<div>').appendTo('#qunit-fixture').dxAccordion({
@@ -148,7 +148,7 @@ QUnit.module('widget rendering', moduleSetup, () => {
148148
}
149149
});
150150

151-
QUnit.test('Item body should be rendered on item opening when the \'deferRendering\' option is true', function(assert) {
151+
QUnit.test('Item body should be rendered on item opening when the deferRendering option is true', function(assert) {
152152
const $element = this.$element.dxAccordion({
153153
items: this.items,
154154
selectedIndex: 0,
@@ -161,7 +161,7 @@ QUnit.module('widget rendering', moduleSetup, () => {
161161
assert.equal($element.find('.' + ACCORDION_ITEM_BODY_CLASS).length, 2, 'body is rendered for just opened item');
162162
});
163163

164-
QUnit.test('Item body should be rendered on item changing when the \'deferRendering\' option is true (T586536)', function(assert) {
164+
QUnit.test('Item body should be rendered on item changing when the deferRendering option is true (T586536)', function(assert) {
165165
const $element = this.$element.dxAccordion({
166166
items: this.items,
167167
selectedIndex: 0,
@@ -174,7 +174,7 @@ QUnit.module('widget rendering', moduleSetup, () => {
174174
assert.equal($element.find('.' + ACCORDION_ITEM_BODY_CLASS).length, 1, 'body is rendered');
175175
});
176176

177-
QUnit.test('Item body should be rendered on item changing and selectionChanging when the \'deferRendering\' option is true (T586536)', function(assert) {
177+
QUnit.test('Item body should be rendered on item changing and selectionChanging when the deferRendering option is true (T586536)', function(assert) {
178178
const $element = this.$element.dxAccordion({
179179
items: this.items,
180180
selectedIndex: 0,
@@ -189,7 +189,7 @@ QUnit.module('widget rendering', moduleSetup, () => {
189189
assert.equal($element.find('.' + ACCORDION_ITEM_BODY_CLASS).length, 2, 'bodies were rendered');
190190
});
191191

192-
QUnit.test('Widget should be rerendered on the \'deferRendering\' option change', function(assert) {
192+
QUnit.test('Widget should be rerendered on the deferRendering option change', function(assert) {
193193
let renderCount = 0;
194194
let prevRenderCount;
195195

@@ -294,7 +294,7 @@ QUnit.module('nested accordion', moduleSetup, () => {
294294
});
295295

296296
QUnit.module('widget options', moduleSetup, () => {
297-
QUnit.test('\'onItemTitleClick\' option', function(assert) {
297+
QUnit.test('onItemTitleClick option', function(assert) {
298298
let actionFiredValue = 0;
299299

300300
this.$element.dxAccordion({
@@ -313,7 +313,7 @@ QUnit.module('widget options', moduleSetup, () => {
313313
assert.equal(actionFiredValue, 2, 'second item was clicked');
314314
});
315315

316-
QUnit.test('\'onItemHold\' option', function(assert) {
316+
QUnit.test('onItemHold option', function(assert) {
317317
let actionFiredValue = 0;
318318

319319
this.$element.dxAccordion({
@@ -328,7 +328,7 @@ QUnit.module('widget options', moduleSetup, () => {
328328
assert.equal(actionFiredValue, 1, 'action is fired');
329329
});
330330

331-
QUnit.test('\'itemHoldTimeout\' option', function(assert) {
331+
QUnit.test('itemHoldTimeout option', function(assert) {
332332
let actionFiredValue = 0;
333333

334334
this.$element.dxAccordion({
@@ -347,7 +347,7 @@ QUnit.module('widget options', moduleSetup, () => {
347347
assert.equal(actionFiredValue, 1, 'action is fired');
348348
});
349349

350-
QUnit.test('\'onSelectionChanged\' option', function(assert) {
350+
QUnit.test('onSelectionChanged option', function(assert) {
351351
let actionFiredValue = 0;
352352

353353
this.$element.dxAccordion({
@@ -473,7 +473,7 @@ QUnit.module('widget options', moduleSetup, () => {
473473
assert.ok(!this.$element.is(':visible'), 'widget is hidden');
474474
});
475475

476-
QUnit.test('height option in \'auto\' mode', function(assert) {
476+
QUnit.test('height option in auto mode', function(assert) {
477477
const $element = $('#html-template-accordion');
478478
const instance = $element.dxAccordion({
479479
items: [
@@ -519,7 +519,7 @@ QUnit.module('widget options', moduleSetup, () => {
519519
assert.notEqual(instance.itemElements().eq(1).get(0).style.height, '', 'auto height not set');
520520
});
521521

522-
QUnit.test('height option in \'auto\' mode when widget is multiple', function(assert) {
522+
QUnit.test('height option in auto mode when widget is multiple', function(assert) {
523523
const $element = $('#html-template-accordion');
524524
const instance = $element.dxAccordion({
525525
items: [
@@ -668,15 +668,15 @@ QUnit.module('widget options changed', moduleSetup, () => {
668668
assert.ok(this.$element.find('.' + ACCORDION_ITEM_CLASS).eq(1).hasClass(ACCORDION_ITEM_OPENED_CLASS), 'second item is opened');
669669

670670
instance.option('selectedIndex', -1);
671-
assert.equal(instance.option('selectedIndex'), 1, '\'selectedIndex\' option set to first when trying to set index which is out of range (-1)');
671+
assert.equal(instance.option('selectedIndex'), 1, 'selectedIndex option set to first when trying to set index which is out of range (-1)');
672672
assert.ok(this.$element.find('.' + ACCORDION_ITEM_CLASS).eq(1).hasClass(ACCORDION_ITEM_OPENED_CLASS), 'first item is opened when index is out of range (-1)');
673673

674674
instance.option('selectedIndex', 5);
675-
assert.equal(instance.option('selectedIndex'), 1, '\'selectedIndex\' option set to first when trying to set index which is out of range (5)');
675+
assert.equal(instance.option('selectedIndex'), 1, 'selectedIndex option set to first when trying to set index which is out of range (5)');
676676
assert.ok(this.$element.find('.' + ACCORDION_ITEM_CLASS).eq(1).hasClass(ACCORDION_ITEM_OPENED_CLASS), 'first item is opened when index is out of range (5)');
677677
});
678678

679-
QUnit.test('\'onItemTitleClick\' option changed', function(assert) {
679+
QUnit.test('onItemTitleClick option changed', function(assert) {
680680
let firstActionFired;
681681
let secondActionFired;
682682

@@ -860,7 +860,7 @@ QUnit.module('widget options changed', moduleSetup, () => {
860860
}
861861
});
862862

863-
QUnit.test('\'itemHoldTimeout\' option changed', function(assert) {
863+
QUnit.test('itemHoldTimeout option changed', function(assert) {
864864
let actionFiredValue = 0;
865865

866866
const instance = this.$element.dxAccordion({
@@ -887,13 +887,13 @@ QUnit.module('widget options changed', moduleSetup, () => {
887887
}).dxAccordion('instance');
888888

889889
instance.option('disabled', true);
890-
assert.ok(this.$element.hasClass('dx-state-disabled'), 'widget has \'disabled\' class');
890+
assert.ok(this.$element.hasClass('dx-state-disabled'), 'widget has disabled class');
891891
$(this.$element.find('.' + ACCORDION_ITEM_TITLE_CLASS).eq(1)).trigger('dxclick');
892892
assert.ok(this.$element.find('.' + ACCORDION_ITEM_CLASS).eq(0).hasClass(ACCORDION_ITEM_OPENED_CLASS), 'no reaction after clicking on disabled widget');
893893
assert.ok(!this.$element.find('.' + ACCORDION_ITEM_CLASS).eq(1).hasClass(ACCORDION_ITEM_OPENED_CLASS), 'no reaction after clicking on disabled widget');
894894

895895
instance.option('disabled', false);
896-
assert.ok(!this.$element.hasClass('dx-state-disabled'), 'widget has no \'disabled\' class');
896+
assert.ok(!this.$element.hasClass('dx-state-disabled'), 'widget has no disabled class');
897897
$(this.$element.find('.' + ACCORDION_ITEM_TITLE_CLASS).eq(1)).trigger('dxclick');
898898
assert.ok(!this.$element.find('.' + ACCORDION_ITEM_CLASS).eq(0).hasClass(ACCORDION_ITEM_OPENED_CLASS), 'item is unselected after clicking on the other title on enabled widget');
899899
assert.ok(this.$element.find('.' + ACCORDION_ITEM_CLASS).eq(1).hasClass(ACCORDION_ITEM_OPENED_CLASS), 'item is selected after clicking on enabled widget');
@@ -910,7 +910,7 @@ QUnit.module('widget options changed', moduleSetup, () => {
910910
assert.ok(this.$element.is(':visible'), 'widget is shown');
911911
});
912912

913-
QUnit.test('\'onItemRendered\' option', function(assert) {
913+
QUnit.test('onItemRendered option', function(assert) {
914914
let actionValue = 0;
915915

916916
this.$element.dxAccordion({
@@ -920,7 +920,7 @@ QUnit.module('widget options changed', moduleSetup, () => {
920920
}
921921
});
922922

923-
assert.equal(actionValue, this.items.length, '\'onItemRendered\' fired once for each item');
923+
assert.equal(actionValue, this.items.length, 'onItemRendered fired once for each item');
924924
});
925925

926926
QUnit.test('subscribe on the itemClick event when a title of item is changed', function(assert) {
@@ -970,7 +970,7 @@ QUnit.module('widget behavior', moduleSetup, () => {
970970
$(this.$element.find('.' + ACCORDION_ITEM_TITLE_CLASS).eq(1)).trigger('dxclick');
971971

972972
assert.equal(instance.option('selectedIndex'), 1, 'second item is selected');
973-
assert.notEqual(this.$element.find('.' + ACCORDION_ITEM_BODY_CLASS).eq(1).css('display'), 'none', 'selected item\'s content is shown');
973+
assert.notEqual(this.$element.find('.' + ACCORDION_ITEM_BODY_CLASS).eq(1).css('display'), 'none', 'selected item content is shown');
974974
});
975975

976976
QUnit.test('only clicked item is opened', function(assert) {
@@ -998,7 +998,7 @@ QUnit.module('widget behavior', moduleSetup, () => {
998998

999999
assert.ok($items.eq(2).hasClass(ACCORDION_ITEM_OPENED_CLASS), 'specified item is opened');
10001000
assert.equal(this.$element.find('.' + ACCORDION_ITEM_OPENED_CLASS).length, 1, 'only one item is opened');
1001-
assert.equal(instance.option('selectedIndex'), 2, '\'selectedIndex\' is correct');
1001+
assert.equal(instance.option('selectedIndex'), 2, 'selectedIndex is correct');
10021002

10031003
instance.option('multiple', true);
10041004
instance.expandItem(0);
@@ -1070,7 +1070,7 @@ QUnit.module('widget behavior', moduleSetup, () => {
10701070
assert.equal(actionValue, 1, 'method executed after animation completed');
10711071
});
10721072

1073-
QUnit.test('\'onItemClick\' firing conditions', function(assert) {
1073+
QUnit.test('onItemClick firing conditions', function(assert) {
10741074
let titleActionFired = 0;
10751075
let itemActionFired = 0;
10761076

@@ -1089,11 +1089,11 @@ QUnit.module('widget behavior', moduleSetup, () => {
10891089

10901090
$($items.eq(0).find('.' + ACCORDION_ITEM_TITLE_CLASS)).trigger('dxclick');
10911091
assert.equal(titleActionFired, 1, 'onItemTitleClick was fired on itemTitle click');
1092-
assert.equal(itemActionFired, 1, '\'onItemClick\' was fired on itemTitle click');
1092+
assert.equal(itemActionFired, 1, 'onItemClick was fired on itemTitle click');
10931093

10941094
$($items.eq(0).find('.' + ACCORDION_ITEM_BODY_CLASS)).trigger('dxclick');
10951095
assert.equal(titleActionFired, 1, 'onItemTitleClick was not fired on itemContent click');
1096-
assert.equal(itemActionFired, 2, '\'onItemClick\' was fired on itemContent click');
1096+
assert.equal(itemActionFired, 2, 'onItemClick was fired on itemContent click');
10971097
});
10981098
});
10991099

@@ -1257,7 +1257,7 @@ QUnit.module('Live Update', {
12571257
}, key: 1 }];
12581258
store.push(pushData);
12591259

1260-
assert.equal(this.itemRenderedSpy.callCount, 1, 'only one item is updated after push');
1260+
assert.strictEqual(this.itemRenderedSpy.callCount, 1, 'only one item is updated after push');
12611261
assert.deepEqual(this.itemRenderedSpy.firstCall.args[0].itemData, pushData[0].data, 'check updated item');
12621262
});
12631263

@@ -1271,7 +1271,7 @@ QUnit.module('Live Update', {
12711271
} }];
12721272
store.push(pushData);
12731273

1274-
assert.equal(this.itemRenderedSpy.callCount, 1, 'only one item is updated after push');
1274+
assert.strictEqual(this.itemRenderedSpy.callCount, 1, 'only one item is updated after push');
12751275
assert.deepEqual(this.itemRenderedSpy.firstCall.args[0].itemData, pushData[0].data, 'check added item');
12761276
});
12771277

@@ -1282,10 +1282,10 @@ QUnit.module('Live Update', {
12821282
const pushData = [{ type: 'remove', key: 1 }];
12831283
store.push(pushData);
12841284

1285-
assert.equal(this.itemRenderedSpy.callCount, 0, 'items are not refreshed after remove');
1286-
assert.equal(this.itemDeletedSpy.callCount, 1, 'removed items count');
1287-
assert.deepEqual(this.itemDeletedSpy.firstCall.args[0].itemData.text, '1', 'check removed item');
1288-
assert.equal(accordion.option('items').length, 2, ' items count');
1285+
assert.strictEqual(this.itemRenderedSpy.callCount, 0, 'items are not refreshed after remove');
1286+
assert.strictEqual(this.itemDeletedSpy.callCount, 1, 'removed items count');
1287+
assert.strictEqual(this.itemDeletedSpy.firstCall.args[0].itemData.text, '1', 'check removed item');
1288+
assert.strictEqual(accordion.option('items').length, 2, ' items count');
12891289
});
12901290

12911291
QUnit.test('repaintChangesOnly, update item instance', function(assert) {
@@ -1298,8 +1298,42 @@ QUnit.module('Live Update', {
12981298
};
12991299
dataSource.load();
13001300

1301-
assert.equal(this.itemRenderedSpy.callCount, 1, 'only one item is updated after reload');
1302-
assert.deepEqual(this.itemRenderedSpy.firstCall.args[0].itemData.text, '0 Updated', 'check updated item');
1301+
assert.strictEqual(this.itemRenderedSpy.callCount, 1, 'only one item is updated after reload');
1302+
assert.strictEqual(this.itemRenderedSpy.firstCall.args[0].itemData.text, '0 Updated', 'check updated item');
1303+
assert.strictEqual($(`.${ACCORDION_ITEM_OPENED_CLASS}`)[0].style.height, '', 'opened item has no additional inline height adjustments');
1304+
});
1305+
1306+
QUnit.test('repaintChangesOnly, update several items, visual states should be restored correctly after update items (T1190407)', function(assert) {
1307+
fx.off = true;
1308+
1309+
const accordion = this.createAccordion({}, true);
1310+
const dataSource = accordion.getDataSource();
1311+
1312+
accordion.option('multiple', true);
1313+
accordion.expandItem(0);
1314+
accordion.expandItem(2);
1315+
1316+
this.data[0] = {
1317+
id: 0,
1318+
text: '0 Updated',
1319+
content: '0 content'
1320+
};
1321+
this.data[2] = {
1322+
id: 2,
1323+
text: '2 Updated',
1324+
content: '2 content'
1325+
};
1326+
dataSource.load();
1327+
1328+
const $openedItems = $('#accordion').find(`.${ACCORDION_ITEM_OPENED_CLASS}`);
1329+
const haveNoInlineHeights = $openedItems
1330+
.toArray()
1331+
.every(el => !el.style.height);
1332+
1333+
assert.strictEqual($openedItems.length, 2, 'two items are opened');
1334+
assert.strictEqual(haveNoInlineHeights, true, 'opened items has no additional inline height adjustments');
1335+
1336+
fx.off = false;
13031337
});
13041338

13051339
QUnit.test('repaintChangesOnly, add item', function(assert) {
@@ -1312,8 +1346,8 @@ QUnit.module('Live Update', {
13121346
});
13131347
dataSource.load();
13141348

1315-
assert.equal(this.itemRenderedSpy.callCount, 1, 'only one item is updated after push');
1316-
assert.deepEqual(this.itemRenderedSpy.firstCall.args[0].itemData.text, '3 Inserted', 'check added item');
1349+
assert.strictEqual(this.itemRenderedSpy.callCount, 1, 'only one item is updated after push');
1350+
assert.strictEqual(this.itemRenderedSpy.firstCall.args[0].itemData.text, '3 Inserted', 'check added item');
13171351
});
13181352

13191353
QUnit.test('repaintChangesOnly, remove item', function(assert) {
@@ -1323,10 +1357,10 @@ QUnit.module('Live Update', {
13231357
this.data.splice(1, 1);
13241358
dataSource.load();
13251359

1326-
assert.equal(this.itemRenderedSpy.callCount, 0, 'items are not refreshed after remove');
1327-
assert.equal(this.itemDeletedSpy.callCount, 1, 'removed items count');
1328-
assert.equal(accordion.option('items').length, 2, ' items count');
1329-
assert.deepEqual(this.itemDeletedSpy.firstCall.args[0].itemData.text, '1', 'check removed item');
1360+
assert.strictEqual(this.itemRenderedSpy.callCount, 0, 'items are not refreshed after remove');
1361+
assert.strictEqual(this.itemDeletedSpy.callCount, 1, 'removed items count');
1362+
assert.strictEqual(accordion.option('items').length, 2, ' items count');
1363+
assert.strictEqual(this.itemDeletedSpy.firstCall.args[0].itemData.text, '1', 'check removed item');
13301364
});
13311365

13321366
QUnit.test('repaintChangesOnly, double remove the same item', function(assert) {
@@ -1340,9 +1374,9 @@ QUnit.module('Live Update', {
13401374
store.remove(1);
13411375
dataSource.load();
13421376

1343-
assert.equal(this.itemRenderedSpy.callCount, 0, 'items are not refreshed after remove');
1344-
assert.equal(this.itemDeletedSpy.callCount, 1, 'removed items count');
1345-
assert.equal(this.itemDeletedSpy.firstCall.args[0].itemData.text, '1', 'check removed item');
1377+
assert.strictEqual(this.itemRenderedSpy.callCount, 0, 'items are not refreshed after remove');
1378+
assert.strictEqual(this.itemDeletedSpy.callCount, 1, 'removed items count');
1379+
assert.strictEqual(this.itemDeletedSpy.firstCall.args[0].itemData.text, '1', 'check removed item');
13461380
});
13471381

13481382
QUnit.test('repaintChangesOnly, change selected index after remove', function(assert) {
@@ -1353,7 +1387,7 @@ QUnit.module('Live Update', {
13531387
dataSource.load();
13541388

13551389
accordion.option('selectedIndex', 1);
1356-
assert.equal(accordion.itemElements().find('.' + ACCORDION_ITEM_BODY_CLASS).length, 2);
1390+
assert.strictEqual(accordion.itemElements().find('.' + ACCORDION_ITEM_BODY_CLASS).length, 2);
13571391
});
13581392

13591393
QUnit.test('repaintChangesOnly, remove selected item', function(assert) {
@@ -1373,7 +1407,7 @@ QUnit.module('Live Update', {
13731407

13741408
accordion.isItemSelected(accordion.itemElements()[1]);
13751409

1376-
assert.equal(accordion.itemElements().find('.' + ACCORDION_ITEM_BODY_CLASS).length, 2);
1410+
assert.strictEqual(accordion.itemElements().find('.' + ACCORDION_ITEM_BODY_CLASS).length, 2);
13771411
clock.restore();
13781412
});
13791413
});

0 commit comments

Comments
 (0)