How to drag and drop in Cypress.io - javascript

I am testing Trello and trying to drag the last list and then drop it into a penultimate column, but the test is not working without ".wait". It would be really helpful if someone could advise about the potential issue here because I prefer to avoid using ".wait". There are no errors throwing, but still, the drag and drop is not happening without ".wait".
describe("Moving list", () => {
it("Waiting For Accept list should be moved from last column to the penultimate column", () => {
cy.get("#board")
.children(".js-list")
.should("have.length", 4)
.and("be.visible");
cy.get(":nth-child(4) > .list")
.should("be.visible")
.and("contain", "Waiting For Accept")
cy.get(":nth-child(4) > .list").trigger("mousedown", {
which: 1
});
cy.get("#board > div:nth-child(2) > .list")
.trigger("mousemove");
cy.get("#board > div:nth-child(3) > .list")
.trigger("mousemove")
.trigger("mouseup");
cy.get(":nth-child(3) > .list")
.should("contain", "Waiting For Accept");
});
});
See image
See image

That doesn't work out of the box, the logged issue for that is https://github.com/cypress-io/cypress/issues/845 . But in that same ticket is also a work around available using the native drag and drop API with draggable attribute on draggable elements:
Create a custom command
Cypress.Commands.add("dragTo", { prevSubject: "element" }, (subject, targetEl) => {
cy.wrap(subject).trigger("dragstart");
cy.get(targetEl).trigger("drop");
}
);
In the testscript you can use:
cy.get(".source").dragTo(".target");

You can use the Cypress drap and drop plugin
https://github.com/4teamwork/cypress-drag-drop

Finally I resolved this issue by using "cy.request"
https://docs.cypress.io/api/commands/request.html#Syntax
describe("Moving list", () => {
it("Waiting For Accept list should be moved from last column to the penultimate column", () => {
cy.request("https://trello.com/b/9lfzKIRu/trello-tests").then(response => {
expect(response.status).to.eq(200);
});
cy.get("#board > div:nth-child(4) > .list").trigger("mousedown", {
which: 1
});
cy.get("#board > div:nth-child(2) > .list").trigger("mousemove");
cy.get("#board > div:nth-child(3) > .list")
.trigger("mousemove")
.trigger("mouseup");
cy.get(":nth-child(3) > .list").should("contain", "Waiting For Accept");
});
});

Best solution is to use https://github.com/4teamwork/cypress-drag-drop it supports dragging to top and bottom too.
cy.get('#placeholder-3').drag('#placeholder-2', { position: 'top', })

I solved it by downgrading my drag and drop version from 2.1.0 to 1.8.0
https://www.npmjs.com/package/#4tw/cypress-drag-drop

Related

react-table v6 multiple tables issues

Good day
I have 2 tables side by side and I want them to act like one table with borders and scroll independently and, when my cursor goes over the 1 row in the first table I want to highlight 1 row in the second table
However this is done internally by react-table.
Any solutions how can I implement highlighting same row from both tables?
And I wonder about simultaneous vertical scroll, are there also any solutions in react way?
Don't want to add event listeners to tables like this
firstTableBody.addEventListener('scroll', e => {
const tablePosition = e.target.scrollTop;
firstTableBody.scrollTop = tablePosition;
});
https://stackblitz.com/edit/react-edgooj?file=src/App.js
ok, I did a highlight for all tables at once
For every table I add
getTrProps={rowProps}
and in rowProps I implement
const rowProps = useCallback((state, rowInfo) => {
if (rowInfo && rowInfo.row) {
const isRowHovered = rowInfo.index === hoveredRow;
return {
style: {
background: isRowHovered ? '#D1DDE1' : ''
},
onMouseEnter: () => {
setHoveredRow(rowInfo.index)
},
onMouseLeave: () => {
setHoveredRow(null)
}
}
}
}, [hoveredRow]);
I really dunno why there is always such a poor documentation in all those libs

How do i get cypress element fixed when detached from the Dom?

Cypress requires elements be attached in the DOM to interact with them.
The previous command that ran was:
cy.get()
This DOM element likely became detached somewhere between the previous and current command.
Common situations why this happens:
Your JS framework re-rendered asynchronously
Your app code reacted to an event firing and removed the element
You typically need to re-query for the element or add 'guards' which delay Cypress from running new commands.
Here is the block of code I am trying to run.
}),
it('Add-Income', ()=> {
cy.get('.add_btn').click()
cy.get(':nth-child(1) > .input_container > input').type('45000')
cy.get(':nth-child(2) > select').select('4')
cy.get(':nth-child(3) > select').select('32')
cy.get(':nth-child(4) > .input_container > input').invoke('removeAttr','type').type('12-12-1990{enter}')
//cy.get('.transaction_btn > button').click()
cy.get('.title > span').click({force: true})
}),
it('Add-Expenditure', ()=> {
cy.get('.add_btn').click()
cy.get('.overlay_card > :nth-child(4)').click()
cy.get(':nth-child(1) > .input_container > input').type('45000')
cy.get(':nth-child(2) > select').select('4')
cy.get(':nth-child(3) > select').select('32')
cy.get(':nth-child(4) > .input_container > input').invoke('removeAttr','type').type('12-12-1990{enter}')
//cy.get('.transaction_btn > button').click()
})

Angular drag-drop icons to canvas

I need help with angular drag and drop. It's like I need to drag an icon to a canvas.
I had gone through many examples and this is the example I have reached. when I drag that object the copy of the object should be moved. I had looked at many examples, please anyone help.
our "fields" are object with text,top and left. So, you can create a function
changePosition(event:CdkDragEnd<any>,field)
{
console.log(field)
field.top=+field.top.replace('px','')+event.distance.y+'px'
field.left=+field.left.replace('px','')+event.distance.x+'px'
}
And you call in the .html
<div *ngFor="let field of fields;" cdkDrag (cdkDragEnded)="changePosition($event,field)"
style="position:absolute;z-index:10" [style.top]="field.top" [style.left]="field.left">
{{field.text}}
</div>
Updated the problem, as Ananthakrishna let me know is that you can drag out of the "dop-zone" one element in drop zone
We need use the event cdkDragDropped
<div *ngFor="let field of fields;" cdkDrag
(cdkDragDropped)="changePosition($event,field)"
style="position:absolute;z-index:10"
[style.top]="field.top"
[style.left]="field.left">
{{field.text}}
</div>
And, in our function changePosition "check" if is droppend inside. I use getBoundingClientRect of the elements relateds:
changePosition(event:CdkDragDrop<any>,field)
{
const rectZone=this.dropZone.nativeElement.getBoundingClientRect()
const rectElement=event.item.element.nativeElement.getBoundingClientRect()
let top=+field.top.replace('px','')+event.distance.y
let left=+field.left.replace('px','')+event.distance.x
const out=top<0 || left<0 ||
(top>(rectZone.height-rectElement.height)) ||
(left>(rectZone.width-rectElement.width))
if (!out) //If is inside
{
field.top=top+'px'
field.left=left+'px'
}
else{ //we can do nothing
this.fields=this.fields.filter(x=>x!=field) //or eliminate the object
}
}
See the forked stackblitz
It's very easy to achieve your goal with ng-dnd. You can check the examples and have a try.
Making a DOM element draggable
<div [dragSource]="source">
drag me
</div>
constructor(private dnd: DndService) { }
source = this.dnd.dragSource("DRAGME", {
beginDrag: () => ({ name: 'Jones McFly' }),
// other DragSourceSpec methods
});
Making a DOM element into a drop target
<div [dropTarget]="target">
drop on me
</div>
constructor(private dnd: DndService) { }
target = this.dnd.dropTarget("DRAGME", {
drop: monitor => {
console.log('dropped an item:', monitor.getItem()); // => { name: 'Jones McFly' }
}
})

How do i select just one expanded panel in Vuejs (vuetify) using the index?

i am using Vuetify in my Vue.js project and i have a problem with the expanded panels. I am doing a v-for over an array of objects and then i am putting the expanded panels with external control. External control means a button separated from the panel that expand the panel and closes it.
my code at the moment is this:
<v-btn #click="showHidePanel(index)">
Button
</v-btn>
<v-expansion-panels v-model="panel>
<v-expansion-panel>
<v-expansion-panel-content>
I am expanded!
</v-expansion-panel-content>
</v-expansion-panel>
</v-expansion-panels>
And now my script:
data() {
return {
panel: [],
}
}
//
showHidePanel(data) {
if (this.panel.length == 0) {
this.panel = [...Array(data).keys()].map((k, i) => i);
} else {
this.panel = [];
}
},
As the 'panel' variable is an array, all panels are expanding but i have tried with an integer and it doesnt worked too.
Can anyone help me please?
It looks like you've copied your showHidePanel function from the docs. That function is designed to toggle all or none of the panels. To toggle just the panel at index use:
showHidePanel(index) {
if (this.panel.indexOf(index)) {
this.panel = this.panel.filter(i => i!=index);
} else {
this.panel.push(index);
}
},

intro.js show and hide data-hint

I want to have a button that can turn on and off the 'hints' function in intro.js.
I have a working version to show and then hide but the show only works once. How can I get it to work repeatedly? This functionality works for the standard data-intro but not for data-hint.
<div class="jumbotron">
<h1 id='step1'>Hints</h1>
<p class="lead">Adding hints using JSON + callbacks</p>
<a id='step2' class="btn btn-large btn-success" href="javascript:void(0);">Add hints</a>
</div>
function addHints(){
intro = introJs();
intro.setOptions({
hints: [
{
element: document.querySelector('#step1'),
hint: "This is a tooltip.",
hintPosition: 'top-middle'
},
{
element: '#step2',
hint: 'More features, more fun.',
position: 'left'
},
{
element: '#step4',
hint: "<b>Another</b> step.",
hintPosition: 'top-middle'
}
]
});
intro.onhintsadded(function() {
console.log('all hints added');
});
intro.onhintclick(function(hintElement, item, stepId) {
console.log('hint clicked', hintElement, item, stepId);
});
intro.onhintclose(function (stepId) {
console.log('hint closed', stepId);
});
intro.addHints();
}
$(function() {
$('#step2').click(function(){
if ( $('#step2').hasClass('clicked') ) {
introJs().hideHints();
$('#step2').removeClass('clicked');
} else {
addHints();
$('#step2').addClass('clicked');
}
});
});
Instead of using hideHints intro.js API method just remove the div block of intro.js from DOM:
var introDiv = document.getElementsByClassName("introjs-hints")[0];
introDiv.parentNode.removeChild(introDiv);
(You can do the same thing with jQuery if you want to).
When the div is removed from DOM, just initialize hints once again as you do with your addHints method when you want to show hints and it'll work.
Instead of deleting the div block with javascript. You can use .removeHints()
This function is part of intro.js, but is not included in the documentation.
Perhaps a bit hacky, but this works for me...
First, put your hints into their own variable:
hints = [{...}, ...]
then, reset your hints in the intro options
intro.onhintclose(function(stepId) {
if (document.querySelectorAll('.introjs-hidehint').length === hints.length) {
intro.setOptions({hints: hints})
}
})
The hidden hints are given a class of introjs-hidehint, and document.querySelectorAll will return all of them in an array. Once that array is the same size as your hints array, reset your hints in your intro options and that will reset all your hints so you can show them all again.
Here's a more complete example that also allows:
(a) toggling hints on/off by clicking a button (located on a nav bar so used across multiple pages).
(b) once all hints have been clicked, the hints div gets removed so that clicking show hints button will again actually...show hints...
(c) allow you to store hints for multiple pages in a single json object array (re: nav bar).
var jquery = require('jquery');
var introJs = require('intro.js');
* ===========================================================================
* define onclick of hints button
* =========================================================================*/
jquery('#hints_button').on('click', function() {
if (document.getElementsByClassName('introjs-hints').length == 0){
addSomeHints();
}
else {
destroyHints();
};
});
/* ===========================================================================
* Add hints using the IntroJS library
* =========================================================================*/
/* define hints */
var theHints = [
{
element: document.querySelector('#step1'),
hint: "This is a tooltip.",
hintPosition: 'top-middle'
},
{
element: '#step2',
hint: 'More features, more fun.',
hintPosition: 'left'
},
{
element: '#step4',
hint: "<b>Another</b> step.",
hintPosition: 'top-middle'
}
];
/* generate hints with introjs */
function addSomeHints() {
intro = introJs();
intro.setOptions({
hints: theHints
});
intro.onhintclose(function (stepId) {
var remaining_hints = all_hints - document.getElementsByClassName("introjs-hidehint").length;
if (remaining_hints == 0) {
destroyHints();
};
});
/* add hints */
intro.addHints();
/* store number of hints created */
var all_hints = document.getElementsByClassName('introjs-hint').length;
};
/* remove hints div */
function destroyHints() {
var hintsDiv = document.getElementsByClassName("introjs-hints")[0]
hintsDiv.parentNode.removeChild(hintsDiv);
};
... hopefully this saves someone the 20 minutes it took me to piece together the answers and adapt it for what seems like a super common use case.

Categories