Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
d2d91d7
Scheduler - Add ESLint naming-convention rule to forbid underscore-pr…
aleksei-semikozov Apr 2, 2026
d3a8e3c
Scheduler - Add legacy members to naming-convention allowlist
aleksei-semikozov Apr 2, 2026
4b12af6
Scheduler - Replace underscore-prefixed with private/protected modifiers
aleksei-semikozov Apr 2, 2026
d47eb48
Scheduler - Remove renamed methods from ESLint allowlist
aleksei-semikozov Apr 2, 2026
0e98856
Scheduler - Extract ESLint allowlist to separate file
aleksei-semikozov Apr 2, 2026
35f9a1e
Scheduler - Replace underscore-prefixed members in shaders with modif…
aleksei-semikozov Apr 2, 2026
ef97b50
Scheduler - Restore _shader to allowlist (used in m_work_space.ts)
aleksei-semikozov Apr 2, 2026
8d9569b
Scheduler - Replace _calculateCellIndex with protected calculateCellI…
aleksei-semikozov Apr 2, 2026
e782ffe
Scheduler - Replace underscore-prefixed in m_position_helper and m_form
aleksei-semikozov Apr 2, 2026
597938c
Scheduler - Replace underscore-prefixed members in header
aleksei-semikozov Apr 2, 2026
974db2b
Scheduler - Replace underscore-prefixed in calendar, popup, r1 compon…
aleksei-semikozov Apr 2, 2026
935aa84
Scheduler - Group legacy allowlist members by source file
aleksei-semikozov Apr 2, 2026
0545895
Scheduler - Replace internal underscore-prefixed members in m_schedul…
aleksei-semikozov Apr 2, 2026
38bf73f
Scheduler - Fix external usages of renamed members
aleksei-semikozov Apr 2, 2026
947e2b8
Scheduler - Revert _dataSource rename (inherited from Widget) and fix…
aleksei-semikozov Apr 2, 2026
28e5bff
Scheduler - Move _dataSource, _dataSourceLoadedCallback, _createActio…
aleksei-semikozov Apr 2, 2026
03256f0
Scheduler - Move _options to Widget overrides
aleksei-semikozov Apr 2, 2026
f53a6f0
Scheduler - Fix remaining _dataSourceChangedHandler reference
aleksei-semikozov Apr 2, 2026
67a96e2
Scheduler - Revert header, calendar, popup, r1, m_scheduler.ts rename…
aleksei-semikozov Apr 3, 2026
36b1564
Scheduler - Replace underscore-prefixed members in header
aleksei-semikozov Apr 3, 2026
b106c15
Scheduler - Replace underscore-prefixed members in m_calendar
aleksei-semikozov Apr 3, 2026
69dfa98
Scheduler - Replace underscore-prefixed in popup/form
aleksei-semikozov Apr 3, 2026
d159e26
Scheduler - Replace underscore-prefixed in r1 components
aleksei-semikozov Apr 3, 2026
03ce643
Scheduler - Rename internal m_scheduler members, move Widget-inherite…
aleksei-semikozov Apr 3, 2026
a4f0129
Scheduler - Add TODO comments to legacy allowlist members
aleksei-semikozov Apr 3, 2026
09915c4
Scheduler - Merge workspace overrides into legacy, group by source file
aleksei-semikozov Apr 3, 2026
2fba25b
Scheduler - Add protected modifier to renamed overrides
aleksei-semikozov Apr 3, 2026
857e020
Scheduler - Revert _getIntervalDuration rename, add to allowlist with…
aleksei-semikozov Apr 3, 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
130 changes: 130 additions & 0 deletions packages/devextreme/eslint-scheduler-allowlist.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
const schedulerDOMComponentOverrides = [
'_createActionByOption',
'_defaultOptionsRules',
'_dimensionChanged',
'_dispose',
'_disposed',
'_getDefaultOptions',
'_init',
'_initTemplates',
'_optionChanged',
'_setOptionsByReference',
'_useTemplates',
'_visibilityChanged',
];

const schedulerWidgetOverrides = [
'_activeStateUnit',
'_clean',
'_cleanFocusState',
'_eventBindingTarget',
'_fireContentReadyAction',
'_focusInHandler',
'_focusOutHandler',
'_focusTarget',
'_initMarkup',
'_keyboardHandler',
'_render',
'_renderContent',
'_renderFocusState',
'_renderFocusTarget',
'_supportedKeys',
'_toggleVisibility',
'_createAction',
'_createEventArgs',
'_dataSource',
'_dataSourceChangedHandler',
'_dataSourceOptions',
'_options',
];

const schedulerCollectionWidgetOverrides = [
'_cleanItemContainer',
'_clearDropDownItemsElements',
'_createItemByTemplate',
'_executeItemRenderAction',
'_filteredItems',
'_findItemElementByItem',
'_focusedItemIndexBeforeRender',
'_getItemContent',
'_itemClass',
'_itemClickHandler',
'_itemContainer',
'_moveFocus',
'_postprocessRenderItem',
'_processItemClick',
'_refreshActiveDescendant',
'_renderDirection',
'_renderItem',
'_sortedItems',
];

const schedulerR1Overrides = [
'_propsInfo',
'_value',
'_viewComponent',
];

const schedulerLegacyMembers = [
// workspaces/m_work_space.ts
'_$allDayPanel',
'_$dateTable',
'_$dateTableScrollableContent',
'_$flexContainer',
'_$groupTable',
'_$headerPanel',
'_$headerPanelContainer',
'_$thead',
'_dateTableScrollable',
'_getCellCount',
'_getGroupCount',
'_groupedStrategy',
'_isHorizontalGroupedWorkSpace',
'_shader',
'_sidebarScrollable',

// m_scheduler.ts
'_appointments',
'_compactAppointmentsHelper',
'_dataAccessors',
'_getDragBehavior',
'_isAppointmentBeingUpdated',
'_layoutManager',
'_workSpace',

// appointments/m_appointment_collection.ts
'_renderAppointmentTemplate',

// workspaces/view_model/m_view_data_generator.ts
'_getIntervalDuration',

// workspaces/m_virtual_scrolling.ts
'_renderGrid',

// appointment_popup/m_popup.ts
'_popup',

// appointment_popup/m_legacy_popup.ts
'_ignorePreventScrollEventsDeprecation',

// header/m_header.ts
'_useShortDateFormat',

// header/m_view_switcher.ts
'_wrapperClassExternal',

// utils/options/constants.ts, utils/options/types.ts
'_appointmentTooltipOffset',
'_draggingMode',
];

const schedulerMemberAllowlist = [
...schedulerDOMComponentOverrides,
...schedulerWidgetOverrides,
...schedulerCollectionWidgetOverrides,
...schedulerR1Overrides,
...schedulerLegacyMembers,
];

export const schedulerMemberAllowlistRegex =
`^(_|__esModule|${schedulerMemberAllowlist.map(s => s.replace(/\$/g, '\\$')).join('|')})$`;
31 changes: 29 additions & 2 deletions packages/devextreme/eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import customRules from './eslint_plugins/index.js';
import spellCheckConfig from 'eslint-config-devextreme/spell-check';
import typescriptConfig from 'eslint-config-devextreme/typescript';
import qunitConfig from 'eslint-config-devextreme/qunit';
import { schedulerMemberAllowlistRegex } from './eslint-scheduler-allowlist.mjs';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
Expand Down Expand Up @@ -556,7 +557,6 @@ export default [
selector: ['variable', 'function', 'parameter'],
format: null,
leadingUnderscore: 'forbid',
// allow only a single underscore identifier `_` to bypass this rule
filter: {
regex: '^_$',
match: false,
Expand All @@ -565,13 +565,40 @@ export default [
{
selector: 'memberLike',
format: null,
leadingUnderscore: 'allow',
leadingUnderscore: 'forbid',
filter: {
regex: schedulerMemberAllowlistRegex,
match: false,
},
},
],
'devextreme-custom/no-deferred': 'error',
'devextreme-custom/prefer-switch-true': ['error', { minBranches: 3 }],
},
},
// Temporarily allow underscore members in appointments/ (pending refactoring)
{
files: ['js/__internal/scheduler/appointments/**/*.ts?(x)'],
rules: {
'@typescript-eslint/naming-convention': [
'error',
{
selector: ['variable', 'function', 'parameter'],
format: null,
leadingUnderscore: 'forbid',
filter: {
regex: '^_$',
match: false,
},
},
{
selector: 'memberLike',
format: null,
leadingUnderscore: 'allow',
},
],
},
},
// Allow Deferred in m_* scheduler files only
{
files: ['js/__internal/scheduler/**/m_*.ts?(x)'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,18 +144,18 @@ export class AppointmentForm {

private readonly resourceManager!: ResourceManager;

private _dxForm?: dxForm;
private dxFormInstance?: dxForm;

private _recurrenceForm!: RecurrenceForm;
private recurrenceForm!: RecurrenceForm;

private _popup!: any;

private _$mainGroup?: dxElementWrapper;
private $mainGroup?: dxElementWrapper;

private _$recurrenceGroup?: dxElementWrapper;
private $recurrenceGroup?: dxElementWrapper;

get dxForm(): dxForm {
return this._dxForm as dxForm;
return this.dxFormInstance as dxForm;
}

private get dxPopup(): Popup {
Expand All @@ -168,7 +168,7 @@ export class AppointmentForm {

set readOnly(value: boolean) {
this.dxForm.option('readOnly', value);
this._recurrenceForm.setReadOnly(value);
this.recurrenceForm.setReadOnly(value);
}

get formData(): Record<string, any> {
Expand Down Expand Up @@ -210,10 +210,10 @@ export class AppointmentForm {
}

dispose(): void {
this._dxForm?.dispose();
this._dxForm = undefined;
if (this._recurrenceForm) {
this._recurrenceForm.dxForm = undefined;
this.dxFormInstance?.dispose();
this.dxFormInstance = undefined;
if (this.recurrenceForm) {
this.recurrenceForm.dxForm = undefined;
}
}

Expand All @@ -222,8 +222,8 @@ export class AppointmentForm {

const mainGroup = this.createMainFormGroup();

this._recurrenceForm = new RecurrenceForm(this.scheduler);
const recurrenceGroup = this._recurrenceForm.createRecurrenceFormGroup();
this.recurrenceForm = new RecurrenceForm(this.scheduler);
const recurrenceGroup = this.recurrenceForm.createRecurrenceFormGroup();

const items = [mainGroup, recurrenceGroup];

Expand Down Expand Up @@ -290,7 +290,7 @@ export class AppointmentForm {
}

if (isRecurrenceRuleChanged || startDateExpr === dataField) {
this._recurrenceForm.updateRecurrenceFormValues(
this.recurrenceForm.updateRecurrenceFormValues(
this.recurrenceRuleRaw,
this.startDate,
);
Expand All @@ -305,15 +305,15 @@ export class AppointmentForm {
}
},
onInitialized: (e): void => {
this._dxForm = e.component;
this._recurrenceForm.dxForm = this.dxForm;
this.dxFormInstance = e.component;
this.recurrenceForm.dxForm = this.dxForm;

onInitialized?.call(this, e);
},
onContentReady: (e): void => {
const $formElement = e.component.$element();
this._$mainGroup = $formElement.find(`.${CLASSES.mainGroup}`);
this._$recurrenceGroup = $formElement.find(`.${CLASSES.recurrenceGroup}`);
this.$mainGroup = $formElement.find(`.${CLASSES.mainGroup}`);
this.$recurrenceGroup = $formElement.find(`.${CLASSES.recurrenceGroup}`);

this.alignIconsWithEditors();

Expand Down Expand Up @@ -680,7 +680,7 @@ export class AppointmentForm {
if (e.value === repeatNeverValue) {
this.dxForm.updateData(recurrenceRuleExpr, '');
} else {
const currentRecurrenceRule = this._recurrenceForm.recurrenceRule.toString() ?? '';
const currentRecurrenceRule = this.recurrenceForm.recurrenceRule.toString() ?? '';
const recurrenceRule = new RecurrenceRule(currentRecurrenceRule, this.startDate);
recurrenceRule.frequency = e.value;
this.dxForm.updateData(recurrenceRuleExpr, recurrenceRule.toString());
Expand Down Expand Up @@ -883,16 +883,16 @@ export class AppointmentForm {
this.dxPopup.option('height', configuredHeight);
}

if (this._$mainGroup) {
this._$mainGroup.removeClass(CLASSES.mainHidden);
this._$mainGroup.removeAttr('inert');
if (this.$mainGroup) {
this.$mainGroup.removeClass(CLASSES.mainHidden);
this.$mainGroup.removeAttr('inert');

this.focusFirstFocusableInGroup(this._$mainGroup);
this.focusFirstFocusableInGroup(this.$mainGroup);
}

if (this._$recurrenceGroup) {
this._$recurrenceGroup.addClass(CLASSES.recurrenceHidden);
this._$recurrenceGroup.attr('inert', true);
if (this.$recurrenceGroup) {
this.$recurrenceGroup.addClass(CLASSES.recurrenceHidden);
this.$recurrenceGroup.attr('inert', true);
}

this._popup.updateToolbarForMainGroup();
Expand All @@ -913,23 +913,23 @@ export class AppointmentForm {
this.dxPopup.option('height', overlayHeight);
}

if (this._$mainGroup) {
this._$mainGroup.addClass(CLASSES.mainHidden);
this._$mainGroup.attr('inert', true);
if (this.$mainGroup) {
this.$mainGroup.addClass(CLASSES.mainHidden);
this.$mainGroup.attr('inert', true);
}

if (this._$recurrenceGroup) {
this._$recurrenceGroup.removeClass(CLASSES.recurrenceHidden);
this._$recurrenceGroup.removeAttr('inert');
if (this.$recurrenceGroup) {
this.$recurrenceGroup.removeClass(CLASSES.recurrenceHidden);
this.$recurrenceGroup.removeAttr('inert');

this.focusFirstFocusableInGroup(this._$recurrenceGroup);
this.focusFirstFocusableInGroup(this.$recurrenceGroup);
}

this._popup.updateToolbarForRecurrenceGroup();
}

saveRecurrenceValue(): void {
const { recurrenceRule } = this._recurrenceForm;
const { recurrenceRule } = this.recurrenceForm;
const { recurrenceRuleExpr } = this.scheduler.getDataAccessors().expr;

const recurrenceRuleSerialized = recurrenceRule.toString() ?? '';
Expand Down Expand Up @@ -1046,12 +1046,12 @@ export class AppointmentForm {
}

private updateAnimationOffset(): void {
if (!this._$mainGroup) {
if (!this.$mainGroup) {
return;
}

const formElement = this.dxForm.$element()[0];
const mainGroupElement = this._$mainGroup[0];
const mainGroupElement = this.$mainGroup[0];
const formRect = formElement.getBoundingClientRect();
const groupRect = mainGroupElement.getBoundingClientRect();
const topOffset = groupRect.top - formRect.top;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const POPUP_CONFIG = {
},
},
],
// TODO: legacy property
_ignorePreventScrollEventsDeprecation: true,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export class AppointmentPopup {

form: AppointmentForm;

// TODO: backing field for popup getter, cannot rename due to name conflict
private _popup?: dxPopup;

private customPopupOptions?: PopupProperties;
Expand Down
Loading
Loading