Skip to content

Commit bb7cfb7

Browse files
Menu: clear focusedElement on submenu hiding (T1291581) (#29855)
1 parent a770521 commit bb7cfb7

File tree

4 files changed

+84
-9
lines changed

4 files changed

+84
-9
lines changed

packages/devextreme/js/__internal/ui/context_menu/m_context_menu.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ class ContextMenu extends MenuBase {
263263

264264
_setFocusedElement($element) {
265265
if ($element && $element.length !== 0) {
266+
// @ts-expect-error
266267
this.option('focusedElement', getPublicElement($element));
267268
this._scrollToElement($element);
268269
}
@@ -921,6 +922,7 @@ class ContextMenu extends MenuBase {
921922
const scrollableInstance = $submenu.dxScrollable('instance');
922923

923924
scrollableInstance.scrollTo(0);
925+
// @ts-expect-error
924926
this.option('focusedElement', null);
925927
}
926928

@@ -1115,6 +1117,7 @@ class ContextMenu extends MenuBase {
11151117
}
11161118

11171119
this._cleanAriaAttributes();
1120+
// @ts-expect-error
11181121
this.option('focusedElement', null);
11191122

11201123
return promise || Deferred().reject().promise();

packages/devextreme/js/__internal/ui/context_menu/m_menu_base.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ const DX_MENU_ITEM_CAPTION_URL_CLASS = `${DX_MENU_ITEM_CAPTION_CLASS}-with-url`;
3737
const DX_ICON_WITH_URL_CLASS = 'dx-icon-with-url';
3838
const ITEM_URL_CLASS = 'dx-item-url';
3939

40-
export type Properties = dxMenuBaseOptions<MenuBase, Item>;
40+
export interface Properties extends dxMenuBaseOptions<MenuBase, Item> {
41+
focusedElement?: dxElementWrapper;
42+
}
4143

4244
class MenuBase extends HierarchicalCollectionWidget<Properties> {
4345
static ItemClass = MenuItem;
@@ -117,6 +119,7 @@ class MenuBase extends HierarchicalCollectionWidget<Properties> {
117119
}
118120

119121
_clean(): void {
122+
// @ts-expect-error
120123
this.option('focusedElement', null);
121124

122125
super._clean();

packages/devextreme/js/__internal/ui/menu/m_menu.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class Menu extends MenuBase {
6464

6565
_submenus!: Submenu[];
6666

67-
_visibleSubmenu?: Submenu;
67+
_visibleSubmenu?: Submenu | null;
6868

6969
_overlay!: dxOverlay<OverlayProperties>;
7070

@@ -183,6 +183,7 @@ class Menu extends MenuBase {
183183
}
184184

185185
if ($newTarget && $newTarget.length !== 0) {
186+
// @ts-expect-error
186187
this.option('focusedElement', getPublicElement($newTarget));
187188
}
188189
}
@@ -266,7 +267,6 @@ class Menu extends MenuBase {
266267
}
267268

268269
_initMarkup(): void {
269-
// @ts-expect-error
270270
this._visibleSubmenu = null;
271271
// @ts-expect-error
272272
this.$element().addClass(DX_MENU_CLASS);
@@ -590,6 +590,7 @@ class Menu extends MenuBase {
590590

591591
const $newItem = $items.eq(itemIndex);
592592

593+
// @ts-expect-error
593594
this.option('focusedElement', getPublicElement($newItem));
594595
}
595596

@@ -693,9 +694,20 @@ class Menu extends MenuBase {
693694

694695
this._actions.onSubmenuHiding(eventArgs);
695696

697+
const { focusedElement } = this.option();
698+
const { focusedElement: submenuFocusedElement } = submenu.option();
699+
700+
const isVisibleSubmenuHiding = this._visibleSubmenu === submenu;
701+
const isFocusedElementHiding = focusedElement === submenuFocusedElement;
702+
703+
if (isVisibleSubmenuHiding && isFocusedElementHiding) {
704+
this.option('focusedElement', $menuAnchorItem);
705+
}
706+
696707
if (!eventArgs.cancel) {
697-
// @ts-expect-error
698-
if (this._visibleSubmenu === submenu) this._visibleSubmenu = null;
708+
if (isVisibleSubmenuHiding) {
709+
this._visibleSubmenu = null;
710+
}
699711
$border.hide();
700712
$menuAnchorItem.removeClass(DX_MENU_ITEM_EXPANDED_CLASS);
701713
}
@@ -815,6 +827,7 @@ class Menu extends MenuBase {
815827
const $closestItem = $target.closest(this._itemElements());
816828

817829
if ($closestItem.hasClass('dx-menu-item-has-submenu')) {
830+
// @ts-expect-error
818831
this.option('focusedElement', null);
819832
return;
820833
}
@@ -905,7 +918,6 @@ class Menu extends MenuBase {
905918
}
906919

907920
if (this._visibleSubmenu === submenu) {
908-
// @ts-expect-error
909921
this._visibleSubmenu = null;
910922
}
911923
// @ts-expect-error

packages/devextreme/testing/tests/DevExpress.ui.widgets/menu.tests.js

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2584,7 +2584,6 @@ QUnit.module('keyboard navigation', {
25842584
itemClickHandler.resetHistory();
25852585

25862586
this.keyboard
2587-
.press('down')
25882587
.press('down')
25892588
.press('right')
25902589
.press('down')
@@ -2631,10 +2630,12 @@ QUnit.module('keyboard navigation', {
26312630
.press('down')
26322631
.press('down')
26332632
.press('down')
2634-
.press('down')
2635-
.press('space');
2633+
.press('down');
26362634

26372635
assert.equal(isRenderer(this.instance.option('focusedElement')), !!config().useJQuery, 'focusedElement is correct');
2636+
2637+
this.keyboard.press('space');
2638+
26382639
assert.equal(this.instance.option('selectedItem').text, 'item2-3', 'correct item is selected');
26392640
});
26402641

@@ -2947,6 +2948,62 @@ QUnit.module('keyboard navigation', {
29472948

29482949
assert.equal($(this.instance._visibleSubmenu.option('focusedElement')).text(), 'Item 113');
29492950
});
2951+
2952+
QUnit.test('focusedElement should be set to main menu item after hiding submenu', function(assert) {
2953+
this.instance.option({
2954+
orientation: 'horizontal',
2955+
items: [
2956+
{
2957+
text: 'Item 1',
2958+
items: [
2959+
{ text: 'Item 11', items: [ { text: 'Item 111' }, { text: 'Item 112' }, { text: 'Item 113' } ] },
2960+
{ text: 'Item 12' }
2961+
],
2962+
},
2963+
]
2964+
});
2965+
2966+
this.keyboard.press('enter')
2967+
.press('down')
2968+
.press('down');
2969+
2970+
assert.strictEqual($(this.instance.option('focusedElement')).text(), 'Item 12', 'focusedElement is submenu item');
2971+
2972+
this.keyboard.press('enter');
2973+
2974+
const mainMenuItemText = $(this.instance.itemElements()[0]).text();
2975+
2976+
assert.strictEqual($(this.instance.option('focusedElement')).text(), mainMenuItemText, 'focusedElement is main menu item');
2977+
});
2978+
2979+
QUnit.test('focusedElement should be set to main menu item after hiding nested submenu', function(assert) {
2980+
this.instance.option({
2981+
orientation: 'horizontal',
2982+
items: [
2983+
{
2984+
text: 'Item 1',
2985+
items: [
2986+
{ text: 'Item 11', items: [ { text: 'Item 111' }, { text: 'Item 112' }, { text: 'Item 113' } ] },
2987+
{ text: 'Item 12' }
2988+
],
2989+
},
2990+
]
2991+
});
2992+
2993+
this.keyboard.press('enter')
2994+
.press('down')
2995+
.press('enter')
2996+
.press('right')
2997+
.press('down');
2998+
2999+
assert.strictEqual($(this.instance.option('focusedElement')).text(), 'Item 112', 'focusedElement is submenu item');
3000+
3001+
this.keyboard.press('enter');
3002+
3003+
const mainMenuItemText = $(this.instance.itemElements()[0]).text();
3004+
3005+
assert.strictEqual($(this.instance.option('focusedElement')).text(), mainMenuItemText, 'focusedElement is main menu item');
3006+
});
29503007
});
29513008

29523009
QUnit.module('Menu with templates', {

0 commit comments

Comments
 (0)