Skip to content

Commit

Permalink
Merge branch 'DevExpress:24_2' into chatDateHeaders__24_2
Browse files Browse the repository at this point in the history
  • Loading branch information
Zedwag authored Oct 16, 2024
2 parents 626b219 + a803db5 commit f212e70
Show file tree
Hide file tree
Showing 4 changed files with 230 additions and 33 deletions.
68 changes: 47 additions & 21 deletions packages/devextreme/js/__internal/ui/m_lookup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ const Lookup = DropDownList.inherit({
return getSize('height');
},
shading: true,
hideOnOutsideClick: false,
hideOnOutsideClick: true,
position: undefined,
animation: {},
title: '',
Expand Down Expand Up @@ -192,7 +192,6 @@ const Lookup = DropDownList.inherit({
dropDownCentered: true,
_scrollToSelectedItemEnabled: true,
dropDownOptions: {
hideOnOutsideClick: true,
_ignoreFunctionValueDeprecation: true,

width: () => getElementWidth(this.$element()),
Expand Down Expand Up @@ -542,7 +541,13 @@ const Lookup = DropDownList.inherit({
return 'auto';
},

_popupTabHandler: noop,
_popupTabHandler(e) {
const shouldLoopFocusInsidePopup = this._shouldLoopFocusInsidePopup();

if (!shouldLoopFocusInsidePopup) {
this.callBase(e);
}
},

_renderPopup() {
if (this.option('usePopover') && !this.option('dropDownOptions.fullScreen')) {
Expand All @@ -561,8 +566,10 @@ const Lookup = DropDownList.inherit({
},

_renderPopover() {
this._popup = this._createComponent(this._$popup, Popover, extend(
this._popupConfig(),
const popupConfig = this._popupConfig();

const options = extend(
popupConfig,
this._options.cache('dropDownOptions'),
{
showEvent: null,
Expand All @@ -573,10 +580,12 @@ const Lookup = DropDownList.inherit({
hideOnParentScroll: true,
_fixWrapperPosition: false,
width: this._isInitialOptionValue('dropDownOptions.width')
? function () { return getOuterWidth(this.$element()); }.bind(this)
: this._popupConfig().width,
? () => getOuterWidth(this.$element())
: popupConfig.width,
},
));
);

this._popup = this._createComponent(this._$popup, Popover, options);

this._popup.$overlayContent().attr('role', 'dialog');

Expand All @@ -588,10 +597,11 @@ const Lookup = DropDownList.inherit({
contentReady: this._contentReadyHandler.bind(this),
});

if (this.option('_scrollToSelectedItemEnabled')) this._popup._$arrow.remove();
if (this.option('_scrollToSelectedItemEnabled')) {
this._popup._$arrow.remove();
}

this._setPopupContentId(this._popup.$content());

this._contentReadyHandler();
},

Expand All @@ -610,23 +620,38 @@ const Lookup = DropDownList.inherit({

_preventFocusOnPopup: noop,

_shouldLoopFocusInsidePopup(): boolean {
const {
usePopover,
dropDownCentered,
// eslint-disable-next-line @typescript-eslint/naming-convention
_scrollToSelectedItemEnabled,
} = this.option();

const result: boolean = _scrollToSelectedItemEnabled
? dropDownCentered
: !usePopover;

return result;
},

_popupConfig() {
const result = extend(this.callBase(), {
const { dropDownOptions } = this.option();
const shouldLoopFocusInsidePopup = this._shouldLoopFocusInsidePopup();

const result = extend(this.callBase(), {
toolbarItems: this._getPopupToolbarItems(),

hideOnParentScroll: false,
onPositioned: null,

maxHeight: '100vh',

showTitle: this.option('dropDownOptions.showTitle'),
title: this.option('dropDownOptions.title'),
showTitle: dropDownOptions.showTitle,
title: dropDownOptions.title,
titleTemplate: this._getTemplateByOption('dropDownOptions.titleTemplate'),
onTitleRendered: this.option('dropDownOptions.onTitleRendered'),
fullScreen: this.option('dropDownOptions.fullScreen'),
shading: this.option('dropDownOptions.shading'),
hideOnOutsideClick: this.option('dropDownOptions.hideOnOutsideClick') || this.option('dropDownOptions.closeOnOutsideClick'),
onTitleRendered: dropDownOptions.onTitleRendered,
fullScreen: dropDownOptions.fullScreen,
shading: dropDownOptions.shading,
hideOnOutsideClick: dropDownOptions.hideOnOutsideClick || dropDownOptions.closeOnOutsideClick,
_loopFocus: shouldLoopFocusInsidePopup,
});

delete result.animation;
Expand All @@ -647,7 +672,8 @@ const Lookup = DropDownList.inherit({
}

each(['position', 'animation', 'width', 'height'], (_, optionName) => {
const popupOptionValue = this.option(`dropDownOptions.${optionName}`);
const popupOptionValue = dropDownOptions[optionName];

if (popupOptionValue !== undefined) {
result[optionName] = popupOptionValue;
}
Expand Down
8 changes: 7 additions & 1 deletion packages/devextreme/js/__internal/ui/overlay/m_overlay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ const Overlay: typeof OverlayInstance = Widget.inherit({
_checkParentVisibility: true,
_hideOnParentScrollTarget: undefined,
_fixWrapperPosition: false,
_loopFocus: false,
});
},

Expand Down Expand Up @@ -684,8 +685,12 @@ const Overlay: typeof OverlayInstance = Widget.inherit({
},

_toggleTabTerminator(enabled) {
// eslint-disable-next-line @typescript-eslint/naming-convention
const { _loopFocus } = this.option();

const eventName = addNamespace('keydown', this.NAME);
if (enabled) {

if (_loopFocus || enabled) {
eventsEngine.on(domAdapter.getDocument(), eventName, this._proxiedTabTerminatorHandler);
} else {
eventsEngine.off(domAdapter.getDocument(), eventName, this._proxiedTabTerminatorHandler);
Expand Down Expand Up @@ -1158,6 +1163,7 @@ const Overlay: typeof OverlayInstance = Widget.inherit({
switch (name) {
case 'animation':
break;
case '_loopFocus':
case 'shading':
this._toggleShading(this.option('visible'));
this._toggleSafariScrolling();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ QUnit.testStart(function() {
$('#widthRootStyle').css('width', '300px');
});

const OVERLAY_CLASS = 'dx-overlay';
const OVERLAY_SHADER_CLASS = 'dx-overlay-shader';
const OVERLAY_WRAPPER_CLASS = 'dx-overlay-wrapper';
const OVERLAY_CONTENT_CLASS = 'dx-overlay-content';
Expand All @@ -89,9 +90,10 @@ const PLACEHOLDER_CLASS = 'dx-placeholder';
const SCROLL_VIEW_LOAD_PANEL_CLASS = 'dx-scrollview-loadpanel';
const SCROLL_VIEW_CONTENT_CLASS = 'dx-scrollview-content';
const LIST_ITEMS_CLASS = 'dx-list-items';

const FOCUSED_CLASS = 'dx-state-focused';

const CANCEL_BUTTON_SELECTOR = '.dx-popup-cancel.dx-button';

const WINDOW_RATIO = 0.8;

const toSelector = function(val) {
Expand Down Expand Up @@ -1033,11 +1035,14 @@ QUnit.module('Lookup', {

openPopupWithList(firstLookup);

// NOTE: in ShadowDOM mode one selected item is inside ShadowDOM
// and other is in document
// NOTE: In ShadowDom mode, the two selected elements are inside ShadowDom.
// This happens because the overlays were closed and moved to the overlay container.
if(QUnit.isInShadowDomMode()) {
assert.strictEqual(document.querySelectorAll(`.${LIST_ITEM_SELECTED_CLASS}`).length, 1);
assert.strictEqual($('#qunit-fixture').get(0).querySelectorAll(`.${LIST_ITEM_SELECTED_CLASS}`).length, 1);
const listItemSelectedInDocument = document.querySelectorAll(`.${LIST_ITEM_SELECTED_CLASS}`);
const listItemSelectedInShadowDOM = $('#qunit-fixture').get(0).querySelectorAll(`.${LIST_ITEM_SELECTED_CLASS}`);

assert.strictEqual(listItemSelectedInDocument.length, 0);
assert.strictEqual(listItemSelectedInShadowDOM.length, 2);
} else {
assert.strictEqual($(`.${LIST_ITEM_SELECTED_CLASS}`).length, 2);
}
Expand Down Expand Up @@ -2156,7 +2161,7 @@ QUnit.module('popup options', {
assert.ok(!$wrapper.hasClass(OVERLAY_SHADER_CLASS));
});

QUnit.test('popup should not be hidden after outsideClick', function(assert) {
QUnit.test('popup should be hidden after outsideClick', function(assert) {
const $lookup = $('#lookupOptions');
const instance = $lookup.dxLookup({
dataSource: [1, 2, 3]
Expand All @@ -2167,13 +2172,13 @@ QUnit.module('popup options', {
const $overlay = $(toSelector(OVERLAY_CONTENT_CLASS)).eq(0);

$(document).trigger('dxpointerdown');
assert.equal($overlay.is(':visible'), true, 'overlay is not hidden');
assert.equal($overlay.is(':visible'), false, 'overlay is hidden');
});

QUnit.test('lookup popup should be hidden after click outside was present', function(assert) {
QUnit.test('lookup popup should not be hidden after click outside was present if dropDownOptions.hideOnOutsideClick is set to false', function(assert) {
const $lookup = $('#lookupOptions');
const instance = $lookup.dxLookup({
'dropDownOptions.hideOnOutsideClick': true,
'dropDownOptions.hideOnOutsideClick': false,
visible: true,
usePopover: false
}).dxLookup('instance');
Expand All @@ -2186,7 +2191,37 @@ QUnit.module('popup options', {
assert.equal($overlay.is(':visible'), true, 'overlay is not hidden');

$(document).trigger('dxpointerdown');
assert.equal($overlay.is(':visible'), false, 'overlay is hidden');
assert.equal($overlay.is(':visible'), true, 'overlay is not hidden');
});

[true, false].forEach(dropDownCentered => {
[true, false].forEach(_scrollToSelectedItemEnabled => {
[true, false].forEach(usePopover => {
QUnit.test(`Popup should have correct _loopFocus option value if usePopover=${usePopover}, _scrollToSelectedItemEnabled=${_scrollToSelectedItemEnabled}, dropDownCentered=${dropDownCentered}`, function(assert) {
const instance = $('#lookupOptions').dxLookup({
_scrollToSelectedItemEnabled,
usePopover,
dropDownCentered,
}).dxLookup('instance');

openPopupWithList(instance);

const $popup = $(`.${POPUP_CLASS}`);

const popup = usePopover && !_scrollToSelectedItemEnabled
? PopoverFull.getInstance($popup)
: PopupFull.getInstance($popup);

const expectedValue = _scrollToSelectedItemEnabled
? dropDownCentered
: !usePopover;

const { _loopFocus } = popup.option();

assert.strictEqual(_loopFocus, expectedValue, `_loopFocus: ${_loopFocus} is correct`);
});
});
});
});

QUnit.test('custom titleTemplate option', function(assert) {
Expand Down Expand Up @@ -2225,7 +2260,8 @@ QUnit.module('popup options', {
QUnit.test('custom titleTemplate and onTitleRendered option is set correctly by options', function(assert) {
assert.expect(2);

const $lookup = $('#lookupOptions').dxLookup(); const instance = $lookup.dxLookup('instance');
const $lookup = $('#lookupOptions').dxLookup();
const instance = $lookup.dxLookup('instance');

instance.option('dropDownOptions.onTitleRendered', function(e) {
assert.ok(true, 'option \'onTitleRendered\' successfully passed to the popup widget raised on titleTemplate');
Expand Down Expand Up @@ -2906,6 +2942,80 @@ QUnit.module('keyboard navigation', {
assert.ok(instance._$list.find(`.${LIST_ITEM_CLASS}`).eq(1).hasClass(FOCUSED_CLASS), 'second list-item is focused after down key pressing');
});

[true, false].forEach(value => {
QUnit.test(`focus from last Popover element should ${value ? 'not' : ''} move to Lookup field while keeping Popup open when usePopover: true and _scrollToSelectedItemEnabled: ${value}`, function(assert) {
if(devices.real().deviceType !== 'desktop') {
assert.ok(true, 'test does not actual for mobile devices');
return;
}

const $element = $('#widget').dxLookup({
_scrollToSelectedItemEnabled: value,
items: [1, 2, 3],
opened: true,
focusStateEnabled: true,
});
const instance = $element.dxLookup('instance');
const tabKeyDownEvent = $.Event('keydown', { key: 'Tab' });
const $overlayContent = $(instance.content()).parent();
const $cancelButton = $overlayContent.find(CANCEL_BUTTON_SELECTOR);

$cancelButton.trigger(tabKeyDownEvent);

assert.strictEqual($element.hasClass(FOCUSED_CLASS), !value);
assert.ok(instance.option('opened'), 'popup is opened');
});
});

[true, false].forEach(value => {
QUnit.test(`focus from last Popover element should not move to Lookup field while keeping Popup open when usePopover: false and dropDownCentered: ${value}`, function(assert) {
if(devices.real().deviceType !== 'desktop') {
assert.ok(true, 'test does not actual for mobile devices');
return;
}

const $element = $('#widget').dxLookup({
dropDownCentered: value,
items: [1, 2, 3],
opened: true,
usePopover: false,
focusStateEnabled: true,
_scrollToSelectedItemEnabled: true,
});
const instance = $element.dxLookup('instance');
const tabKeyDownEvent = $.Event('keydown', { key: 'Tab' });
const $overlayContent = $(instance.content()).parent();
const $cancelButton = $overlayContent.find(CANCEL_BUTTON_SELECTOR);

$cancelButton.trigger(tabKeyDownEvent);

assert.strictEqual($element.hasClass(FOCUSED_CLASS), false);
assert.ok(instance.option('opened'), 'popup is opened');
});
});

QUnit.test('focus from first Popover element should move back to Lookup field while keeping Popup open when usePopover: true and shift+Tab is pressed', function(assert) {
if(devices.real().deviceType !== 'desktop') {
assert.ok(true, 'test does not actual for mobile devices');
return;
}

const $element = $('#widget').dxLookup({
opened: true,
items: [1, 2, 3],
focusStateEnabled: true,
});
const instance = $element.dxLookup('instance');
const shiftTabKeyDownEvent = $.Event('keydown', { key: 'Tab', shiftKey: true });
const $overlayContent = $(instance.content()).parent();
const $searchInput = $overlayContent.find(`.${LOOKUP_SEARCH_CLASS} .${TEXTEDITOR_INPUT_CLASS}`);

$searchInput.trigger(shiftTabKeyDownEvent);

assert.ok($element.hasClass(FOCUSED_CLASS), 'lookup field is focused');
assert.ok(instance.option('opened'), 'popup is opened');
});

QUnit.testInActiveWindow('lookup item should be selected after \'enter\' key pressing', function(assert) {
if(devices.real().deviceType !== 'desktop') {
assert.ok(true, 'test does not actual for mobile devices');
Expand Down
Loading

0 comments on commit f212e70

Please sign in to comment.