Skip to content

Scheduler - Appointments refactoring - Drag-n-Drop#33690

Merged
Tucchhaa merged 7 commits into
DevExpress:26_1from
Tucchhaa:appt_dnd_26_1
May 27, 2026
Merged

Scheduler - Appointments refactoring - Drag-n-Drop#33690
Tucchhaa merged 7 commits into
DevExpress:26_1from
Tucchhaa:appt_dnd_26_1

Conversation

@Tucchhaa

@Tucchhaa Tucchhaa commented May 25, 2026

Copy link
Copy Markdown
Contributor

This PR adds DnD functionality for refactored appointments.
The old DnD implementation was scattered across several files:

  1. /m_appointment_drag_behavior
  2. workspaces/m_work_space
  3. m_compact_appointments_helper
  4. m_subscribes/m_scheduler/m_appointments_collection received dragEvent and managed it depending on the update results

In the new implementation the whole DnD is located in the appointments_drag_controller.ts and dragEvent is not passed to the scheduler at all.

@Tucchhaa Tucchhaa self-assigned this May 25, 2026
@Tucchhaa Tucchhaa added the 26_1 label May 25, 2026
);
}

public getCellFromDragTarget($dragTarget: dxElementWrapper): dxElementWrapper | null {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method has the same logic as old method

appointment,
targetedAppointment,
color: this.resourceManager.getAppointmentColor(appointmentConfig),
settings,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change won't affect anything for the old impl. Needed to match AppointmentTooltipItem type

@Tucchhaa Tucchhaa marked this pull request as ready for review May 26, 2026 10:06
@Tucchhaa Tucchhaa requested a review from a team as a code owner May 26, 2026 10:06
Copilot AI review requested due to automatic review settings May 26, 2026 10:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR advances the Scheduler “new appointments” refactoring by introducing a dedicated drag-and-drop controller and wiring it into the workspace + tooltip flows when _newAppointments is enabled, while gating/annotating legacy drag code paths.

Changes:

  • Added AppointmentDragController and integrated it into Scheduler for workspace appointment dragging and tooltip list item dragging under _newAppointments.
  • Added workspace hit-testing (getCellFromDragTarget) and tooltip list lifecycle hooks (onInitialized/onDisposing) to support the new DnD flow.
  • Updated/added Jest tests and test POM helpers for the new dragging behavior; adjusted an existing QUnit tooltip test assertion.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/desktopTooltip.tests.js Loosened List options assertion (removed “keys length” check).
packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts Disabled legacy drag events for new appointments; added drag target → cell hit-testing helpers.
packages/devextreme/js/__internal/scheduler/types.ts Updated tooltip/compact appointment typing (settings on tooltip items; simplified compact items type).
packages/devextreme/js/__internal/scheduler/tooltip_strategies/tooltip_strategy_base.ts Added List lifecycle hooks (onInitialized / onDisposing) to tooltip list options.
packages/devextreme/js/__internal/scheduler/tooltip_strategies/desktop_tooltip_strategy.ts For new appointments: forward list initialization; skip legacy drag behavior hook in onContentReady.
packages/devextreme/js/__internal/scheduler/m_subscribes.ts Added TODO markers around legacy-only subscribe methods.
packages/devextreme/js/__internal/scheduler/m_scheduler.ts Integrated AppointmentDragController, added draggable container, and added drop-update path for new DnD.
packages/devextreme/js/__internal/scheduler/m_appointment_drag_behavior.ts Marked legacy drag behavior file as TODO for removal.
packages/devextreme/js/__internal/scheduler/appointments_new/appointments.ts Refactored rendering flow, added tooltip item construction and drag-clone rendering helpers.
packages/devextreme/js/__internal/scheduler/appointments_new/appointments.test.ts Updated tests for unified showAppointmentTooltip callback shape/signature.
packages/devextreme/js/__internal/scheduler/appointment_drag_controller.ts New controller implementing workspace + tooltip dragging, clone creation, and cell highlighting.
packages/devextreme/js/__internal/scheduler/tests/appointments_dragging.test.ts New Jest test suite covering new appointment dragging behavior.
packages/devextreme/js/__internal/scheduler/tests/mock/model/tooltip.ts Updated tooltip selectors and added a helper to retrieve the tooltip list element.
packages/devextreme/js/__internal/scheduler/tests/mock/model/scheduler.ts Added helper to locate the dragged clone in the new draggable container.
packages/devextreme/js/__internal/scheduler/tests/mock/model/appointment.ts Added isDragSource() helper for drag-source class assertions.

Comment thread packages/devextreme/js/__internal/scheduler/appointment_drag_controller.ts Outdated
Comment thread packages/devextreme/js/__internal/scheduler/m_scheduler.ts
this.mainContainer = $('<div>').addClass('dx-scheduler-container');
this.$draggableContainer = $('<div>')
.addClass('dx-scheduler-draggable-container')
.appendTo(this.mainContainer);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the old implementation the drag clones were placed in the workspace.$fixedContainer.

In new implementation I create a container inside scheduler's mainContainer, because I pass the container to the draggable at DragController's creation at this moment workSpace is not defined yet.
And if feels more right, because draggable container is used not only by workspace, but also by tooltip

}

if (isPromise(updatingOptions.cancel) && dragEvent) {
if (isPromise(updatingOptions.cancel) && (dragEvent || this.option('_newAppointments'))) {

@Tucchhaa Tucchhaa May 26, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in new appts no need to pass dragEvent, because dragController receives promise on updateAppointmentOnDrop, after which it removes the necessary css classes.

Copilot AI review requested due to automatic review settings May 26, 2026 11:00
return this.viewItemBySortedIndex[sortedIndex];
}

public getViewModelBySortedIndex(sortedIndex: number): AppointmentViewModelPlain {

@Tucchhaa Tucchhaa May 26, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needed to create tooltip item for single appt (not collector) on tooltip showing

return result;
}

public getAppointmentData($element: dxElementWrapper): SafeAppointment {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needed by DnD, because to update the dragged appointment, the appointmentData is needed.

Currently it's a linear search, which I think is ok (drop happens very rarely).

Another approach would be to use $element.data('appointmentData') to attach appointmentData to the element, but I don't think this is a good idea, because later it could be used anywhere in the codebase which is not supposed to happen


// TODO: remove passing index to appointmentTemplate, need only to avoid BC
private renderViewItem(
fragment: DocumentFragment,

@Tucchhaa Tucchhaa May 26, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved fragment logic to the viewModel rendering functions, because renderDragClone doesn't pass fragment.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.


this.options.updateAppointmentOnDrop(e.itemData, this.$highlightedCell)
.finally(() => { this.removeDraggingClasses($(e.itemElement)); })
.catch((err) => { throw err; });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.catch((err) => { throw err; });

Seems like a unhandled promise rejection. Maybe its better to write something like

import { noop } from '@js/core/utils/common';
...
.catch(noop);

What do you think?

@Tucchhaa Tucchhaa May 26, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this code, catch will fire only if function inside finally throws, because updateAppointmentOnDrop always resolve (L1685).

I don't want to put noop here, because in this case we wouldn't notice if the error actually happens. This .catch handler is here just to make eslint happy, but basically it's no difference if it's present or not

this.removeCellHighlight();
}

private onDragCancel(e): void {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add type here for e?

@Tucchhaa Tucchhaa May 26, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

onDragCancel is a private callback, so there's no type for it, but I will add DragEndEvent because they have similar parameters

this.removeDraggingClasses($(e.itemElement));
}

public removeDraggingClasses($dragSource: dxElementWrapper): void {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this method to be public? Its being used only in class

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

made it private

@sjbur

sjbur commented May 26, 2026

Copy link
Copy Markdown
Contributor

I think I found a bug:

  1. Create recurrent appointment
  2. Try to drag an appointment and cancel/close the recurrent appointment edit window
  3. The drag styles for the appointment don't dissappear, it is still semitransparent and it does not change on click or other actions

Copilot AI review requested due to automatic review settings May 27, 2026 05:37

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.

aleksei-semikozov added a commit to aleksei-semikozov/DevExtreme that referenced this pull request May 27, 2026
@Tucchhaa Tucchhaa merged commit fc2c76d into DevExpress:26_1 May 27, 2026
103 checks passed
@Tucchhaa Tucchhaa deleted the appt_dnd_26_1 branch May 27, 2026 09:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants