Testing-library unable to find rc-menu - antd

I'm trying to implement integration tests on our React frontend which uses Ant design. Whenever we create a table, we add an action column in which we have a Menu and Menu Items to perform certain actions.
However, I seem unable to find the correct button in the menu item when using react-testing-library. The menu Ant design uses is rc-menu and I believe it renders outside of the rendered component.
Reading the testing-library documentation, I've tried using baseElement and queryByRole to get the correct DOM element, but it doesn't find anything.
Here's an example of what I'm trying to do. It's all async since the table has to wait on certain data before it gets filled in, just an FYI.
Tried it on codesandbox
const row = await screen.findByRole('row', {
name: /test/i
})
const menu = await within(row).findByRole('menu')
fireEvent.mouseDown(menu)
expect(queryByRole('button', { name: /delete/i })).toBeDisabled()
the menu being opened with the delete action as menu item

I had a same issue testing antd + rc-menu component. Looks like the issue is related to delayed popup rendering. Here is an example how I solved it:
jest.useFakeTimers();
const { queryByTestId, getByText } = renderMyComponent();
const nav = await waitFor(() => getByText("My Menu item text"));
act(() => {
fireEvent.mouseEnter(nav);
jest.runAllTimers(); // ! <- this was a trick !
});
jest.useRealTimers();
expect(queryByTestId("submenu-item-test-id")).toBeTruthy();

Related

How to inject a script (using playwright) that will create an unique attribute on every button of the page?

I am scrapping a site. I am using this code:
const SELECTOR = 'button:has-text("ClickMe") >> nth=1'
const loc = this.page.locator(SELECTOR)
await loc.click()
and inspector shows this:
...
done scrolling
performing click action
<div class="blabla">…</div> intercepts pointer events
I do not understand why this is happening. Very frustrating, what is this interception (intercept pointer event, done by a parent node) ? It works well when I use the console with:
.locator('button:has-text("ClickMe") >> nth=1').element.click()
How could I bypass/solve this interception ? I have thought an idea, do you know if it s possible to add an incremental id, such as my-data-id, on every buttons inside the page... For example, after the DOM has loaded, and before the script executes, I inject a script that will add this my-data-id attribute. So that, I can select directly the buttons I want to click with the unique attribute and I don t suffer anymore looking for the good selector:
[my-data-id=142] for exemple, will select the button #142
[my-data-id=143] for exemple, will select the button #143
You can use force: true to disable actionability checks and more precisely to avoid this error.
const SELECTOR = 'button:has-text("ClickMe") >> nth=1'
const loc = this.page.locator(SELECTOR)
await loc.click({force: true})
See issue log, try adding the environment setting
// playwright.config.ts
process.env.PLAYWRIGHT_NO_LAYOUT_SHIFT_CHECK = '1';
const config = {
// ...
};
export default config;

Umbraco 7 - Custom Menu Items & Trees, How does navigation work?

I have a custom section, with a custom tree.
I'm having a bit of trouble understanding how you set the correct behavior when:
You click a node in your tree to edit it.
You click a menu item on a node like "Create"
In my solution I'm using the same view to edit and create a record.
In my tree this is how a node is generated.
var routeToView = "rewards/rewardsTree/editcampaign/campaign-" + campaign.Id.ToString();
var campaignNode = CreateTreeNode("campaign-" + campaign.Id.ToString(), id.Split('-')[1], queryStrings, campaign.CampaignName, "icon-folder color-yellow", true, routeToView);
This is producing the route I want: (the name of my html file is editcampaign.html) and it is also passing "campaign-6"
/umbraco#/rewards/rewardsTree/editcampaign/campaign-6
When a user clicks the create 'menu Item' on the node - I want to send them to the same URL but just with a diffrent Id for example:
umbraco#/rewards/rewardsTree/editcampaign/brand-1
and I don't want it to pop up out of the side
This is what I have tried so far:
//This finds the view, but it comes up in a dialog also how do I pass the Id (brand-1)
MenuItem mi = new MenuItem("editcampaign", "Create Campaign");
menuItemCollection.Items.Add(mi);
//Also Tried this finds puts a whole another umbraco UI inside a dialog
mi.LaunchDialogView("#rewards/rewardsTree/editcampaign/brand-1", "TITLE GOES HERE");
Can anyone point me to the fullest documentation for Menu's trees and navigation around the back office in general?
I believe there is an option to set view path on the "Create" menu item, which makes it open normally? Also, wouldn't it make more sense to have your path like /view/path/here/id ? Then when you create a new item just send 0 as id. Umbrangular on Github is a project with great examples of custom sections and views.
EDIT: Here's an example
protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings)
{
var menu = new MenuItemCollection();
MenuItem createCategory = new MenuItem("createcategory", "Create Category");
createCategory.AdditionalData.Add("ParentCategoryId", id);
createCategory.NavigateToRoute("/path/to/view/category/0");
createCategory.Icon = "add";
menu.Items.Add(createCategory);
return menu;
}

Drag and drop in AngularJS

I am trying to implement Drag and Drop using c0deformer's jQuery UI implementation (see here: http://codef0rmer.github.io/angular-dragdrop/#/) The dragging part works fine, but I can't seem to get the functionality I am after in terms of the drop. In this application, I want to be able to drop the draggable items anywhere within a target div, i.e., I don't want the destination scope to be limited to a list-type structure (or a set of repeated divs). Mainly this is because the user will be dragging items on the fly and there will be no way of knowing how many items the user will drag and drop in advance.
I have scoured the web and cannot find an example in Angular that uses drag and drop without effectively dragging from one list to another list. Can this be done? If so, I am not sure how I would appropriately update the scope after an item has been dragged. In this example code below, the dropped items are pushed into the scope of a second list and the new scope is applied. Ideally, the scope of the dropped items is the target div I mentioned above. I'm really new to Angular, so any advice is immensely appreciated.
Snippet from c0deformer:
app.directive('droppable', function($compile) {
return {
restrict: 'A',
link: function(scope,element,attrs){
//This makes an element Droppable
element.droppable({
drop:function(event,ui) {
var dragIndex = angular.element(ui.draggable).data('index'),
dragEl = angular.element(ui.draggable).parent(),
dropEl = angular.element(this);
console.log(dropEl);
if (dragEl.hasClass('list1') && !dropEl.hasClass('list1') && reject !== true) {
scope.list2.push(scope.list1[dragIndex]);
scope.list1.splice(dragIndex, 1);
} else if (dragEl.hasClass('list2') && !dropEl.hasClass('list2') && reject !== true) {
scope.list1.push(scope.list2[dragIndex]);
scope.list2.splice(dragIndex, 1);
}
scope.$apply();
}
});
}
};
});
I recently created an angular directive for drag and drop that doesn't rely on jquery-ui. It uses the html5 drag and drop api. It also doesn't have any requirements on the format of the data to be dragged or dropped, it simply sets up a hook for you to be notified when one element is dragged onto another.
Post here: http://jasonturim.wordpress.com/2013/09/01/angularjs-drag-and-drop/
Demo here: http://logicbomb.github.io/ng-directives/drag-drop.html

Chosen not working in jquery dialog on reloading mvc partial

I am loading two MVC Partial Views in jQuery UI dialog using following code for editing and adding a record:
$.get(url, function(data)
{
dialogDiv.html(data);
var $form = $(formid);
$form.unbind();
$form.data("validator", null);
$.validator.unobtrusive.parse(document);
var dat = $form.data("unobtrusiveValidation");
var opts = dat ? dat.options || '' : '';
$form.validate(opts);
//THIS FUNCTION ADDS PLUGINS ETC.
runEditCreateStartScripts();
dialogDiv.dialog('open');
});
Following is the function that wires-up chosen functionality.
function runEditCreateStartScripts(){
$("select.chzn-select").chosen(
{
no_results_text: "no match",
allow_single_deselect: true
});
}
Everything is perfect on first call. After opening one dialog say edit a few times everything is broken. There is only hyperlink available in place of chosen stuff. This also happens if I open one dialog say add and then second dialog. The bindings and other functionality from first one (add) is gone.
Any insights on why this might be happening?
The problem that caused my issue was that the modals I was loading via AJAX had inputs with the SAME ID as an input field that was already on the page (using Django that has generic ID generators for model fields). This caused collision between the two inputs when re-triggering .chosen() on the selector. When I made the ID fields unique, all worked as expected.
Hope this would have helped.

How to delete a jqgrid row without reloading the entire grid?

I have a webpage with multiple jqgrids each with inline editing enabled, "action" column (edit icons) enabled and pager disabled. I need to handle the delete event for each row so that I can process the delete without reloading server-side data. I've looked at the approach mentioned in jqGrid Delete a Row and it's very helpful except I have two questions that are stumping me -
Are there more details around the rp_ge parameter in the delOptions.onClickSubmit event?
My column has the delOptions set as this -
delOptions: {onclickSubmit: function(rp_ge, rowid) {return onRowDelete(rp_ge,rowid);}},processing:true }},
Is there a way to get the grid id from within that event? I'd like to have a generic function that I can use to handle delete events from all the grids on the page. The rp_ge parameter has a gbox which sometimes contains the grid id appended? But I have no idea what it is since i'm not able to figure out when it's populated, when it's not.
function onRowDelete(rp_ge, rowid) {
//hardcoded grid id.. don't like it.
var gridid = '#Grid_X';
//what is this gbox?? can i get grid id predictable from it?
//var gridid = rp_ge.gbox.replace("#gbox_", "");
var grid = $('#Grid_X');
rp_ge.processing = true;
var result = grid.delRowData(rowid);
if (result) {
$("#delmod" + grid[0].id).hide();
}
return true;
}
In the jqGrid Delete a Row approach, the code $("#delmod"+grid[0].id).hide(); is hiding the popup delete confirmation dialog manually. What I noticed is that when the dialog pops-up, jqgrid de-emphasizes the background page (makes it light greyish). But after the popup is manually closed (hidden actually?), the background remains de-emphasized. So it looks like the page doesn't have focus (or even disabled). Any way this can be fixed? This can also be seen on the demo that Oleg wrote.
Any help would be appreciated.
(PS - I would've commented on the same post but I don't have enough points to comment on someone else's answer yet.)
In answer to your second point.
Several examples by Oleg such as this one have the following modification.
$("#delmod" + grid[0].id).hide();
is replaced with
$.jgrid.hideModal(
"#delmod"+grid_id,
{gb:"#gbox_"+grid_id,jqm:rp_ge.jqModal,onClose:rp_ge.onClose}
);
This will return focus after the delete operation.

Resources