Skip to content

Commit bd08e78

Browse files
Editors: get rid of the ctor overrides (#34934)
1 parent c4ccedc commit bd08e78

8 files changed

Lines changed: 211 additions & 30 deletions

File tree

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import {
2+
afterEach, describe, expect, it,
3+
} from '@jest/globals';
4+
import { data } from '@js/core/element_data';
5+
import $ from '@js/core/renderer';
6+
import Validator from '@ts/ui/validator';
7+
8+
import type { EditorProperties } from '../editor';
9+
import Editor from '../editor';
10+
11+
const VALIDATION_TARGET = 'dx-validation-target';
12+
13+
interface ValidationRequestArgs {
14+
value: unknown;
15+
editor: unknown;
16+
}
17+
18+
const disposables: { dispose: () => void }[] = [];
19+
20+
const createEditor = (options: Partial<EditorProperties> = {}): Editor => {
21+
const element = $('<div>').appendTo(document.body).get(0) as HTMLElement;
22+
// @ts-expect-error DOMComponent constructor is not typed for direct instantiation
23+
const instance: Editor = new Editor(element, options);
24+
25+
disposables.push(instance);
26+
27+
return instance;
28+
};
29+
30+
describe('Editor initialization', () => {
31+
afterEach(() => {
32+
disposables.forEach((instance) => instance.dispose());
33+
disposables.length = 0;
34+
document.body.innerHTML = '';
35+
});
36+
37+
it('lets a validator attach while the editor initialization is deferred by beginUpdate', () => {
38+
const instance = createEditor({
39+
onInitializing(this: Editor): void {
40+
this.beginUpdate();
41+
},
42+
} as Partial<EditorProperties>);
43+
44+
expect(() => {
45+
// @ts-expect-error DOMComponent constructor is not typed for direct instantiation
46+
const validator: Validator = new Validator(instance.$element().get(0), {});
47+
48+
disposables.push(validator);
49+
}).not.toThrow();
50+
51+
instance.endUpdate();
52+
});
53+
54+
it('marks the element as a validation target with the validation state ready', () => {
55+
const instance = createEditor();
56+
const element = instance.$element().get(0) as HTMLElement;
57+
58+
expect(data(element, VALIDATION_TARGET)).toBe(instance);
59+
expect(instance.validationRequest).toBeDefined();
60+
expect(instance.showValidationMessageTimeout).toBeUndefined();
61+
});
62+
63+
it('fires validationRequest when the value changes', () => {
64+
const instance = createEditor({ value: 'initial' });
65+
const calls: ValidationRequestArgs[] = [];
66+
67+
instance.validationRequest.add((args: ValidationRequestArgs) => {
68+
calls.push(args);
69+
});
70+
instance.option('value', 'changed');
71+
72+
expect(calls).toHaveLength(1);
73+
expect(calls[0].value).toBe('changed');
74+
expect(calls[0].editor).toBe(instance);
75+
});
76+
});

‎packages/devextreme/js/__internal/ui/editor/editor.ts‎

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -92,15 +92,10 @@ class Editor<
9292
return instance instanceof Editor;
9393
}
9494

95-
ctor(element: Element, options: TProperties): void {
96-
this.showValidationMessageTimeout = undefined;
97-
this.validationRequest = Callbacks();
98-
99-
super.ctor(element, options);
100-
}
101-
10295
_createElement(element: Element): void {
10396
super._createElement(element);
97+
this.showValidationMessageTimeout = undefined;
98+
this.validationRequest = Callbacks();
10499
const $element = this.$element();
105100
if ($element) {
106101
data($element[0], VALIDATION_TARGET, this);

‎packages/devextreme/js/__internal/ui/map/map.ts‎

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -120,13 +120,11 @@ class Map extends Widget<MapProperties> {
120120
]);
121121
}
122122

123-
ctor(element: Element, options: MapProperties): void {
124-
super.ctor(element, options);
123+
_initOptions(options: MapProperties): void {
124+
super._initOptions(options);
125125

126-
if (options) {
127-
if ('provider' in options && options.provider === 'bing') {
128-
this._logDeprecatedBingProvider();
129-
}
126+
if ('provider' in options && options.provider === 'bing') {
127+
this._logDeprecatedBingProvider();
130128
}
131129
}
132130

‎packages/devextreme/js/__internal/ui/overlay/overlay.ts‎

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -380,13 +380,11 @@ class Overlay<
380380
return this._$content;
381381
}
382382

383-
ctor(element: Element, options: TProperties): void {
384-
super.ctor(element, options);
383+
_initOptions(options: TProperties): void {
384+
super._initOptions(options);
385385

386-
if (options) {
387-
if ('preventScrollEvents' in options && !options._ignorePreventScrollEventsDeprecation) {
388-
this._logDeprecatedPreventScrollEventsInfo();
389-
}
386+
if ('preventScrollEvents' in options && !options._ignorePreventScrollEventsDeprecation) {
387+
this._logDeprecatedPreventScrollEventsInfo();
390388
}
391389
}
392390

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import {
2+
afterEach, describe, expect, it,
3+
} from '@jest/globals';
4+
import $ from '@js/core/renderer';
5+
6+
import TextBox, { type TextBoxProperties } from '../text_box';
7+
8+
const textBoxes: TextBox[] = [];
9+
10+
const createTextBox = (options: Partial<TextBoxProperties> = {}): TextBox => {
11+
const element = $('<div>').appendTo(document.body).get(0) as HTMLElement;
12+
// @ts-expect-error DOMComponent constructor is not typed for direct instantiation
13+
const instance: TextBox = new TextBox(element, options);
14+
15+
textBoxes.push(instance);
16+
17+
return instance;
18+
};
19+
20+
describe('TextBox search mode and the clear button', () => {
21+
afterEach(() => {
22+
textBoxes.forEach((instance) => instance.dispose());
23+
textBoxes.length = 0;
24+
document.body.innerHTML = '';
25+
});
26+
27+
it('turns the clear button on in search mode when showClearButton is not specified', () => {
28+
const instance = createTextBox({ mode: 'search' });
29+
30+
expect(instance.option('showClearButton')).toBe(true);
31+
});
32+
33+
it('keeps showClearButton disabled in search mode when it is specified explicitly (T218573)', () => {
34+
const instance = createTextBox({ mode: 'search', showClearButton: false });
35+
36+
expect(instance.option('showClearButton')).toBe(false);
37+
});
38+
39+
it('restores the original showClearButton when search mode is turned off', () => {
40+
const instance = createTextBox({ mode: 'search' });
41+
42+
expect(instance.option('showClearButton')).toBe(true);
43+
44+
instance.option('mode', 'text');
45+
46+
expect(instance.option('showClearButton')).toBe(false);
47+
});
48+
});
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import {
2+
afterEach, describe, expect, it,
3+
} from '@jest/globals';
4+
import $ from '@js/core/renderer';
5+
6+
import TextEditor from '../text_editor';
7+
import type { TextEditorBaseProperties } from '../text_editor.base';
8+
9+
class ProbeTextEditor extends TextEditor {
10+
public hasButtonCollectionOnInitMarkup?: boolean;
11+
12+
_initMarkup(): void {
13+
this.hasButtonCollectionOnInitMarkup = Boolean(this._buttonCollection);
14+
15+
super._initMarkup();
16+
}
17+
}
18+
19+
const editors: ProbeTextEditor[] = [];
20+
21+
const createEditor = (options: Partial<TextEditorBaseProperties> = {}): ProbeTextEditor => {
22+
const element = $('<div>').appendTo(document.body).get(0) as HTMLElement;
23+
// @ts-expect-error DOMComponent constructor is not typed for direct instantiation
24+
const instance: ProbeTextEditor = new ProbeTextEditor(element, options);
25+
26+
editors.push(instance);
27+
28+
return instance;
29+
};
30+
31+
const customButton: NonNullable<TextEditorBaseProperties['buttons']> = [
32+
{ name: 'custom', location: 'after', options: { text: 'Go' } },
33+
];
34+
35+
describe('TextEditorBase buttons initialization', () => {
36+
afterEach(() => {
37+
editors.forEach((instance) => instance.dispose());
38+
editors.length = 0;
39+
document.body.innerHTML = '';
40+
});
41+
42+
it('creates the button collection before the markup is rendered', () => {
43+
const instance = createEditor();
44+
45+
expect(instance.hasButtonCollectionOnInitMarkup).toBe(true);
46+
});
47+
48+
it('renders a button declared in the buttons option', () => {
49+
const instance = createEditor({ buttons: customButton });
50+
51+
expect(instance.getButton('custom')).toBeDefined();
52+
expect(instance._$afterButtonsContainer).not.toBeNull();
53+
});
54+
55+
it('renders the declared button again after repaint', () => {
56+
const instance = createEditor({ buttons: customButton });
57+
58+
instance.repaint();
59+
60+
expect(instance.getButton('custom')).toBeDefined();
61+
});
62+
63+
it('throws E1053 when the buttons option is not an array', () => {
64+
expect(() => createEditor({
65+
buttons: 'custom' as unknown as TextEditorBaseProperties['buttons'],
66+
})).toThrow(/E1053/);
67+
});
68+
});

‎packages/devextreme/js/__internal/ui/text_box/text_box.ts‎

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,10 @@ class TextBox<
3131

3232
_showClearButton?: boolean;
3333

34-
ctor(element: Element, options: TProperties): void {
35-
if (options) {
36-
this._showClearButton = options.showClearButton;
37-
}
34+
_initOptions(options: TProperties): void {
35+
super._initOptions(options);
3836

39-
super.ctor(element, options);
37+
this._showClearButton = options.showClearButton;
4038
}
4139

4240
_getDefaultOptions(): TProperties {

‎packages/devextreme/js/__internal/ui/text_box/text_editor.base.ts‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,12 @@ class TextEditorBase<
133133

134134
_enterKeyAction?: ((event?: Record<string, unknown>) => void);
135135

136-
ctor(element: Element, options: TProperties): void {
137-
if (options) {
138-
checkButtonsOptionType(options.buttons);
139-
}
136+
_init(): void {
137+
super._init();
138+
139+
const { buttons } = this.option();
140+
141+
checkButtonsOptionType(buttons);
140142

141143
this._buttonCollection = new TextEditorButtonCollection(
142144
this as unknown as TextEditorBase,
@@ -146,8 +148,6 @@ class TextEditorBase<
146148
this._$beforeButtonsContainer = null;
147149
this._$afterButtonsContainer = null;
148150
this._labelContainerElement = null;
149-
150-
super.ctor(element, options);
151151
}
152152

153153
_getDefaultOptions(): TProperties {

0 commit comments

Comments
 (0)