Skip to content

Conversation

@sjbur
Copy link
Contributor

@sjbur sjbur commented Jan 21, 2026

No description provided.

@sjbur sjbur self-assigned this Jan 21, 2026
@sjbur sjbur added the 26_1 label Jan 21, 2026
@sjbur sjbur marked this pull request as ready for review January 22, 2026 12:07
@sjbur sjbur requested a review from a team as a code owner January 22, 2026 12:07
Copilot AI review requested due to automatic review settings January 22, 2026 12:07
Copy link
Contributor

Copilot AI left a 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.ts functionality into m_date_navigator.ts and removed the separate file
  • Converted all methods in header-related files to have explicit return type annotations
  • Changed async methods in SchedulerCalendar and SchedulerHeader to 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);
Copy link

Copilot AI Jan 22, 2026

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'.

Suggested change
?? (toolbar.visible === undefined && toolbar.items.length);
?? toolbar.items.length > 0;

Copilot uses AI. Check for mistakes.
sourceType: 'script',
parserOptions: {
project: './tsconfig.json',
tsconfigRootDir: `${__dirname}/js/__internal`,
Copy link

Copilot AI Jan 22, 2026

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.

Copilot uses AI. Check for mistakes.
@@ -1,4 +1,3 @@
import '@js/ui/button_group';
import '@js/ui/drop_down_button';
Copy link

Copilot AI Jan 22, 2026

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.

Suggested change
import '@js/ui/drop_down_button';
import '@js/ui/drop_down_button';
import '@js/ui/button_group';

Copilot uses AI. Check for mistakes.
return subMS(date);
};

const getNextPeriodStartDate = (currentPeriodEndDate: Date, step): Date => {
Copy link
Contributor

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

Copy link
Contributor Author

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 = (
Copy link
Contributor

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]());
};

Copy link
Contributor Author

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>
Copilot AI review requested due to automatic review settings January 22, 2026 14:07
Copy link
Contributor

Copilot AI left a 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.

Comment on lines +244 to 247
onValueChanged: async (e) => {
this._updateCurrentDate(e.value);
this._calendar.hide();
await this._calendar?.hide();
},
Copy link

Copilot AI Jan 22, 2026

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.

Copilot uses AI. Check for mistakes.
case 'week':
case 'month':
return getPeriodStart(date, step, false, firstDayOfWeek);
return getPeriodStart(date, step, false, firstDayOfWeek) as Date;
Copy link

Copilot AI Jan 22, 2026

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.

Copilot uses AI. Check for mistakes.
Comment on lines 31 to +38
this._calendar?._keyboardHandler(opts);
return true;
Copy link

Copilot AI Jan 22, 2026

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.

Suggested change
this._calendar?._keyboardHandler(opts);
return true;
const handled = this._calendar?._keyboardHandler(opts);
if (typeof handled === 'boolean') {
return handled;
}
return !!this._calendar;

Copilot uses AI. Check for mistakes.
Comment on lines +118 to +129
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
Copy link
Contributor

@Tucchhaa Tucchhaa Jan 23, 2026

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 {
Copy link
Contributor

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

Copilot AI review requested due to automatic review settings January 23, 2026 09:27
Copy link
Contributor

Copilot AI left a 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants