Skip to content

Commit fa0259c

Browse files
committed
small fix and add tests
1 parent b9c194b commit fa0259c

10 files changed

Lines changed: 863 additions & 3 deletions

File tree

packages/devextreme/js/__internal/core/localization/date.global_formats.test.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ const saveAndRestore = (): { save: () => void; restore: () => void } => {
2626
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
2727
delete currentConfig[key];
2828
} else {
29-
currentConfig[key] = savedValues[key];
29+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
30+
(currentConfig as any)[key] = savedValues[key];
3031
}
3132
});
3233
},
@@ -187,7 +188,10 @@ describe('date localization - dateTimeFormatPresets', () => {
187188
},
188189
});
189190

190-
const customFormatter = (d: Date): string => `custom:${d.getFullYear()}`;
191+
const customFormatter = (value: number | Date): string => {
192+
const d = value instanceof Date ? value : new Date(value);
193+
return `custom:${d.getFullYear()}`;
194+
};
191195
const result = dateLocalization.format(new Date(2020, 0, 2), { formatter: customFormatter });
192196

193197
expect(result).toBe('custom:2020');
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import {
2+
afterEach, beforeEach, describe, expect, it,
3+
} from '@jest/globals';
4+
import config from '@js/core/config';
5+
6+
const FORMAT_KEYS = ['dateFormat', 'timeFormat', 'dateTimeFormat', 'numberFormat', 'dateTimeFormatPresets'] as const;
7+
8+
describe('config() - clearing format properties with undefined', () => {
9+
let savedValues: Record<string, unknown>;
10+
11+
beforeEach(() => {
12+
const currentConfig = config();
13+
savedValues = {};
14+
FORMAT_KEYS.forEach((key) => {
15+
savedValues[key] = currentConfig[key];
16+
});
17+
});
18+
19+
afterEach(() => {
20+
const currentConfig = config();
21+
FORMAT_KEYS.forEach((key) => {
22+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
23+
(currentConfig as any)[key] = savedValues[key];
24+
});
25+
});
26+
27+
it('should clear dateFormat when set to undefined', () => {
28+
config({ dateFormat: 'dd/MM/yyyy' });
29+
expect(config().dateFormat).toBe('dd/MM/yyyy');
30+
31+
config({ dateFormat: undefined });
32+
expect(config().dateFormat).toBeUndefined();
33+
});
34+
35+
it('should clear timeFormat when set to undefined', () => {
36+
config({ timeFormat: 'HH:mm:ss' });
37+
expect(config().timeFormat).toBe('HH:mm:ss');
38+
39+
config({ timeFormat: undefined });
40+
expect(config().timeFormat).toBeUndefined();
41+
});
42+
43+
it('should clear dateTimeFormat when set to undefined', () => {
44+
config({ dateTimeFormat: 'dd/MM/yyyy HH:mm' });
45+
expect(config().dateTimeFormat).toBe('dd/MM/yyyy HH:mm');
46+
47+
config({ dateTimeFormat: undefined });
48+
expect(config().dateTimeFormat).toBeUndefined();
49+
});
50+
51+
it('should clear numberFormat when set to undefined', () => {
52+
config({ numberFormat: '#,##0.00' });
53+
expect(config().numberFormat).toBe('#,##0.00');
54+
55+
config({ numberFormat: undefined });
56+
expect(config().numberFormat).toBeUndefined();
57+
});
58+
59+
it('should clear dateTimeFormatPresets when set to undefined', () => {
60+
config({ dateTimeFormatPresets: { shortDate: 'dd/MM/yyyy' } });
61+
expect(config().dateTimeFormatPresets).toEqual({ shortDate: 'dd/MM/yyyy' });
62+
63+
config({ dateTimeFormatPresets: undefined });
64+
expect(config().dateTimeFormatPresets).toBeUndefined();
65+
});
66+
67+
it('should clear all format keys at once', () => {
68+
config({
69+
dateFormat: 'dd/MM/yyyy',
70+
timeFormat: 'HH:mm',
71+
dateTimeFormat: 'dd/MM/yyyy HH:mm',
72+
numberFormat: '#,##0.00',
73+
dateTimeFormatPresets: { shortDate: 'dd/MM/yyyy' },
74+
});
75+
76+
config({
77+
dateFormat: undefined,
78+
timeFormat: undefined,
79+
dateTimeFormat: undefined,
80+
numberFormat: undefined,
81+
dateTimeFormatPresets: undefined,
82+
});
83+
84+
expect(config().dateFormat).toBeUndefined();
85+
expect(config().timeFormat).toBeUndefined();
86+
expect(config().dateTimeFormat).toBeUndefined();
87+
expect(config().numberFormat).toBeUndefined();
88+
expect(config().dateTimeFormatPresets).toBeUndefined();
89+
});
90+
91+
it('should not affect non-format properties when clearing format keys', () => {
92+
const originalRtl = config().rtlEnabled;
93+
94+
config({
95+
dateFormat: 'dd/MM/yyyy',
96+
rtlEnabled: true,
97+
});
98+
99+
config({
100+
dateFormat: undefined,
101+
});
102+
103+
expect(config().dateFormat).toBeUndefined();
104+
expect(config().rtlEnabled).toBe(true);
105+
106+
// Restore
107+
config({ rtlEnabled: originalRtl });
108+
});
109+
110+
it('should allow re-setting a format after clearing', () => {
111+
config({ dateFormat: 'dd/MM/yyyy' });
112+
config({ dateFormat: undefined });
113+
config({ dateFormat: 'yyyy-MM-dd' });
114+
115+
expect(config().dateFormat).toBe('yyyy-MM-dd');
116+
});
117+
118+
it('should not clear a format key if it is not in the newConfig object', () => {
119+
config({ dateFormat: 'dd/MM/yyyy', timeFormat: 'HH:mm' });
120+
121+
// Only clear dateFormat, timeFormat should remain
122+
config({ dateFormat: undefined });
123+
124+
expect(config().dateFormat).toBeUndefined();
125+
expect(config().timeFormat).toBe('HH:mm');
126+
});
127+
});

packages/devextreme/js/__internal/core/m_config.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,12 @@ const configMethod = (...args) => {
8383
});
8484

8585
extend(config, newConfig);
86+
const formatKeys = ['dateFormat', 'timeFormat', 'dateTimeFormat', 'numberFormat', 'dateTimeFormatPresets'] as const;
87+
for (const key of formatKeys) {
88+
if (key in newConfig && newConfig[key] === undefined) {
89+
config[key] = undefined;
90+
}
91+
}
8692
};
8793

8894
// @ts-expect-error typescript cant see global

packages/devextreme/js/__internal/core/m_global_format_config.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ describe('m_global_format_config', () => {
2727
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
2828
delete currentConfig[key];
2929
} else {
30-
currentConfig[key] = savedValues[key];
30+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
31+
(currentConfig as any)[key] = savedValues[key];
3132
}
3233
});
3334
});
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import {
2+
afterEach, beforeEach, describe, expect, it,
3+
} from '@jest/globals';
4+
import config from '@js/core/config';
5+
import type { Field } from '@js/ui/filter_builder';
6+
7+
import { getCurrentValueText } from '../m_utils';
8+
9+
const FORMAT_KEYS = ['dateFormat', 'timeFormat', 'dateTimeFormat', 'numberFormat', 'dateTimeFormatPresets'] as const;
10+
11+
describe('FilterBuilder - global format integration', () => {
12+
let savedValues: Record<string, unknown> = {};
13+
14+
beforeEach(() => {
15+
const currentConfig = config();
16+
savedValues = {};
17+
FORMAT_KEYS.forEach((key) => {
18+
savedValues[key] = currentConfig[key];
19+
});
20+
});
21+
22+
afterEach(() => {
23+
const currentConfig = config();
24+
FORMAT_KEYS.forEach((key) => {
25+
if (savedValues[key] === undefined) {
26+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
27+
delete currentConfig[key];
28+
} else {
29+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
30+
(currentConfig as any)[key] = savedValues[key];
31+
}
32+
});
33+
});
34+
35+
describe('date fields with global dateFormat', () => {
36+
it('should use global dateFormat when field has no explicit format', () => {
37+
config({ ...config(), dateFormat: 'dd/MM/yyyy' });
38+
39+
const field: Field = { dataType: 'date' };
40+
const value = new Date(2025, 0, 15);
41+
42+
expect(getCurrentValueText(field, value, null)).toBe('15/01/2025');
43+
});
44+
45+
it('should prefer explicit field.format over global dateFormat', () => {
46+
config({ ...config(), dateFormat: 'dd/MM/yyyy' });
47+
48+
const field: Field = { dataType: 'date', format: 'yyyy-MM-dd' };
49+
const value = new Date(2025, 0, 15);
50+
51+
expect(getCurrentValueText(field, value, null)).toBe('2025-01-15');
52+
});
53+
54+
it('should fall back to shortDate when no global dateFormat and no field format', () => {
55+
const field: Field = { dataType: 'date' };
56+
const value = new Date(2025, 0, 15);
57+
58+
const result = getCurrentValueText(field, value, null);
59+
expect(result).toBeTruthy();
60+
expect(typeof result).toBe('string');
61+
});
62+
});
63+
64+
describe('datetime fields with global dateTimeFormat', () => {
65+
it('should use global dateTimeFormat when field has no explicit format', () => {
66+
config({ ...config(), dateTimeFormat: 'dd/MM/yyyy HH:mm' });
67+
68+
const field: Field = { dataType: 'datetime' };
69+
const value = new Date(2025, 0, 15, 14, 30);
70+
71+
expect(getCurrentValueText(field, value, null)).toBe('15/01/2025 14:30');
72+
});
73+
74+
it('should prefer explicit field.format over global dateTimeFormat', () => {
75+
config({ ...config(), dateTimeFormat: 'dd/MM/yyyy HH:mm' });
76+
77+
const field: Field = { dataType: 'datetime', format: 'yyyy-MM-dd' };
78+
const value = new Date(2025, 0, 15, 14, 30);
79+
80+
expect(getCurrentValueText(field, value, null)).toBe('2025-01-15');
81+
});
82+
});
83+
84+
describe('number fields', () => {
85+
it('should use built-in shortDate format for date field when no global format set', () => {
86+
const field: Field = { dataType: 'date' };
87+
const value = new Date(2017, 8, 5, 12, 30, 0);
88+
89+
// This is the existing behavior — should keep working
90+
expect(getCurrentValueText(field, value, null)).toBe('9/5/2017');
91+
});
92+
});
93+
94+
describe('dateTimeFormatPresets interaction', () => {
95+
it('should apply preset override to shortDate default format', () => {
96+
config({
97+
...config(),
98+
dateTimeFormatPresets: {
99+
shortDate: 'dd.MM.yyyy',
100+
},
101+
});
102+
103+
const field: Field = { dataType: 'date' };
104+
const value = new Date(2025, 0, 15);
105+
106+
// With no global dateFormat set, falls back to DEFAULT_FORMAT['date'] = 'shortDate'
107+
// The preset override should be applied via dateLocalization.format()
108+
expect(getCurrentValueText(field, value, null)).toBe('15.01.2025');
109+
});
110+
});
111+
});
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import {
2+
afterEach, beforeEach, describe, expect, it,
3+
} from '@jest/globals';
4+
import config from '@js/core/config';
5+
6+
import { setDefaultFieldValueFormatting } from '../m_widget_utils';
7+
8+
const FORMAT_KEYS = ['dateFormat', 'timeFormat', 'dateTimeFormat', 'numberFormat', 'dateTimeFormatPresets'] as const;
9+
10+
describe('PivotGrid - setDefaultFieldValueFormatting global format', () => {
11+
let savedValues: Record<string, unknown> = {};
12+
13+
beforeEach(() => {
14+
const currentConfig = config();
15+
savedValues = {};
16+
FORMAT_KEYS.forEach((key) => {
17+
savedValues[key] = currentConfig[key];
18+
});
19+
});
20+
21+
afterEach(() => {
22+
const currentConfig = config();
23+
FORMAT_KEYS.forEach((key) => {
24+
if (savedValues[key] === undefined) {
25+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
26+
delete currentConfig[key];
27+
} else {
28+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
29+
(currentConfig as any)[key] = savedValues[key];
30+
}
31+
});
32+
});
33+
34+
describe('date field formatting', () => {
35+
it('should use global dateFormat when no format or groupInterval is set', () => {
36+
config({ ...config(), dateFormat: 'dd/MM/yyyy' });
37+
38+
const field: Record<string, unknown> = { dataType: 'date' };
39+
setDefaultFieldValueFormatting(field);
40+
41+
expect(field.format).toBe('dd/MM/yyyy');
42+
});
43+
44+
it('should use DATE_INTERVAL_FORMATS when groupInterval is set (e.g. month)', () => {
45+
config({ ...config(), dateFormat: 'dd/MM/yyyy' });
46+
47+
const field: Record<string, unknown> = { dataType: 'date', groupInterval: 'month' };
48+
setDefaultFieldValueFormatting(field);
49+
50+
// groupInterval "month" maps to a function in DATE_INTERVAL_FORMATS
51+
expect(typeof field.format).toBe('function');
52+
});
53+
54+
it('should prefer DATE_INTERVAL_FORMATS over global dateFormat', () => {
55+
config({ ...config(), dateFormat: 'dd/MM/yyyy' });
56+
57+
const field: Record<string, unknown> = { dataType: 'date', groupInterval: 'quarter' };
58+
setDefaultFieldValueFormatting(field);
59+
60+
// quarter maps to a DATE_INTERVAL_FORMATS function
61+
expect(typeof field.format).toBe('function');
62+
});
63+
64+
it('should not set format when groupInterval has no matching DATE_INTERVAL_FORMAT and no global dateFormat', () => {
65+
const field: Record<string, unknown> = { dataType: 'date', groupInterval: 'year' };
66+
setDefaultFieldValueFormatting(field);
67+
68+
// 'year' has no entry in DATE_INTERVAL_FORMATS and no global dateFormat
69+
expect(field.format).toBeUndefined();
70+
});
71+
72+
it('should use global dateFormat when groupInterval has no matching DATE_INTERVAL_FORMAT', () => {
73+
config({ ...config(), dateFormat: 'dd/MM/yyyy' });
74+
75+
const field: Record<string, unknown> = { dataType: 'date', groupInterval: 'year' };
76+
setDefaultFieldValueFormatting(field);
77+
78+
expect(field.format).toBe('dd/MM/yyyy');
79+
});
80+
81+
it('should not override explicit field.format', () => {
82+
config({ ...config(), dateFormat: 'dd/MM/yyyy' });
83+
84+
const field: Record<string, unknown> = { dataType: 'date', format: 'yyyy-MM-dd' };
85+
setDefaultFieldValueFormatting(field);
86+
87+
expect(field.format).toBe('yyyy-MM-dd');
88+
});
89+
});
90+
91+
describe('non-date fields', () => {
92+
it('should not set format for number field without groupInterval', () => {
93+
config({ ...config(), numberFormat: '#,##0.00' });
94+
95+
const field: Record<string, unknown> = { dataType: 'number' };
96+
setDefaultFieldValueFormatting(field);
97+
98+
// Number fields don't get a default format from setDefaultFieldValueFormatting
99+
// (number format is handled via formatHelper.format at display time)
100+
expect(field.format).toBeUndefined();
101+
});
102+
103+
it('should not set format for string field', () => {
104+
const field: Record<string, unknown> = { dataType: 'string' };
105+
setDefaultFieldValueFormatting(field);
106+
107+
expect(field.format).toBeUndefined();
108+
});
109+
});
110+
});

0 commit comments

Comments
 (0)