Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
0016a32
Scheduler — Add hiddenDays option to hide arbitrary days of the week
aleksei-semikozov Apr 7, 2026
2467600
Scheduler — Rename hiddenDays option to hiddenWeekDays per spec
aleksei-semikozov Apr 8, 2026
fbeace8
Scheduler — Add hiddenWeekDays to scheduler.d.ts source for ts-bundle…
aleksei-semikozov Apr 8, 2026
18943b8
Revert "Scheduler — Add hiddenWeekDays to scheduler.d.ts source for t…
sjbur Apr 8, 2026
c385706
Revert "Scheduler — Rename hiddenDays option to hiddenWeekDays per spec"
sjbur Apr 8, 2026
7cadb02
feat: rename hiddenDays to hiddenWeekDays and fix .d.ts
sjbur Apr 8, 2026
bbb309c
Merge branch '26_1' into feature/scheduler-hidden-days-impl-26_1
sjbur Apr 8, 2026
97f1fe8
feat: add storybook
sjbur Apr 8, 2026
4904ffa
refactor: fix typing
sjbur Apr 8, 2026
a4b0767
refactor: fix typing
sjbur Apr 8, 2026
f4ac369
feat: create helper for skipped days
sjbur Apr 8, 2026
91d780d
fix: fix calculating hiddenDays for timeline
sjbur Apr 8, 2026
b1263af
fix: fix hiddenDays for timelineMonth
sjbur Apr 8, 2026
2a11d14
refactor: optimize ts
sjbur Apr 8, 2026
19f3964
fix: fix bug for view_generator for workWeek
sjbur Apr 9, 2026
d414219
fix: fix test
sjbur Apr 9, 2026
2ab9e9b
refactor: remove useless typing
sjbur Apr 9, 2026
d9cedc5
fix: fix hiddenWeekDays for workWeek
sjbur Apr 9, 2026
da7160f
refactor: revert SkippedDaysAnchorKind
sjbur Apr 9, 2026
1bcb103
refactor: optimize
sjbur Apr 9, 2026
2b82fa5
feat: allow hiddenWeekDays for agenda
sjbur Apr 9, 2026
9c71b48
refactor: optimize
sjbur Apr 9, 2026
e32349a
refactor: optimize
sjbur Apr 9, 2026
b341ca6
feat: create type WeekdayIndex for skippedDays
sjbur Apr 9, 2026
6a2a314
feat: update agenda and header logic to support hiddenWeekDays
sjbur Apr 10, 2026
9c81b76
feat: change agenda logic to show only calendar dates in interval min…
sjbur Apr 10, 2026
e30a7bd
fix: fix anchor day for workWeek
sjbur Apr 10, 2026
54283e7
feat: remove WeekDayIndex interface because it requires too much main…
sjbur Apr 10, 2026
2ccc554
fix: fix tests
sjbur Apr 10, 2026
faf52d4
test: update snapshot for santiago timezone for workWeek
sjbur Apr 10, 2026
c1b4969
test: fix test
sjbur Apr 10, 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import type { Meta, StoryObj } from "@storybook/react-webpack5";
import dxScheduler from "devextreme/ui/scheduler";
import { wrapDxWithReact } from "../utils";
import { data, resources } from "./data";

const Scheduler = wrapDxWithReact(dxScheduler);

const viewNames = ['day', 'week', 'workWeek', 'month', 'agenda', 'timelineDay', 'timelineWeek', 'timelineWorkWeek', 'timelineMonth'];

const meta: Meta<typeof Scheduler> = {
title: 'Components/Scheduler/HiddenWeekDays',
component: Scheduler,
parameters: { layout: 'padded' },
};

export default meta;

type Story = StoryObj<typeof Scheduler>;

export const Overview: Story = {
args: {
height: 600,
views: viewNames,
currentView: 'week',
currentDate: new Date(2021, 3, 26),
firstDayOfWeek: 0,
startDayHour: 9,
endDayHour: 22,
dataSource: data,
resources,
hiddenWeekDays: [],
},
argTypes: {
height: { control: 'number' },
views: { control: 'object' },
hiddenWeekDays: { control: 'object' },
currentView: { control: 'select', options: viewNames },
},
};
26 changes: 23 additions & 3 deletions packages/devextreme-angular/src/ui/scheduler/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,16 @@ export class DxSchedulerComponent extends DxComponent implements OnDestroy, OnCh
}



@Input()
get hiddenWeekDays(): Array<number> {
return this._getOption('hiddenWeekDays');
}
set hiddenWeekDays(value: Array<number>) {
this._setOption('hiddenWeekDays', value);
}


/**
* [descr:WidgetOptions.hint]

Expand Down Expand Up @@ -894,10 +904,10 @@ export class DxSchedulerComponent extends DxComponent implements OnDestroy, OnCh

*/
@Input()
get views(): Array<Record<string, any> | string> | { agendaDuration?: number, allDayPanelMode?: AllDayPanelMode, appointmentCollectorTemplate?: any, appointmentTemplate?: any, appointmentTooltipTemplate?: any, cellDuration?: number, dataCellTemplate?: any, dateCellTemplate?: any, endDayHour?: number, firstDayOfWeek?: FirstDayOfWeek | undefined, groupByDate?: boolean, groupOrientation?: Orientation, groups?: Array<string>, intervalCount?: number, maxAppointmentsPerCell?: CellAppointmentsLimit | number, name?: string | undefined, offset?: number, resourceCellTemplate?: any, scrolling?: dxSchedulerScrolling, snapToCellsMode?: SnapToCellsMode, startDate?: Date | number | string | undefined, startDayHour?: number, timeCellTemplate?: any, type?: undefined | ViewType }[] {
get views(): Array<Record<string, any> | string> | { agendaDuration?: number, allDayPanelMode?: AllDayPanelMode, appointmentCollectorTemplate?: any, appointmentTemplate?: any, appointmentTooltipTemplate?: any, cellDuration?: number, dataCellTemplate?: any, dateCellTemplate?: any, endDayHour?: number, firstDayOfWeek?: FirstDayOfWeek | undefined, groupByDate?: boolean, groupOrientation?: Orientation, groups?: Array<string>, hiddenWeekDays?: Array<number>, intervalCount?: number, maxAppointmentsPerCell?: CellAppointmentsLimit | number, name?: string | undefined, offset?: number, resourceCellTemplate?: any, scrolling?: dxSchedulerScrolling, snapToCellsMode?: SnapToCellsMode, startDate?: Date | number | string | undefined, startDayHour?: number, timeCellTemplate?: any, type?: undefined | ViewType }[] {
return this._getOption('views');
}
set views(value: Array<Record<string, any> | string> | { agendaDuration?: number, allDayPanelMode?: AllDayPanelMode, appointmentCollectorTemplate?: any, appointmentTemplate?: any, appointmentTooltipTemplate?: any, cellDuration?: number, dataCellTemplate?: any, dateCellTemplate?: any, endDayHour?: number, firstDayOfWeek?: FirstDayOfWeek | undefined, groupByDate?: boolean, groupOrientation?: Orientation, groups?: Array<string>, intervalCount?: number, maxAppointmentsPerCell?: CellAppointmentsLimit | number, name?: string | undefined, offset?: number, resourceCellTemplate?: any, scrolling?: dxSchedulerScrolling, snapToCellsMode?: SnapToCellsMode, startDate?: Date | number | string | undefined, startDayHour?: number, timeCellTemplate?: any, type?: undefined | ViewType }[]) {
set views(value: Array<Record<string, any> | string> | { agendaDuration?: number, allDayPanelMode?: AllDayPanelMode, appointmentCollectorTemplate?: any, appointmentTemplate?: any, appointmentTooltipTemplate?: any, cellDuration?: number, dataCellTemplate?: any, dateCellTemplate?: any, endDayHour?: number, firstDayOfWeek?: FirstDayOfWeek | undefined, groupByDate?: boolean, groupOrientation?: Orientation, groups?: Array<string>, hiddenWeekDays?: Array<number>, intervalCount?: number, maxAppointmentsPerCell?: CellAppointmentsLimit | number, name?: string | undefined, offset?: number, resourceCellTemplate?: any, scrolling?: dxSchedulerScrolling, snapToCellsMode?: SnapToCellsMode, startDate?: Date | number | string | undefined, startDayHour?: number, timeCellTemplate?: any, type?: undefined | ViewType }[]) {
this._setOption('views', value);
}

Expand Down Expand Up @@ -1274,6 +1284,13 @@ export class DxSchedulerComponent extends DxComponent implements OnDestroy, OnCh
*/
@Output() heightChange: EventEmitter<number | string | undefined>;

/**

* This member supports the internal infrastructure and is not intended to be used directly from your code.

*/
@Output() hiddenWeekDaysChange: EventEmitter<Array<number>>;

/**

* This member supports the internal infrastructure and is not intended to be used directly from your code.
Expand Down Expand Up @@ -1482,7 +1499,7 @@ export class DxSchedulerComponent extends DxComponent implements OnDestroy, OnCh
* This member supports the internal infrastructure and is not intended to be used directly from your code.

*/
@Output() viewsChange: EventEmitter<Array<Record<string, any> | string> | { agendaDuration?: number, allDayPanelMode?: AllDayPanelMode, appointmentCollectorTemplate?: any, appointmentTemplate?: any, appointmentTooltipTemplate?: any, cellDuration?: number, dataCellTemplate?: any, dateCellTemplate?: any, endDayHour?: number, firstDayOfWeek?: FirstDayOfWeek | undefined, groupByDate?: boolean, groupOrientation?: Orientation, groups?: Array<string>, intervalCount?: number, maxAppointmentsPerCell?: CellAppointmentsLimit | number, name?: string | undefined, offset?: number, resourceCellTemplate?: any, scrolling?: dxSchedulerScrolling, snapToCellsMode?: SnapToCellsMode, startDate?: Date | number | string | undefined, startDayHour?: number, timeCellTemplate?: any, type?: undefined | ViewType }[]>;
@Output() viewsChange: EventEmitter<Array<Record<string, any> | string> | { agendaDuration?: number, allDayPanelMode?: AllDayPanelMode, appointmentCollectorTemplate?: any, appointmentTemplate?: any, appointmentTooltipTemplate?: any, cellDuration?: number, dataCellTemplate?: any, dateCellTemplate?: any, endDayHour?: number, firstDayOfWeek?: FirstDayOfWeek | undefined, groupByDate?: boolean, groupOrientation?: Orientation, groups?: Array<string>, hiddenWeekDays?: Array<number>, intervalCount?: number, maxAppointmentsPerCell?: CellAppointmentsLimit | number, name?: string | undefined, offset?: number, resourceCellTemplate?: any, scrolling?: dxSchedulerScrolling, snapToCellsMode?: SnapToCellsMode, startDate?: Date | number | string | undefined, startDayHour?: number, timeCellTemplate?: any, type?: undefined | ViewType }[]>;

/**

Expand Down Expand Up @@ -1558,6 +1575,7 @@ export class DxSchedulerComponent extends DxComponent implements OnDestroy, OnCh
{ emit: 'groupByDateChange' },
{ emit: 'groupsChange' },
{ emit: 'heightChange' },
{ emit: 'hiddenWeekDaysChange' },
{ emit: 'hintChange' },
{ emit: 'indicatorUpdateIntervalChange' },
{ emit: 'maxChange' },
Expand Down Expand Up @@ -1610,6 +1628,7 @@ export class DxSchedulerComponent extends DxComponent implements OnDestroy, OnCh
super.ngOnChanges(changes);
this.setupChanges('dataSource', changes);
this.setupChanges('groups', changes);
this.setupChanges('hiddenWeekDays', changes);
this.setupChanges('resources', changes);
this.setupChanges('selectedCellData', changes);
this.setupChanges('views', changes);
Expand All @@ -1624,6 +1643,7 @@ export class DxSchedulerComponent extends DxComponent implements OnDestroy, OnCh
ngDoCheck() {
this._idh.doCheck('dataSource');
this._idh.doCheck('groups');
this._idh.doCheck('hiddenWeekDays');
this._idh.doCheck('resources');
this._idh.doCheck('selectedCellData');
this._idh.doCheck('views');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,14 @@ export class DxiSchedulerViewComponent extends CollectionNestedOption {
this._setOption('groups', value);
}

@Input()
get hiddenWeekDays(): Array<number> {
return this._getOption('hiddenWeekDays');
}
set hiddenWeekDays(value: Array<number>) {
this._setOption('hiddenWeekDays', value);
}

@Input()
get intervalCount(): number {
return this._getOption('intervalCount');
Expand Down
1 change: 1 addition & 0 deletions packages/devextreme-metadata/make-angular-metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ Ng.makeMetadata({
removeMembers(/\/scheduler:dxSchedulerOptions\.editing\.popup/),
removeMembers(/\/scheduler:dxSchedulerOptions\.resources\.icon/),
removeMembers(/\/scheduler:.*\.snapToCellsMode/),
removeMembers(/\/scheduler:.*\.hiddenWeekDays/),
removeMembers(/\/stepper:/),
removeMembers(/\/speech_to_text:/),
removeMembers(/\/tree_list:dxTreeListColumnButton.onClick/),
Expand Down
1 change: 1 addition & 0 deletions packages/devextreme-react/src/scheduler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1454,6 +1454,7 @@ type IViewProps = React.PropsWithChildren<{
groupByDate?: boolean;
groupOrientation?: Orientation;
groups?: Array<string>;
hiddenWeekDays?: Array<number>;
intervalCount?: number;
maxAppointmentsPerCell?: CellAppointmentsLimit | number;
name?: string | undefined;
Expand Down
5 changes: 5 additions & 0 deletions packages/devextreme-vue/src/scheduler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ type AccessibleOptions = Pick<Properties,
"groupByDate" |
"groups" |
"height" |
"hiddenWeekDays" |
"hint" |
"indicatorUpdateInterval" |
"max" |
Expand Down Expand Up @@ -248,6 +249,7 @@ const componentConfig = {
groupByDate: Boolean,
groups: Array as PropType<Array<string>>,
height: [Number, String],
hiddenWeekDays: Array as PropType<Array<number>>,
hint: String,
indicatorUpdateInterval: Number,
max: [Date, Number, String],
Expand Down Expand Up @@ -331,6 +333,7 @@ const componentConfig = {
"update:groupByDate": null,
"update:groups": null,
"update:height": null,
"update:hiddenWeekDays": null,
"update:hint": null,
"update:indicatorUpdateInterval": null,
"update:max": null,
Expand Down Expand Up @@ -1774,6 +1777,7 @@ const DxViewConfig = {
"update:groupByDate": null,
"update:groupOrientation": null,
"update:groups": null,
"update:hiddenWeekDays": null,
"update:intervalCount": null,
"update:maxAppointmentsPerCell": null,
"update:name": null,
Expand All @@ -1800,6 +1804,7 @@ const DxViewConfig = {
groupByDate: Boolean,
groupOrientation: String as PropType<Orientation>,
groups: Array as PropType<Array<string>>,
hiddenWeekDays: Array as PropType<Array<number>>,
intervalCount: Number,
maxAppointmentsPerCell: [String, Number] as PropType<CellAppointmentsLimit | number>,
name: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing

exports[`scheduler should render correct workspace in Santiago DST for view: Day DST 1`] = `
[
Expand Down Expand Up @@ -307,7 +307,7 @@ exports[`scheduler should render correct workspace in Santiago DST for view: Tim
"Wed 4",
"Thu 5",
"Fri 6",
"Sat 7",
"Mon 9",
"12:00 AM",
"6:00 AM",
"12:00 PM",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export class SchedulerHeader extends Widget<HeaderOptions> {
firstDayOfWeek,
intervalCount: currentView.intervalCount,
agendaDuration: currentView.agendaDuration,
skippedDays: currentView.skippedDays,
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { describe, expect, it } from '@jest/globals';

import { getCaptionInterval, getNextIntervalDate } from './m_utils';

describe('agenda hiddenWeekDays support in header utils', () => {
const skippedDays: number[] = [0, 6];
const options = {
date: new Date(2026, 3, 11),
step: 'agenda' as const,
intervalCount: 1,
agendaDuration: 3,
skippedDays,
};

it('should build caption interval by calendar days', () => {
expect(getCaptionInterval(options)).toEqual({
startDate: new Date(2026, 3, 11),
endDate: new Date(2026, 3, 13, 23, 59, 59, 999),
});
});

it('should navigate to next agenda interval by calendar days', () => {
expect(getNextIntervalDate(options, 1)).toEqual(new Date(2026, 3, 14));
});

it('should navigate to previous agenda interval by calendar days', () => {
expect(getNextIntervalDate(options, -1)).toEqual(new Date(2026, 3, 8));
});
});
42 changes: 35 additions & 7 deletions packages/devextreme/js/__internal/scheduler/header/m_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import type { BaseFormat } from '@ts/core/localization/date';
import { camelize } from '@ts/core/utils/m_inflector';
import type { IntervalOptions, Step } from '@ts/scheduler/header/types';
import type { NormalizedView, RawViewType, ViewType } from '@ts/scheduler/utils/options/types';
import {
getDateAfterVisibleDays,
getFirstVisibleDate,
} from '@ts/scheduler/utils/skipped_days';

import type { Direction } from './constants';

Expand Down Expand Up @@ -78,13 +82,21 @@ const nextAgendaStart = (
): Date => addDateInterval(date, { days: agendaDuration }, 1);

const getIntervalStartDate = (options: IntervalOptions): Date => {
const { date, step, firstDayOfWeek } = options;
const {
date, step, firstDayOfWeek, skippedDays,
} = options;

switch (step) {
case 'day':
case 'week':
case 'month':
return getPeriodStart(date, step, false, firstDayOfWeek) as Date;
case 'week': {
const weekStart = getPeriodStart(date, step, false, firstDayOfWeek) as Date;
if (skippedDays.length > 0) {
return getFirstVisibleDate(weekStart, skippedDays, nextDay);
}
return weekStart;
}
case 'workWeek':
return getWorkWeekStart(getWeekStart(date, firstDayOfWeek));
case 'agenda':
Expand All @@ -98,10 +110,18 @@ const getPeriodEndDate = (
currentPeriodStartDate: Date,
step: Step,
agendaDuration: number,
skippedDays: number[],
): Date => {
const calculators: Record<Step, () => Date> = {
day: () => nextDay(currentPeriodStartDate),
week: () => nextWeek(currentPeriodStartDate),
week: () => (skippedDays.length > 0
? getDateAfterVisibleDays(
currentPeriodStartDate,
7 - skippedDays.length,
skippedDays,
nextDay,
)
: nextWeek(currentPeriodStartDate)),
month: () => nextMonth(currentPeriodStartDate),
workWeek: () => getDateAfterWorkWeek(currentPeriodStartDate),
agenda: () => nextAgendaStart(currentPeriodStartDate, agendaDuration),
Expand All @@ -110,20 +130,28 @@ const getPeriodEndDate = (
return subMS(calculators[step]());
};

const getNextPeriodStartDate = (currentPeriodEndDate: Date, step: Step): Date => {
const getNextPeriodStartDate = (
currentPeriodEndDate: Date,
step: Step,
skippedDays: number[],
): Date => {
let date = addMS(currentPeriodEndDate);

if (step === 'workWeek') {
while (isWeekend(date)) {
date = nextDay(date);
}
} else if (step === 'week' && skippedDays.length > 0) {
date = getFirstVisibleDate(date, skippedDays, nextDay);
}

return date;
};

const getIntervalEndDate = (startDate: Date, options: IntervalOptions): Date => {
const { intervalCount, step, agendaDuration } = options;
const {
intervalCount, step, agendaDuration, skippedDays,
} = options;

let periodStartDate = new Date(startDate);
let periodEndDate = new Date(startDate);
Expand All @@ -132,9 +160,9 @@ const getIntervalEndDate = (startDate: Date, options: IntervalOptions): Date =>
for (let i = 0; i < intervalCount; i += 1) {
periodStartDate = nextPeriodStartDate;

periodEndDate = getPeriodEndDate(periodStartDate, step, agendaDuration ?? 0);
periodEndDate = getPeriodEndDate(periodStartDate, step, agendaDuration ?? 0, skippedDays);

nextPeriodStartDate = getNextPeriodStartDate(periodEndDate, step);
nextPeriodStartDate = getNextPeriodStartDate(periodEndDate, step, skippedDays);
}

return periodEndDate;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export interface IntervalOptions {
firstDayOfWeek?: number;
intervalCount: number;
agendaDuration?: number;
skippedDays: number[];
}

export interface HeaderCalendarOptions {
Expand Down
3 changes: 3 additions & 0 deletions packages/devextreme/js/__internal/scheduler/m_scheduler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,9 @@ class Scheduler extends SchedulerOptionsBaseWidget {
this.updateOption('header', 'views', this.views);
}
break;
case 'hiddenWeekDays':
this.repaint();
break;
case 'useDropDownViewSwitcher':
this.updateOption('header', name, value);
break;
Expand Down
Loading
Loading