Browse Source

Provide declarative support for listing components.

Fixes vaadin/framework8-issues#388


Change-Id: I4f8045bba51d308f4343a1f6d01b3f1ddca63e69
tags/8.0.0.alpha8
Denis Anisimov 7 years ago
parent
commit
159d413602
25 changed files with 1746 additions and 305 deletions
  1. 258
    0
      server/src/main/java/com/vaadin/ui/AbstractListing.java
  2. 70
    64
      server/src/main/java/com/vaadin/ui/AbstractMultiSelect.java
  3. 66
    0
      server/src/main/java/com/vaadin/ui/AbstractSingleSelect.java
  4. 51
    0
      server/src/main/java/com/vaadin/ui/CheckBoxGroup.java
  5. 104
    49
      server/src/main/java/com/vaadin/ui/ComboBox.java
  6. 50
    0
      server/src/main/java/com/vaadin/ui/DeclarativeCaptionGenerator.java
  7. 52
    0
      server/src/main/java/com/vaadin/ui/DeclarativeIconGenerator.java
  8. 52
    0
      server/src/main/java/com/vaadin/ui/DeclarativeItemEnabledProvider.java
  9. 14
    0
      server/src/main/java/com/vaadin/ui/Grid.java
  10. 59
    46
      server/src/main/java/com/vaadin/ui/RadioButtonGroup.java
  11. 44
    34
      server/src/test/java/com/vaadin/tests/design/DeclarativeTestBase.java
  12. 4
    3
      server/src/test/java/com/vaadin/tests/design/DeclarativeTestBaseBase.java
  13. 22
    9
      server/src/test/java/com/vaadin/tests/server/component/abstractcomponent/AbstractComponentDeclarativeTestBase.java
  14. 181
    0
      server/src/test/java/com/vaadin/tests/server/component/abstractlisting/AbstractListingDeclarativeTest.java
  15. 108
    0
      server/src/test/java/com/vaadin/tests/server/component/abstractmultiselect/AbstractMultiSelectDeclarativeTest.java
  16. 140
    0
      server/src/test/java/com/vaadin/tests/server/component/abstractsingleselect/AbstractSingleSelectDeclarativeTest.java
  17. 93
    0
      server/src/test/java/com/vaadin/tests/server/component/checkboxgroup/CheckBoxGroupDeclarativeTest.java
  18. 109
    0
      server/src/test/java/com/vaadin/tests/server/component/combobox/ComboBoxDeclarativeTest.java
  19. 29
    40
      server/src/test/java/com/vaadin/tests/server/component/listselect/ListSelectDeclarativeTest.java
  20. 42
    0
      server/src/test/java/com/vaadin/tests/server/component/nativeselect/NativeSelectDeclarativeTest.java
  21. 93
    0
      server/src/test/java/com/vaadin/tests/server/component/radiobuttongroup/RadioButtonGroupDeclarativeTest.java
  22. 45
    36
      server/src/test/java/com/vaadin/tests/server/component/twincolselect/TwinColSelectDeclarativeTest.java
  23. 17
    5
      server/src/test/java/com/vaadin/ui/AbstractListingTest.java
  24. 22
    0
      server/src/test/java/com/vaadin/ui/AbstractSingleSelectTest.java
  25. 21
    19
      uitest/src/test/java/com/vaadin/tests/components/radiobuttongroup/RadioButtonGroupTest.java

+ 258
- 0
server/src/main/java/com/vaadin/ui/AbstractListing.java View File

@@ -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
@@ -142,6 +163,64 @@ public abstract class AbstractListing<T> extends AbstractComponent
return getDataCommunicator().getDataProvider();
}

/**
* 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();
}
}

+ 70
- 64
server/src/main/java/com/vaadin/ui/AbstractMultiSelect.java View File

@@ -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;
@@ -120,17 +126,6 @@ public abstract class AbstractMultiSelect<T> extends AbstractListing<T>
.findMethod(MultiSelectionListener.class, "accept",
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);
}

/**
@@ -254,40 +234,6 @@ public abstract class AbstractMultiSelect<T> extends AbstractListing<T>
new ValueChangeEvent<>(this, event.isUserOriginated())));
}

/**
* 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>
@@ -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);

+ 66
- 0
server/src/main/java/com/vaadin/ui/AbstractSingleSelect.java View File

@@ -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() {


+ 51
- 0
server/src/main/java/com/vaadin/ui/CheckBoxGroup.java View File

@@ -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;
}
}

+ 104
- 49
server/src/main/java/com/vaadin/ui/ComboBox.java View File

@@ -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;

@@ -63,6 +71,36 @@ public class ComboBox<T> extends AbstractSingleSelect<T> implements HasValue<T>,
public interface NewItemHandler extends Consumer<String>, Serializable {
}

/**
* 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;
}

}

+ 50
- 0
server/src/main/java/com/vaadin/ui/DeclarativeCaptionGenerator.java View File

@@ -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);
}

}

+ 52
- 0
server/src/main/java/com/vaadin/ui/DeclarativeIconGenerator.java View File

@@ -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);
}

}

+ 52
- 0
server/src/main/java/com/vaadin/ui/DeclarativeItemEnabledProvider.java View File

@@ -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);
}

}

+ 14
- 0
server/src/main/java/com/vaadin/ui/Grid.java View File

@@ -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
}

}

+ 59
- 46
server/src/main/java/com/vaadin/ui/RadioButtonGroup.java View File

@@ -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;
}
}

+ 44
- 34
server/src/test/java/com/vaadin/tests/design/DeclarativeTestBase.java View File

@@ -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;
}

}

+ 4
- 3
server/src/test/java/com/vaadin/tests/design/DeclarativeTestBaseBase.java View File

@@ -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);

+ 22
- 9
server/src/test/java/com/vaadin/tests/server/component/abstractcomponent/AbstractComponentDeclarativeTestBase.java View File

@@ -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 {

+ 181
- 0
server/src/test/java/com/vaadin/tests/server/component/abstractlisting/AbstractListingDeclarativeTest.java View File

@@ -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;
}

}

+ 108
- 0
server/src/test/java/com/vaadin/tests/server/component/abstractmultiselect/AbstractMultiSelectDeclarativeTest.java View File

@@ -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);
}

}

+ 140
- 0
server/src/test/java/com/vaadin/tests/server/component/abstractsingleselect/AbstractSingleSelectDeclarativeTest.java View File

@@ -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;
}
}

}

+ 93
- 0
server/src/test/java/com/vaadin/tests/server/component/checkboxgroup/CheckBoxGroupDeclarativeTest.java View File

@@ -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;
}

}

+ 109
- 0
server/src/test/java/com/vaadin/tests/server/component/combobox/ComboBoxDeclarativeTest.java View File

@@ -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);
}

}

+ 29
- 40
server/src/test/java/com/vaadin/tests/server/component/listselect/ListSelectDeclarativeTest.java View File

@@ -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;
}

}

+ 42
- 0
server/src/test/java/com/vaadin/tests/server/component/nativeselect/NativeSelectDeclarativeTest.java View File

@@ -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;
}

}

+ 93
- 0
server/src/test/java/com/vaadin/tests/server/component/radiobuttongroup/RadioButtonGroupDeclarativeTest.java View File

@@ -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;
}

}

+ 45
- 36
server/src/test/java/com/vaadin/tests/server/component/twincolselect/TwinColSelectDeclarativeTest.java View File

@@ -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;
}

}

+ 17
- 5
server/src/test/java/com/vaadin/ui/AbstractListingTest.java View File

@@ -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());

+ 22
- 0
server/src/test/java/com/vaadin/ui/AbstractSingleSelectTest.java View File

@@ -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<>();

uitest/src/test/java/com/vaadin/tests/components/radiobutton/RadioButtonGroupTest.java → uitest/src/test/java/com/vaadin/tests/components/radiobuttongroup/RadioButtonGroupTest.java View File

@@ -13,22 +13,24 @@
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.tests.components.radiobutton;
package com.vaadin.tests.components.radiobuttongroup;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.util.Arrays;
import java.util.List;

import com.vaadin.server.FontAwesome;
import com.vaadin.testbench.By;
import com.vaadin.testbench.customelements.RadioButtonGroupElement;
import com.vaadin.tests.tb3.MultiBrowserTest;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.WebElement;

import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import com.vaadin.server.FontAwesome;
import com.vaadin.testbench.By;
import com.vaadin.testbench.customelements.RadioButtonGroupElement;
import com.vaadin.tests.components.radiobutton.RadioButtonGroupTestUI;
import com.vaadin.tests.tb3.MultiBrowserTest;

/**
* Test for RadioButtonGroup
@@ -97,13 +99,14 @@ public class RadioButtonGroupTest extends MultiBrowserTest {

@Test
public void itemIconGenerator() {
selectMenuPath("Component", "Item Icon Generator", "Use Item Icon Generator");
selectMenuPath("Component", "Item Icon Generator",
"Use Item Icon Generator");
assertItemsSuffices(20);

List<WebElement> icons = getSelect().findElements(By.
cssSelector(".v-select-optiongroup .v-icon"));
List<WebElement> icons = getSelect()
.findElements(By.cssSelector(".v-select-optiongroup .v-icon"));

assertEquals(20,icons.size());
assertEquals(20, icons.size());

for (int i = 0; i < icons.size(); i++) {
Assert.assertEquals(FontAwesome.values()[i + 1].getCodepoint(),
@@ -111,7 +114,6 @@ public class RadioButtonGroupTest extends MultiBrowserTest {
}
}


@Test
public void clickToSelect_reenable() {
selectMenuPath("Component", "State", "Enabled");
@@ -133,15 +135,15 @@ public class RadioButtonGroupTest extends MultiBrowserTest {

@Test
public void itemCaptionGenerator() {
selectMenuPath("Component", "Item Caption Generator", "Item Caption Generator",
"Custom Caption Generator");
selectMenuPath("Component", "Item Caption Generator",
"Item Caption Generator", "Custom Caption Generator");
assertItems(20, " Caption");
}

@Test
public void nullItemCaptionGenerator() {
selectMenuPath("Component", "Item Caption Generator", "Item Caption Generator",
"Null Caption Generator");
selectMenuPath("Component", "Item Caption Generator",
"Item Caption Generator", "Null Caption Generator");
for (String text : getSelect().getOptions()) {
Assert.assertEquals("", text);
}

Loading…
Cancel
Save