Skip to content

Commit

Permalink
Additions to generic DnD support (#6612)
Browse files Browse the repository at this point in the history
Improve javadocs about browser differences.
Adds missing method for customizing draggable element.
Adds unit test for customizing draggable element.
Adds test UI for manually testing customized draggable element.
Fixes typo in API and deprecates typoed method.
  • Loading branch information
pleku authored Oct 7, 2019
1 parent d04fbe5 commit bd18dc6
Show file tree
Hide file tree
Showing 5 changed files with 200 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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}.
*
* <em>NOTE:</em> For Edge, Safari and IE11 this method will <b>always
* report <code>false</code></b> 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}.
* <em>NOTE:</em> For Edge, Safari and IE11 this method will <b>always
* report <code>false</code></b> 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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()}.
*
* <p>
* 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();
}

Expand All @@ -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()
Expand All @@ -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(),
Expand All @@ -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");
}

/**
Expand Down Expand Up @@ -254,6 +272,14 @@ default Object getDragData() {
* <p>
* By default the value is {@link EffectAllowed#UNINITIALIZED} which is
* equivalent to {@link EffectAllowed#ALL}.
* <p>
* <em>NOTE:</em> 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.
* <p>
* <em>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.</em>
*
* @param effect
* Effects to allow for this draggable element. Cannot be {@code
Expand All @@ -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());
}

Expand All @@ -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)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ default boolean isActive() {
* <p>
* <em>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.</em>
* Chrome the drop is prevented if the properties don't match.</em>
*
* @param dropEffect
* the drop effect to be set or {@code null} to not modify
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -30,7 +32,8 @@
public class DropTargetTest {

@Tag("div")
class TestComponent extends Component implements DropTarget<TestComponent> {
class TestComponent extends Component
implements DropTarget<TestComponent>, HasComponents {

}

Expand Down Expand Up @@ -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<RouterLink> dragSource = DragSource.create(source);
DropTarget<TestComponent> dropTarget = DropTarget.create(target);

DragStartEvent<RouterLink> startEvent = new DragStartEvent<RouterLink>(
source, true);
ComponentUtil.fireEvent(source, startEvent);
Assert.assertEquals(source, ui.getActiveDragSourceComponent());

ComponentEventListener<DropEvent<TestComponent>> 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<TestComponent> dropEvent = new DropEvent<>(target, true,
"all");
ComponentUtil.fireEvent(target, dropEvent);

Assert.assertEquals("Drag source component should have been moved",
source.getParent().get(), target);

DragEndEvent<RouterLink> endEvent = new DragEndEvent<>(source, true,
"move");
// should not throw for removing the active drag source
ComponentUtil.fireEvent(source, endEvent);

Assert.assertNull(ui.getActiveDragSourceComponent());
}
}
Original file line number Diff line number Diff line change
@@ -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<DraggableItem> {

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<DraggableItem> event) {
dropTarget.getStyle().set("background-color", "lightgreen");
}

private void onDragEnd(DragEndEvent<DraggableItem> event) {
dropTarget.getStyle().remove("background-color");
}

}

0 comments on commit bd18dc6

Please sign in to comment.