Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
aa4c78f
fix: fix bug
sjbur Feb 4, 2026
64271bf
test: try to add test
sjbur Feb 4, 2026
e34db03
test: try to fix test
sjbur Feb 5, 2026
c960d85
fix: fix test
sjbur Feb 5, 2026
6a133ad
css: fix active color state for list item in collector
sjbur Feb 6, 2026
421678b
refactor: improve appointment deletion logic in tooltip strategy
sjbur Feb 9, 2026
0f53edd
refactor: create isDeletingAllowed method
sjbur Feb 11, 2026
2b1a00a
test: add test for disabled appointment
sjbur Feb 11, 2026
b4f1e8c
css: revert list colors
sjbur Feb 12, 2026
ff9174d
css: update focus color for appointment in tooltip
sjbur Feb 12, 2026
f0b3332
feat: update test to support all themes
sjbur Feb 12, 2026
afb88c8
fix: fix build
sjbur Feb 12, 2026
1678acd
refactor: standardize method visibility and naming conventions in too…
sjbur Feb 13, 2026
cfe1143
fix: fix build
sjbur Feb 19, 2026
4afd4e9
test: add tests
sjbur Feb 19, 2026
59b2061
test: cancel edits in testcafe tests
sjbur Feb 19, 2026
01cbee5
test: create pom for tooltip
sjbur Feb 20, 2026
e3f213e
refactor: rename test file
sjbur Feb 20, 2026
393fdeb
test: fix naming in test
sjbur Feb 20, 2026
65b7a24
test: fix test
sjbur Feb 20, 2026
fd57ba8
test: add test for delete button tabindex
sjbur Feb 20, 2026
5055c5d
test: add test for allowDelete = false
sjbur Feb 20, 2026
7f2de0e
test: add test for deleting appointment on button click
sjbur Feb 20, 2026
68a4882
test: remove pressDeleteOnItem method
sjbur Feb 20, 2026
cd20f51
test: updated tests
sjbur Feb 23, 2026
205aa85
test: update tests
sjbur Feb 23, 2026
6cec4b3
Merge branch '26_1' into v2_issue_308_26_1
sjbur Feb 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import dataSource from './init/widget.data';
import { createScheduler, scroll } from './init/widget.setup';
import url from '../../../../helpers/getPageUrl';
import { DEFAULT_BROWSER_SIZE } from '../../../../helpers/const';
import { Themes } from '../../../../helpers/themes';
import { testScreenshot } from '../../../../helpers/themeUtils';

fixture.disablePageReloads`Appointment tooltip behavior during scrolling in the Scheduler (T755449)`
Expand Down Expand Up @@ -125,6 +126,55 @@ test.meta({ browserSize: [600, 400] })('The tooltip should hide after manually s
}));
});

test.meta({
browserSize: DEFAULT_BROWSER_SIZE,
themes: [Themes.fluentBlue, Themes.genericLight, Themes.materialBlue],
})('Collector tooltip focused list item screenshot', async (t) => {
const { takeScreenshot, compareResults } = createScreenshotsComparer(t);
const scheduler = new Scheduler('#container');
const collector = scheduler.collectors.find('2 more');
const { appointmentTooltip } = scheduler;

await t
.expect(collector.element.exists)
.ok()
.click(collector.element)
.expect(appointmentTooltip.isVisible())
.ok()
.pressKey('tab');

await testScreenshot(
t,
takeScreenshot,
'collector-tooltip-focused-list-item.png',
{ element: scheduler.element },
);

await t
.expect(compareResults.isValid())
.ok(compareResults.errorMessages());
}).before(async () => createWidget('dxScheduler', {
dataSource: [{
text: 'Text',
startDate: new Date(2017, 4, 22, 9, 30, 0, 0),
endDate: new Date(2017, 4, 22, 10, 30, 0, 0),
}, {
text: 'Text2',
startDate: new Date(2017, 4, 22, 9, 30, 0, 0),
endDate: new Date(2017, 4, 22, 10, 30, 0, 0),
}, {
text: 'Text3',
startDate: new Date(2017, 4, 22, 9, 30, 0, 0),
endDate: new Date(2017, 4, 22, 10, 30, 0, 0),
}],
views: [{
type: 'month',
maxAppointmentsPerCell: 1,
}],
currentView: 'month',
currentDate: new Date(2017, 4, 22),
}));

test.meta({ browserSize: [600, 1000] })('Tooltip on mobile devices should have enough hight if there are async templates (React)', async (t) => {
const { takeScreenshot, compareResults } = createScreenshotsComparer(t);
const scheduler = new Scheduler('#container');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ $scheduler-fill-focused-appointment: null !default;
$scheduler-is-shadow-color-for-focused-state: null !default;
$scheduler-dd-appointment-hover-text-color: null !default;
$scheduler-tooltip-appointment-text-color: null !default;
$scheduler-tooltip-list-focused-bg: null !default;
$scheduler-timeline-cell-height: null !default;

$scheduler-appointment-overlay-bg: null !default;
Expand All @@ -47,7 +48,8 @@ $agenda-appointment-recurrence-icon-color: null !default;
@use "./tooltip" with (
$scheduler-appointment-base-color: $scheduler-appointment-base-color,
$scheduler-tooltip-appointment-text-color: $scheduler-tooltip-appointment-text-color,
$scheduler-appointment-overlay-bg: $scheduler-appointment-overlay-bg
$scheduler-appointment-overlay-bg: $scheduler-appointment-overlay-bg,
$scheduler-tooltip-list-focused-bg: $scheduler-tooltip-list-focused-bg
);

$scheduler-appointment-min-size: 4px;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
$scheduler-appointment-base-color: null !default;
$scheduler-tooltip-appointment-text-color: null !default;
$scheduler-appointment-overlay-bg: null !default;
$scheduler-tooltip-list-focused-bg: null !default;

.dx-popup-wrapper.dx-scheduler-appointment-tooltip-wrapper,
.dx-scheduler-overlay-panel {
Expand All @@ -15,6 +16,10 @@ $scheduler-appointment-overlay-bg: null !default;
.dx-list-item-content {
padding: 5px;
}

&.dx-state-focused {
background-color: $scheduler-tooltip-list-focused-bg;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
@use "../colors" as *;
@use "sizes" as *;
@use "../sizes" as *;
@use "../list/colors" as *;
@use "../form/sizes" as *;
@use "../radioGroup/sizes" as *;
@use "../toolbar/sizes" as *;
Expand All @@ -13,6 +14,7 @@
@use "../../base/scheduler" as baseScheduler with (
$scheduler-tooltip-appointment-text-color: $scheduler-tooltip-appointment-text-color,
$scheduler-appointment-overlay-bg: $scheduler-appointment-overlay-bg,
$scheduler-tooltip-list-focused-bg: $list-item-hover-bg,
$scheduler-appointment-base-color: $scheduler-appointment-base-color,
$scheduler-appointment-start-color: $scheduler-appointment-start-color,
$scheduler-appointment-active-color: $scheduler-appointment-active-color,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
@use "../colors" as *;
@use "sizes" as *;
@use "../sizes" as *;
@use "../list/colors" as *;
@use "../toolbar/sizes" as *;
@use '../../base/scheduler/layout/header' as *;
@use "../../base/mixins" as *;
@use "../../base/icon_fonts" as *;
@use "../../base/scheduler" as baseScheduler with (
$scheduler-tooltip-appointment-text-color: $scheduler-tooltip-appointment-text-color,
$scheduler-appointment-overlay-bg: $scheduler-appointment-overlay-bg,
$scheduler-tooltip-list-focused-bg: $list-item-hover-bg,
$scheduler-appointment-base-color: $scheduler-appointment-base-color,
$scheduler-appointment-start-color: $scheduler-appointment-start-color,
$scheduler-base-border-color: $scheduler-base-border-color,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
@use "../colors" as *;
@use "sizes" as *;
@use "../sizes" as *;
@use "../list/colors" as *;
@use "../radioGroup/sizes" as *;
@use "../toolbar/sizes" as *;
@use '../../base/scheduler/layout/header' as *;
Expand All @@ -12,6 +13,7 @@
@use "../../base/scheduler" as baseScheduler with (
$scheduler-tooltip-appointment-text-color: $scheduler-tooltip-appointment-text-color,
$scheduler-appointment-overlay-bg: $scheduler-appointment-overlay-bg,
$scheduler-tooltip-list-focused-bg: $list-item-hover-bg,
$scheduler-appointment-base-color: $scheduler-appointment-base-color,
$scheduler-appointment-start-color: $scheduler-appointment-start-color,
$scheduler-base-border-color: $scheduler-base-border-color,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import $ from '@js/core/renderer';
import { within } from '@testing-library/dom';
import { ToolbarModel } from '@ts/scheduler/__tests__/__mock__/model/toolbar';
import List from '@ts/ui/list/list.edit';

import { APPOINTMENT_POPUP_CLASS } from '../../../appointment_popup/m_popup';
import { POPUP_DIALOG_CLASS } from '../../../m_scheduler';
Expand Down Expand Up @@ -55,6 +57,59 @@ export class SchedulerModel {
return getTexts(collectors);
}

getCollectorButton(index = 0): HTMLElement {
Comment thread
Tucchhaa marked this conversation as resolved.
const allButtons = this.queries.queryAllByRole('button') as HTMLElement[];
const collectors = allButtons.filter((btn) => btn.classList.contains('dx-scheduler-appointment-collector'));

if (collectors.length === 0) {
throw new Error('Collector button not found');
}

return collectors[index];
}

isTooltipVisible(): boolean {
return document.querySelector('.dx-overlay-wrapper.dx-scheduler-appointment-tooltip-wrapper') !== null;
}

getTooltipDeleteButton(index = 0): HTMLElement {
const buttons = document.querySelectorAll<HTMLElement>('.dx-tooltip-appointment-item-delete-button');
Comment thread
Tucchhaa marked this conversation as resolved.
Outdated

if (buttons.length === 0) {
throw new Error('Tooltip delete button not found');
}

return buttons[index];
}

pressDeleteOnTooltipItem(index = 0): void {
const wrapper = document.querySelector('.dx-overlay-wrapper.dx-scheduler-appointment-tooltip-wrapper');
const listScrollable = wrapper?.querySelector('.dx-scrollable');

if (!listScrollable) {
throw new Error('Tooltip list not found');
}

const listItems = wrapper?.querySelectorAll<HTMLElement>('.dx-list-item');
if (!listItems || listItems?.length === 0) {
throw new Error('Tooltip list item not found');
}

const listInstance = List.getInstance($(listScrollable));
const targetItem = listItems[index];
listInstance.option('focusedElement', $(targetItem));

const keyEvent = new KeyboardEvent('keydown', {
key: 'Delete',
bubbles: true,
});
const keyboardOptions = {
keyName: 'del',
originalEvent: keyEvent,
};
listInstance._keyboardHandler(keyboardOptions);
}
Comment thread
Tucchhaa marked this conversation as resolved.
Outdated

getDateTableContent(): string[] {
const cells = this.container.querySelectorAll('.dx-scheduler-date-table-cell');
return getTexts(cells);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import {
afterEach, beforeEach, describe, expect, it, jest,
} from '@jest/globals';
import fx from '@js/common/core/animation/fx';

import { createScheduler } from './__mock__/create_scheduler';
import { setupSchedulerTestEnvironment } from './__mock__/m_mock_scheduler';

describe('Collector tooltip behavior', () => {
Comment thread
Tucchhaa marked this conversation as resolved.
Outdated
beforeEach(() => {
fx.off = true;
setupSchedulerTestEnvironment();
});

afterEach(() => {
fx.off = false;
jest.useRealTimers();
document.body.innerHTML = '';
});

it('should delete appointment by Delete key when focused in tooltip from collector', async () => {
const { POM } = await createScheduler({
dataSource: [
{
text: 'Apt1',
startDate: new Date(2017, 4, 22, 9, 30),
endDate: new Date(2017, 4, 22, 10, 30),
},
{
text: 'Apt2',
startDate: new Date(2017, 4, 22, 9, 30),
endDate: new Date(2017, 4, 22, 10, 30),
},
],
views: [{ type: 'month', maxAppointmentsPerCell: 1 }],
currentView: 'month',
currentDate: new Date(2017, 4, 22),
height: 600,
});

POM.getCollectorButton().click();
POM.pressDeleteOnTooltipItem();

expect(POM.isTooltipVisible()).toBe(false);
});

it('should not delete disabled appointment by Delete key when focused in tooltip from collector', async () => {
const { POM } = await createScheduler({
dataSource: [
{
text: 'Apt1',
startDate: new Date(2017, 4, 22, 9, 30),
endDate: new Date(2017, 4, 22, 10, 30),
},
{
text: 'Apt2',
startDate: new Date(2017, 4, 22, 9, 30),
endDate: new Date(2017, 4, 22, 10, 30),
disabled: true,
},
],
views: [{ type: 'month', maxAppointmentsPerCell: 1 }],
currentView: 'month',
currentDate: new Date(2017, 4, 22),
height: 600,
});

POM.getCollectorButton().click();
POM.pressDeleteOnTooltipItem();

expect(POM.isTooltipVisible()).toBe(true);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,47 +8,47 @@ const APPOINTMENT_TOOLTIP_WRAPPER_CLASS = 'dx-scheduler-appointment-tooltip-wrap
const MAX_TOOLTIP_HEIGHT = 200;

export class DesktopTooltipStrategy extends TooltipStrategyBase {
_prepareBeforeVisibleChanged(dataList) {
protected override prepareBeforeVisibleChanged(dataList) {
this._tooltip.option('position', {
my: 'bottom',
at: 'top',
boundary: this._getBoundary(dataList),
boundary: this.getBoundary(dataList),
offset: this._extraOptions.offset,
collision: 'fit flipfit',
});
}

_getBoundary(dataList) {
private getBoundary(dataList) {
return this._options.isAppointmentInAllDayPanel(dataList[0].appointment) ? this._options.container : this._options.getScrollableContainer();
}

_onShown() {
super._onShown();
protected override onShown() {
super.onShown();
if (this._extraOptions.isButtonClick) {
this._list.focus();
this._list.option('focusedElement', null);
}
}

// @ts-expect-error
_createListOption(target, dataList) {
protected override createListOption(target, dataList) {
// @ts-expect-error
const result: any = super._createListOption(target, dataList);
const result: any = super.createListOption(target, 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;
}

_createTooltip(target, dataList) {
const tooltipElement = this._createTooltipElement(APPOINTMENT_TOOLTIP_WRAPPER_CLASS);
protected override createTooltip(target, dataList) {
const tooltipElement = this.createTooltipElement(APPOINTMENT_TOOLTIP_WRAPPER_CLASS);

const tooltip = this._options.createComponent(tooltipElement, Tooltip, {
target,
maxHeight: MAX_TOOLTIP_HEIGHT,
rtlEnabled: this._extraOptions.rtlEnabled,
onShown: this._onShown.bind(this),
contentTemplate: this._getContentTemplate(dataList),
onShown: this.onShown.bind(this),
contentTemplate: this.getContentTemplate(dataList),
wrapperAttr: { class: APPOINTMENT_TOOLTIP_WRAPPER_CLASS },
_loopFocus: this._extraOptions._loopFocus,
});
Expand All @@ -61,11 +61,11 @@ export class DesktopTooltipStrategy extends TooltipStrategyBase {
return tooltip;
}

_onListRender(e) {
protected override onListRender(e) {
return this._extraOptions.dragBehavior && this._extraOptions.dragBehavior(e);
}

_onListItemContextMenu(e) {
protected override onListItemContextMenu(e) {
const contextMenuEventArgs = this._options.createEventArgs(e);
this._options.onItemContextMenu(contextMenuEventArgs);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const createTabletDeviceConfig = (listHeight) => {
};

export class MobileTooltipStrategy extends TooltipStrategyBase {
_shouldUseTarget() {
protected override shouldUseTarget() {
return false;
}

Expand All @@ -91,17 +91,17 @@ export class MobileTooltipStrategy extends TooltipStrategyBase {
this.setTooltipConfig();
}

_createTooltip(target, dataList) {
const element = this._createTooltipElement(CLASS.slidePanel);
protected override createTooltip(target, dataList) {
const element = this.createTooltipElement(CLASS.slidePanel);

return this._options.createComponent(element, Overlay, {
target: getWindow(),
hideOnOutsideClick: true,
animation: animationConfig,

onShowing: () => this._onShowing(),
onShown: this._onShown.bind(this),
contentTemplate: this._getContentTemplate(dataList),
onShown: this.onShown.bind(this),
contentTemplate: this.getContentTemplate(dataList),
wrapperAttr: { class: CLASS.slidePanel },
});
}
Expand Down
Loading
Loading