Skip to content

Commit 23eab53

Browse files
Ruslan FarkhutdinovRuslan FarkhutdinovCopilot
authored
RadioGroup: Improve types (DevExpress#33488)
Co-authored-by: Ruslan Farkhutdinov <ruslan.farkhutdinov@devexpress.com> Co-authored-by: Copilot <copilot@github.com>
1 parent c0c7acb commit 23eab53

3 files changed

Lines changed: 87 additions & 83 deletions

File tree

packages/devextreme/js/__internal/ui/radio_group/m_radio_button.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import type { dxElementWrapper } from '@js/core/renderer';
88
import $ from '@js/core/renderer';
99
import type { DxEvent } from '@js/events';
1010
import type { OptionChanged } from '@ts/core/widget/types';
11-
import type { EditorProperties } from '@ts/ui/editor/editor';
11+
import type { EditorProperties, ValueChangedEvent } from '@ts/ui/editor/editor';
1212
import Editor from '@ts/ui/editor/editor';
1313

1414
const RADIO_BUTTON_CLASS = 'dx-radiobutton';
@@ -24,11 +24,10 @@ class RadioButton extends Editor {
2424

2525
_clickAction?: (event?: Record<string, unknown>) => void;
2626

27-
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
28-
_supportedKeys(): Record<string, (e: KeyboardEvent) => void | boolean> {
29-
const click = function (e) {
27+
_supportedKeys(): Record<string, (e: KeyboardEvent) => void> {
28+
const click = (e: KeyboardEvent): void => {
3029
e.preventDefault();
31-
this._clickAction({ event: e });
30+
this._clickAction?.({ event: e });
3231
};
3332
return {
3433
...super._supportedKeys(),
@@ -70,10 +69,12 @@ class RadioButton extends Editor {
7069
}
7170

7271
_initMarkup(): void {
72+
const { value } = this.option();
73+
7374
super._initMarkup();
7475

7576
this._renderIcon();
76-
this._renderCheckedState(this.option('value'));
77+
this._renderCheckedState(value);
7778
this._renderClick();
7879
this.setAria('role', 'radio');
7980
}
@@ -85,7 +86,7 @@ class RadioButton extends Editor {
8586
this.$element().append(this._$icon);
8687
}
8788

88-
_renderCheckedState(checked): void {
89+
_renderCheckedState(checked: boolean): void {
8990
this.$element()
9091
.toggleClass(RADIO_BUTTON_CHECKED_CLASS, checked)
9192
.find(`.${RADIO_BUTTON_ICON_CLASS}`)
@@ -94,10 +95,9 @@ class RadioButton extends Editor {
9495
}
9596

9697
_renderClick(): void {
97-
// @ts-expect-error ts-error
98-
const eventName = addNamespace(clickEventName, this.NAME);
98+
const eventName = addNamespace(clickEventName, this.NAME ?? '');
9999

100-
this._clickAction = this._createAction((args): void => {
100+
this._clickAction = this._createAction((args: { event: ValueChangedEvent }): void => {
101101
this._clickHandler(args.event);
102102
});
103103

@@ -107,7 +107,7 @@ class RadioButton extends Editor {
107107
});
108108
}
109109

110-
_clickHandler(e): void {
110+
_clickHandler(e: ValueChangedEvent): void {
111111
this._saveValueChangeEvent(e);
112112
this.option('value', true);
113113
this._saveValueChangeEvent(undefined);

packages/devextreme/js/__internal/ui/radio_group/m_radio_collection.ts

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ import Guid from '@js/core/guid';
22
import type { dxElementWrapper } from '@js/core/renderer';
33
import $ from '@js/core/renderer';
44
import { deferRender } from '@js/core/utils/common';
5-
import { extend } from '@js/core/utils/extend';
5+
import type { DxEvent } from '@js/events';
6+
import type { CollectionWidgetItem as CollectionWidgetItemProperties } from '@js/ui/collection/ui.collection_widget.base';
67
import DataExpressionMixin from '@js/ui/editor/ui.data_expression';
7-
import type { CollectionWidgetBaseProperties } from '@ts/ui/collection/collection_widget.base';
8+
import type { CollectionWidgetBaseProperties, PostprocessRenderItemInfo } from '@ts/ui/collection/collection_widget.base';
89
import CollectionWidget from '@ts/ui/collection/collection_widget.edit';
910

1011
const RADIO_BUTTON_CHECKED_CLASS = 'dx-radiobutton-checked';
@@ -29,10 +30,12 @@ class RadioCollection extends CollectionWidget<Properties> {
2930
}
3031

3132
_getDefaultOptions(): Properties {
32-
const defaultOptions = super._getDefaultOptions();
33-
34-
// @ts-expect-error
35-
return extend(defaultOptions, DataExpressionMixin._dataExpressionDefaultOptions());
33+
return {
34+
...super._getDefaultOptions(),
35+
// @ts-expect-error DataExpressionMixin._dataExpressionDefaultOptions()
36+
// should be added to the type
37+
...DataExpressionMixin._dataExpressionDefaultOptions(),
38+
} as Properties;
3639
}
3740

3841
_initMarkup(): void {
@@ -59,7 +62,7 @@ class RadioCollection extends CollectionWidget<Properties> {
5962
return $target;
6063
}
6164

62-
_postprocessRenderItem(args): void {
65+
_postprocessRenderItem(args: PostprocessRenderItemInfo<CollectionWidgetItemProperties>): void {
6366
const { itemData, itemElement } = args;
6467
const { html } = itemData;
6568

@@ -126,22 +129,21 @@ class RadioCollection extends CollectionWidget<Properties> {
126129
this._renderContent();
127130
}
128131

129-
_supportedKeys(): Record<string, (e: KeyboardEvent) => void> {
132+
_supportedKeys(): Record<string, (e: DxEvent<KeyboardEvent>) => void> {
130133
const parent = super._supportedKeys();
131134

132-
return extend({}, parent, {
133-
enter(e) {
135+
return {
136+
...parent,
137+
enter(e: DxEvent<KeyboardEvent>): void {
134138
e.preventDefault();
135-
// @ts-expect-error
136-
return parent.enter.apply(this, arguments);
139+
parent.enter?.apply(this, [e]);
137140
},
138141

139-
space(e) {
142+
space(e: DxEvent<KeyboardEvent>): void {
140143
e.preventDefault();
141-
// @ts-expect-error
142-
return parent.space.apply(this, arguments);
144+
parent.space?.apply(this, [e]);
143145
},
144-
});
146+
};
145147
}
146148

147149
_itemElements(): dxElementWrapper {

packages/devextreme/js/__internal/ui/radio_group/m_radio_group.ts

Lines changed: 58 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ import type { dxElementWrapper } from '@js/core/renderer';
55
import $ from '@js/core/renderer';
66
import type { DeferredObj } from '@js/core/utils/deferred';
77
import { Deferred } from '@js/core/utils/deferred';
8-
import { extend } from '@js/core/utils/extend';
98
import { isDefined } from '@js/core/utils/type';
9+
import type { ItemInfo, NativeEventInfo } from '@js/events';
10+
import type { ItemLike } from '@js/ui/collection/ui.collection_widget.base';
1011
import DataExpressionMixin from '@js/ui/editor/ui.data_expression';
1112
import type { Properties } from '@js/ui/radio_group';
1213
import type { OptionChanged } from '@ts/core/widget/types';
13-
import type { EditorProperties, UnresolvedEvents } from '@ts/ui/editor/editor';
14+
import type { EditorProperties, UnresolvedEvents, ValueChangedEvent } from '@ts/ui/editor/editor';
1415
import Editor from '@ts/ui/editor/editor';
1516

1617
import RadioCollection from './m_radio_collection';
@@ -28,21 +29,18 @@ interface RadioGroupProperties extends Properties,
2829
class RadioGroup extends Editor<RadioGroupProperties> {
2930
private _radios?: RadioCollection;
3031

31-
private _areRadiosCreated!: DeferredObj<unknown>;
32+
private _areRadiosCreated!: DeferredObj<void>;
3233

3334
private _$submitElement!: dxElementWrapper;
3435

35-
// eslint-disable-next-line class-methods-use-this
3636
_dataSourceOptions(): { paginate: boolean } {
3737
return { paginate: false };
3838
}
3939

40-
// eslint-disable-next-line class-methods-use-this
4140
protected _activeStateUnit(): string {
4241
return `.${RADIO_BUTTON_CLASS}`;
4342
}
4443

45-
// eslint-disable-next-line class-methods-use-this
4644
protected _feedbackHideTimeout(): number {
4745
return RADIO_FEEDBACK_HIDE_TIMEOUT;
4846
}
@@ -63,32 +61,38 @@ class RadioGroup extends Editor<RadioGroupProperties> {
6361
}]);
6462
}
6563

66-
// @ts-expect-error
64+
// @ts-expect-error widget.ts _fireContentReadyAction should accept an optional `force` parameter
6765
_fireContentReadyAction(force: boolean): void {
68-
force && super._fireContentReadyAction();
66+
if (!force) {
67+
return;
68+
}
69+
70+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
71+
super._fireContentReadyAction();
6972
}
7073

71-
_focusTarget() {
74+
_focusTarget(): dxElementWrapper {
7275
return this.$element();
7376
}
7477

75-
_getAriaTarget() {
78+
_getAriaTarget(): dxElementWrapper {
7679
return this.$element();
7780
}
7881

79-
_getDefaultOptions() {
80-
const defaultOptions = super._getDefaultOptions();
81-
82-
// @ts-expect-error
83-
return extend(defaultOptions, extend(DataExpressionMixin._dataExpressionDefaultOptions(), {
82+
_getDefaultOptions(): RadioGroupProperties {
83+
return {
84+
...super._getDefaultOptions(),
85+
// @ts-expect-error DataExpressionMixin should expose _dataExpressionDefaultOptions as a
86+
// typed static method
87+
...DataExpressionMixin._dataExpressionDefaultOptions(),
8488
hoverStateEnabled: true,
8589
activeStateEnabled: true,
8690
layout: 'vertical',
87-
}));
91+
} as RadioGroupProperties;
8892
}
8993

90-
_getItemValue(item) {
91-
// @ts-expect-error
94+
_getItemValue(item: ItemLike): unknown {
95+
// @ts-expect-error valueGetter is injected by DataExpressionMixin
9296
return this._valueGetter ? this._valueGetter(item) : item.text;
9397
}
9498

@@ -99,7 +103,7 @@ class RadioGroup extends Editor<RadioGroupProperties> {
99103
_init(): void {
100104
super._init();
101105

102-
// @ts-expect-error
106+
// @ts-expect-error _initDataExpressions is injected by DataExpressionMixin
103107
this._initDataExpressions();
104108
}
105109

@@ -112,28 +116,29 @@ class RadioGroup extends Editor<RadioGroupProperties> {
112116
super._initMarkup();
113117
}
114118

115-
_itemClickHandler({ itemElement, event, itemData }): void {
116-
// @ts-expect-error
117-
if (this.itemElements().is(itemElement)) {
119+
_itemClickHandler({ itemElement, event, itemData }:
120+
NativeEventInfo<RadioCollection, MouseEvent | PointerEvent> & ItemInfo): void {
121+
if (this.itemElements()?.is($(itemElement))) {
122+
const { value } = this.option();
118123
const newValue = this._getItemValue(itemData);
119124

120-
if (newValue !== this.option('value')) {
121-
this._saveValueChangeEvent(event);
125+
if (newValue !== value) {
126+
this._saveValueChangeEvent(event as unknown as ValueChangedEvent);
122127
this.option('value', newValue);
123128
}
124129
}
125130
}
126131

127-
_getSelectedItemKeys(value) {
128-
// @ts-expect-error
129-
const isNullSelectable = this.option('valueExpr') !== 'this';
130-
const shouldSelectValue = isNullSelectable && value === null || isDefined(value);
132+
_getSelectedItemKeys(value: unknown): unknown[] {
133+
const { valueExpr } = this.option();
134+
const isNullSelectable = valueExpr !== 'this';
135+
const shouldSelectValue = (isNullSelectable && value === null) || isDefined(value);
131136

132137
return shouldSelectValue ? [value] : [];
133138
}
134139

135-
_setSelection(currentValue): void {
136-
// @ts-expect-error
140+
_setSelection(currentValue: unknown): void {
141+
// @ts-expect-error _unwrappedValue is injected by DataExpressionMixin
137142
const value = this._unwrappedValue(currentValue);
138143
this._setCollectionWidgetOption('selectedItemKeys', this._getSelectedItemKeys(value));
139144
}
@@ -152,7 +157,7 @@ class RadioGroup extends Editor<RadioGroupProperties> {
152157

153158
_optionChanged(args: OptionChanged<RadioGroupProperties>): void {
154159
const { name, value } = args;
155-
// @ts-expect-error
160+
// @ts-expect-error _dataExpressionOptionChanged is injected by DataExpressionMixin
156161
this._dataExpressionOptionChanged(args);
157162

158163
switch (name) {
@@ -169,7 +174,7 @@ class RadioGroup extends Editor<RadioGroupProperties> {
169174
this._setCollectionWidgetOption(name, value);
170175
break;
171176
case 'valueExpr':
172-
// @ts-expect-error
177+
// @ts-expect-error _getCollectionKeyExpr is injected by DataExpressionMixin
173178
this._setCollectionWidgetOption('keyExpr', this._getCollectionKeyExpr());
174179
break;
175180
case 'value':
@@ -178,7 +183,7 @@ class RadioGroup extends Editor<RadioGroupProperties> {
178183
super._optionChanged(args);
179184
break;
180185
case 'items':
181-
this._setSelection(this.option('value'));
186+
this._setSelection(this.option().value);
182187
break;
183188
case 'itemTemplate':
184189
case 'displayExpr':
@@ -220,21 +225,20 @@ class RadioGroup extends Editor<RadioGroupProperties> {
220225
onInitialized: ({ component }) => {
221226
this._radios = component;
222227
},
223-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
224-
onContentReady: (e) => {
228+
onContentReady: () => {
225229
this._fireContentReadyAction(true);
226230
},
227-
// @ts-expect-error
228231
onItemClick: this._itemClickHandler.bind(this),
229232
displayExpr,
230233
accessKey,
231-
// @ts-expect-error
234+
// @ts-expect-error _dataSource is injected by DataExpressionMixin
232235
dataSource: this._dataSource,
233236
focusStateEnabled,
234237
itemTemplate,
235-
// @ts-expect-error
238+
// @ts-expect-error _getCollectionKeyExpr is injected by DataExpressionMixin
236239
keyExpr: this._getCollectionKeyExpr(),
237240
noDataText: '',
241+
// @ts-expect-error scrollingEnabled is absent from CollectionWidgetProperties
238242
scrollingEnabled: false,
239243
selectByClick: false,
240244
selectionMode: 'single',
@@ -254,35 +258,33 @@ class RadioGroup extends Editor<RadioGroupProperties> {
254258

255259
_setOptionsByReference(): void {
256260
super._setOptionsByReference();
257-
extend(this._optionsByReference, { value: true });
261+
this._optionsByReference = { ...this._optionsByReference, value: true };
258262
}
259263

260264
_setSubmitValue(value?: unknown): void {
261-
value = value ?? this.option('value');
262-
// @ts-expect-error
263-
const submitValue = this.option('valueExpr') === 'this'
264-
// @ts-expect-error
265-
? this._displayGetter(value)
266-
: value;
267-
268-
this._$submitElement.val(submitValue);
269-
}
265+
const { valueExpr, value: optionValue } = this.option();
266+
const resolvedValue = value ?? optionValue;
267+
268+
const submitValue = valueExpr === 'this'
269+
// @ts-expect-error _displayGetter is injected by DataExpressionMixin
270+
? this._displayGetter(resolvedValue)
271+
: resolvedValue;
270272

271-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
272-
_setCollectionWidgetOption(name: string, value: unknown): void {
273-
// @ts-expect-error
273+
this._$submitElement.val(submitValue as string | number | undefined);
274+
}
274275

275-
this._areRadiosCreated.done(this._setWidgetOption.bind(this, '_radios', arguments));
276+
_setCollectionWidgetOption(...args: [string, unknown]): void {
277+
// @ts-expect-error widget._setWidgetOption args should be typed as ArrayLike<unknown>
278+
this._areRadiosCreated.done(this._setWidgetOption.bind(this, '_radios', args));
276279
}
277280

278281
_updateItemsSize(): void {
279-
const { layout } = this.option();
282+
const { layout, items } = this.option();
280283

281284
if (layout === 'horizontal') {
282285
this.itemElements()?.css('height', 'auto');
283286
} else {
284-
// @ts-expect-error
285-
const itemsCount = this.option('items').length;
287+
const itemsCount = (items ?? []).length;
286288

287289
this.itemElements()?.css('height', `${100 / itemsCount}%`);
288290
}
@@ -296,7 +298,7 @@ class RadioGroup extends Editor<RadioGroupProperties> {
296298
return this._radios?._itemElements();
297299
}
298300
}
299-
// @ts-expect-error
301+
// @ts-expect-error Widget base class should define a typed static include() method
300302
RadioGroup.include(DataExpressionMixin);
301303

302304
registerComponent('dxRadioGroup', RadioGroup);

0 commit comments

Comments
 (0)