Skip to content

Commit f79a495

Browse files
Merge branch '25_2' into 25_2_leak_test_tune_v2
2 parents 60798e8 + 5234e9c commit f79a495

4 files changed

Lines changed: 151 additions & 89 deletions

File tree

packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.integration.test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import {
22
afterEach, beforeEach, describe, expect, it, jest,
33
} from '@jest/globals';
4+
import dateLocalization from '@js/common/core/localization/date';
45
import { CustomStore } from '@js/common/data/custom_store';
56
import $ from '@js/core/renderer';
7+
import { loadMessages, locale } from '@js/localization';
68
import type { GroupItem, Item as FormItem, SimpleItem } from '@js/ui/form';
79
import type { ToolbarItem } from '@js/ui/popup';
810
import { toMilliseconds } from '@ts/utils/toMilliseconds';
@@ -1409,6 +1411,40 @@ describe('Appointment Form', () => {
14091411
expect(POM.popup.getInputValue('recurrenceEndCountEditor')).toBe('10 occurrence(s)');
14101412
});
14111413

1414+
it('T1325870: should use current locale for recurrence editors after locale change', async () => {
1415+
const currentLocale = locale();
1416+
1417+
loadMessages({
1418+
de: {
1419+
'dxScheduler-recurrenceYearly': 'custom yearly',
1420+
'dxScheduler-recurrenceRepeatYearly': 'custom repeat yearly',
1421+
},
1422+
});
1423+
locale('de');
1424+
1425+
try {
1426+
const { scheduler, POM } = await createScheduler(getDefaultConfig());
1427+
1428+
scheduler.showAppointmentPopup({
1429+
text: 'Meeting',
1430+
startDate: new Date(2017, 4, 1, 10, 30),
1431+
endDate: new Date(2017, 4, 1, 11),
1432+
recurrenceRule: 'FREQ=YEARLY;INTERVAL=2;BYMONTHDAY=1;BYMONTH=5;COUNT=10',
1433+
repeatEnd: 'count',
1434+
});
1435+
POM.popup.editSeriesButton.click();
1436+
1437+
expect(POM.popup.getInputValue('repeatEditor')).toBe('custom yearly');
1438+
1439+
POM.popup.recurrenceSettingsButton.click();
1440+
1441+
expect(POM.popup.getInputValue('recurrencePeriodEditor')).toBe('Custom repeat yearly');
1442+
expect(POM.popup.getInputValue('recurrenceDayOfYearMonthEditor')).toBe(dateLocalization.getMonthNames()[4]);
1443+
} finally {
1444+
locale(currentLocale);
1445+
}
1446+
});
1447+
14121448
it('should have correct input values for appointment with no end', async () => {
14131449
const { scheduler, POM } = await createScheduler(getDefaultConfig());
14141450

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import dateLocalization from '@js/common/core/localization/date';
2+
import messageLocalization from '@js/common/core/localization/message';
3+
import { capitalize } from '@ts/core/utils/capitalize';
4+
5+
export const REPEAT_NEVER_VALUE = 'never';
6+
export const ICAL_WEEK_DAYS = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'];
7+
8+
interface TextValueItem {
9+
text: string;
10+
value: string;
11+
}
12+
13+
interface MonthItem {
14+
value: number;
15+
text: string;
16+
}
17+
18+
interface WeekDayItem {
19+
text: string;
20+
key: string;
21+
}
22+
23+
const repeatSelectItemDefs = [
24+
{
25+
messageId: 'dxScheduler-recurrenceNever',
26+
value: REPEAT_NEVER_VALUE,
27+
}, {
28+
messageId: 'dxScheduler-recurrenceHourly',
29+
value: 'hourly',
30+
}, {
31+
messageId: 'dxScheduler-recurrenceDaily',
32+
value: 'daily',
33+
}, {
34+
messageId: 'dxScheduler-recurrenceWeekly',
35+
value: 'weekly',
36+
}, {
37+
messageId: 'dxScheduler-recurrenceMonthly',
38+
value: 'monthly',
39+
}, {
40+
messageId: 'dxScheduler-recurrenceYearly',
41+
value: 'yearly',
42+
},
43+
] as const;
44+
45+
const recurrenceFrequencyItemDefs = [
46+
{
47+
messageId: 'dxScheduler-recurrenceRepeatHourly',
48+
value: 'hourly',
49+
}, {
50+
messageId: 'dxScheduler-recurrenceRepeatDaily',
51+
value: 'daily',
52+
}, {
53+
messageId: 'dxScheduler-recurrenceRepeatWeekly',
54+
value: 'weekly',
55+
}, {
56+
messageId: 'dxScheduler-recurrenceRepeatMonthly',
57+
value: 'monthly',
58+
}, {
59+
messageId: 'dxScheduler-recurrenceRepeatYearly',
60+
value: 'yearly',
61+
},
62+
] as const;
63+
64+
export const getRepeatSelectItems = (): TextValueItem[] => repeatSelectItemDefs
65+
.map((item) => ({
66+
text: messageLocalization.format(item.messageId),
67+
value: item.value,
68+
}));
69+
70+
export const getRecurrenceFrequencyItems = (): TextValueItem[] => recurrenceFrequencyItemDefs
71+
.map((item) => ({
72+
text: capitalize(
73+
messageLocalization.format(item.messageId),
74+
),
75+
value: item.value,
76+
}));
77+
78+
export const getRecurrenceMonthItems = (): MonthItem[] => dateLocalization
79+
.getMonthNames()
80+
.map((monthName, index) => ({
81+
value: index + 1,
82+
text: monthName,
83+
}));
84+
85+
export const getRecurrenceWeekDayItems = (
86+
firstDayOfWeek?: number,
87+
): WeekDayItem[] => {
88+
const localizedDayNames = dateLocalization.getDayNames('abbreviated');
89+
const orderedWeekDayItems = ICAL_WEEK_DAYS.map((day, index) => ({
90+
text: localizedDayNames[index].slice(0, 1).toUpperCase(),
91+
key: day,
92+
}));
93+
const validFirstDayOfWeek = firstDayOfWeek ?? dateLocalization.firstDayOfWeekIndex();
94+
95+
return orderedWeekDayItems
96+
.slice(validFirstDayOfWeek)
97+
.concat(orderedWeekDayItems.slice(0, validFirstDayOfWeek));
98+
};

packages/devextreme/js/__internal/scheduler/appointment_popup/m_form.ts

Lines changed: 6 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import type { ResourceLoader } from '../utils/loader/resource_loader';
3232
import { DEFAULT_ICONS_SHOW_MODE } from '../utils/options/constants';
3333
import { getAppointmentGroupIndex, getRawAppointmentGroupValues, getSafeGroupValues } from '../utils/resource_manager/appointment_groups_utils';
3434
import type { ResourceManager } from '../utils/resource_manager/resource_manager';
35+
import { getRepeatSelectItems, REPEAT_NEVER_VALUE } from './localized_items';
3536
import { customizeFormItems } from './m_customize_form_items';
3637
import { RecurrenceForm } from './m_recurrence_form';
3738
import { createFormIconTemplate, getStartDateCommonConfig, RecurrenceRule } from './utils';
@@ -80,35 +81,6 @@ const CLASSES = {
8081
recurrenceHidden: 'dx-scheduler-form-recurrence-group-hidden',
8182
};
8283

83-
const repeatSelectBoxItems = [
84-
{
85-
recurrence: 'dxScheduler-recurrenceNever',
86-
value: 'never',
87-
}, {
88-
recurrence: 'dxScheduler-recurrenceHourly',
89-
value: 'hourly',
90-
}, {
91-
recurrence: 'dxScheduler-recurrenceDaily',
92-
value: 'daily',
93-
}, {
94-
recurrence: 'dxScheduler-recurrenceWeekly',
95-
value: 'weekly',
96-
}, {
97-
recurrence: 'dxScheduler-recurrenceMonthly',
98-
value: 'monthly',
99-
}, {
100-
recurrence: 'dxScheduler-recurrenceYearly',
101-
value: 'yearly',
102-
},
103-
].map(
104-
(item) => ({
105-
text: messageLocalization.format(item.recurrence),
106-
value: item.value,
107-
}),
108-
);
109-
110-
const repeatNeverValue = repeatSelectBoxItems[0].value;
111-
11284
const createTimeZoneDataSource = (): DataSource => new DataSource({
11385
store: timeZoneUtils.getTimeZonesCache(),
11486
paginate: true,
@@ -676,14 +648,14 @@ export class AppointmentForm {
676648
},
677649
editorType: 'dxSelectBox',
678650
editorOptions: {
679-
items: repeatSelectBoxItems,
651+
items: getRepeatSelectItems(),
680652
valueExpr: 'value',
681653
displayExpr: 'text',
682654
onContentReady: (): void => {
683655
this.updateRepeatEditorValue();
684656
},
685657
onValueChanged: (e): void => {
686-
if (e.value === repeatNeverValue) {
658+
if (e.value === REPEAT_NEVER_VALUE) {
687659
this.dxForm.updateData(recurrenceRuleExpr, '');
688660
} else {
689661
const currentRecurrenceRule = this.recurrenceForm.recurrenceRule.toString() ?? '';
@@ -692,7 +664,7 @@ export class AppointmentForm {
692664
this.dxForm.updateData(recurrenceRuleExpr, recurrenceRule.toString());
693665
}
694666

695-
if (e.value !== repeatNeverValue && e.event) {
667+
if (e.value !== REPEAT_NEVER_VALUE && e.event) {
696668
this.showRecurrenceGroup();
697669
}
698670

@@ -989,11 +961,11 @@ export class AppointmentForm {
989961
}
990962

991963
if (this.recurrenceRuleRaw === null) {
992-
repeatEditor.option('value', repeatNeverValue);
964+
repeatEditor.option('value', REPEAT_NEVER_VALUE);
993965
} else {
994966
const recurrenceRule = new RecurrenceRule(this.recurrenceRuleRaw, this.startDate);
995967
const { frequency } = recurrenceRule;
996-
const value = frequency ?? repeatNeverValue;
968+
const value = frequency ?? REPEAT_NEVER_VALUE;
997969

998970
repeatEditor.option('value', value);
999971
}

packages/devextreme/js/__internal/scheduler/appointment_popup/m_recurrence_form.ts

Lines changed: 11 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import dateLocalization from '@js/common/core/localization/date';
21
import messageLocalization from '@js/common/core/localization/message';
32
import $, { type dxElementWrapper } from '@js/core/renderer';
43
import { extend } from '@js/core/utils/extend';
@@ -10,9 +9,14 @@ import type dxForm from '@js/ui/form';
109
import type { Properties as NumberBoxProperties } from '@js/ui/number_box';
1110
import type { Properties as RadioGroupProperties } from '@js/ui/radio_group';
1211
import type { Properties as SelectBoxProperties } from '@js/ui/select_box';
13-
import { capitalize } from '@ts/core/utils/capitalize';
1412

1513
import type Scheduler from '../m_scheduler';
14+
import {
15+
getRecurrenceFrequencyItems,
16+
getRecurrenceMonthItems,
17+
getRecurrenceWeekDayItems,
18+
ICAL_WEEK_DAYS,
19+
} from './localized_items';
1620
import { createFormIconTemplate, getStartDateCommonConfig, RecurrenceRule } from './utils';
1721

1822
const CLASSES = {
@@ -36,34 +40,6 @@ const CLASSES = {
3640
recurrenceSettingsGroup: 'dx-scheduler-form-recurrence-settings-group',
3741
};
3842

39-
const frequenciesValues = [
40-
{
41-
recurrence: 'dxScheduler-recurrenceRepeatHourly',
42-
value: 'hourly',
43-
}, {
44-
recurrence: 'dxScheduler-recurrenceRepeatDaily',
45-
value: 'daily',
46-
}, {
47-
recurrence: 'dxScheduler-recurrenceRepeatWeekly',
48-
value: 'weekly',
49-
}, {
50-
recurrence: 'dxScheduler-recurrenceRepeatMonthly',
51-
value: 'monthly',
52-
}, {
53-
recurrence: 'dxScheduler-recurrenceRepeatYearly',
54-
value: 'yearly',
55-
},
56-
].map((item) => ({
57-
// todo: check if it works with runtime localization change
58-
text: capitalize(messageLocalization.format(item.recurrence)),
59-
value: item.value,
60-
}));
61-
62-
const monthsValues = dateLocalization.getMonthNames().map((monthName, index) => ({
63-
value: index + 1,
64-
text: monthName,
65-
}));
66-
6743
const FREQ = {
6844
HOURLY: 'hourly',
6945
DAILY: 'daily',
@@ -103,8 +79,6 @@ const ICON_NAMES = {
10379
recurrenceEndIcon: 'recurrenceEndIcon',
10480
};
10581

106-
const weekDays = dateLocalization.getDayNames('abbreviated').map((dayName) => dayName.slice(0, 2).toUpperCase());
107-
10882
const RECURRENCE_GROUP_NAME = 'recurrenceGroup';
10983

11084
export class RecurrenceForm {
@@ -114,31 +88,12 @@ export class RecurrenceForm {
11488

11589
private dxFormInstance?: dxForm;
11690

117-
private readonly weekDayItems: { text: string; key: string }[] = [];
118-
11991
private weekDayButtons: Record<string, dxButton> = {};
12092

12193
private readOnly = false;
12294

12395
constructor(scheduler: Scheduler) {
12496
this.scheduler = scheduler;
125-
this.weekDayItems = this.createWeekDayItems();
126-
}
127-
128-
private createWeekDayItems(): { text: string; key: string }[] {
129-
const weekDayItems = weekDays.map((day) => ({
130-
text: day[0],
131-
key: day,
132-
}));
133-
134-
const firstDayOfWeek = this.scheduler.getFirstDayOfWeek()
135-
?? dateLocalization.firstDayOfWeekIndex();
136-
137-
const arrangeWeekDayItems = weekDayItems
138-
.slice(firstDayOfWeek)
139-
.concat(weekDayItems.slice(0, firstDayOfWeek));
140-
141-
return arrangeWeekDayItems;
14297
}
14398

14499
private createByMonthDayNumberBoxItem(
@@ -309,7 +264,7 @@ export class RecurrenceForm {
309264
},
310265
editorOptions: {
311266
labelMode: 'hidden',
312-
items: frequenciesValues,
267+
items: getRecurrenceFrequencyItems(),
313268
valueExpr: 'value',
314269
displayExpr: 'text',
315270
onContentReady: (e): void => {
@@ -352,8 +307,9 @@ export class RecurrenceForm {
352307
},
353308
template: (): dxElementWrapper => {
354309
const $container = $('<div>').addClass(CLASSES.daysOfWeekButtons);
310+
const weekDayItems = getRecurrenceWeekDayItems(this.scheduler.getFirstDayOfWeek());
355311

356-
this.weekDayItems.forEach((item) => {
312+
weekDayItems.forEach((item) => {
357313
const buttonContainer = $('<div>').appendTo($container);
358314

359315
this.weekDayButtons[item.key]?.dispose();
@@ -418,7 +374,7 @@ export class RecurrenceForm {
418374
text: messageLocalization.format('dxScheduler-recurrenceRepeatEvery'),
419375
},
420376
editorOptions: {
421-
items: monthsValues,
377+
items: getRecurrenceMonthItems(),
422378
displayExpr: 'text',
423379
valueExpr: 'value',
424380
onContentReady: (e): void => {
@@ -590,7 +546,7 @@ export class RecurrenceForm {
590546

591547
if (recurrenceRule.byDay.length === 0) {
592548
const defaultByDay = [
593-
weekDays[startDate?.getDay() ?? this.scheduler.getFirstDayOfWeek()],
549+
ICAL_WEEK_DAYS[startDate?.getDay() ?? this.scheduler.getFirstDayOfWeek()],
594550
];
595551

596552
recurrenceRule.byDay = defaultByDay;

0 commit comments

Comments
 (0)