How to drag and drop one div onto another in Cypress test using cypress-drag-drop package? - drag-and-drop

I am trying to write a Cypress test that drags & drops column A onto column B on this webpage - https://the-internet.herokuapp.com/drag_and_drop
I installed the #4tw/cypress-drag-drop package & added the following to my support/commands.js:
require("#4tw/cypress-drag-drop");
Here is my Cypress code:
cy.get("#column-a").drag("#column-b", { force: true });
The test passes, but the columns aren't behaving the same way visually as it does when I manually drag column A onto column B.
Instead, this is what appears on the browser in Cypress Explorer:
As you can see, column A is greyed out, as if it were dragged, but not dropped
Can someone please point out what I'm doing incorrectly?

You cannot, the library does not support the correct events for this page.
But you can do it using Cypress commands.
These are the events used on the page
col.addEventListener("dragstart", handleDragStart, false);
col.addEventListener("dragenter", handleDragEnter, false);
col.addEventListener("dragover", handleDragOver, false);
col.addEventListener("dragleave", handleDragLeave, false);
col.addEventListener("drop", handleDrop, false);
col.addEventListener("dragend", handleDragEnd, false);
This is the test that passes
// check initial order
cy.get('div.column')
.then($cols => [...$cols].map(col => col.innerText.trim()))
.should('deep.eq', ['A', 'B'])
const dataTransfer = new DataTransfer;
cy.get("#column-a")
.trigger('dragstart', {dataTransfer})
cy.get("#column-b")
.trigger('dragenter')
.trigger('dragover', {dataTransfer})
.trigger('drop', {dataTransfer})
cy.get("#column-a")
.trigger('dragend')
// check new order
cy.get('div.column')
.then($cols => [...$cols].map(col => col.innerText.trim()))
.should('deep.eq', ['B', 'A'])
// check drag opacity reverted back
cy.get("#column-a").should('have.css', 'opacity', '1')
cy.get("#column-b").should('have.css', 'opacity', '1')

Related

Ag-grid cell renderer doesn't update its rowIndex in real time?

I'm wondering how I can complete my cell renderer. The renderer adds a new row when clicking an icon, and removes it when clicked again. The problem is the invididual cell renderers row indexes do not change unless I hard refresh the entire grid (which then disables the state of the dropdown itself)
How can I complete this?
const insertIndex = params.rowIndex + 1;
this.eventListener = () => {
if (!this.eButton.classList.contains('open')) {
gridOptions.api.applyTransaction({add:[{step: this.dropdownValue, dropdownRow: true}], addIndex: insertIndex });
} else {
let rowData = [];
gridOptions.api.forEachNode(function (node) {
rowData.push(node);
});
let insertedRow = rowData[insertIndex];
gridOptions.api.applyTransaction({remove:[insertedRow.data]});
}
this.eButton.classList.toggle('open');
};
this.eButton.addEventListener('click', this.eventListener);
}
option 1: Hard refresh the grid, but I lose the 'open' status of the dropdown icon. How can i carry state over after a hard refresh?
option 2: Find a way that params.rowIndex is updated after applyTransaction.
Ive tried using the default rowGroup feature, however it hides all columns by default and I havent found a way to make it work the way I need.
The solution im after is a simple 'open/close' toggle for a single row underneath without affecting the other rows

React-Bootstap-Typeahead: Manually set custom display value in onChange() upon menu selection

In the onChange of React-Bootstrap-Typeahead, I need to manually set a custom display value. My first thought was to use a ref and do something similar to the .clear() in this example.
But although .clear() works, inputNode.value = 'abc' does not work, and I'm left with the old selected value from the menu.
onChange={option => {
typeaheadRef.current.blur(); // This works
typeaheadRef.current.inputNode.value = 'abc'; // This does not work (old value is retained)
}}
I also tried directly accessing the DOM input element, whose ID I know, and doing
var inputElement = document.querySelector('input[id=myTypeahead]');
inputElement.value = 'abc';
But that didn't work either. For a brief second, right after my changed value = , I do see the new display label, but then it's quickly lost. I think the component saves or retains the menu-selected value.
Note: I cannot use selected, I use defaultSelected. I have some Formik-related behavior that I've introduced, and it didn't work with selected, so I'm stuck with defaultSelected.
The only workaround I found is to re-render the Typeahead component (hide and re-show, from a blank state) with a new defaultSelected="abc" which is a one-time Mount-time value specification for the control.
I couldn't get selected=.. to work, I have a wrapper around the component which makes it fit into Formik with custom onChange and onInputChange and selected wasn't working with that.
So the simple workaround that works is, if the visibility of the Typeahead depends on some condition (otherwise it won't be rendered), use that to momentarily hide and re-show the component (a brand new repaint) with a new defaultSelected, e.g.
/* Conditions controlling the visibility of the Typeahead */
!isEmptyObject(values) &&
(values.approverId === null || (values.approverId !== null && detailedApproverUserInfo)
)
&&
<AsyncTypehead defaultSelected={{...whatever is needed to build the string, or the literal string itself...}}
..
// Given the above visibility condition, we'll hide/re-show the component
// The below will first hide the control in React's renders
setFieldValue("approver", someId);
setDetailedUserInfo(null);
// The below will re-show the control in React's renders, after a small delay (a fetch)
setDetailedUserInfo(fetchDetailedUserInfo());

How can I update data within Detail table and don't loose range selection and filters?

I have latest enterprise React agGrid table with Master/Detail grid. My data is fetched on the client every 5 seconds and then put immutably to the redux store. React grid component is using deltaRowDataMode={true} props and deltaRowDataMode: true in Detail options.
My master grid performs normally as expected: if I have range selected, grid would keep selection after the data updates, so would filters and visibility menu would be still opened. But Detail grid behaves differently: on data refresh selections are being removed, visibility menu closes and grid jumps if filters were changed.
I've read in docs that when I open Detail grid it's being created from scratch, but in my case I don't close Detail. Anywhere I've tried keepDetailRows=true flag, which solved problems with jumping on update and selection loss, but Detail grid doesn't update data now.
It seems there are only two possible options according to the docs https://www.ag-grid.com/javascript-grid-master-detail/#changing-data-refresh. The first one is a detail table redraws everytime a data in a master row changes and the second one is a detail row doesn't changes at all if a flag suppressRefresh is enabled. Strange decision, awful beahviour...
Update.
Hello again. I found a coupe of solutions.
The first one is to use a detailCellRendererParams in table's options and set suppressRefresh to true. It gives an opportunity to use getDetailGridInfo to get detail-table's api.
While the detail-table's refreshing is disabled, using detailGridInfo allows to set a new data to a detail-table.
useEffect(() => {
const api = gridApiRef;
api && api.forEachNode(node => {
const { detailNode, expanded } = node;
if (detailNode && expanded) {
const detailGridInfo = api.getDetailGridInfo(detailNode.id);
const rowData = detailNode.data.someData; // your nested data
if (detailGridInfo) {
detailGridInfo.api.setRowData(rowData);
}
}
});
}, [results]);
The second one is to use a custom cellRenderer, wicth is much more flexible and allows to use any content inside a cellRenderer.
In table's options set detailCellRenderer: 'yourCustomCellRendereForDetailTable.
In yourCustomCellRendereForDetailTable you can use
this.state = {
rowData: [],
}
Every cellRenderer has a refresh metod which can be used as follow.
refresh(params) {
const newData = [ ...params.data.yourSomeData];
const oldData = this.state.rowData;
if (newData.length !== oldData.length) {
this.setState({
rowData: newData,
});
}
if (newData.length === oldData.length) {
if (newData.some((elem, index) => {
return !isEqual(elem, oldData[index]);
})) {
this.setState({
rowData: newData,
});
}
}
return true;
}
Using method refresh this way gives a fully customizable approach of using a detailCellRenderer.
Note. To get a better performance with using an immutable data like a redux it needs to set immutableData to true in both main and detail tables.

ag-grid programmatically selecting row does not highlight

Using Angular 4 (typescript), I have some code like below using ag-grid 12.0.2. All I'm trying to do is load my grid and automatically (programmatically) select the first row.
:
this.gridOptions = ....
suppressCellSelection = true;
rowSelection = 'single'
:
loadRowData() {
this.rowData = [];
// build the row data array...
this.gridOptions.api.setRowData(this.rowData);
let node = this.gridOptions.api.getRowNode(...);
// console logging here shows node holds the intended row
node.setSelected(true);
// console logging here shows node.selected == true
// None of these succeeded in highlighting the first row
this.gridOptions.api.redrawRows({ rowNodes: [node] });
this.gridOptions.api.redrawRows();
this.gridOptions.api.refreshCells({ rowNodes: [node], force: true });
First node is selected but the row refuses to highlight in the grid. Otherwise, row selection by mouse works just fine. This code pattern is identical to the sample code here: https://www.ag-grid.com/javascript-grid-refresh/#gsc.tab=0 but it does not work.
Sorry I am not allowed to post the actual code.
The onGridReady means the grid is ready but the data is not.
Use the onFirstDataRendered method:
<ag-grid-angular (firstDataRendered)="onFirstDataRendered($event)">
</ag-grid-angular>
onFirstDataRendered(params) {
this.gridApi.getDisplayedRowAtIndex(0).setSelected(true);
}
This will automatically select the top row in the grid.
I had a similar issue, and came to the conclusion that onGridReady() was called before the rows were loaded. Just because the grid is ready doesn't mean your rows are ready.(I'm using ag-grid community version 19) The solution is to setup your api event handlers after your data has loaded. For demonstration purposes, I'll use a simple setTimeout(), to ensure some duration of time has passed before I interact with the grid. In real life you'll want to use some callback that gets fired when your data is loaded.
My requirement was that the handler resizes the grid on window resize (not relevant to you), and that clicking or navigating to a cell highlights the entire row (relevant to you), and I also noticed that the row associated with the selected cell was not being highlighted.
setUpGridHandlers({api}){
setTimeout(()=>{
api.sizeColumnsToFit();
window.addEventListener("resize", function() {
setTimeout(function() {
api.sizeColumnsToFit();
});
});
api.addEventListener('cellFocused',({rowIndex})=>api.getDisplayedRowAtIndex(rowIndex).setSelected(true));
},5000);
}
Since you want to select the first row on page load, you can do onething in constructor. But your gridApi, should be initialized in OnGridReady($event) method
this.gridApi.forEachNode((node) => {
if (node.rowIndex === 0) {
node.setSelected(true);
}
It's setSelected(true) that does this.
We were using MasterDetail feature, its a nested grid and on expanding a row we needed to change the selection to expanded one.
Expanding a row was handled in
detailCellRendererParams: {
getDetailRowData: loadNestedData,
detailGridOptions: #nestedDetailGridOptionsFor('child'),
}
and withing loadNesteddata, we get params using we can select expanded row as
params.node.parent.setSelected(true)
Hope this helps.

AgGrid: add/remove rows, in Grid already having grouping/sorting/filtering applied

To be able to add/remove rows, in a grid which has already applied grouping/sorting/filtering, without reapplying the grouping/sorting/filtering on this row add/remove. It is that user can work on a group, updating some new values/adding new record which may still not belong to same group of grouping is reapplied, but we do not want to reapply grouping as user input will get scattered in grid, and hard to review for user.
What we tried with ("ag-grid": "^10.0.0", "ag-grid-enterprise": "^10.0.0", "ag-grid-react": "^10.0.0",)
Add a new row on some event/ btn click, in an already rendered grid with some data on startup, using the below(Skipping the refresh):
Let newItems = [];
let newItem= {
id: 'SomethingNewId'+Math.random(),
qty: 300,
owner:"Deepak Kumar SHarma",
isUpdated: true
};
newItems.push(newItem); this.gridOptions.api.insertItemsAtIndex(4, newItems, true); // Saying do not refresh the grid
After, above step, I see by iterating that a rowNode has been added, but row was not visible in grid. Then tried to refresh this newly added row only, so that it is visible in grid, expecting it to be without reapply this filter/sorting/filtering:
let nodes = [];
this.gridOptions.api.forEachNode( (node, index) => {
if (node.data!==null && node.data!==undefined && node.data.isUpdated===true) {
nodes.push(node);
}
});
this. gridOptions.api.refreshRows(nodes);
But still do not see the newly added row in grid.
If I just change the sorting/filtering/grouping manually again to some new setting, I see the new row in grid.