aboutsummaryrefslogtreecommitdiffstats
path: root/server/src
diff options
context:
space:
mode:
Diffstat (limited to 'server/src')
-rw-r--r--server/src/main/java/com/vaadin/ui/AbstractListing.java258
-rw-r--r--server/src/main/java/com/vaadin/ui/AbstractMultiSelect.java134
-rw-r--r--server/src/main/java/com/vaadin/ui/AbstractSingleSelect.java66
-rw-r--r--server/src/main/java/com/vaadin/ui/CheckBoxGroup.java51
-rw-r--r--server/src/main/java/com/vaadin/ui/ComboBox.java153
-rw-r--r--server/src/main/java/com/vaadin/ui/DeclarativeCaptionGenerator.java50
-rw-r--r--server/src/main/java/com/vaadin/ui/DeclarativeIconGenerator.java52
-rw-r--r--server/src/main/java/com/vaadin/ui/DeclarativeItemEnabledProvider.java52
-rw-r--r--server/src/main/java/com/vaadin/ui/Grid.java14
-rw-r--r--server/src/main/java/com/vaadin/ui/RadioButtonGroup.java105
-rw-r--r--server/src/test/java/com/vaadin/tests/design/DeclarativeTestBase.java78
-rw-r--r--server/src/test/java/com/vaadin/tests/design/DeclarativeTestBaseBase.java7
-rw-r--r--server/src/test/java/com/vaadin/tests/server/component/abstractcomponent/AbstractComponentDeclarativeTestBase.java31
-rw-r--r--server/src/test/java/com/vaadin/tests/server/component/abstractlisting/AbstractListingDeclarativeTest.java181
-rw-r--r--server/src/test/java/com/vaadin/tests/server/component/abstractmultiselect/AbstractMultiSelectDeclarativeTest.java108
-rw-r--r--server/src/test/java/com/vaadin/tests/server/component/abstractsingleselect/AbstractSingleSelectDeclarativeTest.java140
-rw-r--r--server/src/test/java/com/vaadin/tests/server/component/checkboxgroup/CheckBoxGroupDeclarativeTest.java93
-rw-r--r--server/src/test/java/com/vaadin/tests/server/component/combobox/ComboBoxDeclarativeTest.java109
-rw-r--r--server/src/test/java/com/vaadin/tests/server/component/listselect/ListSelectDeclarativeTest.java69
-rw-r--r--server/src/test/java/com/vaadin/tests/server/component/nativeselect/NativeSelectDeclarativeTest.java42
-rw-r--r--server/src/test/java/com/vaadin/tests/server/component/radiobuttongroup/RadioButtonGroupDeclarativeTest.java93
-rw-r--r--server/src/test/java/com/vaadin/tests/server/component/twincolselect/TwinColSelectDeclarativeTest.java81
-rw-r--r--server/src/test/java/com/vaadin/ui/AbstractListingTest.java22
-rw-r--r--server/src/test/java/com/vaadin/ui/AbstractSingleSelectTest.java22
24 files changed, 1725 insertions, 286 deletions
diff --git a/server/src/main/java/com/vaadin/ui/AbstractListing.java b/server/src/main/java/com/vaadin/ui/AbstractListing.java
index f49d8715de..ce44bb8352 100644
--- a/server/src/main/java/com/vaadin/ui/AbstractListing.java
+++ b/server/src/main/java/com/vaadin/ui/AbstractListing.java
@@ -15,14 +15,25 @@
*/
package com.vaadin.ui;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
+import org.jsoup.nodes.Attributes;
+import org.jsoup.nodes.Element;
+
import com.vaadin.data.Listing;
import com.vaadin.data.SelectionModel;
import com.vaadin.server.AbstractExtension;
+import com.vaadin.server.Resource;
import com.vaadin.server.data.DataCommunicator;
import com.vaadin.server.data.DataGenerator;
import com.vaadin.server.data.DataProvider;
+import com.vaadin.server.data.Query;
+import com.vaadin.ui.declarative.DesignAttributeHandler;
+import com.vaadin.ui.declarative.DesignContext;
+import com.vaadin.ui.declarative.DesignException;
+import com.vaadin.ui.declarative.DesignFormatter;
/**
* A base class for listing components. Provides common handling for fetching
@@ -37,6 +48,16 @@ import com.vaadin.server.data.DataProvider;
*/
public abstract class AbstractListing<T> extends AbstractComponent
implements Listing<T> {
+ /**
+ * The item icon caption provider.
+ */
+ private ItemCaptionGenerator<T> itemCaptionGenerator = String::valueOf;
+
+ /**
+ * The item icon provider. It is up to the implementing class to support
+ * this or not.
+ */
+ private IconGenerator<T> itemIconGenerator = item -> null;
/**
* A helper base class for creating extensions for Listing components. This
@@ -143,6 +164,64 @@ public abstract class AbstractListing<T> extends AbstractComponent
}
/**
+ * Gets the item caption generator that is used to produce the strings shown
+ * in the combo box for each item.
+ *
+ * @return the item caption generator used, not null
+ */
+ protected ItemCaptionGenerator<T> getItemCaptionGenerator() {
+ return itemCaptionGenerator;
+ }
+
+ /**
+ * Sets the item caption generator that is used to produce the strings shown
+ * in the combo box for each item. By default,
+ * {@link String#valueOf(Object)} is used.
+ *
+ * @param itemCaptionGenerator
+ * the item caption provider to use, not null
+ */
+ protected void setItemCaptionGenerator(
+ ItemCaptionGenerator<T> itemCaptionGenerator) {
+ Objects.requireNonNull(itemCaptionGenerator,
+ "Item caption generators must not be null");
+ this.itemCaptionGenerator = itemCaptionGenerator;
+ getDataCommunicator().reset();
+ }
+
+ /**
+ * Sets the item icon generator that is used to produce custom icons for
+ * showing items in the popup. The generator can return null for items with
+ * no icon.
+ *
+ * @see IconGenerator
+ *
+ * @param itemIconGenerator
+ * the item icon generator to set, not null
+ * @throws NullPointerException
+ * if {@code itemIconGenerator} is {@code null}
+ */
+ protected void setItemIconGenerator(IconGenerator<T> itemIconGenerator) {
+ Objects.requireNonNull(itemIconGenerator,
+ "Item icon generator must not be null");
+ this.itemIconGenerator = itemIconGenerator;
+ getDataCommunicator().reset();
+ }
+
+ /**
+ * Gets the currently used item icon generator. The default item icon
+ * provider returns null for all items, resulting in no icons being used.
+ *
+ * @see IconGenerator
+ * @see #setItemIconGenerator(IconGenerator)
+ *
+ * @return the currently used item icon generator, not null
+ */
+ protected IconGenerator<T> getItemIconGenerator() {
+ return itemIconGenerator;
+ }
+
+ /**
* Adds the given data generator to this listing. If the generator was
* already added, does nothing.
*
@@ -172,4 +251,183 @@ public abstract class AbstractListing<T> extends AbstractComponent
public DataCommunicator<T> getDataCommunicator() {
return dataCommunicator;
}
+
+ @Override
+ public void writeDesign(Element design, DesignContext designContext) {
+ super.writeDesign(design, designContext);
+
+ // Write options if warranted
+ if (designContext.shouldWriteData(this)) {
+ writeItems(design, designContext);
+ }
+
+ AbstractListing<T> select = designContext.getDefaultInstance(this);
+ Attributes attr = design.attributes();
+ DesignAttributeHandler.writeAttribute("readonly", attr, isReadOnly(),
+ select.isReadOnly(), Boolean.class, designContext);
+ }
+
+ /**
+ * Writes the data source items to a design. Hierarchical select components
+ * should override this method to only write the root items.
+ *
+ * @param design
+ * the element into which to insert the items
+ * @param context
+ * the DesignContext instance used in writing
+ */
+ protected void writeItems(Element design, DesignContext context) {
+ getDataProvider().fetch(new Query<>())
+ .forEach(item -> writeItem(design, item, context));
+ }
+
+ /**
+ * Writes a data source Item to a design. Hierarchical select components
+ * should override this method to recursively write any child items as well.
+ *
+ * @param design
+ * the element into which to insert the item
+ * @param item
+ * the item to write
+ * @param context
+ * the DesignContext instance used in writing
+ * @return a JSOUP element representing the {@code item}
+ */
+ protected Element writeItem(Element design, T item, DesignContext context) {
+ Element element = design.appendElement("option");
+
+ String caption = getItemCaptionGenerator().apply(item);
+ if (caption != null) {
+ element.html(DesignFormatter.encodeForTextNode(caption));
+ } else {
+ element.html(DesignFormatter.encodeForTextNode(item.toString()));
+ }
+ element.attr("item", serializeDeclarativeRepresentation(item));
+
+ Resource icon = getItemIconGenerator().apply(item);
+ if (icon != null) {
+ DesignAttributeHandler.writeAttribute("icon", element.attributes(),
+ icon, null, Resource.class, context);
+ }
+
+ return element;
+ }
+
+ @Override
+ public void readDesign(Element design, DesignContext context) {
+ super.readDesign(design, context);
+ Attributes attr = design.attributes();
+ if (attr.hasKey("readonly")) {
+ setReadOnly(DesignAttributeHandler.readAttribute("readonly", attr,
+ Boolean.class));
+ }
+ readItems(design, context);
+ }
+
+ /**
+ * Reads the data source items from the {@code design}.
+ *
+ * @param design
+ * The element to obtain the state from
+ * @param context
+ * The DesignContext instance used for parsing the design
+ */
+ protected void readItems(Element design, DesignContext context) {
+ setItemCaptionGenerator(new DeclarativeCaptionGenerator<>());
+ setItemIconGenerator(new DeclarativeIconGenerator<>());
+ }
+
+ /**
+ * Reads an Item from a design and inserts it into the data source.
+ * <p>
+ * Doesn't care about selection/value (if any).
+ *
+ * @param child
+ * a child element representing the item
+ * @param context
+ * the DesignContext instance used in parsing
+ * @return the item id of the new item
+ *
+ * @throws DesignException
+ * if the tag name of the {@code child} element is not
+ * {@code option}.
+ */
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ protected T readItem(Element child, DesignContext context) {
+ if (!"option".equals(child.tagName())) {
+ throw new DesignException("Unrecognized child element in "
+ + getClass().getSimpleName() + ": " + child.tagName());
+ }
+
+ String serializedItem = "";
+ String caption = DesignFormatter.decodeFromTextNode(child.html());
+ List<T> items = new ArrayList<>();
+ if (child.hasAttr("item")) {
+ serializedItem = child.attr("item");
+ }
+
+ T item = deserializeDeclarativeRepresentation(serializedItem);
+ items.add(item);
+
+ ItemCaptionGenerator<T> captionGenerator = getItemCaptionGenerator();
+ if (captionGenerator instanceof DeclarativeCaptionGenerator) {
+ ((DeclarativeCaptionGenerator) captionGenerator).setCaption(item,
+ caption);
+ } else {
+ throw new IllegalStateException(String.format(
+ "Don't know how "
+ + "to set caption using current caption generator '%s'",
+ captionGenerator.getClass().getName()));
+ }
+
+ IconGenerator<T> iconGenerator = getItemIconGenerator();
+ if (child.hasAttr("icon")) {
+ if (iconGenerator instanceof DeclarativeIconGenerator) {
+ ((DeclarativeIconGenerator) iconGenerator).setIcon(item,
+ DesignAttributeHandler.readAttribute("icon",
+ child.attributes(), Resource.class));
+ } else {
+ throw new IllegalStateException(String.format(
+ "Don't know how "
+ + "to set icon using current caption generator '%s'",
+ iconGenerator.getClass().getName()));
+ }
+ }
+
+ return item;
+ }
+
+ /**
+ * Deserializes a string to a data item.
+ * <p>
+ * Default implementation is able to handle only {@link String} as an item
+ * type. There will be a {@link ClassCastException} if {@code T } is not a
+ * {@link String}.
+ *
+ * @see #serializeDeclarativeRepresentation(Object)
+ *
+ * @param item
+ * string to deserialize
+ * @throws ClassCastException
+ * if type {@code T} is not a {@link String}
+ * @return deserialized item
+ */
+ protected T deserializeDeclarativeRepresentation(String item) {
+ return (T) item;
+ }
+
+ /**
+ * Serializes an {@code item} to a string for saving declarative format.
+ * <p>
+ * Default implementation delegates a call to {@code item.toString()}.
+ *
+ * @see #serializeDeclarativeRepresentation(Object)
+ *
+ * @param item
+ * a data item
+ * @return string representation of the {@code item}.
+ */
+ protected String serializeDeclarativeRepresentation(T item) {
+ return item.toString();
+ }
}
diff --git a/server/src/main/java/com/vaadin/ui/AbstractMultiSelect.java b/server/src/main/java/com/vaadin/ui/AbstractMultiSelect.java
index 3c85496e41..5ae0ee4ebd 100644
--- a/server/src/main/java/com/vaadin/ui/AbstractMultiSelect.java
+++ b/server/src/main/java/com/vaadin/ui/AbstractMultiSelect.java
@@ -16,7 +16,9 @@
package com.vaadin.ui;
import java.lang.reflect.Method;
+import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Optional;
@@ -25,6 +27,8 @@ import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
+import org.jsoup.nodes.Element;
+
import com.vaadin.data.HasValue;
import com.vaadin.data.SelectionModel;
import com.vaadin.data.SelectionModel.Multi;
@@ -38,6 +42,8 @@ import com.vaadin.shared.AbstractFieldState;
import com.vaadin.shared.Registration;
import com.vaadin.shared.data.selection.MultiSelectServerRpc;
import com.vaadin.shared.ui.ListingJsonConstants;
+import com.vaadin.ui.declarative.DesignContext;
+import com.vaadin.ui.declarative.DesignException;
import com.vaadin.util.ReflectTools;
import elemental.json.JsonObject;
@@ -121,17 +127,6 @@ public abstract class AbstractMultiSelect<T> extends AbstractListing<T>
MultiSelectionEvent.class);
/**
- * The item icon caption provider.
- */
- private ItemCaptionGenerator<T> itemCaptionGenerator = String::valueOf;
-
- /**
- * The item icon provider. It is up to the implementing class to support
- * this or not.
- */
- private IconGenerator<T> itemIconGenerator = item -> null;
-
- /**
* The item enabled status provider. It is up to the implementing class to
* support this or not.
*/
@@ -163,30 +158,15 @@ public abstract class AbstractMultiSelect<T> extends AbstractListing<T>
SELECTION_CHANGE_METHOD);
}
- /**
- * Gets the item caption generator that is used to produce the strings shown
- * in the select for each item.
- *
- * @return the item caption generator used, not {@code null}
- * @see #setItemCaptionGenerator(ItemCaptionGenerator)
- */
+ @Override
public ItemCaptionGenerator<T> getItemCaptionGenerator() {
- return itemCaptionGenerator;
+ return super.getItemCaptionGenerator();
}
- /**
- * Sets the item caption generator that is used to produce the strings shown
- * in the select for each item. By default, {@link String#valueOf(Object)}
- * is used.
- *
- * @param itemCaptionGenerator
- * the item caption generator to use, not {@code null}
- */
+ @Override
public void setItemCaptionGenerator(
ItemCaptionGenerator<T> itemCaptionGenerator) {
- Objects.requireNonNull(itemCaptionGenerator);
- this.itemCaptionGenerator = itemCaptionGenerator;
- getDataCommunicator().reset();
+ super.setItemCaptionGenerator(itemCaptionGenerator);
}
/**
@@ -255,40 +235,6 @@ public abstract class AbstractMultiSelect<T> extends AbstractListing<T>
}
/**
- * Returns the item icon generator for this multiselect.
- * <p>
- * <em>Implementation note:</em> Override this method and
- * {@link #setItemIconGenerator(IconGenerator)} as {@code public} and invoke
- * {@code super} methods to support this feature in the multiselect
- * component.
- *
- * @return the item icon generator, not {@code null}
- * @see #setItemIconGenerator(IconGenerator)
- */
- protected IconGenerator<T> getItemIconGenerator() {
- return itemIconGenerator;
- }
-
- /**
- * Sets the item icon generator for this multiselect. The icon generator is
- * queried for each item to optionally display an icon next to the item
- * caption. If the generator returns null for an item, no icon is displayed.
- * The default provider always returns null (no icons).
- * <p>
- * <em>Implementation note:</em> Override this method and
- * {@link #getItemIconGenerator()} as {@code public} and invoke
- * {@code super} methods to support this feature in the multiselect
- * component.
- *
- * @param itemIconGenerator
- * the item icon generator to set, not {@code null}
- */
- protected void setItemIconGenerator(IconGenerator<T> itemIconGenerator) {
- Objects.requireNonNull(itemIconGenerator);
- this.itemIconGenerator = itemIconGenerator;
- }
-
- /**
* Returns the item enabled provider for this multiselect.
* <p>
* <em>Implementation note:</em> Override this method and
@@ -461,6 +407,66 @@ public abstract class AbstractMultiSelect<T> extends AbstractListing<T>
updateSelection(set -> set.add(item), userOriginated);
}
+ @Override
+ protected Collection<String> getCustomAttributes() {
+ Collection<String> attributes = super.getCustomAttributes();
+ // "value" is not an attribute for the component. "selected" attribute
+ // is used in "option"'s tag to mark selection which implies value for
+ // multiselect component
+ attributes.add("value");
+ return attributes;
+ }
+
+ @Override
+ protected Element writeItem(Element design, T item, DesignContext context) {
+ Element element = super.writeItem(design, item, context);
+
+ if (isSelected(item)) {
+ element.attr("selected", "");
+ }
+
+ return element;
+ }
+
+ @Override
+ protected void readItems(Element design, DesignContext context) {
+ super.readItems(design, context);
+ Set<T> selected = new HashSet<>();
+ design.children().stream()
+ .forEach(child -> readItem(child, selected, context));
+ deselectAll();
+ selected.forEach(this::select);
+ }
+
+ /**
+ * Reads an Item from a design and inserts it into the data source.
+ * Hierarchical select components should override this method to recursively
+ * recursively read any child items as well.
+ *
+ * @param child
+ * a child element representing the item
+ * @param selected
+ * A set accumulating selected items. If the item that is read is
+ * marked as selected, its item id should be added to this set.
+ * @param context
+ * the DesignContext instance used in parsing
+ * @return the item id of the new item
+ *
+ * @throws DesignException
+ * if the tag name of the {@code child} element is not
+ * {@code option}.
+ */
+ protected T readItem(Element child, Set<T> selected,
+ DesignContext context) {
+ T item = readItem(child, context);
+
+ if (child.hasAttr("selected")) {
+ selected.add(item);
+ }
+
+ return item;
+ }
+
private void updateSelection(Consumer<Set<T>> handler,
boolean userOriginated) {
LinkedHashSet<T> oldSelection = new LinkedHashSet<>(selection);
diff --git a/server/src/main/java/com/vaadin/ui/AbstractSingleSelect.java b/server/src/main/java/com/vaadin/ui/AbstractSingleSelect.java
index cdd91424e0..d2602746fe 100644
--- a/server/src/main/java/com/vaadin/ui/AbstractSingleSelect.java
+++ b/server/src/main/java/com/vaadin/ui/AbstractSingleSelect.java
@@ -16,8 +16,13 @@
package com.vaadin.ui;
import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.HashSet;
import java.util.Objects;
import java.util.Optional;
+import java.util.Set;
+
+import org.jsoup.nodes.Element;
import com.vaadin.data.HasValue;
import com.vaadin.data.SelectionModel;
@@ -28,6 +33,8 @@ import com.vaadin.server.data.DataCommunicator;
import com.vaadin.shared.Registration;
import com.vaadin.shared.data.selection.SelectionServerRpc;
import com.vaadin.shared.ui.AbstractSingleSelectState;
+import com.vaadin.ui.declarative.DesignContext;
+import com.vaadin.ui.declarative.DesignException;
import com.vaadin.util.ReflectTools;
/**
@@ -307,6 +314,65 @@ public abstract class AbstractSingleSelect<T> extends AbstractListing<T>
return Objects.equals(getValue(), item);
}
+ @Override
+ protected Element writeItem(Element design, T item, DesignContext context) {
+ Element element = super.writeItem(design, item, context);
+
+ if (isSelected(item)) {
+ element.attr("selected", "");
+ }
+
+ return element;
+ }
+
+ @Override
+ protected void readItems(Element design, DesignContext context) {
+ super.readItems(design, context);
+ Set<T> selected = new HashSet<>();
+ design.children().stream()
+ .forEach(child -> readItem(child, selected, context));
+ selected.forEach(this::setValue);
+ }
+
+ /**
+ * Reads an Item from a design and inserts it into the data source.
+ * Hierarchical select components should override this method to recursively
+ * recursively read any child items as well.
+ *
+ * @param child
+ * a child element representing the item
+ * @param selected
+ * A set accumulating selected items. If the item that is read is
+ * marked as selected, its item id should be added to this set.
+ * @param context
+ * the DesignContext instance used in parsing
+ * @return the item id of the new item
+ *
+ * @throws DesignException
+ * if the tag name of the {@code child} element is not
+ * {@code option}.
+ */
+ protected T readItem(Element child, Set<T> selected,
+ DesignContext context) {
+ T item = readItem(child, context);
+
+ if (child.hasAttr("selected")) {
+ selected.add(item);
+ }
+
+ return item;
+ }
+
+ @Override
+ protected Collection<String> getCustomAttributes() {
+ Collection<String> attributes = super.getCustomAttributes();
+ // "value" is not an attribute for the component. "selected" attribute
+ // is used in "option"'s tag to mark selection which implies value for
+ // single select component
+ attributes.add("value");
+ return attributes;
+ }
+
private void init() {
registerRpc(new SelectionServerRpc() {
diff --git a/server/src/main/java/com/vaadin/ui/CheckBoxGroup.java b/server/src/main/java/com/vaadin/ui/CheckBoxGroup.java
index d5df147f57..5098cca628 100644
--- a/server/src/main/java/com/vaadin/ui/CheckBoxGroup.java
+++ b/server/src/main/java/com/vaadin/ui/CheckBoxGroup.java
@@ -17,6 +17,9 @@
package com.vaadin.ui;
import java.util.Collection;
+import java.util.Set;
+
+import org.jsoup.nodes.Element;
import com.vaadin.data.Listing;
import com.vaadin.event.FieldEvents.BlurEvent;
@@ -30,6 +33,8 @@ import com.vaadin.server.SerializablePredicate;
import com.vaadin.server.data.DataProvider;
import com.vaadin.shared.Registration;
import com.vaadin.shared.ui.optiongroup.CheckBoxGroupState;
+import com.vaadin.ui.declarative.DesignContext;
+import com.vaadin.ui.declarative.DesignFormatter;
/**
* A group of Checkboxes. Individual checkboxes are made from items supplied by
@@ -171,4 +176,50 @@ public class CheckBoxGroup<T> extends AbstractMultiSelect<T>
public void removeBlurListener(BlurListener listener) {
removeListener(BlurEvent.EVENT_ID, BlurEvent.class, listener);
}
+
+ @Override
+ protected void readItems(Element design, DesignContext context) {
+ setItemEnabledProvider(new DeclarativeItemEnabledProvider<>());
+ super.readItems(design, context);
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ @Override
+ protected T readItem(Element child, Set<T> selected,
+ DesignContext context) {
+ T item = super.readItem(child, selected, context);
+
+ SerializablePredicate<T> provider = getItemEnabledProvider();
+ if (provider instanceof DeclarativeItemEnabledProvider) {
+ if (child.hasAttr("disabled")) {
+ ((DeclarativeItemEnabledProvider) provider).addDisabled(item);
+ }
+ } else {
+ throw new IllegalStateException(String.format(
+ "Don't know how "
+ + "to disable item using current item enabled provider '%s'",
+ provider.getClass().getName()));
+ }
+ return item;
+ }
+
+ @Override
+ protected Element writeItem(Element design, T item, DesignContext context) {
+ Element elem = super.writeItem(design, item, context);
+
+ if (!getItemEnabledProvider().test(item)) {
+ elem.attr("disabled", "");
+ }
+
+ if (isHtmlContentAllowed()) {
+ // need to unencode HTML entities. AbstractMultiSelect.writeDesign
+ // can't check if HTML content is allowed, so it always encodes
+ // entities like '>', '<' and '&'; in case HTML content is allowed
+ // this is undesirable so we need to unencode entities. Entities
+ // other than '<' and '>' will be taken care by Jsoup.
+ elem.html(DesignFormatter.decodeFromTextNode(elem.html()));
+ }
+
+ return elem;
+ }
}
diff --git a/server/src/main/java/com/vaadin/ui/ComboBox.java b/server/src/main/java/com/vaadin/ui/ComboBox.java
index 905d180a7f..a932d60423 100644
--- a/server/src/main/java/com/vaadin/ui/ComboBox.java
+++ b/server/src/main/java/com/vaadin/ui/ComboBox.java
@@ -18,10 +18,15 @@ package com.vaadin.ui;
import java.io.Serializable;
import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Consumer;
+import org.jsoup.nodes.Element;
+
import com.vaadin.data.HasValue;
import com.vaadin.event.FieldEvents;
import com.vaadin.event.FieldEvents.BlurEvent;
@@ -40,6 +45,9 @@ import com.vaadin.shared.data.DataCommunicatorConstants;
import com.vaadin.shared.ui.combobox.ComboBoxConstants;
import com.vaadin.shared.ui.combobox.ComboBoxServerRpc;
import com.vaadin.shared.ui.combobox.ComboBoxState;
+import com.vaadin.ui.declarative.DesignAttributeHandler;
+import com.vaadin.ui.declarative.DesignContext;
+import com.vaadin.ui.declarative.DesignFormatter;
import elemental.json.JsonObject;
@@ -64,6 +72,36 @@ public class ComboBox<T> extends AbstractSingleSelect<T> implements HasValue<T>,
}
/**
+ * Item style generator class for declarative support.
+ * <p>
+ * Provides a straightforward mapping between an item and its style.
+ *
+ * @param <T>
+ * item type
+ */
+ protected static class DeclarativeStyleGenerator<T>
+ implements StyleGenerator<T> {
+ private Map<T, String> styles = new HashMap<>();
+
+ @Override
+ public String apply(T item) {
+ return styles.get(item);
+ }
+
+ /**
+ * Sets a {@code style} for the {@code item}.
+ *
+ * @param item
+ * a data item
+ * @param style
+ * a style for the {@code item}
+ */
+ protected void setStyle(T item, String style) {
+ styles.put(item, style);
+ }
+ }
+
+ /**
* Filter can be used to customize the filtering of items based on user
* input.
*
@@ -107,10 +145,7 @@ public class ComboBox<T> extends AbstractSingleSelect<T> implements HasValue<T>,
*/
private NewItemHandler newItemHandler;
- private ItemCaptionGenerator<T> itemCaptionGenerator = String::valueOf;
-
private StyleGenerator<T> itemStyleGenerator = item -> null;
- private IconGenerator<T> itemIconGenerator = item -> null;
private ItemFilter<T> filter = (filterText, item) -> {
if (filterText == null) {
@@ -200,7 +235,7 @@ public class ComboBox<T> extends AbstractSingleSelect<T> implements HasValue<T>,
if (style != null) {
jsonObject.put(ComboBoxConstants.STYLE, style);
}
- Resource icon = itemIconGenerator.apply(data);
+ Resource icon = getItemIconGenerator().apply(data);
if (icon != null) {
String iconUrl = ResourceReference
.create(icon, ComboBox.this, null).getURL();
@@ -377,30 +412,15 @@ public class ComboBox<T> extends AbstractSingleSelect<T> implements HasValue<T>,
return getState(false).scrollToSelectedItem;
}
- /**
- * Gets the item caption generator that is used to produce the strings shown
- * in the combo box for each item.
- *
- * @return the item caption generator used, not null
- */
+ @Override
public ItemCaptionGenerator<T> getItemCaptionGenerator() {
- return itemCaptionGenerator;
+ return super.getItemCaptionGenerator();
}
- /**
- * Sets the item caption generator that is used to produce the strings shown
- * in the combo box for each item. By default,
- * {@link String#valueOf(Object)} is used.
- *
- * @param itemCaptionGenerator
- * the item caption provider to use, not null
- */
+ @Override
public void setItemCaptionGenerator(
ItemCaptionGenerator<T> itemCaptionGenerator) {
- Objects.requireNonNull(itemCaptionGenerator,
- "Item caption generators must not be null");
- this.itemCaptionGenerator = itemCaptionGenerator;
- getDataCommunicator().reset();
+ super.setItemCaptionGenerator(itemCaptionGenerator);
}
/**
@@ -437,36 +457,14 @@ public class ComboBox<T> extends AbstractSingleSelect<T> implements HasValue<T>,
return itemStyleGenerator;
}
- /**
- * Sets the item icon generator that is used to produce custom icons for
- * showing items in the popup. The generator can return null for items with
- * no icon.
- *
- * @see IconGenerator
- *
- * @param itemIconGenerator
- * the item icon generator to set, not null
- * @throws NullPointerException
- * if {@code itemIconGenerator} is {@code null}
- */
+ @Override
public void setItemIconGenerator(IconGenerator<T> itemIconGenerator) {
- Objects.requireNonNull(itemIconGenerator,
- "Item icon generator must not be null");
- this.itemIconGenerator = itemIconGenerator;
- getDataCommunicator().reset();
+ super.setItemIconGenerator(itemIconGenerator);
}
- /**
- * Gets the currently used item icon generator. The default item icon
- * provider returns null for all items, resulting in no icons being used.
- *
- * @see IconGenerator
- * @see #setItemIconGenerator(IconGenerator)
- *
- * @return the currently used item icon generator, not null
- */
+ @Override
public IconGenerator<T> getItemIconGenerator() {
- return itemIconGenerator;
+ return super.getItemIconGenerator();
}
/**
@@ -548,4 +546,61 @@ public class ComboBox<T> extends AbstractSingleSelect<T> implements HasValue<T>,
getState().selectedItemCaption = selectedCaption;
}
+ @Override
+ protected Element writeItem(Element design, T item, DesignContext context) {
+ Element element = design.appendElement("option");
+
+ String caption = getItemCaptionGenerator().apply(item);
+ if (caption != null) {
+ element.html(DesignFormatter.encodeForTextNode(caption));
+ } else {
+ element.html(DesignFormatter.encodeForTextNode(item.toString()));
+ }
+ element.attr("item", item.toString());
+
+ Resource icon = getItemIconGenerator().apply(item);
+ if (icon != null) {
+ DesignAttributeHandler.writeAttribute("icon", element.attributes(),
+ icon, null, Resource.class, context);
+ }
+
+ String style = getStyleGenerator().apply(item);
+ if (style != null) {
+ element.attr("style", style);
+ }
+
+ if (isSelected(item)) {
+ element.attr("selected", "");
+ }
+
+ return element;
+ }
+
+ @Override
+ protected void readItems(Element design, DesignContext context) {
+ setStyleGenerator(new DeclarativeStyleGenerator<>());
+ super.readItems(design, context);
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ @Override
+ protected T readItem(Element child, Set<T> selected,
+ DesignContext context) {
+ T item = super.readItem(child, selected, context);
+
+ if (child.hasAttr("style")) {
+ StyleGenerator<T> styleGenerator = getStyleGenerator();
+ if (styleGenerator instanceof DeclarativeStyleGenerator) {
+ ((DeclarativeStyleGenerator) styleGenerator).setStyle(item,
+ child.attr("style"));
+ } else {
+ throw new IllegalStateException(String.format(
+ "Don't know how "
+ + "to set style using current style generator '%s'",
+ styleGenerator.getClass().getName()));
+ }
+ }
+ return item;
+ }
+
}
diff --git a/server/src/main/java/com/vaadin/ui/DeclarativeCaptionGenerator.java b/server/src/main/java/com/vaadin/ui/DeclarativeCaptionGenerator.java
new file mode 100644
index 0000000000..9aa6035d9d
--- /dev/null
+++ b/server/src/main/java/com/vaadin/ui/DeclarativeCaptionGenerator.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2000-2016 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.ui;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Item caption generator class for declarative support.
+ * <p>
+ * Provides a straightforward mapping between an item and its caption.
+ *
+ * @param <T>
+ * item type
+ */
+class DeclarativeCaptionGenerator<T> implements ItemCaptionGenerator<T> {
+
+ private Map<T, String> captions = new HashMap<>();
+
+ @Override
+ public String apply(T item) {
+ return captions.get(item);
+ }
+
+ /**
+ * Sets a {@code caption} for the {@code item}.
+ *
+ * @param item
+ * a data item
+ * @param caption
+ * a caption for the {@code item}
+ */
+ protected void setCaption(T item, String caption) {
+ captions.put(item, caption);
+ }
+
+}
diff --git a/server/src/main/java/com/vaadin/ui/DeclarativeIconGenerator.java b/server/src/main/java/com/vaadin/ui/DeclarativeIconGenerator.java
new file mode 100644
index 0000000000..984ac4cf47
--- /dev/null
+++ b/server/src/main/java/com/vaadin/ui/DeclarativeIconGenerator.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2000-2016 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.ui;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.vaadin.server.Resource;
+
+/**
+ * Icon generator class for declarative support.
+ * <p>
+ * Provides a straightforward mapping between an item and its icon.
+ *
+ * @param <T>
+ * item type
+ */
+class DeclarativeIconGenerator<T> implements IconGenerator<T> {
+
+ private Map<T, Resource> captions = new HashMap<>();
+
+ @Override
+ public Resource apply(T item) {
+ return captions.get(item);
+ }
+
+ /**
+ * Sets an {@code icon} for the {@code item}.
+ *
+ * @param item
+ * a data item
+ * @param icon
+ * an icon for the {@code item}
+ */
+ protected void setIcon(T item, Resource icon) {
+ captions.put(item, icon);
+ }
+
+}
diff --git a/server/src/main/java/com/vaadin/ui/DeclarativeItemEnabledProvider.java b/server/src/main/java/com/vaadin/ui/DeclarativeItemEnabledProvider.java
new file mode 100644
index 0000000000..f16e54f1b2
--- /dev/null
+++ b/server/src/main/java/com/vaadin/ui/DeclarativeItemEnabledProvider.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2000-2016 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.ui;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.vaadin.server.SerializablePredicate;
+
+/**
+ * Item enabled provider class for declarative support.
+ * <p>
+ * Provides a straightforward mapping between an item and its enable state.
+ *
+ * @param <T>
+ * item type
+ */
+class DeclarativeItemEnabledProvider<T> implements SerializablePredicate<T> {
+
+ private Set<T> disabled = new HashSet<>();
+
+ @Override
+ public boolean test(T item) {
+ return !disabled.contains(item);
+ }
+
+ /**
+ * Adds the {@code item} to disabled items list.
+ *
+ * @param item
+ * a data item
+ * @param caption
+ * a caption for the {@code item}
+ */
+ protected void addDisabled(T item) {
+ disabled.add(item);
+ }
+
+}
diff --git a/server/src/main/java/com/vaadin/ui/Grid.java b/server/src/main/java/com/vaadin/ui/Grid.java
index a019476aeb..b9f5f5afbb 100644
--- a/server/src/main/java/com/vaadin/ui/Grid.java
+++ b/server/src/main/java/com/vaadin/ui/Grid.java
@@ -36,6 +36,8 @@ import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import org.jsoup.nodes.Element;
+
import com.vaadin.data.Binder;
import com.vaadin.data.SelectionModel;
import com.vaadin.event.ConnectorEvent;
@@ -65,6 +67,7 @@ import com.vaadin.ui.components.grid.Footer;
import com.vaadin.ui.components.grid.Header;
import com.vaadin.ui.components.grid.Header.Row;
import com.vaadin.ui.components.grid.SingleSelectionModel;
+import com.vaadin.ui.declarative.DesignContext;
import com.vaadin.ui.renderers.AbstractRenderer;
import com.vaadin.ui.renderers.Renderer;
import com.vaadin.ui.renderers.TextRenderer;
@@ -2668,4 +2671,15 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents {
fireEvent(new ColumnResizeEvent(this, column, userOriginated));
}
+ @Override
+ protected Element writeItem(Element design, T item, DesignContext context) {
+ // TODO see vaadin/framework8-issues#390
+ return null;
+ }
+
+ @Override
+ protected void readItems(Element design, DesignContext context) {
+ // TODO see vaadin/framework8-issues#390
+ }
+
}
diff --git a/server/src/main/java/com/vaadin/ui/RadioButtonGroup.java b/server/src/main/java/com/vaadin/ui/RadioButtonGroup.java
index 2a6ab48402..5ee3a7b807 100644
--- a/server/src/main/java/com/vaadin/ui/RadioButtonGroup.java
+++ b/server/src/main/java/com/vaadin/ui/RadioButtonGroup.java
@@ -18,6 +18,9 @@ package com.vaadin.ui;
import java.util.Collection;
import java.util.Objects;
+import java.util.Set;
+
+import org.jsoup.nodes.Element;
import com.vaadin.data.Listing;
import com.vaadin.event.FieldEvents.BlurEvent;
@@ -35,6 +38,8 @@ import com.vaadin.server.data.DataProvider;
import com.vaadin.shared.Registration;
import com.vaadin.shared.ui.ListingJsonConstants;
import com.vaadin.shared.ui.optiongroup.RadioButtonGroupState;
+import com.vaadin.ui.declarative.DesignContext;
+import com.vaadin.ui.declarative.DesignFormatter;
import elemental.json.JsonObject;
@@ -50,10 +55,6 @@ import elemental.json.JsonObject;
public class RadioButtonGroup<T> extends AbstractSingleSelect<T>
implements FocusNotifier, BlurNotifier {
- private IconGenerator<T> itemIconGenerator = item -> null;
-
- private ItemCaptionGenerator<T> itemCaptionGenerator = String::valueOf;
-
private SerializablePredicate<T> itemEnabledProvider = item -> true;
/**
@@ -174,59 +175,25 @@ public class RadioButtonGroup<T> extends AbstractSingleSelect<T>
return (RadioButtonGroupState) super.getState(markAsDirty);
}
- /**
- * Returns the item icon generator.
- *
- * @return the currently set icon generator
- * @see #setItemIconGenerator
- * @see IconGenerator
- */
+ @Override
public IconGenerator<T> getItemIconGenerator() {
- return itemIconGenerator;
+ return super.getItemIconGenerator();
}
- /**
- * Sets the item icon generator for this radiobutton group. The icon
- * generator is queried for each item to optionally display an icon next to
- * the item caption. If the generator returns null for an item, no icon is
- * displayed. The default generator always returns null (no icons).
- *
- * @param itemIconGenerator
- * the icon generator, not null
- * @see IconGenerator
- */
+ @Override
public void setItemIconGenerator(IconGenerator<T> itemIconGenerator) {
- Objects.requireNonNull(itemIconGenerator,
- "Item icon generator cannot be null.");
- this.itemIconGenerator = itemIconGenerator;
+ super.setItemIconGenerator(itemIconGenerator);
}
- /**
- * Returns the currently set item caption generator.
- *
- * @return the currently set caption generator
- * @see #setItemCaptionGenerator
- * @see ItemCaptionGenerator
- */
+ @Override
public ItemCaptionGenerator<T> getItemCaptionGenerator() {
- return itemCaptionGenerator;
+ return super.getItemCaptionGenerator();
}
- /**
- * Sets the item caption generator for this radiobutton group. The caption
- * generator is queried for each item to optionally display an item textual
- * representation. The default generator returns
- * {@code String.valueOf(item)}.
- *
- * @param itemCaptionGenerator
- * the item caption generator, not null
- * @see ItemCaptionGenerator
- */
+ @Override
public void setItemCaptionGenerator(
ItemCaptionGenerator<T> itemCaptionGenerator) {
- Objects.requireNonNull(itemCaptionGenerator,
- "Item caption generator cannot be null.");
- this.itemCaptionGenerator = itemCaptionGenerator;
+ super.setItemCaptionGenerator(itemCaptionGenerator);
}
/**
@@ -278,4 +245,50 @@ public class RadioButtonGroup<T> extends AbstractSingleSelect<T>
public void removeBlurListener(BlurListener listener) {
removeListener(BlurEvent.EVENT_ID, BlurEvent.class, listener);
}
+
+ @Override
+ protected void readItems(Element design, DesignContext context) {
+ setItemEnabledProvider(new DeclarativeItemEnabledProvider<>());
+ super.readItems(design, context);
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ @Override
+ protected T readItem(Element child, Set<T> selected,
+ DesignContext context) {
+ T item = super.readItem(child, selected, context);
+
+ SerializablePredicate<T> provider = getItemEnabledProvider();
+ if (provider instanceof DeclarativeItemEnabledProvider) {
+ if (child.hasAttr("disabled")) {
+ ((DeclarativeItemEnabledProvider) provider).addDisabled(item);
+ }
+ } else {
+ throw new IllegalStateException(String.format(
+ "Don't know how "
+ + "to disable item using current item enabled provider '%s'",
+ provider.getClass().getName()));
+ }
+ return item;
+ }
+
+ @Override
+ protected Element writeItem(Element design, T item, DesignContext context) {
+ Element elem = super.writeItem(design, item, context);
+
+ if (!getItemEnabledProvider().test(item)) {
+ elem.attr("disabled", "");
+ }
+
+ if (isHtmlContentAllowed()) {
+ // need to unencode HTML entities. AbstractMultiSelect.writeDesign
+ // can't check if HTML content is allowed, so it always encodes
+ // entities like '>', '<' and '&'; in case HTML content is allowed
+ // this is undesirable so we need to unencode entities. Entities
+ // other than '<' and '>' will be taken care by Jsoup.
+ elem.html(DesignFormatter.decodeFromTextNode(elem.html()));
+ }
+
+ return elem;
+ }
}
diff --git a/server/src/test/java/com/vaadin/tests/design/DeclarativeTestBase.java b/server/src/test/java/com/vaadin/tests/design/DeclarativeTestBase.java
index 3e8e4dccd6..168225b493 100644
--- a/server/src/test/java/com/vaadin/tests/design/DeclarativeTestBase.java
+++ b/server/src/test/java/com/vaadin/tests/design/DeclarativeTestBase.java
@@ -38,55 +38,31 @@ public abstract class DeclarativeTestBase<T extends Component>
public class IntrospectorEqualsAsserter<C> implements EqualsAsserter<C> {
- private final Class<C> c;
+ private final Class<C> clazz;
- public IntrospectorEqualsAsserter(Class<C> c) {
- this.c = c;
+ public IntrospectorEqualsAsserter(Class<C> clazz) {
+ this.clazz = clazz;
}
@Override
- public void assertObjectEquals(C o1, C o2) {
+ public void assertObjectEquals(C object1, C object2) {
try {
- BeanInfo bi = Introspector.getBeanInfo(c);
+ BeanInfo bi = Introspector.getBeanInfo(clazz);
for (PropertyDescriptor pd : bi.getPropertyDescriptors()) {
Method readMethod = pd.getReadMethod();
Method writeMethod = pd.getWriteMethod();
- if (readMethod == null || writeMethod == null) {
- continue;
- }
- // Needed to access public properties inherited from a
- // nonpublic superclass, see #17425
- readMethod.setAccessible(true);
- writeMethod.setAccessible(true);
- if (Connector.class.isAssignableFrom(c)
- && readMethod.getName().equals("getParent")) {
- // Hack to break cycles in the connector hierarchy
- continue;
- }
- try {
- c.getDeclaredMethod(readMethod.getName());
- } catch (Exception e) {
- // Not declared in this class, will be tested by parent
- // class tester
- if (debug) {
- System.out.println("Skipped " + c.getSimpleName()
- + "." + readMethod.getName());
- }
- continue;
- }
- if (debug) {
- System.out.println("Testing " + c.getSimpleName() + "."
- + readMethod.getName());
+ if (acceptProperty(clazz, readMethod, writeMethod)) {
+ Object property1 = readMethod.invoke(object1);
+ Object property2 = readMethod.invoke(object2);
+ assertEquals(pd.getDisplayName(), property1, property2);
}
- Object v1 = readMethod.invoke(o1);
- Object v2 = readMethod.invoke(o2);
- assertEquals(pd.getDisplayName(), v1, v2);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
+
}
{
@@ -106,6 +82,39 @@ public abstract class DeclarativeTestBase<T extends Component>
});
}
+ protected boolean acceptProperty(Class<?> clazz, Method readMethod,
+ Method writeMethod) {
+ if (readMethod == null || writeMethod == null) {
+ return false;
+ }
+ // Needed to access public properties inherited from a
+ // nonpublic superclass, see #17425
+ readMethod.setAccessible(true);
+ writeMethod.setAccessible(true);
+ if (Connector.class.isAssignableFrom(clazz)
+ && readMethod.getName().equals("getParent")) {
+ // Hack to break cycles in the connector hierarchy
+ return false;
+ }
+ try {
+ clazz.getDeclaredMethod(readMethod.getName());
+ } catch (Exception e) {
+ // Not declared in this class, will be tested by parent
+ // class tester
+ if (debug) {
+ System.out.println("Skipped " + clazz.getSimpleName() + "."
+ + readMethod.getName());
+ }
+ return false;
+ }
+
+ if (debug) {
+ System.out.println("Testing " + clazz.getSimpleName() + "."
+ + readMethod.getName());
+ }
+ return true;
+ }
+
@Override
protected EqualsAsserter getComparator(Class c) {
com.vaadin.tests.design.DeclarativeTestBaseBase.EqualsAsserter<?> comp = comparators
@@ -122,4 +131,5 @@ public abstract class DeclarativeTestBase<T extends Component>
}
return comp;
}
+
}
diff --git a/server/src/test/java/com/vaadin/tests/design/DeclarativeTestBaseBase.java b/server/src/test/java/com/vaadin/tests/design/DeclarativeTestBaseBase.java
index e2e8148871..161d053cca 100644
--- a/server/src/test/java/com/vaadin/tests/design/DeclarativeTestBaseBase.java
+++ b/server/src/test/java/com/vaadin/tests/design/DeclarativeTestBaseBase.java
@@ -219,11 +219,12 @@ public abstract class DeclarativeTestBaseBase<T extends Component> {
Assert.assertEquals("", l.getMessages());
}
- public void testWrite(String design, T expected, boolean writeData) {
- String written = write(expected, writeData);
+ public void testWrite(String expectedDesign, T component,
+ boolean writeData) {
+ String written = write(component, writeData);
Element producedElem = Jsoup.parse(written).body().child(0);
- Element comparableElem = Jsoup.parse(design).body().child(0);
+ Element comparableElem = Jsoup.parse(expectedDesign).body().child(0);
String produced = elementToHtml(producedElem);
String comparable = elementToHtml(comparableElem);
diff --git a/server/src/test/java/com/vaadin/tests/server/component/abstractcomponent/AbstractComponentDeclarativeTestBase.java b/server/src/test/java/com/vaadin/tests/server/component/abstractcomponent/AbstractComponentDeclarativeTestBase.java
index d4b5901922..7733855e95 100644
--- a/server/src/test/java/com/vaadin/tests/server/component/abstractcomponent/AbstractComponentDeclarativeTestBase.java
+++ b/server/src/test/java/com/vaadin/tests/server/component/abstractcomponent/AbstractComponentDeclarativeTestBase.java
@@ -18,6 +18,8 @@ package com.vaadin.tests.server.component.abstractcomponent;
import static org.junit.Assert.assertTrue;
import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.util.Locale;
import org.junit.Test;
@@ -71,7 +73,8 @@ public abstract class AbstractComponentDeclarativeTestBase<T extends AbstractCom
@Test
public void abstractComponentAttributesDeserialization()
- throws InstantiationException, IllegalAccessException {
+ throws InstantiationException, IllegalAccessException,
+ IllegalArgumentException, InvocationTargetException {
String id = "testId";
String caption = "testCaption";
boolean captionAsHtml = true;
@@ -87,12 +90,14 @@ public abstract class AbstractComponentDeclarativeTestBase<T extends AbstractCom
boolean responsive = true;
String styleName = "testStyleName";
boolean visible = false;
+ boolean requiredIndicator = true;
String design = String.format(
"<%s id='%s' caption='%s' caption-as-html description='%s' "
+ "error='%s' enabled='false' width='%s' height='%s' "
+ "icon='%s' locale='%s' primary-style-name='%s' "
- + "readonly responsive style-name='%s' visible='false'/>",
+ + "readonly responsive style-name='%s' visible='false' "
+ + "required-indicator-visible/>",
getComponentTag(), id, caption, description, error, width,
height, icon, locale.toString(), primaryStyle, styleName);
@@ -110,13 +115,9 @@ public abstract class AbstractComponentDeclarativeTestBase<T extends AbstractCom
component.setIcon(new FileResource(new File(icon)));
component.setLocale(locale);
component.setPrimaryStyleName(primaryStyle);
- try {
- component.getClass()
- .getMethod("setReadOnly", new Class[] { boolean.class })
- .invoke(component, readOnly);
- } catch (Exception e) {
- // Ignore
- }
+ callBooleanSetter(readOnly, "setReadOnly", component);
+ callBooleanSetter(requiredIndicator, "setRequiredIndicatorVisible",
+ component);
component.setResponsive(responsive);
component.setStyleName(styleName);
component.setVisible(visible);
@@ -125,6 +126,18 @@ public abstract class AbstractComponentDeclarativeTestBase<T extends AbstractCom
testWrite(design, component);
}
+ private void callBooleanSetter(boolean value, String setterName,
+ T component)
+ throws IllegalAccessException, InvocationTargetException {
+ try {
+ Method method = component.getClass().getMethod(setterName,
+ new Class[] { boolean.class });
+ method.invoke(component, value);
+ } catch (NoSuchMethodException ignore) {
+ // ignore if there is no such method
+ }
+ }
+
@Test
public void externalIcon()
throws InstantiationException, IllegalAccessException {
diff --git a/server/src/test/java/com/vaadin/tests/server/component/abstractlisting/AbstractListingDeclarativeTest.java b/server/src/test/java/com/vaadin/tests/server/component/abstractlisting/AbstractListingDeclarativeTest.java
new file mode 100644
index 0000000000..c056836dc0
--- /dev/null
+++ b/server/src/test/java/com/vaadin/tests/server/component/abstractlisting/AbstractListingDeclarativeTest.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2000-2016 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.server.component.abstractlisting;
+
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+
+import com.vaadin.server.ExternalResource;
+import com.vaadin.server.FileResource;
+import com.vaadin.server.Resource;
+import com.vaadin.server.SerializablePredicate;
+import com.vaadin.server.ThemeResource;
+import com.vaadin.tests.server.component.abstractcomponent.AbstractComponentDeclarativeTestBase;
+import com.vaadin.ui.AbstractComponent;
+import com.vaadin.ui.AbstractListing;
+import com.vaadin.ui.IconGenerator;
+import com.vaadin.ui.ItemCaptionGenerator;
+
+/**
+ * {@link AbstractListing} component declarative test.
+ * <p>
+ * Test ignores comparison for {@link ItemCaptionGenerator},
+ * {@link IconGenerator} and {@link SerializablePredicate} "properties" since
+ * they are functions and it doesn't matter which implementation is chosen. But
+ * test checks generated item captions, item icon generation and enabled items
+ * generations if they are available in the component as public methods.
+ * <p>
+ * Common {@link AbstractComponent} properties are tested in
+ * {@link AbstractComponentDeclarativeTestBase}
+ *
+ * @see AbstractComponentDeclarativeTestBase
+ *
+ * @author Vaadin Ltd
+ *
+ *
+ * @param <T>
+ * a component type
+ */
+@SuppressWarnings({ "unchecked", "rawtypes" })
+public abstract class AbstractListingDeclarativeTest<T extends AbstractListing>
+ extends AbstractComponentDeclarativeTestBase<T> {
+
+ private static final String EXTERNAL_URL = "http://example.com/example.gif";
+
+ private static final String FILE_PATH = "img/example.gif";
+
+ private static final String THEME_PATH = "example.gif";
+
+ @Test
+ public abstract void dataSerialization() throws InstantiationException,
+ IllegalAccessException, InvocationTargetException;
+
+ @Test
+ public abstract void valueSerialization() throws InstantiationException,
+ IllegalAccessException, InvocationTargetException;
+
+ @Test
+ public void itemIconsSerialization() throws InstantiationException,
+ IllegalAccessException, InvocationTargetException {
+ T component = getComponentClass().newInstance();
+ Method setIconGenerator = getIconGeneratorMethod(component);
+ if (setIconGenerator == null) {
+ return;
+ }
+
+ List<String> items = Arrays.asList("foo", "bar", "foobar", "barfoo");
+
+ String design = String.format(
+ "<%s>\n" + "<option item='foo' icon='%s'>foo</option>\n"
+ + "<option item='bar' icon='%s'>bar</option>"
+ + "<option item='foobar' icon='theme://%s'>foobar</option>"
+ + "<option item='barfoo'>barfoo</option>" + "</%s>",
+ getComponentTag(), EXTERNAL_URL, FILE_PATH, THEME_PATH,
+ getComponentTag());
+
+ component.setItems(items);
+ IconGenerator generator = item -> generateIcons(item, items);
+ setIconGenerator.invoke(component, generator);
+
+ testRead(design, component);
+ testWrite(design, component, true);
+ }
+
+ @Test
+ public void enabledItemsSerialization() throws InstantiationException,
+ IllegalAccessException, InvocationTargetException {
+ T component = getComponentClass().newInstance();
+ Method setEnabledITemsGenerator = getEnabledItemsProviderMethod(
+ component);
+ if (setEnabledITemsGenerator == null) {
+ return;
+ }
+
+ List<String> items = Arrays.asList("foo", "bar", "foobar");
+
+ String design = String.format(
+ "<%s>\n" + "<option item='foo'>foo</option>\n"
+ + "<option item='bar' disabled>bar</option>"
+ + "<option item='foobar'>foobar</option>",
+ getComponentTag(), getComponentTag());
+
+ component.setItems(items);
+ SerializablePredicate predicate = item -> !item.equals("bar");
+ setEnabledITemsGenerator.invoke(component, predicate);
+
+ testRead(design, component);
+ testWrite(design, component, true);
+ }
+
+ @Test
+ public abstract void readOnlySelection() throws InstantiationException,
+ IllegalAccessException, InvocationTargetException;
+
+ @Override
+ protected boolean acceptProperty(Class<?> clazz, Method readMethod,
+ Method writeMethod) {
+ if (readMethod != null) {
+ Class<?> returnType = readMethod.getReturnType();
+ if (ItemCaptionGenerator.class.equals(returnType)
+ || IconGenerator.class.equals(returnType)
+ || SerializablePredicate.class.equals(returnType)) {
+ return false;
+ }
+ }
+ return super.acceptProperty(clazz, readMethod, writeMethod);
+ }
+
+ private Method getIconGeneratorMethod(T component)
+ throws IllegalAccessException, InvocationTargetException {
+ try {
+ return component.getClass().getMethod("setItemIconGenerator",
+ new Class[] { IconGenerator.class });
+ } catch (NoSuchMethodException ignore) {
+ // ignore if there is no such method
+ return null;
+ }
+ }
+
+ private Method getEnabledItemsProviderMethod(T component)
+ throws IllegalAccessException, InvocationTargetException {
+ try {
+ return component.getClass().getMethod("setItemEnabledProvider",
+ new Class[] { SerializablePredicate.class });
+ } catch (NoSuchMethodException ignore) {
+ // ignore if there is no such method
+ return null;
+ }
+ }
+
+ private Resource generateIcons(Object item, List<String> items) {
+ int index = items.indexOf(item);
+ switch (index) {
+ case 0:
+ return new ExternalResource(EXTERNAL_URL);
+ case 1:
+ return new FileResource(new File(FILE_PATH));
+ case 2:
+ return new ThemeResource(THEME_PATH);
+ }
+ return null;
+ }
+
+}
diff --git a/server/src/test/java/com/vaadin/tests/server/component/abstractmultiselect/AbstractMultiSelectDeclarativeTest.java b/server/src/test/java/com/vaadin/tests/server/component/abstractmultiselect/AbstractMultiSelectDeclarativeTest.java
new file mode 100644
index 0000000000..da8127ea8d
--- /dev/null
+++ b/server/src/test/java/com/vaadin/tests/server/component/abstractmultiselect/AbstractMultiSelectDeclarativeTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2000-2016 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.server.component.abstractmultiselect;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
+import org.junit.Test;
+
+import com.vaadin.tests.server.component.abstractlisting.AbstractListingDeclarativeTest;
+import com.vaadin.ui.AbstractMultiSelect;
+
+/**
+ * {@link AbstractMultiSelect} component declarative test.
+ * <p>
+ * Test inherits test methods from a {@link AbstractListingDeclarativeTest}
+ * class providing here only common cases for {@link AbstractMultiSelect}s.
+ *
+ * @see AbstractListingDeclarativeTest
+ *
+ * @author Vaadin Ltd
+ *
+ *
+ * @param <T>
+ * a component type
+ */
+@SuppressWarnings({ "unchecked", "rawtypes" })
+public abstract class AbstractMultiSelectDeclarativeTest<T extends AbstractMultiSelect>
+ extends AbstractListingDeclarativeTest<T> {
+
+ @Override
+ @Test
+ public void dataSerialization() throws InstantiationException,
+ IllegalAccessException, InvocationTargetException {
+ List<String> items = Arrays.asList("foo", "bar", "foobar");
+
+ String design = String.format(
+ "<%s>\n" + "<option item='foo' selected>foo1</option>\n"
+ + "<option item='bar'>bar1</option>"
+ + "<option item='foobar' selected>foobar1</option></%s>",
+ getComponentTag(), getComponentTag());
+ T component = getComponentClass().newInstance();
+ component.setItems(items);
+ component.select("foo");
+ component.select("foobar");
+ component.setItemCaptionGenerator(item -> item + "1");
+
+ testRead(design, component);
+ testWrite(design, component, true);
+ }
+
+ @Override
+ @Test
+ public void valueSerialization() throws InstantiationException,
+ IllegalAccessException, InvocationTargetException {
+ List<String> items = Arrays.asList("foo", "bar", "foobar");
+
+ String design = String.format(
+ "<%s>\n" + "<option item='foo' selected>foo1</option>\n"
+ + "<option item='bar'>bar1</option>"
+ + "<option item='foobar' selected>foobar1</option></%s>",
+ getComponentTag(), getComponentTag());
+ T component = getComponentClass().newInstance();
+ component.setItems(items);
+ component.setValue(new HashSet<>(Arrays.asList("foo", "foobar")));
+ component.setItemCaptionGenerator(item -> item + "1");
+
+ testRead(design, component);
+ testWrite(design, component, true);
+ }
+
+ @Override
+ @Test
+ public void readOnlySelection() throws InstantiationException,
+ IllegalAccessException, InvocationTargetException {
+ T component = getComponentClass().newInstance();
+
+ List<String> items = Arrays.asList("foo", "bar", "foobar");
+
+ String design = String.format(
+ "<%s readonly>\n" + "<option item='foo'>foo</option>\n"
+ + "<option item='bar'>bar</option>"
+ + "<option item='foobar'>foobar</option>",
+ getComponentTag(), getComponentTag());
+
+ component.setItems(items);
+ component.setReadOnly(true);
+
+ testRead(design, component);
+ testWrite(design, component, true);
+ }
+
+}
diff --git a/server/src/test/java/com/vaadin/tests/server/component/abstractsingleselect/AbstractSingleSelectDeclarativeTest.java b/server/src/test/java/com/vaadin/tests/server/component/abstractsingleselect/AbstractSingleSelectDeclarativeTest.java
new file mode 100644
index 0000000000..48b71ffcf1
--- /dev/null
+++ b/server/src/test/java/com/vaadin/tests/server/component/abstractsingleselect/AbstractSingleSelectDeclarativeTest.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2000-2016 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.server.component.abstractsingleselect;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+
+import com.vaadin.tests.server.component.abstractlisting.AbstractListingDeclarativeTest;
+import com.vaadin.ui.AbstractSingleSelect;
+import com.vaadin.ui.ItemCaptionGenerator;
+
+/**
+ * {@link AbstractSingleSelect} component declarative test.
+ * <p>
+ * Test inherits test methods from a {@link AbstractListingDeclarativeTest}
+ * class providing here only common cases for {@link AbstractSingleSelect}s.
+ *
+ * @author Vaadin Ltd
+ *
+ *
+ * @param <T>
+ * a component type
+ */
+@SuppressWarnings({ "unchecked", "rawtypes" })
+public abstract class AbstractSingleSelectDeclarativeTest<T extends AbstractSingleSelect>
+ extends AbstractListingDeclarativeTest<T> {
+
+ @Override
+ @Test
+ public void dataSerialization() throws InstantiationException,
+ IllegalAccessException, InvocationTargetException {
+ List<String> items = Arrays.asList("foo", "bar", "foobar");
+
+ String design = String.format(
+ "<%s>\n" + "<option item='foo'>foo</option>\n"
+ + "<option item='bar' selected>bar</option>"
+ + "<option item='foobar'>foobar</option></%s>",
+ getComponentTag(), getComponentTag());
+ T component = getComponentClass().newInstance();
+ component.setItems(items);
+ component.setSelectedItem("bar");
+
+ testRead(design, component);
+ testWrite(design, component, true);
+ }
+
+ @Override
+ @Test
+ public void valueSerialization() throws InstantiationException,
+ IllegalAccessException, InvocationTargetException {
+ List<String> items = Arrays.asList("foo", "bar", "foobar");
+
+ String design = String.format(
+ "<%s>\n" + "<option item='foo'>foo</option>\n"
+ + "<option item='bar' selected>bar</option>"
+ + "<option item='foobar'>foobar</option></%s>",
+ getComponentTag(), getComponentTag());
+ T component = getComponentClass().newInstance();
+ component.setItems(items);
+ component.setValue("bar");
+
+ testRead(design, component);
+ testWrite(design, component, true);
+ }
+
+ @Test
+ public void dataWithCaptionGeneratorSerialization()
+ throws InstantiationException, IllegalAccessException,
+ InvocationTargetException {
+ List<String> items = Arrays.asList("foo", "bar", "foobar");
+ T component = getComponentClass().newInstance();
+ Method setItemCaptionGenerator = getItemCaptionGeneratorMethod(
+ component);
+ if (setItemCaptionGenerator == null) {
+ return;
+ }
+ String design = String.format(
+ "<%s>\n" + "<option item='foo'>foo1</option>\n"
+ + "<option item='bar' selected>bar1</option>"
+ + "<option item='foobar'>foobar1</option></%s>",
+ getComponentTag(), getComponentTag());
+ component.setItems(items);
+ component.setValue("bar");
+ ItemCaptionGenerator generator = item -> item + "1";
+ setItemCaptionGenerator.invoke(component, generator);
+
+ testRead(design, component);
+ testWrite(design, component, true);
+ }
+
+ @Override
+ @Test
+ public void readOnlySelection() throws InstantiationException,
+ IllegalAccessException, InvocationTargetException {
+ T component = getComponentClass().newInstance();
+
+ List<String> items = Arrays.asList("foo", "bar", "foobar");
+
+ String design = String.format(
+ "<%s readonly>\n" + "<option item='foo'>foo</option>\n"
+ + "<option item='bar'>bar</option>"
+ + "<option item='foobar'>foobar</option>",
+ getComponentTag(), getComponentTag());
+
+ component.setItems(items);
+ component.setReadOnly(true);
+
+ testRead(design, component);
+ testWrite(design, component, true);
+ }
+
+ private Method getItemCaptionGeneratorMethod(T component)
+ throws IllegalAccessException, InvocationTargetException {
+ try {
+ return component.getClass().getMethod("setItemCaptionGenerator",
+ new Class[] { ItemCaptionGenerator.class });
+ } catch (NoSuchMethodException ignore) {
+ // ignore if there is no such method
+ return null;
+ }
+ }
+
+}
diff --git a/server/src/test/java/com/vaadin/tests/server/component/checkboxgroup/CheckBoxGroupDeclarativeTest.java b/server/src/test/java/com/vaadin/tests/server/component/checkboxgroup/CheckBoxGroupDeclarativeTest.java
new file mode 100644
index 0000000000..c28776f0dc
--- /dev/null
+++ b/server/src/test/java/com/vaadin/tests/server/component/checkboxgroup/CheckBoxGroupDeclarativeTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2000-2016 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.server.component.checkboxgroup;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+
+import com.vaadin.tests.server.component.abstractmultiselect.AbstractMultiSelectDeclarativeTest;
+import com.vaadin.ui.CheckBoxGroup;
+
+/**
+ * Declarative support test for CheckBoxGroup.
+ * <p>
+ * Only {@link CheckBoxGroup#setHtmlContentAllowed(boolean)} is tested here
+ * explicitly. All other tests are in the super class (
+ * {@link AbstractMultiSelectDeclarativeTest}).
+ *
+ * @see AbstractMultiSelectDeclarativeTest
+ *
+ * @author Vaadin Ltd
+ *
+ */
+@SuppressWarnings("rawtypes")
+public class CheckBoxGroupDeclarativeTest
+ extends AbstractMultiSelectDeclarativeTest<CheckBoxGroup> {
+
+ private static final String SIMPLE_HTML = "<span>foo</span>";
+
+ private static final String HTML = "<div class='wrapper'><div>bar</div></div>";
+
+ private static final String HTML_ENTITIES = "<b>a & b</b>";
+
+ @Test
+ public void serializeDataWithHtmlContentAllowed() {
+ CheckBoxGroup<String> group = new CheckBoxGroup<>();
+
+ List<String> items = Arrays.asList("foo", "bar", "foobar");
+
+ String design = String.format(
+ "<%s html-content-allowed>\n"
+ + "<option item='foo'>%s</option>\n"
+ + "<option item='bar'>%s</option>"
+ + "<option item='foobar'>%s</option>",
+ getComponentTag(), SIMPLE_HTML, HTML,
+ HTML_ENTITIES.replace("&", "&amp;"), getComponentTag());
+
+ group.setItems(items);
+ group.setHtmlContentAllowed(true);
+ group.setItemCaptionGenerator(item -> generateCaption(item, items));
+
+ testRead(design, group);
+ testWrite(design, group, true);
+ }
+
+ @Override
+ protected String getComponentTag() {
+ return "vaadin-check-box-group";
+ }
+
+ @Override
+ protected Class<CheckBoxGroup> getComponentClass() {
+ return CheckBoxGroup.class;
+ }
+
+ private String generateCaption(String item, List<String> items) {
+ int index = items.indexOf(item);
+ switch (index) {
+ case 0:
+ return SIMPLE_HTML;
+ case 1:
+ return HTML;
+ case 2:
+ return HTML_ENTITIES;
+ }
+ return null;
+ }
+
+} \ No newline at end of file
diff --git a/server/src/test/java/com/vaadin/tests/server/component/combobox/ComboBoxDeclarativeTest.java b/server/src/test/java/com/vaadin/tests/server/component/combobox/ComboBoxDeclarativeTest.java
new file mode 100644
index 0000000000..d0ec56e0a7
--- /dev/null
+++ b/server/src/test/java/com/vaadin/tests/server/component/combobox/ComboBoxDeclarativeTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2000-2016 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.server.component.combobox;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+
+import com.vaadin.tests.server.component.abstractsingleselect.AbstractSingleSelectDeclarativeTest;
+import com.vaadin.ui.ComboBox;
+import com.vaadin.ui.StyleGenerator;
+
+/**
+ * Declarative support test for ComboBox.
+ * <p>
+ * There are only ComboBox specific properties explicit tests. All other tests
+ * are in the super class ( {@link AbstractSingleSelectDeclarativeTest}).
+ *
+ * @see AbstractSingleSelectDeclarativeTest
+ *
+ * @author Vaadin Ltd
+ *
+ */
+@SuppressWarnings("rawtypes")
+public class ComboBoxDeclarativeTest
+ extends AbstractSingleSelectDeclarativeTest<ComboBox> {
+
+ @Test
+ public void comboBoxSpecificPropertiesSerialize() {
+ String placeholder = "testPlaceholder";
+ boolean textInputAllowed = false;
+ int pageLength = 7;
+ String popupWidth = "11%";
+ boolean emptySelectionAllowed = false;
+
+ String design = String.format(
+ "<%s placeholder='%s' text-input-allowed='%s' page-length='%d' "
+ + "popup-width='%s' empty-selection-allowed='%s' scroll-to-selected-item/>",
+ getComponentTag(), placeholder, textInputAllowed, pageLength,
+ popupWidth, emptySelectionAllowed);
+
+ ComboBox<String> comboBox = new ComboBox<>();
+ comboBox.setPlaceholder(placeholder);
+ comboBox.setTextInputAllowed(textInputAllowed);
+ comboBox.setPageLength(pageLength);
+ comboBox.setPopupWidth(popupWidth);
+ comboBox.setScrollToSelectedItem(true);
+ comboBox.setEmptySelectionAllowed(emptySelectionAllowed);
+
+ testRead(design, comboBox);
+ testWrite(design, comboBox);
+ }
+
+ @Test
+ public void optionStylesSerialization() throws InstantiationException,
+ IllegalAccessException, InvocationTargetException {
+ List<String> items = Arrays.asList("foo", "bar", "foobar");
+
+ String design = String.format(
+ "<%s>\n" + "<option item='foo' style='foo-style'>foo</option>\n"
+ + "<option item='bar' style='bar-style'>bar</option>"
+ + "<option item='foobar' style='foobar-style'>foobar</option></%s>",
+ getComponentTag(), getComponentTag());
+ ComboBox<String> comboBox = new ComboBox<>();
+ comboBox.setItems(items);
+ comboBox.setStyleGenerator(item -> item + "-style");
+
+ testRead(design, comboBox);
+ testWrite(design, comboBox, true);
+ }
+
+ @Override
+ protected String getComponentTag() {
+ return "vaadin-combo-box";
+ }
+
+ @Override
+ protected Class<? extends ComboBox> getComponentClass() {
+ return ComboBox.class;
+ }
+
+ @Override
+ protected boolean acceptProperty(Class<?> clazz, Method readMethod,
+ Method writeMethod) {
+ if (readMethod != null) {
+ Class<?> returnType = readMethod.getReturnType();
+ if (StyleGenerator.class.equals(returnType))
+ return false;
+ }
+ return super.acceptProperty(clazz, readMethod, writeMethod);
+ }
+
+}
diff --git a/server/src/test/java/com/vaadin/tests/server/component/listselect/ListSelectDeclarativeTest.java b/server/src/test/java/com/vaadin/tests/server/component/listselect/ListSelectDeclarativeTest.java
index a3079bcdc3..a71aed674b 100644
--- a/server/src/test/java/com/vaadin/tests/server/component/listselect/ListSelectDeclarativeTest.java
+++ b/server/src/test/java/com/vaadin/tests/server/component/listselect/ListSelectDeclarativeTest.java
@@ -15,59 +15,48 @@
*/
package com.vaadin.tests.server.component.listselect;
-import java.util.Arrays;
-
import org.junit.Test;
-import com.vaadin.tests.design.DeclarativeTestBase;
+import com.vaadin.tests.server.component.abstractmultiselect.AbstractMultiSelectDeclarativeTest;
import com.vaadin.ui.ListSelect;
+/**
+ * List select declarative test.
+ * <p>
+ * There is only {@link ListSelect#setRows(int)}/{@link ListSelect#getRows()}
+ * explicit test. All other tests are in the super class (
+ * {@link AbstractMultiSelectDeclarativeTest}).
+ *
+ * @see AbstractMultiSelectDeclarativeTest
+ *
+ * @author Vaadin Ltd
+ *
+ */
+@SuppressWarnings("rawtypes")
public class ListSelectDeclarativeTest
- extends DeclarativeTestBase<ListSelect<String>> {
-
- private ListSelect<String> getWithOptionsExpected() {
- ListSelect<String> ls = new ListSelect<>(null,
- Arrays.asList("Male", "Female"));
- ls.setRows(9); // 10 is default
- return ls;
- }
-
- private String getWithOptionsDesign() {
- return "<vaadin-list-select rows=9>\n"
- + " <option>Male</option>\n"
- + " <option>Female</option>\n"
- + "</vaadin-list-select>\n" + "";
- }
+ extends AbstractMultiSelectDeclarativeTest<ListSelect> {
@Test
- public void testReadWithOptions() {
- testRead(getWithOptionsDesign(), getWithOptionsExpected());
- }
+ public void rowsPropertySerialization() {
+ int rows = 7;
+ String design = String.format("<%s rows='%s'/>", getComponentTag(),
+ rows);
- @Test
- public void testWriteWithOptions() {
- testWrite(stripOptionTags(getWithOptionsDesign()),
- getWithOptionsExpected());
- }
+ ListSelect<String> select = new ListSelect<>();
+ select.setRows(rows);
- private ListSelect<String> getBasicExpected() {
- ListSelect<String> ls = new ListSelect<>();
- ls.setCaption("Hello");
- return ls;
+ testRead(design, select);
+ testWrite(design, select);
}
- private String getBasicDesign() {
- return "<vaadin-list-select caption='Hello' />";
+ @Override
+ protected String getComponentTag() {
+ return "vaadin-list-select";
}
- @Test
- public void testReadBasic() {
- testRead(getBasicDesign(), getBasicExpected());
- }
-
- @Test
- public void testWriteBasic() {
- testWrite(getBasicDesign(), getBasicExpected());
+ @Override
+ protected Class<? extends ListSelect> getComponentClass() {
+ return ListSelect.class;
}
}
diff --git a/server/src/test/java/com/vaadin/tests/server/component/nativeselect/NativeSelectDeclarativeTest.java b/server/src/test/java/com/vaadin/tests/server/component/nativeselect/NativeSelectDeclarativeTest.java
new file mode 100644
index 0000000000..f7114394d4
--- /dev/null
+++ b/server/src/test/java/com/vaadin/tests/server/component/nativeselect/NativeSelectDeclarativeTest.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2000-2016 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.server.component.nativeselect;
+
+import com.vaadin.tests.server.component.abstractsingleselect.AbstractSingleSelectDeclarativeTest;
+import com.vaadin.ui.NativeSelect;
+
+/**
+ * Declarative support tests for {@link NativeSelect}. All tests are in the
+ * super class ({@link AbstractSingleSelectDeclarativeTest}). This class
+ * declares only tag name and native select class (test parameters).
+ *
+ * @author Vaadin Ltd
+ *
+ */
+public class NativeSelectDeclarativeTest
+ extends AbstractSingleSelectDeclarativeTest<NativeSelect> {
+
+ @Override
+ protected String getComponentTag() {
+ return "vaadin-native-select";
+ }
+
+ @Override
+ protected Class<NativeSelect> getComponentClass() {
+ return NativeSelect.class;
+ }
+
+}
diff --git a/server/src/test/java/com/vaadin/tests/server/component/radiobuttongroup/RadioButtonGroupDeclarativeTest.java b/server/src/test/java/com/vaadin/tests/server/component/radiobuttongroup/RadioButtonGroupDeclarativeTest.java
new file mode 100644
index 0000000000..620fb170b6
--- /dev/null
+++ b/server/src/test/java/com/vaadin/tests/server/component/radiobuttongroup/RadioButtonGroupDeclarativeTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2000-2016 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.server.component.radiobuttongroup;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+
+import com.vaadin.tests.server.component.abstractsingleselect.AbstractSingleSelectDeclarativeTest;
+import com.vaadin.ui.RadioButtonGroup;
+
+/**
+ * Declarative support test for RadioButtonGroup.
+ * <p>
+ * Only {@link RadioButtonGroup#setHtmlContentAllowed(boolean)} is tested here
+ * explicitly. All other tests are in the super class (
+ * {@link AbstractSingleSelectDeclarativeTest}).
+ *
+ * @see AbstractSingleSelectDeclarativeTest
+ *
+ * @author Vaadin Ltd
+ *
+ */
+@SuppressWarnings("rawtypes")
+public class RadioButtonGroupDeclarativeTest
+ extends AbstractSingleSelectDeclarativeTest<RadioButtonGroup> {
+
+ private static final String SIMPLE_HTML = "<span>foo</span>";
+
+ private static final String HTML = "<div class='wrapper'><div>bar</div></div>";
+
+ private static final String HTML_ENTITIES = "<b>a & b</b>";
+
+ @Test
+ public void serializeDataWithHtmlContentAllowed() {
+ RadioButtonGroup<String> group = new RadioButtonGroup<>();
+
+ List<String> items = Arrays.asList("foo", "bar", "foobar");
+
+ String design = String.format(
+ "<%s html-content-allowed>\n"
+ + "<option item='foo'>%s</option>\n"
+ + "<option item='bar'>%s</option>"
+ + "<option item='foobar'>%s</option>",
+ getComponentTag(), SIMPLE_HTML, HTML,
+ HTML_ENTITIES.replace("&", "&amp;"), getComponentTag());
+
+ group.setItems(items);
+ group.setHtmlContentAllowed(true);
+ group.setItemCaptionGenerator(item -> generateCaption(item, items));
+
+ testRead(design, group);
+ testWrite(design, group, true);
+ }
+
+ @Override
+ protected String getComponentTag() {
+ return "vaadin-radio-button-group";
+ }
+
+ @Override
+ protected Class<RadioButtonGroup> getComponentClass() {
+ return RadioButtonGroup.class;
+ }
+
+ private String generateCaption(String item, List<String> items) {
+ int index = items.indexOf(item);
+ switch (index) {
+ case 0:
+ return SIMPLE_HTML;
+ case 1:
+ return HTML;
+ case 2:
+ return HTML_ENTITIES;
+ }
+ return null;
+ }
+
+} \ No newline at end of file
diff --git a/server/src/test/java/com/vaadin/tests/server/component/twincolselect/TwinColSelectDeclarativeTest.java b/server/src/test/java/com/vaadin/tests/server/component/twincolselect/TwinColSelectDeclarativeTest.java
index 87d4c8c216..99841e9cf9 100644
--- a/server/src/test/java/com/vaadin/tests/server/component/twincolselect/TwinColSelectDeclarativeTest.java
+++ b/server/src/test/java/com/vaadin/tests/server/component/twincolselect/TwinColSelectDeclarativeTest.java
@@ -17,61 +17,70 @@ package com.vaadin.tests.server.component.twincolselect;
import org.junit.Test;
-import com.vaadin.tests.design.DeclarativeTestBase;
+import com.vaadin.tests.server.component.abstractmultiselect.AbstractMultiSelectDeclarativeTest;
import com.vaadin.ui.TwinColSelect;
/**
- * Test cases for reading the properties of selection components.
- *
+ * TwinColSelectt declarative test.
+ * <p>
+ * There are only TwinColSelect specific properties explicit tests. All other
+ * tests are in the super class ( {@link AbstractMultiSelectDeclarativeTest}).
+ *
+ * @see AbstractMultiSelectDeclarativeTest
+ *
* @author Vaadin Ltd
+ *
*/
public class TwinColSelectDeclarativeTest
- extends DeclarativeTestBase<TwinColSelect<String>> {
+ extends AbstractMultiSelectDeclarativeTest<TwinColSelect> {
- public String getBasicDesign() {
- return "<vaadin-twin-col-select rows=5 right-column-caption='Selected values' left-column-caption='Unselected values'>\n"
- + " <option>First item</option>\n"
- + " <option selected>Second item</option>\n"
- + " <option selected>Third item</option>\n"
- + "</vaadin-twin-col-select>";
+ @Test
+ public void rowsPropertySerialization() {
+ int rows = 7;
+ String design = String.format("<%s rows='%s'/>", getComponentTag(),
+ rows);
- }
+ TwinColSelect<String> select = new TwinColSelect<>();
+ select.setRows(rows);
- public TwinColSelect<String> getBasicExpected() {
- TwinColSelect<String> s = new TwinColSelect<>();
- s.setRightColumnCaption("Selected values");
- s.setLeftColumnCaption("Unselected values");
- /*
- * This is broken for now : declarative doesn't read data and doesn't
- * set value/selection. See #388
- *
- * s.setItems("First item", "Second item", "Third item");
- * s.getSelectionModel().select("Second item");
- * s.getSelectionModel().select("Third item");
- *
- */
- s.setRows(5);
- return s;
+ testRead(design, select);
+ testWrite(design, select);
}
@Test
- public void testReadBasic() {
- testRead(getBasicDesign(), getBasicExpected());
+ public void rightColumnCaptionPropertySerialization() {
+ String rightColumnCaption = "foo";
+ String design = String.format("<%s right-column-caption='%s'/>",
+ getComponentTag(), rightColumnCaption);
+
+ TwinColSelect<String> select = new TwinColSelect<>();
+ select.setRightColumnCaption(rightColumnCaption);
+
+ testRead(design, select);
+ testWrite(design, select);
}
@Test
- public void testWriteBasic() {
- testWrite(stripOptionTags(getBasicDesign()), getBasicExpected());
+ public void leftColumnCaptionPropertySerialization() {
+ String leftColumnCaption = "foo";
+ String design = String.format("<%s left-column-caption='%s'/>",
+ getComponentTag(), leftColumnCaption);
+
+ TwinColSelect<String> select = new TwinColSelect<>();
+ select.setLeftColumnCaption(leftColumnCaption);
+
+ testRead(design, select);
+ testWrite(design, select);
}
- @Test
- public void testReadEmpty() {
- testRead("<vaadin-twin-col-select />", new TwinColSelect());
+ @Override
+ protected String getComponentTag() {
+ return "vaadin-twin-col-select";
}
- @Test
- public void testWriteEmpty() {
- testWrite("<vaadin-twin-col-select />", new TwinColSelect());
+ @Override
+ protected Class<? extends TwinColSelect> getComponentClass() {
+ return TwinColSelect.class;
}
} \ No newline at end of file
diff --git a/server/src/test/java/com/vaadin/ui/AbstractListingTest.java b/server/src/test/java/com/vaadin/ui/AbstractListingTest.java
index eca5eed854..b2f303ae24 100644
--- a/server/src/test/java/com/vaadin/ui/AbstractListingTest.java
+++ b/server/src/test/java/com/vaadin/ui/AbstractListingTest.java
@@ -6,15 +6,17 @@ import java.util.LinkedList;
import java.util.List;
import java.util.stream.Stream;
-import com.vaadin.server.data.BackEndDataProvider;
+import org.jsoup.nodes.Element;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
+import com.vaadin.server.data.BackEndDataProvider;
import com.vaadin.server.data.DataProvider;
import com.vaadin.server.data.ListDataProvider;
import com.vaadin.server.data.Query;
import com.vaadin.ui.AbstractListing.AbstractListingExtension;
+import com.vaadin.ui.declarative.DesignContext;
import elemental.json.JsonObject;
@@ -28,6 +30,16 @@ public class AbstractListingTest {
public void runDataGeneration() {
super.getDataCommunicator().beforeClientResponse(true);
}
+
+ @Override
+ protected Element writeItem(Element design, String item,
+ DesignContext context) {
+ return null;
+ }
+
+ @Override
+ protected void readItems(Element design, DesignContext context) {
+ }
}
private final class CountGenerator
@@ -87,10 +99,10 @@ public class AbstractListingTest {
public void testSetDataProvider() {
ListDataProvider<String> dataProvider = DataProvider.create(items);
listing.setDataProvider(dataProvider);
- Assert.assertEquals("setDataProvider did not set data provider", dataProvider,
- listing.getDataProvider());
- listing.setDataProvider(new BackEndDataProvider<>(q -> Stream.of(ITEM_ARRAY)
- .skip(q.getOffset()).limit(q.getLimit()),
+ Assert.assertEquals("setDataProvider did not set data provider",
+ dataProvider, listing.getDataProvider());
+ listing.setDataProvider(new BackEndDataProvider<>(q -> Stream
+ .of(ITEM_ARRAY).skip(q.getOffset()).limit(q.getLimit()),
q -> ITEM_ARRAY.length));
Assert.assertNotEquals("setDataProvider did not replace data provider",
dataProvider, listing.getDataProvider());
diff --git a/server/src/test/java/com/vaadin/ui/AbstractSingleSelectTest.java b/server/src/test/java/com/vaadin/ui/AbstractSingleSelectTest.java
index 83cfbd1ee1..4ea3fdcfa0 100644
--- a/server/src/test/java/com/vaadin/ui/AbstractSingleSelectTest.java
+++ b/server/src/test/java/com/vaadin/ui/AbstractSingleSelectTest.java
@@ -25,6 +25,7 @@ import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
+import org.jsoup.nodes.Element;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -36,6 +37,7 @@ import com.vaadin.event.selection.SingleSelectionListener;
import com.vaadin.server.data.provider.bov.Person;
import com.vaadin.shared.Registration;
import com.vaadin.shared.data.DataCommunicatorClientRpc;
+import com.vaadin.ui.declarative.DesignContext;
/**
* Test for {@link AbstractSingleSelect} and {@link AbstractSingleSelection}
@@ -47,6 +49,16 @@ public class AbstractSingleSelectTest {
private List<Person> selectionChanges;
private static class PersonListing extends AbstractSingleSelect<Person> {
+
+ @Override
+ protected Element writeItem(Element design, Person item,
+ DesignContext context) {
+ return null;
+ }
+
+ @Override
+ protected void readItems(Element design, DesignContext context) {
+ }
}
@Before
@@ -220,6 +232,16 @@ public class AbstractSingleSelectTest {
public String getValue() {
return value;
}
+
+ @Override
+ protected Element writeItem(Element design, String item,
+ DesignContext context) {
+ return null;
+ }
+
+ @Override
+ protected void readItems(Element design, DesignContext context) {
+ }
};
AtomicReference<ValueChangeEvent<?>> event = new AtomicReference<>();