Skip to content

Commit 650749c

Browse files
r-farkhutdinovRuslan Farkhutdinov
andauthored
Calendar: Should not switch to earliest date if same value is set programmatically (T1279950) (DevExpress#29446)
Co-authored-by: Ruslan Farkhutdinov <[email protected]>
1 parent 6ddd567 commit 650749c

File tree

5 files changed

+117
-3
lines changed

5 files changed

+117
-3
lines changed

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
@@ -1521,11 +1521,17 @@ const Calendar = Editor.inherit({
15211521
this._correctZoomLevel();
15221522
this._updateButtonsVisibility();
15231523
break;
1524-
case 'value':
1525-
this._selectionStrategy.processValueChanged(value, previousValue);
1524+
case 'value': {
1525+
const isSameValue = dateUtils.sameDatesArrays(value, previousValue);
1526+
1527+
if (!isSameValue) {
1528+
this._selectionStrategy.processValueChanged(value, previousValue);
1529+
}
1530+
15261531
this._setSubmitValue(value);
15271532
this.callBase(args);
15281533
break;
1534+
}
15291535
case 'viewsCount':
15301536
this._refreshViews();
15311537
this._renderNavigator();

packages/devextreme/js/core/utils/date.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -699,6 +699,26 @@ const getMachineTimezoneName = () => {
699699
: null;
700700
};
701701

702+
const sameDatesArrays = (arr1, arr2) => {
703+
if(!Array.isArray(arr1) || !Array.isArray(arr2) || arr1.length !== arr2.length) {
704+
return false;
705+
}
706+
707+
return arr1.every((date1, index) => {
708+
const date2 = arr2[index];
709+
710+
if([date1, date2].some((date) => date !== null && !(date instanceof Date))) {
711+
return false;
712+
}
713+
714+
if(date1 instanceof Date && date2 instanceof Date) {
715+
return sameDate(date1, date2);
716+
}
717+
718+
return date1 === date2;
719+
});
720+
};
721+
702722
const dateUtils = {
703723
dateUnitIntervals: dateUnitIntervals,
704724

@@ -762,6 +782,7 @@ const dateUtils = {
762782
createDateWithFullYear: createDateWithFullYear,
763783

764784
getMachineTimezoneName: getMachineTimezoneName,
785+
sameDatesArrays
765786
};
766787

767788
dateUtils.sameView = function(view, date1, date2) {

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)