Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import {
beforeEach, describe, expect, it,
} from '@jest/globals';
import type { Properties } from '@js/ui/scheduler';

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

const APPOINTMENT_SELECTOR = '.dx-scheduler-appointment';
const REDUCED_SELECTOR = '.dx-scheduler-appointment-reduced';
const HANDLE_LEFT_SELECTOR = '.dx-resizable-handle-left';
const HANDLE_RIGHT_SELECTOR = '.dx-resizable-handle-right';

const getResizeHandles = (container: HTMLElement): string[][] => Array
.from(container.querySelectorAll(APPOINTMENT_SELECTOR))
.map((part) => [
...(part.querySelector(HANDLE_LEFT_SELECTOR) ? ['left'] : []),
...(part.querySelector(HANDLE_RIGHT_SELECTOR) ? ['right'] : []),
]);

const baseConfig: Properties = {
dataSource: [{
text: 'Long',
roomId: 1,
startDate: new Date(2021, 3, 12, 9),
endDate: new Date(2021, 3, 14, 12),
}],
currentDate: new Date(2021, 3, 14),
editing: { allowUpdating: true, allowResizing: true },
groups: ['roomId'],
resources: [{
fieldExpr: 'roomId',
dataSource: [{ id: 1, text: 'Room 1' }, { id: 2, text: 'Room 2' }],
}],
height: 600,
};

describe('Appointments resizing', () => {
beforeEach(() => {
setupSchedulerTestEnvironment();
});

describe('grouping by date', () => {
it('should render resize handles only on the edges of a long appointment [month]', async () => {
const { container } = await createScheduler({
...baseConfig,
views: ['month'],
currentView: 'month',
groupByDate: true,
});

expect(getResizeHandles(container)).toEqual([['left'], [], ['right']]);
});

it('should not render the reduced icon on parts of a long appointment [month]', async () => {
const { container } = await createScheduler({
...baseConfig,
views: ['month'],
currentView: 'month',
groupByDate: true,
});

expect(container.querySelectorAll(APPOINTMENT_SELECTOR).length).toBe(3);
expect(container.querySelectorAll(REDUCED_SELECTOR).length).toBe(0);
});

it('should render resize handles only on the edges of a long all-day appointment [week]', async () => {
const { container } = await createScheduler({
...baseConfig,
dataSource: [{
text: 'Long',
roomId: 1,
allDay: true,
startDate: new Date(2021, 3, 12, 9),
endDate: new Date(2021, 3, 14, 12),
}],
views: ['week'],
currentView: 'week',
groupByDate: true,
});

expect(getResizeHandles(container)).toEqual([['left'], [], ['right']]);
});
});

describe('grouping by resource', () => {
it('should render resize handles only on the edges of a long appointment [month]', async () => {
const { container } = await createScheduler({
...baseConfig,
dataSource: [{
text: 'Long',
roomId: 1,
startDate: new Date(2021, 3, 9, 9),
endDate: new Date(2021, 3, 14, 12),
}],
views: ['month'],
currentView: 'month',
groupByDate: false,
});

expect(getResizeHandles(container)).toEqual([['left'], ['right']]);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export class Appointment extends DOMComponent<AppointmentProperties> {
allowDrag: true,
allowResize: true,
reduced: null,
hideReducedIcon: false,
isCompact: false,
direction: 'vertical',
resizableConfig: { keepAspectRatio: false },
Expand Down Expand Up @@ -102,6 +103,7 @@ export class Appointment extends DOMComponent<AppointmentProperties> {
case 'allowDrag':
case 'allowResize':
case 'reduced':
case 'hideReducedIcon':
case 'sortedIndex':
case 'isCompact':
case 'direction':
Expand Down Expand Up @@ -230,7 +232,7 @@ export class Appointment extends DOMComponent<AppointmentProperties> {
_renderReducedAppointment() {
const reducedPart: any = this.option('reduced');

if (!reducedPart) {
if (!reducedPart || this.option('hideReducedIcon')) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface AppointmentProperties extends Record<string, unknown> {
allowDelete: boolean;
allDay: boolean;
reduced: string;
hideReducedIcon: boolean;
isCompact: boolean;
startDate: Date;
cellWidth: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -757,8 +757,8 @@ class SchedulerAppointments extends CollectionWidget<any> {
allowDrag,
allowDelete,
allDay,
// NOTE: hide reduced icon for grouped by date workspace
reduced: isGroupByDate ? undefined : settings.reduced,
reduced: settings.reduced,
hideReducedIcon: isGroupByDate,
startDate: new Date(settings.info?.appointment.startDate),
cellWidth: this.invoke('getCellWidth'),
cellHeight: this.invoke('getCellHeight'),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import { describe, expect, it } from '@jest/globals';

import { getAppointmentDateRange } from './m_core';
import type { GetAppointmentDateRangeOptions, Rect } from './types';

const CELL_WIDTH = 100;
const CELL_HEIGHT = 24;
const FIRST_DATE = new Date(2021, 3, 11);

const addDays = (date: Date, days: number): Date => new Date(
date.getFullYear(),
date.getMonth(),
date.getDate() + days,
);

// NOTE: Every date column is repeated for each group when grouping by date,
// so a cell column index is not equal to a date index anymore.
const createAllDayRow = (dateCount: number, groupCount: number): {
startDate: Date;
endDate: Date;
index: number;
groupIndex: number;
}[] => Array.from({ length: dateCount * groupCount }, (_, columnIndex) => {
const dateIndex = Math.floor(columnIndex / groupCount);
const startDate = addDays(FIRST_DATE, dateIndex);

return {
startDate,
endDate: startDate,
index: dateIndex,
groupIndex: columnIndex % groupCount,
};
});

const createOptions = ({
dateCount = 7,
groupCount = 2,
left,
width,
handles,
rtlEnabled = false,
appointment = {
startDate: new Date(2021, 3, 12),
endDate: new Date(2021, 3, 14),
allDay: false,
},
}: {
dateCount?: number;
groupCount?: number;
left: number;
width: number;
handles: { left: boolean; right: boolean };
rtlEnabled?: boolean;
appointment?: { startDate: Date; endDate: Date; allDay: boolean };
}): GetAppointmentDateRangeOptions => {
const row = createAllDayRow(dateCount, groupCount);
const cellsMeta = row.map(() => ({ width: CELL_WIDTH, height: CELL_HEIGHT }));
const rect = (values: Partial<Rect>): Rect => ({
top: 0, left: 0, width: 0, height: 0, ...values,
});

return {
handles,
rtlEnabled,
isVerticalGroupedWorkSpace: false,
appointmentSettings: {
allDay: true,
rowIndex: 0,
columnIndex: 0,
info: {
appointment: { allDay: appointment.allDay },
sourceAppointment: {
startDate: appointment.startDate,
endDate: appointment.endDate,
},
},
},
appointmentRect: rect({ left, width, height: CELL_HEIGHT }),
parentAppointmentRect: rect({}),
DOMMetaData: { allDayPanelCellsMeta: cellsMeta },
viewDataProvider: {
getCellData: (
rowIndex: number,
columnIndex: number,
isAllDay: boolean,
rtl: boolean,
) => ({ ...row[rtl ? row.length - 1 - columnIndex : columnIndex] }),
},
} as unknown as GetAppointmentDateRangeOptions;
};

describe('getAppointmentDateRange', () => {
describe('grouping by date', () => {
it('should take the end date from the cell under the right appointment border', () => {
const dateRange = getAppointmentDateRange(createOptions({
left: 2 * CELL_WIDTH,
width: 5 * CELL_WIDTH,
handles: { left: false, right: true },
}));

expect(dateRange).toEqual({
startDate: new Date(2021, 3, 12),
endDate: new Date(2021, 3, 15),
});
});

it('should take the start date from the cell under the left appointment border', () => {
const dateRange = getAppointmentDateRange(createOptions({
left: 0,
width: 5 * CELL_WIDTH,
handles: { left: true, right: false },
}));

expect(dateRange).toEqual({
startDate: new Date(2021, 3, 11),
endDate: new Date(2021, 3, 14),
});
});

it('should not go outside of the row when the appointment is wider than the row', () => {
const dateRange = getAppointmentDateRange(createOptions({
left: 12 * CELL_WIDTH,
width: 5 * CELL_WIDTH,
handles: { left: false, right: true },
}));

expect(dateRange).toEqual({
startDate: new Date(2021, 3, 12),
endDate: new Date(2021, 3, 18),
});
});

it('should mirror cell indexes in RTL', () => {
const dateRange = getAppointmentDateRange(createOptions({
left: 11 * CELL_WIDTH,
width: 3 * CELL_WIDTH,
handles: { left: false, right: true },
rtlEnabled: true,
}));

expect(dateRange).toEqual({
startDate: new Date(2021, 3, 11),
endDate: new Date(2021, 3, 14),
});
});
});

describe('without grouping', () => {
it('should take the end date from the cell under the right appointment border', () => {
const dateRange = getAppointmentDateRange(createOptions({
groupCount: 1,
left: CELL_WIDTH,
width: 3 * CELL_WIDTH,
handles: { left: false, right: true },
}));

expect(dateRange).toEqual({
startDate: new Date(2021, 3, 12),
endDate: new Date(2021, 3, 15),
});
});

it('should keep the all day appointment end date inside the last occupied cell', () => {
const dateRange = getAppointmentDateRange(createOptions({
groupCount: 1,
left: CELL_WIDTH,
width: 3 * CELL_WIDTH,
handles: { left: false, right: true },
appointment: {
startDate: new Date(2021, 3, 12),
endDate: new Date(2021, 3, 14),
allDay: true,
},
}));

expect(dateRange).toEqual({
startDate: new Date(2021, 3, 12),
endDate: new Date(2021, 3, 14),
});
});
});
});
Loading
Loading