diff --git a/packages/devextreme/js/__internal/scheduler/__tests__/appointments_new.test.ts b/packages/devextreme/js/__internal/scheduler/__tests__/appointments_new.test.ts
index 426af8afe22f..65445cb0fbf8 100644
--- a/packages/devextreme/js/__internal/scheduler/__tests__/appointments_new.test.ts
+++ b/packages/devextreme/js/__internal/scheduler/__tests__/appointments_new.test.ts
@@ -4,6 +4,7 @@ import {
import type { dxElementWrapper } from '@js/core/renderer';
import $ from '@js/core/renderer';
import type { Properties } from '@js/ui/scheduler';
+import { fireEvent } from '@testing-library/dom';
import { createScheduler as baseCreateScheduler } from './__mock__/create_scheduler';
import { setupSchedulerTestEnvironment } from './__mock__/m_mock_scheduler';
@@ -375,4 +376,84 @@ describe('New Appointments', () => {
expect(onAppointmentRendered).toHaveBeenCalledTimes(1);
});
});
+
+ describe('Keyboard navigation', () => {
+ it('should delete appointment by delete key', async () => {
+ const { POM } = await createScheduler({
+ dataSource: [{
+ startDate: new Date(2015, 1, 9, 8),
+ endDate: new Date(2015, 1, 9, 9),
+ }],
+ currentDate: new Date(2015, 1, 9, 8),
+ });
+
+ const appointment = POM.getAppointments()[0];
+ appointment.element.focus();
+ fireEvent.keyDown(appointment.element, { key: 'Delete' });
+
+ expect(POM.getAppointments().length).toBe(0);
+ });
+
+ it('should delete recurring appointment occurrence by delete key', async () => {
+ const { POM } = await createScheduler({
+ dataSource: [{
+ startDate: new Date(2015, 1, 9, 8),
+ endDate: new Date(2015, 1, 9, 9),
+ recurrenceRule: 'FREQ=DAILY;COUNT=3',
+ }],
+ currentDate: new Date(2015, 1, 9),
+ currentView: 'week',
+ recurrenceEditMode: 'occurrence',
+ });
+
+ expect(POM.getAppointments().length).toBe(3);
+
+ const appointment = POM.getAppointments()[0];
+ appointment.element.focus();
+ fireEvent.keyDown(appointment.element, { key: 'Delete' });
+
+ expect(POM.getAppointments().length).toBe(2);
+ });
+
+ it.each([
+ { editing: true },
+ { editing: { allowDeleting: true } },
+ { editing: { allowDeleting: true, allowUpdating: false } },
+ ])('should delete appointment when editing=$editing', async ({ editing }) => {
+ const { POM } = await createScheduler({
+ dataSource: [{
+ startDate: new Date(2015, 1, 9, 8),
+ endDate: new Date(2015, 1, 9, 9),
+ }],
+ currentDate: new Date(2015, 1, 9, 8),
+ editing,
+ });
+
+ const appointment = POM.getAppointments()[0];
+ appointment.element.focus();
+ fireEvent.keyDown(appointment.element, { key: 'Delete' });
+
+ expect(POM.getAppointments().length).toBe(0);
+ });
+
+ it.each([
+ { editing: { allowDeleting: false } },
+ { editing: false },
+ ])('should NOT delete appointment when editing=$editing', async ({ editing }) => {
+ const { POM } = await createScheduler({
+ dataSource: [{
+ startDate: new Date(2015, 1, 9, 8),
+ endDate: new Date(2015, 1, 9, 9),
+ }],
+ currentDate: new Date(2015, 1, 9, 8),
+ editing,
+ });
+
+ const appointment = POM.getAppointments()[0];
+ appointment.element.focus();
+ fireEvent.keyDown(appointment.element, { key: 'Delete' });
+
+ expect(POM.getAppointments().length).toBe(1);
+ });
+ });
});
diff --git a/packages/devextreme/js/__internal/scheduler/appointments_new/appointments.focus_controller.ts b/packages/devextreme/js/__internal/scheduler/appointments_new/appointments.focus_controller.ts
index fafa90451a22..5ba830187abb 100644
--- a/packages/devextreme/js/__internal/scheduler/appointments_new/appointments.focus_controller.ts
+++ b/packages/devextreme/js/__internal/scheduler/appointments_new/appointments.focus_controller.ts
@@ -5,6 +5,7 @@ import { focus } from '@ts/events/m_short';
import { getRawAppointmentGroupValues } from '../utils/resource_manager/appointment_groups_utils';
import type { SortedEntity } from '../view_model/types';
+import { AppointmentCollector } from './appointment_collector';
import type { Appointments } from './appointments';
import type { ViewItem } from './view_item';
@@ -14,15 +15,15 @@ export class AppointmentsFocusController {
private needRestoreFocusIndex = -1;
private get sortedAppointments(): SortedEntity[] {
- return this.appointments.option().getSortedAppointments();
+ return (this.appointments.option()).getSortedAppointments();
}
private get isVirtualScrolling(): boolean {
- return this.appointments.option().isVirtualScrolling();
+ return (this.appointments.option()).isVirtualScrolling();
}
private get tabIndex(): number | undefined {
- return this.appointments.option().tabIndex;
+ return (this.appointments.option()).tabIndex;
}
constructor(private readonly appointments: Appointments) { }
@@ -50,8 +51,28 @@ export class AppointmentsFocusController {
}
public onViewItemKeyDown(viewItem: ViewItem, e: KeyboardKeyDownEvent): void {
- if (e.key === 'Tab') {
- this.handleTabKeyDown(e, viewItem.option().sortedIndex);
+ switch (true) {
+ case e.key === 'Tab':
+ this.handleTabKeyDown(e, viewItem.option().sortedIndex);
+ break;
+ case e.key === 'Delete':
+ if (viewItem instanceof AppointmentCollector) { break; }
+ this.handleDeleteKeyDown(viewItem.option().sortedIndex, e);
+ break;
+ case e.key === 'Home':
+ this.handleHomeKeyDown(e);
+ break;
+ case e.key === 'End':
+ this.handleEndKeyDown(e);
+ break;
+ case e.key === 'Enter':
+ this.handleEnterKeyDown(viewItem.option().sortedIndex);
+ break;
+ case e.key === ' ':
+ this.handleEnterKeyDown(viewItem.option().sortedIndex);
+ break;
+ default:
+ break;
}
}
@@ -92,6 +113,41 @@ export class AppointmentsFocusController {
this.focusByItemData(nextItemData);
}
+ private handleDeleteKeyDown(sortedIndex: number, e: KeyboardKeyDownEvent): void {
+ const { editing, onDeleteKeyPress, getDataAccessor } = this.appointments.option();
+ if (!editing.allowDeleting) { return; }
+
+ const sortedItem = this.sortedAppointments[sortedIndex];
+ if (!sortedItem) { return; }
+
+ e.originalEvent.preventDefault();
+ const occurrence = { ...sortedItem.itemData };
+ getDataAccessor().set('startDate', occurrence, new Date(sortedItem.source.startDate));
+
+ onDeleteKeyPress({ appointment: sortedItem.itemData, occurrence });
+ }
+
+ private handleHomeKeyDown(e: KeyboardKeyDownEvent): void {
+ const firstAppointment = this.sortedAppointments[0];
+ if (!firstAppointment) { return; }
+ e.originalEvent.preventDefault();
+ this.focusByItemData(firstAppointment);
+ }
+
+ private handleEndKeyDown(e: KeyboardKeyDownEvent): void {
+ const lastAppointment = this.sortedAppointments[this.sortedAppointments.length - 1];
+ if (!lastAppointment) { return; }
+ e.originalEvent.preventDefault();
+ this.focusByItemData(lastAppointment);
+ }
+
+ private handleEnterKeyDown(sortedIndex: number): void {
+ const { onItemActivate } = this.appointments.option();
+ const sortedItem = this.sortedAppointments[sortedIndex];
+ if (!sortedItem) { return; }
+ onItemActivate({ data: sortedItem.itemData, target: null });
+ }
+
private focusByItemData(itemData: SortedEntity): void {
if (this.isVirtualScrolling) {
this.scrollToItem(itemData);
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 d7686404f264..99bd66a52b45 100644
--- a/packages/devextreme/js/__internal/scheduler/appointments_new/appointments.test.ts
+++ b/packages/devextreme/js/__internal/scheduler/appointments_new/appointments.test.ts
@@ -2,8 +2,8 @@ import {
afterEach, beforeEach, describe, expect, it, jest,
} from '@jest/globals';
import $ from '@js/core/renderer';
+import { fireEvent } from '@testing-library/dom';
-import fx from '../../../common/core/animation/fx';
import { mockAppointmentDataAccessor } from '../__mock__/appointment_data_accessor.mock';
import { getResourceManagerMock } from '../__mock__/resource_manager.mock';
import type { ResourceConfig } from '../utils/loader/types';
@@ -48,6 +48,10 @@ const getProperties = (options: {
getAppointmentDataSource: mockAppointmentDataSource,
getResourceManager: () => getResourceManagerMock(options.resources ?? []),
getDataAccessor: () => mockAppointmentDataAccessor,
+
+ editing: { allowDeleting: false },
+ onDeleteKeyPress: (): void => {},
+ onItemActivate: (): void => {},
});
const createAppointments = (
@@ -67,8 +71,6 @@ const defaultAppointmentData = {
describe('Appointments', () => {
beforeEach(() => {
- fx.off = true;
-
const $container = $('
')
.addClass('container')
.appendTo(document.body);
@@ -84,8 +86,6 @@ describe('Appointments', () => {
afterEach(() => {
$('.container').remove();
- fx.off = false;
- jest.useRealTimers();
});
describe('Classes', () => {
@@ -548,9 +548,7 @@ describe('Appointments', () => {
const viewItem1 = instance.getViewItemBySortedIndex(1);
(viewItem0?.$element().get(0) as HTMLElement).click();
- viewItem0?.$element().get(0).dispatchEvent(
- new KeyboardEvent('keydown', { key: 'Tab', bubbles: true }),
- );
+ fireEvent.keyDown(viewItem0?.$element().get(0) as HTMLElement, { key: 'Tab' });
expect(viewItem0?.$element().attr('tabindex')).toBe('-1');
expect(viewItem1?.$element().attr('tabindex')).toBe('0');
@@ -574,9 +572,7 @@ describe('Appointments', () => {
const viewItem1 = instance.getViewItemBySortedIndex(1);
(viewItem1?.$element().get(0) as HTMLElement).click();
- viewItem1?.$element().get(0).dispatchEvent(
- new KeyboardEvent('keydown', { key: 'Tab', shiftKey: true, bubbles: true }),
- );
+ fireEvent.keyDown(viewItem1?.$element().get(0) as HTMLElement, { key: 'Tab', shiftKey: true });
expect(viewItem0?.$element().attr('tabindex')).toBe('0');
expect(viewItem1?.$element().attr('tabindex')).toBe('-1');
@@ -648,9 +644,7 @@ describe('Appointments', () => {
const viewItem1 = instance.getViewItemBySortedIndex(0);
(viewItem1?.$element().get(0) as HTMLElement).click();
- viewItem1?.$element().get(0).dispatchEvent(
- new KeyboardEvent('keydown', { key: 'Tab', bubbles: true }),
- );
+ fireEvent.keyDown(viewItem1?.$element().get(0) as HTMLElement, { key: 'Tab' });
expect(scrollTo).toHaveBeenCalled();
});
@@ -673,9 +667,7 @@ describe('Appointments', () => {
const viewItem2 = instance.getViewItemBySortedIndex(2);
(viewItem1?.$element().get(0) as HTMLElement).click();
- viewItem1?.$element().get(0).dispatchEvent(
- new KeyboardEvent('keydown', { key: 'Tab', bubbles: true }),
- );
+ fireEvent.keyDown(viewItem1?.$element().get(0) as HTMLElement, { key: 'Tab' });
expect(document.activeElement).toBe(viewItem2?.$element().get(0));
});
@@ -698,9 +690,7 @@ describe('Appointments', () => {
const viewItem1 = instance.getViewItemBySortedIndex(1);
(viewItem1?.$element().get(0) as HTMLElement).click();
- viewItem1?.$element().get(0).dispatchEvent(
- new KeyboardEvent('keydown', { key: 'Tab', bubbles: true }),
- );
+ fireEvent.keyDown(viewItem1?.$element().get(0) as HTMLElement, { key: 'Tab' });
// item2 is not rendered yet, so focus cannot move yet
expect(instance.getViewItemBySortedIndex(2)).toBeUndefined();
@@ -734,9 +724,7 @@ describe('Appointments', () => {
const viewItem0 = instance.getViewItemBySortedIndex(0);
(viewItem0?.$element().get(0) as HTMLElement).click();
- viewItem0?.$element().get(0).dispatchEvent(
- new KeyboardEvent('keydown', { key: 'Tab', bubbles: true }),
- );
+ fireEvent.keyDown(viewItem0?.$element().get(0) as HTMLElement, { key: 'Tab' });
expect(scrollTo).toHaveBeenCalledWith(appointmentStartDate, expect.anything());
});
@@ -760,9 +748,7 @@ describe('Appointments', () => {
const viewItem0 = instance.getViewItemBySortedIndex(0);
(viewItem0?.$element().get(0) as HTMLElement).click();
- viewItem0?.$element().get(0).dispatchEvent(
- new KeyboardEvent('keydown', { key: 'Tab', bubbles: true }),
- );
+ fireEvent.keyDown(viewItem0?.$element().get(0) as HTMLElement, { key: 'Tab' });
expect(scrollTo).toHaveBeenCalledWith(startViewDate, expect.anything());
});
@@ -771,9 +757,7 @@ describe('Appointments', () => {
describe('Navigation after partial render', () => {
const pressTab = (): void => {
const activeElement = document.activeElement as HTMLElement;
- activeElement.dispatchEvent(
- new KeyboardEvent('keydown', { key: 'Tab', bubbles: true }),
- );
+ fireEvent.keyDown(activeElement, { key: 'Tab' });
};
it('should navigate to the last appointment correctly after an appointment is added', () => {
@@ -878,6 +862,193 @@ describe('Appointments', () => {
expect(lastViewItem?.$element().attr('tabindex')).toBe('0');
});
});
+
+ describe('Home/End navigation', () => {
+ it('should move focus to first appointment on Home key', () => {
+ const viewModel = [
+ mockGridViewModel({ ...defaultAppointmentData }, { sortedIndex: 0 }),
+ mockGridViewModel({ ...defaultAppointmentData }, { sortedIndex: 1 }),
+ mockGridViewModel({ ...defaultAppointmentData }, { sortedIndex: 2 }),
+ ];
+
+ const instance = createAppointments({
+ ...getProperties(),
+ getSortedAppointments: () => viewModel as unknown as SortedEntity[],
+ });
+ instance.option('viewModel', viewModel);
+
+ const viewItem0 = instance.getViewItemBySortedIndex(0);
+ const viewItem2 = instance.getViewItemBySortedIndex(2);
+
+ (viewItem2?.$element().get(0) as HTMLElement).click();
+ fireEvent.keyDown(viewItem2?.$element().get(0) as HTMLElement, { key: 'Home' });
+
+ expect(viewItem0?.$element().attr('tabindex')).toBe('0');
+ expect(viewItem2?.$element().attr('tabindex')).toBe('-1');
+ expect(document.activeElement).toBe(viewItem0?.$element().get(0));
+ });
+
+ it('should move focus to last appointment on End key', () => {
+ const viewModel = [
+ mockGridViewModel({ ...defaultAppointmentData }, { sortedIndex: 0 }),
+ mockGridViewModel({ ...defaultAppointmentData }, { sortedIndex: 1 }),
+ mockGridViewModel({ ...defaultAppointmentData }, { sortedIndex: 2 }),
+ ];
+
+ const instance = createAppointments({
+ ...getProperties(),
+ getSortedAppointments: () => viewModel as unknown as SortedEntity[],
+ });
+ instance.option('viewModel', viewModel);
+
+ const viewItem0 = instance.getViewItemBySortedIndex(0);
+ const viewItem2 = instance.getViewItemBySortedIndex(2);
+
+ (viewItem0?.$element().get(0) as HTMLElement).click();
+ fireEvent.keyDown(viewItem0?.$element().get(0) as HTMLElement, { key: 'End' });
+
+ expect(viewItem2?.$element().attr('tabindex')).toBe('0');
+ expect(viewItem0?.$element().attr('tabindex')).toBe('-1');
+ expect(document.activeElement).toBe(viewItem2?.$element().get(0));
+ });
+ it('should call preventDefault on Home key', () => {
+ const viewModel = [
+ mockGridViewModel({ ...defaultAppointmentData }, { sortedIndex: 0 }),
+ mockGridViewModel({ ...defaultAppointmentData }, { sortedIndex: 1 }),
+ ];
+
+ const instance = createAppointments({
+ ...getProperties(),
+ getSortedAppointments: () => viewModel as unknown as SortedEntity[],
+ });
+ instance.option('viewModel', viewModel);
+
+ const viewItem1 = instance.getViewItemBySortedIndex(1);
+ (viewItem1?.$element().get(0) as HTMLElement).click();
+
+ const event = new KeyboardEvent('keydown', { key: 'Home', bubbles: true, cancelable: true });
+ viewItem1?.$element().get(0)?.dispatchEvent(event);
+
+ expect(event.defaultPrevented).toBe(true);
+ });
+
+ it('should call preventDefault on End key', () => {
+ const viewModel = [
+ mockGridViewModel({ ...defaultAppointmentData }, { sortedIndex: 0 }),
+ mockGridViewModel({ ...defaultAppointmentData }, { sortedIndex: 1 }),
+ ];
+
+ const instance = createAppointments({
+ ...getProperties(),
+ getSortedAppointments: () => viewModel as unknown as SortedEntity[],
+ });
+ instance.option('viewModel', viewModel);
+
+ const viewItem0 = instance.getViewItemBySortedIndex(0);
+ (viewItem0?.$element().get(0) as HTMLElement).click();
+
+ const event = new KeyboardEvent('keydown', { key: 'End', bubbles: true, cancelable: true });
+ viewItem0?.$element().get(0)?.dispatchEvent(event);
+
+ expect(event.defaultPrevented).toBe(true);
+ });
+ });
+
+ describe('Keyboard actions', () => {
+ it('should call onDeleteKeyPress when Delete is pressed and allowDelete is true', () => {
+ const onDeleteKeyPress = jest.fn();
+ const viewModel = [
+ mockGridViewModel({ ...defaultAppointmentData }, { sortedIndex: 0 }),
+ mockGridViewModel({ ...defaultAppointmentData }, { sortedIndex: 1 }),
+ ];
+
+ const instance = createAppointments({
+ ...getProperties(),
+ editing: { allowDeleting: true },
+ onDeleteKeyPress,
+ getSortedAppointments: () => [{
+ sortedIndex: 0,
+ itemData: defaultAppointmentData,
+ source: { startDate: 0 },
+ }] as unknown as SortedEntity[],
+ });
+ instance.option('viewModel', viewModel);
+
+ const viewItem = instance.getViewItemBySortedIndex(0);
+ (viewItem?.$element().get(0) as HTMLElement).click();
+ fireEvent.keyDown(viewItem?.$element().get(0) as HTMLElement, { key: 'Delete' });
+
+ expect(onDeleteKeyPress).toHaveBeenCalledTimes(1);
+ expect(onDeleteKeyPress).toHaveBeenCalledWith(
+ expect.objectContaining({ appointment: defaultAppointmentData }),
+ );
+ });
+
+ it('should not call onDeleteKeyPress when Delete is pressed and allowDelete is false', () => {
+ const onDeleteKeyPress = jest.fn();
+ const viewModel = [
+ mockGridViewModel({ ...defaultAppointmentData }, { sortedIndex: 0 }),
+ ];
+
+ const instance = createAppointments({
+ ...getProperties(),
+ editing: { allowDeleting: false },
+ getSortedAppointments: () => viewModel as unknown as SortedEntity[],
+ });
+ instance.option('viewModel', viewModel);
+
+ const viewItem = instance.getViewItemBySortedIndex(0);
+ (viewItem?.$element().get(0) as HTMLElement).click();
+ fireEvent.keyDown(viewItem?.$element().get(0) as HTMLElement, { key: 'Delete' });
+
+ expect(onDeleteKeyPress).not.toHaveBeenCalled();
+ });
+
+ it('should call onItemActivate when Enter is pressed', () => {
+ const onItemActivate = jest.fn();
+ const viewModel = [
+ mockGridViewModel({ ...defaultAppointmentData }, { sortedIndex: 0 }),
+ ];
+
+ const instance = createAppointments({
+ ...getProperties(),
+ onItemActivate,
+ getSortedAppointments: () => viewModel as unknown as SortedEntity[],
+ });
+ instance.option('viewModel', viewModel);
+
+ const viewItem = instance.getViewItemBySortedIndex(0);
+ (viewItem?.$element().get(0) as HTMLElement).click();
+ fireEvent.keyDown(viewItem?.$element().get(0) as HTMLElement, { key: 'Enter' });
+
+ expect(onItemActivate).toHaveBeenCalledTimes(1);
+ expect(onItemActivate).toHaveBeenCalledWith(
+ expect.objectContaining({ data: defaultAppointmentData }),
+ );
+ });
+ it('should call onItemActivate when Space is pressed', () => {
+ const onItemActivate = jest.fn();
+ const viewModel = [
+ mockGridViewModel({ ...defaultAppointmentData }, { sortedIndex: 0 }),
+ ];
+
+ const instance = createAppointments({
+ ...getProperties(),
+ onItemActivate,
+ getSortedAppointments: () => viewModel as unknown as SortedEntity[],
+ });
+ instance.option('viewModel', viewModel);
+
+ const viewItem = instance.getViewItemBySortedIndex(0);
+ (viewItem?.$element().get(0) as HTMLElement).click();
+ fireEvent.keyDown(viewItem?.$element().get(0) as HTMLElement, { key: ' ' });
+
+ expect(onItemActivate).toHaveBeenCalledTimes(1);
+ expect(onItemActivate).toHaveBeenCalledWith(
+ expect.objectContaining({ data: defaultAppointmentData }),
+ );
+ });
+ });
});
describe('onAppointmentRendered', () => {
diff --git a/packages/devextreme/js/__internal/scheduler/appointments_new/appointments.ts b/packages/devextreme/js/__internal/scheduler/appointments_new/appointments.ts
index 263b4f4d9fc6..3d99849b1ef6 100644
--- a/packages/devextreme/js/__internal/scheduler/appointments_new/appointments.ts
+++ b/packages/devextreme/js/__internal/scheduler/appointments_new/appointments.ts
@@ -53,6 +53,11 @@ export interface AppointmentsProperties extends DOMComponentProperties
SortedEntity[];
isVirtualScrolling: () => boolean;
scrollTo: (date: Date, options?: ScrollToOptions) => void;
+
+ editing: { allowDeleting: boolean };
+ onDeleteKeyPress: (options:
+ { appointment: SafeAppointment; occurrence: SafeAppointment }) => void;
+ onItemActivate: (options: { data: SafeAppointment; target: EventTarget | null }) => void;
}
export class Appointments extends DOMComponent {
@@ -109,6 +114,9 @@ export class Appointments extends DOMComponent {},
+ editing: { allowDeleting: false },
+ onDeleteKeyPress: (): void => {},
+ onItemActivate: (): void => {},
};
}
diff --git a/packages/devextreme/js/__internal/scheduler/m_scheduler.ts b/packages/devextreme/js/__internal/scheduler/m_scheduler.ts
index 8289fa26eac5..1dbf21ff1016 100644
--- a/packages/devextreme/js/__internal/scheduler/m_scheduler.ts
+++ b/packages/devextreme/js/__internal/scheduler/m_scheduler.ts
@@ -503,7 +503,11 @@ class Scheduler extends SchedulerOptionsBaseWidget {
this.initEditing();
const { editing } = this;
- this.bringEditingModeToAppointments(editing);
+ if (this.option('_newAppointments')) {
+ this._appointments.option('editing', this.editing);
+ } else {
+ this.bringEditingModeToAppointments(editing);
+ }
this.hideAppointmentTooltip();
this.createAppointmentPopupForm();
@@ -1071,6 +1075,7 @@ class Scheduler extends SchedulerOptionsBaseWidget {
const appointmentsConfig: Partial = {
tabIndex: this.option('tabIndex'),
currentView: this.option('currentView') as ViewType,
+ editing: this.editing,
appointmentTemplate: this.getViewOption('appointmentTemplate'),
appointmentCollectorTemplate: this.getViewOption('appointmentCollectorTemplate'),
onAppointmentRendered: (e) => {
@@ -1081,6 +1086,11 @@ class Scheduler extends SchedulerOptionsBaseWidget {
targetedAppointmentData: e.targetedAppointmentData,
});
},
+ onDeleteKeyPress: (e) => {
+ this.checkAndDeleteAppointment(e.appointment, e.occurrence);
+ },
+ onItemActivate: ({ data }) => { this.showAppointmentPopup(data); },
+
getResourceManager: () => this.resourceManager,
getAppointmentDataSource: () => this.appointmentDataSource,
getDataAccessor: () => this._dataAccessors,