Skip to content
Closed
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
Expand Up @@ -94,6 +94,7 @@ $reduced-icon-offset: null !default;
background-clip: padding-box;
position: absolute;
cursor: default;
outline: none;

@include user-select(none);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,18 @@ describe('New Appointments', () => {
return $(POM.getAppointments()[0].element).text() === 'Custom appointment';
};

it('should render default template', async () => {
const { POM } = await createScheduler(config);

if (templateName === 'appointmentCollectorTemplate') {
const collectorButton = POM.getCollectorButton();
expect(collectorButton.textContent).toBe('1');
} else {
const appointment = POM.getAppointments()[0];
expect(appointment.getText()).toBe('Appointment 1');
}
});

it('should apply custom template', async () => {
const { POM } = await createScheduler({
...config,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import $ from '@js/core/renderer';
import { EmptyTemplate } from '@ts/core/templates/m_empty_template';
import type { SafeAppointment, TargetedAppointment } from '@ts/scheduler/types';

import type { AppointmentCollectorProperties } from '../appointment_collector';
import { AppointmentCollector } from '../appointment_collector';

export const getAppointmentCollectorProperties = (
appointmentsData: SafeAppointment[],
): AppointmentCollectorProperties => {
const targetedAppointmentData: TargetedAppointment = {
...appointmentsData[0],
displayStartDate: appointmentsData[0].startDate as Date,
displayEndDate: appointmentsData[0].endDate as Date,
};

const config: AppointmentCollectorProperties = {
tabIndex: 0,
sortedIndex: 0,
appointmentsData,
isCompact: false,
geometry: {
height: 30,
width: 30,
top: 0,
left: 0,
},
targetedAppointmentData,
appointmentCollectorTemplate: new EmptyTemplate(),
onFocusIn: () => {},
onFocusOut: () => {},
onKeyDown: () => {},
};

return config;
};

export const createAppointmentCollector = (
properties: AppointmentCollectorProperties,
): AppointmentCollector => {
const $element = $('.root');

// @ts-expect-error
return new AppointmentCollector($element, properties);
};
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import $ from '@js/core/renderer';
import { EmptyTemplate } from '@ts/core/templates/m_empty_template';
import { mockAppointmentDataAccessor } from '@ts/scheduler/__mock__/appointment_data_accessor.mock';
import type { SafeAppointment, TargetedAppointment } from '@ts/scheduler/types';
import type { AppointmentDataAccessor } from '@ts/scheduler/utils/data_accessor/appointment_data_accessor';

import type { BaseAppointmentViewProperties } from '../appointment/base_appointment';
import { BaseAppointmentView, type BaseAppointmentViewProperties } from '../appointment/base_appointment';

export const getBaseAppointmentProperties = (
export const getBaseAppointmentViewProperties = (
appointmentData: SafeAppointment,
targetedAppointmentData?: TargetedAppointment,
): BaseAppointmentViewProperties => {
Expand All @@ -17,13 +18,32 @@ export const getBaseAppointmentProperties = (

const config: BaseAppointmentViewProperties = {
index: 0,
tabIndex: 0,
sortedIndex: 0,
appointmentData,
targetedAppointmentData: normalizedTargetedAppointmentData,
appointmentTemplate: new EmptyTemplate(),
onAppointmentRendered: () => {},
onRendered: () => {},
onFocusIn: () => {},
onFocusOut: () => {},
onKeyDown: () => {},
getDataAccessor: (): AppointmentDataAccessor => mockAppointmentDataAccessor,
getResourceColor: (): Promise<string | undefined> => Promise.resolve(undefined),
};

return config;
};

export const createBaseAppointment = async (
properties: BaseAppointmentViewProperties,
): Promise<BaseAppointmentView> => {
const $element = $('.root');

// @ts-expect-error
const instance = new BaseAppointmentView($element, properties);

// Await for resources
await new Promise(process.nextTick);

return instance;
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { SafeAppointment, TargetedAppointment } from '@ts/scheduler/types';
import type { AppointmentResource } from '@ts/scheduler/utils/resource_manager/appointment_groups_utils';

import fx from '../../../common/core/animation/fx';
import { getBaseAppointmentProperties } from '../__mock__/appointment_properties';
import { getBaseAppointmentViewProperties } from '../__mock__/appointment_properties';
import { AGENDA_APPOINTMENT_CLASSES, APPOINTMENT_CLASSES } from '../const';
import type { AgendaAppointmentViewProperties } from './agenda_appointment';
import { AgendaAppointmentView } from './agenda_appointment';
Expand All @@ -15,7 +15,7 @@ const getProperties = (
appointmentData: SafeAppointment,
targetedAppointmentData?: TargetedAppointment,
): AgendaAppointmentViewProperties => {
const baseProperties = getBaseAppointmentProperties(
const baseProperties = getBaseAppointmentViewProperties(
appointmentData,
targetedAppointmentData,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import type { SafeAppointment } from '@ts/scheduler/types';
import type { AppointmentResource } from '@ts/scheduler/utils/resource_manager/appointment_groups_utils';

import {
AGENDA_APPOINTMENT_CLASSES, ALL_DAY_TEXT, APPOINTMENT_CLASSES, RECURRING_LABEL,
AGENDA_APPOINTMENT_CLASSES,
ALL_DAY_TEXT,
APPOINTMENT_CLASSES,
APPOINTMENT_TYPE_CLASSES,
RECURRING_LABEL,
} from '../const';
import type { BaseAppointmentViewProperties } from './base_appointment';
import { BaseAppointmentView } from './base_appointment';
Expand Down Expand Up @@ -32,11 +36,11 @@ export class AgendaAppointmentView extends BaseAppointmentView<AgendaAppointment
.toggleClass(AGENDA_APPOINTMENT_CLASSES.LAST_IN_DATE, this.option().modifiers.isLastInGroup);
}

public override resize(): void {
this.$element().css({
height: this.option().geometry.height,
width: this.option().geometry.width,
});
public override resize(geometry?: { height: number, width: string | number }): void {
const newGeometry = geometry ?? this.option().geometry;
const { height, width } = newGeometry;

this.$element().css({ height, width });
}

protected override defaultAppointmentContent(
Expand All @@ -60,6 +64,7 @@ export class AgendaAppointmentView extends BaseAppointmentView<AgendaAppointment
// eslint-disable-next-line no-void
void this.option().getResourceColor().then((color) => {
if (color) {
this.$element().addClass(APPOINTMENT_TYPE_CLASSES.HAS_RESOURCE);
$marker.css('backgroundColor', color);
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,8 @@ import {
import $ from '@js/core/renderer';

import fx from '../../../common/core/animation/fx';
import { getBaseAppointmentProperties } from '../__mock__/appointment_properties';
import { createBaseAppointment, getBaseAppointmentViewProperties as getProperties } from '../__mock__/appointment_properties';
import { APPOINTMENT_CLASSES, APPOINTMENT_TYPE_CLASSES } from '../const';
import type { BaseAppointmentViewProperties } from './base_appointment';
import { BaseAppointmentView } from './base_appointment';

const createBaseAppointment = async (
properties: BaseAppointmentViewProperties,
): Promise<BaseAppointmentView> => {
const $element = $('.root');

// @ts-expect-error
const instance = new BaseAppointmentView($element, properties);

// Await for resources
await new Promise(process.nextTick);

return instance;
};

const defaultAppointmentData = {
title: 'Test appointment',
Expand Down Expand Up @@ -51,7 +35,7 @@ describe('BaseAppointment', () => {
describe('Classes', () => {
it('should have container class', async () => {
const instance = await createBaseAppointment(
getBaseAppointmentProperties(defaultAppointmentData),
getProperties(defaultAppointmentData),
);

expect(instance.$element().hasClass(APPOINTMENT_CLASSES.CONTAINER)).toBe(true);
Expand All @@ -61,7 +45,7 @@ describe('BaseAppointment', () => {
true, false,
])('should have correct class for isRecurring = %o', async (isRecurring) => {
const instance = await createBaseAppointment(
getBaseAppointmentProperties({
getProperties({
...defaultAppointmentData,
recurrenceRule: isRecurring ? 'FREQ=DAILY;COUNT=5' : undefined,
}),
Expand All @@ -76,7 +60,7 @@ describe('BaseAppointment', () => {
true, false,
])('should have correct class for allDay = %o', async (allDay) => {
const instance = await createBaseAppointment(
getBaseAppointmentProperties({
getProperties({
...defaultAppointmentData,
allDay,
}),
Expand All @@ -91,7 +75,7 @@ describe('BaseAppointment', () => {
describe('Aria', () => {
it('should have role button', async () => {
const instance = await createBaseAppointment(
getBaseAppointmentProperties(defaultAppointmentData),
getProperties(defaultAppointmentData),
);

expect(instance.$element().attr('role')).toBe('button');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,28 @@ import registerComponent from '@js/core/component_registrator';
import type { DxElement } from '@js/core/element';
import type { dxElementWrapper } from '@js/core/renderer';
import $ from '@js/core/renderer';
import type { DxEvent } from '@js/events';
import { getPublicElement } from '@ts/core/m_element';
import { EmptyTemplate } from '@ts/core/templates/m_empty_template';
import { FunctionTemplate } from '@ts/core/templates/m_function_template';
import type { TemplateBase } from '@ts/core/templates/m_template_base';
import type { DOMComponentProperties } from '@ts/core/widget/dom_component';
import DOMComponent from '@ts/core/widget/dom_component';
import { click } from '@ts/events/m_short';
import type { SafeAppointment, TargetedAppointment } from '@ts/scheduler/types';
import type { AppointmentDataAccessor } from '@ts/scheduler/utils/data_accessor/appointment_data_accessor';

import { APPOINTMENT_CLASSES, APPOINTMENT_TYPE_CLASSES } from '../const';
import { APPOINTMENT_CLASSES, APPOINTMENT_TYPE_CLASSES, FOCUSED_STATE_CLASS } from '../const';
import { DateFormatType, getDateTextFromTargetAppointment } from '../utils/get_date_text';
import type { ViewItemProperties } from '../view_item';
import { EVENTS_NAMESPACE, ViewItem } from '../view_item';

export interface BaseAppointmentViewProperties
// eslint-disable-next-line @typescript-eslint/no-explicit-any
extends DOMComponentProperties<BaseAppointmentView<any>> {
extends ViewItemProperties {
index: number;
appointmentData: SafeAppointment;
targetedAppointmentData: TargetedAppointment;
appointmentTemplate: TemplateBase;

onAppointmentRendered: (e: {
onRendered: (e: {
element: DxElement;
appointmentData: SafeAppointment;
targetedAppointmentData: TargetedAppointment;
Expand All @@ -35,7 +36,7 @@ export interface BaseAppointmentViewProperties

export class BaseAppointmentView<
TProperties extends BaseAppointmentViewProperties = BaseAppointmentViewProperties,
> extends DOMComponent<BaseAppointmentView<TProperties>, TProperties> {
> extends ViewItem<TProperties> {
protected get targetedAppointmentData(): TargetedAppointment {
return this.option().targetedAppointmentData;
}
Expand All @@ -60,10 +61,17 @@ export class BaseAppointmentView<
this.resize();
this.applyElementClasses();
this.applyAria();
this.attachFocusEvents();
this.attachClickEvent();
this.attachKeydownEvents();
this.renderContentTemplate();
}

public resize(): void { }
override _dispose(): void {
super._dispose();

click.off(this.$element(), EVENTS_NAMESPACE);
}

protected applyElementClasses(): void {
this.$element()
Expand All @@ -74,7 +82,31 @@ export class BaseAppointmentView<

protected applyAria(): void {
this.$element()
.attr('role', 'button');
.attr('role', 'button')
.attr('tabindex', -1);
}

private attachClickEvent(): void {
click.off(this.$element(), EVENTS_NAMESPACE);
click.on(
this.$element(),
this.onClick.bind(this),
EVENTS_NAMESPACE,
);
}

protected override onFocusIn(): void {
this.$element().addClass(FOCUSED_STATE_CLASS);

super.onFocusIn();
}

protected override onFocusOut(e: DxEvent): void {
this.$element()
.removeClass(FOCUSED_STATE_CLASS)
.attr('tabindex', -1);

super.onFocusOut(e);
}

protected getTitleText(): string {
Expand Down Expand Up @@ -128,7 +160,7 @@ export class BaseAppointmentView<
},
index: this.option().index,
onRendered: () => {
this.option().onAppointmentRendered({
this.option().onRendered({
element: getPublicElement(this.$element()),
appointmentData: this.appointmentData,
targetedAppointmentData: this.targetedAppointmentData,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import $ from '@js/core/renderer';
import type { SafeAppointment } from '@ts/scheduler/types';

import fx from '../../../common/core/animation/fx';
import { getBaseAppointmentProperties } from '../__mock__/appointment_properties';
import { getBaseAppointmentViewProperties } from '../__mock__/appointment_properties';
import { APPOINTMENT_CLASSES, APPOINTMENT_TYPE_CLASSES } from '../const';
import type { GridAppointmentViewProperties } from './grid_appointment';
import { GridAppointmentView } from './grid_appointment';

const getProperties = (
appointmentData: SafeAppointment,
): GridAppointmentViewProperties => {
const baseProperties = getBaseAppointmentProperties(appointmentData);
const baseProperties = getBaseAppointmentViewProperties(appointmentData);

return {
...baseProperties,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,16 @@ export class GridAppointmentView extends BaseAppointmentView<GridAppointmentView
void this.applyElementColor();
}

public override resize(): void {
const { geometry } = this.option();
public override resize(
geometry?: { height: number, width: number, top: number, left: number },
): void {
const newGeometry = geometry ?? this.option().geometry;
const {
top, left, width, height,
} = newGeometry;

this.$element().css({
height: geometry.height,
width: geometry.width,
top: geometry.top,
left: geometry.left,
height, width, top, left,
});
}

Expand Down Expand Up @@ -84,6 +86,7 @@ export class GridAppointmentView extends BaseAppointmentView<GridAppointmentView
const color = await this.option().getResourceColor();

if (color) {
this.$element().addClass(APPOINTMENT_TYPE_CLASSES.HAS_RESOURCE);
this.$element().css('backgroundColor', color);
}
}
Expand Down
Loading
Loading