Skip to content

Commit b2e88a6

Browse files
authored
fix: close a notification on detach (vaadin#3154)
1 parent 5f95ad2 commit b2e88a6

File tree

3 files changed

+109
-18
lines changed
  • vaadin-notification-flow-parent

3 files changed

+109
-18
lines changed

vaadin-notification-flow-parent/vaadin-notification-flow-integration-tests/src/main/java/com/vaadin/flow/component/notification/tests/NotificationView.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ private void createNotificationUsingStaticConvenienceMethod() {
7676
Notification notification = Notification.show(
7777
"This is a notification created with static convenience method");
7878
notification.setId("static-notification");
79-
addCard("Notification using static convenience method", notification);
79+
addCard("Notification using static convenience method");
8080
}
8181

8282
private void createNotificationWithComponents() {

vaadin-notification-flow-parent/vaadin-notification-flow/src/main/java/com/vaadin/flow/component/notification/Notification.java

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -202,13 +202,25 @@ private void initBaseElementsAndListeners() {
202202
getElement().appendChild(templateElement);
203203
getElement().appendVirtualChild(container);
204204

205-
getElement().addEventListener("opened-changed", event -> {
206-
if (autoAddedToTheUi && !isOpened()) {
207-
getElement().removeFromParent();
208-
autoAddedToTheUi = false;
209-
}
205+
getElement().addEventListener("opened-changed",
206+
event -> removeAutoAdded());
207+
208+
addDetachListener(event -> {
209+
// If the notification gets detached, it needs to be marked
210+
// as closed so it won't auto-open when reattached.
211+
setOpened(false);
212+
removeAutoAdded();
210213
});
214+
}
211215

216+
/**
217+
* Removes the notification from its parent if it was added automatically.
218+
*/
219+
private void removeAutoAdded() {
220+
if (autoAddedToTheUi && !isOpened()) {
221+
autoAddedToTheUi = false;
222+
getElement().removeFromParent();
223+
}
212224
}
213225

214226
/**
@@ -433,12 +445,14 @@ public void setOpened(boolean opened) {
433445
+ "That may happen if you call the method from the custom thread without "
434446
+ "'UI::access' or from tests without proper initialization.");
435447
}
436-
if (opened && getElement().getNode().getParent() == null) {
437-
ui.beforeClientResponse(ui, context -> {
448+
449+
ui.beforeClientResponse(ui, context -> {
450+
if (isOpened() && getElement().getNode().getParent() == null) {
438451
ui.addToModalComponent(this);
439452
autoAddedToTheUi = true;
440-
});
441-
}
453+
}
454+
});
455+
442456
super.setOpened(opened);
443457
}
444458

vaadin-notification-flow-parent/vaadin-notification-flow/src/test/java/com/vaadin/flow/component/notification/NotificationTest.java

Lines changed: 85 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -185,10 +185,7 @@ public void setText_notificationHasAddedComponents_innerHtmlIsTextValue() {
185185

186186
notification.open();
187187

188-
UIInternals internals = ui.getInternals();
189-
VaadinSession session = Mockito.mock(VaadinSession.class);
190-
internals.setSession(session);
191-
internals.getStateTree().runExecutionsBeforeClientResponse();
188+
flushBeforeClientResponse();
192189

193190
Element templateElement = notification.getElement().getChildren()
194191
.findFirst().get();
@@ -206,10 +203,7 @@ public void add_notificationHasText_innerHtmlIsTemplateValue() {
206203

207204
notification.open();
208205

209-
UIInternals internals = ui.getInternals();
210-
VaadinSession session = Mockito.mock(VaadinSession.class);
211-
internals.setSession(session);
212-
internals.getStateTree().runExecutionsBeforeClientResponse();
206+
flushBeforeClientResponse();
213207

214208
Element templateElement = notification.getElement().getChildren()
215209
.findFirst().get();
@@ -241,4 +235,87 @@ public void hasStyle() {
241235
Notification notification = new Notification();
242236
Assert.assertTrue(notification instanceof HasStyle);
243237
}
238+
239+
@Test
240+
public void createNotificationh_closeOnParentDetach() {
241+
// Create a Notification manually and add it to a parent container
242+
Notification notification = new Notification();
243+
notification.open();
244+
Div parent = new Div(notification);
245+
// Add the parent to the UI
246+
ui.add(parent);
247+
248+
// The notification was opened manually. Check that it's still open.
249+
Assert.assertTrue(notification.isOpened());
250+
251+
// Remove the parent container from the UI
252+
ui.remove(parent);
253+
254+
// The notification should have been closed on detach, even if it was
255+
// the parent that was removed
256+
Assert.assertFalse(notification.isOpened());
257+
// The parent reference should not have changed
258+
Assert.assertEquals(notification.getParent().get(), parent);
259+
}
260+
261+
@Test
262+
public void showNotification_closeAndDetachOnParentDetach() {
263+
// Create a modal parent container and add it to the UI
264+
Div parent = new Div();
265+
ui.add(parent);
266+
ui.setChildComponentModal(parent, true);
267+
268+
// Use Notification.show() helper to create a notification.
269+
// It will be automatically added to the modal parent container (before
270+
// client response)
271+
Notification notification = Notification.show("foo");
272+
flushBeforeClientResponse();
273+
274+
// Check that the notification is opened and attached to the parent
275+
// container
276+
Assert.assertTrue(notification.isOpened());
277+
Assert.assertTrue(parent.getChildren().collect(Collectors.toList())
278+
.contains(notification));
279+
280+
// Remove the modal parent container from the UI
281+
ui.remove(parent);
282+
283+
// The notification should have been closed on detach, even if it was
284+
// the parent that was removed
285+
Assert.assertFalse(notification.isOpened());
286+
// The notification should have been automatically removed from the
287+
// parent container
288+
Assert.assertFalse(parent.getChildren().collect(Collectors.toList())
289+
.contains(notification));
290+
}
291+
292+
@Test
293+
public void showNotification_addManually_dontDetachOnParentDetach() {
294+
// Use Notification.show() helper to create a notification.
295+
Notification notification = Notification.show("foo");
296+
297+
// Manually add the notification to a parent
298+
Div parent = new Div(notification);
299+
ui.add(parent);
300+
// Flush
301+
flushBeforeClientResponse();
302+
303+
// Check that the notification is attached to the parent container
304+
Assert.assertEquals(notification.getParent().get(), parent);
305+
306+
// Remove the modal parent container from the UI
307+
ui.remove(parent);
308+
309+
// Even though the notification was created using Notification.show(),
310+
// it got was manually added to the parent container so it should not
311+
// have been automatically removed from it.
312+
Assert.assertEquals(notification.getParent().get(), parent);
313+
}
314+
315+
private void flushBeforeClientResponse() {
316+
UIInternals internals = ui.getInternals();
317+
VaadinSession session = Mockito.mock(VaadinSession.class);
318+
internals.setSession(session);
319+
internals.getStateTree().runExecutionsBeforeClientResponse();
320+
}
244321
}

0 commit comments

Comments
 (0)