diff --git a/flow-dnd/src/main/java/com/vaadin/flow/component/dnd/DragEndEvent.java b/flow-dnd/src/main/java/com/vaadin/flow/component/dnd/DragEndEvent.java index d400c6ab70d..493a7a9df59 100644 --- a/flow-dnd/src/main/java/com/vaadin/flow/component/dnd/DragEndEvent.java +++ b/flow-dnd/src/main/java/com/vaadin/flow/component/dnd/DragEndEvent.java @@ -76,11 +76,30 @@ public DropEffect getDropEffect() { /** * Returns whether the drop event succesful or was it cancelled or didn't * succeed. This is a shorthand for {@code dropEffect != NONE}. - * + * NOTE: For Edge, Safari and IE11 this method will always + * report false due to bugs in the browsers! + * + * @deprecated replaced with {@link #isSuccessful()} since 2.1 (v14.1), this + * method will be removed later. * @return {@code true} if the drop event succeeded, {@code false} * otherwise. */ + @Deprecated public boolean isSuccesful() { + return isSuccessful(); + } + + /** + * Returns whether the drop event succesful or was it cancelled or didn't + * succeed. This is a shorthand for {@code dropEffect != NONE}. + * NOTE: For Edge, Safari and IE11 this method will always + * report false due to bugs in the browsers! + * + * @return {@code true} if the drop event succeeded, {@code false} + * otherwise. + * @since 2.1 + */ + public boolean isSuccessful() { return getDropEffect() != DropEffect.NONE; } diff --git a/flow-dnd/src/main/java/com/vaadin/flow/component/dnd/DragSource.java b/flow-dnd/src/main/java/com/vaadin/flow/component/dnd/DragSource.java index 8c531fe56bd..a748427c730 100644 --- a/flow-dnd/src/main/java/com/vaadin/flow/component/dnd/DragSource.java +++ b/flow-dnd/src/main/java/com/vaadin/flow/component/dnd/DragSource.java @@ -142,15 +142,27 @@ default T getDragSourceComponent() { return (T) this; }; + @Override + default Element getElement() { + return getDragSourceComponent().getElement(); + } + /** * Returns the element where the {@code draggable} attribute is applied, * making it draggable by the user. By default it is the element of the * component returned by {@link #getDragSourceComponent()}. - * + *

+ * Override this method to provide another element to be draggable instead + * of the root element of the component. + * * @return the element made draggable + * @since 2.1 */ - @Override - default Element getElement() { + /* + * Implementation note: this is added so that user can change the draggable + * element to be something else than the root element of the component. + */ + default Element getDraggableElement() { return getDragSourceComponent().getElement(); } @@ -166,9 +178,13 @@ default void setDraggable(boolean draggable) { } if (draggable) { // The attribute is an enumerated one and not a Boolean one. - getElement().setProperty("draggable", Boolean.TRUE.toString()); - getElement().executeJavaScript("window.Vaadin.Flow.dndConnector" - + ".activateDragSource($0)", getElement()); + getDraggableElement().setProperty("draggable", + Boolean.TRUE.toString()); + getDraggableElement() + .executeJavaScript( + "window.Vaadin.Flow.dndConnector" + + ".activateDragSource($0)", + getDraggableElement()); // store & clear the component as active drag source for the UI Registration startListenerRegistration = addDragStartListener( event -> getDragSourceComponent().getUI() @@ -187,9 +203,11 @@ default void setDraggable(boolean draggable) { DndUtil.END_LISTENER_REGISTRATION_KEY, endListenerRegistration); } else { - getElement().removeProperty("draggable"); - getElement().executeJavaScript("window.Vaadin.Flow.dndConnector" - + ".deactivateDragSource($0)", getElement()); + getDraggableElement().removeProperty("draggable"); + getDraggableElement().executeJavaScript( + "window.Vaadin.Flow.dndConnector" + + ".deactivateDragSource($0)", + getDraggableElement()); // clear listeners for setting active data source Object startListenerRegistration = ComponentUtil.getData( getDragSourceComponent(), @@ -212,7 +230,7 @@ default void setDraggable(boolean draggable) { * @return {@code true} draggable, {@code false} if not */ default boolean isDraggable() { - return getElement().hasProperty("draggable"); + return getDraggableElement().hasProperty("draggable"); } /** @@ -254,6 +272,14 @@ default Object getDragData() { *

* By default the value is {@link EffectAllowed#UNINITIALIZED} which is * equivalent to {@link EffectAllowed#ALL}. + *

+ * NOTE: The effect should be set in advance, setting it after the + * user has started dragging and the {@link DragStartEvent} has been fired + * is too late - it will take effect only for next drag operation. + *

+ * NOTE 2: Edge, Safari and IE11 will allow the drop to occur even when + * the effect allowed does not match the drop effect set on the drop target. + * Chrome and Firefox prevent the drop if those do not match. * * @param effect * Effects to allow for this draggable element. Cannot be {@code @@ -266,7 +292,8 @@ default void setEffectAllowed(EffectAllowed effect) { if (effect == null) { throw new IllegalArgumentException("Allowed effect cannot be null"); } - getElement().setProperty(DndUtil.EFFECT_ALLOWED_ELEMENT_PROPERTY, + getDraggableElement().setProperty( + DndUtil.EFFECT_ALLOWED_ELEMENT_PROPERTY, effect.getClientPropertyValue()); } @@ -278,7 +305,7 @@ default void setEffectAllowed(EffectAllowed effect) { * @return effects that are allowed for this draggable element. */ default EffectAllowed getEffectAllowed() { - return EffectAllowed.valueOf(getElement().getProperty( + return EffectAllowed.valueOf(getDraggableElement().getProperty( DndUtil.EFFECT_ALLOWED_ELEMENT_PROPERTY, EffectAllowed.UNINITIALIZED.getClientPropertyValue() .toUpperCase(Locale.ENGLISH))); diff --git a/flow-dnd/src/main/java/com/vaadin/flow/component/dnd/DropTarget.java b/flow-dnd/src/main/java/com/vaadin/flow/component/dnd/DropTarget.java index 73dde9f152b..9ce2d9ff7e1 100644 --- a/flow-dnd/src/main/java/com/vaadin/flow/component/dnd/DropTarget.java +++ b/flow-dnd/src/main/java/com/vaadin/flow/component/dnd/DropTarget.java @@ -184,7 +184,7 @@ default boolean isActive() { *

* NOTE: If the drop effect that doesn't match the effectAllowed of the * drag source, it DOES NOT prevent drop on IE11 and Safari! For FireFox and - * Chrome the drop is prevented if there they don't match. + * Chrome the drop is prevented if the properties don't match. * * @param dropEffect * the drop effect to be set or {@code null} to not modify diff --git a/flow-dnd/src/test/java/com/vaadin/flow/component/dnd/DropTargetTest.java b/flow-dnd/src/test/java/com/vaadin/flow/component/dnd/DropTargetTest.java index 04844a7b73a..9223e69098f 100644 --- a/flow-dnd/src/test/java/com/vaadin/flow/component/dnd/DropTargetTest.java +++ b/flow-dnd/src/test/java/com/vaadin/flow/component/dnd/DropTargetTest.java @@ -19,7 +19,9 @@ import java.util.concurrent.atomic.AtomicReference; import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.ComponentEventListener; import com.vaadin.flow.component.ComponentUtil; +import com.vaadin.flow.component.HasComponents; import com.vaadin.flow.component.Tag; import com.vaadin.flow.component.dnd.internal.DndUtil; import com.vaadin.flow.router.RouterLink; @@ -30,7 +32,8 @@ public class DropTargetTest { @Tag("div") - class TestComponent extends Component implements DropTarget { + class TestComponent extends Component + implements DropTarget, HasComponents { } @@ -176,4 +179,47 @@ public void testDropTarget_notAttachedToUIAndReceivesDropEvent_throws() { "all"); ComponentUtil.fireEvent(component, dropEvent); } + + @Test + public void testDropTarget_dropInSideSameUI_moveComponentToTargetInDropEvent() { + RouterLink source = new RouterLink(); + TestComponent target = new TestComponent(); + ui.add(source, target); + + DragSource dragSource = DragSource.create(source); + DropTarget dropTarget = DropTarget.create(target); + + DragStartEvent startEvent = new DragStartEvent( + source, true); + ComponentUtil.fireEvent(source, startEvent); + Assert.assertEquals(source, ui.getActiveDragSourceComponent()); + + ComponentEventListener> dropListener = event -> { + event.getDragSourceComponent().ifPresent(dragSourceComponent -> { + TestComponent component = event.getComponent(); + + Assert.assertEquals("Invalid drag source component", + dragSourceComponent, source); + Assert.assertEquals("Invalid event source", component, target); + + target.add(dragSourceComponent); + }); + // ELSE will be failed by the checks outside the listener + }; + target.addDropListener(dropListener); + + DropEvent dropEvent = new DropEvent<>(target, true, + "all"); + ComponentUtil.fireEvent(target, dropEvent); + + Assert.assertEquals("Drag source component should have been moved", + source.getParent().get(), target); + + DragEndEvent endEvent = new DragEndEvent<>(source, true, + "move"); + // should not throw for removing the active drag source + ComponentUtil.fireEvent(source, endEvent); + + Assert.assertNull(ui.getActiveDragSourceComponent()); + } } diff --git a/flow-tests/test-root-context/src/main/java/com/vaadin/flow/uitest/ui/DnDCustomComponentView.java b/flow-tests/test-root-context/src/main/java/com/vaadin/flow/uitest/ui/DnDCustomComponentView.java new file mode 100644 index 00000000000..01e5e0f9ce3 --- /dev/null +++ b/flow-tests/test-root-context/src/main/java/com/vaadin/flow/uitest/ui/DnDCustomComponentView.java @@ -0,0 +1,93 @@ +/* + * Copyright 2000-2019 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.flow.uitest.ui; + +import java.util.stream.Stream; + +import com.vaadin.flow.component.Text; +import com.vaadin.flow.component.dnd.DragEndEvent; +import com.vaadin.flow.component.dnd.DragSource; +import com.vaadin.flow.component.dnd.DragStartEvent; +import com.vaadin.flow.component.dnd.DropTarget; +import com.vaadin.flow.component.dnd.EffectAllowed; +import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.component.html.Span; +import com.vaadin.flow.dom.Element; +import com.vaadin.flow.router.Route; +import com.vaadin.flow.uitest.servlet.ViewTestLayout; + +@Route(value = "com.vaadin.flow.uitest.ui.DnDCustomComponentView", layout = ViewTestLayout.class) +public class DnDCustomComponentView extends Div { + + private final Div dropTarget; + + public class DraggableItem extends Div + implements DragSource { + + private final Div dragHandle; + + public DraggableItem(EffectAllowed effectAllowed) { + dragHandle = new Div(); + dragHandle.setHeight("50px"); + dragHandle.setWidth("80px"); + dragHandle.getStyle().set("background", "#000 "); + dragHandle.getStyle().set("margin", "5 10px"); + dragHandle.getStyle().set("display", "inline-block"); + + setDraggable(true); + setDragData(effectAllowed); + addDragStartListener(DnDCustomComponentView.this::onDragStart); + addDragEndListener(DnDCustomComponentView.this::onDragEnd); + + setHeight("50px"); + setWidth("200px"); + getStyle().set("border", "1px solid black"); + getStyle().set("display", "inline-block"); + + add(dragHandle, new Span(effectAllowed.toString())); + } + + @Override + public Element getDraggableElement() { + return dragHandle.getElement(); + } + } + + public DnDCustomComponentView() { + Stream.of(EffectAllowed.values()).map(DraggableItem::new) + .forEach(this::add); + + dropTarget = new Div(); + dropTarget.add(new Text("Drop Here")); + dropTarget.setWidth("200px"); + dropTarget.setHeight("200px"); + dropTarget.getStyle().set("border", "solid 1px pink"); + add(dropTarget); + + DropTarget.create(dropTarget).addDropListener(event -> event.getSource() + .add(new Span(event.getDragData().get().toString()))); + } + + private void onDragStart(DragStartEvent event) { + dropTarget.getStyle().set("background-color", "lightgreen"); + } + + private void onDragEnd(DragEndEvent event) { + dropTarget.getStyle().remove("background-color"); + } + +}