From 77cb409c0758fdcde4764115e62d63937ff06dda Mon Sep 17 00:00:00 2001 From: Aleksey Semikozov Date: Wed, 25 Mar 2026 23:49:55 -0300 Subject: [PATCH 1/6] Scheduler - replace action enum with onDone/onCancel callbacks in AppointmentPopup --- .../__mock__/create_appointment_popup.ts | 15 +++-- .../appointment_popup.test.ts | 42 +++++++++++--- .../scheduler/appointment_popup/m_popup.ts | 53 +++++------------- .../js/__internal/scheduler/m_scheduler.ts | 56 +++++++++++++------ 4 files changed, 96 insertions(+), 70 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 0ca0948beaee..3e222f9cce6a 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 @@ -6,7 +6,6 @@ import { Deferred } from '@js/core/utils/deferred'; import { mockTimeZoneCalculator } from '../../__mock__/timezone_calculator.mock'; import { AppointmentForm } from '../../appointment_popup/m_form'; import { - ACTION_TO_APPOINTMENT, APPOINTMENT_POPUP_CLASS, AppointmentPopup, } from '../../appointment_popup/m_popup'; @@ -59,13 +58,13 @@ const resolvedDeferred = (): any => { interface CreateAppointmentPopupOptions { appointmentData?: Record; - action?: number; editing?: Record; firstDayOfWeek?: number; startDayHour?: number; onAppointmentFormOpening?: (...args: unknown[]) => void; - addAppointment?: jest.Mock; - updateAppointment?: jest.Mock; + onDone?: jest.Mock; + title?: string; + readOnly?: boolean; } interface CreateAppointmentPopupResult { @@ -78,6 +77,7 @@ interface CreateAppointmentPopupResult { updateAppointment: jest.Mock; focus: jest.Mock; updateScrollPosition: jest.Mock; + onDone: jest.Mock; }; dispose: () => void; } @@ -112,6 +112,7 @@ export const createAppointmentPopup = async ( ?? jest.fn(resolvedDeferred); const focus = jest.fn(); const updateScrollPosition = jest.fn(); + const onDone = options.onDone ?? jest.fn(resolvedDeferred); const formSchedulerProxy = { getResourceById: (): Record => resourceManager.resourceById, @@ -161,9 +162,10 @@ export const createAppointmentPopup = async ( const appointmentData = options.appointmentData ?? { ...DEFAULT_APPOINTMENT }; - const action = options.action ?? ACTION_TO_APPOINTMENT.CREATE; + const title = options.title ?? 'New Appointment'; + const readOnly = options.readOnly ?? false; - popup.show(appointmentData, { action, allowSaving: true }); + popup.show(appointmentData, { onDone, title, readOnly }); await new Promise(process.nextTick); const selector = `.dx-overlay-wrapper.${APPOINTMENT_POPUP_CLASS}`; @@ -196,6 +198,7 @@ export const createAppointmentPopup = async ( updateAppointment, focus, updateScrollPosition, + onDone, }, dispose, }; diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts index 1f5ae059c285..49a7913b7112 100644 --- a/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts +++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts @@ -7,7 +7,6 @@ import { createAppointmentPopup, disposeAppointmentPopups, } from '../__tests__/__mock__/create_appointment_popup'; -import { ACTION_TO_APPOINTMENT } from './m_popup'; describe('Isolated AppointmentPopup environment', () => { beforeEach(() => { @@ -45,20 +44,19 @@ describe('Isolated AppointmentPopup environment', () => { expect(POM.cancelButton).toBeTruthy(); }); - it('should call addAppointment on Save click for CREATE action', async () => { + it('should call onDone callback on Save click', async () => { const { POM, callbacks } = await createAppointmentPopup({ appointmentData: { text: 'New Appointment', startDate: new Date(2021, 3, 26, 9, 30), endDate: new Date(2021, 3, 26, 11, 0), }, - action: ACTION_TO_APPOINTMENT.CREATE, }); POM.saveButton.click(); - expect(callbacks.addAppointment).toHaveBeenCalledTimes(1); - expect(callbacks.addAppointment).toHaveBeenCalledWith( + expect(callbacks.onDone).toHaveBeenCalledTimes(1); + expect(callbacks.onDone).toHaveBeenCalledWith( expect.objectContaining({ text: 'New Appointment', startDate: new Date(2021, 3, 26, 9, 30), @@ -67,19 +65,45 @@ describe('Isolated AppointmentPopup environment', () => { ); }); - it('should call updateAppointment on Save click for UPDATE action', async () => { + it('should not call addAppointment or updateAppointment directly', async () => { const { POM, callbacks } = await createAppointmentPopup({ appointmentData: { - text: 'Existing Appointment', + text: 'Test', startDate: new Date(2021, 3, 26, 9, 30), endDate: new Date(2021, 3, 26, 11, 0), }, - action: ACTION_TO_APPOINTMENT.UPDATE, }); POM.saveButton.click(); - expect(callbacks.updateAppointment).toHaveBeenCalledTimes(1); + expect(callbacks.addAppointment).not.toHaveBeenCalled(); + expect(callbacks.updateAppointment).not.toHaveBeenCalled(); + }); + + it('should display title from config', async () => { + const { POM } = await createAppointmentPopup({ + title: 'Edit Appointment', + }); + + const titleElement = POM.element.querySelector('.dx-toolbar-label'); + expect(titleElement?.textContent).toBe('Edit Appointment'); + }); + + it('should hide Save button when readOnly is true', async () => { + const { POM } = await createAppointmentPopup({ + readOnly: true, + }); + + const saveButtons = POM.element.querySelectorAll('.dx-popup-done'); + expect(saveButtons.length).toBe(0); + }); + + it('should show Save button when readOnly is false', async () => { + const { POM } = await createAppointmentPopup({ + readOnly: false, + }); + + expect(POM.saveButton).toBeTruthy(); }); it('should hide popup on Cancel click', async () => { diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_popup.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/m_popup.ts index 4aa093eae31c..cf8e6ed2f06b 100644 --- a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_popup.ts +++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/m_popup.ts @@ -24,11 +24,11 @@ const POPUP_FULL_SCREEN_MODE_WINDOW_WIDTH_THRESHOLD = 485; const DAY_IN_MS = dateUtils.dateToMilliseconds('day'); -export const ACTION_TO_APPOINTMENT = { - CREATE: 0, - UPDATE: 1, - EXCLUDE_FROM_SERIES: 2, -}; +export interface AppointmentPopupConfig { + onDone: (appointment: Record) => PromiseLike; + title: string; + readOnly: boolean; +} export class AppointmentPopup { scheduler: any; @@ -41,6 +41,8 @@ export class AppointmentPopup { state: any; + private config!: AppointmentPopupConfig; + get popup(): dxPopup { return this._popup as dxPopup; } @@ -54,7 +56,6 @@ export class AppointmentPopup { this.form = form; this.state = { - action: null, lastEditData: null, saveChangesLocker: false, appointment: { @@ -63,11 +64,9 @@ export class AppointmentPopup { }; } - show(appointment, config) { + show(appointment, config: AppointmentPopupConfig) { this.state.appointment.data = appointment; - this.state.action = config.action; - this.state.allowSaving = config.allowSaving; - this.state.excludeInfo = config.excludeInfo; + this.config = config; this.disposePopup(); @@ -173,16 +172,8 @@ export class AppointmentPopup { }); } - private isReadOnly(appointmentAdapter: AppointmentAdapter): boolean { - if (Boolean(appointmentAdapter.source) && appointmentAdapter.disabled) { - return true; - } - - if (this.state.action === ACTION_TO_APPOINTMENT.CREATE) { - return false; - } - - return !this.scheduler.getEditingConfig().allowUpdating; + private isReadOnly(): boolean { + return this.config.readOnly; } private createAppointmentAdapter(rawAppointment): AppointmentAdapter { @@ -200,7 +191,7 @@ export class AppointmentPopup { const formData = this.createFormData(appointmentAdapter); - this.form.readOnly = this.isReadOnly(appointmentAdapter); + this.form.readOnly = this.isReadOnly(); this.form.formData = formData; this.form.showMainGroup(); @@ -286,20 +277,7 @@ export class AppointmentPopup { const appointment = clonedAdapter.source; - switch (this.state.action) { - case ACTION_TO_APPOINTMENT.CREATE: - this.scheduler.addAppointment(appointment).done(deferred.resolve); - break; - case ACTION_TO_APPOINTMENT.UPDATE: - this.scheduler.updateAppointment(this.state.appointment.data, appointment).done(deferred.resolve); - break; - case ACTION_TO_APPOINTMENT.EXCLUDE_FROM_SERIES: - this.scheduler.updateAppointment(this.state.excludeInfo.sourceAppointment, this.state.excludeInfo.updatedAppointment); - this.scheduler.addAppointment(appointment).done(deferred.resolve); - break; - default: - break; - } + when(this.config.onDone(appointment)).done(deferred.resolve); deferred.done(() => { hideLoading(); @@ -413,13 +391,10 @@ export class AppointmentPopup { return; } - const isCreating = this.state.action === ACTION_TO_APPOINTMENT.CREATE; - const formTitleKey = isCreating ? 'dxScheduler-newPopupTitle' : 'dxScheduler-editPopupTitle'; - const toolbarItems: ToolbarItem[] = [{ toolbar: 'top', location: 'before', - text: messageLocalization.format(formTitleKey), + text: this.config.title, cssClass: 'dx-toolbar-label', }]; diff --git a/packages/devextreme/js/__internal/scheduler/m_scheduler.ts b/packages/devextreme/js/__internal/scheduler/m_scheduler.ts index 7b446445240b..dedb723dd2b0 100644 --- a/packages/devextreme/js/__internal/scheduler/m_scheduler.ts +++ b/packages/devextreme/js/__internal/scheduler/m_scheduler.ts @@ -1623,14 +1623,24 @@ class Scheduler extends SchedulerOptionsBaseWidget { } if (isPopupEditing) { - this.appointmentPopup.show(singleRawAppointment, { - isToolbarVisible: true, // TODO: remove when legacyForm is deleted - action: ACTION_TO_APPOINTMENT.EXCLUDE_FROM_SERIES, - excludeInfo: { - sourceAppointment: rawAppointment, - updatedAppointment: appointment.source, - }, - }); + const popupConfig = this.editing.legacyForm + ? { + isToolbarVisible: true, + action: ACTION_TO_APPOINTMENT.EXCLUDE_FROM_SERIES, + excludeInfo: { + sourceAppointment: rawAppointment, + updatedAppointment: appointment.source, + }, + } + : { + onDone: (newAppointment) => { + this.updateAppointment(rawAppointment, appointment.source); + return this.addAppointment(newAppointment); + }, + title: messageLocalization.format('dxScheduler-editPopupTitle'), + readOnly: false, + }; + this.appointmentPopup.show(singleRawAppointment, popupConfig); this.editAppointmentData = rawAppointment; } else { this.updateAppointmentCore(rawAppointment, appointment.source, () => { @@ -2005,20 +2015,34 @@ class Scheduler extends SchedulerOptionsBaseWidget { if (isCreateAppointment) { delete this.editAppointmentData; // TODO - this.editing.allowAdding && this.appointmentPopup.show(rawAppointment, { - isToolbarVisible: true, // TODO: remove when legacyForm is deleted - action: ACTION_TO_APPOINTMENT.CREATE, - }); + if (this.editing.allowAdding) { + const popupConfig = this.editing.legacyForm + ? { isToolbarVisible: true, action: ACTION_TO_APPOINTMENT.CREATE } + : { + onDone: (appointment) => this.addAppointment(appointment), + title: messageLocalization.format('dxScheduler-newPopupTitle'), + readOnly: false, + }; + this.appointmentPopup.show(rawAppointment, popupConfig); + } } else { const startDate = this._dataAccessors.get('startDate', newRawTargetedAppointment || rawAppointment); this.checkRecurringAppointment(rawAppointment, newTargetedAppointment, startDate, () => { this.editAppointmentData = rawAppointment; // TODO - this.appointmentPopup.show(rawAppointment, { - isToolbarVisible: this.editing.allowUpdating, // TODO: remove when legacyForm is deleted - action: ACTION_TO_APPOINTMENT.UPDATE, - }); + const adapter = new AppointmentAdapter(rawAppointment, this._dataAccessors); + const isDisabled = Boolean(adapter.source) && adapter.disabled; + const readOnly = isDisabled || !this.editing.allowUpdating; + + const popupConfig = this.editing.legacyForm + ? { isToolbarVisible: this.editing.allowUpdating, action: ACTION_TO_APPOINTMENT.UPDATE } + : { + onDone: (appointment) => this.updateAppointment(rawAppointment, appointment), + title: messageLocalization.format('dxScheduler-editPopupTitle'), + readOnly, + }; + this.appointmentPopup.show(rawAppointment, popupConfig); }, false, true); } } From 31cd08f3c19757a445cdcad4f833dd7cbdb7f3c0 Mon Sep 17 00:00:00 2001 From: Aleksey Semikozov Date: Wed, 25 Mar 2026 23:56:00 -0300 Subject: [PATCH 2/6] Scheduler - add default config and exclude-from-series test --- .../appointment_popup.test.ts | 33 ++++++++++++++++++- .../scheduler/appointment_popup/m_popup.ts | 6 +++- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts index 49a7913b7112..ea7417c3874d 100644 --- a/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts +++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts @@ -1,5 +1,5 @@ import { - afterEach, beforeEach, describe, expect, it, + afterEach, beforeEach, describe, expect, it, jest, } from '@jest/globals'; import fx from '../../../common/core/animation/fx'; @@ -113,4 +113,35 @@ describe('Isolated AppointmentPopup environment', () => { POM.cancelButton.click(); expect(popup.visible).toBe(false); }); + + it('should support composite onDone for exclude-from-series scenario', async () => { + const updateAppointment = jest.fn(); + const addAppointment = jest.fn(() => Promise.resolve()); + + const sourceAppointment = { text: 'Series', recurrenceRule: 'FREQ=DAILY' }; + const updatedAppointment = { text: 'Series', recurrenceException: '20210426' }; + + const onDone = jest.fn((newAppointment) => { + updateAppointment(sourceAppointment, updatedAppointment); + return addAppointment(newAppointment); + }); + + const { POM } = await createAppointmentPopup({ + appointmentData: { + text: 'Single occurrence', + startDate: new Date(2021, 3, 26, 9, 30), + endDate: new Date(2021, 3, 26, 11, 0), + }, + title: 'Edit Appointment', + onDone, + }); + + POM.saveButton.click(); + + expect(onDone).toHaveBeenCalledTimes(1); + expect(updateAppointment).toHaveBeenCalledWith(sourceAppointment, updatedAppointment); + expect(addAppointment).toHaveBeenCalledWith( + expect.objectContaining({ text: 'Single occurrence' }), + ); + }); }); diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_popup.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/m_popup.ts index cf8e6ed2f06b..caddcf83b9d7 100644 --- a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_popup.ts +++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/m_popup.ts @@ -41,7 +41,11 @@ export class AppointmentPopup { state: any; - private config!: AppointmentPopupConfig; + private config: AppointmentPopupConfig = { + onDone: () => Promise.resolve(), + title: '', + readOnly: false, + }; get popup(): dxPopup { return this._popup as dxPopup; From 9c52ce83c4be5225ca7d768149f37482f6f9ab2a Mon Sep 17 00:00:00 2001 From: Aleksey Semikozov Date: Thu, 26 Mar 2026 00:22:29 -0300 Subject: [PATCH 3/6] Scheduler - preserve isToolbarVisible in popup config for backward compatibility --- .../js/__internal/scheduler/appointment_popup/m_popup.ts | 1 + packages/devextreme/js/__internal/scheduler/m_scheduler.ts | 3 +++ 2 files changed, 4 insertions(+) diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_popup.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/m_popup.ts index caddcf83b9d7..ee822180253a 100644 --- a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_popup.ts +++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/m_popup.ts @@ -28,6 +28,7 @@ export interface AppointmentPopupConfig { onDone: (appointment: Record) => PromiseLike; title: string; readOnly: boolean; + isToolbarVisible?: boolean; } export class AppointmentPopup { diff --git a/packages/devextreme/js/__internal/scheduler/m_scheduler.ts b/packages/devextreme/js/__internal/scheduler/m_scheduler.ts index dedb723dd2b0..be592ba4c778 100644 --- a/packages/devextreme/js/__internal/scheduler/m_scheduler.ts +++ b/packages/devextreme/js/__internal/scheduler/m_scheduler.ts @@ -1639,6 +1639,7 @@ class Scheduler extends SchedulerOptionsBaseWidget { }, title: messageLocalization.format('dxScheduler-editPopupTitle'), readOnly: false, + isToolbarVisible: true, }; this.appointmentPopup.show(singleRawAppointment, popupConfig); this.editAppointmentData = rawAppointment; @@ -2022,6 +2023,7 @@ class Scheduler extends SchedulerOptionsBaseWidget { onDone: (appointment) => this.addAppointment(appointment), title: messageLocalization.format('dxScheduler-newPopupTitle'), readOnly: false, + isToolbarVisible: true, }; this.appointmentPopup.show(rawAppointment, popupConfig); } @@ -2041,6 +2043,7 @@ class Scheduler extends SchedulerOptionsBaseWidget { onDone: (appointment) => this.updateAppointment(rawAppointment, appointment), title: messageLocalization.format('dxScheduler-editPopupTitle'), readOnly, + isToolbarVisible: this.editing.allowUpdating, }; this.appointmentPopup.show(rawAppointment, popupConfig); }, false, true); From ab421e73bfa50a8171e2dbfc1c2a560b3163f0fa Mon Sep 17 00:00:00 2001 From: Aleksey Semikozov Date: Thu, 2 Apr 2026 09:55:53 -0300 Subject: [PATCH 4/6] Scheduler - fix review findings: remove dead isToolbarVisible, fix readOnly in exclude-from-series, fix mock types --- .../scheduler/__tests__/__mock__/create_appointment_popup.ts | 2 ++ .../js/__internal/scheduler/appointment_popup/m_popup.ts | 1 - packages/devextreme/js/__internal/scheduler/m_scheduler.ts | 5 +---- 3 files changed, 3 insertions(+), 5 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 3e222f9cce6a..3d4d139f2626 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 @@ -65,6 +65,8 @@ interface CreateAppointmentPopupOptions { onDone?: jest.Mock; title?: string; readOnly?: boolean; + addAppointment?: jest.Mock; + updateAppointment?: jest.Mock; } interface CreateAppointmentPopupResult { diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_popup.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/m_popup.ts index ee822180253a..caddcf83b9d7 100644 --- a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_popup.ts +++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/m_popup.ts @@ -28,7 +28,6 @@ export interface AppointmentPopupConfig { onDone: (appointment: Record) => PromiseLike; title: string; readOnly: boolean; - isToolbarVisible?: boolean; } export class AppointmentPopup { diff --git a/packages/devextreme/js/__internal/scheduler/m_scheduler.ts b/packages/devextreme/js/__internal/scheduler/m_scheduler.ts index be592ba4c778..c28a50eecdb1 100644 --- a/packages/devextreme/js/__internal/scheduler/m_scheduler.ts +++ b/packages/devextreme/js/__internal/scheduler/m_scheduler.ts @@ -1638,8 +1638,7 @@ class Scheduler extends SchedulerOptionsBaseWidget { return this.addAppointment(newAppointment); }, title: messageLocalization.format('dxScheduler-editPopupTitle'), - readOnly: false, - isToolbarVisible: true, + readOnly: Boolean(appointment.source) && appointment.disabled, }; this.appointmentPopup.show(singleRawAppointment, popupConfig); this.editAppointmentData = rawAppointment; @@ -2023,7 +2022,6 @@ class Scheduler extends SchedulerOptionsBaseWidget { onDone: (appointment) => this.addAppointment(appointment), title: messageLocalization.format('dxScheduler-newPopupTitle'), readOnly: false, - isToolbarVisible: true, }; this.appointmentPopup.show(rawAppointment, popupConfig); } @@ -2043,7 +2041,6 @@ class Scheduler extends SchedulerOptionsBaseWidget { onDone: (appointment) => this.updateAppointment(rawAppointment, appointment), title: messageLocalization.format('dxScheduler-editPopupTitle'), readOnly, - isToolbarVisible: this.editing.allowUpdating, }; this.appointmentPopup.show(rawAppointment, popupConfig); }, false, true); From ab94f41eb6009b10b152cccfd5a090a106118e4e Mon Sep 17 00:00:00 2001 From: Aleksey Semikozov Date: Thu, 2 Apr 2026 10:19:42 -0300 Subject: [PATCH 5/6] Scheduler - fix QUnit test: replace isToolbarVisible assertion with readOnly --- .../integration.appointmentTooltip.tests.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/integration.appointmentTooltip.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/integration.appointmentTooltip.tests.js index 77d1006c646a..1bc011f1702e 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/integration.appointmentTooltip.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/integration.appointmentTooltip.tests.js @@ -335,7 +335,7 @@ module('Integration: Appointment tooltip', moduleConfig, () => { }, 'show has a right appointment data arg'); - assert.equal(args[1].isToolbarVisible, true, 'show has a right createNewAppointment arg'); + assert.equal(args[1].readOnly, false, 'show has a right readOnly arg'); assert.notOk(scheduler.tooltip.isVisible(), 'tooltip was hidden'); }); From c6b9fee796a9bbd6e8d730557cd776c43800ab51 Mon Sep 17 00:00:00 2001 From: Aleksey Semikozov Date: Fri, 3 Apr 2026 04:58:30 -0300 Subject: [PATCH 6/6] Scheduler - apply review: rename onDone to onSave, inline isReadOnly --- .../__tests__/__mock__/create_appointment_popup.ts | 10 +++++----- .../appointment_popup/appointment_popup.test.ts | 14 +++++++------- .../scheduler/appointment_popup/m_popup.ts | 12 ++++-------- .../js/__internal/scheduler/m_scheduler.ts | 6 +++--- 4 files changed, 19 insertions(+), 23 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 3d4d139f2626..cfce7df413a0 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 @@ -62,7 +62,7 @@ interface CreateAppointmentPopupOptions { firstDayOfWeek?: number; startDayHour?: number; onAppointmentFormOpening?: (...args: unknown[]) => void; - onDone?: jest.Mock; + onSave?: jest.Mock; title?: string; readOnly?: boolean; addAppointment?: jest.Mock; @@ -79,7 +79,7 @@ interface CreateAppointmentPopupResult { updateAppointment: jest.Mock; focus: jest.Mock; updateScrollPosition: jest.Mock; - onDone: jest.Mock; + onSave: jest.Mock; }; dispose: () => void; } @@ -114,7 +114,7 @@ export const createAppointmentPopup = async ( ?? jest.fn(resolvedDeferred); const focus = jest.fn(); const updateScrollPosition = jest.fn(); - const onDone = options.onDone ?? jest.fn(resolvedDeferred); + const onSave = options.onSave ?? jest.fn(resolvedDeferred); const formSchedulerProxy = { getResourceById: (): Record => resourceManager.resourceById, @@ -167,7 +167,7 @@ export const createAppointmentPopup = async ( const title = options.title ?? 'New Appointment'; const readOnly = options.readOnly ?? false; - popup.show(appointmentData, { onDone, title, readOnly }); + popup.show(appointmentData, { onSave, title, readOnly }); await new Promise(process.nextTick); const selector = `.dx-overlay-wrapper.${APPOINTMENT_POPUP_CLASS}`; @@ -200,7 +200,7 @@ export const createAppointmentPopup = async ( updateAppointment, focus, updateScrollPosition, - onDone, + onSave, }, dispose, }; diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts index ea7417c3874d..8ebdddc70365 100644 --- a/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts +++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts @@ -44,7 +44,7 @@ describe('Isolated AppointmentPopup environment', () => { expect(POM.cancelButton).toBeTruthy(); }); - it('should call onDone callback on Save click', async () => { + it('should call onSave callback on Save click', async () => { const { POM, callbacks } = await createAppointmentPopup({ appointmentData: { text: 'New Appointment', @@ -55,8 +55,8 @@ describe('Isolated AppointmentPopup environment', () => { POM.saveButton.click(); - expect(callbacks.onDone).toHaveBeenCalledTimes(1); - expect(callbacks.onDone).toHaveBeenCalledWith( + expect(callbacks.onSave).toHaveBeenCalledTimes(1); + expect(callbacks.onSave).toHaveBeenCalledWith( expect.objectContaining({ text: 'New Appointment', startDate: new Date(2021, 3, 26, 9, 30), @@ -114,14 +114,14 @@ describe('Isolated AppointmentPopup environment', () => { expect(popup.visible).toBe(false); }); - it('should support composite onDone for exclude-from-series scenario', async () => { + it('should support composite onSave for exclude-from-series scenario', async () => { const updateAppointment = jest.fn(); const addAppointment = jest.fn(() => Promise.resolve()); const sourceAppointment = { text: 'Series', recurrenceRule: 'FREQ=DAILY' }; const updatedAppointment = { text: 'Series', recurrenceException: '20210426' }; - const onDone = jest.fn((newAppointment) => { + const onSave = jest.fn((newAppointment) => { updateAppointment(sourceAppointment, updatedAppointment); return addAppointment(newAppointment); }); @@ -133,12 +133,12 @@ describe('Isolated AppointmentPopup environment', () => { endDate: new Date(2021, 3, 26, 11, 0), }, title: 'Edit Appointment', - onDone, + onSave, }); POM.saveButton.click(); - expect(onDone).toHaveBeenCalledTimes(1); + expect(onSave).toHaveBeenCalledTimes(1); expect(updateAppointment).toHaveBeenCalledWith(sourceAppointment, updatedAppointment); expect(addAppointment).toHaveBeenCalledWith( expect.objectContaining({ text: 'Single occurrence' }), diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_popup.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/m_popup.ts index caddcf83b9d7..cb34f02ad2b7 100644 --- a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_popup.ts +++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/m_popup.ts @@ -25,7 +25,7 @@ const POPUP_FULL_SCREEN_MODE_WINDOW_WIDTH_THRESHOLD = 485; const DAY_IN_MS = dateUtils.dateToMilliseconds('day'); export interface AppointmentPopupConfig { - onDone: (appointment: Record) => PromiseLike; + onSave: (appointment: Record) => PromiseLike; title: string; readOnly: boolean; } @@ -42,7 +42,7 @@ export class AppointmentPopup { state: any; private config: AppointmentPopupConfig = { - onDone: () => Promise.resolve(), + onSave: () => Promise.resolve(), title: '', readOnly: false, }; @@ -176,10 +176,6 @@ export class AppointmentPopup { }); } - private isReadOnly(): boolean { - return this.config.readOnly; - } - private createAppointmentAdapter(rawAppointment): AppointmentAdapter { return new AppointmentAdapter( rawAppointment, @@ -195,7 +191,7 @@ export class AppointmentPopup { const formData = this.createFormData(appointmentAdapter); - this.form.readOnly = this.isReadOnly(); + this.form.readOnly = this.config.readOnly; this.form.formData = formData; this.form.showMainGroup(); @@ -281,7 +277,7 @@ export class AppointmentPopup { const appointment = clonedAdapter.source; - when(this.config.onDone(appointment)).done(deferred.resolve); + when(this.config.onSave(appointment)).done(deferred.resolve); deferred.done(() => { hideLoading(); diff --git a/packages/devextreme/js/__internal/scheduler/m_scheduler.ts b/packages/devextreme/js/__internal/scheduler/m_scheduler.ts index c28a50eecdb1..e50398773cfd 100644 --- a/packages/devextreme/js/__internal/scheduler/m_scheduler.ts +++ b/packages/devextreme/js/__internal/scheduler/m_scheduler.ts @@ -1633,7 +1633,7 @@ class Scheduler extends SchedulerOptionsBaseWidget { }, } : { - onDone: (newAppointment) => { + onSave: (newAppointment) => { this.updateAppointment(rawAppointment, appointment.source); return this.addAppointment(newAppointment); }, @@ -2019,7 +2019,7 @@ class Scheduler extends SchedulerOptionsBaseWidget { const popupConfig = this.editing.legacyForm ? { isToolbarVisible: true, action: ACTION_TO_APPOINTMENT.CREATE } : { - onDone: (appointment) => this.addAppointment(appointment), + onSave: (appointment) => this.addAppointment(appointment), title: messageLocalization.format('dxScheduler-newPopupTitle'), readOnly: false, }; @@ -2038,7 +2038,7 @@ class Scheduler extends SchedulerOptionsBaseWidget { const popupConfig = this.editing.legacyForm ? { isToolbarVisible: this.editing.allowUpdating, action: ACTION_TO_APPOINTMENT.UPDATE } : { - onDone: (appointment) => this.updateAppointment(rawAppointment, appointment), + onSave: (appointment) => this.updateAppointment(rawAppointment, appointment), title: messageLocalization.format('dxScheduler-editPopupTitle'), readOnly, };