diff --git a/packages/devextreme/js/__internal/scheduler/__tests__/workspace.recalculation.test.ts b/packages/devextreme/js/__internal/scheduler/__tests__/workspace.recalculation.test.ts deleted file mode 100644 index 13ffa2259716..000000000000 --- a/packages/devextreme/js/__internal/scheduler/__tests__/workspace.recalculation.test.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { - afterEach, beforeEach, describe, expect, it, -} from '@jest/globals'; -import $ from '@js/core/renderer'; - -import fx from '../../../common/core/animation/fx'; -import CustomStore from '../../../data/custom_store'; -import { createScheduler } from './__mock__/create_scheduler'; -import { setupSchedulerTestEnvironment } from './__mock__/m_mock_scheduler'; - -const CLASSES = { - scheduler: 'dx-scheduler', - workSpace: 'dx-scheduler-work-space', -}; - -describe('Workspace Recalculation with Async Templates (T661335)', () => { - beforeEach(() => { - fx.off = true; - setupSchedulerTestEnvironment({ height: 600 }); - }); - - afterEach(() => { - const $scheduler = $(document.querySelector(`.${CLASSES.scheduler}`)); - // @ts-expect-error - $scheduler.dxScheduler('dispose'); - document.body.innerHTML = ''; - fx.off = false; - }); - - it('should not duplicate workspace elements when resources are loaded asynchronously (T661335)', async () => { - const { scheduler, container } = await createScheduler({ - templatesRenderAsynchronously: true, - currentView: 'day', - views: ['day'], - groups: ['owner'], - resources: [ - { - fieldExpr: 'owner', - dataSource: [{ id: 1, text: 'Owner 1' }], - }, - { - fieldExpr: 'room', - dataSource: new CustomStore({ - load(): Promise { - return new Promise((resolve) => { - setTimeout(() => { - resolve([{ id: 1, text: 'Room 1', color: '#ff0000' }]); - }); - }); - }, - }), - }, - ], - dataSource: [ - { - text: 'Meeting in Room 1', - startDate: new Date(2017, 4, 25, 9, 0), - endDate: new Date(2017, 4, 25, 10, 0), - roomId: 1, - }, - ], - startDayHour: 9, - currentDate: new Date(2017, 4, 25), - height: 600, - }); - - scheduler.option('groups', ['room']); - - await new Promise((r) => { setTimeout(r); }); - - const $workSpaces = $(container).find(`.${CLASSES.workSpace}`); - const $groupHeader = $(container).find('.dx-scheduler-group-header'); - - expect($workSpaces.length).toBe(1); - - expect($groupHeader.length).toBeGreaterThan(0); - expect($groupHeader.text()).toContain('Room 1'); - }); -}); diff --git a/packages/devextreme/js/__internal/scheduler/__tests__/workspace.test.ts b/packages/devextreme/js/__internal/scheduler/__tests__/workspace.test.ts new file mode 100644 index 000000000000..856c638eeef5 --- /dev/null +++ b/packages/devextreme/js/__internal/scheduler/__tests__/workspace.test.ts @@ -0,0 +1,129 @@ +import { + afterEach, beforeEach, describe, expect, it, jest, +} from '@jest/globals'; +import $ from '@js/core/renderer'; +import { getWidth } from '@js/core/utils/size'; + +import fx from '../../../common/core/animation/fx'; +import CustomStore from '../../../data/custom_store'; +import { createScheduler } from './__mock__/create_scheduler'; +import { setupSchedulerTestEnvironment } from './__mock__/m_mock_scheduler'; + +const CLASSES = { + scheduler: 'dx-scheduler', + workSpace: 'dx-scheduler-work-space', +}; + +describe('Workspace', () => { + describe('Recalculation with Async Templates (T661335)', () => { + beforeEach(() => { + fx.off = true; + setupSchedulerTestEnvironment({ height: 600 }); + }); + + afterEach(() => { + const $scheduler = $(document.querySelector(`.${CLASSES.scheduler}`)); + // @ts-expect-error + $scheduler.dxScheduler('dispose'); + document.body.innerHTML = ''; + fx.off = false; + }); + + it('should not duplicate workspace elements when resources are loaded asynchronously (T661335)', async () => { + const { scheduler, container } = await createScheduler({ + templatesRenderAsynchronously: true, + currentView: 'day', + views: ['day'], + groups: ['owner'], + resources: [ + { + fieldExpr: 'owner', + dataSource: [{ id: 1, text: 'Owner 1' }], + }, + { + fieldExpr: 'room', + dataSource: new CustomStore({ + load(): Promise { + return new Promise((resolve) => { + setTimeout(() => { + resolve([{ id: 1, text: 'Room 1', color: '#ff0000' }]); + }); + }); + }, + }), + }, + ], + dataSource: [ + { + text: 'Meeting in Room 1', + startDate: new Date(2017, 4, 25, 9, 0), + endDate: new Date(2017, 4, 25, 10, 0), + roomId: 1, + }, + ], + startDayHour: 9, + currentDate: new Date(2017, 4, 25), + height: 600, + }); + + scheduler.option('groups', ['room']); + + await new Promise((r) => { setTimeout(r); }); + + const $workSpaces = $(container).find(`.${CLASSES.workSpace}`); + const $groupHeader = $(container).find('.dx-scheduler-group-header'); + + expect($workSpaces.length).toBe(1); + + expect($groupHeader.length).toBeGreaterThan(0); + expect($groupHeader.text()).toContain('Room 1'); + }); + }); + + describe('scrollTo (T1310544)', () => { + beforeEach(() => { + fx.off = true; + setupSchedulerTestEnvironment({ height: 600 }); + }); + + afterEach(() => { + document.body.innerHTML = ''; + fx.off = false; + }); + + it('T1310544: should scroll to date with offset: 720 (12 hours)', async () => { + const { scheduler } = await createScheduler({ + views: ['timelineDay'], + currentView: 'timelineDay', + currentDate: new Date(2021, 1, 2), + firstDayOfWeek: 0, + startDayHour: 6, + endDayHour: 18, + offset: 720, + cellDuration: 60, + height: 580, + }); + + const workspace = scheduler.getWorkSpace(); + const scrollable = workspace.getScrollable(); + const $scrollable = scrollable.$element(); + const scrollBySpy = jest.spyOn(scrollable, 'scrollBy'); + + // With offset: 720 (12 hours), cells start at 18:00 (6:00 + 12h) + // For date 22:00, this should be cell index 4 (18:00=0, 19:00=1, 20:00=2, 21:00=3, 22:00=4) + const leftCellCount = 4; + const cellWidth = workspace.getCellWidth(); + const scrollableWidth = getWidth($scrollable); + const expectedLeft = leftCellCount * cellWidth - (scrollableWidth - cellWidth) / 2; + + const targetDate = new Date(2021, 1, 2, 22, 0); + scheduler.scrollTo(targetDate, undefined, false); + + expect(scrollBySpy).toHaveBeenCalledTimes(1); + const scrollParams = scrollBySpy.mock.calls[0][0] as { left: number; top: number }; + expect(scrollParams.left).toBeCloseTo(expectedLeft, 1); + + scrollBySpy.mockRestore(); + }); + }); +}); 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 c1649f57b664..aed9cc16935c 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts @@ -1411,13 +1411,16 @@ class SchedulerWorkSpace extends Widget { const currentDate = date || new Date(this.option('currentDate')); const startDayHour = this.option('startDayHour'); const endDayHour = this.option('endDayHour'); + const viewOffset = this.option('viewOffset'); - if (hours < startDayHour) { - hours = startDayHour; - } + if (viewOffset === 0) { + if (hours < startDayHour) { + hours = startDayHour; + } - if (hours >= endDayHour) { - hours = endDayHour - 1; + if (hours >= endDayHour) { + hours = endDayHour - 1; + } } currentDate.setHours(hours, minutes, 0, 0); @@ -1865,10 +1868,14 @@ class SchedulerWorkSpace extends Widget { } _isValidScrollDate(date, throwWarning = true) { + const viewOffset = this.option('viewOffset') as number; const min = this.getStartViewDate(); const max = this.getEndViewDate(); - if (date < min || date > max) { + const extendedMin = new Date(min.getTime() - viewOffset); + const extendedMax = new Date(max.getTime() + viewOffset); + + if (date < extendedMin || date > extendedMax) { throwWarning && errors.log('W1008', date); return false; }