Skip to content

Commit 58ab766

Browse files
authored
fix: ensure row details stay open after refreshing item (#7374)
1 parent 571e1eb commit 58ab766

File tree

3 files changed

+85
-79
lines changed

3 files changed

+85
-79
lines changed

vaadin-grid-flow-parent/vaadin-grid-flow-integration-tests/src/main/java/com/vaadin/flow/component/grid/it/GridDetailsRowPage.java

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -22,52 +22,57 @@
2222
import com.vaadin.flow.component.grid.Grid;
2323
import com.vaadin.flow.component.grid.Grid.SelectionMode;
2424
import com.vaadin.flow.component.html.Div;
25-
import com.vaadin.flow.data.bean.Person;
25+
import com.vaadin.flow.component.html.NativeButton;
2626
import com.vaadin.flow.data.provider.ListDataProvider;
2727
import com.vaadin.flow.data.renderer.ComponentRenderer;
2828
import com.vaadin.flow.router.Route;
2929

3030
@Route("vaadin-grid/grid-details-row")
3131
public class GridDetailsRowPage extends Div {
3232

33-
private Grid<Person> grid = new Grid<>();
3433
private List<Person> items = new ArrayList<>();
34+
private Grid<Person> grid = new Grid<>();
3535

36-
private int nbUpdates;
37-
private Person person3 = new Person("Person 3", 2);
38-
private Person person4 = new Person("Person 4", 1111);
36+
private static record Person(int id, String name) {
37+
}
3938

4039
public GridDetailsRowPage() {
40+
items.add(new Person(0, "Person 0"));
41+
items.add(new Person(1, "Person 1"));
42+
items.add(new Person(2, "Person 2"));
4143

42-
items.add(new Person("Person 1", 99));
43-
items.add(new Person("Person 2", 1));
44-
items.add(person3);
45-
items.add(person4);
46-
47-
ListDataProvider<Person> ldp = new ListDataProvider<>(items);
48-
grid.setItems(ldp);
44+
ListDataProvider<Person> dataProvider = new ListDataProvider<>(items) {
45+
@Override
46+
public Object getId(Person person) {
47+
return person.id();
48+
};
49+
};
50+
grid.setItems(dataProvider);
4951
grid.setSelectionMode(SelectionMode.NONE);
50-
grid.addColumn(Person::getFirstName).setHeader("name");
51-
grid.setItemDetailsRenderer(new ComponentRenderer<>(
52-
item -> new Button(item.getFirstName())));
52+
grid.addColumn(person -> person.name()).setHeader("Name");
53+
grid.setItemDetailsRenderer(
54+
new ComponentRenderer<>(person -> new Button(person.name())));
5355

54-
add(grid, new Button("click to open details",
55-
e -> setFirstAndSecondItemsVisible()));
56-
Button updatePerson3 = new Button("update and refresh person 3", e -> {
57-
nbUpdates++;
58-
person3.setFirstName("Person 3 - updates " + nbUpdates);
59-
grid.getDataProvider().refreshItem(person3);
56+
NativeButton updatePerson2 = new NativeButton("Update person 2", e -> {
57+
Person updatedPerson = new Person(2, "Updated Person 2");
58+
items.set(2, updatedPerson);
59+
grid.getDataProvider().refreshItem(updatedPerson);
6060
});
61-
updatePerson3.setId("update-button");
62-
add(updatePerson3);
61+
updatePerson2.setId("update-person-2");
6362

64-
Button removeButton = new Button("remove person 4", e -> {
65-
items.remove(person4);
63+
NativeButton removePerson2 = new NativeButton("Remove person 2", e -> {
64+
items.remove(2);
6665
grid.getDataProvider().refreshAll();
6766
});
68-
removeButton.setId("remove-button");
69-
add(removeButton);
67+
removePerson2.setId("remove-person-2");
68+
7069
setFirstAndSecondItemsVisible();
70+
71+
NativeButton openDetails = new NativeButton("Open details", e -> {
72+
setFirstAndSecondItemsVisible();
73+
});
74+
75+
add(grid, openDetails, updatePerson2, removePerson2);
7176
}
7277

7378
public void setFirstAndSecondItemsVisible() {

vaadin-grid-flow-parent/vaadin-grid-flow-integration-tests/src/test/java/com/vaadin/flow/component/grid/it/GridDetailsRowIT.java

Lines changed: 36 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -18,82 +18,74 @@
1818
import java.util.List;
1919

2020
import org.junit.Assert;
21+
import org.junit.Before;
2122
import org.junit.Test;
2223
import org.openqa.selenium.By;
2324
import org.openqa.selenium.WebElement;
2425

2526
import com.vaadin.flow.component.grid.testbench.GridElement;
27+
import com.vaadin.flow.component.grid.testbench.GridTHTDElement;
2628
import com.vaadin.flow.testutil.TestPath;
2729
import com.vaadin.tests.AbstractComponentIT;
2830

2931
@TestPath("vaadin-grid/grid-details-row")
3032
public class GridDetailsRowIT extends AbstractComponentIT {
33+
private GridElement grid;
3134

32-
@Test
33-
public void gridTwoItemsSelectedWhenOpen() {
35+
@Before
36+
public void init() {
3437
open();
35-
GridElement grid = $(GridElement.class).first();
36-
37-
// each detail contain a button
38-
List<WebElement> detailsElements = getDetailsElements(grid);
38+
grid = $(GridElement.class).first();
39+
}
3940

41+
@Test
42+
public void initiallyOpenedDetailsDisplayed() {
43+
List<WebElement> detailsElements = getDetailsElements();
4044
Assert.assertEquals(2, detailsElements.size());
41-
42-
Assert.assertEquals("Person 1", detailsElements.get(0).getText());
43-
Assert.assertEquals("Person 2", detailsElements.get(1).getText());
45+
Assert.assertEquals("Person 0", detailsElements.get(0).getText());
46+
Assert.assertEquals("Person 1", detailsElements.get(1).getText());
4447
}
4548

4649
@Test
47-
public void shouldNotThrowOnDetailsClick() {
48-
open();
49-
GridElement grid = $(GridElement.class).first();
50+
public void clickDetails_doesNotThrow() {
5051
grid.getRow(1).getDetails().click(0, 0);
5152
checkLogsForErrors();
5253
}
5354

54-
/**
55-
* Click on an item, hide the other details
56-
*/
5755
@Test
58-
public void gridSelectItem4DisplayDetails() {
59-
open();
60-
GridElement grid = $(GridElement.class).first();
61-
// select row 4
62-
grid.getCell(3, 0).click();
63-
waitUntil(e -> getDetailsElements(grid).size() == 1, 1);
64-
65-
// detail on row 3 has the correct text
66-
Assert.assertEquals("Person 4",
67-
getDetailsElements(grid).get(0).getText());
56+
public void selectItem_onlyItsDetailsAreDisplayed() {
57+
grid.getCell(2, 0).click();
58+
59+
List<WebElement> detailsElements = getDetailsElements();
60+
Assert.assertEquals(1, detailsElements.size());
61+
Assert.assertEquals("Person 2", detailsElements.get(0).getText());
6862
}
6963

70-
/**
71-
* If the details of an item is opened and the item updated then the detail
72-
* should be updated
73-
*/
7464
@Test
75-
public void gridUpdateItemUpdateDetails() {
76-
open();
77-
GridElement grid = $(GridElement.class).first();
78-
// select row 3
65+
public void updateItem_detailsUpdated() {
7966
grid.getCell(2, 0).click();
80-
waitUntil(e -> getDetailsElements(grid).size() == 1, 1);
8167

82-
// detail on row 3 has the correct text
83-
Assert.assertEquals("Person 3",
84-
getDetailsElements(grid).get(0).getText());
68+
GridTHTDElement details = grid.getRow(2).getDetails();
8569

86-
WebElement updateButton = findElement(By.id("update-button"));
87-
updateButton.click();
88-
waitUntil(e -> getDetailsElements(grid).size() == 1, 1);
70+
Assert.assertFalse(details.hasAttribute("hidden"));
71+
Assert.assertEquals("Person 2", details.getText());
8972

90-
// detail on row 3 has the correct text
91-
Assert.assertEquals("Person 3 - updates 1",
92-
getDetailsElements(grid).get(0).getText());
73+
findElement(By.id("update-person-2")).click();
74+
75+
Assert.assertFalse(details.hasAttribute("hidden"));
76+
Assert.assertEquals("Updated Person 2", details.getText());
77+
}
78+
79+
@Test
80+
public void removeItem_detailsRemoved() {
81+
grid.getCell(2, 0).click();
82+
findElement(By.id("remove-person-2")).click();
9383

84+
GridTHTDElement details = grid.getRow(1).getDetails();
85+
Assert.assertTrue(details.hasAttribute("hidden"));
9486
}
9587

96-
private List<WebElement> getDetailsElements(GridElement grid) {
88+
private List<WebElement> getDetailsElements() {
9789
return grid.findElements(By.tagName("vaadin-button"));
9890
}
9991
}

vaadin-grid-flow-parent/vaadin-grid-flow/src/main/java/com/vaadin/flow/component/grid/Grid.java

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1264,7 +1264,7 @@ protected Grid<T> getGrid() {
12641264
*/
12651265
private class DetailsManager extends AbstractGridExtension<T> {
12661266

1267-
private final HashSet<T> detailsVisible = new HashSet<>();
1267+
private final HashMap<Object, T> detailsVisible = new HashMap<>();
12681268

12691269
/**
12701270
* Constructs a new details manager for the given grid.
@@ -1286,17 +1286,19 @@ public DetailsManager(Grid<T> grid) {
12861286
* {@code false} if it should be hidden
12871287
*/
12881288
public void setDetailsVisible(T item, boolean visible) {
1289+
Object itemId = getItemId(item);
1290+
12891291
boolean refresh = false;
12901292
if (!visible) {
1291-
refresh = detailsVisible.remove(item);
1293+
refresh = detailsVisible.remove(itemId) != null;
12921294
} else {
1293-
detailsVisible.add(item);
1295+
detailsVisible.put(itemId, item);
12941296
refresh = true;
12951297
}
12961298

12971299
if (itemDetailsDataGenerator != null && refresh) {
12981300
refresh(item);
1299-
if (!detailsVisible.contains(item)) {
1301+
if (!detailsVisible.containsKey(itemId)) {
13001302
itemDetailsDataGenerator.destroyData(item);
13011303
}
13021304
}
@@ -1313,7 +1315,7 @@ public void setDetailsVisible(T item, boolean visible) {
13131315
*/
13141316
public boolean isDetailsVisible(T item) {
13151317
return itemDetailsDataGenerator != null
1316-
&& detailsVisible.contains(item);
1318+
&& detailsVisible.containsKey(getItemId(item));
13171319
}
13181320

13191321
@Override
@@ -1332,7 +1334,7 @@ public void generateData(T item, JsonObject jsonObject) {
13321334
*/
13331335
@Override
13341336
public void destroyData(T item) {
1335-
detailsVisible.remove(item);
1337+
detailsVisible.remove(getItemId(item));
13361338
if (itemDetailsDataGenerator != null) {
13371339
itemDetailsDataGenerator.destroyData(item);
13381340
}
@@ -1361,17 +1363,24 @@ public void refreshData(T item) {
13611363

13621364
private void setDetailsVisibleFromClient(Set<T> items) {
13631365
Set<T> toRefresh = new HashSet<>();
1364-
toRefresh.addAll(detailsVisible);
1366+
toRefresh.addAll(detailsVisible.values());
13651367
toRefresh.addAll(items);
13661368

13671369
detailsVisible.clear();
1368-
detailsVisible.addAll(items);
1370+
for (T item : items) {
1371+
detailsVisible.put(getItemId(item), item);
1372+
}
1373+
13691374
if (itemDetailsDataGenerator != null) {
13701375
for (T item : toRefresh) {
13711376
refresh(item);
13721377
}
13731378
}
13741379
}
1380+
1381+
private Object getItemId(T item) {
1382+
return getDataProvider().getId(item);
1383+
}
13751384
}
13761385

13771386
private class GridArrayUpdaterImpl implements GridArrayUpdater {

0 commit comments

Comments
 (0)