diff --git a/packages/devextreme/js/__internal/ui/form/m_form.layout_manager.utils.ts b/packages/devextreme/js/__internal/ui/form/m_form.layout_manager.utils.ts index 78f497dfae0..a39176229f6 100644 --- a/packages/devextreme/js/__internal/ui/form/m_form.layout_manager.utils.ts +++ b/packages/devextreme/js/__internal/ui/form/m_form.layout_manager.utils.ts @@ -37,6 +37,8 @@ const DROP_DOWN_EDITORS: FormItemComponent[] = [ 'dxDateRangeBox', ]; +type DropDownOptions = dxDropDownEditorOptions; + export function convertToRenderFieldItemOptions({ $parent, rootElementCssClassList, @@ -165,6 +167,52 @@ export function convertToLabelMarkOptions( }; } +// eslint-disable-next-line @typescript-eslint/naming-convention +function _getDropDownEditorOptions( + $parent, + editorType: FormItemComponent, + editorInputId: string, + onContentReadyExternal?: DropDownOptions['onContentReady'], +): DropDownOptions { + const isDropDownEditor = DROP_DOWN_EDITORS.includes(editorType); + + if (!isDropDownEditor) { + return {}; + } + + return { + onContentReady: (e) => { + const { component } = e; + const openOnFieldClick = component.option('openOnFieldClick') as DropDownOptions['openOnFieldClick']; + const initialHideOnOutsideClick = component.option('dropDownOptions.hideOnOutsideClick') as dxOverlayOptions['hideOnOutsideClick']; + + if (openOnFieldClick) { + component.option('dropDownOptions', { + hideOnOutsideClick: (e) => { + if (isBoolean(initialHideOnOutsideClick)) { + return initialHideOnOutsideClick; + } + + const $target = $(e.target); + const $label = $parent.find(`label[for="${editorInputId}"]`); + const isLabelClicked = !!$target.closest($label).length; + + if (!isFunction(initialHideOnOutsideClick)) { + return !isLabelClicked; + } + + return !isLabelClicked && initialHideOnOutsideClick(e); + }, + }); + } + + if (isFunction(onContentReadyExternal)) { + onContentReadyExternal(e); + } + }, + }; +} + // eslint-disable-next-line @typescript-eslint/naming-convention function _convertToEditorOptions({ $parent, @@ -197,48 +245,13 @@ function _convertToEditorOptions({ const stylingMode = externalEditorOptions?.stylingMode || editorStylingMode; const useSpecificLabelOptions = EDITORS_WITH_SPECIFIC_LABELS.includes(editorType); - let editorOptionsWithDropDown: Partial> = {}; - const useDropDownOptions = DROP_DOWN_EDITORS.includes(editorType); - - if (useDropDownOptions) { - editorOptionsWithDropDown = { - onContentReady: (e) => { - const { component } = e; - const openOnFieldClick = component.option('openOnFieldClick') as dxDropDownEditorOptions['openOnFieldClick']; - const initialHideOnOutsideClick = component.option('dropDownOptions.hideOnOutsideClick') as dxOverlayOptions['hideOnOutsideClick']; - - if (openOnFieldClick) { - component.option('dropDownOptions', { - hideOnOutsideClick: (e) => { - if (isBoolean(initialHideOnOutsideClick)) { - return initialHideOnOutsideClick; - } - - const $target = $(e.target); - const $label = $parent.find(`label[for="${editorInputId}"]`); - const isLabelClicked = !!$target.closest($label).length; - - if (!isFunction(initialHideOnOutsideClick)) { - return !isLabelClicked; - } - - return !isLabelClicked && initialHideOnOutsideClick(e); - }, - }); - } - - if (isFunction(externalEditorOptions?.onContentReady)) { - externalEditorOptions.onContentReady(e); - } - }, - }; - } + const dropDownEditorOptions = _getDropDownEditorOptions($parent, editorType, editorInputId, externalEditorOptions?.onContentReady); const result = extend( true, editorOptionsWithValue, externalEditorOptions, - editorOptionsWithDropDown, + dropDownEditorOptions, { inputAttr: { id: editorInputId }, validationBoundary: editorValidationBoundary, @@ -261,6 +274,7 @@ function _convertToEditorOptions({ if (defaultEditorName && !result.name) { result.name = defaultEditorName; } + return result; } diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.form/form.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.form/form.tests.js index 9e6519b0ad3..0a28a0451ea 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.form/form.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.form/form.tests.js @@ -4576,9 +4576,14 @@ QUnit.test('form should be dirty when some editors are dirty', function(assert) pointerMock($label).click(); - const willHideByInput = openOnFieldClick || (hideOnOutsideClick === false); - - assert.equal(editorInstance.option('opened'), willHideByInput, `drop down list is hidden by ${willHideByInput ? 'triggered input click' : 'label click'}`); + // NOTE: In the real environment, clicking the label triggers a click on the editor, + // toggling the popup visibility if openOnFieldClick=true. + // This assertion only takes hideOnOutsideClick into account + if(hideOnOutsideClick === false) { + assert.true(editorInstance.option('opened'), `drop down list ${openOnFieldClick ? 'is hidden by triggered input click' : 'is visible'}`); + } else { + assert.strictEqual(editorInstance.option('opened'), openOnFieldClick, `drop down list is hidden by ${openOnFieldClick ? 'triggered input click' : 'outside click'}`); + } }); }); });