From d8e4a763c8814140573f28e55d04180af409dad0 Mon Sep 17 00:00:00 2001 From: Aleksey Semikozov Date: Mon, 27 Apr 2026 10:07:04 -0300 Subject: [PATCH 1/3] Scheduler - Replace AppointmentForm scheduler proxy with plain config --- .../__mock__/create_appointment_popup.ts | 16 ++- .../scheduler/appointment_popup/m_form.ts | 97 +++++++++++-------- .../appointment_popup/m_recurrence_form.ts | 22 +++-- .../js/__internal/scheduler/m_scheduler.ts | 25 +++-- 4 files changed, 87 insertions(+), 73 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/__tests__/__mock__/create_appointment_popup.ts b/packages/devextreme/js/__internal/scheduler/__tests__/__mock__/create_appointment_popup.ts index 6e5fa1f384fb..8dfc1e3e3ee8 100644 --- a/packages/devextreme/js/__internal/scheduler/__tests__/__mock__/create_appointment_popup.ts +++ b/packages/devextreme/js/__internal/scheduler/__tests__/__mock__/create_appointment_popup.ts @@ -114,23 +114,21 @@ export const createAppointmentPopup = async ( const onSave = options.onSave ?? jest.fn<(appointment: Record) => PromiseLike>(resolvedDeferred); - const formSchedulerProxy = { - getResourceById: (): Record => resourceManager.resourceById, - getDataAccessors: (): AppointmentDataAccessor => dataAccessors, + const formConfig = { + dataAccessors, + editing, + resourceManager, + firstDayOfWeek: options.firstDayOfWeek ?? 0, + startDayHour: options.startDayHour ?? 0, createComponent, - getEditingConfig: (): typeof editing => editing, - getResourceManager: (): ResourceManager => resourceManager, - getFirstDayOfWeek: (): number => options.firstDayOfWeek ?? 0, - getStartDayHour: (): number => options.startDayHour ?? 0, getCalculatedEndDate: (startDate: Date): Date => { const endDate = new Date(startDate); endDate.setHours(endDate.getHours() + 1); return endDate; }, - getTimeZoneCalculator: (): typeof timeZoneCalculator => timeZoneCalculator, }; - const form = new AppointmentForm(formSchedulerProxy); + const form = new AppointmentForm(formConfig); const noop = (): void => {}; diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_form.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/m_form.ts index f399691e525d..7b81d8f2249a 100644 --- a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_form.ts +++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/m_form.ts @@ -28,6 +28,7 @@ import type Popup from '@ts/ui/popup/m_popup'; import timeZoneUtils from '../m_utils_time_zone'; import type { SafeAppointment } from '../types'; +import type { AppointmentDataAccessor } from '../utils/data_accessor/appointment_data_accessor'; import type { ResourceLoader } from '../utils/loader/resource_loader'; import { DEFAULT_ICONS_SHOW_MODE } from '../utils/options/constants'; import { getAppointmentGroupIndex, getRawAppointmentGroupValues, getSafeGroupValues } from '../utils/resource_manager/appointment_groups_utils'; @@ -37,6 +38,16 @@ import { customizeFormItems } from './m_customize_form_items'; import { RecurrenceForm } from './m_recurrence_form'; import { createFormIconTemplate, getStartDateCommonConfig, RecurrenceRule } from './utils'; +export interface AppointmentFormConfig { + dataAccessors: AppointmentDataAccessor; + editing: SchedulerProperties['editing']; + resourceManager: ResourceManager; + firstDayOfWeek: number; + startDayHour: number; + createComponent: (element: dxElementWrapper, Component: any, options: any) => any; + getCalculatedEndDate: (startDate: Date) => Date; +} + const CLASSES = { form: 'dx-scheduler-form', icon: 'dx-icon', @@ -118,9 +129,7 @@ const RESOURCES_GROUP_ICON_NAME = 'resourcesGroupIcon'; const DESCRIPTION_ICON_NAME = 'descriptionIcon'; export class AppointmentForm { - private readonly scheduler: any; - - private readonly resourceManager!: ResourceManager; + private readonly config: AppointmentFormConfig; private dxFormInstance?: dxForm; @@ -132,6 +141,10 @@ export class AppointmentForm { private $recurrenceGroup?: dxElementWrapper; + private get resourceManager(): ResourceManager { + return this.config.resourceManager; + } + get dxForm(): dxForm { return this.dxFormInstance as dxForm; } @@ -158,29 +171,28 @@ export class AppointmentForm { } get startDate(): Date | null { - const { startDateExpr } = this.scheduler.getDataAccessors().expr; + const { startDateExpr } = this.config.dataAccessors.expr; const value = this.getFormDataField(startDateExpr); return value ? new Date(dateSerialization.deserializeDate(value)) : null; } get endDate(): Date | null { - const { endDateExpr } = this.scheduler.getDataAccessors().expr; + const { endDateExpr } = this.config.dataAccessors.expr; const value = this.getFormDataField(endDateExpr); return value ? new Date(dateSerialization.deserializeDate(value)) : null; } get recurrenceRuleRaw(): string | null { - const { recurrenceRuleExpr } = this.scheduler.getDataAccessors().expr; + const { recurrenceRuleExpr } = this.config.dataAccessors.expr; const value = this.getFormDataField(recurrenceRuleExpr) as string | undefined; return value ?? null; } - constructor(scheduler: any) { - this.scheduler = scheduler; - this.resourceManager = scheduler.getResourceManager(); + constructor(config: AppointmentFormConfig) { + this.config = config; } private getFormDataField(field: string): any { @@ -200,7 +212,10 @@ export class AppointmentForm { const mainGroup = this.createMainFormGroup(); - this.recurrenceForm = new RecurrenceForm(this.scheduler); + this.recurrenceForm = new RecurrenceForm({ + firstDayOfWeek: this.config.firstDayOfWeek, + createComponent: this.config.createComponent, + }); const recurrenceGroup = this.recurrenceForm.createRecurrenceFormGroup(); const items = [mainGroup, recurrenceGroup]; @@ -212,28 +227,28 @@ export class AppointmentForm { this.applyFormItemDefaults(mainGroup, showMainGroupIcons); this.applyFormItemDefaults(recurrenceGroup, showRecurrenceGroupIcons); - const editingConfig = this.scheduler.getEditingConfig(); - const customizedItems = customizeFormItems(items, editingConfig?.form?.items); + const customizedItems = customizeFormItems(items, this.getEditingForm()?.items); this.createForm(customizedItems); } - private getIconsShowMode(): AppointmentFormIconsShowMode { - const editingConfig = this.scheduler.getEditingConfig() as SchedulerProperties['editing']; - - if (isBoolean(editingConfig)) { - return DEFAULT_ICONS_SHOW_MODE; + private getEditingForm(): NonNullable['form']> | undefined { + const { editing } = this.config; + if (isBoolean(editing) || !editing) { + return undefined; } + return editing.form ?? undefined; + } - return editingConfig?.form?.iconsShowMode ?? DEFAULT_ICONS_SHOW_MODE; + private getIconsShowMode(): AppointmentFormIconsShowMode { + return this.getEditingForm()?.iconsShowMode ?? DEFAULT_ICONS_SHOW_MODE; } private createForm(items: FormProperties['items']): dxForm { const element = $('
'); - const editingConfig = this.scheduler.getEditingConfig(); const { items: formItems, onContentReady, onInitialized, ...customFormOptions - } = editingConfig?.form ?? {}; + } = this.getEditingForm() ?? {}; const defaultOptions: FormProperties = { items, @@ -251,7 +266,7 @@ export class AppointmentForm { onFieldDataChanged: (e) => { const { startDateExpr, endDateExpr, recurrenceRuleExpr, - } = this.scheduler.getDataAccessors().expr; + } = this.config.dataAccessors.expr; const { dataField } = e; @@ -261,7 +276,7 @@ export class AppointmentForm { const isDateRangeChanged = [startDateExpr, endDateExpr].includes(dataField); const isRecurrenceRuleChanged = dataField === recurrenceRuleExpr; - const isResourceChanged = Object.keys(this.scheduler.getResourceById()).includes(dataField); + const isResourceChanged = Object.keys(this.config.resourceManager.resourceById).includes(dataField); if (isDateRangeChanged) { this.updateDateEditorsValues(); @@ -300,7 +315,7 @@ export class AppointmentForm { } as FormProperties; const formOptions = extend(true, defaultOptions, customFormOptions); - return this.scheduler.createComponent(element, dxForm, formOptions) as dxForm; + return this.config.createComponent(element, dxForm, formOptions) as dxForm; } private createMainFormGroup(): GroupItem { @@ -320,7 +335,7 @@ export class AppointmentForm { } private createSubjectGroup(): GroupItem { - const { textExpr } = this.scheduler.getDataAccessors().expr; + const { textExpr } = this.config.dataAccessors.expr; return { name: SUBJECT_GROUP_NAME, @@ -375,7 +390,7 @@ export class AppointmentForm { } private createAllDaySwitch(): SimpleItem { - const { allDayExpr, startDateExpr, endDateExpr } = this.scheduler.getDataAccessors().expr; + const { allDayExpr, startDateExpr, endDateExpr } = this.config.dataAccessors.expr; return { name: ALL_DAY_EDITOR_NAME, @@ -403,10 +418,9 @@ export class AppointmentForm { this.dxForm.updateData(startDateExpr, allDayStartDate); this.dxForm.updateData(endDateExpr, allDayStartDate); } else { - const startHour = this.scheduler.getStartDayHour(); - startDate.setHours(startHour); + startDate.setHours(this.config.startDayHour); - const calculatedEndDate = this.scheduler.getCalculatedEndDate(startDate); + const calculatedEndDate = this.config.getCalculatedEndDate(startDate); this.dxForm.updateData(startDateExpr, startDate); this.dxForm.updateData(endDateExpr, calculatedEndDate); @@ -419,7 +433,7 @@ export class AppointmentForm { private createStartDateGroup(): GroupItem { const { startDateExpr, startDateTimeZoneExpr, endDateTimeZoneExpr, - } = this.scheduler.getDataAccessors().expr; + } = this.config.dataAccessors.expr; return this.createDateGroup( startDateExpr, @@ -459,7 +473,7 @@ export class AppointmentForm { } private createEndDateGroup(): GroupItem { - const { endDateExpr, endDateTimeZoneExpr } = this.scheduler.getDataAccessors().expr; + const { endDateExpr, endDateTimeZoneExpr } = this.config.dataAccessors.expr; return this.createDateGroup( endDateExpr, @@ -498,8 +512,9 @@ export class AppointmentForm { timeItemOptions?: SimpleItem, timezoneItemOptions?: SimpleItem, ): GroupItem { - const { allowTimeZoneEditing } = this.scheduler.getEditingConfig(); - const { startDateExpr, endDateExpr } = this.scheduler.getDataAccessors().expr; + const { editing } = this.config; + const allowTimeZoneEditing = !isBoolean(editing) ? editing?.allowTimeZoneEditing : undefined; + const { startDateExpr, endDateExpr } = this.config.dataAccessors.expr; const isStartDateEditor = dateExpr === startDateExpr; const getEditorsDate = (): Date | null => (isStartDateEditor ? this.startDate : this.endDate); @@ -567,7 +582,7 @@ export class AppointmentForm { items: [ extend( true, - getStartDateCommonConfig(this.scheduler.getFirstDayOfWeek()), + getStartDateCommonConfig(this.config.firstDayOfWeek), { editorOptions: { onValueChanged: (e) => { @@ -593,7 +608,7 @@ export class AppointmentForm { type: 'time', useMaskBehavior: true, calendarOptions: { - firstDayOfWeek: this.scheduler.getFirstDayOfWeek(), + firstDayOfWeek: this.config.firstDayOfWeek, }, onValueChanged: (e) => { dateValueChanged(e, (date: Date): void => { @@ -625,7 +640,7 @@ export class AppointmentForm { } private createRepeatGroup(): GroupItem { - const { recurrenceRuleExpr } = this.scheduler.getDataAccessors().expr; + const { recurrenceRuleExpr } = this.config.dataAccessors.expr; return { name: REPEAT_GROUP_NAME, @@ -677,7 +692,7 @@ export class AppointmentForm { } private createDescriptionGroup(): GroupItem { - const { descriptionExpr } = this.scheduler.getDataAccessors().expr; + const { descriptionExpr } = this.config.dataAccessors.expr; return { name: DESCRIPTION_GROUP_NAME, @@ -709,7 +724,7 @@ export class AppointmentForm { } private createResourcesGroup(): GroupItem { - const resourcesLoaders: ResourceLoader[] = Object.values(this.scheduler.getResourceById()); + const resourcesLoaders: ResourceLoader[] = Object.values(this.config.resourceManager.resourceById); let resourcesItems: FormItem[] = resourcesLoaders.map((resourceLoader) => { const { dataSource, dataAccessor } = resourceLoader; @@ -854,8 +869,8 @@ export class AppointmentForm { showMainGroup(): void { const currentHeight = this.dxPopup.option('height') as string | number | undefined; - const editingConfig = this.scheduler.getEditingConfig(); - const configuredHeight = editingConfig?.popup?.height ?? 'auto'; + const { editing } = this.config; + const configuredHeight = (!isBoolean(editing) && editing?.popup?.height) || 'auto'; if (typeof currentHeight === 'number') { this.dxPopup.option('height', configuredHeight); @@ -908,7 +923,7 @@ export class AppointmentForm { saveRecurrenceValue(): void { const { recurrenceRule } = this.recurrenceForm; - const { recurrenceRuleExpr } = this.scheduler.getDataAccessors().expr; + const { recurrenceRuleExpr } = this.config.dataAccessors.expr; const recurrenceRuleSerialized = recurrenceRule.toString() ?? ''; @@ -1003,7 +1018,7 @@ export class AppointmentForm { } private updateDateTimeEditorsVisibility(): void { - const { allDayExpr } = this.scheduler.getDataAccessors().expr; + const { allDayExpr } = this.config.dataAccessors.expr; const visible = !this.getFormDataField(allDayExpr); const dateOptionsGroupPath = `${MAIN_GROUP_NAME}.${DATE_GROUP_NAME}.${DATE_OPTIONS_GROUP_NAME}`; diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_recurrence_form.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/m_recurrence_form.ts index 0247a77a473d..05873a75d7ff 100644 --- a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_recurrence_form.ts +++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/m_recurrence_form.ts @@ -10,7 +10,6 @@ import type { Properties as NumberBoxProperties } from '@js/ui/number_box'; import type { Properties as RadioGroupProperties } from '@js/ui/radio_group'; import type { Properties as SelectBoxProperties } from '@js/ui/select_box'; -import type Scheduler from '../m_scheduler'; import { getRecurrenceFrequencyItems, getRecurrenceMonthItems, @@ -19,6 +18,11 @@ import { } from './localized_items'; import { createFormIconTemplate, getStartDateCommonConfig, RecurrenceRule } from './utils'; +export interface RecurrenceFormConfig { + firstDayOfWeek: number; + createComponent: (element: dxElementWrapper, Component: any, options: any) => any; +} + const CLASSES = { groupWithIcon: 'dx-scheduler-form-group-with-icon', formIcon: 'dx-scheduler-form-icon', @@ -84,7 +88,7 @@ const RECURRENCE_GROUP_NAME = 'recurrenceGroup'; export class RecurrenceForm { recurrenceRule: RecurrenceRule = new RecurrenceRule('', new Date()); - private readonly scheduler: any; + private readonly config: RecurrenceFormConfig; private dxFormInstance?: dxForm; @@ -92,8 +96,8 @@ export class RecurrenceForm { private readOnly = false; - constructor(scheduler: Scheduler) { - this.scheduler = scheduler; + constructor(config: RecurrenceFormConfig) { + this.config = config; } private createByMonthDayNumberBoxItem( @@ -166,7 +170,7 @@ export class RecurrenceForm { }, extend( true, - getStartDateCommonConfig(this.scheduler.getFirstDayOfWeek()), + getStartDateCommonConfig(this.config.firstDayOfWeek), { name: EDITOR_NAMES.recurrenceStartDateEditor, cssClass: CLASSES.recurrenceStartDateEditor, @@ -307,13 +311,13 @@ export class RecurrenceForm { }, template: (): dxElementWrapper => { const $container = $('
').addClass(CLASSES.daysOfWeekButtons); - const weekDayItems = getRecurrenceWeekDayItems(this.scheduler.getFirstDayOfWeek()); + const weekDayItems = getRecurrenceWeekDayItems(this.config.firstDayOfWeek); weekDayItems.forEach((item) => { const buttonContainer = $('
').appendTo($container); this.weekDayButtons[item.key]?.dispose(); - this.weekDayButtons[item.key] = this.scheduler.createComponent(buttonContainer, Button, { + this.weekDayButtons[item.key] = this.config.createComponent(buttonContainer, Button, { text: item.text, disabled: this.readOnly, onContentReady: (e): void => { @@ -472,7 +476,7 @@ export class RecurrenceForm { type: 'date', useMaskBehavior: true, calendarOptions: { - firstDayOfWeek: this.scheduler.getFirstDayOfWeek(), + firstDayOfWeek: this.config.firstDayOfWeek, }, inputAttr: { 'aria-label': messageLocalization.format('dxScheduler-recurrenceUntilDateLabel'), @@ -546,7 +550,7 @@ export class RecurrenceForm { if (recurrenceRule.byDay.length === 0) { const defaultByDay = [ - ICAL_WEEK_DAYS[startDate?.getDay() ?? this.scheduler.getFirstDayOfWeek()], + ICAL_WEEK_DAYS[startDate?.getDay() ?? this.config.firstDayOfWeek], ]; recurrenceRule.byDay = defaultByDay; diff --git a/packages/devextreme/js/__internal/scheduler/m_scheduler.ts b/packages/devextreme/js/__internal/scheduler/m_scheduler.ts index 66fa3dcb5f61..18694df113d4 100644 --- a/packages/devextreme/js/__internal/scheduler/m_scheduler.ts +++ b/packages/devextreme/js/__internal/scheduler/m_scheduler.ts @@ -37,7 +37,7 @@ import { dateUtilsTs } from '@ts/core/utils/date'; import { createA11yStatusContainer } from './a11y_status/a11y_status_render'; import { getA11yStatusText } from './a11y_status/a11y_status_text'; -import { AppointmentForm } from './appointment_popup/m_form'; +import { AppointmentForm, type AppointmentFormConfig } from './appointment_popup/m_form'; import { AppointmentPopup } from './appointment_popup/m_popup'; import AppointmentCollection from './appointments/m_appointment_collection'; import type { AppointmentsProperties } from './appointments_new/appointments'; @@ -276,7 +276,7 @@ class Scheduler extends SchedulerOptionsBaseWidget { case 'firstDayOfWeek': this.updateOption('workSpace', name, value); this.updateOption('header', name, value); - this.cleanPopup(); + this.createAppointmentPopupForm(); break; case 'currentDate': { const dateValue = this.getViewOption(name); @@ -397,6 +397,7 @@ class Scheduler extends SchedulerOptionsBaseWidget { this.setRemoteFilterIfNeeded(); this.postponeDataSourceLoading(); + this.createAppointmentPopupForm(); break; // TODO Vinogradov refactoring: merge it with startDayHour / endDayHour case 'offset': @@ -506,7 +507,7 @@ class Scheduler extends SchedulerOptionsBaseWidget { this.bringEditingModeToAppointments(editing); this.hideAppointmentTooltip(); - this.cleanPopup(); + this.createAppointmentPopupForm(); break; } case 'showAllDayPanel': @@ -1134,22 +1135,18 @@ class Scheduler extends SchedulerOptionsBaseWidget { } createAppointmentForm() { - const scheduler = { - getResourceById: () => this.resourceManager.resourceById, - getDataAccessors: () => this._dataAccessors, + const config: AppointmentFormConfig = { + dataAccessors: this._dataAccessors, + editing: this.editing, + resourceManager: this.resourceManager, + firstDayOfWeek: this.option('firstDayOfWeek'), + startDayHour: this.option('startDayHour'), // @ts-expect-error createComponent: (element, component, options) => this._createComponent(element, component, options), - - getEditingConfig: () => this.editing, - getResourceManager: () => this.resourceManager, - - getFirstDayOfWeek: () => this.option('firstDayOfWeek'), - getStartDayHour: () => this.option('startDayHour'), getCalculatedEndDate: (startDateWithStartHour) => this._workSpace.calculateEndDate(startDateWithStartHour), - getTimeZoneCalculator: () => this.timeZoneCalculator, }; - return new AppointmentForm(scheduler); + return new AppointmentForm(config); } createAppointmentPopup(form) { From cb358bc9dfde635db85bb36dcf7c73694fb8b277 Mon Sep 17 00:00:00 2001 From: Aleksey Semikozov Date: Mon, 27 Apr 2026 10:24:22 -0300 Subject: [PATCH 2/3] Scheduler - Fix TS strict build errors in form refactor --- .../scheduler/appointment_popup/m_form.ts | 17 +++++++++++------ .../scheduler/appointment_popup/utils.ts | 2 +- .../js/__internal/scheduler/m_scheduler.ts | 4 ++-- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_form.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/m_form.ts index 7b81d8f2249a..3977c679a02a 100644 --- a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_form.ts +++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/m_form.ts @@ -38,6 +38,8 @@ import { customizeFormItems } from './m_customize_form_items'; import { RecurrenceForm } from './m_recurrence_form'; import { createFormIconTemplate, getStartDateCommonConfig, RecurrenceRule } from './utils'; +type SchedulerEditingObject = Exclude, boolean>; + export interface AppointmentFormConfig { dataAccessors: AppointmentDataAccessor; editing: SchedulerProperties['editing']; @@ -232,12 +234,17 @@ export class AppointmentForm { this.createForm(customizedItems); } - private getEditingForm(): NonNullable['form']> | undefined { + private getEditingForm(): SchedulerEditingObject['form'] { + const editing = this.getEditingObject(); + return editing?.form; + } + + private getEditingObject(): SchedulerEditingObject | undefined { const { editing } = this.config; if (isBoolean(editing) || !editing) { return undefined; } - return editing.form ?? undefined; + return editing; } private getIconsShowMode(): AppointmentFormIconsShowMode { @@ -512,8 +519,7 @@ export class AppointmentForm { timeItemOptions?: SimpleItem, timezoneItemOptions?: SimpleItem, ): GroupItem { - const { editing } = this.config; - const allowTimeZoneEditing = !isBoolean(editing) ? editing?.allowTimeZoneEditing : undefined; + const allowTimeZoneEditing = this.getEditingObject()?.allowTimeZoneEditing; const { startDateExpr, endDateExpr } = this.config.dataAccessors.expr; const isStartDateEditor = dateExpr === startDateExpr; @@ -869,8 +875,7 @@ export class AppointmentForm { showMainGroup(): void { const currentHeight = this.dxPopup.option('height') as string | number | undefined; - const { editing } = this.config; - const configuredHeight = (!isBoolean(editing) && editing?.popup?.height) || 'auto'; + const configuredHeight = this.getEditingObject()?.popup?.height ?? 'auto'; if (typeof currentHeight === 'number') { this.dxPopup.option('height', configuredHeight); diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/utils.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/utils.ts index 7acc0615dd6a..6bb29ddd4ff9 100644 --- a/packages/devextreme/js/__internal/scheduler/appointment_popup/utils.ts +++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/utils.ts @@ -13,7 +13,7 @@ import type { Rule } from '../recurrence/types'; export const createFormIconTemplate = (iconName: string): () => dxElementWrapper => (): dxElementWrapper => getImageContainer(iconName) ?? $('
').addClass('dx-scheduler-form-icon-sized-gap'); -export const getStartDateCommonConfig = (firstDayOfWeek: string): SimpleItem => ({ +export const getStartDateCommonConfig = (firstDayOfWeek: number): SimpleItem => ({ colSpan: 1, itemType: 'simple', editorType: 'dxDateBox', diff --git a/packages/devextreme/js/__internal/scheduler/m_scheduler.ts b/packages/devextreme/js/__internal/scheduler/m_scheduler.ts index 18694df113d4..d4e5dbd04c58 100644 --- a/packages/devextreme/js/__internal/scheduler/m_scheduler.ts +++ b/packages/devextreme/js/__internal/scheduler/m_scheduler.ts @@ -1139,8 +1139,8 @@ class Scheduler extends SchedulerOptionsBaseWidget { dataAccessors: this._dataAccessors, editing: this.editing, resourceManager: this.resourceManager, - firstDayOfWeek: this.option('firstDayOfWeek'), - startDayHour: this.option('startDayHour'), + firstDayOfWeek: this.option('firstDayOfWeek') ?? 0, + startDayHour: this.option('startDayHour') ?? 0, // @ts-expect-error createComponent: (element, component, options) => this._createComponent(element, component, options), getCalculatedEndDate: (startDateWithStartHour) => this._workSpace.calculateEndDate(startDateWithStartHour), From 561378dcab3206233fea61cd9544a50f4f8cdb4b Mon Sep 17 00:00:00 2001 From: Aleksey Semikozov Date: Tue, 28 Apr 2026 07:09:41 -0300 Subject: [PATCH 3/3] Scheduler - Use locale-aware getFirstDayOfWeek and widen createComponent type --- .../js/__internal/scheduler/appointment_popup/m_form.ts | 8 +++++++- .../scheduler/appointment_popup/m_recurrence_form.ts | 6 +++++- .../devextreme/js/__internal/scheduler/m_scheduler.ts | 2 +- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_form.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/m_form.ts index 3977c679a02a..25ae8163d8f9 100644 --- a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_form.ts +++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/m_form.ts @@ -40,13 +40,19 @@ import { createFormIconTemplate, getStartDateCommonConfig, RecurrenceRule } from type SchedulerEditingObject = Exclude, boolean>; +export type CreateComponentFn = ( + element: string | HTMLElement | dxElementWrapper | Element, + Component: any, + options: any, +) => any; + export interface AppointmentFormConfig { dataAccessors: AppointmentDataAccessor; editing: SchedulerProperties['editing']; resourceManager: ResourceManager; firstDayOfWeek: number; startDayHour: number; - createComponent: (element: dxElementWrapper, Component: any, options: any) => any; + createComponent: CreateComponentFn; getCalculatedEndDate: (startDate: Date) => Date; } diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_recurrence_form.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/m_recurrence_form.ts index 05873a75d7ff..6a00f615b494 100644 --- a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_recurrence_form.ts +++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/m_recurrence_form.ts @@ -20,7 +20,11 @@ import { createFormIconTemplate, getStartDateCommonConfig, RecurrenceRule } from export interface RecurrenceFormConfig { firstDayOfWeek: number; - createComponent: (element: dxElementWrapper, Component: any, options: any) => any; + createComponent: ( + element: string | HTMLElement | dxElementWrapper | Element, + Component: any, + options: any, + ) => any; } const CLASSES = { diff --git a/packages/devextreme/js/__internal/scheduler/m_scheduler.ts b/packages/devextreme/js/__internal/scheduler/m_scheduler.ts index d4e5dbd04c58..71491e4a3451 100644 --- a/packages/devextreme/js/__internal/scheduler/m_scheduler.ts +++ b/packages/devextreme/js/__internal/scheduler/m_scheduler.ts @@ -1139,7 +1139,7 @@ class Scheduler extends SchedulerOptionsBaseWidget { dataAccessors: this._dataAccessors, editing: this.editing, resourceManager: this.resourceManager, - firstDayOfWeek: this.option('firstDayOfWeek') ?? 0, + firstDayOfWeek: this.getFirstDayOfWeek(), startDayHour: this.option('startDayHour') ?? 0, // @ts-expect-error createComponent: (element, component, options) => this._createComponent(element, component, options),