Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MultiView: implement selectedIndex property update when selected item is hidden #28145

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -1659,3 +1659,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');
});
});
ksercs marked this conversation as resolved.
Show resolved Hide resolved
});
nikkithelegendarypokemonster marked this conversation as resolved.
Show resolved Hide resolved
Loading