Skip to content

Commit b972d91

Browse files
Scheduler — Add ESLint naming-convention rule to forbid underscore-prefixed members (#33126)
1 parent b2fae5a commit b972d91

27 files changed

+394
-220
lines changed
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
const schedulerDOMComponentOverrides = [
2+
'_createActionByOption',
3+
'_defaultOptionsRules',
4+
'_dimensionChanged',
5+
'_dispose',
6+
'_disposed',
7+
'_getDefaultOptions',
8+
'_init',
9+
'_initTemplates',
10+
'_optionChanged',
11+
'_setOptionsByReference',
12+
'_useTemplates',
13+
'_visibilityChanged',
14+
];
15+
16+
const schedulerWidgetOverrides = [
17+
'_activeStateUnit',
18+
'_clean',
19+
'_cleanFocusState',
20+
'_eventBindingTarget',
21+
'_fireContentReadyAction',
22+
'_focusInHandler',
23+
'_focusOutHandler',
24+
'_focusTarget',
25+
'_initMarkup',
26+
'_keyboardHandler',
27+
'_render',
28+
'_renderContent',
29+
'_renderFocusState',
30+
'_renderFocusTarget',
31+
'_supportedKeys',
32+
'_toggleVisibility',
33+
'_createAction',
34+
'_createEventArgs',
35+
'_dataSource',
36+
'_dataSourceChangedHandler',
37+
'_dataSourceOptions',
38+
'_options',
39+
];
40+
41+
const schedulerCollectionWidgetOverrides = [
42+
'_cleanItemContainer',
43+
'_clearDropDownItemsElements',
44+
'_createItemByTemplate',
45+
'_executeItemRenderAction',
46+
'_filteredItems',
47+
'_findItemElementByItem',
48+
'_focusedItemIndexBeforeRender',
49+
'_getItemContent',
50+
'_itemClass',
51+
'_itemClickHandler',
52+
'_itemContainer',
53+
'_moveFocus',
54+
'_postprocessRenderItem',
55+
'_processItemClick',
56+
'_refreshActiveDescendant',
57+
'_renderDirection',
58+
'_renderItem',
59+
'_sortedItems',
60+
];
61+
62+
const schedulerR1Overrides = [
63+
'_propsInfo',
64+
'_value',
65+
'_viewComponent',
66+
];
67+
68+
const schedulerLegacyMembers = [
69+
// workspaces/m_work_space.ts
70+
'_$allDayPanel',
71+
'_$dateTable',
72+
'_$dateTableScrollableContent',
73+
'_$flexContainer',
74+
'_$groupTable',
75+
'_$headerPanel',
76+
'_$headerPanelContainer',
77+
'_$thead',
78+
'_dateTableScrollable',
79+
'_getCellCount',
80+
'_getGroupCount',
81+
'_groupedStrategy',
82+
'_isHorizontalGroupedWorkSpace',
83+
'_shader',
84+
'_sidebarScrollable',
85+
86+
// m_scheduler.ts
87+
'_appointments',
88+
'_compactAppointmentsHelper',
89+
'_dataAccessors',
90+
'_getDragBehavior',
91+
'_isAppointmentBeingUpdated',
92+
'_layoutManager',
93+
'_workSpace',
94+
95+
// appointments/m_appointment_collection.ts
96+
'_renderAppointmentTemplate',
97+
98+
// workspaces/view_model/m_view_data_generator.ts
99+
'_getIntervalDuration',
100+
101+
// workspaces/m_virtual_scrolling.ts
102+
'_renderGrid',
103+
104+
// appointment_popup/m_popup.ts
105+
'_popup',
106+
107+
// appointment_popup/m_legacy_popup.ts
108+
'_ignorePreventScrollEventsDeprecation',
109+
110+
// header/m_header.ts
111+
'_useShortDateFormat',
112+
113+
// header/m_view_switcher.ts
114+
'_wrapperClassExternal',
115+
116+
// utils/options/constants.ts, utils/options/types.ts
117+
'_appointmentTooltipOffset',
118+
'_draggingMode',
119+
];
120+
121+
const schedulerMemberAllowlist = [
122+
...schedulerDOMComponentOverrides,
123+
...schedulerWidgetOverrides,
124+
...schedulerCollectionWidgetOverrides,
125+
...schedulerR1Overrides,
126+
...schedulerLegacyMembers,
127+
];
128+
129+
export const schedulerMemberAllowlistRegex =
130+
`^(_|__esModule|${schedulerMemberAllowlist.map(s => s.replace(/\$/g, '\\$')).join('|')})$`;

packages/devextreme/eslint.config.mjs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import customRules from './eslint_plugins/index.js';
1717
import spellCheckConfig from 'eslint-config-devextreme/spell-check';
1818
import typescriptConfig from 'eslint-config-devextreme/typescript';
1919
import qunitConfig from 'eslint-config-devextreme/qunit';
20+
import { schedulerMemberAllowlistRegex } from './eslint-scheduler-allowlist.mjs';
2021

2122
const __filename = fileURLToPath(import.meta.url);
2223
const __dirname = path.dirname(__filename);
@@ -556,7 +557,6 @@ export default [
556557
selector: ['variable', 'function', 'parameter'],
557558
format: null,
558559
leadingUnderscore: 'forbid',
559-
// allow only a single underscore identifier `_` to bypass this rule
560560
filter: {
561561
regex: '^_$',
562562
match: false,
@@ -565,13 +565,40 @@ export default [
565565
{
566566
selector: 'memberLike',
567567
format: null,
568-
leadingUnderscore: 'allow',
568+
leadingUnderscore: 'forbid',
569+
filter: {
570+
regex: schedulerMemberAllowlistRegex,
571+
match: false,
572+
},
569573
},
570574
],
571575
'devextreme-custom/no-deferred': 'error',
572576
'devextreme-custom/prefer-switch-true': ['error', { minBranches: 3 }],
573577
},
574578
},
579+
// Temporarily allow underscore members in appointments/ (pending refactoring)
580+
{
581+
files: ['js/__internal/scheduler/appointments/**/*.ts?(x)'],
582+
rules: {
583+
'@typescript-eslint/naming-convention': [
584+
'error',
585+
{
586+
selector: ['variable', 'function', 'parameter'],
587+
format: null,
588+
leadingUnderscore: 'forbid',
589+
filter: {
590+
regex: '^_$',
591+
match: false,
592+
},
593+
},
594+
{
595+
selector: 'memberLike',
596+
format: null,
597+
leadingUnderscore: 'allow',
598+
},
599+
],
600+
},
601+
},
575602
// Allow Deferred in m_* scheduler files only
576603
{
577604
files: ['js/__internal/scheduler/**/m_*.ts?(x)'],

packages/devextreme/js/__internal/scheduler/appointment_popup/m_form.ts

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -144,18 +144,18 @@ export class AppointmentForm {
144144

145145
private readonly resourceManager!: ResourceManager;
146146

147-
private _dxForm?: dxForm;
147+
private dxFormInstance?: dxForm;
148148

149-
private _recurrenceForm!: RecurrenceForm;
149+
private recurrenceForm!: RecurrenceForm;
150150

151151
private _popup!: any;
152152

153-
private _$mainGroup?: dxElementWrapper;
153+
private $mainGroup?: dxElementWrapper;
154154

155-
private _$recurrenceGroup?: dxElementWrapper;
155+
private $recurrenceGroup?: dxElementWrapper;
156156

157157
get dxForm(): dxForm {
158-
return this._dxForm as dxForm;
158+
return this.dxFormInstance as dxForm;
159159
}
160160

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

169169
set readOnly(value: boolean) {
170170
this.dxForm.option('readOnly', value);
171-
this._recurrenceForm.setReadOnly(value);
171+
this.recurrenceForm.setReadOnly(value);
172172
}
173173

174174
get formData(): Record<string, any> {
@@ -210,10 +210,10 @@ export class AppointmentForm {
210210
}
211211

212212
dispose(): void {
213-
this._dxForm?.dispose();
214-
this._dxForm = undefined;
215-
if (this._recurrenceForm) {
216-
this._recurrenceForm.dxForm = undefined;
213+
this.dxFormInstance?.dispose();
214+
this.dxFormInstance = undefined;
215+
if (this.recurrenceForm) {
216+
this.recurrenceForm.dxForm = undefined;
217217
}
218218
}
219219

@@ -222,8 +222,8 @@ export class AppointmentForm {
222222

223223
const mainGroup = this.createMainFormGroup();
224224

225-
this._recurrenceForm = new RecurrenceForm(this.scheduler);
226-
const recurrenceGroup = this._recurrenceForm.createRecurrenceFormGroup();
225+
this.recurrenceForm = new RecurrenceForm(this.scheduler);
226+
const recurrenceGroup = this.recurrenceForm.createRecurrenceFormGroup();
227227

228228
const items = [mainGroup, recurrenceGroup];
229229

@@ -290,7 +290,7 @@ export class AppointmentForm {
290290
}
291291

292292
if (isRecurrenceRuleChanged || startDateExpr === dataField) {
293-
this._recurrenceForm.updateRecurrenceFormValues(
293+
this.recurrenceForm.updateRecurrenceFormValues(
294294
this.recurrenceRuleRaw,
295295
this.startDate,
296296
);
@@ -305,15 +305,15 @@ export class AppointmentForm {
305305
}
306306
},
307307
onInitialized: (e): void => {
308-
this._dxForm = e.component;
309-
this._recurrenceForm.dxForm = this.dxForm;
308+
this.dxFormInstance = e.component;
309+
this.recurrenceForm.dxForm = this.dxForm;
310310

311311
onInitialized?.call(this, e);
312312
},
313313
onContentReady: (e): void => {
314314
const $formElement = e.component.$element();
315-
this._$mainGroup = $formElement.find(`.${CLASSES.mainGroup}`);
316-
this._$recurrenceGroup = $formElement.find(`.${CLASSES.recurrenceGroup}`);
315+
this.$mainGroup = $formElement.find(`.${CLASSES.mainGroup}`);
316+
this.$recurrenceGroup = $formElement.find(`.${CLASSES.recurrenceGroup}`);
317317

318318
this.alignIconsWithEditors();
319319

@@ -680,7 +680,7 @@ export class AppointmentForm {
680680
if (e.value === repeatNeverValue) {
681681
this.dxForm.updateData(recurrenceRuleExpr, '');
682682
} else {
683-
const currentRecurrenceRule = this._recurrenceForm.recurrenceRule.toString() ?? '';
683+
const currentRecurrenceRule = this.recurrenceForm.recurrenceRule.toString() ?? '';
684684
const recurrenceRule = new RecurrenceRule(currentRecurrenceRule, this.startDate);
685685
recurrenceRule.frequency = e.value;
686686
this.dxForm.updateData(recurrenceRuleExpr, recurrenceRule.toString());
@@ -883,16 +883,16 @@ export class AppointmentForm {
883883
this.dxPopup.option('height', configuredHeight);
884884
}
885885

886-
if (this._$mainGroup) {
887-
this._$mainGroup.removeClass(CLASSES.mainHidden);
888-
this._$mainGroup.removeAttr('inert');
886+
if (this.$mainGroup) {
887+
this.$mainGroup.removeClass(CLASSES.mainHidden);
888+
this.$mainGroup.removeAttr('inert');
889889

890-
this.focusFirstFocusableInGroup(this._$mainGroup);
890+
this.focusFirstFocusableInGroup(this.$mainGroup);
891891
}
892892

893-
if (this._$recurrenceGroup) {
894-
this._$recurrenceGroup.addClass(CLASSES.recurrenceHidden);
895-
this._$recurrenceGroup.attr('inert', true);
893+
if (this.$recurrenceGroup) {
894+
this.$recurrenceGroup.addClass(CLASSES.recurrenceHidden);
895+
this.$recurrenceGroup.attr('inert', true);
896896
}
897897

898898
this._popup.updateToolbarForMainGroup();
@@ -913,23 +913,23 @@ export class AppointmentForm {
913913
this.dxPopup.option('height', overlayHeight);
914914
}
915915

916-
if (this._$mainGroup) {
917-
this._$mainGroup.addClass(CLASSES.mainHidden);
918-
this._$mainGroup.attr('inert', true);
916+
if (this.$mainGroup) {
917+
this.$mainGroup.addClass(CLASSES.mainHidden);
918+
this.$mainGroup.attr('inert', true);
919919
}
920920

921-
if (this._$recurrenceGroup) {
922-
this._$recurrenceGroup.removeClass(CLASSES.recurrenceHidden);
923-
this._$recurrenceGroup.removeAttr('inert');
921+
if (this.$recurrenceGroup) {
922+
this.$recurrenceGroup.removeClass(CLASSES.recurrenceHidden);
923+
this.$recurrenceGroup.removeAttr('inert');
924924

925-
this.focusFirstFocusableInGroup(this._$recurrenceGroup);
925+
this.focusFirstFocusableInGroup(this.$recurrenceGroup);
926926
}
927927

928928
this._popup.updateToolbarForRecurrenceGroup();
929929
}
930930

931931
saveRecurrenceValue(): void {
932-
const { recurrenceRule } = this._recurrenceForm;
932+
const { recurrenceRule } = this.recurrenceForm;
933933
const { recurrenceRuleExpr } = this.scheduler.getDataAccessors().expr;
934934

935935
const recurrenceRuleSerialized = recurrenceRule.toString() ?? '';
@@ -1046,12 +1046,12 @@ export class AppointmentForm {
10461046
}
10471047

10481048
private updateAnimationOffset(): void {
1049-
if (!this._$mainGroup) {
1049+
if (!this.$mainGroup) {
10501050
return;
10511051
}
10521052

10531053
const formElement = this.dxForm.$element()[0];
1054-
const mainGroupElement = this._$mainGroup[0];
1054+
const mainGroupElement = this.$mainGroup[0];
10551055
const formRect = formElement.getBoundingClientRect();
10561056
const groupRect = mainGroupElement.getBoundingClientRect();
10571057
const topOffset = groupRect.top - formRect.top;

packages/devextreme/js/__internal/scheduler/appointment_popup/m_legacy_popup.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ const POPUP_CONFIG = {
3636
},
3737
},
3838
],
39+
// TODO: legacy property
3940
_ignorePreventScrollEventsDeprecation: true,
4041
};
4142

packages/devextreme/js/__internal/scheduler/appointment_popup/m_popup.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export class AppointmentPopup {
3535

3636
form: AppointmentForm;
3737

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

4041
private customPopupOptions?: PopupProperties;

0 commit comments

Comments
 (0)