From d7da410f961f8cde2e6d66895f91c57127e3885d Mon Sep 17 00:00:00 2001 From: Sergio Bur Date: Mon, 4 May 2026 12:47:47 +0200 Subject: [PATCH 1/5] refactor: optimize ts --- .../m_desktop_tooltip_strategy.ts | 47 ++-- .../m_mobile_tooltip_strategy.ts | 21 +- .../m_tooltip_strategy_base.ts | 235 +++++++++++++----- 3 files changed, 217 insertions(+), 86 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/tooltip_strategies/m_desktop_tooltip_strategy.ts b/packages/devextreme/js/__internal/scheduler/tooltip_strategies/m_desktop_tooltip_strategy.ts index 39de4b390f81..b60c1217a5fd 100644 --- a/packages/devextreme/js/__internal/scheduler/tooltip_strategies/m_desktop_tooltip_strategy.ts +++ b/packages/devextreme/js/__internal/scheduler/tooltip_strategies/m_desktop_tooltip_strategy.ts @@ -1,57 +1,64 @@ import messageLocalization from '@js/common/core/localization/message'; +import type { dxElementWrapper } from '@js/core/renderer'; +import type { ContentReadyEvent, ItemContextMenuEvent, Properties as ListProperties } from '@js/ui/list'; import supportUtils from '@ts/core/utils/m_support'; import Tooltip from '@ts/ui/m_tooltip'; +import type { AppointmentTooltipItem } from '../types'; import { TooltipStrategyBase } from './m_tooltip_strategy_base'; const APPOINTMENT_TOOLTIP_WRAPPER_CLASS = 'dx-scheduler-appointment-tooltip-wrapper'; const MAX_TOOLTIP_HEIGHT = 200; export class DesktopTooltipStrategy extends TooltipStrategyBase { - protected override prepareBeforeVisibleChanged(dataList) { - this.tooltip.option('position', { + protected override prepareBeforeVisibleChanged(dataList: AppointmentTooltipItem[]): void { + this.tooltip?.option('position', { my: 'bottom', at: 'top', boundary: this.getBoundary(dataList), - offset: this.extraOptions.offset, + offset: this.extraOptions?.offset, collision: 'fit flipfit', }); } - private getBoundary(dataList) { - return this._options.isAppointmentInAllDayPanel(dataList[0].appointment) ? this._options.container : this._options.getScrollableContainer(); + private getBoundary(dataList: AppointmentTooltipItem[]): dxElementWrapper { + return this._options.isAppointmentInAllDayPanel(dataList[0].appointment) + ? this._options.container + : this._options.getScrollableContainer(); } - protected override onShown() { + protected override onShown(): void { super.onShown(); - if (this.extraOptions.isButtonClick) { + if (this.extraOptions?.isButtonClick) { this.list.focus(); this.list.option('focusedElement', null); } } - // @ts-expect-error - protected override createListOption(target, dataList) { - // @ts-expect-error - const result: any = super.createListOption(target, dataList); + protected override createListOption( + dataList: AppointmentTooltipItem[], + ): ListProperties { + const result = super.createListOption(dataList); // T724287 this condition is not covered by tests, because touch variable cannot be overridden. // In the future, it is necessary to cover the tests result.showScrollbar = supportUtils.touch ? 'always' : 'onHover'; return result; } - protected override createTooltip(dataList) { + protected override createTooltip( + dataList: AppointmentTooltipItem[], + ): Tooltip { const tooltipElement = this.createTooltipElement(APPOINTMENT_TOOLTIP_WRAPPER_CLASS); const tooltip = this._options.createComponent(tooltipElement, Tooltip, { target: this.$target, maxHeight: MAX_TOOLTIP_HEIGHT, - rtlEnabled: this.extraOptions.rtlEnabled, + rtlEnabled: this.extraOptions?.rtlEnabled, onShown: this.onShown.bind(this), contentTemplate: this.getContentTemplate(dataList), wrapperAttr: { class: APPOINTMENT_TOOLTIP_WRAPPER_CLASS }, - tabFocusLoopEnabled: this.extraOptions.tabFocusLoopEnabled, - }); + tabFocusLoopEnabled: this.extraOptions?.tabFocusLoopEnabled, + }) as Tooltip; tooltip.setAria({ role: 'dialog', @@ -61,11 +68,15 @@ export class DesktopTooltipStrategy extends TooltipStrategyBase { return tooltip; } - protected override onListRender(e) { - return this.extraOptions.dragBehavior && this.extraOptions.dragBehavior(e); + protected override onListRender( + e: ContentReadyEvent, + ): void { + this.extraOptions?.dragBehavior?.(e); } - protected override onListItemContextMenu(e) { + protected override onListItemContextMenu( + e: ItemContextMenuEvent, + ): void { const contextMenuEventArgs = this._options.createEventArgs(e); this._options.onItemContextMenu(contextMenuEventArgs); } diff --git a/packages/devextreme/js/__internal/scheduler/tooltip_strategies/m_mobile_tooltip_strategy.ts b/packages/devextreme/js/__internal/scheduler/tooltip_strategies/m_mobile_tooltip_strategy.ts index cd6199c15388..03ac3e5642d0 100644 --- a/packages/devextreme/js/__internal/scheduler/tooltip_strategies/m_mobile_tooltip_strategy.ts +++ b/packages/devextreme/js/__internal/scheduler/tooltip_strategies/m_mobile_tooltip_strategy.ts @@ -1,7 +1,10 @@ import { getHeight, getOuterHeight, getWidth } from '@js/core/utils/size'; import { getWindow } from '@js/core/utils/window'; +import type dxOverlay from '@js/ui/overlay'; +import type { Properties as OverlayProperties } from '@js/ui/overlay'; import Overlay from '@js/ui/overlay/ui.overlay'; +import type { AppointmentTooltipItem } from '../types'; import { TooltipStrategyBase } from './m_tooltip_strategy_base'; const CLASS = { @@ -22,7 +25,7 @@ const MAX_WIDTH = { TABLET: '80%', }; -const animationConfig = { +const animationConfig: OverlayProperties['animation'] = { show: { type: 'slide', duration: 300, @@ -37,7 +40,7 @@ const animationConfig = { }, }; -const createPhoneDeviceConfig = (listHeight) => ({ +const createPhoneDeviceConfig = (listHeight: number): OverlayProperties => ({ shading: false, width: MAX_WIDTH.PHONE, height: listHeight > MAX_HEIGHT.PHONE ? MAX_HEIGHT.PHONE : MAX_HEIGHT.DEFAULT, @@ -48,7 +51,7 @@ const createPhoneDeviceConfig = (listHeight) => ({ }, }); -const createTabletDeviceConfig = (listHeight) => { +const createTabletDeviceConfig = (listHeight: number): OverlayProperties => { const currentMaxHeight = getHeight(getWindow()) * MAX_TABLET_OVERLAY_HEIGHT_FACTOR; return { @@ -64,7 +67,7 @@ const createTabletDeviceConfig = (listHeight) => { }; export class MobileTooltipStrategy extends TooltipStrategyBase { - protected override isDesktop() { + protected override isDesktop(): boolean { return false; } @@ -72,7 +75,7 @@ export class MobileTooltipStrategy extends TooltipStrategyBase { const isTabletWidth = getWidth(getWindow()) > 700; const listHeight = getOuterHeight(this.list.$element().find(CLASS.scrollableContent)); - this.tooltip.option( + this.tooltip?.option( isTabletWidth ? createTabletDeviceConfig(listHeight) : createPhoneDeviceConfig(listHeight), @@ -80,7 +83,7 @@ export class MobileTooltipStrategy extends TooltipStrategyBase { } private async onShowing(): Promise { - this.tooltip.option('height', MAX_HEIGHT.DEFAULT); + this.tooltip?.option('height', MAX_HEIGHT.DEFAULT); /* NOTE: there are two setTooltipConfig calls to reduce blinking of overlay. The first one sets initial sizes, the second updates them after rendering async templates @@ -91,7 +94,9 @@ export class MobileTooltipStrategy extends TooltipStrategyBase { this.setTooltipConfig(); } - protected override createTooltip(dataList) { + protected override createTooltip( + dataList: AppointmentTooltipItem[], + ): dxOverlay { const element = this.createTooltipElement(CLASS.slidePanel); return this._options.createComponent(element, Overlay, { @@ -103,6 +108,6 @@ export class MobileTooltipStrategy extends TooltipStrategyBase { onShown: this.onShown.bind(this), contentTemplate: this.getContentTemplate(dataList), wrapperAttr: { class: CLASS.slidePanel }, - }); + }) as dxOverlay; } } diff --git a/packages/devextreme/js/__internal/scheduler/tooltip_strategies/m_tooltip_strategy_base.ts b/packages/devextreme/js/__internal/scheduler/tooltip_strategies/m_tooltip_strategy_base.ts index 454890a61e11..8ce6ab0d3104 100644 --- a/packages/devextreme/js/__internal/scheduler/tooltip_strategies/m_tooltip_strategy_base.ts +++ b/packages/devextreme/js/__internal/scheduler/tooltip_strategies/m_tooltip_strategy_base.ts @@ -1,12 +1,24 @@ +import type { DxElement } from '@js/core/element'; import type { dxElementWrapper } from '@js/core/renderer'; import $ from '@js/core/renderer'; import { FunctionTemplate } from '@js/core/templates/function_template'; import { isRenderer } from '@js/core/utils/type'; +import type { ClickEvent as ButtonClickEvent } from '@js/ui/button'; import Button from '@js/ui/button'; +import type { + ContentReadyEvent, + ItemClickEvent, + ItemContextMenuEvent, + Properties as ListProperties, +} from '@js/ui/list'; +import type dxOverlay from '@js/ui/overlay'; +import type { Properties as OverlayProperties } from '@js/ui/overlay'; +import type { Appointment, Properties as SchedulerProperties } from '@js/ui/scheduler'; import { createPromise } from '@ts/core/utils/promise'; import List from '@ts/ui/list/list.edit'; +import type Tooltip from '@ts/ui/m_tooltip'; -import type { CompactAppointmentOptions } from '../types'; +import type { AppointmentTooltipItem, CompactAppointmentOptions, TargetedAppointment } from '../types'; const TOOLTIP_APPOINTMENT_ITEM = 'dx-tooltip-appointment-item'; const TOOLTIP_APPOINTMENT_ITEM_CONTENT = `${TOOLTIP_APPOINTMENT_ITEM}-content`; @@ -20,27 +32,73 @@ const TOOLTIP_APPOINTMENT_ITEM_DELETE_BUTTON = `${TOOLTIP_APPOINTMENT_ITEM}-dele const APPOINTMENT_TOOLTIP_TEMPLATE = 'appointmentTooltipTemplate'; -export class TooltipStrategyBase { +interface AppointmentTooltipOptions { + createComponent: ( + element: dxElementWrapper, + component: unknown, + options: unknown, + ) => unknown; + container: dxElementWrapper; + getScrollableContainer: () => dxElementWrapper; + addDefaultTemplates: (templates: Record) => void; + getAppointmentTemplate: (optionName: string) => FunctionTemplate; + showAppointmentPopup: ( + appointment: Appointment, + createNewAppointment: boolean, + targetedAppointment?: Appointment | TargetedAppointment, + ) => void; + checkAndDeleteAppointment: ( + appointment: Appointment, + targetedAppointment?: Appointment | TargetedAppointment, + ) => void; + isAppointmentInAllDayPanel: (appointment: Appointment) => boolean; + createFormattedDateText: ( + appointment: Appointment, + targetedAppointment?: Appointment | TargetedAppointment, + format?: string, + ) => { + text: string; + formatDate: string; + }; + getAppointmentDisabled: (appointment: Appointment) => boolean | undefined; + onItemContextMenu: (eventArgs: unknown) => void; + createEventArgs: (e: ItemContextMenuEvent) => unknown; +} + +interface AppointmentTooltipExtraOptions { + clickEvent?: (e: ItemClickEvent) => void; + dragBehavior?: (e: ContentReadyEvent) => void; + editing?: SchedulerProperties['editing']; + focusStateEnabled?: boolean; + isButtonClick?: boolean; + offset?: unknown; + rtlEnabled?: boolean; + tabFocusLoopEnabled?: boolean; +} + +export abstract class TooltipStrategyBase { protected asyncTemplatePromises = new Set>(); - protected tooltip: any; + protected tooltip: Tooltip | dxOverlay | null = null; // TODO: make private once external usages in m_scheduler.ts are removed - _options: any; + _options: AppointmentTooltipOptions; - protected extraOptions: any; + protected extraOptions: AppointmentTooltipExtraOptions | null = null; - protected list: any; + protected list!: List; protected $target: dxElementWrapper | null = null; - constructor(options) { - this.tooltip = null; + constructor(options: AppointmentTooltipOptions) { this._options = options; - this.extraOptions = null; } - show(target: dxElementWrapper, dataList, extraOptions) { + show( + target: dxElementWrapper, + dataList: AppointmentTooltipItem[], + extraOptions: AppointmentTooltipExtraOptions, + ): void { if (dataList.length) { this.hide(); this.$target = target; @@ -52,7 +110,7 @@ export class TooltipStrategyBase { public setTarget($target: dxElementWrapper): void { this.$target = $target; - if (this.isDesktop()) { + if (this.isDesktop() && this.tooltip) { const originalAnimationValue = this.tooltip.option('animation'); this.tooltip.option('animation', null); @@ -73,8 +131,11 @@ export class TooltipStrategyBase { this.list.option('dataSource', dataList); } - private showCore(dataList) { - const describedByValue = isRenderer(this.$target) && this.$target?.attr('aria-describedby') as string; + private showCore(dataList: AppointmentTooltipItem[]): void { + const { $target } = this; + const describedByValue = $target && isRenderer($target) + ? $target.attr('aria-describedby') + : undefined; if (!this.tooltip) { this.tooltip = this.createTooltip(dataList); @@ -89,36 +150,41 @@ export class TooltipStrategyBase { this.prepareBeforeVisibleChanged(dataList); this.tooltip.option('visible', true); - describedByValue && this.$target?.attr('aria-describedby', describedByValue); + if (describedByValue) { + this.$target?.attr('aria-describedby', describedByValue); + } } // eslint-disable-next-line @typescript-eslint/no-unused-vars - protected prepareBeforeVisibleChanged(dataList) { + protected prepareBeforeVisibleChanged(dataList: AppointmentTooltipItem[]): void { } - private isDeletingAllowed(appointment) { - const { editing } = this.extraOptions; + private isDeletingAllowed(appointment: Appointment): boolean { + const { editing } = this.extraOptions ?? {}; const disabled = this._options.getAppointmentDisabled(appointment); - const isDeletingAllowed = editing === true || editing?.allowDeleting === true; + const isDeletingAllowed = editing === true + || (typeof editing === 'object' && editing.allowDeleting === true); return !disabled && isDeletingAllowed; } - protected getContentTemplate(dataList) { - return (container) => { + protected getContentTemplate(dataList: AppointmentTooltipItem[]) { + return (container: DxElement): void => { const listElement = $('
'); $(container).append(listElement); this.list = this.createList(listElement, dataList); - this.list.registerKeyHandler?.('escape', () => { + this.list.registerKeyHandler?.('escape', (): void => { this.hide(); - this.tooltip.option('target').focus(); + const target = this.tooltip?.option('target') as { focus?: () => void } | undefined; + target?.focus?.(); }); - this.list.registerKeyHandler?.('del', () => { + this.list.registerKeyHandler?.('del', (): void => { const { focusedElement } = this.list.option(); if (!focusedElement) { return; } + // @ts-expect-error const { appointment, targetedAppointment } = this.list._getItemData(focusedElement); if (!appointment) { return; @@ -139,14 +205,14 @@ export class TooltipStrategyBase { return $target.get(0) === this.$target?.get(0); } - protected onShown() { - this.list.option('focusStateEnabled', this.extraOptions.focusStateEnabled); + protected onShown(): void { + this.list.option('focusStateEnabled', this.extraOptions?.focusStateEnabled); } - dispose() { + dispose(): void { } - hide() { + hide(): void { if (this.tooltip) { this.tooltip.option('visible', false); } @@ -156,52 +222,83 @@ export class TooltipStrategyBase { return true; } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - protected createTooltip(dataList) { - } + protected abstract createTooltip( + dataList: AppointmentTooltipItem[], + ): Tooltip | dxOverlay; - protected createListOption(dataList) { + protected createListOption( + dataList: AppointmentTooltipItem[], + ): ListProperties { return { dataSource: dataList, onContentReady: this.onListRender.bind(this), - onItemClick: (e) => this.onListItemClick(e), + onItemClick: ( + e: ItemClickEvent, + ): void => this.onListItemClick(e), onItemContextMenu: this.onListItemContextMenu.bind(this), - itemTemplate: (item, index) => this.renderTemplate(item.appointment, item.targetedAppointment, index, item.color), + itemTemplate: ( + item: AppointmentTooltipItem, + index: number, + ): FunctionTemplate => this.renderTemplate( + item.appointment, + item.targetedAppointment, + index, + item.color, + ), pageLoadMode: 'scrollBottom', }; } // eslint-disable-next-line @typescript-eslint/no-unused-vars - protected onListRender(e) { } + protected onListRender(e: ContentReadyEvent): void { } - protected createTooltipElement(wrapperClass) { + protected createTooltipElement(wrapperClass: string): dxElementWrapper { return $('
').appendTo(this._options.container).addClass(wrapperClass); } - private createList(listElement, dataList) { - return this._options.createComponent(listElement, List, this.createListOption(dataList)); + private createList( + listElement: dxElementWrapper, + dataList: AppointmentTooltipItem[], + ): List { + return this._options.createComponent( + listElement, + List, + this.createListOption(dataList), + ) as List; } - private renderTemplate(appointment, targetedAppointment, index, color) { + private renderTemplate( + appointment: Appointment, + targetedAppointment: Appointment | TargetedAppointment | undefined, + index: number, + color: Promise, + ): FunctionTemplate { const itemListContent = this.createItemListContent(appointment, targetedAppointment, color); this._options.addDefaultTemplates({ - // @ts-expect-error - appointmentTooltip: new FunctionTemplate((options) => { - const $container = $(options.container); - $container.append(itemListContent); - return $container; - }), + appointmentTooltip: new FunctionTemplate( + // @ts-expect-error + (options: { container: DxElement }): dxElementWrapper => { + const $container = $(options.container); + $container.append(itemListContent); + return $container; + }, + ), }); const template = this._options.getAppointmentTemplate(APPOINTMENT_TOOLTIP_TEMPLATE); return this.createFunctionTemplate(template, appointment, targetedAppointment, index); } - private createFunctionTemplate(template, appointmentData, targetedAppointmentData, index) { - const isButtonClicked = Boolean(this.extraOptions.isButtonClick); + private createFunctionTemplate( + template: FunctionTemplate, + appointmentData: Appointment, + targetedAppointmentData: Appointment | TargetedAppointment | undefined, + index: number, + ): FunctionTemplate { + const isButtonClicked = Boolean(this.extraOptions?.isButtonClick); // @ts-expect-error - return new FunctionTemplate((options) => { + return new FunctionTemplate((options: { container: DxElement }): DxElement => { // eslint-disable-next-line @typescript-eslint/no-invalid-void-type const { promise, resolve } = createPromise(); this.asyncTemplatePromises.add(promise); @@ -212,6 +309,7 @@ export class TooltipStrategyBase { isButtonClicked, }, container: options.container, + // @ts-expect-error index, onRendered: () => { this.asyncTemplatePromises.delete(promise); @@ -221,19 +319,33 @@ export class TooltipStrategyBase { }); } - private onListItemClick(e) { + private onListItemClick(e: ItemClickEvent): void { + if (!e.itemData) { + return; + } + this.hide(); - this.extraOptions.clickEvent && this.extraOptions.clickEvent(e); - this._options.showAppointmentPopup(e.itemData.appointment, false, e.itemData.targetedAppointment); + this.extraOptions?.clickEvent?.(e); + this._options.showAppointmentPopup( + e.itemData.appointment, + false, + e.itemData.targetedAppointment, + ); } // eslint-disable-next-line @typescript-eslint/no-unused-vars - protected onListItemContextMenu(e) { } + protected onListItemContextMenu(e: ItemContextMenuEvent): void { } - private createItemListContent(appointment, targetedAppointment, color) { + private createItemListContent( + appointment: Appointment, + targetedAppointment: Appointment | TargetedAppointment | undefined, + color: Promise, + ): dxElementWrapper { const $itemElement = $('
').addClass(TOOLTIP_APPOINTMENT_ITEM); $itemElement.append(this.createItemListMarker(color)); - $itemElement.append(this.createItemListInfo(this._options.createFormattedDateText(appointment, targetedAppointment))); + $itemElement.append(this.createItemListInfo( + this._options.createFormattedDateText(appointment, targetedAppointment), + )); if (this.isDeletingAllowed(appointment)) { $itemElement.append(this.createDeleteButton(appointment, targetedAppointment)); @@ -242,21 +354,21 @@ export class TooltipStrategyBase { return $itemElement; } - private createItemListMarker(color) { + private createItemListMarker(color: Promise): dxElementWrapper { const $marker = $('
').addClass(TOOLTIP_APPOINTMENT_ITEM_MARKER); const $markerBody = $('
').addClass(TOOLTIP_APPOINTMENT_ITEM_MARKER_BODY); $marker.append($markerBody); - color.then((value) => { + color.then((value: string | undefined): void => { if (value) { $markerBody.css('background', value); } - }); + }, () => {}); return $marker; } - private createItemListInfo(object) { + private createItemListInfo(object: { text: string; formatDate: string }): dxElementWrapper { const result = $('
').addClass(TOOLTIP_APPOINTMENT_ITEM_CONTENT); const $title = $('
').addClass(TOOLTIP_APPOINTMENT_ITEM_CONTENT_SUBJECT).text(object.text); const $date = $('
').addClass(TOOLTIP_APPOINTMENT_ITEM_CONTENT_DATE).text(object.formatDate); @@ -264,7 +376,10 @@ export class TooltipStrategyBase { return result.append($title).append($date); } - private createDeleteButton(appointment, targetedAppointment) { + private createDeleteButton( + appointment: Appointment, + targetedAppointment: Appointment | TargetedAppointment | undefined, + ): dxElementWrapper { const $container = $('
').addClass(TOOLTIP_APPOINTMENT_ITEM_DELETE_BUTTON_CONTAINER); const $deleteButton = $('
').addClass(TOOLTIP_APPOINTMENT_ITEM_DELETE_BUTTON); @@ -273,8 +388,8 @@ export class TooltipStrategyBase { icon: 'trash', stylingMode: 'text', tabIndex: -1, - onClick: (e) => { - e.event.stopPropagation(); + onClick: (e: ButtonClickEvent): void => { + e.event?.stopPropagation(); this._options.checkAndDeleteAppointment(appointment, targetedAppointment); }, }); From 87c24052d1866b83ea35e948edef22f7e70e00c6 Mon Sep 17 00:00:00 2001 From: Sergio Bur Date: Mon, 4 May 2026 13:05:35 +0200 Subject: [PATCH 2/5] fix: fix test --- .../js/__internal/scheduler/m_compact_appointments_helper.ts | 4 +++- .../tooltip_strategies/m_desktop_tooltip_strategy.ts | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/m_compact_appointments_helper.ts b/packages/devextreme/js/__internal/scheduler/m_compact_appointments_helper.ts index 5948d999ba90..88e14a398747 100644 --- a/packages/devextreme/js/__internal/scheduler/m_compact_appointments_helper.ts +++ b/packages/devextreme/js/__internal/scheduler/m_compact_appointments_helper.ts @@ -58,7 +58,9 @@ export class CompactAppointmentsHelper { private getExtraOptionsForTooltip(options: CompactAppointmentOptions, $appointmentCollector) { return { clickEvent: this.clickEvent(options.onAppointmentClick).bind(this), - dragBehavior: options.allowDrag && this.createTooltipDragBehavior($appointmentCollector).bind(this), + dragBehavior: options.allowDrag + ? this.createTooltipDragBehavior($appointmentCollector).bind(this) + : undefined, isButtonClick: true, tabFocusLoopEnabled: true, }; diff --git a/packages/devextreme/js/__internal/scheduler/tooltip_strategies/m_desktop_tooltip_strategy.ts b/packages/devextreme/js/__internal/scheduler/tooltip_strategies/m_desktop_tooltip_strategy.ts index b60c1217a5fd..7453b41b6a59 100644 --- a/packages/devextreme/js/__internal/scheduler/tooltip_strategies/m_desktop_tooltip_strategy.ts +++ b/packages/devextreme/js/__internal/scheduler/tooltip_strategies/m_desktop_tooltip_strategy.ts @@ -71,7 +71,9 @@ export class DesktopTooltipStrategy extends TooltipStrategyBase { protected override onListRender( e: ContentReadyEvent, ): void { - this.extraOptions?.dragBehavior?.(e); + if (this.extraOptions?.dragBehavior) { + this.extraOptions.dragBehavior(e); + } } protected override onListItemContextMenu( From 644b9695a85eb253404943eb16a55214128a1b44 Mon Sep 17 00:00:00 2001 From: Sergio Bur Date: Tue, 5 May 2026 15:47:00 +0200 Subject: [PATCH 3/5] fix: review --- .../tooltip_strategies/m_tooltip_strategy_base.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/tooltip_strategies/m_tooltip_strategy_base.ts b/packages/devextreme/js/__internal/scheduler/tooltip_strategies/m_tooltip_strategy_base.ts index 8ce6ab0d3104..9e52fb916cfc 100644 --- a/packages/devextreme/js/__internal/scheduler/tooltip_strategies/m_tooltip_strategy_base.ts +++ b/packages/devextreme/js/__internal/scheduler/tooltip_strategies/m_tooltip_strategy_base.ts @@ -18,7 +18,7 @@ import { createPromise } from '@ts/core/utils/promise'; import List from '@ts/ui/list/list.edit'; import type Tooltip from '@ts/ui/m_tooltip'; -import type { AppointmentTooltipItem, CompactAppointmentOptions, TargetedAppointment } from '../types'; +import type { AppointmentTooltipItem, TargetedAppointment } from '../types'; const TOOLTIP_APPOINTMENT_ITEM = 'dx-tooltip-appointment-item'; const TOOLTIP_APPOINTMENT_ITEM_CONTENT = `${TOOLTIP_APPOINTMENT_ITEM}-content`; @@ -123,7 +123,7 @@ export abstract class TooltipStrategyBase { return this.$target; } - public setListItems(dataList: CompactAppointmentOptions['items']): void { + public setListItems(dataList: AppointmentTooltipItem[]): void { if (dataList.length === 0) { this.hide(); } @@ -175,8 +175,7 @@ export abstract class TooltipStrategyBase { this.list = this.createList(listElement, dataList); this.list.registerKeyHandler?.('escape', (): void => { this.hide(); - const target = this.tooltip?.option('target') as { focus?: () => void } | undefined; - target?.focus?.(); + (this.getTarget()?.get(0) as HTMLElement | undefined)?.focus(); }); this.list.registerKeyHandler?.('del', (): void => { const { focusedElement } = this.list.option(); From c5b8c1736bd93c4fe8c8afb0a4c5ed6b34dde8a7 Mon Sep 17 00:00:00 2001 From: Sergio Bur Date: Wed, 6 May 2026 10:54:40 +0200 Subject: [PATCH 4/5] refactor: rename files --- packages/devextreme/js/__internal/scheduler/m_scheduler.ts | 4 ++-- ...esktop_tooltip_strategy.ts => desktop_tooltip_strategy.ts} | 2 +- ..._mobile_tooltip_strategy.ts => mobile_tooltip_strategy.ts} | 2 +- .../{m_tooltip_strategy_base.ts => tooltip_strategy_base.ts} | 0 .../DevExpress.ui.widgets.scheduler/desktopTooltip.tests.js | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) rename packages/devextreme/js/__internal/scheduler/tooltip_strategies/{m_desktop_tooltip_strategy.ts => desktop_tooltip_strategy.ts} (97%) rename packages/devextreme/js/__internal/scheduler/tooltip_strategies/{m_mobile_tooltip_strategy.ts => mobile_tooltip_strategy.ts} (98%) rename packages/devextreme/js/__internal/scheduler/tooltip_strategies/{m_tooltip_strategy_base.ts => tooltip_strategy_base.ts} (100%) diff --git a/packages/devextreme/js/__internal/scheduler/m_scheduler.ts b/packages/devextreme/js/__internal/scheduler/m_scheduler.ts index 66fa3dcb5f61..cfa046ca5852 100644 --- a/packages/devextreme/js/__internal/scheduler/m_scheduler.ts +++ b/packages/devextreme/js/__internal/scheduler/m_scheduler.ts @@ -62,8 +62,8 @@ import { } from './r1/utils/index'; import { validateRRule } from './recurrence/validate_rule'; import { SchedulerOptionsBaseWidget } from './scheduler_options_base_widget'; -import { DesktopTooltipStrategy } from './tooltip_strategies/m_desktop_tooltip_strategy'; -import { MobileTooltipStrategy } from './tooltip_strategies/m_mobile_tooltip_strategy'; +import { DesktopTooltipStrategy } from './tooltip_strategies/desktop_tooltip_strategy'; +import { MobileTooltipStrategy } from './tooltip_strategies/mobile_tooltip_strategy'; import type { AppointmentTooltipItem, SafeAppointment, diff --git a/packages/devextreme/js/__internal/scheduler/tooltip_strategies/m_desktop_tooltip_strategy.ts b/packages/devextreme/js/__internal/scheduler/tooltip_strategies/desktop_tooltip_strategy.ts similarity index 97% rename from packages/devextreme/js/__internal/scheduler/tooltip_strategies/m_desktop_tooltip_strategy.ts rename to packages/devextreme/js/__internal/scheduler/tooltip_strategies/desktop_tooltip_strategy.ts index 7453b41b6a59..f0c87f33febf 100644 --- a/packages/devextreme/js/__internal/scheduler/tooltip_strategies/m_desktop_tooltip_strategy.ts +++ b/packages/devextreme/js/__internal/scheduler/tooltip_strategies/desktop_tooltip_strategy.ts @@ -5,7 +5,7 @@ import supportUtils from '@ts/core/utils/m_support'; import Tooltip from '@ts/ui/m_tooltip'; import type { AppointmentTooltipItem } from '../types'; -import { TooltipStrategyBase } from './m_tooltip_strategy_base'; +import { TooltipStrategyBase } from './tooltip_strategy_base'; const APPOINTMENT_TOOLTIP_WRAPPER_CLASS = 'dx-scheduler-appointment-tooltip-wrapper'; const MAX_TOOLTIP_HEIGHT = 200; diff --git a/packages/devextreme/js/__internal/scheduler/tooltip_strategies/m_mobile_tooltip_strategy.ts b/packages/devextreme/js/__internal/scheduler/tooltip_strategies/mobile_tooltip_strategy.ts similarity index 98% rename from packages/devextreme/js/__internal/scheduler/tooltip_strategies/m_mobile_tooltip_strategy.ts rename to packages/devextreme/js/__internal/scheduler/tooltip_strategies/mobile_tooltip_strategy.ts index 03ac3e5642d0..565e5d974e17 100644 --- a/packages/devextreme/js/__internal/scheduler/tooltip_strategies/m_mobile_tooltip_strategy.ts +++ b/packages/devextreme/js/__internal/scheduler/tooltip_strategies/mobile_tooltip_strategy.ts @@ -5,7 +5,7 @@ import type { Properties as OverlayProperties } from '@js/ui/overlay'; import Overlay from '@js/ui/overlay/ui.overlay'; import type { AppointmentTooltipItem } from '../types'; -import { TooltipStrategyBase } from './m_tooltip_strategy_base'; +import { TooltipStrategyBase } from './tooltip_strategy_base'; const CLASS = { slidePanel: 'dx-scheduler-overlay-panel', diff --git a/packages/devextreme/js/__internal/scheduler/tooltip_strategies/m_tooltip_strategy_base.ts b/packages/devextreme/js/__internal/scheduler/tooltip_strategies/tooltip_strategy_base.ts similarity index 100% rename from packages/devextreme/js/__internal/scheduler/tooltip_strategies/m_tooltip_strategy_base.ts rename to packages/devextreme/js/__internal/scheduler/tooltip_strategies/tooltip_strategy_base.ts diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/desktopTooltip.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/desktopTooltip.tests.js index 152a78021cac..233773960cb2 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/desktopTooltip.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/desktopTooltip.tests.js @@ -1,4 +1,4 @@ -import { DesktopTooltipStrategy } from '__internal/scheduler/tooltip_strategies/m_desktop_tooltip_strategy'; +import { DesktopTooltipStrategy } from '@ts/scheduler/tooltip_strategies/desktop_tooltip_strategy'; import { FunctionTemplate } from 'core/templates/function_template'; import { extend } from 'core/utils/extend'; import Tooltip from 'ui/tooltip'; From 542af36c10550f279665a0aa323396fbc6ec6999 Mon Sep 17 00:00:00 2001 From: Sergio Bur Date: Wed, 6 May 2026 11:05:47 +0200 Subject: [PATCH 5/5] fix: fix renaming --- .../DevExpress.ui.widgets.scheduler/desktopTooltip.tests.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/desktopTooltip.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/desktopTooltip.tests.js index 233773960cb2..5326f4b556ab 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/desktopTooltip.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/desktopTooltip.tests.js @@ -1,4 +1,4 @@ -import { DesktopTooltipStrategy } from '@ts/scheduler/tooltip_strategies/desktop_tooltip_strategy'; +import { DesktopTooltipStrategy } from '__internal/scheduler/tooltip_strategies/desktop_tooltip_strategy'; import { FunctionTemplate } from 'core/templates/function_template'; import { extend } from 'core/utils/extend'; import Tooltip from 'ui/tooltip';