Change-Id: I60c5d9f183d1955fbbd3341e3ac9f93aae755f80tags/8.0.0.alpha8
@@ -39,6 +39,8 @@ import com.vaadin.ui.declarative.DesignContext; | |||
import com.vaadin.ui.declarative.DesignException; | |||
import com.vaadin.util.ReflectTools; | |||
import elemental.json.Json; | |||
/** | |||
* An abstract base class for listing components that only support single | |||
* selection and no lazy loading of data items. | |||
@@ -241,6 +243,12 @@ public abstract class AbstractSingleSelect<T> extends AbstractListing<T> | |||
} | |||
doSetSelectedKey(key); | |||
// Update diffstate so that a change will be sent to the client if the | |||
// selection is changed to its original value | |||
updateDiffstate("selectedItemKey", | |||
key == null ? Json.createNull() : Json.create(key)); | |||
fireEvent(new SingleSelectionEvent<>(AbstractSingleSelect.this, true)); | |||
} | |||
@@ -52,6 +52,7 @@ import com.vaadin.ui.declarative.DesignAttributeHandler; | |||
import com.vaadin.ui.declarative.DesignContext; | |||
import com.vaadin.ui.declarative.DesignFormatter; | |||
import elemental.json.Json; | |||
import elemental.json.JsonObject; | |||
/** | |||
@@ -670,4 +671,21 @@ public class ComboBox<T> extends AbstractSingleSelect<T> | |||
// ComboBox. | |||
return (DataCommunicator<T, String>) super.getDataCommunicator(); | |||
} | |||
@Override | |||
protected void setSelectedFromClient(String key) { | |||
super.setSelectedFromClient(key); | |||
/* | |||
* The client side for combo box always expects a state change for | |||
* selectedItemKey after it has sent a selection change. This means that | |||
* we must store a value in the diffstate that guarantees that a new | |||
* value will be sent, regardless of what the value actually is at the | |||
* time when changes are sent. | |||
* | |||
* Keys are always strings (or null), so using a non-string type will | |||
* always trigger a diff mismatch and a resend. | |||
*/ | |||
updateDiffstate("selectedItemKey", Json.create(0)); | |||
} | |||
} |
@@ -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.ui; | |||
import org.junit.Test; | |||
import com.vaadin.shared.data.selection.SelectionServerRpc; | |||
import com.vaadin.tests.util.MockUI; | |||
public class ComboBoxTest { | |||
@Test | |||
public void testResetValue() { | |||
ComboBox<String> comboBox = new ComboBox<>(); | |||
comboBox.setItems("one", "two"); | |||
// Reset value whenever it changes (in a real case, this listener would | |||
// do something with the selected value before discarding it) | |||
comboBox.addValueChangeListener(e -> comboBox.setValue(null)); | |||
// "Attach" the component and initialize diffstate | |||
new MockUI().setContent(comboBox); | |||
ComponentTest.syncToClient(comboBox); | |||
// Emulate selection of "one" | |||
String oneKey = comboBox.getDataCommunicator().getKeyMapper() | |||
.key("one"); | |||
ComponentTest.getRpcProxy(comboBox, SelectionServerRpc.class) | |||
.select(oneKey); | |||
ComponentTest.assertEncodedStateProperties(comboBox, | |||
"Selection change done by the listener should be sent to the client", | |||
"selectedItemKey"); | |||
} | |||
} |
@@ -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.tests.components.combobox; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.tests.components.AbstractTestUIWithLog; | |||
import com.vaadin.ui.ComboBox; | |||
public class ComboBoxAutoresetValue extends AbstractTestUIWithLog { | |||
public static final String RESET = "Reset"; | |||
public static final String CHANGE = "Change to something else"; | |||
public static final String SOMETHING = "Something else"; | |||
@Override | |||
protected void setup(VaadinRequest request) { | |||
ComboBox<String> comboBox = new ComboBox<>(); | |||
comboBox.setItems(RESET, CHANGE, SOMETHING); | |||
comboBox.addValueChangeListener(e -> { | |||
String value = e.getValue(); | |||
log("Value changed to " + value); | |||
if (e.isUserOriginated()) { | |||
if (RESET.equals(value)) { | |||
e.getSource().setValue(null); | |||
} else if (CHANGE.equals(value)) { | |||
e.getSource().setValue(SOMETHING); | |||
} | |||
} | |||
}); | |||
addComponent(comboBox); | |||
} | |||
@Override | |||
public String getDescription() { | |||
return "Changing the ComboBox value in its own value change listener should work"; | |||
} | |||
} |
@@ -0,0 +1,59 @@ | |||
/* | |||
* 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 org.junit.Assert; | |||
import org.junit.Test; | |||
import com.vaadin.testbench.customelements.ComboBoxElement; | |||
import com.vaadin.tests.tb3.SingleBrowserTest; | |||
public class ComboBoxAutoresetValueTest extends SingleBrowserTest { | |||
@Test | |||
public void testValueChanges() { | |||
openTestURL(); | |||
ComboBoxElement comboBox = $(ComboBoxElement.class).first(); | |||
Assert.assertEquals("", comboBox.getValue()); | |||
comboBox.selectByText(ComboBoxAutoresetValue.RESET); | |||
assertLogChange(1, ComboBoxAutoresetValue.RESET, 1); | |||
assertLogChange(2, null, 0); | |||
Assert.assertEquals("", comboBox.getValue()); | |||
comboBox.selectByText(ComboBoxAutoresetValue.CHANGE); | |||
assertLogChange(3, ComboBoxAutoresetValue.CHANGE, 1); | |||
assertLogChange(4, ComboBoxAutoresetValue.SOMETHING, 0); | |||
Assert.assertEquals(ComboBoxAutoresetValue.SOMETHING, | |||
comboBox.getValue()); | |||
comboBox.selectByText(ComboBoxAutoresetValue.SOMETHING); | |||
// No new log items | |||
assertLogChange(4, ComboBoxAutoresetValue.SOMETHING, 0); | |||
Assert.assertEquals(ComboBoxAutoresetValue.SOMETHING, | |||
comboBox.getValue()); | |||
} | |||
private void assertLogChange(int sequenceNumber, String expectedValue, | |||
int rowIndex) { | |||
Assert.assertEquals( | |||
sequenceNumber + ". Value changed to " + expectedValue, | |||
getLogRow(rowIndex)); | |||
} | |||
} |