diff --git a/vaadin-dashboard-flow-parent/vaadin-dashboard-flow-integration-tests/src/test/java/com/vaadin/flow/component/dashboard/tests/DashboardDragReorderIT.java b/vaadin-dashboard-flow-parent/vaadin-dashboard-flow-integration-tests/src/test/java/com/vaadin/flow/component/dashboard/tests/DashboardDragReorderIT.java index fb5cdc82a6b..ac316882bb5 100644 --- a/vaadin-dashboard-flow-parent/vaadin-dashboard-flow-integration-tests/src/test/java/com/vaadin/flow/component/dashboard/tests/DashboardDragReorderIT.java +++ b/vaadin-dashboard-flow-parent/vaadin-dashboard-flow-integration-tests/src/test/java/com/vaadin/flow/component/dashboard/tests/DashboardDragReorderIT.java @@ -36,7 +36,7 @@ public void init() { public void reorderWidgetOnClientSide_itemsAreReorderedCorrectly() { var draggedWidget = dashboardElement.getWidgets().get(0); var targetWidget = dashboardElement.getWidgets().get(1); - dragResizeElement(draggedWidget, targetWidget); + dragReorderElement(draggedWidget, targetWidget); Assert.assertEquals(draggedWidget.getTitle(), dashboardElement.getWidgets().get(1).getTitle()); } @@ -45,7 +45,7 @@ public void reorderWidgetOnClientSide_itemsAreReorderedCorrectly() { public void reorderSectionOnClientSide_itemsAreReorderedCorrectly() { var draggedSection = dashboardElement.getSections().get(1); var targetWidget = dashboardElement.getWidgets().get(0); - dragResizeElement(draggedSection, targetWidget); + dragReorderElement(draggedSection, targetWidget); Assert.assertEquals(draggedSection.getTitle(), dashboardElement.getSections().get(0).getTitle()); } @@ -55,7 +55,7 @@ public void reorderWidgetInSectionOnClientSide_itemsAreReorderedCorrectly() { var firstSection = dashboardElement.getSections().get(0); var draggedWidget = firstSection.getWidgets().get(0); var targetWidget = firstSection.getWidgets().get(1); - dragResizeElement(draggedWidget, targetWidget); + dragReorderElement(draggedWidget, targetWidget); firstSection = dashboardElement.getSections().get(0); Assert.assertEquals(draggedWidget.getTitle(), firstSection.getWidgets().get(1).getTitle()); @@ -69,7 +69,7 @@ public void detachReattach_reorderWidgetOnClientSide_itemsAreReorderedCorrectly( reorderWidgetOnClientSide_itemsAreReorderedCorrectly(); } - private void dragResizeElement(TestBenchElement draggedElement, + private void dragReorderElement(TestBenchElement draggedElement, TestBenchElement targetElement) { var dragHandle = getDragHandle(draggedElement); @@ -83,10 +83,6 @@ private void dragResizeElement(TestBenchElement draggedElement, .release(targetElement).build().perform(); } - private static boolean isDragHandleVisible(TestBenchElement element) { - return !"none".equals(getDragHandle(element).getCssValue("display")); - } - private static TestBenchElement getDragHandle(TestBenchElement element) { return element.$("*").withClassName("drag-handle").first(); } diff --git a/vaadin-dashboard-flow-parent/vaadin-dashboard-flow-integration-tests/src/test/java/com/vaadin/flow/component/dashboard/tests/DashboardDragResizeIT.java b/vaadin-dashboard-flow-parent/vaadin-dashboard-flow-integration-tests/src/test/java/com/vaadin/flow/component/dashboard/tests/DashboardDragResizeIT.java index 371b641d566..32698305e27 100644 --- a/vaadin-dashboard-flow-parent/vaadin-dashboard-flow-integration-tests/src/test/java/com/vaadin/flow/component/dashboard/tests/DashboardDragResizeIT.java +++ b/vaadin-dashboard-flow-parent/vaadin-dashboard-flow-integration-tests/src/test/java/com/vaadin/flow/component/dashboard/tests/DashboardDragResizeIT.java @@ -81,12 +81,6 @@ private void resizeWidget(int widgetIndexToResize, double xResizeRatio, .release().build().perform(); } - private boolean isResizeHandleVisible( - DashboardWidgetElement widgetElement) { - return !"none" - .equals(getResizeHandle(widgetElement).getCssValue("display")); - } - private static TestBenchElement getResizeHandle( DashboardWidgetElement widgetElement) { return widgetElement.$("*").withClassName("resize-handle").first(); diff --git a/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/main/java/com/vaadin/flow/component/dashboard/Dashboard.java b/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/main/java/com/vaadin/flow/component/dashboard/Dashboard.java index fbb51ed726e..d13a30bc5bd 100644 --- a/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/main/java/com/vaadin/flow/component/dashboard/Dashboard.java +++ b/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/main/java/com/vaadin/flow/component/dashboard/Dashboard.java @@ -27,6 +27,8 @@ import com.vaadin.flow.component.Tag; import com.vaadin.flow.component.dependency.JsModule; import com.vaadin.flow.component.dependency.NpmPackage; +import com.vaadin.flow.dom.DomEvent; +import com.vaadin.flow.dom.DomListenerRegistration; import com.vaadin.flow.dom.Element; import com.vaadin.flow.shared.Registration; @@ -55,9 +57,9 @@ public class Dashboard extends Component implements HasWidgets, HasSize { */ public Dashboard() { childDetachHandler = getChildDetachHandler(); - addItemReorderEndListener(this::onItemReorderEnd); - addItemResizeEndListener(this::onItemResizeEnd); - addItemRemovedListener(this::onItemRemoved); + initItemMovedClientEventListener(); + initItemResizedClientEventListener(); + initItemRemovedClientEventListener(); } /** @@ -305,51 +307,27 @@ public boolean isEditable() { } /** - * Adds an item reorder start listener to this dashboard. + * Adds an item moved listener to this dashboard. * * @param listener * the listener to add, not null * @return a handle that can be used for removing the listener */ - public Registration addItemReorderStartListener( - ComponentEventListener listener) { - return addListener(DashboardItemReorderStartEvent.class, listener); + public Registration addItemMovedListener( + ComponentEventListener listener) { + return addListener(DashboardItemMovedEvent.class, listener); } /** - * Adds an item reorder end listener to this dashboard. + * Adds an item resized listener to this dashboard. * * @param listener * the listener to add, not null * @return a handle that can be used for removing the listener */ - public Registration addItemReorderEndListener( - ComponentEventListener listener) { - return addListener(DashboardItemReorderEndEvent.class, listener); - } - - /** - * Adds an item resize start listener to this dashboard. - * - * @param listener - * the listener to add, not null - * @return a handle that can be used for removing the listener - */ - public Registration addItemResizeStartListener( - ComponentEventListener listener) { - return addListener(DashboardItemResizeStartEvent.class, listener); - } - - /** - * Adds an item resize end listener to this dashboard. - * - * @param listener - * the listener to add, not null - * @return a handle that can be used for removing the listener - */ - public Registration addItemResizeEndListener( - ComponentEventListener listener) { - return addListener(DashboardItemResizeEndEvent.class, listener); + public Registration addItemResizedListener( + ComponentEventListener listener) { + return addListener(DashboardItemResizedEvent.class, listener); } /** @@ -389,7 +367,7 @@ protected void onAttach(AttachEvent attachEvent) { super.onAttach(attachEvent); getElement().executeJs( "Vaadin.FlowComponentHost.patchVirtualContainer(this);"); - customizeItemReorderEndEvent(); + customizeItemMovedEvent(); doUpdateClient(); } @@ -493,95 +471,166 @@ void removeChild(Component child) { }; } - private void onItemReorderEnd( - DashboardItemReorderEndEvent dashboardItemReorderEndEvent) { - if (!isEditable()) { - return; - } - JsonArray orderedItemsFromClient = dashboardItemReorderEndEvent - .getItems(); - reorderItems(orderedItemsFromClient); - updateClient(); - } - - private void onItemResizeEnd( - DashboardItemResizeEndEvent dashboardItemResizeEndEvent) { - if (!isEditable()) { - return; - } - DashboardWidget resizedWidget = dashboardItemResizeEndEvent - .getResizedWidget(); - resizedWidget.setRowspan(dashboardItemResizeEndEvent.getRowspan()); - resizedWidget.setColspan(dashboardItemResizeEndEvent.getColspan()); - } - - private void onItemRemoved( - DashboardItemRemovedEvent dashboardItemRemovedEvent) { - if (!isEditable()) { - return; - } - dashboardItemRemovedEvent.getRemovedItem().removeFromParent(); - } - - private void reorderItems(JsonArray orderedItemsFromClient) { - // Keep references to the root level children before clearing them - Map nodeIdToComponent = childrenComponents.stream() - .collect(Collectors.toMap( - component -> component.getElement().getNode().getId(), - Function.identity())); - // Remove all children and add them back using the node IDs from client - // items - childrenComponents.clear(); - for (int rootLevelItemIdx = 0; rootLevelItemIdx < orderedItemsFromClient - .length(); rootLevelItemIdx++) { - JsonObject rootLevelItemFromClient = orderedItemsFromClient - .getObject(rootLevelItemIdx); - int rootLevelItemNodeId = (int) rootLevelItemFromClient - .getNumber("nodeid"); - Component componentMatch = nodeIdToComponent - .get(rootLevelItemNodeId); - childrenComponents.add(componentMatch); - // Reorder the widgets in sections separately - if (componentMatch instanceof DashboardSection sectionMatch) { - reorderSectionWidgets(sectionMatch, rootLevelItemFromClient); + private void initItemMovedClientEventListener() { + String itemKey = "event.detail.item"; + String itemsKey = "event.detail.items"; + String sectionKey = "event.detail.section"; + getElement().addEventListener("dashboard-item-moved-flow", e -> { + if (!isEditable()) { + return; } + handleItemMovedClientEvent(e, itemKey, itemsKey, sectionKey); + updateClient(); + }).addEventData(itemKey).addEventData(itemsKey) + .addEventData(sectionKey); + } + + private void handleItemMovedClientEvent(DomEvent e, String itemKey, + String itemsKey, String sectionKey) { + int itemNodeId = (int) e.getEventData().getNumber(itemKey); + JsonArray itemsNodeIds = e.getEventData().getArray(itemsKey); + Integer sectionNodeId = e.getEventData().hasKey(sectionKey) + ? (int) e.getEventData().getNumber(sectionKey) + : null; + DashboardSection section = null; + List reorderedItems; + if (sectionNodeId == null) { + reorderedItems = getReorderedItemsList(itemsNodeIds, this); + childrenComponents.clear(); + childrenComponents.addAll(reorderedItems); + } else { + section = getChildren() + .filter(child -> sectionNodeId + .equals(child.getElement().getNode().getId())) + .map(DashboardSection.class::cast).findAny().orElseThrow(); + reorderedItems = getReorderedItemsList( + getSectionItems(itemsNodeIds, sectionNodeId), section); + section.removeAll(); + reorderedItems.stream().map(DashboardWidget.class::cast) + .forEach(section::add); } + Component movedItem = reorderedItems.stream().filter( + item -> itemNodeId == item.getElement().getNode().getId()) + .findAny().orElseThrow(); + fireEvent(new DashboardItemMovedEvent(this, true, movedItem, + getChildren().toList(), section)); + } + + private void initItemResizedClientEventListener() { + String nodeIdKey = "event.detail.item.nodeid"; + String colspanKey = "event.detail.item.colspan"; + String rowspanKey = "event.detail.item.rowspan"; + getElement().addEventListener("dashboard-item-resized", e -> { + if (!isEditable()) { + return; + } + handleItemResizedClientEvent(e, nodeIdKey, colspanKey, rowspanKey); + updateClient(); + }).addEventData(nodeIdKey).addEventData(colspanKey) + .addEventData(rowspanKey); + } + + private void handleItemResizedClientEvent(DomEvent e, String nodeIdKey, + String colspanKey, String rowspanKey) { + int nodeId = (int) e.getEventData().getNumber(nodeIdKey); + int colspan = (int) e.getEventData().getNumber(colspanKey); + int rowspan = (int) e.getEventData().getNumber(rowspanKey); + DashboardWidget resizedWidget = getWidgets().stream() + .filter(child -> nodeId == child.getElement().getNode().getId()) + .findAny().orElseThrow(); + resizedWidget.setRowspan(rowspan); + resizedWidget.setColspan(colspan); + fireEvent(new DashboardItemResizedEvent(this, true, resizedWidget, + getChildren().toList())); + } + + private void initItemRemovedClientEventListener() { + String nodeIdKey = "event.detail.item.nodeid"; + DomListenerRegistration registration = getElement() + .addEventListener("dashboard-item-removed", e -> { + if (!isEditable()) { + return; + } + handleItemRemovedClientEvent(e, nodeIdKey); + updateClient(); + }); + registration.addEventData(nodeIdKey); + } + + private void handleItemRemovedClientEvent(DomEvent e, String nodeIdKey) { + int nodeId = (int) e.getEventData().getNumber(nodeIdKey); + Component removedItem = getRemovedItem(nodeId); + removedItem.removeFromParent(); + fireEvent(new DashboardItemRemovedEvent(this, true, removedItem, + getChildren().toList())); + } + + private Component getRemovedItem(int nodeId) { + return getChildren().map(item -> { + if (nodeId == item.getElement().getNode().getId()) { + return item; + } + if (item instanceof DashboardSection section) { + return section.getWidgets().stream() + .filter(sectionItem -> nodeId == sectionItem + .getElement().getNode().getId()) + .findAny().orElse(null); + } + return null; + }).filter(Objects::nonNull).findAny().orElseThrow(); } - private void reorderSectionWidgets(DashboardSection section, - JsonObject rootLevelItem) { - // Keep references to the widgets before clearing them - Map nodeIdToWidget = section.getWidgets() - .stream() - .collect(Collectors.toMap( - widget -> widget.getElement().getNode().getId(), - Function.identity())); - // Remove all widgets and add them back using the node IDs from client - // items - section.removeAll(); - JsonArray sectionWidgetsFromClient = rootLevelItem.getArray("items"); - for (int sectionWidgetIdx = 0; sectionWidgetIdx < sectionWidgetsFromClient - .length(); sectionWidgetIdx++) { - int sectionItemNodeId = (int) sectionWidgetsFromClient - .getObject(sectionWidgetIdx).getNumber("nodeid"); - section.add(nodeIdToWidget.get(sectionItemNodeId)); - } - } - - private void customizeItemReorderEndEvent() { + private void customizeItemMovedEvent() { getElement().executeJs( """ - this.addEventListener('dashboard-item-reorder-end', (e) => { + this.addEventListener('dashboard-item-moved', (e) => { function mapItems(items) { return items.map(({nodeid, items}) => ({ nodeid, ...(items && { items: mapItems(items) }) })); } - const flowReorderEvent = new CustomEvent('dashboard-item-reorder-end-flow', { - detail: { items: mapItems(this.items) } + const flowItemMovedEvent = new CustomEvent('dashboard-item-moved-flow', { + detail: { + item: e.detail.item.nodeid, + items: mapItems(e.detail.items), + section: e.detail.section?.nodeid + } }); - this.dispatchEvent(flowReorderEvent); + this.dispatchEvent(flowItemMovedEvent); });"""); } + + private static List getReorderedItemsList( + JsonArray reorderedItemsFromClient, + Component reorderedItemsParent) { + Objects.requireNonNull(reorderedItemsFromClient); + Map nodeIdToItems = reorderedItemsParent + .getChildren() + .collect(Collectors.toMap( + item -> item.getElement().getNode().getId(), + Function.identity())); + List items = new ArrayList<>(); + for (int index = 0; index < reorderedItemsFromClient + .length(); index++) { + int nodeIdFromClient = (int) ((JsonObject) reorderedItemsFromClient + .get(index)).getNumber("nodeid"); + items.add(nodeIdToItems.get(nodeIdFromClient)); + } + return items; + } + + private static JsonArray getSectionItems(JsonArray items, + int sectionNodeId) { + for (int rootLevelIdx = 0; rootLevelIdx < items + .length(); rootLevelIdx++) { + JsonObject item = items.get(rootLevelIdx); + int itemNodeId = (int) item.getNumber("nodeid"); + if (sectionNodeId == itemNodeId) { + JsonObject sectionObj = items.get(rootLevelIdx); + return sectionObj.getArray("items"); + } + } + return null; + } } diff --git a/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/main/java/com/vaadin/flow/component/dashboard/DashboardItemMovedEvent.java b/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/main/java/com/vaadin/flow/component/dashboard/DashboardItemMovedEvent.java new file mode 100644 index 00000000000..4bc081bffea --- /dev/null +++ b/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/main/java/com/vaadin/flow/component/dashboard/DashboardItemMovedEvent.java @@ -0,0 +1,84 @@ +/** + * Copyright 2000-2024 Vaadin Ltd. + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See {@literal } for the full + * license. + */ +package com.vaadin.flow.component.dashboard; + +import java.util.List; +import java.util.Optional; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.ComponentEvent; +import com.vaadin.flow.component.ComponentEventListener; + +/** + * Widget or section moved event of {@link Dashboard}. + * + * @author Vaadin Ltd. + * @see Dashboard#addItemMovedListener(ComponentEventListener) + */ +public class DashboardItemMovedEvent extends ComponentEvent { + + private final Component item; + + private final List items; + + private final DashboardSection section; + + /** + * Creates a dashboard item moved event. + * + * @param source + * Dashboard that contains the item that was moved + * @param fromClient + * {@code true} if the event originated from the client side, + * {@code false} otherwise + * @param item + * The moved item + * @param items + * The root level items of the dashboard + * @param section + * The section that contains the moved item, {@code null} if the + * item is a direct child of the dashboard + */ + public DashboardItemMovedEvent(Dashboard source, boolean fromClient, + Component item, List items, DashboardSection section) { + super(source, fromClient); + this.item = item; + this.items = items; + this.section = section; + } + + /** + * Returns the moved item + * + * @return the moved item + */ + public Component getItem() { + return item; + } + + /** + * Returns the root level items of the dashboard + * + * @return the root level items of the dashboard + */ + public List getItems() { + return items; + } + + /** + * Returns the section that contains the moved item, or an empty optional if + * the item is a direct child of the dashboard + * + * @return the section that contains the moved item, or an empty optional if + * the item is a direct child of the dashboard + */ + public Optional getSection() { + return Optional.ofNullable(section); + } +} diff --git a/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/main/java/com/vaadin/flow/component/dashboard/DashboardItemRemovedEvent.java b/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/main/java/com/vaadin/flow/component/dashboard/DashboardItemRemovedEvent.java index 8702a08ecfe..e270d45554b 100644 --- a/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/main/java/com/vaadin/flow/component/dashboard/DashboardItemRemovedEvent.java +++ b/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/main/java/com/vaadin/flow/component/dashboard/DashboardItemRemovedEvent.java @@ -8,13 +8,11 @@ */ package com.vaadin.flow.component.dashboard; -import java.util.Objects; +import java.util.List; import com.vaadin.flow.component.Component; import com.vaadin.flow.component.ComponentEvent; import com.vaadin.flow.component.ComponentEventListener; -import com.vaadin.flow.component.DomEvent; -import com.vaadin.flow.component.EventData; /** * Widget or section removed event of {@link Dashboard}. @@ -22,10 +20,11 @@ * @author Vaadin Ltd. * @see Dashboard#addItemRemovedListener(ComponentEventListener) */ -@DomEvent("dashboard-item-removed") public class DashboardItemRemovedEvent extends ComponentEvent { - private final Component removedItem; + private final Component item; + + private final List items; /** * Creates a dashboard item removed event. @@ -33,13 +32,18 @@ public class DashboardItemRemovedEvent extends ComponentEvent { * @param source * Dashboard that contains the item that was removed * @param fromClient - * true if the event originated from the client - * side, false otherwise + * {@code true} if the event originated from the client side, + * {@code false} otherwise + * @param item + * The removed item + * @param items + * The root level items of the dashboard */ public DashboardItemRemovedEvent(Dashboard source, boolean fromClient, - @EventData("event.detail.item.nodeid") int nodeId) { + Component item, List items) { super(source, fromClient); - this.removedItem = getRemovedItem(source, nodeId); + this.item = item; + this.items = items; } /** @@ -47,22 +51,16 @@ public DashboardItemRemovedEvent(Dashboard source, boolean fromClient, * * @return the removed item */ - public Component getRemovedItem() { - return removedItem; + public Component getItem() { + return item; } - private static Component getRemovedItem(Dashboard dashboard, int nodeId) { - return dashboard.getChildren().map(item -> { - if (nodeId == item.getElement().getNode().getId()) { - return item; - } - if (item instanceof DashboardSection section) { - return section.getWidgets().stream() - .filter(sectionItem -> nodeId == sectionItem - .getElement().getNode().getId()) - .findAny().orElse(null); - } - return null; - }).filter(Objects::nonNull).findAny().orElse(null); + /** + * Returns the root level items of the dashboard + * + * @return the root level items of the dashboard + */ + public List getItems() { + return items; } } diff --git a/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/main/java/com/vaadin/flow/component/dashboard/DashboardItemReorderEndEvent.java b/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/main/java/com/vaadin/flow/component/dashboard/DashboardItemReorderEndEvent.java deleted file mode 100644 index 22ff4dbf79e..00000000000 --- a/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/main/java/com/vaadin/flow/component/dashboard/DashboardItemReorderEndEvent.java +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright 2000-2024 Vaadin Ltd. - * - * This program is available under Vaadin Commercial License and Service Terms. - * - * See {@literal } for the full - * license. - */ -package com.vaadin.flow.component.dashboard; - -import com.vaadin.flow.component.ComponentEvent; -import com.vaadin.flow.component.ComponentEventListener; -import com.vaadin.flow.component.DomEvent; -import com.vaadin.flow.component.EventData; - -import elemental.json.JsonArray; - -/** - * Widget or section reorder end event of {@link Dashboard}. - * - * @author Vaadin Ltd. - * @see Dashboard#addItemReorderEndListener(ComponentEventListener) - */ -@DomEvent("dashboard-item-reorder-end-flow") -public class DashboardItemReorderEndEvent extends ComponentEvent { - - private final JsonArray items; - - /** - * Creates a dashboard item reorder end event. - * - * @param source - * Dashboard that contains the item that was dragged - * @param fromClient - * true if the event originated from the client - * side, false otherwise - */ - public DashboardItemReorderEndEvent(Dashboard source, boolean fromClient, - @EventData("event.detail.items") JsonArray items) { - super(source, fromClient); - this.items = items; - } - - /** - * Returns the ordered items from the client side - * - * @return items the ordered items as a {@link JsonArray} - */ - public JsonArray getItems() { - return items; - } -} diff --git a/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/main/java/com/vaadin/flow/component/dashboard/DashboardItemReorderStartEvent.java b/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/main/java/com/vaadin/flow/component/dashboard/DashboardItemReorderStartEvent.java deleted file mode 100644 index d2910539cfb..00000000000 --- a/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/main/java/com/vaadin/flow/component/dashboard/DashboardItemReorderStartEvent.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright 2000-2024 Vaadin Ltd. - * - * This program is available under Vaadin Commercial License and Service Terms. - * - * See {@literal } for the full - * license. - */ -package com.vaadin.flow.component.dashboard; - -import com.vaadin.flow.component.ComponentEvent; -import com.vaadin.flow.component.ComponentEventListener; -import com.vaadin.flow.component.DomEvent; - -/** - * Widget or section reorder start event of {@link Dashboard}. - * - * @author Vaadin Ltd. - * @see Dashboard#addItemReorderStartListener(ComponentEventListener) - */ -@DomEvent("dashboard-item-reorder-start") -public class DashboardItemReorderStartEvent extends ComponentEvent { - - /** - * Creates a dashboard item reorder start event. - * - * @param source - * Dashboard that contains the item that was dragged - * @param fromClient - * true if the event originated from the client - * side, false otherwise - */ - public DashboardItemReorderStartEvent(Dashboard source, - boolean fromClient) { - super(source, fromClient); - } -} diff --git a/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/main/java/com/vaadin/flow/component/dashboard/DashboardItemResizeEndEvent.java b/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/main/java/com/vaadin/flow/component/dashboard/DashboardItemResizeEndEvent.java deleted file mode 100644 index 013ec38963c..00000000000 --- a/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/main/java/com/vaadin/flow/component/dashboard/DashboardItemResizeEndEvent.java +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Copyright 2000-2024 Vaadin Ltd. - * - * This program is available under Vaadin Commercial License and Service Terms. - * - * See {@literal } for the full - * license. - */ -package com.vaadin.flow.component.dashboard; - -import com.vaadin.flow.component.ComponentEvent; -import com.vaadin.flow.component.ComponentEventListener; -import com.vaadin.flow.component.DomEvent; -import com.vaadin.flow.component.EventData; - -/** - * Widget resize end event of {@link Dashboard}. - * - * @author Vaadin Ltd. - * @see Dashboard#addItemResizeEndListener(ComponentEventListener) - */ -@DomEvent("dashboard-item-resize-end") -public class DashboardItemResizeEndEvent extends ComponentEvent { - - private final DashboardWidget resizedWidget; - - private final int colspan; - - private final int rowspan; - - /** - * Creates a dashboard item resize end event. - * - * @param source - * Dashboard that contains the widget that was dragged - * @param fromClient - * true if the event originated from the client - * side, false otherwise - * @param nodeId - * Node ID the resized widget - * @param colspan - * New colspan of the resized widget - * @param rowspan - * New rowspan of the resized widget - */ - public DashboardItemResizeEndEvent(Dashboard source, boolean fromClient, - @EventData("event.detail.item.nodeid") int nodeId, - @EventData("event.detail.item.colspan") int colspan, - @EventData("event.detail.item.rowspan") int rowspan) { - super(source, fromClient); - this.resizedWidget = source.getWidgets().stream() - .filter(child -> nodeId == child.getElement().getNode().getId()) - .findAny().orElse(null); - this.colspan = colspan; - this.rowspan = rowspan; - } - - /** - * Returns the resized widget - * - * @return the resized widget - */ - public DashboardWidget getResizedWidget() { - return resizedWidget; - } - - /** - * Returns the new colspan of the resized item - * - * @return new colspan of the resized item - */ - public int getColspan() { - return colspan; - } - - /** - * Returns the new rowspan of the resized item - * - * @return new rowspan of the resized item - */ - public int getRowspan() { - return rowspan; - } -} diff --git a/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/main/java/com/vaadin/flow/component/dashboard/DashboardItemResizeStartEvent.java b/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/main/java/com/vaadin/flow/component/dashboard/DashboardItemResizeStartEvent.java deleted file mode 100644 index ce762351439..00000000000 --- a/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/main/java/com/vaadin/flow/component/dashboard/DashboardItemResizeStartEvent.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright 2000-2024 Vaadin Ltd. - * - * This program is available under Vaadin Commercial License and Service Terms. - * - * See {@literal } for the full - * license. - */ -package com.vaadin.flow.component.dashboard; - -import com.vaadin.flow.component.ComponentEvent; -import com.vaadin.flow.component.ComponentEventListener; -import com.vaadin.flow.component.DomEvent; -import com.vaadin.flow.component.EventData; - -/** - * Widget resize start event of {@link Dashboard}. - * - * @author Vaadin Ltd. - * @see Dashboard#addItemResizeStartListener(ComponentEventListener) - */ -@DomEvent("dashboard-item-resize-start") -public class DashboardItemResizeStartEvent extends ComponentEvent { - - private final DashboardWidget resizedWidget; - - /** - * Creates a dashboard item resize start event. - * - * @param source - * Dashboard that contains the widget that was dragged - * @param fromClient - * true if the event originated from the client - * side, false otherwise - * @param nodeId - * Node ID the resized widget - */ - public DashboardItemResizeStartEvent(Dashboard source, boolean fromClient, - @EventData("event.detail.item.nodeid") int nodeId) { - super(source, fromClient); - this.resizedWidget = source.getWidgets().stream() - .filter(child -> nodeId == child.getElement().getNode().getId()) - .findAny().orElse(null); - } - - /** - * Returns the resized widget - * - * @return the resized widget - */ - public DashboardWidget getResizedWidget() { - return resizedWidget; - } -} diff --git a/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/main/java/com/vaadin/flow/component/dashboard/DashboardItemResizedEvent.java b/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/main/java/com/vaadin/flow/component/dashboard/DashboardItemResizedEvent.java new file mode 100644 index 00000000000..ba34d7976e9 --- /dev/null +++ b/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/main/java/com/vaadin/flow/component/dashboard/DashboardItemResizedEvent.java @@ -0,0 +1,66 @@ +/** + * Copyright 2000-2024 Vaadin Ltd. + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See {@literal } for the full + * license. + */ +package com.vaadin.flow.component.dashboard; + +import java.util.List; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.ComponentEvent; +import com.vaadin.flow.component.ComponentEventListener; + +/** + * Widget resized event of {@link Dashboard}. + * + * @author Vaadin Ltd. + * @see Dashboard#addItemResizedListener(ComponentEventListener) + */ +public class DashboardItemResizedEvent extends ComponentEvent { + + private final DashboardWidget item; + + private final List items; + + /** + * Creates a dashboard item resized event. + * + * @param source + * Dashboard that contains the widget that was resized + * @param fromClient + * {@code true} if the event originated from the client side, + * {@code false} otherwise + * @param item + * The resized widget + * @param items + * The root level items of the dashboard + */ + public DashboardItemResizedEvent(Dashboard source, boolean fromClient, + DashboardWidget item, List items) { + super(source, fromClient); + this.item = item; + this.items = items; + } + + /** + * Returns the resized widget + * + * @return the resized widget + */ + public DashboardWidget getItem() { + return item; + } + + /** + * Returns the root level items of the dashboard + * + * @return the root level items of the dashboard + */ + public List getItems() { + return items; + } +} diff --git a/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/test/java/com/vaadin/flow/component/dashboard/tests/DashboardDragReorderTest.java b/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/test/java/com/vaadin/flow/component/dashboard/tests/DashboardDragReorderTest.java index bdddead6019..35f036a0641 100644 --- a/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/test/java/com/vaadin/flow/component/dashboard/tests/DashboardDragReorderTest.java +++ b/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/test/java/com/vaadin/flow/component/dashboard/tests/DashboardDragReorderTest.java @@ -10,17 +10,21 @@ import java.util.ArrayList; import java.util.List; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import com.vaadin.flow.component.ComponentUtil; +import com.vaadin.flow.component.Component; import com.vaadin.flow.component.dashboard.Dashboard; -import com.vaadin.flow.component.dashboard.DashboardItemReorderEndEvent; import com.vaadin.flow.component.dashboard.DashboardSection; import com.vaadin.flow.component.dashboard.DashboardWidget; +import com.vaadin.flow.dom.DomEvent; +import com.vaadin.flow.internal.nodefeature.ElementListenerMap; import elemental.json.Json; import elemental.json.JsonArray; @@ -50,6 +54,11 @@ public void reorderWidget_orderIsUpdated() { assertRootLevelItemReorder(0, 1); } + @Test + public void reorderWidgetToSamePosition_orderIsNotUpdated() { + assertRootLevelItemReorder(0, 0); + } + @Test public void reorderSection_orderIsUpdated() { assertRootLevelItemReorder(2, 1); @@ -60,18 +69,132 @@ public void reorderWidgetInSection_orderIsUpdated() { assertSectionWidgetReorder(2, 0, 1); } + @Test + public void reorderWidgetInSectionToSamePosition_orderIsNotUpdated() { + assertSectionWidgetReorder(2, 0, 0); + } + @Test public void setDashboardNotEditable_reorderWidget_orderIsNotUpdated() { dashboard.setEditable(false); + int movedWidgetNodeId = dashboard.getChildren().toList().get(0) + .getElement().getNode().getId(); List expectedRootLevelNodeIds = getRootLevelNodeIds(); reorderRootLevelItem(0, 1); - fireItemReorderEndEvent(); + fireItemMovedEvent(movedWidgetNodeId); Assert.assertEquals(expectedRootLevelNodeIds, getRootLevelNodeIds()); } - private void fireItemReorderEndEvent() { - ComponentUtil.fireEvent(dashboard, - new DashboardItemReorderEndEvent(dashboard, false, itemsArray)); + @Test + public void reorderWidget_eventCorrectlyFired() { + int initialIndex = 0; + int finalIndex = 1; + Component movedItem = dashboard.getChildren().toList() + .get(initialIndex); + int movedItemNodeId = movedItem.getElement().getNode().getId(); + List expectedItems = dashboard.getChildren() + .collect(Collectors.toCollection(ArrayList::new)); + expectedItems.add(finalIndex, expectedItems.remove(initialIndex)); + Runnable itemMoveAction = () -> { + reorderRootLevelItem(initialIndex, finalIndex); + fireItemMovedEvent(movedItemNodeId); + }; + assertEventCorrectlyFired(itemMoveAction, 1, movedItem, expectedItems, + null); + } + + @Test + public void reorderSection_eventCorrectlyFired() { + int initialIndex = 2; + int finalIndex = 1; + Component movedItem = dashboard.getChildren().toList() + .get(initialIndex); + int movedItemNodeId = movedItem.getElement().getNode().getId(); + List expectedItems = dashboard.getChildren() + .collect(Collectors.toCollection(ArrayList::new)); + expectedItems.add(finalIndex, expectedItems.remove(initialIndex)); + Runnable itemMoveAction = () -> { + reorderRootLevelItem(initialIndex, finalIndex); + fireItemMovedEvent(movedItemNodeId); + }; + assertEventCorrectlyFired(itemMoveAction, 1, movedItem, expectedItems, + null); + } + + @Test + public void reorderWidgetInSection_eventCorrectlyFired() { + int sectionIndex = 2; + int initialIndex = 0; + int finalIndex = 1; + List expectedItems = dashboard.getChildren().toList(); + DashboardSection section = (DashboardSection) expectedItems + .get(sectionIndex); + int sectionNodeId = section.getElement().getNode().getId(); + Component movedItem = section.getWidgets().get(initialIndex); + int movedItemNodeId = movedItem.getElement().getNode().getId(); + Runnable itemMoveAction = () -> { + reorderSectionWidget(sectionIndex, initialIndex, finalIndex); + fireItemMovedEvent(movedItemNodeId, sectionNodeId); + }; + assertEventCorrectlyFired(itemMoveAction, 1, movedItem, expectedItems, + section); + } + + @Test + public void setDashboardNotEditable_reorderWidget_eventNotFired() { + dashboard.setEditable(false); + int initialIndex = 0; + int finalIndex = 1; + Component movedItem = dashboard.getChildren().toList() + .get(initialIndex); + int movedItemNodeId = movedItem.getElement().getNode().getId(); + Runnable itemMoveAction = () -> { + reorderRootLevelItem(initialIndex, finalIndex); + fireItemMovedEvent(movedItemNodeId); + }; + assertEventCorrectlyFired(itemMoveAction, 0, null, null, null); + } + + private void assertEventCorrectlyFired(Runnable itemMoveAction, + int expectedListenerInvokedCount, Component expectedItem, + List expectedItems, DashboardSection expectedSection) { + AtomicInteger listenerInvokedCount = new AtomicInteger(0); + AtomicReference eventItem = new AtomicReference<>(); + AtomicReference> eventItems = new AtomicReference<>(); + AtomicReference> eventSection = new AtomicReference<>(); + dashboard.addItemMovedListener(e -> { + listenerInvokedCount.incrementAndGet(); + eventItem.set(e.getItem()); + eventItems.set(e.getItems()); + eventSection.set(e.getSection()); + e.unregisterListener(); + }); + itemMoveAction.run(); + Assert.assertEquals(expectedListenerInvokedCount, + listenerInvokedCount.get()); + if (expectedListenerInvokedCount > 0) { + Assert.assertEquals(expectedItem, eventItem.get()); + Assert.assertEquals(expectedItems, eventItems.get()); + Assert.assertEquals(Optional.ofNullable(expectedSection), + eventSection.get()); + } + } + + private void fireItemMovedEvent(int itemNodeId) { + fireItemMovedEvent(itemNodeId, null); + } + + private void fireItemMovedEvent(int itemNodeId, Integer sectionNodeId) { + JsonObject eventData = Json.createObject(); + eventData.put("event.detail.item", itemNodeId); + eventData.put("event.detail.items", itemsArray); + if (sectionNodeId != null) { + eventData.put("event.detail.section", sectionNodeId); + } + DomEvent itemMovedDomEvent = new DomEvent(dashboard.getElement(), + "dashboard-item-moved-flow", eventData); + dashboard.getElement().getNode().getFeature(ElementListenerMap.class) + .fireEvent(itemMovedDomEvent); } private List getSectionWidgetNodeIds(int sectionIndex) { @@ -122,19 +245,26 @@ private void reorderRootLevelItem(int initialIndex, int finalIndex) { private void assertSectionWidgetReorder(int sectionIndex, int initialIndex, int finalIndex) { + DashboardSection section = (DashboardSection) dashboard.getChildren() + .toList().get(sectionIndex); + int sectionNodeId = section.getElement().getNode().getId(); + int movedWidgetNodeId = section.getWidgets().get(initialIndex) + .getElement().getNode().getId(); reorderSectionWidget(sectionIndex, initialIndex, finalIndex); List expectedSectionWidgetNodeIds = getExpectedSectionWidgetNodeIds( sectionIndex, initialIndex, finalIndex); - fireItemReorderEndEvent(); + fireItemMovedEvent(movedWidgetNodeId, sectionNodeId); Assert.assertEquals(expectedSectionWidgetNodeIds, getSectionWidgetNodeIds(sectionIndex)); } private void assertRootLevelItemReorder(int initialIndex, int finalIndex) { + int movedItemNodeId = dashboard.getChildren().toList().get(initialIndex) + .getElement().getNode().getId(); reorderRootLevelItem(initialIndex, finalIndex); List expectedRootLevelNodeIds = getExpectedRootLevelItemNodeIds( initialIndex, finalIndex); - fireItemReorderEndEvent(); + fireItemMovedEvent(movedItemNodeId); Assert.assertEquals(expectedRootLevelNodeIds, getRootLevelNodeIds()); } diff --git a/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/test/java/com/vaadin/flow/component/dashboard/tests/DashboardDragResizeTest.java b/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/test/java/com/vaadin/flow/component/dashboard/tests/DashboardDragResizeTest.java index 8a901a0f961..7a396a7e4bb 100644 --- a/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/test/java/com/vaadin/flow/component/dashboard/tests/DashboardDragResizeTest.java +++ b/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/test/java/com/vaadin/flow/component/dashboard/tests/DashboardDragResizeTest.java @@ -8,15 +8,23 @@ */ package com.vaadin.flow.component.dashboard.tests; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import com.vaadin.flow.component.ComponentUtil; +import com.vaadin.flow.component.Component; import com.vaadin.flow.component.dashboard.Dashboard; -import com.vaadin.flow.component.dashboard.DashboardItemResizeEndEvent; import com.vaadin.flow.component.dashboard.DashboardSection; import com.vaadin.flow.component.dashboard.DashboardWidget; +import com.vaadin.flow.dom.DomEvent; +import com.vaadin.flow.internal.nodefeature.ElementListenerMap; + +import elemental.json.Json; +import elemental.json.JsonObject; public class DashboardDragResizeTest extends DashboardTestBase { private Dashboard dashboard; @@ -68,30 +76,82 @@ public void resizeWidgetInSectionBothHorizontallyAndVertically_sizeIsUpdated() { public void setDashboardNotEditable_resizeWidget_sizeIsNotUpdated() { dashboard.setEditable(false); DashboardWidget widgetToResize = dashboard.getWidgets().get(0); - fireItemResizeEndEvent(widgetToResize, 2, 2); + fireItemResizedEvent(widgetToResize, 2, 2); Assert.assertEquals(1, widgetToResize.getColspan()); Assert.assertEquals(1, widgetToResize.getRowspan()); } + @Test + public void resizeWidget_eventCorrectlyFired() { + DashboardWidget resizedWidget = (DashboardWidget) dashboard + .getChildren().toList().get(0); + assertEventCorrectlyFired(resizedWidget, 1, resizedWidget, + dashboard.getChildren().toList()); + } + + @Test + public void resizeWidgetInSection_eventCorrectlyFired() { + DashboardSection section = (DashboardSection) dashboard.getChildren() + .toList().get(1); + DashboardWidget resizedWidget = section.getWidgets().get(0); + assertEventCorrectlyFired(resizedWidget, 1, resizedWidget, + dashboard.getChildren().toList()); + } + + @Test + public void setDashboardNotEditable_resizeWidget_eventNotFired() { + dashboard.setEditable(false); + DashboardWidget resizedWidget = (DashboardWidget) dashboard + .getChildren().toList().get(0); + assertEventCorrectlyFired(resizedWidget, 0, null, null); + } + + private void assertEventCorrectlyFired(DashboardWidget widgetToResize, + int expectedListenerInvokedCount, Component expectedResizedWidget, + List expectedItems) { + AtomicInteger listenerInvokedCount = new AtomicInteger(0); + AtomicReference eventResizedWidget = new AtomicReference<>(); + AtomicReference> eventItems = new AtomicReference<>(); + dashboard.addItemResizedListener(e -> { + listenerInvokedCount.incrementAndGet(); + eventResizedWidget.set(e.getItem()); + eventItems.set(e.getItems()); + e.unregisterListener(); + }); + fireItemResizedEvent(widgetToResize, 2, 2); + Assert.assertEquals(expectedListenerInvokedCount, + listenerInvokedCount.get()); + if (expectedListenerInvokedCount > 0) { + Assert.assertEquals(expectedResizedWidget, + eventResizedWidget.get()); + Assert.assertEquals(expectedItems, eventItems.get()); + } + } + private void assertWidgetResized(int widgetIndexToResize, int targetColspan, int targetRowspan) { DashboardWidget widgetToResize = dashboard.getWidgets() .get(widgetIndexToResize); // Assert widget is enlarged - fireItemResizeEndEvent(widgetToResize, targetColspan, targetRowspan); + fireItemResizedEvent(widgetToResize, targetColspan, targetRowspan); Assert.assertEquals(targetColspan, widgetToResize.getColspan()); Assert.assertEquals(targetRowspan, widgetToResize.getRowspan()); // Assert widget is shrunk - fireItemResizeEndEvent(widgetToResize, 1, 1); + fireItemResizedEvent(widgetToResize, 1, 1); Assert.assertEquals(1, widgetToResize.getColspan()); Assert.assertEquals(1, widgetToResize.getRowspan()); } - private void fireItemResizeEndEvent(DashboardWidget widget, - int targetColspan, int targetRowspan) { - ComponentUtil.fireEvent(dashboard, - new DashboardItemResizeEndEvent(dashboard, false, - widget.getElement().getNode().getId(), targetColspan, - targetRowspan)); + private void fireItemResizedEvent(DashboardWidget widget, int targetColspan, + int targetRowspan) { + JsonObject eventData = Json.createObject(); + eventData.put("event.detail.item.nodeid", + widget.getElement().getNode().getId()); + eventData.put("event.detail.item.rowspan", targetRowspan); + eventData.put("event.detail.item.colspan", targetColspan); + DomEvent itemResizedDomEvent = new DomEvent(dashboard.getElement(), + "dashboard-item-resized", eventData); + dashboard.getElement().getNode().getFeature(ElementListenerMap.class) + .fireEvent(itemResizedDomEvent); } } diff --git a/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/test/java/com/vaadin/flow/component/dashboard/tests/DashboardTest.java b/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/test/java/com/vaadin/flow/component/dashboard/tests/DashboardTest.java index b6cdaf46836..8c73e5eabd5 100644 --- a/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/test/java/com/vaadin/flow/component/dashboard/tests/DashboardTest.java +++ b/vaadin-dashboard-flow-parent/vaadin-dashboard-flow/src/test/java/com/vaadin/flow/component/dashboard/tests/DashboardTest.java @@ -8,19 +8,27 @@ */ package com.vaadin.flow.component.dashboard.tests; +import java.util.ArrayList; +import java.util.List; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import com.vaadin.flow.component.ComponentUtil; +import com.vaadin.flow.component.Component; import com.vaadin.flow.component.dashboard.Dashboard; -import com.vaadin.flow.component.dashboard.DashboardItemRemovedEvent; import com.vaadin.flow.component.dashboard.DashboardSection; import com.vaadin.flow.component.dashboard.DashboardWidget; import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.dom.DomEvent; +import com.vaadin.flow.internal.nodefeature.ElementListenerMap; + +import elemental.json.Json; +import elemental.json.JsonObject; public class DashboardTest extends DashboardTestBase { private Dashboard dashboard; @@ -852,6 +860,56 @@ public void setDashboardEditable_removeWidget_widgetIsRemoved() { Assert.assertFalse(actualNodeIds.contains(nodeIdToBeRemoved)); } + public void setDashboardEditable_removeWidget_eventCorrectlyFired() { + dashboard.setEditable(true); + DashboardWidget widget = new DashboardWidget(); + dashboard.add(widget); + fakeClientCommunication(); + int removedWidgetNodeId = widget.getElement().getNode().getId(); + List expectedItems = dashboard.getChildren() + .collect(Collectors.toCollection(ArrayList::new)); + expectedItems.remove(widget); + assertItemRemoveEventCorrectlyFired(removedWidgetNodeId, 1, widget, + expectedItems); + } + + @Test + public void setDashboardEditable_removeSection_eventCorrectlyFired() { + dashboard.setEditable(true); + DashboardSection section = dashboard.addSection(); + fakeClientCommunication(); + int removedSectionNodeId = section.getElement().getNode().getId(); + List expectedItems = dashboard.getChildren() + .collect(Collectors.toCollection(ArrayList::new)); + expectedItems.remove(section); + assertItemRemoveEventCorrectlyFired(removedSectionNodeId, 1, section, + expectedItems); + } + + @Test + public void setDashboardEditable_removeWidgetInSection_eventCorrectlyFired() { + dashboard.setEditable(true); + DashboardSection section = dashboard.addSection(); + DashboardWidget widget = new DashboardWidget(); + section.add(widget); + fakeClientCommunication(); + int removedWidgetNodeId = widget.getElement().getNode().getId(); + List expectedItems = dashboard.getChildren() + .collect(Collectors.toCollection(ArrayList::new)); + expectedItems.remove(widget); + assertItemRemoveEventCorrectlyFired(removedWidgetNodeId, 1, widget, + expectedItems); + } + + @Test + public void dashboardNotEditable_removeWidget_eventNotFired() { + DashboardWidget widget = new DashboardWidget(); + dashboard.add(widget); + fakeClientCommunication(); + int removedWidgetNodeId = widget.getElement().getNode().getId(); + assertItemRemoveEventCorrectlyFired(removedWidgetNodeId, 0, null, null); + } + @Test public void setDashboardVisibility_exceptionIsThrown() { Assert.assertThrows(UnsupportedOperationException.class, @@ -880,8 +938,33 @@ public void getSectionVisibility_returnsTrue() { Assert.assertTrue(section.isVisible()); } + private void assertItemRemoveEventCorrectlyFired(int nodeIdToRemove, + int expectedListenerInvokedCount, Component expectedRemovedItem, + List expectedItems) { + AtomicInteger listenerInvokedCount = new AtomicInteger(0); + AtomicReference eventRemovedItem = new AtomicReference<>(); + AtomicReference> eventItems = new AtomicReference<>(); + dashboard.addItemRemovedListener(e -> { + listenerInvokedCount.incrementAndGet(); + eventRemovedItem.set(e.getItem()); + eventItems.set(e.getItems()); + e.unregisterListener(); + }); + fireItemRemovedEvent(nodeIdToRemove); + Assert.assertEquals(expectedListenerInvokedCount, + listenerInvokedCount.get()); + if (expectedListenerInvokedCount > 0) { + Assert.assertEquals(expectedRemovedItem, eventRemovedItem.get()); + Assert.assertEquals(expectedItems, eventItems.get()); + } + } + private void fireItemRemovedEvent(int nodeId) { - ComponentUtil.fireEvent(dashboard, - new DashboardItemRemovedEvent(dashboard, false, nodeId)); + JsonObject eventData = Json.createObject(); + eventData.put("event.detail.item.nodeid", nodeId); + DomEvent itemRemovedDomEvent = new DomEvent(dashboard.getElement(), + "dashboard-item-removed", eventData); + dashboard.getElement().getNode().getFeature(ElementListenerMap.class) + .fireEvent(itemRemovedDomEvent); } }