|
|
@@ -0,0 +1,188 @@ |
|
|
|
package com.vaadin.tests.components.textfield; |
|
|
|
|
|
|
|
import java.util.Collections; |
|
|
|
import java.util.HashMap; |
|
|
|
import java.util.Map; |
|
|
|
|
|
|
|
import com.vaadin.data.Item; |
|
|
|
import com.vaadin.data.Property.ValueChangeEvent; |
|
|
|
import com.vaadin.data.Property.ValueChangeListener; |
|
|
|
import com.vaadin.data.util.BeanContainer; |
|
|
|
import com.vaadin.data.util.BeanItem; |
|
|
|
import com.vaadin.event.ShortcutAction.KeyCode; |
|
|
|
import com.vaadin.tests.components.TestBase; |
|
|
|
import com.vaadin.ui.Button; |
|
|
|
import com.vaadin.ui.Button.ClickEvent; |
|
|
|
import com.vaadin.ui.Button.ClickListener; |
|
|
|
import com.vaadin.ui.CustomComponent; |
|
|
|
import com.vaadin.ui.Form; |
|
|
|
import com.vaadin.ui.HorizontalLayout; |
|
|
|
import com.vaadin.ui.Table; |
|
|
|
import com.vaadin.ui.VerticalSplitPanel; |
|
|
|
|
|
|
|
public class OutOfSyncIssueWithKeyboardShortcut extends TestBase { |
|
|
|
|
|
|
|
public static class TestToppingsView extends CustomComponent { |
|
|
|
|
|
|
|
public static class Topping { |
|
|
|
private Long id; |
|
|
|
private String name = "ham"; |
|
|
|
|
|
|
|
public void setId(Long id) { |
|
|
|
this.id = id; |
|
|
|
} |
|
|
|
|
|
|
|
public Long getId() { |
|
|
|
return id; |
|
|
|
} |
|
|
|
|
|
|
|
public void setName(String name) { |
|
|
|
this.name = name; |
|
|
|
} |
|
|
|
|
|
|
|
public String getName() { |
|
|
|
return name; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private Table table = new Table(); |
|
|
|
|
|
|
|
private Form form = new Form(); |
|
|
|
|
|
|
|
private Map<Long, Topping> toppings = new HashMap<Long, Topping>(); |
|
|
|
private long index = 1; |
|
|
|
|
|
|
|
private String previousFragment = null; |
|
|
|
|
|
|
|
public TestToppingsView() { |
|
|
|
setSizeFull(); |
|
|
|
|
|
|
|
VerticalSplitPanel mainLayout = new VerticalSplitPanel(); |
|
|
|
mainLayout.setSplitPosition(30); |
|
|
|
setCompositionRoot(mainLayout); |
|
|
|
|
|
|
|
table.setSizeFull(); |
|
|
|
table.setImmediate(true); |
|
|
|
table.setSelectable(true); |
|
|
|
|
|
|
|
mainLayout.setFirstComponent(table); |
|
|
|
mainLayout.setSecondComponent(form); |
|
|
|
|
|
|
|
form.setImmediate(true); |
|
|
|
// TODO this is critical for the problem to occur |
|
|
|
form.setWriteThrough(false); |
|
|
|
|
|
|
|
HorizontalLayout footer = new HorizontalLayout(); |
|
|
|
footer.setSpacing(true); |
|
|
|
form.setFooter(footer); |
|
|
|
Button saveButton = new Button("Save"); |
|
|
|
footer.addComponent(saveButton); |
|
|
|
|
|
|
|
// make saving the form the default action on Enter keypress |
|
|
|
saveButton.setClickShortcut(KeyCode.ENTER); |
|
|
|
|
|
|
|
table.addListener(new ValueChangeListener() { |
|
|
|
public void valueChange(ValueChangeEvent event) { |
|
|
|
Object value = event.getProperty().getValue(); |
|
|
|
if (value != null) { |
|
|
|
String fragment = "edit/" |
|
|
|
+ String.valueOf(value) |
|
|
|
.replaceAll("[^0-9]", ""); |
|
|
|
if (!fragment.equals(previousFragment)) { |
|
|
|
navigateTo(fragment); |
|
|
|
previousFragment = fragment; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
saveButton.addListener(new ClickListener() { |
|
|
|
public void buttonClick(ClickEvent event) { |
|
|
|
form.commit(); |
|
|
|
Topping entity = getEntityForItem(form.getItemDataSource()); |
|
|
|
if (entity != null && entity.getId() == null) { |
|
|
|
entity.setId(index++); |
|
|
|
toppings.put(entity.getId(), entity); |
|
|
|
} |
|
|
|
navigateTo(null); |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
// create new entity at the beginning |
|
|
|
navigateTo("new"); |
|
|
|
} |
|
|
|
|
|
|
|
public void navigateTo(String requestedDataId) { |
|
|
|
// refresh table |
|
|
|
BeanContainer<Long, Topping> container = new BeanContainer<Long, Topping>( |
|
|
|
Topping.class); |
|
|
|
container.setBeanIdProperty("id"); |
|
|
|
for (Topping entity : toppings.values()) { |
|
|
|
container.addBean(entity); |
|
|
|
} |
|
|
|
table.setContainerDataSource(container); |
|
|
|
|
|
|
|
if ("new".equals(requestedDataId)) { |
|
|
|
table.setValue(null); |
|
|
|
form.setVisible(true); |
|
|
|
setCurrentEntity(new Topping()); |
|
|
|
form.focus(); |
|
|
|
} else if (requestedDataId != null |
|
|
|
&& requestedDataId.startsWith("edit/")) { |
|
|
|
try { |
|
|
|
Long id = Long.valueOf(requestedDataId.substring(5)); |
|
|
|
setCurrentEntity(getEntityForItem(table.getItem(id))); |
|
|
|
form.focus(); |
|
|
|
} catch (NumberFormatException e) { |
|
|
|
setCurrentEntity(null); |
|
|
|
} |
|
|
|
} else { |
|
|
|
setCurrentEntity(null); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
protected void setCurrentEntity(Topping entity) { |
|
|
|
form.setVisible(entity != null); |
|
|
|
if (entity != null) { |
|
|
|
Item item = table.getItem(entity.getId()); |
|
|
|
if (item == null) { |
|
|
|
item = new BeanItem<Topping>(entity); |
|
|
|
} |
|
|
|
form.setItemDataSource(item, Collections.singleton("name")); |
|
|
|
} else { |
|
|
|
form.setItemDataSource(null); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public Topping getEntityForItem(Item item) { |
|
|
|
if (item != null) { |
|
|
|
return ((BeanItem<Topping>) item).getBean(); |
|
|
|
} else { |
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
protected void setup() { |
|
|
|
TestToppingsView testToppingsView = new TestToppingsView(); |
|
|
|
addComponent(testToppingsView); |
|
|
|
getLayout().setSizeFull(); |
|
|
|
getLayout().setExpandRatio(testToppingsView, 1); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
protected String getDescription() { |
|
|
|
return "Open the toppings view and create a new topping \"ham\".\n" |
|
|
|
+ "Select the topping in the table to edit it (might require two clicks due to #6833).\n" |
|
|
|
+ "Edit the name to \"ahm\" and press ENTER without first moving the focus out of the field. This triggers the Save button.\n" |
|
|
|
+ "Deselect the row in the table (needed because of #6833).\n" |
|
|
|
+ "An out of sync error is displayed, as there is a variable change for the text field that is no longer on the screen."; |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
protected Integer getTicketNumber() { |
|
|
|
return 6834; |
|
|
|
} |
|
|
|
} |