-
Notifications
You must be signed in to change notification settings - Fork 665
Scheduler: refactor Header module #32258
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 26_1
Are you sure you want to change the base?
Conversation
This reverts commit 2ea8c28.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request refactors the Scheduler Header module to improve TypeScript type safety. The changes add strict TypeScript rules for the scheduler/header directory, remove the separate today.ts file by consolidating its functionality into m_date_navigator.ts, and add comprehensive type annotations throughout the module.
Changes:
- Added strict TypeScript ESLint rules for scheduler/header files including explicit return types and no-explicit-any
- Consolidated
today.tsfunctionality intom_date_navigator.tsand removed the separate file - Converted all methods in header-related files to have explicit return type annotations
- Changed async methods in
SchedulerCalendarandSchedulerHeaderto return Promises - Updated import paths for Widget and Calendar components to use TypeScript module paths
- Added proper typing for event handlers, options interfaces, and method parameters
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
packages/devextreme/eslint.config.mjs |
Added strict TypeScript rules configuration for scheduler/header files |
packages/devextreme/js/__internal/scheduler/m_scheduler.ts |
Added return type and type assertions for getFirstDayOfWeek method |
packages/devextreme/js/__internal/scheduler/header/types.ts |
Added new interfaces HeaderCalendarOptions and EventMapHandler type |
packages/devextreme/js/__internal/scheduler/header/today.ts |
Removed file (functionality moved to m_date_navigator.ts) |
packages/devextreme/js/__internal/scheduler/header/m_header.ts |
Added explicit type annotations, made calendar methods async, updated imports |
packages/devextreme/js/__internal/scheduler/header/m_calendar.ts |
Converted to use strict typing, made show/hide async, improved type safety |
packages/devextreme/js/__internal/scheduler/header/m_date_navigator.ts |
Moved getTodayButtonOptions from deleted file, added type annotations |
packages/devextreme/js/__internal/scheduler/header/m_view_switcher.ts |
Added explicit return types and improved theme API usage |
packages/devextreme/js/__internal/scheduler/header/m_utils.ts |
Added return type annotations and minor refactoring improvements |
packages/devextreme/js/__internal/scheduler/header/m_date_navigator.test.ts |
Updated type assertions in tests |
| || (toolbarOptions.visible === undefined && toolbarOptions.items.length); | ||
| const { toolbar } = this.option(); | ||
| const isHeaderShown = toolbar.visible | ||
| ?? (toolbar.visible === undefined && toolbar.items.length); |
Copilot
AI
Jan 22, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic for determining 'isHeaderShown' is incorrect. The expression uses the nullish coalescing operator '??' but evaluates 'toolbar.visible' twice. When 'toolbar.visible' is defined, the right-hand side of '??' will never be evaluated. The condition should be: 'toolbar.visible ?? toolbar.items.length > 0' or the original logic should be preserved with proper boolean conversion: 'toolbar.visible !== undefined ? toolbar.visible : toolbar.items.length > 0'.
| ?? (toolbar.visible === undefined && toolbar.items.length); | |
| ?? toolbar.items.length > 0; |
| sourceType: 'script', | ||
| parserOptions: { | ||
| project: './tsconfig.json', | ||
| tsconfigRootDir: `${__dirname}/js/__internal`, |
Copilot
AI
Jan 22, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The 'tsconfigRootDir' path appears to be incorrect. It's set to '${__dirname}/js/__internal', but the tsconfig.json is located at the package root. This should likely be just '__dirname' or the path should correctly resolve to where tsconfig.json exists. This misconfiguration could cause TypeScript parsing issues in the ESLint rules.
| @@ -1,4 +1,3 @@ | |||
| import '@js/ui/button_group'; | |||
| import '@js/ui/drop_down_button'; | |||
Copilot
AI
Jan 22, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The side-effect import 'import "@js/ui/button_group"' was removed from this file, but ButtonGroup widgets are still being used in the dateNavigator and viewSwitcher toolbar items (via 'widget: "dxButtonGroup"'). This import is necessary to ensure the ButtonGroup component is registered and available when these toolbar items are rendered. Without this import, the ButtonGroup widgets may not be properly initialized.
| import '@js/ui/drop_down_button'; | |
| import '@js/ui/drop_down_button'; | |
| import '@js/ui/button_group'; |
| return subMS(date); | ||
| }; | ||
|
|
||
| const getNextPeriodStartDate = (currentPeriodEndDate: Date, step): Date => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are forget here step: Step
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wrote about this in DM
|
|
||
| const getPeriodEndDate = (currentPeriodStartDate: Date, step: Step, agendaDuration: number): Date => { | ||
| let date; | ||
| const getPeriodEndDate = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can rewrite simple without eslint issues and without create redundant default value;
let date: Date = new Date();
const getPeriodEndDate = (
currentPeriodStartDate: Date,
step: Step,
agendaDuration: number,
): Date => {
const calculators: Record<Step, () => Date> = {
day: () => nextDay(currentPeriodStartDate),
week: () => nextWeek(currentPeriodStartDate),
month: () => nextMonth(currentPeriodStartDate),
workWeek: () => getDateAfterWorkWeek(currentPeriodStartDate),
agenda: () => nextAgendaStart(currentPeriodStartDate, agendaDuration),
};
return subMS(calculators[step]());
};
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will apply this change to function
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Sergei Burkatskii <sergei.burkatskii@devexpress.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.
| onValueChanged: async (e) => { | ||
| this._updateCurrentDate(e.value); | ||
| this._calendar.hide(); | ||
| await this._calendar?.hide(); | ||
| }, |
Copilot
AI
Jan 22, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The onValueChanged callback doesn't need to be async since _updateCurrentDate is synchronous and the await expression is the last statement. Either make this a regular function with this._calendar?.hide() (without await), or if the async nature is intentional for future changes, add a comment explaining why.
| case 'week': | ||
| case 'month': | ||
| return getPeriodStart(date, step, false, firstDayOfWeek); | ||
| return getPeriodStart(date, step, false, firstDayOfWeek) as Date; |
Copilot
AI
Jan 22, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type assertion as Date suggests that getPeriodStart may return undefined or another type. Consider adding a default case that returns a valid Date instead of relying on type assertion, or update the return type of getIntervalStartDate to match what getPeriodStart actually returns.
| this._calendar?._keyboardHandler(opts); | ||
| return true; |
Copilot
AI
Jan 22, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method always returns true regardless of whether _calendar exists or what _calendar._keyboardHandler returns. This could mask keyboard handling failures. Consider returning false when _calendar is undefined, or returning the result from _calendar._keyboardHandler(opts) if it returns a boolean.
| this._calendar?._keyboardHandler(opts); | |
| return true; | |
| const handled = this._calendar?._keyboardHandler(opts); | |
| if (typeof handled === 'boolean') { | |
| return handled; | |
| } | |
| return !!this._calendar; |
| const { | ||
| value, min, max, firstDayOfWeek, focusStateEnabled, tabIndex, onValueChanged, | ||
| } = this.option(); | ||
| return { | ||
| value: this.option('value'), | ||
| min: this.option('min'), | ||
| max: this.option('max'), | ||
| firstDayOfWeek: this.option('firstDayOfWeek'), | ||
| focusStateEnabled: this.option('focusStateEnabled'), | ||
| onValueChanged: this.option('onValueChanged'), | ||
| value, | ||
| min, | ||
| max, | ||
| firstDayOfWeek, | ||
| focusStateEnabled, | ||
| tabIndex, | ||
| onValueChanged, | ||
| // @ts-expect-error skipFocusCheck is an internal Calendar property |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another way to do this is to declare two functions in SchedulerCalendar:
setOption<K extends keyof HeaderCalendarOptions>(
optionName: K,
value: HeaderCalendarOptions[K],
): void {
this.option(optionName, value);
}
getOption<K extends keyof HeaderCalendarOptions>(
optionName: K,
): HeaderCalendarOptions[K] {
return this.option(optionName) as unknown as HeaderCalendarOptions[K];
}and use them in the code instead (also in other modules like m_header.ts), this way Component.option() will be used as intended (there's some caching logic behind optionManger), though, through a wrapper functions.
However I don't push applying this changes, but rather I would like to discuss how it should be done in the future.
From my perspective, it's a problem that basic Component doesn't support typed values that should be resolved. Otherwise it feels a little bit messy to refactor code to ts, with structures like this.
But to push this change to Component may require some time, and we need to decide what approach to use temporarily.
What do you think? @aleksei-semikozov @sjbur
| } | ||
|
|
||
| _createOverlayContent() { | ||
| _createOverlayContent(): dxElementWrapper { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like to ask to add function modifiers like public and private, if you don't mind
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.
No description provided.