diff --git a/packages/devextreme/js/__internal/scheduler/__tests__/appointments_new.test.ts b/packages/devextreme/js/__internal/scheduler/__tests__/appointments_new.test.ts new file mode 100644 index 000000000000..3f56d8ea273f --- /dev/null +++ b/packages/devextreme/js/__internal/scheduler/__tests__/appointments_new.test.ts @@ -0,0 +1,328 @@ +import { + afterEach, beforeEach, describe, expect, it, jest, +} from '@jest/globals'; +import type { dxElementWrapper } from '@js/core/renderer'; +import $ from '@js/core/renderer'; +import type { Properties } from '@js/ui/scheduler'; + +import { createScheduler as baseCreateScheduler } from './__mock__/create_scheduler'; +import { setupSchedulerTestEnvironment } from './__mock__/m_mock_scheduler'; +import type { SchedulerModel } from './__mock__/model/scheduler'; + +const createScheduler = (config: Properties) => baseCreateScheduler({ + ...config, + // eslint-disable-next-line @typescript-eslint/naming-convention + _newAppointments: true, +}); + +describe('New Appointments', () => { + beforeEach(() => { + setupSchedulerTestEnvironment(); + }); + + afterEach(() => { + const $scheduler = $('.dx-scheduler'); + // @ts-expect-error + $scheduler.dxScheduler('dispose'); + document.body.innerHTML = ''; + }); + + describe('Templates', () => { + describe.each([ + 'appointmentTemplate', + 'appointmentCollectorTemplate', + ])('%s common', (templateName) => { + const config = { + dataSource: [ + { text: 'Appointment 1', startDate: new Date(2015, 1, 9, 8), endDate: new Date(2015, 1, 9, 9) }, + { text: 'Appointment 2', startDate: new Date(2015, 1, 9, 8), endDate: new Date(2015, 1, 9, 9) }, + { text: 'Appointment 3', startDate: new Date(2015, 1, 9, 8), endDate: new Date(2015, 1, 9, 9) }, + ], + maxAppointmentsPerCell: 2, + currentView: 'day', + currentDate: new Date(2015, 1, 9, 8), + }; + + const appointmentCollectorTemplate = ( + data: any, + container: HTMLElement, + ): dxElementWrapper => $(container).text('Custom collector'); + + const appointmentTemplate = ( + data: any, + index: number, + container: HTMLElement, + ): dxElementWrapper => $(container).text('Custom appointment'); + + const templateFunction = templateName === 'appointmentCollectorTemplate' + ? appointmentCollectorTemplate + : appointmentTemplate; + + const isTemplateApplied = (POM: SchedulerModel): boolean => { + if (templateName === 'appointmentCollectorTemplate') { + return $(POM.getCollectorButton()).text() === 'Custom collector'; + } + + return $(POM.getAppointments()[0].element).text() === 'Custom appointment'; + }; + + it('should apply custom template', async () => { + const { POM } = await createScheduler({ + ...config, + [templateName]: templateFunction, + }); + + expect(isTemplateApplied(POM)).toBe(true); + }); + + it('should apply custom template after .option() change', async () => { + const { POM, scheduler } = await createScheduler(config); + + scheduler.option(templateName, templateFunction); + await new Promise(process.nextTick); + + expect(isTemplateApplied(POM)).toBe(true); + }); + + it('should apply default template if current view does not have it', async () => { + const defaultTemplate = jest.fn(); + + const { POM } = await createScheduler({ + ...config, + views: [{ type: 'day' }], + [templateName]: defaultTemplate, + }); + + expect(defaultTemplate).toHaveBeenCalled(); + expect(isTemplateApplied(POM)).toBe(false); + }); + + it('should apply default template after .option() change with default value', async () => { + const defaultValue = templateName === 'appointmentCollectorTemplate' + ? 'appointmentCollector' + : 'appointment'; + + const { POM, scheduler } = await createScheduler({ + ...config, + [templateName]: templateFunction, + }); + + scheduler.option(templateName, defaultValue); + await new Promise(process.nextTick); + + expect(isTemplateApplied(POM)).toBe(false); + + if (templateName === 'appointmentTemplate') { + const appointment = POM.getAppointments()[0]; + expect(appointment.getText()).toBe('Appointment 1'); + } else { + const collectorButton = POM.getCollectorButton(); + expect(collectorButton.textContent).toBe('1'); + } + }); + + it('should apply current view\'s template', async () => { + const defaultTemplate = jest.fn(); + + const { POM } = await createScheduler({ + ...config, + views: [{ + type: 'day', + [templateName]: templateFunction, + }], + [templateName]: defaultTemplate, + }); + + expect(defaultTemplate).not.toHaveBeenCalled(); + + expect(isTemplateApplied(POM)).toBe(true); + }); + + it('should apply current view\'s template after .option() change', async () => { + const { POM, scheduler } = await createScheduler({ + ...config, + views: [{ + type: 'day', + [templateName]: templateFunction, + }], + }); + + const defaultTemplate = jest.fn(); + + scheduler.option(templateName, defaultTemplate); + await new Promise(process.nextTick); + + expect(defaultTemplate).not.toHaveBeenCalled(); + expect(isTemplateApplied(POM)).toBe(true); + }); + + it('should apply current view\'s template after current view was changed', async () => { + const defaultTemplate = jest.fn(); + + const { POM, scheduler } = await createScheduler({ + ...config, + views: [ + { type: 'workWeek', [templateName]: defaultTemplate }, + { type: 'day', [templateName]: templateFunction }, + ], + currentView: 'workWeek', + }); + + defaultTemplate.mockClear(); + + scheduler.option('currentView', 'day'); + await new Promise(process.nextTick); + + expect(defaultTemplate).not.toHaveBeenCalled(); + expect(isTemplateApplied(POM)).toBe(true); + }); + }); + + describe('AppointmentTemplate', () => { + it('should call template function with correct parameters', async () => { + const appointmentTemplate = jest.fn(); + + const appointmentData1 = { + text: 'Appointment 1', + startDate: new Date(2015, 1, 9, 8), + endDate: new Date(2015, 1, 9, 9), + }; + const appointmentData2 = { + text: 'Appointment 2', + startDate: new Date(2015, 1, 9, 10), + endDate: new Date(2015, 1, 9, 11), + }; + + await createScheduler({ + dataSource: [appointmentData1, appointmentData2], + currentView: 'day', + currentDate: new Date(2015, 1, 9, 8), + appointmentTemplate, + }); + + expect(appointmentTemplate).toHaveBeenCalledTimes(2); + expect(appointmentTemplate.mock.calls[0]).toHaveLength(3); + expect(appointmentTemplate.mock.calls[0]).toEqual([ + expect.objectContaining({ + appointmentData: appointmentData1, + targetedAppointmentData: expect.objectContaining({ + ...appointmentData1, + displayStartDate: appointmentData1.startDate, + displayEndDate: appointmentData1.endDate, + }), + }), + 0, + expect.any(HTMLElement), + ]); + + const container1 = appointmentTemplate.mock.calls[0][2] as HTMLElement; + expect(container1.classList.contains('dx-scheduler-appointment-content')).toBe(true); + expect(container1.innerHTML).toBe(''); + + expect(appointmentTemplate.mock.calls[1]).toHaveLength(3); + expect(appointmentTemplate.mock.calls[1]).toEqual([ + expect.objectContaining({ + appointmentData: appointmentData2, + targetedAppointmentData: expect.objectContaining({ + ...appointmentData2, + displayStartDate: appointmentData2.startDate, + displayEndDate: appointmentData2.endDate, + }), + }), + 1, + expect.any(HTMLElement), + ]); + + const container2 = appointmentTemplate.mock.calls[1][2] as HTMLElement; + expect(container2.classList.contains('dx-scheduler-appointment-content')).toBe(true); + expect(container2.innerHTML).toBe(''); + }); + }); + + describe('AppointmentCollectorTemplate', () => { + it('should call template function with correct parameters', async () => { + const appointmentCollectorTemplate = jest.fn(); + + await createScheduler({ + dataSource: [ + { + text: 'Appointment 1', + startDate: new Date(2015, 1, 9, 8), + endDate: new Date(2015, 1, 9, 9), + }, + { + text: 'Appointment 2', + startDate: new Date(2015, 1, 9, 8), + endDate: new Date(2015, 1, 9, 9), + }, + { + text: 'Appointment 3', + startDate: new Date(2015, 1, 9, 8), + endDate: new Date(2015, 1, 9, 9), + }, + ], + currentView: 'day', + currentDate: new Date(2015, 1, 9, 8), + maxAppointmentsPerCell: 2, + appointmentCollectorTemplate, + }); + + expect(appointmentCollectorTemplate).toHaveBeenCalledTimes(1); + expect(appointmentCollectorTemplate.mock.calls[0]).toHaveLength(2); + expect(appointmentCollectorTemplate.mock.calls[0]).toEqual([ + expect.objectContaining({ + appointmentCount: 1, + isCompact: true, + items: [ + expect.objectContaining({ + text: 'Appointment 3', + }), + ], + }), + expect.any(HTMLElement), + ]); + + const container = appointmentCollectorTemplate.mock.calls[0][1] as HTMLElement; + expect(container.classList.contains('dx-button-content')).toBe(true); + }); + }); + }); + + describe('onAppointmentRendered', () => { + it('should call onAppointmentRendered callback', async () => { + const onAppointmentRendered = jest.fn(); + + await createScheduler({ + dataSource: [{ + text: 'Appointment 1', + startDate: new Date(2015, 1, 9, 8), + endDate: new Date(2015, 1, 9, 9), + }], + currentView: 'day', + currentDate: new Date(2015, 1, 9, 8), + onAppointmentRendered, + }); + + expect(onAppointmentRendered).toHaveBeenCalledTimes(1); + }); + + it('should call onAppointmentRendered after .option() change', async () => { + const { scheduler } = await createScheduler({ + dataSource: [{ + text: 'Appointment 1', + startDate: new Date(2015, 1, 9, 8), + endDate: new Date(2015, 1, 9, 9), + }], + currentView: 'day', + currentDate: new Date(2015, 1, 9, 8), + }); + + const onAppointmentRendered = jest.fn(); + scheduler.option('onAppointmentRendered', onAppointmentRendered); + scheduler.repaint(); + await new Promise(process.nextTick); + + expect(onAppointmentRendered).toHaveBeenCalledTimes(1); + }); + }); +}); diff --git a/packages/devextreme/js/__internal/scheduler/appointments_new/__mock__/appointment_properties.ts b/packages/devextreme/js/__internal/scheduler/appointments_new/__mock__/appointment_properties.ts index b6b9b422e044..1a10a4fc3cd9 100644 --- a/packages/devextreme/js/__internal/scheduler/appointments_new/__mock__/appointment_properties.ts +++ b/packages/devextreme/js/__internal/scheduler/appointments_new/__mock__/appointment_properties.ts @@ -16,6 +16,7 @@ export const getBaseAppointmentProperties = ( }; const config: BaseAppointmentViewProperties = { + index: 0, appointmentData, targetedAppointmentData: normalizedTargetedAppointmentData, appointmentTemplate: new EmptyTemplate(), diff --git a/packages/devextreme/js/__internal/scheduler/appointments_new/appointment/agenda_appointment.ts b/packages/devextreme/js/__internal/scheduler/appointments_new/appointment/agenda_appointment.ts index 0b08a316c87f..ad4b8985b0ea 100644 --- a/packages/devextreme/js/__internal/scheduler/appointments_new/appointment/agenda_appointment.ts +++ b/packages/devextreme/js/__internal/scheduler/appointments_new/appointment/agenda_appointment.ts @@ -50,7 +50,7 @@ export class AgendaAppointmentView extends BaseAppointmentView') - .addClass('dx-scheduler-agenda-appointment-left-layout') + .addClass(AGENDA_APPOINTMENT_CLASSES.LEFT_LAYOUT) .appendTo($container); const $marker = $('
') @@ -74,7 +74,7 @@ export class AgendaAppointmentView extends BaseAppointmentView') - .addClass('dx-scheduler-agenda-appointment-right-layout') + .addClass(AGENDA_APPOINTMENT_CLASSES.RIGHT_LAYOUT) .appendTo($container); $('
') diff --git a/packages/devextreme/js/__internal/scheduler/appointments_new/appointment/base_appointment.ts b/packages/devextreme/js/__internal/scheduler/appointments_new/appointment/base_appointment.ts index 3334c3143058..51cb121cc754 100644 --- a/packages/devextreme/js/__internal/scheduler/appointments_new/appointment/base_appointment.ts +++ b/packages/devextreme/js/__internal/scheduler/appointments_new/appointment/base_appointment.ts @@ -3,7 +3,6 @@ import registerComponent from '@js/core/component_registrator'; import type { DxElement } from '@js/core/element'; import type { dxElementWrapper } from '@js/core/renderer'; import $ from '@js/core/renderer'; -import { when } from '@js/core/utils/deferred'; import { getPublicElement } from '@ts/core/m_element'; import { EmptyTemplate } from '@ts/core/templates/m_empty_template'; import { FunctionTemplate } from '@ts/core/templates/m_function_template'; @@ -19,6 +18,7 @@ import { DateFormatType, getDateTextFromTargetAppointment } from '../utils/get_d export interface BaseAppointmentViewProperties // eslint-disable-next-line @typescript-eslint/no-explicit-any extends DOMComponentProperties> { + index: number; appointmentData: SafeAppointment; targetedAppointmentData: TargetedAppointment; appointmentTemplate: TemplateBase; @@ -120,20 +120,20 @@ export class BaseAppointmentView< ? this.defaultAppointmentTemplate : this.option().appointmentTemplate; - const $renderPromise = template.render({ + template.render({ container: getPublicElement($content), model: { appointmentData: this.appointmentData, targetedAppointmentData: this.targetedAppointmentData, }, - }); - - when($renderPromise).done(() => { - this.option().onAppointmentRendered({ - element: getPublicElement(this.$element()), - appointmentData: this.appointmentData, - targetedAppointmentData: this.targetedAppointmentData, - }); + index: this.option().index, + onRendered: () => { + this.option().onAppointmentRendered({ + element: getPublicElement(this.$element()), + appointmentData: this.appointmentData, + targetedAppointmentData: this.targetedAppointmentData, + }); + }, }); } diff --git a/packages/devextreme/js/__internal/scheduler/appointments_new/appointment_collector.test.ts b/packages/devextreme/js/__internal/scheduler/appointments_new/appointment_collector.test.ts index 7e9cc8afe6da..0df69c54b1ca 100644 --- a/packages/devextreme/js/__internal/scheduler/appointments_new/appointment_collector.test.ts +++ b/packages/devextreme/js/__internal/scheduler/appointments_new/appointment_collector.test.ts @@ -11,16 +11,16 @@ import { AppointmentCollector } from './appointment_collector'; import { APPOINTMENT_COLLECTOR_CLASSES } from './const'; const getProperties = ( - appointmentData: SafeAppointment, + appointmentsData: SafeAppointment[], ): AppointmentCollectorProperties => { const targetedAppointmentData: TargetedAppointment = { - ...appointmentData, - displayStartDate: appointmentData.startDate as Date, - displayEndDate: appointmentData.endDate as Date, + ...appointmentsData[0], + displayStartDate: appointmentsData[0].startDate as Date, + displayEndDate: appointmentsData[0].endDate as Date, }; return { - appointmentsCount: 1, + appointmentsData, isCompact: false, geometry: { height: 30, @@ -70,7 +70,7 @@ describe('AppointmentCollector', () => { describe('Classes', () => { it('should have correct container class', () => { const instance = createAppointmentCollector( - getProperties(defaultAppointmentData), + getProperties([defaultAppointmentData]), ); expect(instance.$element().hasClass('dx-scheduler-appointment-collector')).toBe(true); @@ -79,7 +79,7 @@ describe('AppointmentCollector', () => { it('should have correct content class', () => { const instance = createAppointmentCollector( - getProperties(defaultAppointmentData), + getProperties([defaultAppointmentData]), ); const $buttonContent = instance.$element().find('.dx-button-content'); @@ -90,7 +90,7 @@ describe('AppointmentCollector', () => { true, false, ])('should have correct compact class for isCompact = %o', (isCompact) => { const instance = createAppointmentCollector({ - ...getProperties(defaultAppointmentData), + ...getProperties([defaultAppointmentData]), isCompact, }); @@ -101,11 +101,11 @@ describe('AppointmentCollector', () => { describe('Aria', () => { it('should have correct aria-roledescription when appointment is in the same date', () => { const instance = createAppointmentCollector( - getProperties({ + getProperties([{ text: 'test', startDate: new Date(2024, 0, 1, 9, 0), endDate: new Date(2024, 0, 1, 10, 0), - }), + }]), ); expect(instance.$element().attr('aria-roledescription')).toBe('January 1, 2024'); @@ -113,11 +113,11 @@ describe('AppointmentCollector', () => { it('should have correct aria-roledescription when appointment is in different dates', () => { const instance = createAppointmentCollector( - getProperties({ + getProperties([{ text: 'test', startDate: new Date(2024, 0, 1, 9, 0), endDate: new Date(2024, 0, 2, 10, 0), - }), + }]), ); expect(instance.$element().attr('aria-roledescription')).toBe('January 1, 2024 - January 2, 2024'); @@ -127,7 +127,7 @@ describe('AppointmentCollector', () => { describe('Geometry', () => { it('should have correct top and left on init', () => { const instance = createAppointmentCollector({ - ...getProperties(defaultAppointmentData), + ...getProperties([defaultAppointmentData]), geometry: { top: 100, left: 200, @@ -144,7 +144,7 @@ describe('AppointmentCollector', () => { it('should have correct top and left after geometry is updated and resize is called', () => { const instance = createAppointmentCollector({ - ...getProperties(defaultAppointmentData), + ...getProperties([defaultAppointmentData]), geometry: { top: 100, left: 200, @@ -172,15 +172,26 @@ describe('AppointmentCollector', () => { it.each([ { isCompact: true, expectedText: '1' }, { isCompact: false, expectedText: '1 more' }, - ])('should have correct text for appointmentsCount = 1 and isCompact = %o', ({ isCompact, expectedText }) => { + ])('should have correct text for single appointment and isCompact = %o', ({ isCompact, expectedText }) => { const instance = createAppointmentCollector({ - ...getProperties(defaultAppointmentData), - appointmentsCount: 1, + ...getProperties([defaultAppointmentData]), isCompact, }); const $buttonContent = instance.$element().find('.dx-button-content'); expect($buttonContent.text()).toBe(expectedText); }); + + it('should have correct text for two appointments and isCompact = true', () => { + const instance = createAppointmentCollector({ + ...getProperties([ + { ...defaultAppointmentData }, { ...defaultAppointmentData }, + ]), + isCompact: true, + }); + const $buttonContent = instance.$element().find('.dx-button-content'); + + expect($buttonContent.text()).toBe('2'); + }); }); }); diff --git a/packages/devextreme/js/__internal/scheduler/appointments_new/appointment_collector.ts b/packages/devextreme/js/__internal/scheduler/appointments_new/appointment_collector.ts index 8b5599c965b8..f220a29b2af4 100644 --- a/packages/devextreme/js/__internal/scheduler/appointments_new/appointment_collector.ts +++ b/packages/devextreme/js/__internal/scheduler/appointments_new/appointment_collector.ts @@ -9,13 +9,13 @@ import { FunctionTemplate } from '@ts/core/templates/m_function_template'; import type { TemplateBase } from '@ts/core/templates/m_template_base'; import type { DOMComponentProperties } from '@ts/core/widget/dom_component'; import DOMComponent from '@ts/core/widget/dom_component'; -import type { TargetedAppointment } from '@ts/scheduler/types'; +import type { SafeAppointment, TargetedAppointment } from '@ts/scheduler/types'; import { APPOINTMENT_COLLECTOR_CLASSES } from './const'; export interface AppointmentCollectorProperties extends DOMComponentProperties { - appointmentsCount: number; + appointmentsData: SafeAppointment[]; isCompact: boolean; geometry: { height: number; @@ -33,6 +33,10 @@ export class AppointmentCollector private buttonInstance?: Button; + private get appointmentsCount(): number { + return this.option().appointmentsData.length; + } + override _init(): void { super._init(); @@ -95,14 +99,22 @@ export class AppointmentCollector type: 'default', width: this.option().geometry.width, height: this.option().geometry.height, - template, + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + template: new FunctionTemplate((e) => template.render({ + container: e.container, + model: { + appointmentCount: this.appointmentsCount, + isCompact: this.option().isCompact, + items: this.option().appointmentsData, + }, + })), }); } private defaultAppointmentCollectorContent( $container: dxElementWrapper, ): dxElementWrapper { - const count = this.option().appointmentsCount; + const count = this.appointmentsCount; const text = this.option().isCompact ? count // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/packages/devextreme/js/__internal/scheduler/appointments_new/appointments.test.ts b/packages/devextreme/js/__internal/scheduler/appointments_new/appointments.test.ts index f9da5465b21f..324ee673523c 100644 --- a/packages/devextreme/js/__internal/scheduler/appointments_new/appointments.test.ts +++ b/packages/devextreme/js/__internal/scheduler/appointments_new/appointments.test.ts @@ -191,6 +191,25 @@ describe('Appointments', () => { expect($allDayContainer.find(`.${APPOINTMENT_CLASSES.CONTAINER}`).length).toBe(0); expect(instance.$element().find(`.${APPOINTMENT_CLASSES.CONTAINER}`).length).toBe(1); }); + + it.each([ + 'appointmentTemplate', + 'appointmentCollectorTemplate', + ])('should rerender appointments if %s is changed', (optionName) => { + const instance = createAppointments(getProperties()); + instance.option('viewModel', [ + mockGridViewModel(defaultAppointmentData, { sortedIndex: 0 }), + ]); + + const elementsBefore = instance.$element().find(`.${APPOINTMENT_CLASSES.CONTAINER}`).toArray(); + expect(elementsBefore.length).toBe(1); + + instance.option(optionName, () => {}); + + const elementsAfter = instance.$element().find(`.${APPOINTMENT_CLASSES.CONTAINER}`).toArray(); + expect(elementsAfter.length).toBe(1); + expect(elementsAfter[0]).not.toBe(elementsBefore[0]); + }); }); describe('Partial rendering', () => { diff --git a/packages/devextreme/js/__internal/scheduler/appointments_new/appointments.ts b/packages/devextreme/js/__internal/scheduler/appointments_new/appointments.ts index d63d4aa7dc3b..2d5eb89a7b41 100644 --- a/packages/devextreme/js/__internal/scheduler/appointments_new/appointments.ts +++ b/packages/devextreme/js/__internal/scheduler/appointments_new/appointments.ts @@ -14,7 +14,6 @@ import type { AppointmentResource } from '../utils/resource_manager/appointment_ import type { ResourceManager } from '../utils/resource_manager/resource_manager'; import type { AppointmentDataSource } from '../view_model/m_appointment_data_source'; import type { - AppointmentAgendaViewModel, AppointmentCollectorViewModel, AppointmentItemViewModel, AppointmentViewModelPlain, @@ -93,17 +92,28 @@ export class Appointments extends DOMComponent { - const appointment = this.renderAppointment(commonFragment, appointmentViewModel); + appointments.forEach((appointmentViewModel, index) => { + const container = this.option().currentView === 'agenda' || !appointmentViewModel.allDay + ? commonFragment + : allDayFragment; + + const appointment = this.renderAppointment(container, appointmentViewModel, index); this.appointmentBySortIndex[appointmentViewModel.sortedIndex] = appointment; }); + if (this.$allDayContainer) { + this.$allDayContainer.get(0).appendChild(allDayFragment); + } this.$commonContainer.get(0).appendChild(commonFragment); } @@ -169,7 +186,8 @@ export class Appointments extends DOMComponent { + // TODO: remove passing index to appointmentTemplate, need only to avoid BC + viewModelDiff.forEach((diffItem, index) => { const { allDay, sortedIndex } = diffItem.item; switch (true) { @@ -183,7 +201,7 @@ export class Appointments extends DOMComponent'); @@ -226,7 +245,7 @@ export class Appointments extends DOMComponent item.itemData), isCompact: appointmentViewModel.isCompact, geometry: { height: appointmentViewModel.height, @@ -240,6 +259,7 @@ export class Appointments extends DOMComponent: update appointmentTemplate and appointmentCollectorTemplate + appointmentTemplate: this.getViewOption('appointmentTemplate'), + appointmentCollectorTemplate: this.getViewOption('appointmentCollectorTemplate'), }); } else { this._appointments.option({ @@ -350,7 +351,11 @@ class Scheduler extends SchedulerOptionsBaseWidget { this.postponedOperations.callPostponedOperations(); break; case 'appointmentTemplate': - this._appointments.option('itemTemplate', value); + if (this.option('_newAppointments')) { + this._appointments.option('appointmentTemplate', this.getViewOption('appointmentTemplate')); + } else { + this._appointments.option('itemTemplate', value); + } break; case 'dateCellTemplate': case 'resourceCellTemplate': @@ -520,6 +525,12 @@ class Scheduler extends SchedulerOptionsBaseWidget { this.repaint(); break; case 'appointmentCollectorTemplate': + if (this.option('_newAppointments')) { + this._appointments.option('appointmentCollectorTemplate', this.getViewOption('appointmentCollectorTemplate')); + } else { + this.repaint(); + } + break; case '_appointmentTooltipOffset': this.repaint(); break; @@ -1039,15 +1050,9 @@ class Scheduler extends SchedulerOptionsBaseWidget { this._layoutManager = new AppointmentLayoutManager(this); if (this.option('_newAppointments')) { - // TODO: convert 'item' to 'appointment' for compatibility - const appointmentTemplateValue = this.getViewOption('appointmentTemplate') === 'item' - ? 'appointment' - : this.getViewOption('appointmentTemplate'); - const appointmentsConfig: Partial = { currentView: this.option('currentView') as ViewType, - // TODO: set custom templates - appointmentTemplate: appointmentTemplateValue, + appointmentTemplate: this.getViewOption('appointmentTemplate'), appointmentCollectorTemplate: this.getViewOption('appointmentCollectorTemplate'), onAppointmentRendered: (e) => { // @ts-expect-error 'component' property is set by action