From 06a34bc0f501b31b902f7bf4322d831acbc75b93 Mon Sep 17 00:00:00 2001 From: Aleksey Semikozov Date: Sat, 14 Mar 2026 21:23:20 -0300 Subject: [PATCH 1/6] Scheduler - Fix appointment form state between popup openings --- .../Angular/app/app.component.ts | 1 - .../ResolveTimeConflicts/React/App.tsx | 1 - .../ResolveTimeConflicts/Vue/App.vue | 1 - .../ResolveTimeConflicts/jQuery/index.js | 1 - .../appointment_popup.test.ts | 14 ++++++++++++ .../scheduler/appointment_popup/m_popup.ts | 22 +++++++++++++------ 6 files changed, 29 insertions(+), 11 deletions(-) diff --git a/apps/demos/Demos/Scheduler/ResolveTimeConflicts/Angular/app/app.component.ts b/apps/demos/Demos/Scheduler/ResolveTimeConflicts/Angular/app/app.component.ts index 5004c5952cd9..280f6a53cbd8 100644 --- a/apps/demos/Demos/Scheduler/ResolveTimeConflicts/Angular/app/app.component.ts +++ b/apps/demos/Demos/Scheduler/ResolveTimeConflicts/Angular/app/app.component.ts @@ -62,7 +62,6 @@ export class AppComponent { onInitialized: (e: DxPopupTypes.InitializedEvent) => { this.popup = e.component; }, onHidden: () => { this.setConflictError(false); - this.form?.updateData('assigneeId', []); }, }; diff --git a/apps/demos/Demos/Scheduler/ResolveTimeConflicts/React/App.tsx b/apps/demos/Demos/Scheduler/ResolveTimeConflicts/React/App.tsx index 34cd5504e91d..492e0e8b7002 100644 --- a/apps/demos/Demos/Scheduler/ResolveTimeConflicts/React/App.tsx +++ b/apps/demos/Demos/Scheduler/ResolveTimeConflicts/React/App.tsx @@ -157,7 +157,6 @@ const App = () => { }, onHidden: () => { setConflictError(false); - formRef.current?.updateData('assigneeId', []); }, }), [setConflictError]); diff --git a/apps/demos/Demos/Scheduler/ResolveTimeConflicts/Vue/App.vue b/apps/demos/Demos/Scheduler/ResolveTimeConflicts/Vue/App.vue index 0fae9e57c385..9a00cfa5be06 100644 --- a/apps/demos/Demos/Scheduler/ResolveTimeConflicts/Vue/App.vue +++ b/apps/demos/Demos/Scheduler/ResolveTimeConflicts/Vue/App.vue @@ -186,7 +186,6 @@ const popupOptions = { }, onHidden: () => { setConflictError(false); - form?.updateData('assigneeId', []); }, }; diff --git a/apps/demos/Demos/Scheduler/ResolveTimeConflicts/jQuery/index.js b/apps/demos/Demos/Scheduler/ResolveTimeConflicts/jQuery/index.js index 490a9224eb90..7b07dd1a5189 100644 --- a/apps/demos/Demos/Scheduler/ResolveTimeConflicts/jQuery/index.js +++ b/apps/demos/Demos/Scheduler/ResolveTimeConflicts/jQuery/index.js @@ -29,7 +29,6 @@ $(() => { }, onHidden: () => { setConflictError(false); - form?.updateData('assigneeId', []); }, }, form: { 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 b56f913186d5..a8362c1f08a5 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 @@ -441,6 +441,20 @@ describe('Appointment Form', () => { expect(POM.popup.getInputValue('roomId')).toBe(''); }); + it('should create a new form instance on each popup opening', async () => { + const { scheduler, POM } = await createScheduler(getDefaultConfig()); + + scheduler.showAppointmentPopup(commonAppointment); + const firstFormInstance = POM.popup.dxForm; + + POM.popup.cancelButton.click(); + + scheduler.showAppointmentPopup(commonAppointment); + const secondFormInstance = POM.popup.dxForm; + + expect(secondFormInstance).not.toBe(firstFormInstance); + }); + it('should have correct repeat editor value when opening recurring appointment after common appointment', async () => { const { scheduler, POM } = await createScheduler(getDefaultConfig()); 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 c942018fc3af..12ea126f4f29 100644 --- a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_popup.ts +++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/m_popup.ts @@ -69,10 +69,10 @@ export class AppointmentPopup { this.state.allowSaving = config.allowSaving; this.state.excludeInfo = config.excludeInfo; - if (!this._popup) { - const popupConfig = this._createPopupConfig(); - this._createPopup(popupConfig); - } + this._disposePopup(); + + const popupConfig = this._createPopupConfig(); + this._createPopup(popupConfig); this._popup!.show(); } @@ -82,9 +82,17 @@ export class AppointmentPopup { } dispose() { - this.form.dispose(); - this._popup?.dispose(); - this._popup = undefined; + this._disposePopup(); + } + + private _disposePopup(): void { + if (this._popup) { + const $element = this._popup.$element(); + this.form.dispose(); + this._popup.dispose(); + $element.remove(); + this._popup = undefined; + } } _createPopup(options): void { From 22458633ffbbf2b5a768e3554a9992fd1d5171f2 Mon Sep 17 00:00:00 2001 From: Aleksey Semikozov Date: Sun, 15 Mar 2026 08:41:38 -0300 Subject: [PATCH 2/6] Scheduler - Remove assigneeId workaround from ReactJs demo --- apps/demos/Demos/Scheduler/ResolveTimeConflicts/ReactJs/App.js | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/demos/Demos/Scheduler/ResolveTimeConflicts/ReactJs/App.js b/apps/demos/Demos/Scheduler/ResolveTimeConflicts/ReactJs/App.js index 03e6b5304159..0d59b060af7e 100644 --- a/apps/demos/Demos/Scheduler/ResolveTimeConflicts/ReactJs/App.js +++ b/apps/demos/Demos/Scheduler/ResolveTimeConflicts/ReactJs/App.js @@ -130,7 +130,6 @@ const App = () => { }, onHidden: () => { setConflictError(false); - formRef.current?.updateData('assigneeId', []); }, }), [setConflictError], From e475ef2167b35b4064968be379e2784702d5c409 Mon Sep 17 00:00:00 2001 From: Aleksey Semikozov Date: Tue, 17 Mar 2026 12:51:36 -0300 Subject: [PATCH 3/6] Scheduler - Remove obsolete state isolation tests (T832711) --- .../appointment_popup.test.ts | 30 ------------------- 1 file changed, 30 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 a8362c1f08a5..5d7e4267b69c 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 @@ -953,36 +953,6 @@ describe('Appointment Form', () => { expect(POM.popup.getInputValue('endDateEditor')).toBe('5/1/2017'); expect(POM.popup.isInputVisible('endTimeEditor')).toBeFalsy(); }); - - it('should show correct dates after switching off allDay and canceling changes (T832711)', async () => { - const { scheduler, POM } = await createScheduler(undefined); - - scheduler.showAppointmentPopup(allDayAppointment); - POM.popup.getInput('allDayEditor').click(); - POM.popup.cancelButton.click(); - - scheduler.showAppointmentPopup(allDayAppointment); - - expect(POM.popup.getInputValue('startDateEditor')).toBe('5/1/2017'); - expect(POM.popup.isInputVisible('startTimeEditor')).toBeFalsy(); - expect(POM.popup.getInputValue('endDateEditor')).toBe('5/1/2017'); - expect(POM.popup.isInputVisible('endTimeEditor')).toBeFalsy(); - }); - - it('should show correct dates after switching on allDay and canceling changes (T832711)', async () => { - const { scheduler, POM } = await createScheduler(getDefaultConfig()); - - scheduler.showAppointmentPopup(commonAppointment); - POM.popup.getInput('allDayEditor').click(); - POM.popup.cancelButton.click(); - - scheduler.showAppointmentPopup(commonAppointment); - - expect(POM.popup.getInputValue('startDateEditor')).toBe('5/9/2017'); - expect(POM.popup.getInputValue('startTimeEditor')).toBe('9:30 AM'); - expect(POM.popup.getInputValue('endDateEditor')).toBe('5/9/2017'); - expect(POM.popup.getInputValue('endTimeEditor')).toBe('11:00 AM'); - }); }); describe('Timezone Editors', () => { From f44237baf022fd888e6867ed8c98570a90e6484c Mon Sep 17 00:00:00 2001 From: Aleksey Semikozov Date: Tue, 17 Mar 2026 15:33:17 -0300 Subject: [PATCH 4/6] Scheduler - Remove redundant state isolation tests --- .../appointment_popup.test.ts | 97 +++++-------------- .../scheduler/appointment_popup/m_form.ts | 2 + 2 files changed, 24 insertions(+), 75 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 5d7e4267b69c..1b7f874c1212 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 @@ -377,70 +377,6 @@ describe('Appointment Form', () => { }); describe('State', () => { - it('should have empty description, subject and timezone inputs when opening second common appointment', async () => { - const { scheduler, POM } = await createScheduler({ - ...getDefaultConfig(), - editing: { - allowUpdating: true, - allowTimeZoneEditing: true, - }, - }); - - scheduler.showAppointmentPopup({ ...commonAppointment }); - - POM.popup.setInputValue('descriptionEditor', 'temp'); - POM.popup.setInputValue('startDateTimeZoneEditor', 'America/Los_Angeles'); - POM.popup.setInputValue('endDateTimeZoneEditor', 'America/Anchorage'); - POM.popup.saveButton.click(); - - scheduler.showAppointmentPopup(); - - expect(POM.popup.getInputValue('subjectEditor')).toBe(''); - expect(POM.popup.getInputValue('descriptionEditor')).toBe(''); - expect(POM.popup.getInputValue('startDateTimeZoneEditor')).toBe(''); - expect(POM.popup.getInputValue('endDateTimeZoneEditor')).toBe(''); - }); - - it('should have correct form data when opening second appointment', async () => { - const { scheduler, POM } = await createScheduler(getDefaultConfig()); - - scheduler.showAppointmentPopup(commonAppointment); - - expect(POM.popup.dxForm.option('formData')).toMatchObject({ ...commonAppointment }); - - POM.popup.cancelButton.click(); - - scheduler.showAppointmentPopup(allDayAppointment); - - expect(POM.popup.dxForm.option('formData')).toMatchObject({ ...allDayAppointment }); - }); - - it('should have empty resource editor value when opening second appointment', async () => { - const { scheduler, POM } = await createScheduler({ - ...getDefaultConfig(), - resources: [{ - fieldExpr: 'roomId', - dataSource: [ - { text: 'Room 1', id: 1, color: '#00af2c' }, - { text: 'Room 2', id: 2, color: '#56ca85' }, - { text: 'Room 3', id: 3, color: '#8ecd3c' }, - ], - }], - }); - - scheduler.showAppointmentPopup({ - text: 'Resource test app', - startDate: new Date(2017, 4, 9, 9, 30), - endDate: new Date(2017, 4, 9, 11), - roomId: 1, - }); - POM.popup.setInputValue('roomId', 2); - scheduler.hideAppointmentPopup(true); - - scheduler.showAppointmentPopup(); - expect(POM.popup.getInputValue('roomId')).toBe(''); - }); - it('should create a new form instance on each popup opening', async () => { const { scheduler, POM } = await createScheduler(getDefaultConfig()); @@ -455,17 +391,6 @@ describe('Appointment Form', () => { expect(secondFormInstance).not.toBe(firstFormInstance); }); - it('should have correct repeat editor value when opening recurring appointment after common appointment', async () => { - const { scheduler, POM } = await createScheduler(getDefaultConfig()); - - scheduler.showAppointmentPopup({ ...commonAppointment }); - POM.popup.saveButton.click(); - - scheduler.showAppointmentPopup({ ...recurringAppointment }); - POM.popup.editSeriesButton.click(); - expect(POM.popup.getInputValue('repeatEditor')).toBe('Daily'); - }); - it('should have correct editor values when opening for empty date cell - 1', async () => { const { POM } = await createScheduler({ ...getDefaultConfig(), @@ -1277,6 +1202,28 @@ describe('Appointment Form', () => { expect(POM.popup.isRecurrenceGroupVisible()).toBe(true); }); + it('should close repeat selectbox popup when navigating to recurrence group via settings button', async () => { + const { POM, scheduler } = await createScheduler({ + ...getDefaultConfig(), + dataSource: [{ ...recurringAppointment }], + }); + + const dataSource = (scheduler as any).getDataSource(); + const appointment = dataSource.items()[0]; + + scheduler.showAppointmentPopup(appointment); + POM.popup.editSeriesButton.click(); + + const repeatEditor = POM.popup.dxForm.getEditor('repeatEditor'); + POM.popup.getInput('repeatEditor').click(); + + expect(repeatEditor?.option('opened')).toBe(true); + + POM.popup.recurrenceSettingsButton.click(); + + expect(repeatEditor?.option('opened')).toBe(false); + }); + it('should have disabled week day buttons when allowUpdating is false', async () => { const { POM, scheduler } = await createScheduler({ ...getDefaultConfig(), 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 dc44f0c7be9a..f86351fbfd26 100644 --- a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_form.ts +++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/m_form.ts @@ -920,6 +920,8 @@ export class AppointmentForm { } showRecurrenceGroup(): void { + this.dxForm.getEditor(REPEAT_EDITOR_NAME)?.close?.(); + this._popup.updateToolbarForRecurrenceGroup(); const currentHeight = this.dxPopup.option('height') as string | number | undefined; From 5741a540875562f71b42db0a7cec6c8185f4ed5f Mon Sep 17 00:00:00 2001 From: Aleksey Semikozov Date: Wed, 18 Mar 2026 05:01:31 -0300 Subject: [PATCH 5/6] Scheduler - Remove weekly recurrence state isolation test --- .../appointment_popup.test.ts | 39 ------------------- 1 file changed, 39 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 1b7f874c1212..36fb06dfb3d4 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 @@ -1458,45 +1458,6 @@ describe('Appointment Form', () => { expect(POM.popup.getInputValue('recurrenceRepeatEndEditor')).toBe('count'); expect(POM.popup.getInputValue('recurrenceEndCountEditor')).toBe('10 occurrence(s)'); }); - - it('should have correct input values when opening second weekly recurring appointment', async () => { - const { scheduler, POM } = await createScheduler(getDefaultConfig()); - - const appointment1 = { - text: 'Meeting', - startDate: new Date(2017, 4, 1, 10, 30), - endDate: new Date(2017, 4, 1, 11), - recurrenceRule: 'FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,WE,FR;COUNT=5', - repeatEnd: 'count', - }; - const appointment2 = { - text: 'Meeting', - startDate: new Date(2017, 4, 2, 10, 30), - endDate: new Date(2017, 4, 2, 11), - recurrenceRule: 'FREQ=WEEKLY;INTERVAL=1;BYDAY=TU,TH;COUNT=5', - repeatEnd: 'count', - }; - - scheduler.showAppointmentPopup(appointment1); - POM.popup.editSeriesButton.click(); - POM.popup.recurrenceSettingsButton.click(); - - scheduler.hideAppointmentPopup(); - - scheduler.showAppointmentPopup(appointment2); - POM.popup.editSeriesButton.click(); - POM.popup.recurrenceSettingsButton.click(); - - expect(POM.popup.getInputValue('repeatEditor')).toBe('Weekly'); - expect(POM.popup.getInputValue('recurrenceStartDateEditor')).toBe('5/2/2017'); - expect(POM.popup.getInputValue('recurrenceCountEditor')).toBe('1'); - expect(POM.popup.getInputValue('recurrencePeriodEditor')).toBe('Week(s)'); - - const expectedWeekDaysSelection = [false, true, false, true, false, false, false]; - expect(POM.popup.getWeekDaysSelection()).toEqual(expectedWeekDaysSelection); - - expect(POM.popup.getInputValue('recurrenceEndCountEditor')).toBe('5 occurrence(s)'); - }); }); describe('Repeat End Values Preservation', () => { From 809e99da1cdc82f96bbb91b0f632f69b9dc8b8ed Mon Sep 17 00:00:00 2001 From: Aleksey Semikozov Date: Wed, 18 Mar 2026 05:04:59 -0300 Subject: [PATCH 6/6] Scheduler - Revert unrelated selectbox close fix from state isolation commit --- .../appointment_popup.test.ts | 22 ------------------- .../scheduler/appointment_popup/m_form.ts | 2 -- 2 files changed, 24 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 36fb06dfb3d4..9f686ef00dc2 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 @@ -1202,28 +1202,6 @@ describe('Appointment Form', () => { expect(POM.popup.isRecurrenceGroupVisible()).toBe(true); }); - it('should close repeat selectbox popup when navigating to recurrence group via settings button', async () => { - const { POM, scheduler } = await createScheduler({ - ...getDefaultConfig(), - dataSource: [{ ...recurringAppointment }], - }); - - const dataSource = (scheduler as any).getDataSource(); - const appointment = dataSource.items()[0]; - - scheduler.showAppointmentPopup(appointment); - POM.popup.editSeriesButton.click(); - - const repeatEditor = POM.popup.dxForm.getEditor('repeatEditor'); - POM.popup.getInput('repeatEditor').click(); - - expect(repeatEditor?.option('opened')).toBe(true); - - POM.popup.recurrenceSettingsButton.click(); - - expect(repeatEditor?.option('opened')).toBe(false); - }); - it('should have disabled week day buttons when allowUpdating is false', async () => { const { POM, scheduler } = await createScheduler({ ...getDefaultConfig(), 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 f86351fbfd26..dc44f0c7be9a 100644 --- a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_form.ts +++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/m_form.ts @@ -920,8 +920,6 @@ export class AppointmentForm { } showRecurrenceGroup(): void { - this.dxForm.getEditor(REPEAT_EDITOR_NAME)?.close?.(); - this._popup.updateToolbarForRecurrenceGroup(); const currentHeight = this.dxPopup.option('height') as string | number | undefined;