Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Menu: Items in adaptive mode should have links if item.url is set (T1181342) #25484

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 8 additions & 16 deletions js/ui/context_menu/ui.menu_base.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const SINGLE_SELECTION_MODE = 'single';
const DEFAULT_DELAY = { 'show': 50, 'hide': 300 };
const DX_MENU_ITEM_CAPTION_URL_CLASS = `${DX_MENU_ITEM_CAPTION_CLASS}-with-url`;
const DX_ICON_WITH_URL_CLASS = 'dx-icon-with-url';
const DX_ITEM_URL_CLASS = 'dx-item-url';
const ITEM_URL_CLASS = 'dx-item-url';


class MenuBase extends HierarchicalCollectionWidget {
Expand Down Expand Up @@ -183,29 +183,21 @@ class MenuBase extends HierarchicalCollectionWidget {
_getLinkContainer(iconContainer, textContainer, { linkAttr, url }) {
iconContainer?.addClass(DX_ICON_WITH_URL_CLASS);
textContainer?.addClass(DX_MENU_ITEM_CAPTION_URL_CLASS);
const linkAttributes = isObject(linkAttr) ? linkAttr : {};
return $('<a>')
.addClass(DX_ITEM_URL_CLASS)
.attr({ ...linkAttributes, href: url })
.append(iconContainer)
.append(textContainer);

return super._getLinkContainer(iconContainer, textContainer, { linkAttr, url });
}

_addContent($container, itemData) {
const { html, url } = itemData;

const iconContainer = this._getIconContainer(itemData);
const textContainer = this._getTextContainer(itemData);

$container.html(html);
if(url) {
const link = this._getLinkContainer(iconContainer, textContainer, itemData);
$container.html(html);
const link = this._getLinkContainer(this._getIconContainer(itemData), this._getTextContainer(itemData), itemData);
$container.append(link);
} else {
$container
.append(iconContainer)
.append(textContainer);
super._addContent($container, itemData);
}

$container.append(this._getPopoutContainer(itemData));
this._addContentClasses(itemData, $container.parent());
}
Expand Down Expand Up @@ -561,7 +553,7 @@ class MenuBase extends HierarchicalCollectionWidget {

_itemClick(actionArgs) {
const args = actionArgs.args[0];
const link = args.event.target.getElementsByClassName(DX_ITEM_URL_CLASS)[0];
const link = args.event.target.getElementsByClassName(ITEM_URL_CLASS)[0];
if(args.itemData.url && link) {
link.click();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import { getImageContainer } from '../../core/utils/icon';
import HierarchicalDataAdapter from './ui.data_adapter';
import CollectionWidget from '../collection/ui.collection_widget.edit';
import { BindableTemplate } from '../../core/templates/bindable_template';
import { isFunction } from '../../core/utils/type';
import { isFunction, isObject } from '../../core/utils/type';
import { noop } from '../../core/utils/common';

const DISABLED_STATE_CLASS = 'dx-state-disabled';
const ITEM_URL_CLASS = 'dx-item-url';

const HierarchicalCollectionWidget = CollectionWidget.inherit({

Expand Down Expand Up @@ -94,6 +95,15 @@ const HierarchicalCollectionWidget = CollectionWidget.inherit({
.append(this._getTextContainer(itemData));
},

_getLinkContainer: function(iconContainer, textContainer, { linkAttr, url }) {
const linkAttributes = isObject(linkAttr) ? linkAttr : {};
return $('<a>')
.addClass(ITEM_URL_CLASS)
.attr({ ...linkAttributes, href: url })
.append(iconContainer)
.append(textContainer);
},

_getIconContainer: function(itemData) {
return itemData.icon ? getImageContainer(itemData.icon) : undefined;
},
Expand Down
34 changes: 32 additions & 2 deletions js/ui/tree_view/ui.tree_view.base.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const DISABLED_STATE_CLASS = 'dx-state-disabled';
const SELECTED_ITEM_CLASS = 'dx-state-selected';
const EXPAND_EVENT_NAMESPACE = 'dxTreeView_expand';
const DATA_ITEM_ID = 'data-item-id';
const ITEM_URL_CLASS = 'dx-item-url';

const TreeViewBase = HierarchicalCollectionWidget.inherit({

Expand Down Expand Up @@ -769,6 +770,22 @@ const TreeViewBase = HierarchicalCollectionWidget.inherit({
return deferred.promise();
},

_getItemExtraPropNames() {
return ['url', 'linkAttr'];
},

_addContent: function($container, itemData) {
const { html, url } = itemData;

if(url) {
$container.html(html);
const link = this._getLinkContainer(this._getIconContainer(itemData), this._getTextContainer(itemData), itemData);
$container.append(link);
} else {
this.callBase($container, itemData);
}
},

_renderSublevel: function($node, node, childNodes) {
const $nestedNodeContainer = this._renderNodeContainer($node, node);

Expand Down Expand Up @@ -1415,11 +1432,24 @@ const TreeViewBase = HierarchicalCollectionWidget.inherit({
});
},

_itemClick: function(actionArgs) {
const args = actionArgs.args[0];
const target = args.event.target[0] || args.event.target;
const link = target.getElementsByClassName(ITEM_URL_CLASS)[0];

if(args.itemData.url && link) {
link.click();
}
},

_itemClickHandler: function(e, $item) {
const itemData = this._getItemData($item);
const node = this._getNodeByElement($item);

this._itemDXEventHandler(e, 'onItemClick', { node: this._dataAdapter.getPublicNode(node) });
this._itemDXEventHandler(e, 'onItemClick', {
node: this._dataAdapter.getPublicNode(node),
}, {
beforeExecute: this._itemClick,
});

if(this.option('selectByClick') && !e.isDefaultPrevented()) {
this._updateItemSelection(!node.internalFields.selected, itemData, e);
Expand Down
24 changes: 13 additions & 11 deletions scss/widgets/base/treeView/_common.scss
Original file line number Diff line number Diff line change
Expand Up @@ -78,16 +78,16 @@ $tree-view-icon-size: 24px;
display: block;
cursor: pointer;

.dx-treeview-item-content > .dx-icon {
display: inline-block;
width: $tree-view-icon-size;
height: $tree-view-icon-size;
vertical-align: middle;
margin-right: 5px;
background-size: $tree-view-icon-size $tree-view-icon-size;
}

.dx-treeview-item-content {
.dx-icon {
display: inline-block;
width: $tree-view-icon-size;
height: $tree-view-icon-size;
vertical-align: middle;
margin-right: 5px;
background-size: $tree-view-icon-size $tree-view-icon-size;
}

span {
vertical-align: middle;
}
Expand Down Expand Up @@ -127,8 +127,10 @@ $tree-view-icon-size: 24px;
}

.dx-treeview-item {
.dx-treeview-item-content > .dx-icon {
margin-right: 0;
.dx-treeview-item-content {
.dx-icon {
margin-right: 0;
}
}
}
}
Expand Down
6 changes: 4 additions & 2 deletions scss/widgets/base/treeView/_index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@
}

.dx-treeview-item {
.dx-treeview-item-content > .dx-icon {
margin-left: 5px;
.dx-treeview-item-content {
.dx-icon {
margin-left: 5px;
}
}
}

Expand Down
12 changes: 10 additions & 2 deletions scss/widgets/generic/treeView/_index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,12 @@ $generic-treeview-toggle-item-visibility-offset: -4px;
> .dx-treeview-item {
background-color: $treeview-focused-bg;
color: $treeview-focus-color;

.dx-item-content {
.dx-item-url {
color: unset;
}
}
}
}
}
Expand All @@ -150,8 +156,10 @@ $generic-treeview-toggle-item-visibility-offset: -4px;
padding: $generic-treeview-item-padding;
min-height: $generic-treeview-min-item-height;

.dx-treeview-item-content > .dx-icon {
@include dx-icon-sizing($generic-base-icon-size);
.dx-treeview-item-content {
.dx-icon {
@include dx-icon-sizing($generic-base-icon-size);
}
}

&.dx-state-hover {
Expand Down
6 changes: 4 additions & 2 deletions scss/widgets/material/treeView/_index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,10 @@ $material-treeview-item-with-checkbox-offset: $material-treeview-checkbox-offset
min-height: $material-treeview-min-item-height;
line-height: math.div($material-treeview-min-item-height, 2) - 2;

.dx-treeview-item-content > .dx-icon {
@include dx-icon-sizing($material-base-icon-size);
.dx-treeview-item-content {
.dx-icon {
@include dx-icon-sizing($material-base-icon-size);
}
}

&.dx-state-hover {
Expand Down
11 changes: 9 additions & 2 deletions testing/testcafe/model/menu/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,19 @@ import ContextMenu from '../contextMenu';
const CLASS = {
menu: 'dx-menu',
item: 'dx-menu-item',
adaptiveItem: 'dx-treeview-item',
contextMenu: 'dx-context-menu',
hamburgerButton: 'dx-menu-hamburger-button',
};

export default class Menu extends Widget {
items: Selector;

constructor() {
constructor(adaptivityEnabled = false) {
super(`.${CLASS.menu}`);

this.items = Selector(`.${CLASS.item}`).filterVisible();
const itemClass = adaptivityEnabled ? `.${CLASS.adaptiveItem}` : `.${CLASS.item}`;
this.items = Selector(itemClass).filterVisible();
}

// eslint-disable-next-line class-methods-use-this
Expand All @@ -25,6 +28,10 @@ export default class Menu extends Widget {
return this.items.nth(index);
}

getHamburgerButton(): Selector {
return this.element.find(`.${CLASS.hamburgerButton}`);
}

// eslint-disable-next-line class-methods-use-this
isElementFocused(element: Selector): Promise<boolean> {
return element.hasClass('dx-state-focused');
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
70 changes: 70 additions & 0 deletions testing/testcafe/tests/navigation/menu/link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,73 @@ test('Items should have links if item.url is set', async (t) => {
}],
}, '#menu');
});

test('Adaptive mode: items should have links if item.url is set', async (t) => {
const { takeScreenshot, compareResults } = createScreenshotsComparer(t);
const menu = new Menu(true);

await t.click(menu.getHamburgerButton())
.click(menu.items(0));

await testScreenshot(t, takeScreenshot, 'Items with links.png', { element: '#container' });

await t.pressKey('down');

await testScreenshot(t, takeScreenshot, 'Items without links.png', { element: '#container' });

await t
.pressKey('down')
.pressKey('down');

await testScreenshot(t, takeScreenshot, 'Items with link and icon focus.png', { element: '#container' });

await t.pressKey('down');

await testScreenshot(t, takeScreenshot, 'Items mode with link focus.png', { element: '#container' });

await t
.expect(compareResults.isValid())
.ok(compareResults.errorMessages());
}).before(async () => {
await appendElementTo('#container', 'div', 'menu');

await setAttribute('#container', 'style', 'width: 200px; height: 400px;');

return createWidget('dxMenu', {
displayExpr: 'name',
adaptivityEnabled: true,
items: [{
id: '1',
name: 'Items',
items: [{
id: '1-1',
name: 'Item 1',
}, {
id: '1-2',
icon: 'more',
}, {
id: '1-3',
name: 'Item 2',
icon: 'unlock',
url: 'https://js.devexpress.com/',
}, {
id: '1-4',
name: 'Item 3',
url: 'https://js.devexpress.com/',
}],
},
{
id: '2',
name: 'Items',
},
{
id: '3',
name: 'Items',
},
{
id: '4',
name: 'Items',
},
],
}, '#menu');
});
Loading