I've tried drag and drop using dragAndDrop & performAction, but none of them working for Vue.Draggable web app (e.g. vuedraggable: Two Lists).
Can anyone share a solution if possible?
Sample Code:
it('should demonstrate the dragAndDrop command', () => {
browser.url('https://sortablejs.github.io/Vue.Draggable/#/two-lists')
const elem = $('#two-lists .row div:nth-child(1) div.list-group div.list-group-item:nth-child(1)')
const target = $('#two-lists .row div:nth-child(2) div.list-group')
elem.dragAndDrop(target)
browser.pause(5000)
})
Built-in dragAndDrop function won't work because of very custom implementation of drag n drop in vuedraggable.
The only option would be to either fully or partially replace it with js.
Here you go
it('should demonstrate the dragAndDrop command', () => {
browser.url('https://sortablejs.github.io/Vue.Draggable/#/two-lists')
const listFrom = $('#two-lists .col-3:nth-child(1) .list-group')
const listTo = $('#two-lists .col-3:nth-child(2) .list-group')
const draggable = listFrom.$('.list-group-item')
const target = listTo.$('.list-group-item')
target.waitForClickable()
// before 4 in left and 3 in right list
expect(listFrom).toHaveChildren(4)
expect(listTo).toHaveChildren(3)
// start dragging
browser.performActions([
{
type: 'pointer',
id: 'finger1',
parameters: { pointerType: 'mouse' },
actions: [
{ type: 'pointerMove', origin: draggable, x: 0, y: 0 },
{ type: 'pointerDown', button: 0 },
{ type: 'pointerMove', origin: 'pointer', x: 5, y: 5, duration: 5 },
],
},
])
// emulate drop with js
browser.execute(
function (elemDrag, elemDrop) {
const pos = elemDrop.getBoundingClientRect()
const center2X = Math.floor((pos.left + pos.right) / 2)
const center2Y = Math.floor((pos.top + pos.bottom) / 2)
function fireMouseEvent(type, relatedTarget, clientX, clientY) {
const evt = new MouseEvent(type, { clientX, clientY, relatedTarget, bubbles: true })
relatedTarget.dispatchEvent(evt)
}
fireMouseEvent('dragover', elemDrop, center2X, center2Y)
fireMouseEvent('dragend', elemDrag, center2X, center2Y)
fireMouseEvent('mouseup', elemDrag, center2X, center2Y)
},
draggable,
target
)
// after 3 in left and 4 in right list
expect(listFrom).toHaveChildren(3)
expect(listTo).toHaveChildren(4)
browser.pause(2000) // demo
})
See also https://ghostinspector.com/blog/simulate-drag-and-drop-javascript-casperjs/
Related
I am trying to use ThreeJS in my NuxtJS app. I am trying to resolve an error while deploying to local host.
The issue is with the following two lines:
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
which throws a syntax error "cannot use import statement outside a module". "node:vm - Missing stack frames".
The full code in the script tag is attached below.
The problem will likely resolve if I adapt the code into the head section of export default, but I cannot find the appropriate syntax for this.
<script>
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
export default {
data() {
return {
camera: new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000),
scene: new THREE.Scene(),
renderer: new THREE.WebGLRenderer({
canvas: document.querySelector('#canvas')
}),
mesh: null
}
},
head() {
return {
script: [
{
async: true,
type: 'module',
src: 'https://unpkg.com/es-module-shims#1.3.6/dist/es-module-shims.js'
},
{
type: 'module',
imports: {
type: 'module',
three: 'https://unpkg.com/three#<version>/build/three.module.js'
}
},
]
}
},
mounted() {
this.init;
},
methods: {
init() {
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.camera.position.setZ(30);
this.renderer.render(this.scene, this.camera);
// add shapes//geometry //materials //mesh
const geometry = new THREE.PlaneGeometry(10, 3, 16, 100)
const material = new THREE.MeshBasicMaterial({ color: 0xFF6347, wireframe: true });
const plane = new THREE.Mesh(geometry, material);
const controls = new OrbitControls(this.camera, this.renderer.domElement);
// renderer.render( scene, camera);
// render
this.scene.add(plane)
// lighting
const pointLight = new THREE.PointLight(0xFFFFFF)
pointLight.position.set(5, 5, 5)
// pointLight.position.set(20,20,20)
const ambientLight = new THREE.AmbientLight(0xFFFFFF);
this.scene.add(pointLight, ambientLight)
const lightHelper = new THREE.PointLightHelper(pointLight)
const gridHelper = new THREE.GridHelper(200, 50);
this.scene.add(lightHelper, gridHelper)
// animate
function animate() {
requestAnimationFrame(animate);
plane.rotation.x += 0.01;
plane.rotation.y += 0.005;
plane.rotation.z += 0.01;
controls.update();
this.renderer.render(this.scene, this.camera);
}
animate()
}
},
}
</script>
I have created a codepen that uses jquery ui droppable(for drag/drop), jsPlumb (for flowcharting) and Panzoom (panning and zooming) to create a flowchart builder. You could drag the list items from the draggable container (1st column) to the flowchart (2nd column) and then connect the items using the dots to create a flowchart. The #flowchart is a Panzoom target with both pan and zoom enabled. This all works fine.
However, I would like to have the #flowchart div always span the whole area of the flowchart-wrapper i.e. the #flowchart should be an infinite canvas that supports panning, zooming and is a droppable container.
It should have the same effect as flowchart-builder-demo. The canvas there is infinite where you can drag and drop items (Questions, Actions, Outputs) from the right column.
Any pointers on how to achieve this (like the relevant events or multiple panzoom elements and/or css changes) would be greatly appreciated.
const BG_SRC_TGT = "#2C7BE5";
const HEX_SRC_ENDPOINT = BG_SRC_TGT;
const HEX_TGT_ENDPOINT = BG_SRC_TGT;
const HEX_ENDPOINT_HOVER = "#fd7e14";
const HEX_CONNECTOR = "#39afd1";
const HEX_CONNECTOR_HOVER = "#fd7e14";
const connectorPaintStyle = {
strokeWidth: 2,
stroke: HEX_CONNECTOR,
joinstyle: "round",
outlineStroke: "white",
outlineWidth: 1
},
connectorHoverStyle = {
strokeWidth: 3,
stroke: HEX_CONNECTOR_HOVER,
outlineWidth: 2,
outlineStroke: "white"
},
endpointHoverStyle = {
fill: HEX_ENDPOINT_HOVER,
stroke: HEX_ENDPOINT_HOVER
},
sourceEndpoint = {
endpoint: "Dot",
paintStyle: {
stroke: HEX_SRC_ENDPOINT,
fill: "transparent",
radius: 4,
strokeWidth: 3
},
isSource: true,
connector: ["Flowchart", { stub: [40, 60], gap: 8, cornerRadius: 5, alwaysRespectStubs: true }],
connectorStyle: connectorPaintStyle,
hoverPaintStyle: endpointHoverStyle,
connectorHoverStyle: connectorHoverStyle,
dragOptions: {},
overlays: [
["Label", {
location: [0.5, 1.5],
label: "Drag",
cssClass: "endpointSourceLabel",
visible: false
}]
]
},
targetEndpoint = {
endpoint: "Dot",
paintStyle: {
fill: HEX_TGT_ENDPOINT,
radius: 5
},
hoverPaintStyle: endpointHoverStyle,
maxConnections: -1,
dropOptions: { hoverClass: "hover", activeClass: "active" },
isTarget: true,
overlays: [
["Label", { location: [0.5, -0.5], label: "Drop", cssClass: "endpointTargetLabel", visible: false }]
]
};
const getUniqueId = () => Math.random().toString(36).substring(2, 8);
// Setup jquery ui draggable, droppable
$("li.list-group-item").draggable({
helper: "clone",
zIndex: 100,
scroll: false,
start: function (event, ui) {
var width = event.target.getBoundingClientRect().width;
$(ui.helper).css({
'width': Math.ceil(width)
});
}
});
$('#flowchart').droppable({
hoverClass: "drop-hover",
tolerance: "pointer",
drop: function (event, ui) {
var helper = $(ui.helper);
var fieldId = getUniqueId();
var offset = $(this).offset(),
x = event.pageX - offset.left,
y = event.pageY - offset.top;
helper.find('div.field').clone(false)
.animate({ 'min-height': '40px', width: '180px' })
.css({ position: 'absolute', left: x, top: y })
.attr('id', fieldId)
.appendTo($(this)).fadeIn('fast', function () {
var field = $("#" + fieldId);
jsPlumbInstance.draggable(field, {
containment: "parent",
scroll: true,
grid: [5, 5],
stop: function (event, ui) {
}
});
field.addClass('panzoom-exclude');
var bottomEndpoints = ["BottomCenter"];
var topEndPoints = ["TopCenter"];
addEndpoints(fieldId, bottomEndpoints, topEndPoints);
jsPlumbInstance.revalidate(fieldId);
});
}
});
const addEndpoints = (toId, sourceAnchors, targetAnchors) => {
for (var i = 0; i < sourceAnchors.length; i++) {
var sourceUUID = toId + sourceAnchors[i];
jsPlumbInstance.addEndpoint(toId, sourceEndpoint, { anchor: sourceAnchors[i], uuid: sourceUUID });
}
for (var j = 0; j < targetAnchors.length; j++) {
var targetUUID = toId + targetAnchors[j];
jsPlumbInstance.addEndpoint(toId, targetEndpoint, { anchor: targetAnchors[j], uuid: targetUUID });
}
$('.jtk-endpoint').addClass('panzoom-exclude');
}
// Setup jsPlumbInstance
var jsPlumbInstance = jsPlumb.getInstance({
DragOptions: { cursor: 'pointer', zIndex: 12000 },
ConnectionOverlays: [
["Arrow", { location: 1 }],
["Label", {
location: 0.1,
id: "label",
cssClass: "aLabel"
}]
],
Container: 'flowchart'
});
// Setup Panzoom
const elem = document.getElementById('flowchart');
const panzoom = Panzoom(elem, {
excludeClass: 'panzoom-exclude',
canvas: true
});
const parent = elem.parentElement;
parent.addEventListener('wheel', panzoom.zoomWithWheel);
I've just been working on the exact same issue and came across this as the only answer
Implementing pan and zoom in jsPlumb
The PanZoom used looks to be quite old - but the idea was the same, use the JQuery Draggable plugin for the movable elements, instead of the in-built JsPlumb one. This allows the elements to move out of bounds.
The below draggable function fixed it for me using the PanZoom library.
var that = this;
var currentScale = 1;
var element = $('.element');
element.draggable({
start: function (e) {
//we need current scale factor to adjust coordinates of dragging element
currentScale = that.panzoom.getScale();
$(this).css("cursor", "move");
that.panzoom.setOptions({ disablePan: true });
},
drag: function (e, ui) {
ui.position.left = ui.position.left / currentScale;
ui.position.top = ui.position.top / currentScale;
if ($(this).hasClass("jtk-connected")) {
that.jsPlumbInstance.repaintEverything();
}
},
stop: function (e, ui) {
var nodeId = $(this).attr('id');
that.jsPlumbInstance.repaintEverything();
$(this).css("cursor", "");
that.panzoom.setOptions({ disablePan: false });
}
});
I'm not sure if redrawing everything on drag is that efficient - so maybe just redraw both the connecting elements.
I am using react-native-testing-library - https://callstack.github.io/react-native-testing-library/docs/getting-started
I have a <SegmentedControlIOS> - https://facebook.github.io/react-native/docs/segmentedcontrolios
I want to pres the first segment. I am doing this:
const testID = "SegmentedControl";
const stub = jest.fn();
const values = [{ label: "foo" }];
const { getByTestId } = render(
<SegmentedControlIOS values={['foo', 'bar']} onChange={stub} testID={testID} />
);
expect(() => {
getByTestId(testID);
}).not.toThrow();
fireEvent(getByTestId(testID), "change ", {
nativeEvent: {
value: values[0],
selectedSegmentIndex: 0,
},
});
However I get the error:
No handler function found for event: "change "
Screenshot below. Anyone know how to press different segments in <SegmentedControlIOS>?
fireEvent(element: ReactTestInstance, eventName: string, ...data:
Array): void
The change function is located in the fireEvent object. Here's how to use it:
Version 5 or later:
fireEvent.change(getByTestId(testID), { target: { value: values[0],selectedSegmentIndex: 0 } });
Version 5 or before:
const input = getByTestId(testID);
input.value = values[0];
input.selectedSegmentIndex = 0;
fireEvent.change(input);
If you want to check the onChange function of SegmentedControlIOS,
using fireEvent with native events that aren't already aliased by the fireEvent api.
// you can omit the `on` prefix
fireEvent(getByTestId(testID), 'onChange');
A solution was posted here, I didn't try it yet, but it looks more right I think - https://github.com/callstack/react-native-testing-library/issues/220#issuecomment-541067962
import React from "react";
import { SegmentedControlIOS } from "react-native";
import { fireEvent, render } from "react-native-testing-library";
const testID = "SegmentedControl";
const stub = jest.fn();
const values = [{ label: "foo" }];
const { getByTestId } = render(
<SegmentedControlIOS
values={["foo", "bar"]}
onChange={stub}
testID={testID}
/>,
);
it("sends events", () => {
fireEvent(getByTestId(testID), "onChange", {
nativeEvent: {
value: values[0],
selectedSegmentIndex: 0,
},
});
});
I have asked a similar question about this before, but now the main problem has changed:
I have an ion-picker with two rows, and the values of the 2nd row change completely depending on what you choose in the 1st row. Now I can change the values that are received when an option is selected, but the picker doesn't update, so even though other values are being used, the old ones are still being displayed, and that's a pretty big problem.
In the answer I received in the earlier question, I was told that there's a function called forceUpdate(), but when I tried it, it changed nothing.
I was told that this could still be in development and maybe even get removed in the near future, but I need to know if there's a way to update the ion-picker now or not.
Here's the code for the ion-picker:
async showJetPicker(id) {
if (this.disabledis[id - 6] !== true) {
const opts: PickerOptions = {
cssClass: 'academy-picker',
buttons: [
...
],
columns: [
{
name: '1st row',
options: this.convertColumns(this.pickerbois[id].options[0])
},
{
name: '2nd row',
options: this.convertColumns(this.pickerbois[id].options[1])
}
]
};
const picker = await this.pickerCtrl.create(opts);
picker.addEventListener('ionPickerColChange', async (event: any) => {
const data = event.detail;
const colSelectedIndex = data.selectedIndex;
const colOptions = data.options;
if (colSelectedIndex < 2) {
picker.columns[1].options = this.convertColumns(this.pickerbois[id].options[colSelectedIndex + 1]);
picker.forceUpdate(); //Here it should update the picker
}
});
this.picker_cancer = picker;
picker.present();
}
}
please try below, which fixed mine.
async showJetPicker(id) {
if (this.disabledis[id - 6] !== true) {
const opts: PickerOptions = {
cssClass: 'academy-picker',
buttons: [
...
],
columns: [
{
name: '1st row',
options: this.convertColumns(this.pickerbois[id].options[0])
},
{
name: '2nd row',
options: this.convertColumns(this.pickerbois[id].options[1])
}
]
};
const picker = await this.pickerCtrl.create(opts);
picker.addEventListener('ionPickerColChange', async (event: any) => {
const data = event.detail;
const colSelectedIndex = data.selectedIndex;
const colOptions = data.options;
let temColumns;
if (colSelectedIndex < 2) {
//picker.columns[1].options = this.convertColumns(this.pickerbois[id].options[colSelectedIndex + 1]);
//picker.columns[1].options = this.convertColumns(this.pickerbois[id].options[colSelectedIndex + 1]);
temColumns = this.convertColumns(this.pickerbois[id].options[colSelectedIndex + 1]);
picker.columns[1].options = JSON.parse(JSON.stringify(temColumns));
picker.forceUpdate(); //Here it should update the picker
}
});
this.picker_cancer = picker;
picker.present();
}
}
While using Vue Rangedate Picker I hit a roadblock trying to configure the prop Initial Range (the initial range that the component spits before the user even select any other range).
Have managed to setup other props like "caption" and "preset ranges" but the initRange is complaining about it not being an Object and being a function.
On my template:
<date-picker v-bind="datePicker" initRange="datePicker.presetRanges.last7Days" #selected="onDateSelected" i18n="EN" ></date-picker>
On my data:
datePicker: {
initRange: {
start: '1505862000000',
end: '1505872000000'
},
captions: {
title: 'Choose Date/Period',
ok_button: 'Apply'
},
presetRanges: {
today: function () {
const n = new Date()
const startToday = new Date(n.getFullYear(), n.getMonth(), n.getDate() + 1, 0, 0)
const endToday = new Date(n.getFullYear(), n.getMonth(), n.getDate() + 1, 23, 59)
return {
label: 'Today',
active: false,
dateRange: {
start: startToday,
end: endToday
}
}
},
last7Days: function () {
const n = new Date()
const weekAgo = new Date(n.getFullYear(), n.getMonth(), n.getDate() - 7, 24, 0)
const endToday = new Date(n.getFullYear(), n.getMonth(), n.getDate() + 1, 0, 0)
return {
label: 'Last 7 Days',
active: 'false',
dateRange: {start: weekAgo, end: endToday}
}
},
On my methods:
methods: {
onDateSelected: function (daterange) {
let that = this;
that.selectedDate = daterange;
let UnixStart = Math.round((Date.parse(that.selectedDate.start)));
let UnixEnd = Math.round((Date.parse(that.selectedDate.end)));
},
How can I solve this?
https://github.com/bliblidotcom/vue-rangedate-picker/issues/71
I leave the comments with this link. you will find it. should work for you.