diff options
6 files changed, 129 insertions, 10 deletions
diff --git a/client/src/main/java/com/vaadin/client/ui/VMenuBar.java b/client/src/main/java/com/vaadin/client/ui/VMenuBar.java index 29897f7dd6..144f6117d0 100644 --- a/client/src/main/java/com/vaadin/client/ui/VMenuBar.java +++ b/client/src/main/java/com/vaadin/client/ui/VMenuBar.java @@ -805,6 +805,7 @@ public class VMenuBar extends FocusableFlowPanel protected boolean checked = false; protected boolean selected = false; protected String description = null; + protected ContentMode contentMode = null; private String styleName; @@ -1114,15 +1115,24 @@ public class VMenuBar extends FocusableFlowPanel MenuBarConstants.ATTRIBUTE_ITEM_DESCRIPTION); } + if (uidl.hasAttribute( + MenuBarConstants.ATTRIBUTE_ITEM_CONTENT_MODE)) { + String contentModeString = uidl.getStringAttribute( + MenuBarConstants.ATTRIBUTE_ITEM_CONTENT_MODE); + contentMode = ContentMode.valueOf(contentModeString); + } else { + contentMode = ContentMode.PREFORMATTED; + } + updateStyleNames(); } public TooltipInfo getTooltip() { - if (description == null) { + if (description == null || contentMode == null) { return null; } - return new TooltipInfo(description, ContentMode.PREFORMATTED, null, + return new TooltipInfo(description, contentMode, null, this); } diff --git a/server/src/main/java/com/vaadin/ui/MenuBar.java b/server/src/main/java/com/vaadin/ui/MenuBar.java index 544b93ea6a..72a3671d4e 100644 --- a/server/src/main/java/com/vaadin/ui/MenuBar.java +++ b/server/src/main/java/com/vaadin/ui/MenuBar.java @@ -31,6 +31,7 @@ import org.jsoup.parser.Tag; import com.vaadin.server.PaintException; import com.vaadin.server.PaintTarget; import com.vaadin.server.Resource; +import com.vaadin.shared.ui.ContentMode; import com.vaadin.shared.ui.menubar.MenuBarConstants; import com.vaadin.shared.ui.menubar.MenuBarState; import com.vaadin.ui.Component.Focusable; @@ -142,6 +143,14 @@ public class MenuBar extends AbstractComponent target.addAttribute(MenuBarConstants.ATTRIBUTE_ITEM_DESCRIPTION, description); } + + ContentMode contentMode = item.getContentMode(); + // If the contentMode is equal to ContentMode.PREFORMATTED, we don't add any attribute. + if (contentMode != null && contentMode != ContentMode.PREFORMATTED) { + target.addAttribute(MenuBarConstants.ATTRIBUTE_ITEM_CONTENT_MODE, + contentMode.name()); + } + if (item.isCheckable()) { // if the "checked" attribute is present (either true or false), // the item is checkable @@ -457,6 +466,7 @@ public class MenuBar extends AbstractComponent private boolean isSeparator = false; private String styleName; private String description; + private ContentMode contentMode = ContentMode.PREFORMATTED; private boolean checkable = false; private boolean checked = false; @@ -782,20 +792,46 @@ public class MenuBar extends AbstractComponent } /** - * Sets the items's description. See {@link #getDescription()} for more + * Analogous method to {@link AbstractComponent#setDescription(String)}. + * Sets the item's description. See {@link #getDescription()} for more * information on what the description is. * * @param description * the new description string for the component. */ public void setDescription(String description) { + setDescription(description, ContentMode.PREFORMATTED); + } + + /** + * Analogous method to + * {@link AbstractComponent#setDescription(String, ContentMode)}. Sets + * the item's description using given content mode. See + * {@link #getDescription()} for more information on what the + * description is. + * <p> + * If the content {@code mode} is {@literal ContentMode.HTML} the + * description is displayed as HTML in tooltips or directly in certain + * components so care should be taken to avoid creating the possibility + * for HTML injection and possibly XSS vulnerabilities. + * + * @see ContentMode + * + * @param description + * the new description string for the component. + * @param mode + * the content mode for the description + * @since + */ + public void setDescription(String description, ContentMode mode) { this.description = description; + this.contentMode = mode; markAsDirty(); } /** * <p> - * Gets the items's description. The description can be used to briefly + * Gets the item's description. The description can be used to briefly * describe the state of the item to the user. The description string * may contain certain XML tags: * </p> @@ -854,6 +890,23 @@ public class MenuBar extends AbstractComponent } /** + * Gets the content mode of the description of the menu item. The + * description is displayed as the tooltip of the menu item in the UI. + * <p> + * If no content mode was explicitly set using the + * {@link #setDescription(String, ContentMode)} method, the content mode + * will be {@link ContentMode#PREFORMATTED} + * </p> + * + * @return the {@link ContentMode} of the description of this menu item + * @see ContentMode + * @since + */ + public ContentMode getContentMode() { + return contentMode; + } + + /** * Gets the checkable state of the item - whether the item has checked * and unchecked states. If an item is checkable its checked state (as * returned by {@link #isChecked()}) is indicated in the UI. @@ -982,6 +1035,9 @@ public class MenuBar extends AbstractComponent DesignAttributeHandler.writeAttribute("description", attr, item.getDescription(), def.getDescription(), String.class, context); + DesignAttributeHandler.writeAttribute("contentmode", attr, + item.getContentMode().name(), def.getContentMode().name(), String.class, + context); DesignAttributeHandler.writeAttribute("style-name", attr, item.getStyleName(), def.getStyleName(), String.class, context); @@ -1041,8 +1097,13 @@ public class MenuBar extends AbstractComponent attr, boolean.class)); } if (menuElement.hasAttr("description")) { - menu.setDescription(DesignAttributeHandler - .readAttribute("description", attr, String.class)); + String description = DesignAttributeHandler.readAttribute("description", attr, String.class); + if (menuElement.hasAttr("contentmode")) { + String contentModeString = DesignAttributeHandler.readAttribute("contentmode", attr, String.class); + menu.setDescription(description, ContentMode.valueOf(contentModeString)); + } else { + menu.setDescription(description); + } } if (menuElement.hasAttr("style-name")) { menu.setStyleName(DesignAttributeHandler.readAttribute("style-name", diff --git a/server/src/test/java/com/vaadin/tests/components/menubar/MenuBarDeclarativeTest.java b/server/src/test/java/com/vaadin/tests/components/menubar/MenuBarDeclarativeTest.java index 5a67d2bcce..68f1fad864 100644 --- a/server/src/test/java/com/vaadin/tests/components/menubar/MenuBarDeclarativeTest.java +++ b/server/src/test/java/com/vaadin/tests/components/menubar/MenuBarDeclarativeTest.java @@ -28,6 +28,7 @@ import com.vaadin.server.ThemeResource; import com.vaadin.tests.design.DeclarativeTestBase; import com.vaadin.ui.MenuBar; import com.vaadin.ui.MenuBar.MenuItem; +import com.vaadin.shared.ui.ContentMode; /** * Tests declarative support for menu bars. @@ -69,6 +70,27 @@ public class MenuBarDeclarativeTest extends DeclarativeTestBase<MenuBar> { } @Test + public void testDescriptionContentMode() { + String design = "<vaadin-menu-bar plain-text>" + + "<menu description=\"This description is implicitly preformatted\">One</menu>" + + "<menu description=\"This description\nis explicitly\n\npreformatted\">preformatted</menu>" + + "<menu contentmode=\"HTML\" description=\"<b>I</b> contain <br/> <e>html</e>\">HTML</menu>" + + "<menu contentmode=\"TEXT\" description=\"Just plain text\">plain text</menu>" + + "</vaadin-menu-bar>"; + MenuBar menuBar = new MenuBar(); + menuBar.addItem("One", null).setDescription("This description is implicitly preformatted"); + menuBar.addItem("preformatted", null) + .setDescription("This description\nis explicitly\n\npreformatted", ContentMode.PREFORMATTED); + menuBar.addItem("HTML", null) + .setDescription("<b>I</b> contain <br/> <e>html</e>", ContentMode.HTML); + menuBar.addItem("plain text", null) + .setDescription("Just plain text", ContentMode.TEXT); + + testWrite(design, menuBar); + testRead(design, menuBar); + } + + @Test // #16328 public void testTicketSpec1() throws IOException { String design = "<vaadin-menu-bar auto-open plain-text tabindex=5> " @@ -165,12 +187,13 @@ public class MenuBarDeclarativeTest extends DeclarativeTestBase<MenuBar> { actual.isSeparator()); assertEquals(baseError + "Enabled", expected.isEnabled(), actual.isEnabled()); - assertEquals(baseError + "Text", expected.getText(), actual.getText()); assertEquals(baseError + "Description", expected.getDescription(), actual.getDescription()); assertEquals(baseError + "Style Name", expected.getStyleName(), actual.getStyleName()); + assertEquals(baseError + "Content Mode", expected.getContentMode(), + actual.getContentMode()); if (expected.getIcon() != null) { assertNotNull(baseError + "Icon was null", actual.getIcon()); diff --git a/shared/src/main/java/com/vaadin/shared/ui/menubar/MenuBarConstants.java b/shared/src/main/java/com/vaadin/shared/ui/menubar/MenuBarConstants.java index 2d211824e6..4f5fcb72ff 100644 --- a/shared/src/main/java/com/vaadin/shared/ui/menubar/MenuBarConstants.java +++ b/shared/src/main/java/com/vaadin/shared/ui/menubar/MenuBarConstants.java @@ -24,6 +24,8 @@ public class MenuBarConstants implements Serializable { @Deprecated public static final String ATTRIBUTE_ITEM_DESCRIPTION = "description"; @Deprecated + public static final String ATTRIBUTE_ITEM_CONTENT_MODE = "contentmode"; + @Deprecated public static final String ATTRIBUTE_ITEM_ICON = "icon"; @Deprecated public static final String ATTRIBUTE_ITEM_DISABLED = "disabled"; diff --git a/uitest/src/main/java/com/vaadin/tests/elements/menubar/MenuBarUI.java b/uitest/src/main/java/com/vaadin/tests/elements/menubar/MenuBarUI.java index a78eae271c..d56ead71e9 100644 --- a/uitest/src/main/java/com/vaadin/tests/elements/menubar/MenuBarUI.java +++ b/uitest/src/main/java/com/vaadin/tests/elements/menubar/MenuBarUI.java @@ -16,6 +16,7 @@ package com.vaadin.tests.elements.menubar; import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.ui.ContentMode; import com.vaadin.tests.components.AbstractTestUI; import com.vaadin.ui.MenuBar; import com.vaadin.ui.MenuBar.Command; @@ -38,8 +39,11 @@ public class MenuBarUI extends AbstractTestUI { String secondaryLevelItemSuffix) { MenuBar menuBar = new MenuBar(); MenuItem file = menuBar.addItem("File" + topLevelItemSuffix, null); - file.addItem("Open" + secondaryLevelItemSuffix, new MenuBarCommand()); - file.addItem("Save" + secondaryLevelItemSuffix, new MenuBarCommand()); + file.addItem("Open" + secondaryLevelItemSuffix, new MenuBarCommand()) + .setDescription("<b>Preformatted</b>\ndescription"); + file.addItem("Save" + secondaryLevelItemSuffix, new MenuBarCommand()) + .setDescription("plain description,\n <b>HTML</b> is visible", + ContentMode.TEXT); file.addItem("Save As.." + secondaryLevelItemSuffix, new MenuBarCommand()); file.addSeparator(); @@ -52,7 +56,9 @@ public class MenuBarUI extends AbstractTestUI { new MenuBarCommand()); file.addSeparator(); - file.addItem("Exit" + secondaryLevelItemSuffix, new MenuBarCommand()); + file.addItem("Exit" + secondaryLevelItemSuffix, new MenuBarCommand()) + .setDescription("<b>HTML</b><br/>description", + ContentMode.HTML); MenuItem edit = menuBar.addItem("Edit" + topLevelItemSuffix, null); edit.addItem("Copy" + secondaryLevelItemSuffix, new MenuBarCommand()); diff --git a/uitest/src/test/java/com/vaadin/tests/elements/menubar/MenuBarUITest.java b/uitest/src/test/java/com/vaadin/tests/elements/menubar/MenuBarUITest.java index dc960f4d49..0fd42421a9 100644 --- a/uitest/src/test/java/com/vaadin/tests/elements/menubar/MenuBarUITest.java +++ b/uitest/src/test/java/com/vaadin/tests/elements/menubar/MenuBarUITest.java @@ -15,6 +15,7 @@ */ package com.vaadin.tests.elements.menubar; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -121,6 +122,22 @@ public class MenuBarUITest extends MultiBrowserTest { assertTrue(isItemVisible("Open")); } + @Test + public void testMenuItemTooltips() { + MenuBarElement first = $(MenuBarElement.class).first(); + first.clickItem("File"); + assertTooltip("Open", "<b>Preformatted</b>\ndescription"); + assertTooltip("Save", "plain description, <b>HTML</b> is visible"); + assertTooltip("Exit", "HTML\ndescription"); + } + + private void assertTooltip(String menuItem, String expectedTooltipText) { + testBenchElement(getMenuElement(menuItem)).showTooltip(); + assertEquals("Unexpected tooltip when hovering '" + menuItem + "'", + expectedTooltipText, + findElement(By.className("v-tooltip-text")).getText()); + } + private boolean isItemVisible(String item) { for (WebElement webElement : getItemCaptions()) { try { |