Skip to content

Commit

Permalink
MultiView: implement selectedIndex property update when selected item…
Browse files Browse the repository at this point in the history
… is hidden (#28145)

Co-authored-by: ksercs <[email protected]>
  • Loading branch information
nikkithelegendarypokemonster and ksercs authored Oct 15, 2024
1 parent c342403 commit d9df47e
Show file tree
Hide file tree
Showing 2 changed files with 267 additions and 12 deletions.
57 changes: 45 additions & 12 deletions packages/devextreme/js/__internal/ui/m_multi_view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ const MultiView = CollectionWidget.inherit({
return this.option('items').length;
},

_normalizeIndex(index) {
_normalizeIndex(index, direction, loop = true) {
const count = this._itemsCount();

if (index < 0) {
Expand All @@ -110,9 +110,9 @@ const MultiView = CollectionWidget.inherit({
index -= count;
}

const step = this._swipeDirection > 0 ? -1 : 1;

while (!this._isItemVisible(index)) {
const step = direction > 0 ? -1 : 1;
const lastNotLoopedIndex = step === -1 ? 0 : count - 1;
while (!this._isItemVisible(index) && (loop || index !== lastNotLoopedIndex)) {
index = (index + step) % count;
}

Expand Down Expand Up @@ -142,14 +142,37 @@ const MultiView = CollectionWidget.inherit({
this._initSwipeable();
},

_ensureSelectedItemIsVisible(): void {
const { items, loop, selectedIndex: currentSelectedIndex } = this.option();

if (this._isItemVisible(currentSelectedIndex)) {
return;
}

const allItemsHidden = items.every((_, index) => !this._isItemVisible(index));
if (allItemsHidden) {
this.option('selectedIndex', 0);
return;
}

const direction = -1 * this._getRTLSignCorrection();
let newSelectedIndex = this._normalizeIndex(currentSelectedIndex, direction, loop);
if (newSelectedIndex === currentSelectedIndex) {
newSelectedIndex = this._normalizeIndex(currentSelectedIndex, -direction, loop);
}

this.option('selectedIndex', newSelectedIndex);
},

_initMarkup() {
this._deferredItems = [];

this.callBase();

this._ensureSelectedItemIsVisible();
const selectedItemIndices = this._getSelectedItemIndices();

this._updateItemsVisibility(selectedItemIndices[0]);

this._setElementAria();
this._setItemsAria();
},
Expand Down Expand Up @@ -391,20 +414,14 @@ const MultiView = CollectionWidget.inherit({

e.maxLeftOffset = toNumber(loop || (rtl ? selectedIndex > firstAvailableIndex : selectedIndex < lastAvailableIndex));
e.maxRightOffset = toNumber(loop || (rtl ? selectedIndex < lastAvailableIndex : selectedIndex > firstAvailableIndex));

this._swipeDirection = null;
},

_swipeUpdateHandler(e) {
const { offset } = e;
const swipeDirection = sign(offset) * this._getRTLSignCorrection();

if (swipeDirection !== this._swipeDirection) {
this._swipeDirection = swipeDirection;
}

const selectedIndex = this.option('selectedIndex');
const newIndex = this._normalizeIndex(selectedIndex - swipeDirection);
const newIndex = this._normalizeIndex(selectedIndex - swipeDirection, swipeDirection);

if (selectedIndex === newIndex) {
return;
Expand Down Expand Up @@ -513,6 +530,15 @@ const MultiView = CollectionWidget.inherit({
this.callBase();
},

_itemOptionChanged(item, property) {
this.callBase(...arguments);

const { selectedItem } = this.option();
if (property === 'visible' && item === selectedItem) {
this._ensureSelectedItemIsVisible();
}
},

_optionChanged(args) {
const { value } = args;

Expand All @@ -533,6 +559,13 @@ const MultiView = CollectionWidget.inherit({
this._findBoundaryIndices();
this.callBase(args);
break;
case 'selectedIndex':
if (this._isItemVisible(value)) {
this.callBase(args);
} else {
this._ensureSelectedItemIsVisible();
}
break;
default:
this.callBase(args);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1763,3 +1763,225 @@ QUnit.module('swipeable disabled state', () => {
assert.equal(multiView.option('swipeEnabled'), false, 'MultiView.swipeEnabled');
});
});

QUnit.module('selectedIndex vs item.visible', () => {
QUnit.module('on init', () => {
QUnit.test('selectedIndex should be updated to the next visible item if initially selected item is hidden', function(assert) {
const $multiView = $('#multiView').dxMultiView({
items: [
{ text: '1', visible: true },
{ text: '2', visible: false },
{ text: '3', visible: true },
],
selectedIndex: 1
});
const instance = $multiView.dxMultiView('instance');

assert.strictEqual(instance.option('selectedIndex'), 2, 'selectedIndex is updated on proper index');
});

QUnit.test('selectedIndex should be updated to the next visible item to the left if initially selected item is hidden and RTL is enabled', function(assert) {
const $multiView = $('#multiView').dxMultiView({
items: [
{ text: '1', visible: true },
{ text: '2', visible: false },
{ text: '3', visible: true },
],
selectedIndex: 1,
rtlEnabled: true
});
const instance = $multiView.dxMultiView('instance');

assert.strictEqual(instance.option('selectedIndex'), 0, 'selectedIndex is updated on proper index');
});

QUnit.test('selectedIndex should be zero when all items are not visible', function(assert) {
const $multiView = $('#multiView').dxMultiView({
items: [
{ text: '1', visible: false },
{ text: '2', visible: false },
{ text: '3', visible: false },
],
selectedIndex: 1
});
const instance = $multiView.dxMultiView('instance');

assert.strictEqual(instance.option('selectedIndex'), 0, 'selectedIndex is updated on proper index');
});

QUnit.test('next visible item should be selected if currently selected item is hidden and loop=true', function(assert) {
const $multiView = $('#multiView').dxMultiView({
items: [
{ text: '1', visible: true },
{ text: '2', visible: false },
{ text: '3', visible: false }
],
selectedIndex: 2,
loop: true
});
const instance = $multiView.dxMultiView('instance');

assert.strictEqual(instance.option('selectedIndex'), 0, 'selectedIndex is updated on proper index');
});

QUnit.test('first visible item should be selected if current selected item is hidden and it is in the end and loop = true', function(assert) {
const $multiView = $('#multiView').dxMultiView({
items: [
{ text: '1', visible: true },
{ text: '2', visible: true },
{ text: '3', visible: false }
],
selectedIndex: 2,
loop: true
});
const instance = $multiView.dxMultiView('instance');

assert.strictEqual(instance.option('selectedIndex'), 0, 'selectedIndex is updated on proper index');
});
});

QUnit.module('on runtime', () => {
QUnit.test('selectedIndex should be updated to the next visible item if it is changed to a hidden item', function(assert) {
const $multiView = $('#multiView').dxMultiView({
items: [
{ text: '1', visible: true },
{ text: '2', visible: false },
{ text: '3', visible: true },
]
});
const instance = $multiView.dxMultiView('instance');
instance.option('selectedIndex', 1);

assert.strictEqual(instance.option('selectedIndex'), 2, 'selectedIndex is updated on proper index');
});

QUnit.test('selectedIndex should be set to zero if all items became hidden', function(assert) {
const $multiView = $('#multiView').dxMultiView({
items: [
{ text: '1', visible: true },
{ text: '2', visible: false },
{ text: '3', visible: true },
],
selectedIndex: 2
});
const instance = $multiView.dxMultiView('instance');
instance.option({
items: [
{ text: '1', visible: false },
{ text: '2', visible: false },
{ text: '3', visible: false },
]
});

assert.strictEqual(instance.option('selectedIndex'), 0, 'selectedIndex is updated on proper index');
});

QUnit.test('when hiding selected item selectedIndex should be set to next visible item', function(assert) {
const $multiView = $('#multiView').dxMultiView({
items: [
{ text: '1', visible: true },
{ text: '2', visible: true },
{ text: '3', visible: true },
],
selectedIndex: 1
});
const instance = $multiView.dxMultiView('instance');
instance.option('items[1].visible', false);

assert.strictEqual(instance.option('selectedIndex'), 2, 'selectedIndex is updated on proper index');
});

QUnit.test('when hiding non-selected item before selectedIndex, selectedIndex should not change', function(assert) {
const $multiView = $('#multiView').dxMultiView({
items: [
{ text: '1', visible: true },
{ text: '2', visible: true },
{ text: '3', visible: true },
],
selectedIndex: 2
});
const instance = $multiView.dxMultiView('instance');
instance.option('items[1].visible', false);

assert.strictEqual(instance.option('selectedIndex'), 2, 'selectedIndex is updated on proper index');
});

QUnit.test('when hiding selected item positioned in the end, next visible item to the left is selected', function(assert) {
const $multiView = $('#multiView').dxMultiView({
items: [
{ text: '1', visible: true },
{ text: '2', visible: true },
{ text: '3', visible: true },
],
selectedIndex: 2
});
const instance = $multiView.dxMultiView('instance');
instance.option('items[2].visible', false);

assert.strictEqual(instance.option('selectedIndex'), 1, 'selectedIndex is updated on proper index');
});

QUnit.test('when showing previously invisible item before selectedIndex, selectedIndex should not change', function(assert) {
const $multiView = $('#multiView').dxMultiView({
items: [
{ text: '1', visible: true },
{ text: '2', visible: false },
{ text: '3', visible: true },
],
selectedIndex: 2
});
const instance = $multiView.dxMultiView('instance');
instance.option('items[1].visible', true);

assert.strictEqual(instance.option('selectedIndex'), 2, 'selectedIndex is updated on proper index');
});

QUnit.test('when showing previously invisible item after selectedIndex, selectedIndex should not change', function(assert) {
const $multiView = $('#multiView').dxMultiView({
items: [
{ text: '1', visible: true },
{ text: '2', visible: false },
{ text: '3', visible: true },
],
selectedIndex: 0
});
const instance = $multiView.dxMultiView('instance');
instance.option('items[1].visible', true);

assert.strictEqual(instance.option('selectedIndex'), 0, 'selectedIndex is updated on proper index');
});

QUnit.test('when hiding last visible item selectedIndex should return to 0 index', function(assert) {
const $multiView = $('#multiView').dxMultiView({
items: [
{ text: '1', visible: false },
{ text: '2', visible: false },
{ text: '3', visible: true },
],
selectedIndex: 2
});
const instance = $multiView.dxMultiView('instance');
instance.option('items[2].visible', false);

assert.strictEqual(instance.option('selectedIndex'), 0, 'selectedIndex is updated on proper index');
});

QUnit.test('after removing selected item selectedIndex should be restored to 0', function(assert) {
const $multiView = $('#multiView').dxMultiView({
items: [
{ text: '1', visible: true },
{ text: '2', visible: true },
{ text: '3', visible: true },
],
selectedIndex: 2
});
const instance = $multiView.dxMultiView('instance');
instance.option('items', [
{ text: '1', visible: true },
{ text: '2', visible: true },
]);

assert.strictEqual(instance.option('selectedIndex'), 0, 'selectedIndex is not changed');
});
});
});

0 comments on commit d9df47e

Please sign in to comment.