Skip to content

Commit 8b71075

Browse files
r-farkhutdinovRuslan Farkhutdinov
andauthored
DropDownEditor: Add fieldAddons option (DevExpress#30766)
Co-authored-by: Ruslan Farkhutdinov <[email protected]>
1 parent fd856d7 commit 8b71075

File tree

3 files changed

+352
-2
lines changed

3 files changed

+352
-2
lines changed

packages/devextreme/js/__internal/ui/drop_down_editor/m_drop_down_editor.ts

Lines changed: 119 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ const DROP_DOWN_EDITOR_OVERLAY_FLIPPED = 'dx-dropdowneditor-overlay-flipped';
4747
const DROP_DOWN_EDITOR_ACTIVE = 'dx-dropdowneditor-active';
4848
const DROP_DOWN_EDITOR_FIELD_CLICKABLE = 'dx-dropdowneditor-field-clickable';
4949
const DROP_DOWN_EDITOR_FIELD_TEMPLATE_WRAPPER = 'dx-dropdowneditor-field-template-wrapper';
50+
export const DROP_DOWN_EDITOR_BEFORE_FIELD_ADDON = 'dx-dropdowneditor-field-before-template';
51+
export const DROP_DOWN_EDITOR_AFTER_FIELD_ADDON = 'dx-dropdowneditor-field-after-template';
5052

5153
const OVERLAY_CONTENT_LABEL = 'Dropdown';
5254

@@ -80,6 +82,17 @@ export interface DropDownEditorProperties extends Omit<
8082
onPopupInitialized?: (e: { component: DropDownEditor; popup: Popup }) => void;
8183
}
8284

85+
interface TemplateRenderPayload {
86+
model: Properties['value'];
87+
container: Element;
88+
onRendered?: () => void;
89+
}
90+
91+
interface FieldAddonsTemplates {
92+
beforeTemplate?: { render: (payload: TemplateRenderPayload) => void };
93+
afterTemplate?: { render: (payload: TemplateRenderPayload) => void };
94+
}
95+
8396
function createTemplateWrapperElement(): dxElementWrapper {
8497
return $('<div>').addClass(DROP_DOWN_EDITOR_FIELD_TEMPLATE_WRAPPER);
8598
}
@@ -97,6 +110,10 @@ class DropDownEditor<
97110

98111
_$templateWrapper?: dxElementWrapper;
99112

113+
_$beforeFieldAddon?: dxElementWrapper | null;
114+
115+
_$afterFieldAddon?: dxElementWrapper | null;
116+
100117
_openAction!: (event?: Record<string, unknown>) => void;
101118

102119
_closeAction!: (event?: Record<string, unknown>) => void;
@@ -314,6 +331,7 @@ class DropDownEditor<
314331
_renderInput(): void {
315332
super._renderInput();
316333
this._renderTemplateWrapper();
334+
this._renderFieldAddons();
317335

318336
this._wrapInput();
319337
this._setDefaultAria();
@@ -374,6 +392,14 @@ class DropDownEditor<
374392
}
375393

376394
_renderField(): void {
395+
const fieldAddonsTemplates = this._getFieldAddonsTemplates();
396+
397+
if (fieldAddonsTemplates) {
398+
this._renderFieldAddonsContent(fieldAddonsTemplates);
399+
400+
return;
401+
}
402+
377403
const fieldTemplate = this._getFieldTemplate();
378404

379405
if (fieldTemplate) {
@@ -404,6 +430,33 @@ class DropDownEditor<
404430
return fieldTemplate ? this._$container : this._$textEditorContainer;
405431
}
406432

433+
_renderBeforeFieldAddon(): void {
434+
if (!this._$beforeFieldAddon) {
435+
this._$beforeFieldAddon = $('<div>')
436+
.addClass(DROP_DOWN_EDITOR_BEFORE_FIELD_ADDON)
437+
.insertBefore(this._$textEditorContainer);
438+
}
439+
}
440+
441+
_renderAfterFieldAddon(): void {
442+
if (!this._$afterFieldAddon) {
443+
this._$afterFieldAddon = $('<div>')
444+
.addClass(DROP_DOWN_EDITOR_AFTER_FIELD_ADDON)
445+
.insertAfter(this._$textEditorContainer);
446+
}
447+
}
448+
449+
_renderFieldAddons(): void {
450+
const { fieldAddons } = this.option();
451+
452+
if (!fieldAddons) {
453+
return;
454+
}
455+
456+
this._renderBeforeFieldAddon();
457+
this._renderAfterFieldAddon();
458+
}
459+
407460
_renderTemplateWrapper(): void {
408461
const fieldTemplate = this._getFieldTemplate();
409462
if (!fieldTemplate) {
@@ -463,6 +516,65 @@ class DropDownEditor<
463516
});
464517
}
465518

519+
_getFieldAddonsTemplates(): FieldAddonsTemplates | null {
520+
const { fieldAddons } = this.option();
521+
522+
if (!fieldAddons) {
523+
return null;
524+
}
525+
526+
const { beforeTemplate: before, afterTemplate: after } = fieldAddons;
527+
528+
const beforeTemplate = before ? this._getTemplate(before) : null;
529+
const afterTemplate = after ? this._getTemplate(after) : null;
530+
531+
return {
532+
beforeTemplate,
533+
afterTemplate,
534+
};
535+
}
536+
537+
_clearFieldAddons(removeField?: boolean): void {
538+
this._$beforeFieldAddon?.empty();
539+
this._$afterFieldAddon?.empty();
540+
541+
if (removeField) {
542+
this._$beforeFieldAddon = null;
543+
this._$afterFieldAddon = null;
544+
}
545+
}
546+
547+
_renderBeforeFieldAddonContent(beforeTemplate?: FieldAddonsTemplates['beforeTemplate'] | null): void {
548+
if (beforeTemplate && this._$beforeFieldAddon) {
549+
beforeTemplate.render({
550+
model: this._fieldRenderData(),
551+
container: getPublicElement(this._$beforeFieldAddon),
552+
});
553+
}
554+
}
555+
556+
_renderAfterFieldAddonContent(afterTemplate?: FieldAddonsTemplates['afterTemplate'] | null): void {
557+
if (afterTemplate && this._$afterFieldAddon) {
558+
afterTemplate.render({
559+
model: this._fieldRenderData(),
560+
container: getPublicElement(this._$afterFieldAddon),
561+
});
562+
}
563+
}
564+
565+
_renderFieldAddonsContent(fieldAddonsTemplates: FieldAddonsTemplates): void {
566+
this._clearFieldAddons();
567+
568+
if (!fieldAddonsTemplates) {
569+
return;
570+
}
571+
572+
const { beforeTemplate, afterTemplate } = fieldAddonsTemplates;
573+
574+
this._renderBeforeFieldAddonContent(beforeTemplate);
575+
this._renderAfterFieldAddonContent(afterTemplate);
576+
}
577+
466578
_integrateInput(): void {
467579
const { isValid } = this.option();
468580

@@ -484,8 +596,9 @@ class DropDownEditor<
484596
this._renderEmptinessEvent();
485597
}
486598

487-
_fieldRenderData(): any {
488-
return this.option('value');
599+
_fieldRenderData(): Properties['value'] {
600+
const { value } = this.option();
601+
return value;
489602
}
490603

491604
_initTemplates(): void {
@@ -874,11 +987,14 @@ class DropDownEditor<
874987
delete this._openOnFieldClickAction;
875988
delete this._$templateWrapper;
876989

990+
this._clearFieldAddons(true);
991+
877992
if (this._$popup) {
878993
this._$popup.remove();
879994
delete this._$popup;
880995
delete this._popup;
881996
}
997+
882998
super._clean();
883999
}
8841000

@@ -1025,6 +1141,7 @@ class DropDownEditor<
10251141
case 'onPopupInitialized': // for dashboards
10261142
this._initPopupInitializedAction();
10271143
break;
1144+
case 'fieldAddons':
10281145
case 'fieldTemplate':
10291146
case 'acceptCustomValue':
10301147
case 'openOnFieldClick':

packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/dropDownEditor.markup.tests.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import $ from 'jquery';
22
import { Deferred } from 'core/utils/deferred';
3+
import {
4+
DROP_DOWN_EDITOR_BEFORE_FIELD_ADDON,
5+
DROP_DOWN_EDITOR_AFTER_FIELD_ADDON
6+
} from '__internal/ui/drop_down_editor/m_drop_down_editor';
37

48
import 'ui/drop_down_editor/ui.drop_down_editor';
59

@@ -112,6 +116,20 @@ module('DropDownEditor markup', {
112116
deferred.resolve();
113117
assert.ok(renderFieldSpy.calledOnce, 'field has been rendered');
114118
});
119+
120+
test('should render fieldAddons content slots if fieldAddons is provided', function(assert) {
121+
const fieldAddons = { beforeTemplate: () => 'before', afterTemplate: 'after' };
122+
123+
const $dropDownEditor = $('#dropDownEditorLazy').dxDropDownEditor({
124+
fieldAddons
125+
});
126+
127+
const $before = $dropDownEditor.find(`.${DROP_DOWN_EDITOR_BEFORE_FIELD_ADDON}`);
128+
const $after = $dropDownEditor.find(`.${DROP_DOWN_EDITOR_AFTER_FIELD_ADDON}`);
129+
130+
assert.strictEqual($before.length, 1, 'before is rendered ');
131+
assert.strictEqual($after.length, 1, 'after slot is rendered ');
132+
});
115133
});
116134

117135
module('option change', function() {

0 commit comments

Comments
 (0)