Fixes vaadin/framework8-issues#388 Change-Id: I4f8045bba51d308f4343a1f6d01b3f1ddca63e69tags/8.0.0.alpha8
@@ -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(); | |||
} | |||
} |
@@ -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); |
@@ -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() { | |||
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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 | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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); |
@@ -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 { |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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("&", "&"), 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; | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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("&", "&"), 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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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()); |
@@ -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<>(); |
@@ -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); | |||
} |