Skip to content

Commit 84cb02c

Browse files
authored
feat: add markdown support to Tooltip (#8124)
1 parent c276e60 commit 84cb02c

File tree

10 files changed

+290
-3
lines changed

10 files changed

+290
-3
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright 2000-2025 Vaadin Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
package com.vaadin.flow.component.button.tests;
17+
18+
import com.vaadin.flow.component.button.Button;
19+
import com.vaadin.flow.component.html.Div;
20+
import com.vaadin.flow.component.html.NativeButton;
21+
import com.vaadin.flow.router.Route;
22+
23+
@Route("vaadin-button/tooltip-markdown")
24+
public class TooltipMarkdownPage extends Div {
25+
public TooltipMarkdownPage() {
26+
Button buttonWithTooltip = new Button("Button with tooltip");
27+
buttonWithTooltip.setTooltipText("Initial tooltip");
28+
29+
NativeButton setMarkdownTooltip = new NativeButton(
30+
"Set markdown tooltip", e -> {
31+
buttonWithTooltip
32+
.setTooltipMarkdown("**Markdown** _tooltip_");
33+
});
34+
setMarkdownTooltip.setId("set-markdown-tooltip");
35+
36+
NativeButton setTextTooltip = new NativeButton("Set text tooltip",
37+
e -> {
38+
buttonWithTooltip
39+
.setTooltipText("**Plain text** _tooltip_");
40+
});
41+
setTextTooltip.setId("set-text-tooltip");
42+
43+
add(buttonWithTooltip, new Div(setMarkdownTooltip, setTextTooltip));
44+
}
45+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright 2000-2025 Vaadin Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
package com.vaadin.flow.component.button.tests;
17+
18+
import org.junit.Assert;
19+
import org.junit.Before;
20+
import org.junit.Test;
21+
import org.openqa.selenium.By;
22+
23+
import com.vaadin.flow.component.button.testbench.ButtonElement;
24+
import com.vaadin.flow.testutil.TestPath;
25+
import com.vaadin.testbench.TestBenchElement;
26+
import com.vaadin.tests.AbstractComponentIT;
27+
28+
@TestPath("vaadin-button/tooltip-markdown")
29+
public class TooltipMarkdownIT extends AbstractComponentIT {
30+
31+
private TestBenchElement tooltipContent;
32+
33+
@Before
34+
public void init() {
35+
open();
36+
tooltipContent = $(ButtonElement.class).first().$("vaadin-tooltip")
37+
.first().$("div").withAttribute("slot", "overlay").first();
38+
}
39+
40+
@Test
41+
public void initialContent_renderedAsText() {
42+
Assert.assertEquals("Initial tooltip",
43+
tooltipContent.getPropertyString("innerHTML"));
44+
}
45+
46+
@Test
47+
public void setMarkdownText_renderedAsMarkdown() {
48+
$("button").id("set-markdown-tooltip").click();
49+
50+
waitForElementPresent(By.tagName("strong"));
51+
52+
Assert.assertEquals("<p><strong>Markdown</strong> <em>tooltip</em></p>",
53+
tooltipContent.getPropertyString("innerHTML").strip());
54+
}
55+
56+
@Test
57+
public void switchBackToText_renderedAsText() {
58+
$("button").id("set-markdown-tooltip").click();
59+
60+
waitForElementPresent(By.tagName("strong"));
61+
62+
$("button").id("set-text-tooltip").click();
63+
64+
Assert.assertEquals("**Plain text** _tooltip_",
65+
tooltipContent.getPropertyString("innerHTML"));
66+
}
67+
}

vaadin-flow-components-shared-parent/vaadin-flow-components-base/src/main/java/com/vaadin/flow/component/shared/HasTooltip.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
public interface HasTooltip extends HasElement {
3838

3939
/**
40-
* Sets a tooltip text for the component.
40+
* Sets a tooltip text for the component as plain text.
4141
*
4242
* @param text
4343
* The tooltip text
@@ -53,6 +53,23 @@ default Tooltip setTooltipText(String text) {
5353
return tooltip;
5454
}
5555

56+
/**
57+
* Sets a tooltip text for the component in Markdown format.
58+
*
59+
* @param markdown
60+
* The tooltip text in Markdown format
61+
*
62+
* @return the tooltip handle
63+
*/
64+
default Tooltip setTooltipMarkdown(String markdown) {
65+
var tooltip = Tooltip.getForElement(getElement());
66+
if (tooltip == null) {
67+
tooltip = Tooltip.forHasTooltip(this);
68+
}
69+
tooltip.setMarkdown(markdown);
70+
return tooltip;
71+
}
72+
5673
/**
5774
* Gets the tooltip handle of the component.
5875
*

vaadin-flow-components-shared-parent/vaadin-flow-components-base/src/main/java/com/vaadin/flow/component/shared/Tooltip.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,13 +169,25 @@ static Tooltip forHasTooltip(HasTooltip hasTooltip) {
169169
}
170170

171171
/**
172-
* String used as a tooltip content.
172+
* Sets the tooltip content as plain text.
173173
*
174174
* @param text
175175
* the text to set
176176
*/
177177
public void setText(String text) {
178178
tooltipElement.setProperty("text", text);
179+
tooltipElement.setProperty("markdown", false);
180+
}
181+
182+
/**
183+
* Sets the tooltip content in Markdown format.
184+
*
185+
* @param markdown
186+
* the text to set in Markdown format
187+
*/
188+
public void setMarkdown(String markdown) {
189+
tooltipElement.setProperty("text", markdown);
190+
tooltipElement.setProperty("markdown", true);
179191
}
180192

181193
/**

vaadin-flow-components-shared-parent/vaadin-flow-components-base/src/test/java/com/vaadin/flow/component/shared/HasTooltipTest.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,32 @@ public void setTooltipText_tooltipHasText() {
9696
component.setTooltipText("foo");
9797
Assert.assertEquals("foo",
9898
getTooltipElement(component).get().getProperty("text"));
99+
Assert.assertFalse(getTooltipElement(component).get()
100+
.getProperty("markdown", false));
101+
}
102+
103+
@Test
104+
public void setTooltipMarkdown_tooltipHasMarkdown() {
105+
component.setTooltipMarkdown("**Markdown** _foo_");
106+
Assert.assertEquals("**Markdown** _foo_",
107+
getTooltipElement(component).get().getProperty("text"));
108+
Assert.assertTrue(getTooltipElement(component).get()
109+
.getProperty("markdown", false));
110+
}
111+
112+
@Test
113+
public void switchContentTypes() {
114+
component.setTooltipText("foo");
115+
Assert.assertFalse(getTooltipElement(component).get()
116+
.getProperty("markdown", false));
117+
118+
component.setTooltipMarkdown("**Markdown** _foo_");
119+
Assert.assertTrue(getTooltipElement(component).get()
120+
.getProperty("markdown", false));
121+
122+
component.setTooltipText("foo");
123+
Assert.assertFalse(getTooltipElement(component).get()
124+
.getProperty("markdown", false));
99125
}
100126

101127
@Test

vaadin-flow-components-shared-parent/vaadin-flow-components-base/src/test/java/com/vaadin/flow/component/shared/TooltipTest.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,38 @@ public void createTooltip_setText() {
9595
Assert.assertEquals("foo",
9696
getTooltipElement().get().getProperty("text"));
9797
Assert.assertEquals("foo", tooltip.getText());
98+
Assert.assertFalse(
99+
getTooltipElement().get().getProperty("markdown", false));
100+
}
101+
102+
@Test
103+
public void createTooltip_setMarkdown() {
104+
var tooltip = Tooltip.forComponent(component);
105+
tooltip.setMarkdown("**Markdown** _foo_");
106+
ui.add(component);
107+
Assert.assertEquals("**Markdown** _foo_",
108+
getTooltipElement().get().getProperty("text"));
109+
Assert.assertEquals("**Markdown** _foo_", tooltip.getText());
110+
Assert.assertTrue(
111+
getTooltipElement().get().getProperty("markdown", false));
112+
}
113+
114+
@Test
115+
public void createTooltip_switchContentType() {
116+
var tooltip = Tooltip.forComponent(component);
117+
ui.add(component);
118+
119+
tooltip.setText("foo");
120+
Assert.assertFalse(
121+
getTooltipElement().get().getProperty("markdown", false));
122+
123+
tooltip.setMarkdown("**Markdown** _foo_");
124+
Assert.assertTrue(
125+
getTooltipElement().get().getProperty("markdown", false));
126+
127+
tooltip.setText("foo");
128+
Assert.assertFalse(
129+
getTooltipElement().get().getProperty("markdown", false));
98130
}
99131

100132
@Test

vaadin-grid-flow-parent/vaadin-grid-flow-integration-tests/src/main/java/com/vaadin/flow/component/grid/it/GridTooltipPage.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,15 @@ public GridTooltipPage() {
5656
});
5757
setAgeTooltipButton.setId("set-age-tooltip-button");
5858

59+
var setMarkdownTooltipButton = new NativeButton("Set markdown tooltip",
60+
event -> {
61+
grid.setTooltipMarkdownEnabled(true);
62+
grid.setTooltipGenerator(
63+
person -> "**Markdown** _tooltip_ for "
64+
+ person.getFirstName());
65+
});
66+
setMarkdownTooltipButton.setId("set-markdown-tooltip-button");
67+
5968
var toggleGridButton = new NativeButton("Toggle grid", event -> {
6069

6170
if (grid.getParent().isPresent()) {
@@ -68,7 +77,7 @@ public GridTooltipPage() {
6877

6978
grid.setId("grid-with-tooltips");
7079
add(setGridTooltipButton, addColumnButton, setAgeTooltipButton,
71-
toggleGridButton, grid);
80+
setMarkdownTooltipButton, toggleGridButton, grid);
7281
}
7382

7483
}

vaadin-grid-flow-parent/vaadin-grid-flow-integration-tests/src/test/java/com/vaadin/flow/component/grid/it/GridTooltipIT.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,16 @@ public void columnTooltipOverridesGridTooltip() {
103103
Assert.assertEquals("Age of the person is 33", getActiveTooltipText());
104104
}
105105

106+
@Test
107+
public void setMarkdownTooltip() {
108+
clickElementWithJs("set-markdown-tooltip-button");
109+
flushScrolling(grid);
110+
showTooltip(grid.getCell(0, 0));
111+
Assert.assertEquals(
112+
"<p><strong>Markdown</strong> <em>tooltip</em> for Jack</p>",
113+
getActiveTooltipHtmlContent());
114+
}
115+
106116
@Test
107117
public void newColumnHasGridTooltipGenerator() {
108118
scrollToElement(grid);
@@ -127,6 +137,12 @@ private String getActiveTooltipText() {
127137
return findElement(By.tagName("vaadin-tooltip")).getText();
128138
}
129139

140+
private String getActiveTooltipHtmlContent() {
141+
return $("vaadin-tooltip").first().$("div")
142+
.withAttribute("slot", "overlay").first()
143+
.getAttribute("innerHTML").strip();
144+
}
145+
130146
/**
131147
* Forces the grid to remove the `scrolling` attribute on the grid scroller,
132148
* which would otherwise prevent a tooltip to open on mouseenter

vaadin-grid-flow-parent/vaadin-grid-flow/src/main/java/com/vaadin/flow/component/grid/Grid.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4918,6 +4918,33 @@ public void setTooltipPosition(TooltipPosition position) {
49184918
.setAttribute("position", position.getPosition()));
49194919
}
49204920

4921+
/**
4922+
* Returns whether the tooltip content is rendered as Markdown.
4923+
*
4924+
* @return {@code true} if the content is rendered as Markdown,
4925+
* {@code false} if it is treated as plain text
4926+
*/
4927+
public boolean isTooltipMarkdownEnabled() {
4928+
return getTooltipElement().map(
4929+
tooltipElement -> tooltipElement.getProperty("markdown", false))
4930+
.orElse(false);
4931+
}
4932+
4933+
/**
4934+
* Sets whether the tooltip content is rendered as Markdown. By default, the
4935+
* content is treated as plain text.
4936+
*
4937+
* @param markdownEnabled
4938+
* {@code true} to render the content as Markdown, {@code false}
4939+
* to treat it as plain text
4940+
*/
4941+
public void setTooltipMarkdownEnabled(boolean markdownEnabled) {
4942+
addTooltipElementToTooltipSlot();
4943+
4944+
getTooltipElement().ifPresent(tooltipElement -> tooltipElement
4945+
.setProperty("markdown", markdownEnabled));
4946+
}
4947+
49214948
private void addTooltipElementToTooltipSlot() {
49224949
if (getTooltipElement().isPresent()) {
49234950
// the grid's tooltip slot has already been filled

vaadin-grid-flow-parent/vaadin-grid-flow/src/test/java/com/vaadin/flow/component/grid/GridTooltipTest.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,42 @@ public void getTooltipPosition_defaultTooltipPosition() {
147147
grid.getTooltipPosition());
148148
}
149149

150+
@Test
151+
public void setTooltipMarkdownEnabled_hasTooltipElement() {
152+
grid.setTooltipMarkdownEnabled(true);
153+
Assert.assertTrue(getTooltipElement(grid).isPresent());
154+
}
155+
156+
@Test
157+
public void setTooltipMarkdownEnabled_hasTooltipWithProperty() {
158+
grid.setTooltipMarkdownEnabled(true);
159+
Assert.assertTrue(getTooltipElement(grid).orElseThrow()
160+
.getProperty("markdown", false));
161+
162+
grid.setTooltipMarkdownEnabled(false);
163+
Assert.assertFalse(getTooltipElement(grid).orElseThrow()
164+
.getProperty("markdown", false));
165+
}
166+
167+
@Test
168+
public void setTooltipMarkdownEnabled_isTooltipMarkdownEnabled() {
169+
grid.setTooltipMarkdownEnabled(true);
170+
Assert.assertTrue(grid.isTooltipMarkdownEnabled());
171+
172+
grid.setTooltipMarkdownEnabled(false);
173+
Assert.assertFalse(grid.isTooltipMarkdownEnabled());
174+
}
175+
176+
@Test
177+
public void isTooltipMarkdownEnabled_defaultValue() {
178+
// without tooltip element
179+
Assert.assertFalse(grid.isTooltipMarkdownEnabled());
180+
181+
// with tooltip element, no property value
182+
grid.setTooltipGenerator(item -> item);
183+
Assert.assertFalse(grid.isTooltipMarkdownEnabled());
184+
}
185+
150186
private Optional<Element> getTooltipElement(Grid<?> grid) {
151187
return getTooltipElements(grid).findFirst();
152188
}

0 commit comments

Comments
 (0)