瀏覽代碼

Create MultiSelect interface to replace Multi selection model.

Fixes vaadin/framework8-issues#423

Change-Id: Ifd252b8feed323708a7ae73af2b836832570d192
tags/8.0.0.alpha7
Denis Anisimov 7 年之前
父節點
當前提交
b6960abff4
共有 26 個文件被更改,包括 788 次插入730 次删除
  1. 1
    62
      server/src/main/java/com/vaadin/data/Listing.java
  2. 2
    2
      server/src/main/java/com/vaadin/event/selection/SingleSelectionChangeEvent.java
  3. 5
    32
      server/src/main/java/com/vaadin/ui/AbstractListing.java
  4. 125
    155
      server/src/main/java/com/vaadin/ui/AbstractMultiSelect.java
  5. 153
    183
      server/src/main/java/com/vaadin/ui/AbstractSingleSelect.java
  6. 12
    37
      server/src/main/java/com/vaadin/ui/ComboBox.java
  7. 160
    4
      server/src/main/java/com/vaadin/ui/Grid.java
  8. 139
    0
      server/src/main/java/com/vaadin/ui/MultiSelect.java
  9. 0
    2
      server/src/main/java/com/vaadin/ui/NativeSelect.java
  10. 1
    2
      server/src/main/java/com/vaadin/ui/RadioButtonGroup.java
  11. 1
    1
      server/src/test/java/com/vaadin/data/BinderMultiSelectTest.java
  12. 13
    11
      server/src/test/java/com/vaadin/tests/components/grid/GridSelectionTest.java
  13. 0
    4
      server/src/test/java/com/vaadin/ui/AbstractListingTest.java
  14. 55
    63
      server/src/test/java/com/vaadin/ui/AbstractMultiSelectTest.java
  15. 53
    69
      server/src/test/java/com/vaadin/ui/AbstractSingleSelectTest.java
  16. 1
    4
      server/src/test/java/com/vaadin/ui/RadioButtonGroupTest.java
  17. 2
    3
      uitest/src/main/java/com/vaadin/tests/components/AbstractListingFocusBlurTest.java
  18. 1
    1
      uitest/src/main/java/com/vaadin/tests/components/abstractlisting/AbstractListingTestUI.java
  19. 7
    12
      uitest/src/main/java/com/vaadin/tests/components/abstractlisting/AbstractMultiSelectTestUI.java
  20. 4
    3
      uitest/src/main/java/com/vaadin/tests/components/abstractlisting/AbstractSingleSelectTestUI.java
  21. 2
    3
      uitest/src/main/java/com/vaadin/tests/components/checkboxgroup/CheckBoxGroupFocusBlur.java
  22. 15
    21
      uitest/src/main/java/com/vaadin/tests/components/grid/basics/GridBasics.java
  23. 2
    3
      uitest/src/main/java/com/vaadin/tests/components/nativeselect/NativeSelectFocusBlur.java
  24. 15
    18
      uitest/src/main/java/com/vaadin/tests/components/radiobutton/RadioButtonGroupTestUI.java
  25. 2
    3
      uitest/src/main/java/com/vaadin/tests/components/radiobuttongroup/RadioButtonGroupFocusBlur.java
  26. 17
    32
      uitest/src/main/java/com/vaadin/tests/data/DummyData.java

+ 1
- 62
server/src/main/java/com/vaadin/data/Listing.java 查看文件

@@ -17,7 +17,6 @@ package com.vaadin.data;

import java.io.Serializable;
import java.util.Collection;
import java.util.Set;

import com.vaadin.server.data.DataSource;

@@ -28,12 +27,9 @@ import com.vaadin.server.data.DataSource;
*
* @param <T>
* the item data type
* @param <SELECTIONMODEL>
* the selection logic supported by this listing
* @since 8.0
*/
public interface Listing<T, SELECTIONMODEL extends SelectionModel<T>>
extends Serializable {
public interface Listing<T> extends Serializable {

/**
* Returns the source of data items used by this listing.
@@ -51,13 +47,6 @@ public interface Listing<T, SELECTIONMODEL extends SelectionModel<T>>
*/
void setDataSource(DataSource<T> dataSource);

/**
* Returns the selection model for this listing.
*
* @return the selection model, not null
*/
SELECTIONMODEL getSelectionModel();

/**
* Sets the collection of data items of this listing.
*
@@ -79,54 +68,4 @@ public interface Listing<T, SELECTIONMODEL extends SelectionModel<T>>
setDataSource(DataSource.create(items));
}

/* SelectionModel helper methods */

/**
* Returns an immutable set of the currently selected items. The iteration
* order of the items in the returned set is specified by the
* {@linkplain #getSelectionModel() selection model} used.
*
* @return the current selection
*
* @see SelectionModel#getSelectedItems
*/
default Set<T> getSelectedItems() {
return getSelectionModel().getSelectedItems();
}

/**
* Selects the given item. If the item is already selected, does nothing.
*
* @param item
* the item to select, not null
*
* @see SelectionModel#select
*/
default void select(T item) {
getSelectionModel().select(item);
}

/**
* Deselects the given item. If the item is not currently selected, does
* nothing.
*
* @param item
* the item to deselect, not null
*
* @see SelectionModel#deselect
*/
default void deselect(T item) {
getSelectionModel().deselect(item);
}

/**
* Returns whether the given item is currently selected.
*
* @param item
* the item to check, not null
* @return {@code true} if the item is selected, {@code false} otherwise
*/
default boolean isSelected(T item) {
return getSelectionModel().isSelected(item);
}
}

+ 2
- 2
server/src/main/java/com/vaadin/event/selection/SingleSelectionChangeEvent.java 查看文件

@@ -67,8 +67,8 @@ public class SingleSelectionChangeEvent<T> extends ValueChangeEvent<T>

@Override
@SuppressWarnings("unchecked")
public AbstractListing<T, ?> getComponent() {
return (AbstractListing<T, ?>) super.getComponent();
public AbstractListing<T> getComponent() {
return (AbstractListing<T>) super.getComponent();
}

@Override

+ 5
- 32
server/src/main/java/com/vaadin/ui/AbstractListing.java 查看文件

@@ -32,13 +32,11 @@ import com.vaadin.server.data.DataSource;
*
* @param <T>
* the item data type
* @param <SELECTIONMODEL>
* the selection logic supported by this listing
*
* @since 8.0
*/
public abstract class AbstractListing<T, SELECTIONMODEL extends SelectionModel<T>>
extends AbstractComponent implements Listing<T, SELECTIONMODEL> {
public abstract class AbstractListing<T> extends AbstractComponent
implements Listing<T> {

/**
* A helper base class for creating extensions for Listing components. This
@@ -57,7 +55,7 @@ public abstract class AbstractListing<T, SELECTIONMODEL extends SelectionModel<T
* @param listing
* the parent component to add to
*/
public void extend(AbstractListing<T, ?> listing) {
public void extend(AbstractListing<T> listing) {
super.extend(listing);
listing.addDataGenerator(this);
}
@@ -82,8 +80,8 @@ public abstract class AbstractListing<T, SELECTIONMODEL extends SelectionModel<T

@Override
@SuppressWarnings("unchecked")
public AbstractListing<T, ?> getParent() {
return (AbstractListing<T, ?>) super.getParent();
public AbstractListing<T> getParent() {
return (AbstractListing<T>) super.getParent();
}

/**
@@ -100,8 +98,6 @@ public abstract class AbstractListing<T, SELECTIONMODEL extends SelectionModel<T

private final DataCommunicator<T> dataCommunicator;

private SELECTIONMODEL selectionModel;

/**
* Creates a new {@code AbstractListing} with a default data communicator.
* <p>
@@ -146,29 +142,6 @@ public abstract class AbstractListing<T, SELECTIONMODEL extends SelectionModel<T
return getDataCommunicator().getDataSource();
}

@Override
public SELECTIONMODEL getSelectionModel() {
assert selectionModel != null : "No selection model set by "
+ getClass().getName() + " constructor";
return selectionModel;
}

/**
* Sets the selection model for this listing.
*
* @param model
* the selection model to use, not null
*/
protected void setSelectionModel(SELECTIONMODEL model) {
if (selectionModel != null) {
throw new IllegalStateException(
"A selection model can't be changed.");
}

Objects.requireNonNull(model, "selection model cannot be null");
selectionModel = model;
}

/**
* Adds the given data generator to this listing. If the generator was
* already added, does nothing.

+ 125
- 155
server/src/main/java/com/vaadin/ui/AbstractMultiSelect.java 查看文件

@@ -52,158 +52,16 @@ import elemental.json.JsonObject;
* @author Vaadin Ltd
* @since 8.0
*/
public abstract class AbstractMultiSelect<T>
extends AbstractListing<T, Multi<T>> implements HasValue<Set<T>> {
public abstract class AbstractMultiSelect<T> extends AbstractListing<T>
implements MultiSelect<T> {

/**
* Simple implementation of multiselectmodel.
*/
protected class SimpleMultiSelectModel implements SelectionModel.Multi<T> {

private Set<T> selection = new LinkedHashSet<>();

@Override
public void select(T item) {
// Not user originated
select(item, false);
}

/**
* Selects the given item. Depending on the implementation, may cause
* other items to be deselected. If the item is already selected, does
* nothing.
*
* @param item
* the item to select, not null
* @param userOriginated
* {@code true} if this was used originated, {@code false} if
* not
*/
protected void select(T item, boolean userOriginated) {
if (selection.contains(item)) {
return;
}

updateSelection(set -> set.add(item), userOriginated);
}

@Override
public void updateSelection(Set<T> addedItems, Set<T> removedItems) {
updateSelection(addedItems, removedItems, false);
}

/**
* Updates the selection by adding and removing the given items.
*
* @param addedItems
* the items added to selection, not {@code} null
* @param removedItems
* the items removed from selection, not {@code} null
* @param userOriginated
* {@code true} if this was used originated, {@code false} if
* not
*/
protected void updateSelection(Set<T> addedItems, Set<T> removedItems,
boolean userOriginated) {
Objects.requireNonNull(addedItems);
Objects.requireNonNull(removedItems);

// if there are duplicates, some item is both added & removed, just
// discard that and leave things as was before
addedItems.removeIf(item -> removedItems.remove(item));

if (selection.containsAll(addedItems)
&& Collections.disjoint(selection, removedItems)) {
return;
}

updateSelection(set -> {
// order of add / remove does not matter since no duplicates
set.removeAll(removedItems);
set.addAll(addedItems);
}, userOriginated);
}

@Override
public Set<T> getSelectedItems() {
return Collections.unmodifiableSet(new LinkedHashSet<>(selection));
}

@Override
public void deselect(T item) {
// Not user originated
deselect(item, false);
}

/**
* Deselects the given item. If the item is not currently selected, does
* nothing.
*
* @param item
* the item to deselect, not null
* @param userOriginated
* {@code true} if this was used originated, {@code false} if
* not
*/
protected void deselect(T item, boolean userOriginated) {
if (!selection.contains(item)) {
return;
}

updateSelection(set -> set.remove(item), userOriginated);
}

/**
* Removes the given items. Any item that is not currently selected, is
* ignored. If none of the items are selected, does nothing.
*
* @param items
* the items to deselect, not {@code null}
* @param userOriginated
* {@code true} if this was used originated, {@code false} if
* not
*/
protected void deselect(Set<T> items, boolean userOriginated) {
Objects.requireNonNull(items);
if (items.stream().noneMatch(i -> isSelected(i))) {
return;
}

updateSelection(set -> set.removeAll(items), userOriginated);
}

@Override
public void deselectAll() {
if (selection.isEmpty()) {
return;
}

updateSelection(Set::clear, false);
}

private void updateSelection(Consumer<Set<T>> handler,
boolean userOriginated) {
LinkedHashSet<T> oldSelection = new LinkedHashSet<>(selection);
handler.accept(selection);
LinkedHashSet<T> newSelection = new LinkedHashSet<>(selection);

fireEvent(new MultiSelectionEvent<>(AbstractMultiSelect.this,
oldSelection, userOriginated));

getDataCommunicator().reset();
}

@Override
public boolean isSelected(T item) {
return selection.contains(item);
}
}
private Set<T> selection = new LinkedHashSet<>();

private class MultiSelectServerRpcImpl implements MultiSelectServerRpc {
@Override
public void updateSelection(Set<String> selectedItemKeys,
Set<String> deselectedItemKeys) {
getSelectionModel().updateSelection(
AbstractMultiSelect.this.updateSelection(
getItemsForSelectionChange(selectedItemKeys),
getItemsForSelectionChange(deselectedItemKeys), true);
}
@@ -223,10 +81,6 @@ public abstract class AbstractMultiSelect<T>
return Optional.of(item);
}

private SimpleMultiSelectModel getSelectionModel() {
return (SimpleMultiSelectModel) AbstractMultiSelect.this
.getSelectionModel();
}
}

private class MultiSelectDataGenerator implements DataGenerator<T> {
@@ -250,7 +104,7 @@ public abstract class AbstractMultiSelect<T>
true);
}

if (getSelectionModel().isSelected(data)) {
if (isSelected(data)) {
jsonObject.put(ListingJsonConstants.JSONKEY_ITEM_SELECTED,
true);
}
@@ -287,8 +141,6 @@ public abstract class AbstractMultiSelect<T>
* Creates a new multi select with an empty data source.
*/
protected AbstractMultiSelect() {
setSelectionModel(new SimpleMultiSelectModel());

registerRpc(new MultiSelectServerRpcImpl());

// #FIXME it should be the responsibility of the SelectionModel
@@ -304,6 +156,7 @@ public abstract class AbstractMultiSelect<T>
* the value change listener, not {@code null}
* @return a registration for the listener
*/
@Override
public Registration addSelectionListener(
MultiSelectionListener<T> listener) {
addListener(MultiSelectionEvent.class, listener,
@@ -376,8 +229,7 @@ public abstract class AbstractMultiSelect<T>
Set<T> copy = value.stream().map(Objects::requireNonNull)
.collect(Collectors.toCollection(LinkedHashSet::new));

getSelectionModel().updateSelection(copy,
new LinkedHashSet<>(getSelectionModel().getSelectedItems()));
updateSelection(copy, new LinkedHashSet<>(getSelectedItems()));
}

@Override
@@ -502,4 +354,122 @@ public abstract class AbstractMultiSelect<T>
public boolean isReadOnly() {
return super.isReadOnly();
}

@Override
public void updateSelection(Set<T> addedItems, Set<T> removedItems) {
updateSelection(addedItems, removedItems, false);
}

/**
* Updates the selection by adding and removing the given items.
*
* @param addedItems
* the items added to selection, not {@code} null
* @param removedItems
* the items removed from selection, not {@code} null
* @param userOriginated
* {@code true} if this was used originated, {@code false} if not
*/
protected void updateSelection(Set<T> addedItems, Set<T> removedItems,
boolean userOriginated) {
Objects.requireNonNull(addedItems);
Objects.requireNonNull(removedItems);

// if there are duplicates, some item is both added & removed, just
// discard that and leave things as was before
addedItems.removeIf(item -> removedItems.remove(item));

if (selection.containsAll(addedItems)
&& Collections.disjoint(selection, removedItems)) {
return;
}

updateSelection(set -> {
// order of add / remove does not matter since no duplicates
set.removeAll(removedItems);
set.addAll(addedItems);
}, userOriginated);
}

@Override
public Set<T> getSelectedItems() {
return Collections.unmodifiableSet(new LinkedHashSet<>(selection));
}

@Override
public void deselectAll() {
if (selection.isEmpty()) {
return;
}

updateSelection(Set::clear, false);
}

@Override
public boolean isSelected(T item) {
return selection.contains(item);
}

/**
* Deselects the given item. If the item is not currently selected, does
* nothing.
*
* @param item
* the item to deselect, not null
* @param userOriginated
* {@code true} if this was used originated, {@code false} if not
*/
protected void deselect(T item, boolean userOriginated) {
if (!selection.contains(item)) {
return;
}

updateSelection(set -> set.remove(item), userOriginated);
}

/**
* Removes the given items. Any item that is not currently selected, is
* ignored. If none of the items are selected, does nothing.
*
* @param items
* the items to deselect, not {@code null}
* @param userOriginated
* {@code true} if this was used originated, {@code false} if not
*/
protected void deselect(Set<T> items, boolean userOriginated) {
Objects.requireNonNull(items);
if (items.stream().noneMatch(i -> isSelected(i))) {
return;
}

updateSelection(set -> set.removeAll(items), userOriginated);
}

/**
* Selects the given item. Depending on the implementation, may cause other
* items to be deselected. If the item is already selected, does nothing.
*
* @param item
* the item to select, not null
* @param userOriginated
* {@code true} if this was used originated, {@code false} if not
*/
protected void select(T item, boolean userOriginated) {
if (selection.contains(item)) {
return;
}

updateSelection(set -> set.add(item), userOriginated);
}

private void updateSelection(Consumer<Set<T>> handler,
boolean userOriginated) {
LinkedHashSet<T> oldSelection = new LinkedHashSet<>(selection);
handler.accept(selection);

fireEvent(new MultiSelectionEvent<>(AbstractMultiSelect.this,
oldSelection, userOriginated));

getDataCommunicator().reset();
}
}

+ 153
- 183
server/src/main/java/com/vaadin/ui/AbstractSingleSelect.java 查看文件

@@ -43,189 +43,9 @@ import com.vaadin.util.ReflectTools;
*
* @since 8.0
*/
public abstract class AbstractSingleSelect<T> extends
AbstractListing<T, AbstractSingleSelect<T>.AbstractSingleSelection>
public abstract class AbstractSingleSelect<T> extends AbstractListing<T>
implements SingleSelect<T> {

/**
* A base class for single selection model implementations. Listens to
* {@code SelectionServerRpc} invocations to track selection requests by the
* client. Maintaining the selection state and communicating it from the
* server to the client is the responsibility of the implementing class.
*/
public abstract class AbstractSingleSelection
implements SelectionModel.Single<T> {

/**
* Creates a new {@code SimpleSingleSelection} instance.
*/
public AbstractSingleSelection() {
registerRpc(new SelectionServerRpc() {

@Override
public void select(String key) {
setSelectedFromClient(key);
}

@Override
public void deselect(String key) {
if (isKeySelected(key)) {
setSelectedFromClient(null);
}
}
});
}

@Override
public void deselect(T item) {
Objects.requireNonNull(item, "deselected item cannot be null");
if (isSelected(item)) {
setSelectedFromServer(null);
}
}

@Override
public void select(T item) {
Objects.requireNonNull(item, "selected item cannot be null");
setSelectedFromServer(item);
}

/**
* Returns the communication key of the selected item or {@code null} if
* no item is selected.
*
* @return the key of the selected item if any, {@code null} otherwise.
*/
protected abstract String getSelectedKey();

/**
* Sets the selected item based on the given communication key. If the
* key is {@code null}, clears the current selection if any.
*
* @param key
* the key of the selected item or {@code null} to clear
* selection
*/
protected abstract void doSetSelectedKey(String key);

/**
* Sets the selection based on a client request. Does nothing if the
* select component is {@linkplain Component#isReadOnly()} or if the
* selection would not change. Otherwise updates the selection and fires
* a selection change event with {@code isUserOriginated == true}.
*
* @param key
* the key of the item to select or {@code null} to clear
* selection
*/
protected void setSelectedFromClient(String key) {
if (isReadOnly()) {
return;
}
if (isKeySelected(key)) {
return;
}

doSetSelectedKey(key);
fireEvent(new SingleSelectionChangeEvent<>(
AbstractSingleSelect.this, true));
}

/**
* Sets the selection based on server API call. Does nothing if the
* selection would not change; otherwise updates the selection and fires
* a selection change event with {@code isUserOriginated == false}.
*
* @param item
* the item to select or {@code null} to clear selection
*/
protected void setSelectedFromServer(T item) {
// TODO creates a key if item not in data source
String key = itemToKey(item);

if (isKeySelected(key) || isSelected(item)) {
return;
}

doSetSelectedKey(key);
fireEvent(new SingleSelectionChangeEvent<>(
AbstractSingleSelect.this, false));
}

/**
* Returns whether the given key maps to the currently selected item.
*
* @param key
* the key to test or {@code null} to test whether nothing is
* selected
* @return {@code true} if the key equals the key of the currently
* selected item (or {@code null} if no selection),
* {@code false} otherwise.
*/
protected boolean isKeySelected(String key) {
return Objects.equals(key, getSelectedKey());
}

/**
* Returns the communication key assigned to the given item.
*
* @param item
* the item whose key to return
* @return the assigned key
*/
protected String itemToKey(T item) {
if (item == null) {
return null;
} else {
// TODO creates a key if item not in data source
return getDataCommunicator().getKeyMapper().key(item);
}
}

/**
* Returns the item that the given key is assigned to, or {@code null}
* if there is no such item.
*
* @param key
* the key whose item to return
* @return the associated item if any, {@code null} otherwise.
*/
protected T keyToItem(String key) {
return getDataCommunicator().getKeyMapper().get(key);
}
}

/**
* A simple single selection model using the {@code AbstractSingleSelect}
* RPC and state to communicate with the client. Has no client-side
* counterpart; the listing connector is expected to handle selection.
* Client-to-server selection is passed via {@link SelectionServerRpc} and
* server-to-client via {@link AbstractSingleSelectState#selectedItemKey}.
*/
protected class SimpleSingleSelection extends AbstractSingleSelection {

/**
* Creates a new {@code SimpleSingleSelection}.
*/
public SimpleSingleSelection() {
}

@Override
public Optional<T> getSelectedItem() {
return Optional.ofNullable(keyToItem(getSelectedKey()));
}

@Override
protected String getSelectedKey() {
return getState(false).selectedItemKey;
}

@Override
protected void doSetSelectedKey(String key) {
getState().selectedItemKey = key;
}
}

@Deprecated
private static final Method SELECTION_CHANGE_METHOD = ReflectTools
.findMethod(SingleSelectionListener.class, "accept",
@@ -240,6 +60,7 @@ public abstract class AbstractSingleSelect<T> extends
* {@link AbstractSingleSelect.AbstractSingleSelection} .
*/
protected AbstractSingleSelect() {
init();
}

/**
@@ -260,6 +81,7 @@ public abstract class AbstractSingleSelect<T> extends
*/
protected AbstractSingleSelect(DataCommunicator<T> dataCommunicator) {
super(dataCommunicator);
init();
}

/**
@@ -285,7 +107,7 @@ public abstract class AbstractSingleSelect<T> extends
* otherwise
*/
public Optional<T> getSelectedItem() {
return getSelectionModel().getSelectedItem();
return Optional.ofNullable(keyToItem(getSelectedKey()));
}

/**
@@ -296,7 +118,7 @@ public abstract class AbstractSingleSelect<T> extends
* the item to select or {@code null} to clear selection
*/
public void setSelectedItem(T item) {
getSelectionModel().setSelectedItem(item);
setSelectedFromServer(item);
}

/**
@@ -369,4 +191,152 @@ public abstract class AbstractSingleSelect<T> extends
public boolean isReadOnly() {
return super.isReadOnly();
}

public void deselect(T item) {
Objects.requireNonNull(item, "deselected item cannot be null");
if (isSelected(item)) {
setSelectedFromServer(null);
}
}

public void select(T item) {
Objects.requireNonNull(item, "selected item cannot be null");
setSelectedFromServer(item);
}

/**
* Returns the communication key of the selected item or {@code null} if no
* item is selected.
*
* @return the key of the selected item if any, {@code null} otherwise.
*/
protected String getSelectedKey() {
return getState(false).selectedItemKey;
}

/**
* Sets the selected item based on the given communication key. If the key
* is {@code null}, clears the current selection if any.
*
* @param key
* the key of the selected item or {@code null} to clear
* selection
*/
protected void doSetSelectedKey(String key) {
getState().selectedItemKey = key;
}

/**
* Sets the selection based on a client request. Does nothing if the select
* component is {@linkplain Component#isReadOnly()} or if the selection
* would not change. Otherwise updates the selection and fires a selection
* change event with {@code isUserOriginated == true}.
*
* @param key
* the key of the item to select or {@code null} to clear
* selection
*/
protected void setSelectedFromClient(String key) {
if (isReadOnly()) {
return;
}
if (isKeySelected(key)) {
return;
}

doSetSelectedKey(key);
fireEvent(new SingleSelectionChangeEvent<>(AbstractSingleSelect.this,
true));
}

/**
* Sets the selection based on server API call. Does nothing if the
* selection would not change; otherwise updates the selection and fires a
* selection change event with {@code isUserOriginated == false}.
*
* @param item
* the item to select or {@code null} to clear selection
*/
protected void setSelectedFromServer(T item) {
// TODO creates a key if item not in data source
String key = itemToKey(item);

if (isKeySelected(key) || isSelected(item)) {
return;
}

doSetSelectedKey(key);
fireEvent(new SingleSelectionChangeEvent<>(AbstractSingleSelect.this,
false));
}

/**
* Returns whether the given key maps to the currently selected item.
*
* @param key
* the key to test or {@code null} to test whether nothing is
* selected
* @return {@code true} if the key equals the key of the currently selected
* item (or {@code null} if no selection), {@code false} otherwise.
*/
protected boolean isKeySelected(String key) {
return Objects.equals(key, getSelectedKey());
}

/**
* Returns the communication key assigned to the given item.
*
* @param item
* the item whose key to return
* @return the assigned key
*/
protected String itemToKey(T item) {
if (item == null) {
return null;
} else {
// TODO creates a key if item not in data source
return getDataCommunicator().getKeyMapper().key(item);
}
}

/**
* Returns the item that the given key is assigned to, or {@code null} if
* there is no such item.
*
* @param key
* the key whose item to return
* @return the associated item if any, {@code null} otherwise.
*/
protected T keyToItem(String key) {
return getDataCommunicator().getKeyMapper().get(key);
}

/**
* Returns whether the given item is currently selected.
*
* @param item
* the item to check, not null
* @return {@code true} if the item is selected, {@code false} otherwise
*/
public boolean isSelected(T item) {
return Objects.equals(getValue(), item);
}

private void init() {
registerRpc(new SelectionServerRpc() {

@Override
public void select(String key) {
setSelectedFromClient(key);
}

@Override
public void deselect(String key) {
if (isKeySelected(key)) {
setSelectedFromClient(null);
}
}
});
}

}

+ 12
- 37
server/src/main/java/com/vaadin/ui/ComboBox.java 查看文件

@@ -55,24 +55,6 @@ import elemental.json.JsonObject;
public class ComboBox<T> extends AbstractSingleSelect<T> implements HasValue<T>,
FieldEvents.BlurNotifier, FieldEvents.FocusNotifier {

/**
* Custom single selection model for ComboBox.
*/
protected class ComboBoxSelectionModel extends SimpleSingleSelection {
@Override
protected void doSetSelectedKey(String key) {
super.doSetSelectedKey(key);

String selectedCaption = null;
T value = getDataCommunicator().getKeyMapper().get(key);
if (value != null) {
selectedCaption = getItemCaptionGenerator().apply(value);
}
getState().selectedItemCaption = selectedCaption;
}

}

/**
* Handler that adds a new item based on user input when the new items
* allowed mode is active.
@@ -158,7 +140,6 @@ public class ComboBox<T> extends AbstractSingleSelect<T> implements HasValue<T>,
};
}
});
setSelectionModel(new ComboBoxSelectionModel());

init();
}
@@ -540,24 +521,6 @@ public class ComboBox<T> extends AbstractSingleSelect<T> implements HasValue<T>,
this.filter = filter;
}

/**
* Sets the value of this object. If the new value is not equal to
* {@code getValue()}, fires a {@link ValueChangeEvent}.
*
* @param value
* the new value, may be {@code null}
*/
@Override
public void setValue(T value) {
getSelectionModel().setSelectedFromServer(value);

}

@Override
public T getValue() {
return getSelectionModel().getSelectedItem().orElse(null);
}

@Override
public Registration addValueChangeListener(
HasValue.ValueChangeListener<T> listener) {
@@ -577,4 +540,16 @@ public class ComboBox<T> extends AbstractSingleSelect<T> implements HasValue<T>,
return (ComboBoxState) super.getState(markAsDirty);
}

@Override
protected void doSetSelectedKey(String key) {
super.doSetSelectedKey(key);

String selectedCaption = null;
T value = getDataCommunicator().getKeyMapper().get(key);
if (value != null) {
selectedCaption = getItemCaptionGenerator().apply(value);
}
getState().selectedItemCaption = selectedCaption;
}

}

+ 160
- 4
server/src/main/java/com/vaadin/ui/Grid.java 查看文件

@@ -37,9 +37,11 @@ import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.vaadin.data.SelectionModel.Single;
import com.vaadin.event.ConnectorEvent;
import com.vaadin.event.ContextClickEvent;
import com.vaadin.event.EventListener;
import com.vaadin.event.selection.SingleSelectionChangeEvent;
import com.vaadin.server.EncodeResult;
import com.vaadin.server.JsonCodec;
import com.vaadin.server.SerializableComparator;
@@ -48,6 +50,7 @@ import com.vaadin.server.data.SortOrder;
import com.vaadin.shared.MouseEventDetails;
import com.vaadin.shared.Registration;
import com.vaadin.shared.data.DataCommunicatorConstants;
import com.vaadin.shared.data.selection.SelectionServerRpc;
import com.vaadin.shared.data.sort.SortDirection;
import com.vaadin.shared.ui.grid.ColumnState;
import com.vaadin.shared.ui.grid.GridConstants;
@@ -488,7 +491,7 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
extends AbstractListingExtension<T> {

@Override
public void extend(AbstractListing<T, ?> grid) {
public void extend(AbstractListing<T> grid) {
if (!(grid instanceof Grid)) {
throw new IllegalArgumentException(
getClass().getSimpleName() + " can only extend Grid");
@@ -1485,7 +1488,7 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
}
}

private final class SingleSelection extends AbstractSingleSelection {
private final class SingleSelection implements Single<T> {
private T selectedItem = null;

SingleSelection() {
@@ -1494,6 +1497,20 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
json.put(DataCommunicatorConstants.SELECTED, true);
}
});
registerRpc(new SelectionServerRpc() {

@Override
public void select(String key) {
setSelectedFromClient(key);
}

@Override
public void deselect(String key) {
if (isKeySelected(key)) {
setSelectedFromClient(null);
}
}
});
}

@Override
@@ -1502,16 +1519,51 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
}

@Override
public void deselect(T item) {
Objects.requireNonNull(item, "deselected item cannot be null");
if (isSelected(item)) {
setSelectedFromServer(null);
}
}

@Override
public void select(T item) {
Objects.requireNonNull(item, "selected item cannot be null");
setSelectedFromServer(item);
}

/**
* Returns whether the given key maps to the currently selected item.
*
* @param key
* the key to test or {@code null} to test whether nothing is
* selected
* @return {@code true} if the key equals the key of the currently
* selected item (or {@code null} if no selection),
* {@code false} otherwise.
*/
protected boolean isKeySelected(String key) {
return Objects.equals(key, getSelectedKey());
}

@Override
/**
* Returns the communication key of the selected item or {@code null} if
* no item is selected.
*
* @return the key of the selected item if any, {@code null} otherwise.
*/
protected String getSelectedKey() {
return itemToKey(selectedItem);
}

@Override
/**
* Sets the selected item based on the given communication key. If the
* key is {@code null}, clears the current selection if any.
*
* @param key
* the key of the selected item or {@code null} to clear
* selection
*/
protected void doSetSelectedKey(String key) {
if (selectedItem != null) {
getDataCommunicator().refresh(selectedItem);
@@ -1521,6 +1573,76 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
getDataCommunicator().refresh(selectedItem);
}
}

/**
* Sets the selection based on a client request. Does nothing if the
* select component is {@linkplain Component#isReadOnly()} or if the
* selection would not change. Otherwise updates the selection and fires
* a selection change event with {@code isUserOriginated == true}.
*
* @param key
* the key of the item to select or {@code null} to clear
* selection
*/
protected void setSelectedFromClient(String key) {
if (isReadOnly()) {
return;
}
if (isKeySelected(key)) {
return;
}

doSetSelectedKey(key);
fireEvent(new SingleSelectionChangeEvent<>(Grid.this, true));
}

/**
* Sets the selection based on server API call. Does nothing if the
* selection would not change; otherwise updates the selection and fires
* a selection change event with {@code isUserOriginated == false}.
*
* @param item
* the item to select or {@code null} to clear selection
*/
protected void setSelectedFromServer(T item) {
// TODO creates a key if item not in data source
String key = itemToKey(item);

if (isKeySelected(key) || isSelected(item)) {
return;
}

doSetSelectedKey(key);
fireEvent(new SingleSelectionChangeEvent<>(Grid.this, false));
}

/**
* Returns the communication key assigned to the given item.
*
* @param item
* the item whose key to return
* @return the assigned key
*/
protected String itemToKey(T item) {
if (item == null) {
return null;
} else {
// TODO creates a key if item not in data source
return getDataCommunicator().getKeyMapper().key(item);
}
}

/**
* Returns the item that the given key is assigned to, or {@code null}
* if there is no such item.
*
* @param key
* the key whose item to return
* @return the associated item if any, {@code null} otherwise.
*/
protected T keyToItem(String key) {
return getDataCommunicator().getKeyMapper().get(key);
}
}

/**
@@ -1713,6 +1835,8 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {

private int counter = 0;

private Single<T> selectionModel;

/**
* Constructor for the {@link Grid} component.
*/
@@ -2561,6 +2685,38 @@ public class Grid<T> extends AbstractSingleSelect<T> implements HasComponents {
fireColumnReorderEvent(false);
}

/**
* Returns the selection model for this listing.
*
* @return the selection model, not null
*/
public Single<T> getSelectionModel() {
assert selectionModel != null : "No selection model set by "
+ getClass().getName() + " constructor";
return selectionModel;
}

@Override
public Optional<T> getSelectedItem() {
return getSelectionModel().getSelectedItem();
}

/**
* Sets the selection model for this listing.
*
* @param model
* the selection model to use, not null
*/
protected void setSelectionModel(Single<T> model) {
if (selectionModel != null) {
throw new IllegalStateException(
"A selection model can't be changed.");
}

Objects.requireNonNull(model, "selection model cannot be null");
selectionModel = model;
}

@Override
protected GridState getState() {
return getState(true);

+ 139
- 0
server/src/main/java/com/vaadin/ui/MultiSelect.java 查看文件

@@ -0,0 +1,139 @@
/*
* 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.io.Serializable;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Stream;

import com.vaadin.data.HasValue;
import com.vaadin.event.selection.MultiSelectionListener;
import com.vaadin.shared.Registration;

/**
* Multi selection component which allows to select and deselect multiple items.
*
* @author Vaadin Ltd
*
* @param <T>
* the type of the items to select
*
* @since 8.0
*
*/
public interface MultiSelect<T> extends HasValue<Set<T>>, Serializable {

/**
* Adds the given items to the set of currently selected items.
* <p>
* By default this does not clear any previous selection. To do that, use
* {@link #deselectAll()}.
* <p>
* If the all the items were already selected, this is a NO-OP.
* <p>
* This is a short-hand for {@link #updateSelection(Set, Set)} with nothing
* to deselect.
*
* @param items
* to add to selection, not {@code null}
*/
public default void select(T... items) {
Objects.requireNonNull(items);
Stream.of(items).forEach(Objects::requireNonNull);

updateSelection(new LinkedHashSet<>(Arrays.asList(items)),
Collections.emptySet());
}

/**
* Removes the given items from the set of currently selected items.
* <p>
* If the none of the items were selected, this is a NO-OP.
* <p>
* This is a short-hand for {@link #updateSelection(Set, Set)} with nothing
* to select.
*
* @param items
* to remove from selection, not {@code null}
*/
public default void deselect(T... items) {
Objects.requireNonNull(items);
Stream.of(items).forEach(Objects::requireNonNull);

updateSelection(Collections.emptySet(),
new LinkedHashSet<>(Arrays.asList(items)));
}

/**
* Updates the selection by adding and removing the given items from it.
* <p>
* If all the added items were already selected and the removed items were
* not selected, this is a NO-OP.
* <p>
* Duplicate items (in both add & remove sets) are ignored.
*
* @param addedItems
* the items to add, not {@code null}
* @param removedItems
* the items to remove, not {@code null}
*/
public void updateSelection(Set<T> addedItems, Set<T> removedItems);

/**
* Returns an immutable set of the currently selected items. It is safe to
* invoke other {@code SelectionModel} methods while iterating over the set.
* <p>
* <em>Implementation note:</em> the iteration order of the items in the
* returned set should be well-defined and documented by the implementing
* class.
*
* @return the items in the current selection, not null
*/
public Set<T> getSelectedItems();

/**
* Deselects all currently selected items.
*/
public default void deselectAll() {
getSelectedItems().forEach(this::deselect);
}

/**
* Returns whether the given item is currently selected.
*
* @param item
* the item to check, not null
* @return {@code true} if the item is selected, {@code false} otherwise
*/
public default boolean isSelected(T item) {
return getSelectedItems().contains(item);
}

/**
* Adds a selection listener that will be called when the selection is
* changed either by the user or programmatically.
*
* @param listener
* the value change listener, not {@code null}
* @return a registration for the listener
*/
public Registration addSelectionListener(
MultiSelectionListener<T> listener);
}

+ 0
- 2
server/src/main/java/com/vaadin/ui/NativeSelect.java 查看文件

@@ -52,8 +52,6 @@ public class NativeSelect<T> extends AbstractSingleSelect<T>
registerRpc(new FocusAndBlurServerRpcDecorator(this, this::fireEvent));
addDataGenerator((item, json) -> json
.put(DataCommunicatorConstants.DATA, String.valueOf(item)));

setSelectionModel(new SimpleSingleSelection());
}

/**

+ 1
- 2
server/src/main/java/com/vaadin/ui/RadioButtonGroup.java 查看文件

@@ -103,7 +103,6 @@ public class RadioButtonGroup<T> extends AbstractSingleSelect<T>
*/
public RadioButtonGroup() {
registerRpc(new FocusAndBlurServerRpcDecorator(this, this::fireEvent));
setSelectionModel(new SimpleSingleSelection());

addDataGenerator(new DataGenerator<T>() {
@Override
@@ -127,7 +126,7 @@ public class RadioButtonGroup<T> extends AbstractSingleSelect<T>
true);
}

if (getSelectionModel().isSelected(data)) {
if (isSelected(data)) {
jsonObject.put(ListingJsonConstants.JSONKEY_ITEM_SELECTED,
true);
}

+ 1
- 1
server/src/test/java/com/vaadin/data/BinderMultiSelectTest.java 查看文件

@@ -157,7 +157,7 @@ public class BinderMultiSelectTest
converterBinder.readBean(new AtomicReference<>("TWO"));

assertEquals(Collections.singleton(TestEnum.TWO),
select.getSelectionModel().getSelectedItems());
select.getSelectedItems());
}

@Test

+ 13
- 11
server/src/test/java/com/vaadin/tests/components/grid/GridSelectionTest.java 查看文件

@@ -1,5 +1,7 @@
package com.vaadin.tests.components.grid;

import java.util.Optional;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -18,17 +20,17 @@ public class GridSelectionTest {

@Test
public void testGridWithSingleSelection() {
Assert.assertFalse(grid.isSelected("Foo"));
grid.select("Foo");
Assert.assertTrue(grid.isSelected("Foo"));
Assert.assertEquals(1, grid.getSelectedItems().size());
Assert.assertEquals("Foo", grid.getSelectedItems().iterator().next());
grid.select("Bar");
Assert.assertFalse(grid.isSelected("Foo"));
Assert.assertTrue(grid.isSelected("Bar"));
grid.deselect("Bar");
Assert.assertFalse(grid.isSelected("Bar"));
Assert.assertEquals(0, grid.getSelectedItems().size());
Assert.assertFalse(grid.getSelectionModel().isSelected("Foo"));
grid.getSelectionModel().select("Foo");
Assert.assertTrue(grid.getSelectionModel().isSelected("Foo"));
Assert.assertEquals(Optional.of("Foo"), grid.getSelectedItem());
grid.getSelectionModel().select("Bar");
Assert.assertFalse(grid.getSelectionModel().isSelected("Foo"));
Assert.assertTrue(grid.getSelectionModel().isSelected("Bar"));
grid.getSelectionModel().deselect("Bar");
Assert.assertFalse(grid.getSelectionModel().isSelected("Bar"));
Assert.assertFalse(
grid.getSelectionModel().getSelectedItem().isPresent());
}

}

+ 0
- 4
server/src/test/java/com/vaadin/ui/AbstractListingTest.java 查看文件

@@ -22,10 +22,6 @@ public class AbstractListingTest {

private final class TestListing extends AbstractSingleSelect<String> {

protected TestListing() {
setSelectionModel(new SimpleSingleSelection());
}

/**
* Used to execute data generation
*/

+ 55
- 63
server/src/test/java/com/vaadin/ui/AbstractMultiSelectTest.java 查看文件

@@ -37,7 +37,6 @@ import org.junit.runners.Parameterized.Parameters;
import org.mockito.Mockito;

import com.vaadin.data.HasValue.ValueChangeEvent;
import com.vaadin.data.SelectionModel.Multi;
import com.vaadin.event.selection.MultiSelectionEvent;
import com.vaadin.event.selection.MultiSelectionListener;
import com.vaadin.server.data.DataSource;
@@ -56,18 +55,16 @@ public class AbstractMultiSelectTest {
@Parameter
public AbstractMultiSelect<String> selectToTest;

private Multi<String> selectionModel;
private MultiSelectServerRpc rpc;

private Registration registration;

@Before
public void setUp() {
selectToTest.getSelectionModel().deselectAll();
selectToTest.deselectAll();
// Intentional deviation from upcoming selection order
selectToTest.setDataSource(
DataSource.create("3", "2", "1", "5", "8", "7", "4", "6"));
selectionModel = selectToTest.getSelectionModel();
rpc = ComponentTest.getRpcProxy(selectToTest,
MultiSelectServerRpc.class);
}
@@ -82,28 +79,28 @@ public class AbstractMultiSelectTest {

@Test
public void stableSelectionOrder() {
selectionModel.select("1");
selectionModel.select("2");
selectionModel.select("3");
selectToTest.select("1");
selectToTest.select("2");
selectToTest.select("3");

assertSelectionOrder(selectionModel, "1", "2", "3");
assertSelectionOrder("1", "2", "3");

selectionModel.deselect("1");
assertSelectionOrder(selectionModel, "2", "3");
selectToTest.deselect("1");
assertSelectionOrder("2", "3");

selectionModel.select("1");
assertSelectionOrder(selectionModel, "2", "3", "1");
selectToTest.select("1");
assertSelectionOrder("2", "3", "1");

selectionModel.selectItems("7", "8", "4");
assertSelectionOrder(selectionModel, "2", "3", "1", "7", "8", "4");
selectToTest.select("7", "8", "4");
assertSelectionOrder("2", "3", "1", "7", "8", "4");

selectionModel.deselectItems("2", "1", "4", "5");
assertSelectionOrder(selectionModel, "3", "7", "8");
selectToTest.deselect("2", "1", "4", "5");
assertSelectionOrder("3", "7", "8");

selectionModel.updateSelection(
selectToTest.updateSelection(
new LinkedHashSet<>(Arrays.asList("5", "2")),
new LinkedHashSet<>(Arrays.asList("3", "8")));
assertSelectionOrder(selectionModel, "7", "5", "2");
assertSelectionOrder("7", "5", "2");
}

@Test
@@ -120,27 +117,27 @@ public class AbstractMultiSelectTest {
selectToTest.select("2");

selectToTest.deselect("2");
selectToTest.getSelectionModel().deselectAll();
selectToTest.deselectAll();

selectToTest.getSelectionModel().selectItems("2", "3", "4");
selectToTest.getSelectionModel().deselectItems("1", "4");
selectToTest.select("2", "3", "4");
selectToTest.deselect("1", "4");

Assert.assertEquals(6, listenerCount.get());

// select partly selected
selectToTest.getSelectionModel().selectItems("2", "3", "4");
selectToTest.select("2", "3", "4");
Assert.assertEquals(7, listenerCount.get());

// select completely selected
selectToTest.getSelectionModel().selectItems("2", "3", "4");
selectToTest.select("2", "3", "4");
Assert.assertEquals(7, listenerCount.get());

// deselect partly not selected
selectToTest.getSelectionModel().selectItems("1", "4");
selectToTest.select("1", "4");
Assert.assertEquals(8, listenerCount.get());

// deselect completely not selected
selectToTest.getSelectionModel().selectItems("1", "4");
selectToTest.select("1", "4");
Assert.assertEquals(8, listenerCount.get());
}

@@ -154,88 +151,88 @@ public class AbstractMultiSelectTest {
});

rpcSelect("1");
assertSelectionOrder(selectionModel, "1");
assertSelectionOrder("1");

rpcSelect("2");
assertSelectionOrder(selectionModel, "1", "2");
rpcDeselect("2");
assertSelectionOrder(selectionModel, "1");
assertSelectionOrder("1", "2");
rpcDeselectItems("2");
assertSelectionOrder("1");
rpcSelect("3", "6");
assertSelectionOrder(selectionModel, "1", "3", "6");
rpcDeselect("1", "3");
assertSelectionOrder(selectionModel, "6");
assertSelectionOrder("1", "3", "6");
rpcDeselectItems("1", "3");
assertSelectionOrder("6");

Assert.assertEquals(5, listenerCount.get());

// select partly selected
rpcSelect("2", "3", "4");
Assert.assertEquals(6, listenerCount.get());
assertSelectionOrder(selectionModel, "6", "2", "3", "4");
assertSelectionOrder("6", "2", "3", "4");

// select completely selected
rpcSelect("2", "3", "4");
Assert.assertEquals(6, listenerCount.get());
assertSelectionOrder(selectionModel, "6", "2", "3", "4");
assertSelectionOrder("6", "2", "3", "4");

// deselect partly not selected
rpcDeselect("1", "4");
rpcDeselectItems("1", "4");
Assert.assertEquals(7, listenerCount.get());
assertSelectionOrder(selectionModel, "6", "2", "3");
assertSelectionOrder("6", "2", "3");

// deselect completely not selected
rpcDeselect("1", "4");
rpcDeselectItems("1", "4");
Assert.assertEquals(7, listenerCount.get());
assertSelectionOrder(selectionModel, "6", "2", "3");
assertSelectionOrder("6", "2", "3");

// select completely selected and deselect completely not selected
rpcUpdateSelection(new String[] { "3" }, new String[] { "1", "4" });
Assert.assertEquals(7, listenerCount.get());
assertSelectionOrder(selectionModel, "6", "2", "3");
assertSelectionOrder("6", "2", "3");

// select partly selected and deselect completely not selected
rpcUpdateSelection(new String[] { "4", "2" },
new String[] { "1", "8" });
Assert.assertEquals(8, listenerCount.get());
assertSelectionOrder(selectionModel, "6", "2", "3", "4");
assertSelectionOrder("6", "2", "3", "4");

// select completely selected and deselect partly not selected
rpcUpdateSelection(new String[] { "4", "3" },
new String[] { "1", "2" });
Assert.assertEquals(9, listenerCount.get());
assertSelectionOrder(selectionModel, "6", "3", "4");
assertSelectionOrder("6", "3", "4");

// duplicate case - ignored
rpcUpdateSelection(new String[] { "2" }, new String[] { "2" });
Assert.assertEquals(9, listenerCount.get());
assertSelectionOrder(selectionModel, "6", "3", "4");
assertSelectionOrder("6", "3", "4");

// duplicate case - duplicate removed
rpcUpdateSelection(new String[] { "2" }, new String[] { "2", "3" });
Assert.assertEquals(10, listenerCount.get());
assertSelectionOrder(selectionModel, "6", "4");
assertSelectionOrder("6", "4");

// duplicate case - duplicate removed
rpcUpdateSelection(new String[] { "6", "8" }, new String[] { "6" });
Assert.assertEquals(11, listenerCount.get());
assertSelectionOrder(selectionModel, "6", "4", "8");
assertSelectionOrder("6", "4", "8");
}

@Test
public void getValue() {
selectionModel.selectItems("1");
selectToTest.select("1");

Assert.assertEquals(Collections.singleton("1"),
selectToTest.getValue());

selectionModel.deselectAll();
selectToTest.deselectAll();
LinkedHashSet<String> set = new LinkedHashSet<>();
set.add("1");
set.add("5");
selectionModel.selectItems(set.toArray(new String[2]));
selectToTest.select(set.toArray(new String[2]));
Assert.assertEquals(set, selectToTest.getValue());

set.add("3");
selectionModel.selectItems("3");
selectToTest.select("3");
Assert.assertEquals(set, selectToTest.getValue());
}

@@ -259,26 +256,21 @@ public class AbstractMultiSelectTest {
selectToTest.setValue(Collections.singleton("1"));

Assert.assertEquals(Collections.singleton("1"),
selectionModel.getSelectedItems());
selectToTest.getSelectedItems());

Set<String> set = new LinkedHashSet<>();
set.add("4");
set.add("3");
selectToTest.setValue(set);

Assert.assertEquals(set, selectionModel.getSelectedItems());
Assert.assertEquals(set, selectToTest.getSelectedItems());
}

@Test
@SuppressWarnings({ "unchecked", "rawtypes", "serial" })
public void setValue_isDelegatedToDeselectAndUpdateSelection() {
Multi<?> model = Mockito.mock(Multi.class);
AbstractMultiSelect<String> select = new AbstractMultiSelect<String>() {
@Override
public Multi<String> getSelectionModel() {
return (Multi<String>) model;
}
};
AbstractMultiSelect<String> select = Mockito
.mock(AbstractMultiSelect.class);

Set set = new LinkedHashSet<>();
set.add("foo1");
@@ -287,11 +279,12 @@ public class AbstractMultiSelectTest {
selected.add("bar1");
selected.add("bar");
selected.add("bar2");
Mockito.when(model.getSelectedItems()).thenReturn(selected);
Mockito.when(select.getSelectedItems()).thenReturn(selected);
Mockito.doCallRealMethod().when(select).setValue(Mockito.anySet());

select.setValue(set);

Mockito.verify(model).updateSelection(set, selected);
Mockito.verify(select).updateSelection(set, selected);
}

@SuppressWarnings({ "unchecked", "serial" })
@@ -336,7 +329,7 @@ public class AbstractMultiSelectTest {
rpcUpdateSelection(keysToSelect, new String[] {});
}

private void rpcDeselect(String... keysToDeselect) {
private void rpcDeselectItems(String... keysToDeselect) {
rpcUpdateSelection(new String[] {}, keysToDeselect);
}

@@ -353,9 +346,8 @@ public class AbstractMultiSelectTest {
.key(dataObject);
}

private static void assertSelectionOrder(Multi<String> selectionModel,
String... selectionOrder) {
private void assertSelectionOrder(String... selectionOrder) {
Assert.assertEquals(Arrays.asList(selectionOrder),
new ArrayList<>(selectionModel.getSelectedItems()));
new ArrayList<>(selectToTest.getSelectedItems()));
}
}

+ 53
- 69
server/src/test/java/com/vaadin/ui/AbstractSingleSelectTest.java 查看文件

@@ -21,7 +21,6 @@ import static org.junit.Assert.assertTrue;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Optional;
@@ -34,13 +33,11 @@ import org.junit.Test;
import org.mockito.Mockito;

import com.vaadin.data.HasValue.ValueChangeEvent;
import com.vaadin.data.SelectionModel.Multi;
import com.vaadin.event.selection.SingleSelectionChangeEvent;
import com.vaadin.event.selection.SingleSelectionListener;
import com.vaadin.server.data.datasource.bov.Person;
import com.vaadin.shared.Registration;
import com.vaadin.shared.data.DataCommunicatorClientRpc;
import com.vaadin.ui.AbstractSingleSelect.AbstractSingleSelection;

/**
* Test for {@link AbstractSingleSelect} and {@link AbstractSingleSelection}
@@ -49,20 +46,15 @@ import com.vaadin.ui.AbstractSingleSelect.AbstractSingleSelection;
*/
public class AbstractSingleSelectTest {

private PersonListing.AbstractSingleSelection selectionModel;
private List<Person> selectionChanges;

private static class PersonListing extends AbstractSingleSelect<Person> {
public PersonListing() {
setSelectionModel(new SimpleSingleSelection());
}
}

@Before
public void initListing() {
listing = new PersonListing();
listing.setItems(PERSON_A, PERSON_B, PERSON_C);
selectionModel = listing.getSelectionModel();
selectionChanges = new ArrayList<>();
listing.addSelectionListener(e -> selectionChanges.add(e.getValue()));
}
@@ -77,18 +69,17 @@ public class AbstractSingleSelectTest {
@Test
public void select() {

selectionModel.select(PERSON_B);
listing.select(PERSON_B);

assertTrue(selectionModel.getSelectedItem().isPresent());
assertTrue(listing.getSelectedItem().isPresent());

assertEquals(PERSON_B, selectionModel.getSelectedItem().orElse(null));
assertEquals(PERSON_B, listing.getSelectedItem().orElse(null));

assertFalse(selectionModel.isSelected(PERSON_A));
assertTrue(selectionModel.isSelected(PERSON_B));
assertFalse(selectionModel.isSelected(PERSON_C));
assertFalse(listing.isSelected(PERSON_A));
assertTrue(listing.isSelected(PERSON_B));
assertFalse(listing.isSelected(PERSON_C));

assertEquals(Collections.singleton(PERSON_B),
selectionModel.getSelectedItems());
assertEquals(Optional.of(PERSON_B), listing.getSelectedItem());

assertEquals(Arrays.asList(PERSON_B), selectionChanges);
}
@@ -96,16 +87,16 @@ public class AbstractSingleSelectTest {
@Test
public void selectDeselect() {

selectionModel.select(PERSON_B);
selectionModel.deselect(PERSON_B);
listing.select(PERSON_B);
listing.deselect(PERSON_B);

assertFalse(selectionModel.getSelectedItem().isPresent());
assertFalse(listing.getSelectedItem().isPresent());

assertFalse(selectionModel.isSelected(PERSON_A));
assertFalse(selectionModel.isSelected(PERSON_B));
assertFalse(selectionModel.isSelected(PERSON_C));
assertFalse(listing.isSelected(PERSON_A));
assertFalse(listing.isSelected(PERSON_B));
assertFalse(listing.isSelected(PERSON_C));

assertTrue(selectionModel.getSelectedItems().isEmpty());
assertFalse(listing.getSelectedItem().isPresent());

assertEquals(Arrays.asList(PERSON_B, null), selectionChanges);
}
@@ -113,17 +104,16 @@ public class AbstractSingleSelectTest {
@Test
public void reselect() {

selectionModel.select(PERSON_B);
selectionModel.select(PERSON_C);
listing.select(PERSON_B);
listing.select(PERSON_C);

assertEquals(PERSON_C, selectionModel.getSelectedItem().orElse(null));
assertEquals(PERSON_C, listing.getSelectedItem().orElse(null));

assertFalse(selectionModel.isSelected(PERSON_A));
assertFalse(selectionModel.isSelected(PERSON_B));
assertTrue(selectionModel.isSelected(PERSON_C));
assertFalse(listing.isSelected(PERSON_A));
assertFalse(listing.isSelected(PERSON_B));
assertTrue(listing.isSelected(PERSON_C));

assertEquals(Collections.singleton(PERSON_C),
selectionModel.getSelectedItems());
assertEquals(Optional.of(PERSON_C), listing.getSelectedItem());

assertEquals(Arrays.asList(PERSON_B, PERSON_C), selectionChanges);
}
@@ -131,17 +121,16 @@ public class AbstractSingleSelectTest {
@Test
public void deselectNoOp() {

selectionModel.select(PERSON_C);
selectionModel.deselect(PERSON_B);
listing.select(PERSON_C);
listing.deselect(PERSON_B);

assertEquals(PERSON_C, selectionModel.getSelectedItem().orElse(null));
assertEquals(PERSON_C, listing.getSelectedItem().orElse(null));

assertFalse(selectionModel.isSelected(PERSON_A));
assertFalse(selectionModel.isSelected(PERSON_B));
assertTrue(selectionModel.isSelected(PERSON_C));
assertFalse(listing.isSelected(PERSON_A));
assertFalse(listing.isSelected(PERSON_B));
assertTrue(listing.isSelected(PERSON_C));

assertEquals(Collections.singleton(PERSON_C),
selectionModel.getSelectedItems());
assertEquals(Optional.of(PERSON_C), listing.getSelectedItem());

assertEquals(Arrays.asList(PERSON_C), selectionChanges);
}
@@ -149,17 +138,16 @@ public class AbstractSingleSelectTest {
@Test
public void selectTwice() {

selectionModel.select(PERSON_C);
selectionModel.select(PERSON_C);
listing.select(PERSON_C);
listing.select(PERSON_C);

assertEquals(PERSON_C, selectionModel.getSelectedItem().orElse(null));
assertEquals(PERSON_C, listing.getSelectedItem().orElse(null));

assertFalse(selectionModel.isSelected(PERSON_A));
assertFalse(selectionModel.isSelected(PERSON_B));
assertTrue(selectionModel.isSelected(PERSON_C));
assertFalse(listing.isSelected(PERSON_A));
assertFalse(listing.isSelected(PERSON_B));
assertTrue(listing.isSelected(PERSON_C));

assertEquals(Collections.singleton(PERSON_C),
selectionModel.getSelectedItems());
assertEquals(Optional.of(PERSON_C), listing.getSelectedItem());

assertEquals(Arrays.asList(PERSON_C), selectionChanges);
}
@@ -167,28 +155,28 @@ public class AbstractSingleSelectTest {
@Test
public void deselectTwice() {

selectionModel.select(PERSON_C);
selectionModel.deselect(PERSON_C);
selectionModel.deselect(PERSON_C);
listing.select(PERSON_C);
listing.deselect(PERSON_C);
listing.deselect(PERSON_C);

assertFalse(selectionModel.getSelectedItem().isPresent());
assertFalse(listing.getSelectedItem().isPresent());

assertFalse(selectionModel.isSelected(PERSON_A));
assertFalse(selectionModel.isSelected(PERSON_B));
assertFalse(selectionModel.isSelected(PERSON_C));
assertFalse(listing.isSelected(PERSON_A));
assertFalse(listing.isSelected(PERSON_B));
assertFalse(listing.isSelected(PERSON_C));

assertTrue(selectionModel.getSelectedItems().isEmpty());
assertFalse(listing.getSelectedItem().isPresent());

assertEquals(Arrays.asList(PERSON_C, null), selectionChanges);
}

@Test
public void getValue() {
selectionModel.setSelectedItem(PERSON_B);
listing.setSelectedItem(PERSON_B);

Assert.assertEquals(PERSON_B, listing.getValue());

selectionModel.deselectAll();
listing.deselect(PERSON_B);
Assert.assertNull(listing.getValue());
}

@@ -211,11 +199,11 @@ public class AbstractSingleSelectTest {
public void setValue() {
listing.setValue(PERSON_C);

Assert.assertEquals(PERSON_C, selectionModel.getSelectedItem().get());
Assert.assertEquals(PERSON_C, listing.getSelectedItem().get());

listing.setValue(null);

Assert.assertFalse(selectionModel.getSelectedItem().isPresent());
Assert.assertFalse(listing.getSelectedItem().isPresent());
}

@Test
@@ -268,15 +256,10 @@ public class AbstractSingleSelectTest {
}

@Test
@SuppressWarnings({ "unchecked", "rawtypes", "serial" })
@SuppressWarnings({ "unchecked", "rawtypes" })
public void setValue_isDelegatedToDeselectAndUpdateSelection() {
Multi<?> model = Mockito.mock(Multi.class);
AbstractMultiSelect<String> select = new AbstractMultiSelect<String>() {
@Override
public Multi<String> getSelectionModel() {
return (Multi<String>) model;
}
};
AbstractMultiSelect<String> select = Mockito
.mock(AbstractMultiSelect.class);

Set set = new LinkedHashSet<>();
set.add("foo1");
@@ -285,11 +268,12 @@ public class AbstractSingleSelectTest {
selected.add("bar1");
selected.add("bar");
selected.add("bar2");
Mockito.when(model.getSelectedItems()).thenReturn(selected);
Mockito.when(select.getSelectedItems()).thenReturn(selected);
Mockito.doCallRealMethod().when(select).setValue(Mockito.anySet());

select.setValue(set);

Mockito.verify(model).updateSelection(set, selected);
Mockito.verify(select).updateSelection(set, selected);
}

}

+ 1
- 4
server/src/test/java/com/vaadin/ui/RadioButtonGroupTest.java 查看文件

@@ -23,14 +23,12 @@ import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import com.vaadin.data.SelectionModel;
import com.vaadin.data.SelectionModel.Multi;
import com.vaadin.server.data.DataSource;
import com.vaadin.shared.data.selection.SelectionServerRpc;

public class RadioButtonGroupTest {
private RadioButtonGroup<String> radioButtonGroup;
private SelectionModel.Single<String> selectionModel;

@Before
public void setUp() {
@@ -38,7 +36,6 @@ public class RadioButtonGroupTest {
// Intentional deviation from upcoming selection order
radioButtonGroup
.setDataSource(DataSource.create("Third", "Second", "First"));
selectionModel = radioButtonGroup.getSelectionModel();
}

@Test
@@ -54,7 +51,7 @@ public class RadioButtonGroupTest {
radioButtonGroup.select("Second");

radioButtonGroup.deselect("Second");
radioButtonGroup.getSelectionModel().deselectAll();
radioButtonGroup.deselect("Second");

Assert.assertEquals(3, listenerCount.get());
}

+ 2
- 3
uitest/src/main/java/com/vaadin/tests/components/AbstractListingFocusBlurTest.java 查看文件

@@ -21,7 +21,6 @@ import java.util.stream.Collectors;
import java.util.stream.IntStream;

import com.googlecode.gentyref.GenericTypeReflector;
import com.vaadin.data.SelectionModel;
import com.vaadin.event.FieldEvents.BlurNotifier;
import com.vaadin.event.FieldEvents.FocusNotifier;
import com.vaadin.server.VaadinRequest;
@@ -31,7 +30,7 @@ import com.vaadin.ui.AbstractListing;
* @author Vaadin Ltd
*
*/
public abstract class AbstractListingFocusBlurTest<T extends AbstractListing<Integer, S> & FocusNotifier & BlurNotifier, S extends SelectionModel<Integer>>
public abstract class AbstractListingFocusBlurTest<T extends AbstractListing<Integer> & FocusNotifier & BlurNotifier>
extends AbstractTestUIWithLog {

@Override
@@ -45,7 +44,7 @@ public abstract class AbstractListingFocusBlurTest<T extends AbstractListing<Int
if (valueType instanceof Class<?>) {
Class<?> clazz = (Class<?>) valueType;
try {
AbstractListing<Integer, ?> select = (AbstractListing<Integer, ?>) clazz
AbstractListing<Integer> select = (AbstractListing<Integer>) clazz
.newInstance();
select.setItems(
IntStream.range(1, 10).mapToObj(Integer::valueOf)

+ 1
- 1
uitest/src/main/java/com/vaadin/tests/components/abstractlisting/AbstractListingTestUI.java 查看文件

@@ -8,7 +8,7 @@ import com.vaadin.tests.components.AbstractComponentTest;
import com.vaadin.ui.AbstractListing;

@Widgetset("com.vaadin.DefaultWidgetSet")
public abstract class AbstractListingTestUI<T extends AbstractListing<Object, ?>>
public abstract class AbstractListingTestUI<T extends AbstractListing<Object>>
extends AbstractComponentTest<T> {

@Override

+ 7
- 12
uitest/src/main/java/com/vaadin/tests/components/abstractlisting/AbstractMultiSelectTestUI.java 查看文件

@@ -5,7 +5,6 @@ import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import com.vaadin.data.SelectionModel.Multi;
import com.vaadin.ui.AbstractMultiSelect;
import com.vaadin.ui.ItemCaptionGenerator;

@@ -38,10 +37,8 @@ public abstract class AbstractMultiSelectTestUI<MULTISELECT extends AbstractMult
}

protected void createSelectionMenu() {
createClickAction(
"Clear selection", selectionCategory, (component, item,
data) -> component.getSelectionModel().deselectAll(),
"");
createClickAction("Clear selection", selectionCategory,
(component, item, data) -> component.deselectAll(), "");

Command<MULTISELECT, String> toggleSelection = (component, item,
data) -> toggleSelection(item);
@@ -59,20 +56,18 @@ public abstract class AbstractMultiSelectTestUI<MULTISELECT extends AbstractMult
}

private void toggleSelection(String item) {
Multi<Object> selectionModel = getComponent().getSelectionModel();
if (selectionModel.isSelected(item)) {
selectionModel.deselect(item);
if (getComponent().isSelected(item)) {
getComponent().deselect(item);
} else {
selectionModel.select(item);
getComponent().select(item);
}
}

private void toggleMultiSelection(boolean add, List<String> items) {
Multi<Object> selectionModel = getComponent().getSelectionModel();
if (add) {
selectionModel.selectItems(items.toArray());
getComponent().select(items.toArray());
} else {
selectionModel.deselectItems(items.toArray());
getComponent().deselect(items.toArray());
}
}


+ 4
- 3
uitest/src/main/java/com/vaadin/tests/components/abstractlisting/AbstractSingleSelectTestUI.java 查看文件

@@ -45,11 +45,12 @@ public abstract class AbstractSingleSelectTestUI<T extends AbstractSingleSelect<
options.put("Item 100", "Item 100");

createSelectAction("Select", "Selection", options, "None",
(c, selected, data) -> {
(component, selected, data) -> {
if (selected != null) {
c.select(selected);
component.select(selected);
} else {
c.getSelectedItems().forEach(c::deselect);
component.getSelectedItem()
.ifPresent(component::deselect);
}
});
}

+ 2
- 3
uitest/src/main/java/com/vaadin/tests/components/checkboxgroup/CheckBoxGroupFocusBlur.java 查看文件

@@ -15,7 +15,6 @@
*/
package com.vaadin.tests.components.checkboxgroup;

import com.vaadin.data.SelectionModel.Multi;
import com.vaadin.tests.components.AbstractListingFocusBlurTest;
import com.vaadin.ui.CheckBoxGroup;

@@ -28,7 +27,7 @@ import com.vaadin.ui.CheckBoxGroup;
* @author Vaadin Ltd
*
*/
public class CheckBoxGroupFocusBlur extends
AbstractListingFocusBlurTest<CheckBoxGroup<Integer>, Multi<Integer>> {
public class CheckBoxGroupFocusBlur
extends AbstractListingFocusBlurTest<CheckBoxGroup<Integer>> {

}

+ 15
- 21
uitest/src/main/java/com/vaadin/tests/components/grid/basics/GridBasics.java 查看文件

@@ -233,11 +233,9 @@ public class GridBasics extends AbstractTestUIWithLog {
MenuItem headerTypeMenu = columnMenu.addItem("Header Type", null);
headerTypeMenu.addItem("Text Header", selectedItem -> grid
.getDefaultHeaderRow().getCell(col).setText("Text Header"));
headerTypeMenu
.addItem("HTML Header",
selectedItem -> grid.getDefaultHeaderRow()
.getCell(col)
.setHtml("<b>HTML Header</b>"));
headerTypeMenu.addItem("HTML Header",
selectedItem -> grid.getDefaultHeaderRow().getCell(col)
.setHtml("<b>HTML Header</b>"));
headerTypeMenu.addItem("Widget Header", selectedItem -> {
final Button button = new Button("Button Header");
button.addClickListener(clickEvent -> log("Button clicked!"));
@@ -293,15 +291,12 @@ public class GridBasics extends AbstractTestUIWithLog {
: null))
.setCheckable(true);
stateMenu
.addItem("Cell description generator",
item -> grid.getColumns().stream().findFirst()
.ifPresent(
c -> c.setDescriptionGenerator(
item.isChecked()
? t -> "Cell tooltip for row "
+ t.getRowNumber()
+ ", Column 0"
: null)))
.addItem("Cell description generator", item -> grid.getColumns()
.stream().findFirst()
.ifPresent(c -> c.setDescriptionGenerator(
item.isChecked() ? t -> "Cell tooltip for row "
+ t.getRowNumber() + ", Column 0"
: null)))
.setCheckable(true);
stateMenu.addItem("Item click listener", new Command() {

@@ -393,10 +388,10 @@ public class GridBasics extends AbstractTestUIWithLog {
private void createBodyMenu(MenuItem rowMenu) {
rowMenu.addItem("Toggle first row selection", menuItem -> {
DataObject item = data.get(0);
if (grid.isSelected(item)) {
grid.deselect(item);
if (grid.getSelectionModel().isSelected(item)) {
grid.getSelectionModel().deselect(item);
} else {
grid.select(item);
grid.getSelectionModel().select(item);
}
});
}
@@ -432,10 +427,9 @@ public class GridBasics extends AbstractTestUIWithLog {
private void createFooterMenu(MenuItem footerMenu) {
footerMenu.addItem("Add default footer row", menuItem -> {
FooterRow defaultFooter = grid.appendFooterRow();
grid.getColumns()
.forEach(column -> defaultFooter.getCell(column)
.setText(grid.getDefaultHeaderRow().getCell(column)
.getText()));
grid.getColumns().forEach(
column -> defaultFooter.getCell(column).setText(grid
.getDefaultHeaderRow().getCell(column).getText()));
footerMenu.removeChild(menuItem);
});
footerMenu.addItem("Append footer row", menuItem -> {

+ 2
- 3
uitest/src/main/java/com/vaadin/tests/components/nativeselect/NativeSelectFocusBlur.java 查看文件

@@ -16,7 +16,6 @@
package com.vaadin.tests.components.nativeselect;

import com.vaadin.tests.components.AbstractListingFocusBlurTest;
import com.vaadin.ui.AbstractSingleSelect;
import com.vaadin.ui.NativeSelect;

/**
@@ -28,7 +27,7 @@ import com.vaadin.ui.NativeSelect;
* @author Vaadin Ltd
*
*/
public class NativeSelectFocusBlur extends
AbstractListingFocusBlurTest<NativeSelect<Integer>, AbstractSingleSelect<Integer>.AbstractSingleSelection> {
public class NativeSelectFocusBlur
extends AbstractListingFocusBlurTest<NativeSelect<Integer>> {

}

+ 15
- 18
uitest/src/main/java/com/vaadin/tests/components/radiobutton/RadioButtonGroupTestUI.java 查看文件

@@ -15,15 +15,14 @@
*/
package com.vaadin.tests.components.radiobutton;

import com.vaadin.data.SelectionModel;
import java.util.LinkedHashMap;
import java.util.stream.IntStream;

import com.vaadin.server.FontAwesome;
import com.vaadin.tests.components.abstractlisting.AbstractListingTestUI;
import com.vaadin.ui.ItemCaptionGenerator;
import com.vaadin.ui.RadioButtonGroup;

import java.util.LinkedHashMap;
import java.util.stream.IntStream;

/**
* Test UI for RadioButtonGroup component
*
@@ -50,9 +49,9 @@ public class RadioButtonGroupTestUI
}

protected void createSelectionMenu() {
createClickAction(
"Clear selection", selectionCategory, (component, item,
data) -> component.getSelectionModel().deselectAll(),
createClickAction("Clear selection", selectionCategory,
(component, item, data) -> component.getSelectedItem()
.ifPresent(component::deselect),
"");

Command<RadioButtonGroup<Object>, String> toggleSelection = (component,
@@ -62,13 +61,14 @@ public class RadioButtonGroupTestUI
.forEach(item -> createClickAction("Toggle " + item,
selectionCategory, toggleSelection, item));
}

private void createItemIconGeneratorMenu() {
createBooleanAction("Use Item Icon Generator", "Item Icon Generator", false,
this::useItemIconGenerator);
createBooleanAction("Use Item Icon Generator", "Item Icon Generator",
false, this::useItemIconGenerator);
}

private void useItemIconGenerator(RadioButtonGroup<Object> group,
boolean activate, Object data) {
boolean activate, Object data) {
if (activate) {
group.setItemIconGenerator(
item -> FontAwesome.values()[getIndex(item) + 1]);
@@ -85,20 +85,18 @@ public class RadioButtonGroupTestUI
options.put("Custom Caption Generator",
item -> item.toString() + " Caption");

createSelectAction("Item Caption Generator", "Item Caption Generator", options,
"None", (radioButtonGroup, captionGenerator, data) -> {
createSelectAction("Item Caption Generator", "Item Caption Generator",
options, "None", (radioButtonGroup, captionGenerator, data) -> {
radioButtonGroup.setItemCaptionGenerator(captionGenerator);
radioButtonGroup.getDataSource().refreshAll();
}, true);
}

private void toggleSelection(String item) {
SelectionModel.Single<Object> selectionModel = getComponent()
.getSelectionModel();
if (selectionModel.isSelected(item)) {
selectionModel.deselect(item);
if (getComponent().isSelected(item)) {
getComponent().deselect(item);
} else {
selectionModel.select(item);
getComponent().select(item);
}
}

@@ -108,7 +106,6 @@ public class RadioButtonGroupTestUI
e -> log("Selected: " + e.getSelectedItem())));
}


private int getIndex(Object item) {
int index = item.toString().indexOf(' ');
if (index < 0) {

+ 2
- 3
uitest/src/main/java/com/vaadin/tests/components/radiobuttongroup/RadioButtonGroupFocusBlur.java 查看文件

@@ -16,7 +16,6 @@
package com.vaadin.tests.components.radiobuttongroup;

import com.vaadin.tests.components.AbstractListingFocusBlurTest;
import com.vaadin.ui.AbstractSingleSelect;
import com.vaadin.ui.RadioButtonGroup;

/**
@@ -28,7 +27,7 @@ import com.vaadin.ui.RadioButtonGroup;
* @author Vaadin Ltd
*
*/
public class RadioButtonGroupFocusBlur extends
AbstractListingFocusBlurTest<RadioButtonGroup<Integer>, AbstractSingleSelect<Integer>.AbstractSingleSelection> {
public class RadioButtonGroupFocusBlur
extends AbstractListingFocusBlurTest<RadioButtonGroup<Integer>> {

}

+ 17
- 32
uitest/src/main/java/com/vaadin/tests/data/DummyData.java 查看文件

@@ -4,19 +4,17 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.Optional;
import java.util.stream.Stream;

import com.vaadin.annotations.Widgetset;
import com.vaadin.data.SelectionModel;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.data.DataCommunicator;
import com.vaadin.server.data.ListDataSource;
import com.vaadin.server.data.Query;
import com.vaadin.shared.data.DataCommunicatorConstants;
import com.vaadin.tests.components.AbstractTestUIWithLog;
import com.vaadin.tests.widgetset.TestingWidgetSet;
import com.vaadin.ui.AbstractListing;
import com.vaadin.ui.AbstractSingleSelect;
import com.vaadin.ui.Button;
import com.vaadin.ui.HorizontalLayout;

@@ -44,26 +42,32 @@ public class DummyData extends AbstractTestUIWithLog {
* Simplified server only selection model. Selection state passed in data,
* shown as bold text.
*/
private static class DummySelectionModel implements SelectionModel<String> {
public static class DummyComponent extends AbstractSingleSelect<String> {

private String selected;
private DataCommunicator<String> communicator;

private DummyComponent() {
addDataGenerator((str, json) -> {
json.put(DataCommunicatorConstants.DATA, str);
if (isSelected(str)) {
json.put(DataCommunicatorConstants.SELECTED, true);
}
});
}

@Override
public Set<String> getSelectedItems() {
if (selected != null) {
return Collections.singleton(selected);
}
return Collections.emptySet();
public Optional<String> getSelectedItem() {
return Optional.ofNullable(selected);
}

@Override
public void select(String item) {
if (selected != null) {
communicator.refresh(selected);
getDataCommunicator().refresh(selected);
}
selected = item;
if (selected != null) {
communicator.refresh(selected);
getDataCommunicator().refresh(selected);
}
}

@@ -73,25 +77,6 @@ public class DummyData extends AbstractTestUIWithLog {
select(null);
}
}

private void setCommunicator(DataCommunicator<String> dataComm) {
communicator = dataComm;
}
}

public static class DummyComponent
extends AbstractListing<String, DummySelectionModel> {

private DummyComponent() {
setSelectionModel(new DummySelectionModel());
addDataGenerator((str, json) -> {
json.put(DataCommunicatorConstants.DATA, str);
if (isSelected(str)) {
json.put(DataCommunicatorConstants.SELECTED, true);
}
});
getSelectionModel().setCommunicator(getDataCommunicator());
}
}

@Override

Loading…
取消
儲存