Skip to content

Commit 40628e1

Browse files
r-farkhutdinovRuslan Farkhutdinov
andauthored
Calendar: Should not switch to earliest date if same value is set programmatically (T1279950) (DevExpress#29445)
Co-authored-by: Ruslan Farkhutdinov <[email protected]>
1 parent 4d580fe commit 40628e1

File tree

5 files changed

+117
-3
lines changed

5 files changed

+117
-3
lines changed

packages/devextreme/js/__internal/core/utils/m_date.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,26 @@ const sameCentury = function (date1, date2) {
460460
return date1 && date2 && startCenturyDate1 === startCenturyDate2;
461461
};
462462

463+
const sameDatesArrays = (arr1: Date[], arr2: Date[]): boolean => {
464+
if (!Array.isArray(arr1) || !Array.isArray(arr2) || arr1.length !== arr2.length) {
465+
return false;
466+
}
467+
468+
return arr1.every((date1, index) => {
469+
const date2 = arr2[index];
470+
471+
if ([date1, date2].some((date) => date !== null && !(date instanceof Date))) {
472+
return false;
473+
}
474+
475+
if (date1 instanceof Date && date2 instanceof Date) {
476+
return sameDate(date1, date2);
477+
}
478+
479+
return date1 === date2;
480+
});
481+
};
482+
463483
function getFirstDecadeInCentury(date) {
464484
return date && date.getFullYear() - date.getFullYear() % 100;
465485
}
@@ -769,6 +789,7 @@ const dateUtils = {
769789
sameDecade,
770790
sameCentury,
771791
sameView,
792+
sameDatesArrays,
772793
getDifferenceInMonth,
773794
getDifferenceInMonthForCells,
774795
getFirstYearInDecade,

packages/devextreme/js/__internal/ui/calendar/m_calendar.multiple.selection.strategy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class CalendarMultiSelectionStrategy extends CalendarSelectionStrategy {
3939
}
4040

4141
getDefaultCurrentDate() {
42-
const dates = this.dateOption('value').filter((value) => value);
42+
const dates = this.dateOption('value').filter(Boolean);
4343
return this._getLowestDateInArray(dates);
4444
}
4545

packages/devextreme/js/__internal/ui/calendar/m_calendar.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1608,11 +1608,17 @@ class Calendar<
16081608
this._correctZoomLevel();
16091609
this._updateButtonsVisibility();
16101610
break;
1611-
case 'value':
1612-
this._selectionStrategy.processValueChanged(value, previousValue);
1611+
case 'value': {
1612+
const isSameValue = dateUtils.sameDatesArrays(value, previousValue);
1613+
1614+
if (!isSameValue) {
1615+
this._selectionStrategy.processValueChanged(value, previousValue);
1616+
}
1617+
16131618
this._setSubmitValue(value);
16141619
super._optionChanged(args);
16151620
break;
1621+
}
16161622
case 'viewsCount':
16171623
this._refreshViews();
16181624
this._renderNavigator();

packages/devextreme/testing/tests/DevExpress.core/utils.date.tests.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1773,3 +1773,50 @@ QUnit.module('intervalsOverlap', () => {
17731773
}), 'Intervals overlaps');
17741774
});
17751775
});
1776+
1777+
QUnit.module('sameDatesArrays', () => {
1778+
QUnit.test('Empty arrays', function(assert) {
1779+
assert.ok(dateUtils.sameDatesArrays([], []), 'empty arrays are equal');
1780+
});
1781+
1782+
QUnit.test('Arrays of different length', function(assert) {
1783+
assert.notOk(dateUtils.sameDatesArrays([], [new Date(2025, 1, 1)]), 'arrays of different length are not equal');
1784+
});
1785+
1786+
QUnit.test('Non-arrays', function(assert) {
1787+
assert.notOk(dateUtils.sameDatesArrays(null, null), 'both null arguments are not array');
1788+
assert.notOk(dateUtils.sameDatesArrays(null, new Date(2025, 1, 1)), 'first null is not an array');
1789+
assert.notOk(dateUtils.sameDatesArrays(new Date(2025, 1, 1), null), 'second null is not an array');
1790+
assert.notOk(dateUtils.sameDatesArrays('string', 'string'), 'strings are not arrays');
1791+
});
1792+
1793+
QUnit.test('Same arrays with valid dates', function(assert) {
1794+
const arr1 = [new Date(2025, 1, 1), new Date(2025, 2, 1)];
1795+
const arr2 = [new Date(2025, 1, 1), new Date(2025, 2, 1)];
1796+
assert.ok(dateUtils.sameDatesArrays(arr1, arr2), 'arrays with same dates are equal');
1797+
});
1798+
1799+
QUnit.test('Same arrays with null dates', function(assert) {
1800+
const arr1 = [new Date(2025, 1, 1), null];
1801+
const arr2 = [new Date(2025, 1, 1), null];
1802+
assert.ok(dateUtils.sameDatesArrays(arr1, arr2), 'arrays with same empty dates are equal');
1803+
});
1804+
1805+
QUnit.test('Different arrays with valid dates', function(assert) {
1806+
const arr1 = [new Date(2025, 1, 1), new Date(2025, 2, 1)];
1807+
const arr2 = [new Date(2025, 1, 1), new Date(2025, 3, 1)];
1808+
assert.notOk(dateUtils.sameDatesArrays(arr1, arr2), 'arrays with different dates are not equal');
1809+
});
1810+
1811+
QUnit.test('Different arrays with null dates', function(assert) {
1812+
const arr1 = [null, new Date(2025, 2, 1)];
1813+
const arr2 = [new Date(2025, 2, 1), null];
1814+
assert.notOk(dateUtils.sameDatesArrays(arr1, arr2), 'arrays with different empty dates are not equal');
1815+
});
1816+
1817+
QUnit.test('Arrays with non-null and non-Date values', function(assert) {
1818+
const arr1 = ['string'];
1819+
const arr2 = ['string'];
1820+
assert.notOk(dateUtils.sameDatesArrays(arr1, arr2), 'arrays with strings are not equal dates');
1821+
});
1822+
});

packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/calendar.tests.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,46 @@ QUnit.module('Views integration', {
873873

874874
assert.strictEqual($contouredCell.length, 0, 'there is no contoured date cell');
875875
});
876+
877+
QUnit.test('should not navigate view after new date UI select, even in React controlled mode (T1279950)', function(assert) {
878+
this.calendar.option({
879+
selectionMode: 'multiple',
880+
value: [new Date(2025, 1, 10), new Date(2025, 2, 10)],
881+
});
882+
883+
const calendar = this.calendar;
884+
const $nextMonthButton = this.$element.find(toSelector(CALENDAR_NAVIGATOR_NEXT_MONTH_CLASS));
885+
886+
assert.ok(
887+
dateUtils.sameMonth(calendar.option('currentDate'), new Date(2025, 1, 10)),
888+
'initially navigated to the earliest date'
889+
);
890+
891+
$($nextMonthButton).trigger('dxclick');
892+
893+
assert.ok(
894+
dateUtils.sameMonth(calendar.option('currentDate'), new Date(2025, 2, 10)),
895+
'navigated to the next month'
896+
);
897+
898+
calendar.option('value', [new Date(2025, 1, 10), new Date(2025, 2, 10)]);
899+
900+
assert.ok(
901+
dateUtils.sameMonth(calendar.option('currentDate'), new Date(2025, 2, 10)),
902+
'did not navigate back to the earliest date'
903+
);
904+
905+
const $cell = this.$element.find('*[data-value="2025/03/10"]');
906+
907+
$cell.trigger('dxclick');
908+
909+
assert.strictEqual(this.calendar.option('value').length, 1, 'deselected correctly');
910+
911+
assert.ok(
912+
dateUtils.sameMonth(calendar.option('currentDate'), new Date(2025, 2, 10)),
913+
'did not navigate back to the earliest date after deseleting'
914+
);
915+
});
876916
});
877917

878918

0 commit comments

Comments
 (0)