Skip to content

Commit

Permalink
feat: support drag drop for dashboard (#6633)
Browse files Browse the repository at this point in the history
* feat: support drag drop for dashboard

* chore: run formatter

* test: fix unit test assertion

* test: update test route names to align with others

* test: add its that test the server client communication

* refactor: simplify js event workaround

* fix: do not remove widgets on detach

* refactor: remove item sanitization on drag reorder

* refactor: check children of element and remove workaround

* refactor: make dashboard not editable by default

* test: use selenium for drag drop and add tests for editable
  • Loading branch information
ugur-vaadin authored Sep 12, 2024
1 parent e2dd326 commit 66643e9
Show file tree
Hide file tree
Showing 15 changed files with 747 additions and 124 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* Copyright 2000-2024 Vaadin Ltd.
*
* This program is available under Vaadin Commercial License and Service Terms.
*
* See {@literal <https://vaadin.com/commercial-license-and-service-terms>} for the full
* license.
*/
package com.vaadin.flow.component.dashboard.tests;

import com.vaadin.flow.component.dashboard.Dashboard;
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.component.html.NativeButton;
import com.vaadin.flow.router.Route;

/**
* @author Vaadin Ltd
*/
@Route("vaadin-dashboard/drag-drop")
public class DashboardDragDropPage extends Div {

public DashboardDragDropPage() {
Dashboard dashboard = new Dashboard();
dashboard.setEditable(true);
dashboard.setMinimumRowHeight("100px");
dashboard.setMaximumColumnWidth("400px");

DashboardWidget widget1 = new DashboardWidget();
widget1.setTitle("Widget 1");

DashboardWidget widget2 = new DashboardWidget();
widget2.setTitle("Widget 2");

dashboard.add(widget1, widget2);

DashboardWidget widget1InSection1 = new DashboardWidget();
widget1InSection1.setTitle("Widget 1 in Section 1");

DashboardWidget widget2InSection1 = new DashboardWidget();
widget2InSection1.setTitle("Widget 2 in Section 1");

DashboardSection section1 = new DashboardSection("Section 1");
section1.add(widget1InSection1, widget2InSection1);

dashboard.addSection(section1);

DashboardWidget widgetInSection2 = new DashboardWidget();
widgetInSection2.setTitle("Widget in Section 2");

DashboardSection section2 = new DashboardSection("Section 2");
section2.add(widgetInSection2);

dashboard.addSection(section2);

NativeButton toggleAttached = new NativeButton("Toggle attached", e -> {
if (dashboard.getParent().isPresent()) {
dashboard.removeFromParent();
} else {
add(dashboard);
}
});
toggleAttached.setId("toggle-attached");

NativeButton toggleEditable = new NativeButton("Toggle editable",
e -> dashboard.setEditable(!dashboard.isEditable()));
toggleEditable.setId("toggle-editable");

add(toggleAttached, toggleEditable, dashboard);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
/**
* @author Vaadin Ltd
*/
@Route("vaadin-dashboard-section")
@Route("vaadin-dashboard/section")
public class DashboardSectionPage extends Div {

public DashboardSectionPage() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
/**
* @author Vaadin Ltd
*/
@Route("vaadin-dashboard-widget")
@Route("vaadin-dashboard/widget")
public class DashboardWidgetPage extends Div {

public DashboardWidgetPage() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/**
* Copyright 2000-2024 Vaadin Ltd.
*
* This program is available under Vaadin Commercial License and Service Terms.
*
* See {@literal <https://vaadin.com/commercial-license-and-service-terms>} for the full
* license.
*/
package com.vaadin.flow.component.dashboard.tests;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.interactions.Actions;

import com.vaadin.flow.component.dashboard.testbench.DashboardElement;
import com.vaadin.flow.testutil.TestPath;
import com.vaadin.testbench.TestBenchElement;
import com.vaadin.tests.AbstractComponentIT;

/**
* @author Vaadin Ltd
*/
@TestPath("vaadin-dashboard/drag-drop")
public class DashboardDragDropIT extends AbstractComponentIT {

private DashboardElement dashboardElement;

@Before
public void init() {
open();
dashboardElement = $(DashboardElement.class).waitForFirst();
}

@Test
public void reorderWidgetOnClientSide_itemsAreReorderedCorrectly() {
var draggedWidget = dashboardElement.getWidgets().get(0);
var targetWidget = dashboardElement.getWidgets().get(1);
dragDropElement(draggedWidget, targetWidget);
Assert.assertEquals(draggedWidget.getTitle(),
dashboardElement.getWidgets().get(1).getTitle());
}

@Test
public void reorderSectionOnClientSide_itemsAreReorderedCorrectly() {
var draggedSection = dashboardElement.getSections().get(1);
var targetWidget = dashboardElement.getWidgets().get(0);
dragDropElement(draggedSection, targetWidget);
Assert.assertEquals(draggedSection.getTitle(),
dashboardElement.getSections().get(0).getTitle());
}

@Test
public void reorderWidgetInSectionOnClientSide_itemsAreReorderedCorrectly() {
var firstSection = dashboardElement.getSections().get(0);
var draggedWidget = firstSection.getWidgets().get(0);
var targetWidget = firstSection.getWidgets().get(1);
dragDropElement(draggedWidget, targetWidget);
firstSection = dashboardElement.getSections().get(0);
Assert.assertEquals(draggedWidget.getTitle(),
firstSection.getWidgets().get(1).getTitle());
}

@Test
public void detachReattach_reorderWidgetOnClientSide_itemsAreReorderedCorrectly() {
clickElementWithJs("toggle-attached");
clickElementWithJs("toggle-attached");
dashboardElement = $(DashboardElement.class).waitForFirst();
reorderWidgetOnClientSide_itemsAreReorderedCorrectly();
}

@Test
public void setDashboardNotEditable_widgetCannotBeDragged() {
var widget = dashboardElement.getWidgets().get(0);
Assert.assertTrue(isHeaderActionsVisible(widget));
clickElementWithJs("toggle-editable");
Assert.assertFalse(isHeaderActionsVisible(widget));
}

@Test
public void setDashboardEditable_widgetCanBeDragged() {
clickElementWithJs("toggle-editable");
clickElementWithJs("toggle-editable");
Assert.assertTrue(
isHeaderActionsVisible(dashboardElement.getWidgets().get(0)));
}

private void dragDropElement(TestBenchElement draggedElement,
TestBenchElement targetElement) {
var dragHandle = getDragHandle(draggedElement);

var yOffset = draggedElement.getLocation().getY() < targetElement
.getLocation().getY() ? 10 : -10;
var xOffset = draggedElement.getLocation().getX() < targetElement
.getLocation().getX() ? 10 : -10;

new Actions(driver).clickAndHold(dragHandle)
.moveToElement(targetElement, xOffset, yOffset)
.release(targetElement).build().perform();
}

private static boolean isHeaderActionsVisible(TestBenchElement element) {
TestBenchElement headerActions = element.$("*").withId("header-actions")
.first();
return !"none".equals(headerActions.getCssValue("display"));
}

private static TestBenchElement getDragHandle(TestBenchElement element) {
return element.$("*").withClassName("drag-handle").first();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
/**
* @author Vaadin Ltd
*/
@TestPath("vaadin-dashboard-section")
@TestPath("vaadin-dashboard/section")
public class DashboardSectionIT extends AbstractComponentIT {

private DashboardElement dashboardElement;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
/**
* @author Vaadin Ltd
*/
@TestPath("vaadin-dashboard-widget")
@TestPath("vaadin-dashboard/widget")
public class DashboardWidgetIT extends AbstractComponentIT {

private DashboardElement dashboardElement;
Expand Down
Loading

0 comments on commit 66643e9

Please sign in to comment.