Skip to content
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
37 changes: 37 additions & 0 deletions articles/components/context-menu/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,43 @@ endif::[]
--


[role="since:com.vaadin:vaadin@V25.2"]
== Tooltips
Comment thread
vursen marked this conversation as resolved.

Menu items can be configured with tooltips to provide additional information.

[.example,themes="lumo,aura"]
--

ifdef::lit[]
[source,typescript]
----
include::{root}/frontend/demo/component/contextmenu/context-menu-tooltip.ts[render,tag=snippet,indent=0,group=Lit]

...

include::{root}/frontend/demo/component/contextmenu/context-menu-tooltip.ts[render,tag=snippethtml,indent=0,group=Lit]
----
endif::[]

ifdef::flow[]
[source,java]
----
include::{root}/src/main/java/com/vaadin/demo/component/contextmenu/ContextMenuTooltip.java[render,tags=snippet,indent=0,group=Flow]
----
endif::[]

ifdef::react[]
[source,tsx]
----
include::{root}/frontend/demo/component/contextmenu/react/context-menu-tooltip.tsx[render,tags=snippet,indent=0,group=React]
----
endif::[]
--

See the <<../tooltip#,Tooltips documentation page>> for details on tooltip configuration.


== Custom Items

You can customize the items to include more than a single line of text.
Expand Down
2 changes: 1 addition & 1 deletion articles/components/menu-bar/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ endif::[]

== Tooltips

Tooltips can be configured on top-level items to provide additional information, especially for icon-only items. When a top-level item is disabled, the corresponding tooltip isn't shown.
Both top-level and [since:com.vaadin:vaadin@V25.2]#sub-menu items# can be configured with tooltips to provide additional context, such as labels for icon-only menu buttons.

[.example,themes="lumo,aura"]
--
Expand Down
71 changes: 71 additions & 0 deletions frontend/demo/component/contextmenu/context-menu-tooltip.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import 'Frontend/demo/init'; // hidden-source-line
import '@vaadin/context-menu';
import '@vaadin/grid';
import '@vaadin/tooltip';
import { html, LitElement } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import type { Grid } from '@vaadin/grid';
import { getPeople } from 'Frontend/demo/domain/DataService';
import { applyTheme } from 'Frontend/demo/theme';
import type Person from 'Frontend/generated/com/vaadin/demo/domain/Person';

@customElement('context-menu-tooltip')
export class Example extends LitElement {
protected override createRenderRoot() {
const root = super.createRenderRoot();
applyTheme(root);
return root;
}

// tag::snippet[]
@state()
private items = [
{ text: 'Edit', tooltip: 'Edit selected person' },
{
text: 'Share',
children: [
{ text: 'Copy link', tooltip: 'Copy a shareable link to the clipboard' },
{
text: 'Email',
tooltip: 'Send the contact details by email',
tooltipPosition: 'end',
},
],
},
];
// end::snippet[]

@state()
private gridItems: Person[] = [];

protected override async firstUpdated() {
this.gridItems = (await getPeople({ count: 5 })).people;
}

protected override render() {
return html`
<!-- tag::snippethtml[] -->
<vaadin-context-menu .items=${this.items}>
<vaadin-tooltip slot="tooltip"></vaadin-tooltip>
<vaadin-grid
all-rows-visible
.items=${this.gridItems}
@vaadin-contextmenu=${this.onContextMenu}
>
<vaadin-grid-column path="firstName"></vaadin-grid-column>
<vaadin-grid-column path="lastName"></vaadin-grid-column>
<vaadin-grid-column path="email"></vaadin-grid-column>
</vaadin-grid>
</vaadin-context-menu>
<!-- end::snippethtml[] -->
`;
}

onContextMenu(e: MouseEvent) {
// Prevent opening context menu on header row.
const target = e.currentTarget as Grid<Person>;
if (target.getEventContext(e).section !== 'body') {
e.stopPropagation();
}
}
}
64 changes: 64 additions & 0 deletions frontend/demo/component/contextmenu/react/context-menu-tooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { reactExample } from 'Frontend/demo/react-example'; // hidden-source-line
import React, { useEffect, useRef } from 'react';
import { useSignals } from '@preact/signals-react/runtime'; // hidden-source-line
import { useSignal } from '@vaadin/hilla-react-signals';
import { ContextMenu, Tooltip } from '@vaadin/react-components';
import { Grid, type GridElement } from '@vaadin/react-components/Grid.js';
import { GridColumn } from '@vaadin/react-components/GridColumn.js';
import { getPeople } from 'Frontend/demo/domain/DataService';
import type Person from 'Frontend/generated/com/vaadin/demo/domain/Person';

function Example() {
useSignals(); // hidden-source-line
const gridItems = useSignal<Person[]>([]);
const gridRef = useRef<GridElement>(null);

useEffect(() => {
getPeople({ count: 5 }).then(({ people }) => {
gridItems.value = people;
});
}, []);

useEffect(() => {
const grid = gridRef.current;
if (grid) {
// Workaround: Prevent opening context menu on header row.
// @ts-expect-error vaadin-contextmenu isn't a GridElement event.
grid.addEventListener('vaadin-contextmenu', (e) => {
if (grid.getEventContext(e).section !== 'body') {
e.stopPropagation();
}
});
}
}, [gridRef.current]);

// tag::snippet[]
const items = [
{ text: 'Edit', tooltip: 'Edit selected person' },
{
text: 'Share',
children: [
{ text: 'Copy link', tooltip: 'Copy a shareable link to the clipboard' },
{
text: 'Email',
tooltip: 'Send the contact details by email',
tooltipPosition: 'end',
},
],
},
];

return (
<ContextMenu items={items}>
<Tooltip slot="tooltip" />
<Grid allRowsVisible items={gridItems.value} ref={gridRef}>
<GridColumn path="firstName" />
<GridColumn path="lastName" />
<GridColumn path="email" />
</Grid>
</ContextMenu>
);
// end::snippet[]
}

export default reactExample(Example); // hidden-source-line
8 changes: 6 additions & 2 deletions frontend/demo/component/menubar/menu-bar-tooltip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,18 @@ export class Example extends LitElement {
{
component: this.createItem('folder'),
tooltip: 'Move',
children: [
{ text: 'To folder…', tooltip: 'Choose a destination folder' },
{ text: 'To archive', tooltip: 'Move to archive', tooltipPosition: 'end' },
],
},
{
component: this.createItem('copy'),
tooltip: 'Duplicate',
},
{
component: this.createItem('archive'),
tooltip: 'Archive',
component: this.createItem('trash'),
tooltip: 'Delete',
disabled: true,
},
];
Expand Down
15 changes: 10 additions & 5 deletions frontend/demo/component/menubar/react/menu-bar-tooltip.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import '@vaadin/icons';
import { reactExample } from 'Frontend/demo/react-example'; // hidden-source-line
import React from 'react';
import { Icon } from '@vaadin/react-components/Icon.js';
import { MenuBar, type MenuBarItem } from '@vaadin/react-components/MenuBar.js';
import { Tooltip } from '@vaadin/react-components/Tooltip.js';
import { Icon, MenuBar, type MenuBarItem, Tooltip } from '@vaadin/react-components';

function createItem(iconName: string) {
return <Icon icon={`vaadin:${iconName}`} />;
Expand All @@ -14,9 +12,16 @@ function Example() {
const items: MenuBarItem[] = [
{ component: createItem('eye'), tooltip: 'View' },
{ component: createItem('pencil'), tooltip: 'Edit' },
{ component: createItem('folder'), tooltip: 'Move' },
{
component: createItem('folder'),
tooltip: 'Move',
children: [
{ text: 'To folder…', tooltip: 'Choose a destination folder' },
{ text: 'To archive', tooltip: 'Move to archive', tooltipPosition: 'end' },
],
},
{ component: createItem('copy'), tooltip: 'Duplicate' },
{ component: createItem('archive'), tooltip: 'Archive', disabled: true },
{ component: createItem('trash'), tooltip: 'Delete', disabled: true },
];

return (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.vaadin.demo.component.contextmenu;

import com.vaadin.demo.DemoExporter; // hidden-source-line
import com.vaadin.demo.domain.DataService;
import com.vaadin.demo.domain.Person;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.grid.contextmenu.GridContextMenu;
import com.vaadin.flow.component.grid.contextmenu.GridMenuItem;
import com.vaadin.flow.component.grid.contextmenu.GridSubMenu;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.shared.Tooltip.TooltipPosition;
import com.vaadin.flow.router.Route;

import java.util.List;

@Route("context-menu-tooltip")
public class ContextMenuTooltip extends Div {

private List<Person> people = DataService.getPeople(5);

public ContextMenuTooltip() {
Grid<Person> grid = new Grid();
grid.setAllRowsVisible(true);
grid.setItems(people);

grid.addColumn(Person::getFirstName).setHeader("First name");
grid.addColumn(Person::getLastName).setHeader("Last name");
grid.addColumn(Person::getEmail).setHeader("Email");

// tag::snippet[]
GridContextMenu<Person> menu = grid.addContextMenu();
GridMenuItem<Person> edit = menu.addItem("Edit");
edit.setTooltipText("Edit selected person");

GridMenuItem<Person> share = menu.addItem("Share");
GridSubMenu<Person> shareSubMenu = share.getSubMenu();
shareSubMenu.addItem("Copy link")
.setTooltipText("Copy a shareable link to the clipboard");
GridMenuItem<Person> email = shareSubMenu.addItem("Email");
email.setTooltipText("Send the contact details by email");
email.setTooltipPosition(TooltipPosition.END);
// end::snippet[]

add(grid);
}

public static class Exporter extends DemoExporter<ContextMenuTooltip> { // hidden-source-line
} // hidden-source-line
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.vaadin.demo.component.menubar;

import com.vaadin.flow.component.contextmenu.MenuItem;
import com.vaadin.flow.component.contextmenu.SubMenu;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.menubar.MenuBar;
import com.vaadin.flow.component.menubar.MenuBarVariant;
import com.vaadin.flow.component.shared.Tooltip.TooltipPosition;
import com.vaadin.flow.router.Route;
import com.vaadin.demo.DemoExporter; // hidden-source-line

Expand All @@ -18,11 +20,18 @@ public MenuBarTooltip() {

createIconItem(menuBar, VaadinIcon.EYE, "View");
createIconItem(menuBar, VaadinIcon.PENCIL, "Edit");
createIconItem(menuBar, VaadinIcon.FOLDER, "Move");

MenuItem move = createIconItem(menuBar, VaadinIcon.FOLDER, "Move");
SubMenu moveSubMenu = move.getSubMenu();
moveSubMenu.addItem("To folder…")
.setTooltipText("Choose a destination folder");
MenuItem toArchive = moveSubMenu.addItem("To archive");
toArchive.setTooltipText("Move to archive");
toArchive.setTooltipPosition(TooltipPosition.END);

createIconItem(menuBar, VaadinIcon.COPY, "Duplicate");
MenuItem archive = createIconItem(menuBar, VaadinIcon.ARCHIVE,
"Archive");
archive.setEnabled(false);
MenuItem delete = createIconItem(menuBar, VaadinIcon.TRASH, "Delete");
delete.setEnabled(false);
// end::snippet[]
add(menuBar);
}
Expand All @@ -31,7 +40,8 @@ public MenuBarTooltip() {
private MenuItem createIconItem(MenuBar menu, VaadinIcon iconName,
String tooltipText) {
Icon icon = new Icon(iconName);
MenuItem item = menu.addItem(icon, tooltipText);
MenuItem item = menu.addItem(icon);
item.setTooltipText(tooltipText);
return item;
}
// end::createIcon[]
Expand Down
Loading