Browse Source

Allow to set caption for the empty selection in a ComboBox.

Fixes vaadin/framework8-issues#163


Change-Id: Ib68ad5421934c8375a91d7948d860381a5adb9bb
tags/8.0.0.alpha9
Denis Anisimov 7 years ago
parent
commit
1bb04399f3

+ 2
- 2
client/src/main/java/com/vaadin/client/ui/VComboBox.java View File

@@ -1592,7 +1592,7 @@ public class VComboBox extends Composite implements Field, KeyDownHandler,
* <p>
* For internal use only. May be removed or replaced in the future.
*/
public final List<ComboBoxSuggestion> currentSuggestions = new ArrayList<ComboBoxSuggestion>();
public final List<ComboBoxSuggestion> currentSuggestions = new ArrayList<>();

/** For internal use only. May be removed or replaced in the future. */
public String serverSelectedKey;
@@ -2067,7 +2067,7 @@ public class VComboBox extends Composite implements Field, KeyDownHandler,
Unit.PX);
}

private static Set<Integer> navigationKeyCodes = new HashSet<Integer>();
private static Set<Integer> navigationKeyCodes = new HashSet<>();
static {
navigationKeyCodes.add(KeyCodes.KEY_DOWN);
navigationKeyCodes.add(KeyCodes.KEY_UP);

+ 99
- 74
client/src/main/java/com/vaadin/client/ui/combobox/ComboBoxConnector.java View File

@@ -15,6 +15,8 @@
*/
package com.vaadin.client.ui.combobox;

import java.util.List;

import com.vaadin.client.Profiler;
import com.vaadin.client.annotations.OnStateChange;
import com.vaadin.client.communication.StateChangeEvent;
@@ -25,6 +27,7 @@ import com.vaadin.client.ui.HasErrorIndicator;
import com.vaadin.client.ui.HasRequiredIndicator;
import com.vaadin.client.ui.SimpleManagedLayout;
import com.vaadin.client.ui.VComboBox;
import com.vaadin.client.ui.VComboBox.ComboBoxSuggestion;
import com.vaadin.client.ui.VComboBox.DataReceivedHandler;
import com.vaadin.shared.EventId;
import com.vaadin.shared.Registration;
@@ -87,6 +90,15 @@ public class ComboBoxConnector extends AbstractListingConnector
Profiler.leave("ComboBoxConnector.onStateChanged update content");
}

@OnStateChange("emptySelectionCaption")
private void onEmptySelectionCaptionChange() {
List<ComboBoxSuggestion> suggestions = getWidget().currentSuggestions;
if (!suggestions.isEmpty() && isFirstPage()) {
suggestions.remove(0);
addEmptySelectionItem();
}
}

@OnStateChange({ "selectedItemKey", "selectedItemCaption" })
private void onSelectionChange() {
getDataReceivedHandler().updateSelectionFromServer(
@@ -249,80 +261,7 @@ public class ComboBoxConnector extends AbstractListingConnector
public void setDataSource(DataSource<JsonObject> dataSource) {
super.setDataSource(dataSource);
dataChangeHandlerRegistration = dataSource
.addDataChangeHandler(range -> {
// try to find selected item if requested
if (getState().scrollToSelectedItem
&& getState().pageLength > 0
&& getWidget().currentPage < 0
&& getWidget().selectedOptionKey != null) {
// search for the item with the selected key
getWidget().currentPage = 0;
for (int i = 0; i < getDataSource().size(); ++i) {
JsonObject row = getDataSource().getRow(i);
if (row != null) {
String key = getRowKey(row);
if (getWidget().selectedOptionKey.equals(key)) {
if (getWidget().nullSelectionAllowed) {
getWidget().currentPage = (i + 1)
/ getState().pageLength;
} else {
getWidget().currentPage = i
/ getState().pageLength;
}
break;
}
}
}
} else if (getWidget().currentPage < 0) {
getWidget().currentPage = 0;
}

getWidget().currentSuggestions.clear();

int start = getWidget().currentPage
* getWidget().pageLength;
int end = getWidget().pageLength > 0
? start + getWidget().pageLength
: getDataSource().size();

if (getWidget().nullSelectionAllowed
&& "".equals(getWidget().lastFilter)) {
// add special null selection item...
if (getWidget().currentPage == 0) {
getWidget().currentSuggestions
.add(getWidget().new ComboBoxSuggestion("",
"", null, null));
} else {
// ...or leave space for it
start = start - 1;
}
// in either case, the last item to show is
// shifted by one
end = end - 1;
}

for (int i = start; i < end; ++i) {
JsonObject row = getDataSource().getRow(i);

if (row != null) {
String key = getRowKey(row);
String caption = row
.getString(DataCommunicatorConstants.NAME);
String style = row
.getString(ComboBoxConstants.STYLE);
String untranslatedIconUri = row
.getString(ComboBoxConstants.ICON);
getWidget().currentSuggestions
.add(getWidget().new ComboBoxSuggestion(key,
caption, style,
untranslatedIconUri));
}
}
getWidget().totalMatches = getDataSource().size()
+ (getState().emptySelectionAllowed ? 1 : 0);

getDataReceivedHandler().dataReceived();
});
.addDataChangeHandler(range -> refreshData());
}

@Override
@@ -335,4 +274,90 @@ public class ComboBoxConnector extends AbstractListingConnector
public boolean isRequiredIndicatorVisible() {
return getState().required && !isReadOnly();
}

private void refreshData() {
updateCurrentPage();

getWidget().currentSuggestions.clear();

int start = getWidget().currentPage * getWidget().pageLength;
int end = getWidget().pageLength > 0 ? start + getWidget().pageLength
: getDataSource().size();

if (getWidget().nullSelectionAllowed
&& "".equals(getWidget().lastFilter)) {
// add special null selection item...
if (isFirstPage()) {
addEmptySelectionItem();
} else {
// ...or leave space for it
start = start - 1;
}
// in either case, the last item to show is
// shifted by one
end = end - 1;
}

updateSuggestions(start, end);
getWidget().totalMatches = getDataSource().size()
+ (getState().emptySelectionAllowed ? 1 : 0);

getDataReceivedHandler().dataReceived();
}

private void updateSuggestions(int start, int end) {
for (int i = start; i < end; ++i) {
JsonObject row = getDataSource().getRow(i);

if (row != null) {
String key = getRowKey(row);
String caption = row.getString(DataCommunicatorConstants.NAME);
String style = row.getString(ComboBoxConstants.STYLE);
String untranslatedIconUri = row
.getString(ComboBoxConstants.ICON);
getWidget().currentSuggestions
.add(getWidget().new ComboBoxSuggestion(key, caption,
style, untranslatedIconUri));
}
}
}

private boolean isFirstPage() {
return getWidget().currentPage == 0;
}

private void addEmptySelectionItem() {
if (isFirstPage()) {
getWidget().currentSuggestions.add(0,
getWidget().new ComboBoxSuggestion("",
getState().emptySelectionCaption, null, null));
}
}

private void updateCurrentPage() {
// try to find selected item if requested
if (getState().scrollToSelectedItem && getState().pageLength > 0
&& getWidget().currentPage < 0
&& getWidget().selectedOptionKey != null) {
// search for the item with the selected key
getWidget().currentPage = 0;
for (int i = 0; i < getDataSource().size(); ++i) {
JsonObject row = getDataSource().getRow(i);
if (row != null) {
String key = getRowKey(row);
if (getWidget().selectedOptionKey.equals(key)) {
if (getWidget().nullSelectionAllowed) {
getWidget().currentPage = (i + 1)
/ getState().pageLength;
} else {
getWidget().currentPage = i / getState().pageLength;
}
break;
}
}
}
} else if (getWidget().currentPage < 0) {
getWidget().currentPage = 0;
}
}
}

+ 37
- 0
server/src/main/java/com/vaadin/ui/ComboBox.java View File

@@ -401,6 +401,43 @@ public class ComboBox<T> extends AbstractSingleSelect<T>
getState().emptySelectionAllowed = emptySelectionAllowed;
}

/**
* Returns the empty selection caption.
* <p>
* The empty string {@code ""} is the default empty selection caption.
*
* @see #setEmptySelectionAllowed(boolean)
* @see #isEmptySelectionAllowed()
* @see #setEmptySelectionCaption(String)
* @see #isSelected(Object)
* @see #select(Object)
*
* @return the empty selection caption, not {@code null}
*/
public String getEmptySelectionCaption() {
return getState(false).emptySelectionCaption;
}

/**
* Sets the empty selection caption.
* <p>
* The empty string {@code ""} is the default empty selection caption.
* <p>
* If empty selection is allowed via the
* {@link #setEmptySelectionAllowed(boolean)} method (it is by default) then
* the empty item will be shown with the given caption.
*
* @param caption
* the caption to set, not {@code null}
* @see #getNullSelectionItemId()
* @see #isSelected(Object)
* @see #select(Object)
*/
public void setEmptySelectionCaption(String caption) {
Objects.nonNull(caption);
getState().emptySelectionCaption = caption;
}

/**
* Sets the suggestion pop-up's width as a CSS string. By using relative
* units (e.g. "50%") it's possible to set the popup's width relative to the

+ 5
- 0
shared/src/main/java/com/vaadin/shared/ui/combobox/ComboBoxState.java View File

@@ -88,4 +88,9 @@ public class ComboBoxState extends AbstractSingleSelectState {
*/
public String selectedItemCaption;

/**
* Caption for item which represents empty selection.
*/
public String emptySelectionCaption = "";

}

+ 48
- 0
uitest/src/main/java/com/vaadin/tests/components/combobox/ComboBoxEmptyCaption.java View File

@@ -0,0 +1,48 @@
/*
* 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.components.combobox;

import java.util.stream.Collectors;
import java.util.stream.IntStream;

import com.vaadin.server.VaadinRequest;
import com.vaadin.tests.components.AbstractTestUI;
import com.vaadin.ui.Button;
import com.vaadin.ui.ComboBox;

/**
* @author Vaadin Ltd
*
*/
public class ComboBoxEmptyCaption extends AbstractTestUI {

@Override
protected void setup(VaadinRequest request) {
ComboBox<String> combo = new ComboBox<>();
combo.setItems(
IntStream.range(1, 100).mapToObj(number -> "item" + number)
.collect(Collectors.toList()));
addComponent(combo);
Button setCaption = new Button("Set empty selection caption to 'empty'",
event -> combo.setEmptySelectionCaption("empty"));
Button resetCaption = new Button(
"Set empty selection caption to empty string",
event -> combo.setEmptySelectionCaption(""));
Button disableCaption = new Button("Disable empty selection caption",
event -> combo.setEmptySelectionAllowed(false));
addComponents(setCaption, resetCaption, disableCaption);
}
}

+ 84
- 0
uitest/src/test/java/com/vaadin/tests/components/combobox/ComboBoxEmptyCaptionTest.java View File

@@ -0,0 +1,84 @@
/*
* 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.components.combobox;

import java.util.ArrayList;
import java.util.Arrays;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import com.vaadin.testbench.elements.ButtonElement;
import com.vaadin.testbench.elements.ComboBoxElement;
import com.vaadin.tests.tb3.MultiBrowserTest;

/**
* @author Vaadin Ltd
*
*/
public class ComboBoxEmptyCaptionTest extends MultiBrowserTest {

@Before
public void setUp() {
openTestURL();
}

@Test
public void emptyItemCaption() {
ComboBoxElement combo = $(ComboBoxElement.class).first();
// empty string in caption becomes &nbsp; because of #7506
ensureSuggestions(combo, " ", "item1", "item2", "item3", "item4",
"item5", "item6", "item7", "item8", "item9");
}

@Test
public void hasEmptyItemCaption() {
ComboBoxElement combo = $(ComboBoxElement.class).first();
// set some caption for the empty selection element
$(ButtonElement.class).first().click();
ensureSuggestions(combo, "empty", "item1", "item2", "item3", "item4",
"item5", "item6", "item7", "item8", "item9");
}

@Test
public void resetEmptyItem() {
ComboBoxElement combo = $(ComboBoxElement.class).first();
// set some caption for the empty selection element
$(ButtonElement.class).first().click();
// set empty string back as an empty caption
$(ButtonElement.class).get(1).click();
ensureSuggestions(combo, " ", "item1", "item2", "item3", "item4",
"item5", "item6", "item7", "item8", "item9");
}

@Test
public void disableEmptyItem() {
ComboBoxElement combo = $(ComboBoxElement.class).first();
// set some caption for the empty selection element
$(ButtonElement.class).get(2).click();
ensureSuggestions(combo, "item1", "item2", "item3", "item4", "item5",
"item6", "item7", "item8", "item9", "item10");
}

private void ensureSuggestions(ComboBoxElement element,
String... suggestions) {
element.openPopup();
System.out.println(element.getPopupSuggestions());
Assert.assertEquals(Arrays.asList(suggestions),
new ArrayList<>(element.getPopupSuggestions()));
}
}

Loading…
Cancel
Save