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 a39176229f65..868bdc5b8423 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 @@ -3,10 +3,10 @@ import $ from '@js/core/renderer'; import { extend } from '@js/core/utils/extend'; import { captionize } from '@js/core/utils/inflector'; import { each } from '@js/core/utils/iterator'; -import { isBoolean, isDefined, isFunction } from '@js/core/utils/type'; +import { isDefined, isFunction } from '@js/core/utils/type'; import type { dxDropDownEditorOptions } from '@js/ui/drop_down_editor/ui.drop_down_editor'; import type { FormItemComponent } from '@js/ui/form'; -import type { dxOverlayOptions } from '@js/ui/overlay'; +import type { Properties as PopupProperties } from '@js/ui/popup'; import type dxTextBox from '@js/ui/text_box'; import { SIMPLE_ITEM_TYPE } from './constants'; @@ -167,12 +167,10 @@ export function convertToLabelMarkOptions( }; } -// eslint-disable-next-line @typescript-eslint/naming-convention -function _getDropDownEditorOptions( +function getDropDownEditorOptions( $parent, editorType: FormItemComponent, editorInputId: string, - onContentReadyExternal?: DropDownOptions['onContentReady'], ): DropDownOptions { const isDropDownEditor = DROP_DOWN_EDITORS.includes(editorType); @@ -181,33 +179,28 @@ function _getDropDownEditorOptions( } return { - onContentReady: (e) => { - const { component } = e; + // @ts-expect-error // unpublished option + onPopupInitialized: ({ component, popup }): void => { 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 initialHideOnOutsideClick = popup.option('hideOnOutsideClick') as PopupProperties['hideOnOutsideClick']; - const $target = $(e.target); - const $label = $parent.find(`label[for="${editorInputId}"]`); - const isLabelClicked = !!$target.closest($label).length; + // Do not overwrite boolean hideOnOutsideClick + if (openOnFieldClick && isFunction(initialHideOnOutsideClick)) { + const hideOnOutsideClick: PopupProperties['hideOnOutsideClick'] = (e) => { + 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); + }; - return !isLabelClicked && initialHideOnOutsideClick(e); - }, + component.option('dropDownOptions', { + hideOnOutsideClick, }); - } - if (isFunction(onContentReadyExternal)) { - onContentReadyExternal(e); + popup.option({ + hideOnOutsideClick, + }); } }, }; @@ -245,7 +238,7 @@ function _convertToEditorOptions({ const stylingMode = externalEditorOptions?.stylingMode || editorStylingMode; const useSpecificLabelOptions = EDITORS_WITH_SPECIFIC_LABELS.includes(editorType); - const dropDownEditorOptions = _getDropDownEditorOptions($parent, editorType, editorInputId, externalEditorOptions?.onContentReady); + const dropDownEditorOptions = getDropDownEditorOptions($parent, editorType, editorInputId); const result = extend( true, 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 9e9112cb9909..5d564cf0e614 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 @@ -4580,15 +4580,100 @@ QUnit.test('form should be dirty when some editors are dirty', function(assert) // 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'}`); + switch(hideOnOutsideClick) { + case true: + assert.false(editorInstance.option('opened'), 'drop down list is hidden by outside click'); + break; + case false: + assert.true(editorInstance.option('opened'), `drop down list ${openOnFieldClick ? 'is hidden by triggered input click' : 'is visible'}`); + break; + default: + assert.strictEqual(editorInstance.option('opened'), openOnFieldClick, `drop down list is hidden by ${openOnFieldClick ? 'triggered input click' : 'outside click'}`); } }); }); }); +QUnit.test('DropDownEditor popup must toggle on input or dropDownButton click if openOnFieldClick = true', function(assert) { + const $form = $('#form').dxForm({ + formData: { CustomerID: 'VINET' }, + items: [{ + itemType: 'group', + colCount: 2, + items: [{ + dataField: 'CustomerID', + editorType: 'dxSelectBox', + editorOptions: { + items: ['VINET', 'VALUE', 'VINS'], + value: '', + openOnFieldClick: true, + }, + }], + }], + }); + + const $dropDownEditorInput = $form.find(`.${EDITOR_INPUT_CLASS}`); + const $dropDownButton = $form.find(`.${DROP_DOWN_EDITOR_BUTTON_CLASS}`); + + pointerMock($dropDownEditorInput).click(); + + const editorInstance = $form.dxForm('instance').getEditor('CustomerID'); + + assert.true(editorInstance.option('opened'), 'drop down list is visible'); + + pointerMock($dropDownEditorInput).click(); + + assert.false(editorInstance.option('opened'), 'drop down list is hidden'); + + pointerMock($dropDownButton).click(); + + assert.true(editorInstance.option('opened'), 'drop down list is visible'); + + pointerMock($dropDownButton).click(); + + assert.false(editorInstance.option('opened'), 'drop down list is hidden'); +}); + +QUnit.test('DropDownEditor popup must toggle on dropDownButton click if openOnFieldClick = false', function(assert) { + const $form = $('#form').dxForm({ + formData: { CustomerID: 'VINET' }, + items: [{ + itemType: 'group', + colCount: 2, + items: [{ + dataField: 'CustomerID', + editorType: 'dxSelectBox', + editorOptions: { + items: ['VINET', 'VALUE', 'VINS'], + value: '', + openOnFieldClick: false, + }, + }], + }], + }); + + const $dropDownEditorInput = $form.find(`.${EDITOR_INPUT_CLASS}`); + const $dropDownButton = $form.find(`.${DROP_DOWN_EDITOR_BUTTON_CLASS}`); + + pointerMock($dropDownEditorInput).click(); + + const editorInstance = $form.dxForm('instance').getEditor('CustomerID'); + + assert.false(editorInstance.option('opened'), 'drop down list is hidden'); + + pointerMock($dropDownButton).click(); + + assert.true(editorInstance.option('opened'), 'drop down list is visible'); + + pointerMock($dropDownEditorInput).click(); + + assert.true(editorInstance.option('opened'), 'drop down list is visible'); + + pointerMock($dropDownButton).click(); + + assert.false(editorInstance.option('opened'), 'drop down list is hidden'); +}); + QUnit.module('reset', () => { [ ['dxCalendar', new Date(2019, 1, 2), { dxCalendar: new Date(2019, 1, 3) } ],