diff --git a/e2e/testcafe-devextreme/tests/scheduler/dragAndDrop/T1263508.ts b/e2e/testcafe-devextreme/tests/scheduler/dragAndDrop/T1263508.ts new file mode 100644 index 000000000000..3dc1a4868bd2 --- /dev/null +++ b/e2e/testcafe-devextreme/tests/scheduler/dragAndDrop/T1263508.ts @@ -0,0 +1,97 @@ +import Scheduler from 'devextreme-testcafe-models/scheduler'; +import { ClientFunction, Selector } from 'testcafe'; +import { MouseAction, MouseUpEvents } from '../../../helpers/mouseUpEvents'; +import { createWidget } from '../../../helpers/createWidget'; +import url from '../../../helpers/getPageUrl'; + +fixture.disablePageReloads`Scheduler Drag-and-Drop Fix` + .page(url(__dirname, '../../container.html')); + +const DRAGGABLE_ITEM_CLASS = 'dx-card'; +const draggingGroupName = 'appointmentsGroup'; + +const initList = ClientFunction(() => { + $('
', { id: 'list' }).appendTo('#parentContainer'); +}); + +const addTasksToList = ClientFunction((tasks) => { + tasks.forEach((task) => { + $('
', { + class: 'dx-card', + text: task.text, + }).appendTo('#list'); + }); +}); + +const createItemElement = async (task) => { + await createWidget('dxDraggable', { + group: draggingGroupName, + data: task, + clone: true, + onDragStart(e) { + e.itemData = e.fromData; + }, + }, `.${DRAGGABLE_ITEM_CLASS}:contains(${task.text})`); +}; + +test('Scheduler - The \'Cannot read properties of undefined (reading \'getTime\')\' error is thrown on an attempt to drag an outside element if the previous drag operation was canceled', async (t) => { + const scheduler = new Scheduler('#container'); + const draggableAppointment = scheduler.getAppointment('Book').element; + const targetCell = scheduler.getDateTableCell(5, 0); + const draggableItem = Selector(`.${DRAGGABLE_ITEM_CLASS}`).withText('Brochures'); + + await t.expect(scheduler.element.exists).ok(); + + await MouseUpEvents.disable(MouseAction.dragToElement); + + await t + .dragToElement(draggableAppointment, targetCell) + .pressKey('esc'); + + await MouseUpEvents.enable(MouseAction.dragToElement); + + await t + .expect(draggableItem.exists) + .ok() + .dragToElement(draggableItem, targetCell); + + const newAppointment = scheduler.getAppointment('Brochures'); + + await t + .expect(newAppointment.element.exists) + .ok(); +}).before(async () => { + const tasks = [ + { text: 'Brochures' }, + ]; + + await initList(); + await addTasksToList(tasks); + await Promise.all(tasks.map((task) => createItemElement(task))); + await createWidget('dxScheduler', { + timeZone: 'America/Los_Angeles', + dataSource: [ + { + text: 'Book', + startDate: new Date('2021-04-26T19:00:00.000Z'), + endDate: new Date('2021-04-26T20:00:00.000Z'), + }, + ], + currentDate: new Date(2021, 3, 26), + startDayHour: 9, + height: 600, + editing: true, + appointmentDragging: { + group: draggingGroupName, + onDragEnd(e) { + e.cancel = e.event.ctrlKey; + }, + onRemove(e) { + e.component.deleteAppointment(e.itemData); + }, + onAdd(e) { + e.component.addAppointment(e.itemData); + }, + }, + }); +}); diff --git a/packages/devextreme/js/__internal/scheduler/m_appointment_drag_behavior.ts b/packages/devextreme/js/__internal/scheduler/m_appointment_drag_behavior.ts index 718913429957..2e4643cb3adf 100644 --- a/packages/devextreme/js/__internal/scheduler/m_appointment_drag_behavior.ts +++ b/packages/devextreme/js/__internal/scheduler/m_appointment_drag_behavior.ts @@ -130,7 +130,7 @@ export default class AppointmentDragBehavior { // NOTE: event.cancel may be promise or different type, so we need strict check here. if (e.cancel === true) { - this.removeDroppableClasses(); + options.onDragCancel(e); } if (e.cancel !== true && isSchedulerComponent(e.toComponent)) { diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts index 638f0589743d..61c249d1a754 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts @@ -51,6 +51,7 @@ import { import WidgetObserver from '../base/m_widget_observer'; import AppointmentDragBehavior from '../m_appointment_drag_behavior'; import { + APPOINTMENT_DRAG_SOURCE_CLASS, DATE_TABLE_CLASS, DATE_TABLE_ROW_CLASS, FIXED_CONTAINER_CLASS, @@ -3383,6 +3384,15 @@ const createDragBehaviorConfig = ( removeDroppableCellClass(); }; + const onDragCancel = (e) => { + if (!isDefaultDraggingMode) { + enableDefaultDragging(); + } + + removeDroppableCellClass(); + e.itemElement?.removeClass?.(APPOINTMENT_DRAG_SOURCE_CLASS); + }; + const cursorOffset = options.isSetCursorOffset ? () => { const $dragElement = $(state.dragElement); @@ -3399,6 +3409,7 @@ const createDragBehaviorConfig = ( onDragStart, onDragMove, onDragEnd, + onDragCancel, cursorOffset, filter: options.filter, };