Skip to content

Scheduler - Appointments Refactoring - Rendering#33014

Merged
Tucchhaa merged 16 commits intoDevExpress:26_1from
Tucchhaa:new_rendering_26_1
Mar 30, 2026
Merged

Scheduler - Appointments Refactoring - Rendering#33014
Tucchhaa merged 16 commits intoDevExpress:26_1from
Tucchhaa:new_rendering_26_1

Conversation

@Tucchhaa
Copy link
Copy Markdown
Contributor

No description provided.

@Tucchhaa Tucchhaa self-assigned this Mar 23, 2026
@Tucchhaa Tucchhaa added the 26_1 label Mar 23, 2026
Comment on lines -17 to -18
startDate: Date | string;
endDate: Date | string;
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.

SafeAppointment type actually doesn't have this properties, so I have removed them

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.

Then we should remove SafeAppointment? Now there is no point in having it

@Tucchhaa Tucchhaa marked this pull request as ready for review March 25, 2026 15:16
@Tucchhaa Tucchhaa requested a review from a team as a code owner March 25, 2026 15:16
Copilot AI review requested due to automatic review settings March 25, 2026 15:16
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 introduces a new internal Scheduler appointments rendering implementation (behind the _newAppointments option), including incremental rendering via view model diffing, plus supporting utilities, styling updates, and test coverage.

Changes:

  • Added appointments_new components (appointments container, base appointment, grid/agenda appointments, collector) and utilities (diffing, targeted appointment, date text) with Jest tests.
  • Integrated the new appointments implementation into m_scheduler.ts when _newAppointments is enabled.
  • Updated Scheduler appointment CSS selectors and refreshed TestCafe visual etalons accordingly.

Reviewed changes

Copilot reviewed 27 out of 33 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/devextreme/js/__internal/scheduler/utils/get_targeted_appointment.ts Marks old targeted-appointment helper for removal after legacy deletion.
packages/devextreme/js/__internal/scheduler/types.ts Simplifies SafeAppointment typing to align with Scheduler Appointment (Date | string dates already supported).
packages/devextreme/js/__internal/scheduler/m_subscribes.ts Switches legacy date-text formatting to new helper and adds legacy-compat TODOs.
packages/devextreme/js/__internal/scheduler/m_scheduler.ts Wires in new Appointments component behind _newAppointments, including onAppointmentRendered action bridging and container wiring.
packages/devextreme/js/__internal/scheduler/m_compact_appointments_helper.ts Marks legacy helper for removal.
packages/devextreme/js/__internal/scheduler/m_classes.ts Marks legacy class exports for cleanup after old impl removal.
packages/devextreme/js/__internal/scheduler/appointments_new/utils/type_helpers.ts Adds type guards for new appointment view model variants.
packages/devextreme/js/__internal/scheduler/appointments_new/utils/get_view_model_diff.ts Adds diffing logic supporting add/remove/resize operations for incremental rendering.
packages/devextreme/js/__internal/scheduler/appointments_new/utils/get_view_model_diff.test.ts Adds Jest coverage for diffing behavior.
packages/devextreme/js/__internal/scheduler/appointments_new/utils/get_targeted_appointment.ts Adds targeted-appointment construction for new appointment view models (incl. resources).
packages/devextreme/js/__internal/scheduler/appointments_new/utils/get_targeted_appointment.test.ts Adds Jest coverage for targeted appointment construction and resource enrichment.
packages/devextreme/js/__internal/scheduler/appointments_new/utils/get_date_text.ts Adds date text formatting utilities used by new/bridged rendering paths.
packages/devextreme/js/__internal/scheduler/appointments_new/const.ts Introduces shared CSS class constants and localized strings for new appointments rendering.
packages/devextreme/js/__internal/scheduler/appointments_new/appointments.ts New appointments container component implementing diff-based incremental DOM updates.
packages/devextreme/js/__internal/scheduler/appointments_new/appointments.test.ts Jest coverage for rendering, partial updates, all-day container routing, and onAppointmentRendered.
packages/devextreme/js/__internal/scheduler/appointments_new/appointment_collector.ts New appointment-collector component (button-based) for compact/“more” UI.
packages/devextreme/js/__internal/scheduler/appointments_new/appointment_collector.test.ts Jest coverage for collector CSS classes, aria, positioning, and text.
packages/devextreme/js/__internal/scheduler/appointments_new/appointment/grid_appointment.ts New grid appointment component (geometry + content + resource color).
packages/devextreme/js/__internal/scheduler/appointments_new/appointment/grid_appointment.test.ts Jest coverage for grid appointment rendering details and resource coloring.
packages/devextreme/js/__internal/scheduler/appointments_new/appointment/base_appointment.ts Base appointment component with templating, action dispatch, aria role, and common helpers.
packages/devextreme/js/__internal/scheduler/appointments_new/appointment/base_appointment.test.ts Jest coverage for base appointment classes and basic aria role.
packages/devextreme/js/__internal/scheduler/appointments_new/appointment/agenda_appointment.ts New agenda appointment component with marker, details, and resource list rendering.
packages/devextreme/js/__internal/scheduler/appointments_new/appointment/agenda_appointment.test.ts Jest coverage for agenda appointment behavior incl. resources and recurrence.
packages/devextreme/js/__internal/scheduler/appointments_new/mock/appointment_properties.ts Test helpers for generating mock view models and base appointment props.
packages/devextreme-scss/scss/widgets/material/scheduler/_index.scss Updates selectors to apply appointment-content styling without relying on .dx-item-content.
packages/devextreme-scss/scss/widgets/generic/scheduler/_index.scss Same selector adjustment for generic theme.
packages/devextreme-scss/scss/widgets/fluent/scheduler/_index.scss Same selector adjustment for fluent theme.
e2e/testcafe-devextreme/tests/scheduler/common/layout/adaptive/etalons/view=day-crossScrolling=true-vertical-rtl (fluent.blue.light).png Updated visual baseline due to styling/rendering changes.
e2e/testcafe-devextreme/tests/scheduler/common/layout/adaptive/etalons/view=day-crossScrolling=true-rtl (fluent.blue.light).png Updated visual baseline due to styling/rendering changes.
e2e/testcafe-devextreme/tests/scheduler/common/layout/adaptive/etalons/view=day-crossScrolling=false-vertical-rtl (fluent.blue.light).png Updated visual baseline due to styling/rendering changes.

Comment thread packages/devextreme/js/__internal/scheduler/appointments_new/appointments.ts Outdated

export class BaseAppointment<
TProperties extends BaseAppointmentProperties = BaseAppointmentProperties,
> extends DOMComponent<BaseAppointment<TProperties>, TProperties> {
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.

I looked at what DOMComponent features are actually used here:

The only place that really needs DOMComponent is _createComponent in AppointmentCollector — it creates a Button and handles disabled/rtl cascading.

But that does not mean every appointment needs to be a DOMComponent. The Collector can get a button factory from the parent instead.

Also: focus() is an empty stub. We lost KBN and accessibility that CollectionWidget gave us for free.

I think each appointment should be a simple object (jQuery element + position + classes), not a full DOMComponent. Template rendering should stay in the parent — like how CollectionWidget does it for List, Gallery, TileView.

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.

Very good point, but I would suggest to postpone removal of DOMComponent. But right now it's a familiar mechanism to pass properties and use common functions.

DOMComponent doesn't introduce a lot of overheads to the appointments and I believe it will be easy to remove DOMComponent in the future.

If you don't mind I would postpone this

Comment thread packages/devextreme-scss/scss/widgets/fluent/scheduler/_index.scss
Comment thread packages/devextreme/js/__internal/scheduler/m_scheduler.ts
@aleksei-semikozov
Copy link
Copy Markdown
Contributor

Good work on isolating the appointment components and the diff-based rendering idea.

I left inline comments, but here is the summary:

1. Feature flag: We are adding ~2800 lines behind _newAppointments that is not enabled and not tested with real Scheduler. This creates 8 if/else branches in m_scheduler.ts.

I think we can do this step by step — replace old code with new code directly, no flag needed:

  • get_date_text.ts can replace m_text_utils.ts + text_utils.ts right now (same logic, cleaner code)
  • get_targeted_appointment.ts can replace the old one in scheduler/utils/
  • AppointmentCollector (107 lines) can replace CompactAppointmentsHelper (205 lines)
  • Then migrate rendering

Each step is small, tested in production, and removes old code instead of adding dead code.

2. No integration tests: 77 unit tests, but no test with a real Scheduler. We do not know if this works end-to-end.

3. DOMComponent per appointment: Each appointment is a full DOMComponent — with template manager, action system, options, lifecycle. But it is just a rectangle with text. This is too much. If we moved away from CollectionWidget, we should use something simpler, not something equally heavy. See inline comments.

Let's discuss the approach before merging.

Copilot AI review requested due to automatic review settings March 27, 2026 13:30
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 28 out of 34 changed files in this pull request and generated 5 comments.

Comment thread packages/devextreme/js/__internal/scheduler/m_scheduler.ts
Comment thread packages/devextreme/js/__internal/scheduler/m_scheduler.ts
Copilot AI review requested due to automatic review settings March 27, 2026 13:55
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 28 out of 34 changed files in this pull request and generated 2 comments.

Comment thread packages/devextreme/js/__internal/scheduler/m_subscribes.ts
Copilot AI review requested due to automatic review settings March 27, 2026 14:33
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 28 out of 34 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

packages/devextreme/js/__internal/scheduler/m_scheduler.ts:998

  • createAppointmentRenderedAction() creates the onAppointmentRendered action without excludeValidators: ['disabled', 'readOnly'], while the legacy path uses that exclusion in getAppointmentRenderedAction(). With _newAppointments enabled, this changes when the handler runs (e.g., it may stop firing when the scheduler is disabled/readOnly). To preserve consistent public event behavior across implementations, pass the same excludeValidators options when creating appointmentRenderedAction.

Comment thread packages/devextreme/js/__internal/scheduler/types.ts
Copy link
Copy Markdown
Contributor

@aleksei-semikozov aleksei-semikozov left a comment

Choose a reason for hiding this comment

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

LGTM

Comment on lines -17 to -18
startDate: Date | string;
endDate: Date | string;
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.

Then we should remove SafeAppointment? Now there is no point in having it

return {
text: adapter.text || messageLocalization.format('dxScheduler-noSubject'),
formatDate: formatDates(startDate, endDate, formatType),
formatDate: getDateText(startDate, endDate, formatType as any),
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.

If you specify "format" argument in createFormattedDateText as of type DateFormatType, then we don't need to cast formatType as any.

@Tucchhaa Tucchhaa merged commit 1c18d71 into DevExpress:26_1 Mar 30, 2026
102 checks passed
@Tucchhaa Tucchhaa deleted the new_rendering_26_1 branch March 30, 2026 14:01
}
case diffItem.needToAdd: {
const fragment = allDay ? allDayFragment : commonFragment;
const appointment = this.renderAppointment(fragment, diffItem.item);
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.

This will break for collectors.

When a collector aggregates all-day appointments, it gets allDay: true from the view model. So it goes into allDayFragment$allDayContainer. But collectors are compact overflow buttons with absolute positioning — they should always go into $commonContainer.

const fragment = (allDay && !isAppointmentCollectorViewModel(diffItem.item))
  ? allDayFragment : commonFragment;

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.

Ok, thank you. I will fix this in the future. I am sure, that not all of the cases are covered in this PR, so I expect that we will notice more and more such cases and fix them pointwise

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.

4 participants