Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Performing a Click() on a Table Cell does not fire the onclick method in the page #399

Closed
Smurf-IV opened this issue May 20, 2021 · 49 comments · Fixed by #501
Closed

Performing a Click() on a Table Cell does not fire the onclick method in the page #399

Smurf-IV opened this issue May 20, 2021 · 49 comments · Fixed by #501

Comments

@Smurf-IV
Copy link

Describe the bug
Blazorise Datagrid (Not important, just what it is) Create a table, that has onclick overrides for each cell.

These are then routed through to allow detection of RowChnaged, RowSelecterd, RowClicked, etc methods.

What is needed is a way to simulate a cell click and for those methods to fire to be able to detect changes in the page selected item.

Example:
None of these work.

                var cellLast = cut.FindAll("table tr td:nth-child(1)");
                await ClickAsync(cellLast[cellLast.Count-1]);

                //Other ways
                //var table = ((AngleSharpWrappers.Wrapper<AngleSharp.Dom.IElement>)cut.Find(@"table")).WrappedElement as IHtmlTableElement;
                //var lastRow = table.Rows.Last();
                //await ClickAsync(lastRow.Cells[0]);
                //await ClickAsync(lastRow.Cells[1]);
                //lastRow.Cells[0].Click();
                //lastRow.Cells[1].Click();
                //lastRow.Cells[0].DoFocus();
                //lastRow.Cells[0].DoClick();
                //lastRow.Cells[1].DoFocus();
                //lastRow.Cells[1].DoClick();
<div class="container-xl"><h2>
        Please select a Project (and model) to use: &nbsp; &nbsp;
        <button id="0HM8RI4IJMBEF" type="button" class="btn disabled" disabled blazor:onclick="1" blazor:elementReference="">Use Selected</button></h2>
    <div class="table-responsive"><table id="0HM8RI4IJMBEG" class="table table-striped table-bordered table-hover table-sm b-datagrid" blazor:ondrag="2" blazor:ondragend="3" blazor:ondragenter="4" blazor:ondragleave="5" blazor:ondragover="6" blazor:ondragstart="7" blazor:ondrop="8" blazor:elementReference=""><thead blazor:ondrag="9" blazor:ondragend="10" blazor:ondragenter="11" blazor:ondragleave="12" blazor:ondragover="13" blazor:ondragstart="14" blazor:ondrop="15"><tr blazor:onclick="23" blazor:ondrag="24" blazor:ondragend="25" blazor:ondragenter="26" blazor:ondragleave="27" blazor:ondragover="28" blazor:ondragstart="29" blazor:ondrop="30"><th scope="col" style="cursor: pointer" blazor:onclick="31" blazor:ondrag="32" blazor:ondragend="33" blazor:ondragenter="34" blazor:ondragleave="35" blazor:ondragover="36" blazor:ondragstart="37" blazor:ondrop="38">Project Name</th><th scope="col" style="cursor: pointer" blazor:onclick="39" blazor:ondrag="40" blazor:ondragend="41" blazor:ondragenter="42" blazor:ondragleave="43" blazor:ondragover="44" blazor:ondragstart="45" blazor:ondrop="46">Created UTC</th><th scope="col" style="cursor: pointer; width: 150px" blazor:onclick="47" blazor:ondrag="48" blazor:ondragend="49" blazor:ondragenter="50" blazor:ondragleave="51" blazor:ondragover="52" blazor:ondragstart="53" blazor:ondrop="54"></th></tr></thead>
        <tbody blazor:ondrag="16" blazor:ondragend="17" blazor:ondragenter="18" blazor:ondragleave="19" blazor:ondragover="20" blazor:ondragstart="21" blazor:ondrop="22"><tr class="table-row-selectable" blazor:onclick="71" blazor:ondrag="72" blazor:ondragend="73" blazor:ondragenter="74" blazor:ondragleave="75" blazor:ondragover="76" blazor:ondragstart="77" blazor:ondrop="78"><td class="text-nowrap" blazor:onclick="135" blazor:ondrag="136" blazor:ondragend="137" blazor:ondragenter="138" blazor:ondragleave="139" blazor:ondragover="140" blazor:ondragstart="141" blazor:ondrop="142">571bde52-2bbd-4994-b232-fbe118737e27</td><td blazor:onclick="143" blazor:ondrag="144" blazor:ondragend="145" blazor:ondragenter="146" blazor:ondragleave="147" blazor:ondragover="148" blazor:ondragstart="149" blazor:ondrop="150">&quot;2021-05-20T12:47:09.075698300Z&quot;</td><td style="width: 150px" blazor:onclick="151" blazor:ondrag="152" blazor:ondragend="153" blazor:ondragenter="154" blazor:ondragleave="155" blazor:ondragover="156" blazor:ondragstart="157" blazor:ondrop="158"><button id="0HM8RI4IJMBEU" type="button" class="btn btn-primary disabled no-padding" disabled blazor:onclick="327" blazor:elementReference="bb9df550-76b0-4187-a5af-a2a046e98136">                        Archive Designs
                    </button></td></tr><tr class="table-row-selectable" blazor:onclick="79" blazor:ondrag="80" blazor:ondragend="81" blazor:ondragenter="82" blazor:ondragleave="83" blazor:ondragover="84" blazor:ondragstart="85" blazor:ondrop="86"><td class="text-nowrap" blazor:onclick="159" blazor:ondrag="160" blazor:ondragend="161" blazor:ondragenter="162" blazor:ondragleave="163" blazor:ondragover="164" blazor:ondragstart="165" blazor:ondrop="166">a5eb5e2f-ea25-41a6-b7a4-9da57fdf0512</td><td blazor:onclick="167" blazor:ondrag="168" blazor:ondragend="169" blazor:ondragenter="170" blazor:ondragleave="171" blazor:ondragover="172" blazor:ondragstart="173" blazor:ondrop="174">&quot;2021-05-20T12:47:10.053221500Z&quot;</td><td style="width: 150px" blazor:onclick="175" blazor:ondrag="176" blazor:ondragend="177" blazor:ondragenter="178" blazor:ondragleave="179" blazor:ondragover="180" blazor:ondragstart="181" blazor:ondrop="182"><button id="0HM8RI4IJMBEV" type="button" class="btn btn-primary disabled no-padding" disabled blazor:onclick="328" blazor:elementReference="073052b8-ba77-4d5b-b74e-84bef7175dd6">                        Archive Designs
                    </button></td></tr><tr class="table-row-selectable" blazor:onclick="87" blazor:ondrag="88" blazor:ondragend="89" blazor:ondragenter="90" blazor:ondragleave="91" blazor:ondragover="92" blazor:ondragstart="93" blazor:ondrop="94"><td class="text-nowrap" blazor:onclick="183" blazor:ondrag="184" blazor:ondragend="185" blazor:ondragenter="186" blazor:ondragleave="187" blazor:ondragover="188" blazor:ondragstart="189" blazor:ondrop="190">a0754d47-5df9-4a4e-84fd-9d7487495470</td><td blazor:onclick="191" blazor:ondrag="192" blazor:ondragend="193" blazor:ondragenter="194" blazor:ondragleave="195" blazor:ondragover="196" blazor:ondragstart="197" blazor:ondrop="198">&quot;2021-05-20T12:47:38.612592200Z&quot;</td><td style="width: 150px" blazor:onclick="199" blazor:ondrag="200" blazor:ondragend="201" blazor:ondragenter="202" blazor:ondragleave="203" blazor:ondragover="204" blazor:ondragstart="205" blazor:ondrop="206"><button id="0HM8RI4IJMBF0" type="button" class="btn btn-primary disabled no-padding" disabled blazor:onclick="329" blazor:elementReference="1e15f046-092b-4967-b192-5cf367085599">                        Archive Designs
                    </button></td></tr><tr class="table-row-selectable" blazor:onclick="95" blazor:ondrag="96" blazor:ondragend="97" blazor:ondragenter="98" blazor:ondragleave="99" blazor:ondragover="100" blazor:ondragstart="101" blazor:ondrop="102"><td class="text-nowrap" blazor:onclick="207" blazor:ondrag="208" blazor:ondragend="209" blazor:ondragenter="210" blazor:ondragleave="211" blazor:ondragover="212" blazor:ondragstart="213" blazor:ondrop="214">09b9941e-d9c6-4b35-9e0c-d392d47df140</td><td blazor:onclick="215" blazor:ondrag="216" blazor:ondragend="217" blazor:ondragenter="218" blazor:ondragleave="219" blazor:ondragover="220" blazor:ondragstart="221" blazor:ondrop="222">&quot;2021-05-20T12:47:40.541344Z&quot;</td><td style="width: 150px" blazor:onclick="223" blazor:ondrag="224" blazor:ondragend="225" blazor:ondragenter="226" blazor:ondragleave="227" blazor:ondragover="228" blazor:ondragstart="229" blazor:ondrop="230"><button id="0HM8RI4IJMBF1" type="button" class="btn btn-primary disabled no-padding" disabled blazor:onclick="330" blazor:elementReference="841b3865-20b2-424f-94a1-f7cb16a13f19">                        Archive Designs
                    </button></td></tr><tr class="table-row-selectable" blazor:onclick="103" blazor:ondrag="104" blazor:ondragend="105" blazor:ondragenter="106" blazor:ondragleave="107" blazor:ondragover="108" blazor:ondragstart="109" blazor:ondrop="110"><td class="text-nowrap" blazor:onclick="231" blazor:ondrag="232" blazor:ondragend="233" blazor:ondragenter="234" blazor:ondragleave="235" blazor:ondragover="236" blazor:ondragstart="237" blazor:ondrop="238">2d25fb2e-3136-4e20-b3d1-a02eb2efc249</td><td blazor:onclick="239" blazor:ondrag="240" blazor:ondragend="241" blazor:ondragenter="242" blazor:ondragleave="243" blazor:ondragover="244" blazor:ondragstart="245" blazor:ondrop="246">&quot;2021-05-20T12:50:56.061792700Z&quot;</td><td style="width: 150px" blazor:onclick="247" blazor:ondrag="248" blazor:ondragend="249" blazor:ondragenter="250" blazor:ondragleave="251" blazor:ondragover="252" blazor:ondragstart="253" blazor:ondrop="254"><button id="0HM8RI4IJMBF2" type="button" class="btn btn-primary disabled no-padding" disabled blazor:onclick="331" blazor:elementReference="871dedf3-f100-47a3-b309-270322d9b675">                        Archive Designs
                    </button></td></tr><tr class="table-row-selectable" blazor:onclick="111" blazor:ondrag="112" blazor:ondragend="113" blazor:ondragenter="114" blazor:ondragleave="115" blazor:ondragover="116" blazor:ondragstart="117" blazor:ondrop="118"><td class="text-nowrap" blazor:onclick="255" blazor:ondrag="256" blazor:ondragend="257" blazor:ondragenter="258" blazor:ondragleave="259" blazor:ondragover="260" blazor:ondragstart="261" blazor:ondrop="262">56bf53ce-fad2-4491-9bfe-df952d180e32</td><td blazor:onclick="263" blazor:ondrag="264" blazor:ondragend="265" blazor:ondragenter="266" blazor:ondragleave="267" blazor:ondragover="268" blazor:ondragstart="269" blazor:ondrop="270">&quot;2021-05-20T12:56:00.321929700Z&quot;</td><td style="width: 150px" blazor:onclick="271" blazor:ondrag="272" blazor:ondragend="273" blazor:ondragenter="274" blazor:ondragleave="275" blazor:ondragover="276" blazor:ondragstart="277" blazor:ondrop="278"><button id="0HM8RI4IJMBF3" type="button" class="btn btn-primary disabled no-padding" disabled blazor:onclick="332" blazor:elementReference="00d35b37-828c-47ed-aff2-6d3901c59384">                        Archive Designs
                    </button></td></tr><tr class="table-row-selectable" blazor:onclick="119" blazor:ondrag="120" blazor:ondragend="121" blazor:ondragenter="122" blazor:ondragleave="123" blazor:ondragover="124" blazor:ondragstart="125" blazor:ondrop="126"><td class="text-nowrap" blazor:onclick="279" blazor:ondrag="280" blazor:ondragend="281" blazor:ondragenter="282" blazor:ondragleave="283" blazor:ondragover="284" blazor:ondragstart="285" blazor:ondrop="286">04a2e7f5-5d7c-4b31-9fdd-df60f2cacf12</td><td blazor:onclick="287" blazor:ondrag="288" blazor:ondragend="289" blazor:ondragenter="290" blazor:ondragleave="291" blazor:ondragover="292" blazor:ondragstart="293" blazor:ondrop="294">&quot;2021-05-20T13:03:18.727588700Z&quot;</td><td style="width: 150px" blazor:onclick="295" blazor:ondrag="296" blazor:ondragend="297" blazor:ondragenter="298" blazor:ondragleave="299" blazor:ondragover="300" blazor:ondragstart="301" blazor:ondrop="302"><button id="0HM8RI4IJMBF4" type="button" class="btn btn-primary disabled no-padding" disabled blazor:onclick="333" blazor:elementReference="df430233-fb0e-46c4-93f3-7398e6d4e880">                        Archive Designs
                    </button></td></tr><tr class="table-row-selectable" blazor:onclick="127" blazor:ondrag="128" blazor:ondragend="129" blazor:ondragenter="130" blazor:ondragleave="131" blazor:ondragover="132" blazor:ondragstart="133" blazor:ondrop="134"><td class="text-nowrap" blazor:onclick="303" blazor:ondrag="304" blazor:ondragend="305" blazor:ondragenter="306" blazor:ondragleave="307" blazor:ondragover="308" blazor:ondragstart="309" blazor:ondrop="310">45cb783c-f566-49f5-80fc-5f846f698831</td><td blazor:onclick="311" blazor:ondrag="312" blazor:ondragend="313" blazor:ondragenter="314" blazor:ondragleave="315" blazor:ondragover="316" blazor:ondragstart="317" blazor:ondrop="318">&quot;2021-05-20T13:14:05.195472300Z&quot;</td><td style="width: 150px" blazor:onclick="319" blazor:ondrag="320" blazor:ondragend="321" blazor:ondragenter="322" blazor:ondragleave="323" blazor:ondragover="324" blazor:ondragstart="325" blazor:ondrop="326"><button id="0HM8RI4IJMBF5" type="button" class="btn btn-primary disabled no-padding" disabled blazor:onclick="334" blazor:elementReference="62cc8471-5ac3-48b4-bb6b-186f5bd416d1">                        Archive Designs
                    </button></td></tr></tbody></table></div>

With this test:

Version info:

  • bUnit version Latest
  • .NET Runtime and Blazor version 5.0 latest
  • OS type and version Window 10 Pro
@egil
Copy link
Member

egil commented May 20, 2021

.Click() should work. Can you create a minimal component under test that shows this bug?

@Smurf-IV
Copy link
Author

As requested:
TestBUnitClick.zip

@egil
Copy link
Member

egil commented May 24, 2021

Hi @Smurf-IV,

Just tested locally, and when stepping through the code, I see that the TriggerBubblingEventAsync method in TriggerEventDispatchExtensions.cs finds the following two event handlers in the DOM tree it will triggers (following the rules of event bubbling in the browser):

  • 143 (the TD in the third row)
  • 87 (the TR that contains the TD).

Both of these elements have an onclick event handler bound to them. Your assertions fail though, but that seems to be related to code in the DataGrid component.

        public async Task Test1()
        {
            using var cut = RenderComponent<DataGrider.Component1>();

            cut.WaitForState(() => cut.Instance.IsInitialised, 10.Seconds());

            // Now wait for the final StateChange to populate the page layout.
            cut.WaitForAssertion(() => cut.Find(@"button"), 10.Seconds());

            var useSelectedBtn = cut.Find(@"button");
            useSelectedBtn.HasAttribute(@"disabled").Should().BeTrue();

            var cells = cut.FindAll("table tr td:nth-child(1)");
            
            var cellLast = cells[cells.Count - 1];
            
            cellLast.Click(); // <-- this triggers the event handlers
            
            useSelectedBtn.HasAttribute(@"disabled").Should().BeFalse();
        }

Unrelated: The test example you shared will reuse a TestContext with all tests due to how NUnit work. That will work in some cases, but is not supported in all cases. See https://bunit.dev/docs/getting-started/writing-tests.html#remove-boilerplate-code-from-tests-1 for the proper way to inherit from TestContext in your NUnit tests.

@egil
Copy link
Member

egil commented May 25, 2021

@Smurf-IV do you still believe this is a bug in bUnit? Otherwise I will convert this issue to a discussion to keep the issues list clean.

@Smurf-IV
Copy link
Author

I do not know. The code works when it is run in a browser (i.e. the selectedItem is Not null and therefore the button is enabled.)
But in BUnit it is not.
So it it either

  • "Swallowed in Blazorise" due to incorrect bubbling
  • Not sent correctly
  • Javascript resources used by Blazorise are not initialised correctly in BUnit to allow the detection of the bubbled event.

Blame / bug cannot be allocated to any of the above yet!

@egil
Copy link
Member

egil commented May 25, 2021

OK. Well, Im very confident that onclick and in general event triggering works with bUnit. Its used in so many tests, both internally in bUnit and I assume in all test suites that uses bUnit, since it is very common thing to do in a test.

Its still not clear to me what you expect should happen when you trigger the onclick event in your example. E.g. a quick look in the Blazorise code seems to indicate that clicking a row just triggers the RowClicked EventCallback parameter.

@Smurf-IV
Copy link
Author

In the Code included , the following line means that the SelectedProject will be set to the row that has been clicked on (Either via a row click or cell click): which it does, when running in a browser.

@bind-SelectedRow="@SelectedProject"

So could it be option 3 ?

@egil
Copy link
Member

egil commented May 26, 2021

Not sure. How are you asserting that a selected row is set?

@Smurf-IV
Copy link
Author

Not sure. How are you asserting that a selected row is set?

In the code SelectedProject != null

@egil
Copy link
Member

egil commented May 27, 2021

Not sure. How are you asserting that a selected row is set?

In the code SelectedProject != null

I might be going blind, but i don't see that in any of the messages here or in the sample you posted. Can you add a test with the expected assertion added that is failing?

@Smurf-IV
Copy link
Author

You can put it into the Test code explicitly,
It is the "state" that enables the button,

  • and then button "enabled state" is currently in the current test you have been given.

@egil
Copy link
Member

egil commented May 28, 2021

I looked at the code for DataGrid.cs, and its not clear to me how it goes from clicking a row to raising the RowSelectedChanged event callback. Perhaps @stsrki can enlighten us.

@stsrki
Copy link

stsrki commented May 28, 2021

It is handled by the TableRow Clicked event(<tr @onclick>. And its handler is located in _DataGridRow under HandleClick method.

@egil
Copy link
Member

egil commented May 28, 2021

Thanks @stsrki. If there any JavaScript involved?

I can see the event is being correctly triggered by bunit, and it is a pretty basic thing to do in tests, so not sure if this is a corner case or the premise/assertion is wrong.

@stsrki
Copy link

stsrki commented May 28, 2021

Now that you mentioned it, there might be some javascript involved. Recently one of our community members implemented column resizing and by quick looking at the code, it has some onclick event on table rows.

@Smurf-IV
Copy link
Author

Smurf-IV commented Jun 2, 2021

  1. Is this the problem
    • Javascript resources used by Blazorise are not initialised correctly in BUnit to allow the detection of the bubbled event.
  2. So what is the solution ?

@egil
Copy link
Member

egil commented Jun 2, 2021

@Smurf-IV not sure what's going on tbh. If we have bUnits JSInterop in strict mode, it only fails the test if some an seemingly unrelated JS call is not configured. I will have to debug with Blazorise's source code locally to figure out what is going on. Unfortunately I am sort on time these days, so I cannot promise an answer soon. Maybe the Blazorise folks have time to look into this?

@stsrki
Copy link

stsrki commented Jun 2, 2021

I would really like to help but I'm also too overworked lately. Can't promise anything yet.

@isulzer
Copy link

isulzer commented Jul 30, 2021

I've actually run into something similar here. After some testing the problem is that it does not execute onclick if there is a reference to a local variable. Stripping it down to the simplest test:
@for(var i = 0;i<5;i++) { var index = i; <div @onclick="@(() => { Console.WriteLine(index); })">test</div> }
If you call cut.FindAll("div").ElementAt(0).Click(), it will fail to write to console. If you hardcode the parameter in the writeline instead of referencing the local variable it will execute OK.

This sort of local variable reference works fine with hosted blazor instead of bunit.

Using bunit 1.2.36-preview

@egil
Copy link
Member

egil commented Jul 30, 2021

@isulzer this is definitely something that should work.

Can you do me a favor and show a complete component and the accompanying test that fails.

@isulzer
Copy link

isulzer commented Jul 31, 2021

I tried to recreate this on my home PC and it's working fine. Not allowed to upload the other. Will try to figure out the difference...

@isulzer
Copy link

isulzer commented Jul 31, 2021

Ah took me a bit to notice it was the second click that had the issue. Notice the first Click() printed to console, but the second one does not. Attached a test with it replicated
BunitClickTest.zip

@isulzer
Copy link

isulzer commented Aug 2, 2021

After some further testing, this occurs with any event - for example onchange - that has an anonymous method and a captured local variable. The first call will work. the second will never fire.

@egil
Copy link
Member

egil commented Aug 2, 2021

That makes sense, since all the On* methods all call TriggerEventAsync. You have likely stumbled on a bug here.

@egil
Copy link
Member

egil commented Aug 3, 2021

I'm going to investigate further ASAP. Thanks for the sample @isulzer.

@egil
Copy link
Member

egil commented Aug 5, 2021

OK @isulzer, @Smurf-IV, I know whats going on now.

Consider this component:

@code
{
    [Parameter] public IList<string> Items { get; set; }
}
<table>
    @for (var i = 0; i < Items.Count; i++)
    {
        var row = Items[i];
        var index = i + 1;
        <tr @onclick="@(() =>
            {
                Console.WriteLine($"Index is {index}");
            })">
            <td>row @row</td>
        </tr>
    }
</table>

and this test:

[Theory, AutoData]
public void TestMethod1(IList<string> data)
{
	var cut = RenderComponent<InlineEventHandlers>(ps => ps.Add(p => p.Items, data));
			
	var rows = cut.FindAll("tr");
	var row1 = rows.ElementAt(0);
	row1.Click();

	//second click does not work
	//but if you change the razor console.writeline to not reference index then it works
	row1 = rows.ElementAt(0);
	row1.Click();

	var row2 = rows.ElementAt(1);
	row2.Click();
	Console.WriteLine("End test");
}

On the line cut.FindAll("tr");, we get all the rows from the current DOM tree. After the first Click() call, that DOM tree is replaced by a new one, because the click causes the component to re-render. Thus, the elements retrieved with the call to cut.FindAll("tr"); no longer references the current DOM tree, and using them to trigger event handlers will fail, because they will reference old, now disposed, event handlers pointers, that have been replaced by new ones.

However, this was very hard to notice because the bUnit would actually swallow an exception thrown by Blazor related to this, so I will be changing bUnit for the next release, so this should not happen again.

The workaround for this is to requery the DOM tree after each time you cause a component to rerender, e.g. when triggering an event by calling Click(), and not cache elements you have found with FindAll. Elements found using Find will automatically requery and update their pointer to the new element in the render tree.

@isulzer
Copy link

isulzer commented Aug 6, 2021

Ah! Makes sense. Thank you so much. Workaround works for me.

@Smurf-IV
Copy link
Author

Smurf-IV commented Aug 6, 2021

But in my test code, I never even got the first click to be actioned..
So does your Re-Render theory hold up in my code ?

@egil
Copy link
Member

egil commented Aug 6, 2021

But in my test code, I never even got the first click to be actioned..
So does your Re-Render theory hold up in my code ?

It might, if your components do some async triggered renders. Try your test with the Find method instead of the FindAll.

@egil
Copy link
Member

egil commented Aug 7, 2021

@Smurf-IV, also, try adding logging to the test runner, as described here: https://bunit.dev/docs/misc-test-tips.html#capturing-logs-from-ilogger-in-test-output

This will give us insight into what is going on in the renderer.

@Smurf-IV
Copy link
Author

This will have to wait a few days, but I'll get to the test eventually

@Smurf-IV
Copy link
Author

Smurf-IV commented Sep 6, 2021

FYI: This keeps slipping down the backlog.. Hope to catch it soon.

@egil
Copy link
Member

egil commented Sep 6, 2021

No worries @Smurf-IV. There is a fix released that could solve this, or at least bring some light to where the issue comes from, so I look forward to see if that solves it for you.

@andz-gl
Copy link

andz-gl commented Sep 20, 2021

Hello!

any update?

for me it does not work also (.Click() on tr/td of Blazorise's DataGrid)

please type here when it is fixed/resolved or you have any workaround

@egil
Copy link
Member

egil commented Sep 20, 2021

@andz-gl if you are seeing something similar, please contribute a minimal reproducible sample that we can use to troubleshot this.

@andz-gl
Copy link

andz-gl commented Sep 20, 2021

hope it could help, here is pseudocode:

        var component = RenderComponent<ComponentWithBlazoriseDataGridWithDetailRowTemplate>();
        var gridRow = component.Find(@"table.b-datagrid tr.table-row-selectable:first-child");
        gridRow.Click();

after that nothing happens (datagrid's row is not expanded, internal elements are not accessible even via 'WaitForAssertion')

@egil
Copy link
Member

egil commented Sep 20, 2021

Can you share something (as simple/minimal as possible) I can download and run myself. Otherwise its hard to help.

@andz-gl
Copy link

andz-gl commented Sep 20, 2021

WebApplication1.zip

image

'span.hfghjgfjhkg' should be visible/accessible via cut.Find("span.hfghjgfjhkg") after tableRow.Click() but it's not the case :(

@egil
Copy link
Member

egil commented Sep 21, 2021

@andz-gl as an experiment, I tried changing your <TestComponent> slightly to verify that calling Click on a row actually does something:

@page "/test-component"
@using Blazorise.DataGrid
@using WebApplication1.Data
@inject WeatherForecastService ForecastService

<h3>TestComponent</h3>

<DataGrid TItem="WeatherForecast"
          Data="@forecasts"
          @bind-SelectedRow="@selectedForecast"
          DetailRowTrigger="@((item)=> 
          { 
              TriggerCount++;
              return item.Date == selectedForecast?.Date;
          })">
    <DataGridColumns>
        <DataGridColumn TItem="WeatherForecast" Field="@nameof(WeatherForecast.Date)" />
        <DataGridColumn TItem="WeatherForecast" Field="@nameof(WeatherForecast.TemperatureC)" />
    </DataGridColumns>
    <DetailRowTemplate>
        <span class="hfghjgfjhkg">@context.Summary</span>
    </DetailRowTemplate>
</DataGrid>

@code {
    private WeatherForecast[] forecasts;
    private WeatherForecast selectedForecast;

    public int TriggerCount { get; set; }

    protected override async Task OnInitializedAsync()
    {
        forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
    }
}

Now the following test passes:

[Test]
public void TestNotOk()
{
    // Arrange
    using var ctx = new Bunit.TestContext();
    ctx.Services.AddBlazorise().AddBootstrapProviders().AddFontAwesomeIcons().AddSingleton<WeatherForecastService>();

    // Act
    var cut = ctx.RenderComponent<TestComponent>();
    var tableRow = cut.Find("tr.table-row-selectable");
    tableRow.Click();

    // Assert
    Assert.AreEqual(20, cut.Instance.TriggerCount);
}

So, maybe @stsrki can help out with further debugging, perhaps by stepping into the Blazorize code and provide some insights into why a rerender does not happen like it does in the browser. What triggers a render after a row is clicked? Is it one of the top level services or a root component?

Another thing I tried was to add logging to the test project (and convert it to xUnit because thats what I know), reduce the number of weatherforecasts to 1 instead of 5, and captured the verbose logs from the Blazor and bUnit renderer, to see what components was being rendered at what time. As far as I can see, there are no new components being rendered out after the Click event is triggered, just the existing table row and two table cells are being rerendered:

Here is the output:

10:14:50.303 Debug - Initializing root component 0 ("Bunit.Rendering.WrapperComponent")
10:14:50.480 Debug - Processing pending renders.
10:14:50.480 Debug - Rendering component 0 of type "Bunit.Rendering.WrapperComponent"
10:14:50.486 Debug - Initializing component 1 ("Bunit.Rendering.FragmentContainer") as child of "Bunit.Rendering.WrapperComponent" ("Bunit.Rendering.WrapperComponent")
10:14:50.486 Debug - Rendering component 1 of type "Bunit.Rendering.FragmentContainer"
10:14:50.488 Debug - Initializing component 2 ("WebApplication1.Pages.TestComponent") as child of "Bunit.Rendering.FragmentContainer" ("Bunit.Rendering.FragmentContainer")
10:14:50.489 Debug - Rendering component 2 of type "WebApplication1.Pages.TestComponent"
10:14:50.493 Debug - Initializing component 3 ("Blazorise.DataGrid.DataGrid`1[WebApplication1.Data.WeatherForecast]") as child of "WebApplication1.Pages.TestComponent" ("WebApplication1.Pages.TestComponent")
10:14:50.496 Debug - Rendering component 3 of type "Blazorise.DataGrid.DataGrid`1[WebApplication1.Data.WeatherForecast]"
10:14:50.496 Debug - Initializing component 4 ("Microsoft.AspNetCore.Components.CascadingValue`1[Blazorise.DataGrid.DataGrid`1[WebApplication1.Data.WeatherForecast]]") as child of "Blazorise.DataGrid.DataGrid`1[WebApplication1.Data.WeatherForecast]" ("Blazorise.DataGrid.DataGrid`1[WebApplication1.Data.WeatherForecast]")
10:14:50.496 Debug - Initializing component 5 ("Microsoft.AspNetCore.Components.CascadingValue`1[Blazorise.DataGrid.DataGrid`1[WebApplication1.Data.WeatherForecast]]") as child of "Blazorise.DataGrid.DataGrid`1[WebApplication1.Data.WeatherForecast]" ("Blazorise.DataGrid.DataGrid`1[WebApplication1.Data.WeatherForecast]")
10:14:50.496 Debug - Rendering component 4 of type "Microsoft.AspNetCore.Components.CascadingValue`1[Blazorise.DataGrid.DataGrid`1[WebApplication1.Data.WeatherForecast]]"
10:14:50.498 Debug - Initializing component 6 ("Blazorise.Table") as child of "Microsoft.AspNetCore.Components.CascadingValue`1[Blazorise.DataGrid.DataGrid`1[WebApplication1.Data.WeatherForecast]]" ("Microsoft.AspNetCore.Components.CascadingValue`1[Blazorise.DataGrid.DataGrid`1[WebApplication1.Data.WeatherForecast]]")
10:14:50.500 Debug - Rendering component 5 of type "Microsoft.AspNetCore.Components.CascadingValue`1[Blazorise.DataGrid.DataGrid`1[WebApplication1.Data.WeatherForecast]]"
10:14:50.504 Debug - Initializing component 7 ("Blazorise.DataGrid.DataGridColumn`1[WebApplication1.Data.WeatherForecast]") as child of "Microsoft.AspNetCore.Components.CascadingValue`1[Blazorise.DataGrid.DataGrid`1[WebApplication1.Data.WeatherForecast]]" ("Microsoft.AspNetCore.Components.CascadingValue`1[Blazorise.DataGrid.DataGrid`1[WebApplication1.Data.WeatherForecast]]")
10:14:50.506 Debug - Initializing component 8 ("Blazorise.DataGrid.DataGridColumn`1[WebApplication1.Data.WeatherForecast]") as child of "Microsoft.AspNetCore.Components.CascadingValue`1[Blazorise.DataGrid.DataGrid`1[WebApplication1.Data.WeatherForecast]]" ("Microsoft.AspNetCore.Components.CascadingValue`1[Blazorise.DataGrid.DataGrid`1[WebApplication1.Data.WeatherForecast]]")
10:14:50.507 Debug - Rendering component 6 of type "Blazorise.Table"
10:14:50.507 Debug - Initializing component 9 ("Microsoft.AspNetCore.Components.CascadingValue`1[Blazorise.Table]") as child of "Blazorise.Table" ("Blazorise.Table")
10:14:50.507 Debug - Rendering component 7 of type "Blazorise.DataGrid.DataGridColumn`1[WebApplication1.Data.WeatherForecast]"
10:14:50.507 Debug - Rendering component 8 of type "Blazorise.DataGrid.DataGridColumn`1[WebApplication1.Data.WeatherForecast]"
10:14:50.507 Debug - Rendering component 9 of type "Microsoft.AspNetCore.Components.CascadingValue`1[Blazorise.Table]"
10:14:50.510 Debug - Initializing component 10 ("Blazorise.TableHeader") as child of "Microsoft.AspNetCore.Components.CascadingValue`1[Blazorise.Table]" ("Microsoft.AspNetCore.Components.CascadingValue`1[Blazorise.Table]")
10:14:50.517 Debug - Initializing component 11 ("Blazorise.TableBody") as child of "Microsoft.AspNetCore.Components.CascadingValue`1[Blazorise.Table]" ("Microsoft.AspNetCore.Components.CascadingValue`1[Blazorise.Table]")
10:14:50.518 Debug - Rendering component 10 of type "Blazorise.TableHeader"
10:14:50.520 Debug - Initializing component 12 ("Blazorise.TableRow") as child of "Blazorise.TableHeader" ("Blazorise.TableHeader")
10:14:50.521 Debug - Rendering component 11 of type "Blazorise.TableBody"
10:14:50.533 Debug - Initializing component 13 ("Blazorise.DataGrid._DataGridRow`1[WebApplication1.Data.WeatherForecast]") as child of "Blazorise.TableBody" ("Blazorise.TableBody")
10:14:50.534 Debug - Rendering component 12 of type "Blazorise.TableRow"
10:14:50.537 Debug - Initializing component 14 ("Blazorise.TableHeaderCell") as child of "Blazorise.TableRow" ("Blazorise.TableRow")
10:14:50.537 Debug - Initializing component 15 ("Blazorise.TableHeaderCell") as child of "Blazorise.TableRow" ("Blazorise.TableRow")
10:14:50.537 Debug - Rendering component 3 of type "Blazorise.DataGrid.DataGrid`1[WebApplication1.Data.WeatherForecast]"
10:14:50.537 Debug - Rendering component 13 of type "Blazorise.DataGrid._DataGridRow`1[WebApplication1.Data.WeatherForecast]"
10:14:50.539 Debug - Initializing component 16 ("Blazorise.TableRow") as child of "Blazorise.DataGrid._DataGridRow`1[WebApplication1.Data.WeatherForecast]" ("Blazorise.DataGrid._DataGridRow`1[WebApplication1.Data.WeatherForecast]")
10:14:50.539 Debug - Rendering component 14 of type "Blazorise.TableHeaderCell"
10:14:50.540 Debug - Rendering component 15 of type "Blazorise.TableHeaderCell"
10:14:50.540 Debug - Rendering component 4 of type "Microsoft.AspNetCore.Components.CascadingValue`1[Blazorise.DataGrid.DataGrid`1[WebApplication1.Data.WeatherForecast]]"
10:14:50.540 Debug - Rendering component 5 of type "Microsoft.AspNetCore.Components.CascadingValue`1[Blazorise.DataGrid.DataGrid`1[WebApplication1.Data.WeatherForecast]]"
10:14:50.540 Debug - Rendering component 16 of type "Blazorise.TableRow"
10:14:50.542 Debug - Initializing component 17 ("Blazorise.TableRowCell") as child of "Blazorise.TableRow" ("Blazorise.TableRow")
10:14:50.542 Debug - Initializing component 18 ("Blazorise.TableRowCell") as child of "Blazorise.TableRow" ("Blazorise.TableRow")
10:14:50.542 Debug - Rendering component 6 of type "Blazorise.Table"
10:14:50.542 Debug - Rendering component 17 of type "Blazorise.TableRowCell"
10:14:50.544 Debug - Rendering component 18 of type "Blazorise.TableRowCell"
10:14:50.545 Debug - Rendering component 9 of type "Microsoft.AspNetCore.Components.CascadingValue`1[Blazorise.Table]"
10:14:50.545 Debug - Rendering component 10 of type "Blazorise.TableHeader"
10:14:50.545 Debug - Rendering component 11 of type "Blazorise.TableBody"
10:14:50.547 Debug - Rendering component 12 of type "Blazorise.TableRow"
10:14:50.547 Debug - Rendering component 13 of type "Blazorise.DataGrid._DataGridRow`1[WebApplication1.Data.WeatherForecast]"
10:14:50.547 Debug - Rendering component 14 of type "Blazorise.TableHeaderCell"
10:14:50.547 Debug - Rendering component 15 of type "Blazorise.TableHeaderCell"
10:14:50.547 Debug - Rendering component 16 of type "Blazorise.TableRow"
10:14:50.547 Debug - Rendering component 17 of type "Blazorise.TableRowCell"
10:14:50.547 Debug - Rendering component 18 of type "Blazorise.TableRowCell"
10:14:50.548 Debug - New render batch received.
10:14:50.548 Debug - Component with ID = 0 has been rendered.
10:14:50.554 Debug - Finished updating components markup.
10:14:50.560 Debug - Rendering component 3 of type "Blazorise.DataGrid.DataGrid`1[WebApplication1.Data.WeatherForecast]"
10:14:50.560 Debug - Rendering component 4 of type "Microsoft.AspNetCore.Components.CascadingValue`1[Blazorise.DataGrid.DataGrid`1[WebApplication1.Data.WeatherForecast]]"
10:14:50.560 Debug - Rendering component 5 of type "Microsoft.AspNetCore.Components.CascadingValue`1[Blazorise.DataGrid.DataGrid`1[WebApplication1.Data.WeatherForecast]]"
10:14:50.560 Debug - Rendering component 6 of type "Blazorise.Table"
10:14:50.560 Debug - Rendering component 9 of type "Microsoft.AspNetCore.Components.CascadingValue`1[Blazorise.Table]"
10:14:50.560 Debug - Rendering component 10 of type "Blazorise.TableHeader"
10:14:50.560 Debug - Rendering component 11 of type "Blazorise.TableBody"
10:14:50.560 Debug - Rendering component 12 of type "Blazorise.TableRow"
10:14:50.560 Debug - Rendering component 3 of type "Blazorise.DataGrid.DataGrid`1[WebApplication1.Data.WeatherForecast]"
10:14:50.560 Debug - Rendering component 13 of type "Blazorise.DataGrid._DataGridRow`1[WebApplication1.Data.WeatherForecast]"
10:14:50.560 Debug - Rendering component 14 of type "Blazorise.TableHeaderCell"
10:14:50.560 Debug - Rendering component 15 of type "Blazorise.TableHeaderCell"
10:14:50.560 Debug - Rendering component 4 of type "Microsoft.AspNetCore.Components.CascadingValue`1[Blazorise.DataGrid.DataGrid`1[WebApplication1.Data.WeatherForecast]]"
10:14:50.560 Debug - Rendering component 5 of type "Microsoft.AspNetCore.Components.CascadingValue`1[Blazorise.DataGrid.DataGrid`1[WebApplication1.Data.WeatherForecast]]"
10:14:50.560 Debug - Rendering component 16 of type "Blazorise.TableRow"
10:14:50.560 Debug - Rendering component 6 of type "Blazorise.Table"
10:14:50.560 Debug - Rendering component 17 of type "Blazorise.TableRowCell"
10:14:50.560 Debug - Rendering component 18 of type "Blazorise.TableRowCell"
10:14:50.560 Debug - Rendering component 9 of type "Microsoft.AspNetCore.Components.CascadingValue`1[Blazorise.Table]"
10:14:50.560 Debug - Rendering component 10 of type "Blazorise.TableHeader"
10:14:50.560 Debug - Rendering component 11 of type "Blazorise.TableBody"
10:14:50.560 Debug - Rendering component 12 of type "Blazorise.TableRow"
10:14:50.560 Debug - Rendering component 13 of type "Blazorise.DataGrid._DataGridRow`1[WebApplication1.Data.WeatherForecast]"
10:14:50.560 Debug - Rendering component 14 of type "Blazorise.TableHeaderCell"
10:14:50.560 Debug - Rendering component 15 of type "Blazorise.TableHeaderCell"
10:14:50.560 Debug - Rendering component 16 of type "Blazorise.TableRow"
10:14:50.560 Debug - Rendering component 17 of type "Blazorise.TableRowCell"
10:14:50.560 Debug - Rendering component 18 of type "Blazorise.TableRowCell"
10:14:50.560 Debug - New render batch received.
10:14:50.560 Debug - Component with ID = 0 has been rendered.
10:14:50.560 Debug - Finished updating components markup.
10:14:50.561 Debug - The initial render of 0 is completed.
+ 10:14:50.610 Debug - Handling event { Id: 5, Name: "HandlingEvent" } of type '"MouseEventArgs"'
+ 10:14:50.610 Debug - Processing pending renders.
+ 10:14:50.611 Debug - Rendering component 16 of type "Blazorise.TableRow"
+ 10:14:50.611 Debug - Rendering component 17 of type "Blazorise.TableRowCell"
+ 10:14:50.611 Debug - Rendering component 18 of type "Blazorise.TableRowCell"
+ 10:14:50.611 Debug - New render batch received.
+ 10:14:50.611 Debug - Component with ID = 0 has been rendered.
+ 10:14:50.611 Debug - Component with ID = 1 has been rendered.
+ 10:14:50.611 Debug - Component with ID = 2 has been rendered.
+ 10:14:50.611 Debug - Finished updating components markup.
10:14:50.613 Debug - Checking the wait condition for component 2
10:14:50.613 Debug - The checker of component 2 throw an exception with message 'No elements were found that matches the selector 'span.hfghjgfjhkg''
10:14:50.614 Debug - Checking the wait condition for component 2
10:14:50.614 Debug - The checker of component 2 throw an exception with message 'No elements were found that matches the selector 'span.hfghjgfjhkg''
10:14:51.617 Debug - The wait for helper for component 2 timed out
10:14:51.617 Debug - The state wait helper for component 2 disposed
10:14:51.622 Debug - Disposing component 0 of type "Bunit.Rendering.WrapperComponent"
10:14:51.622 Debug - Disposing component 1 of type "Bunit.Rendering.FragmentContainer"
10:14:51.622 Debug - Disposing component 2 of type "WebApplication1.Pages.TestComponent"
10:14:51.622 Debug - Disposing component 3 of type "Blazorise.DataGrid.DataGrid`1[WebApplication1.Data.WeatherForecast]"
10:14:51.623 Debug - Disposing component 4 of type "Microsoft.AspNetCore.Components.CascadingValue`1[Blazorise.DataGrid.DataGrid`1[WebApplication1.Data.WeatherForecast]]"
10:14:51.623 Debug - Disposing component 5 of type "Microsoft.AspNetCore.Components.CascadingValue`1[Blazorise.DataGrid.DataGrid`1[WebApplication1.Data.WeatherForecast]]"
10:14:51.623 Debug - Disposing component 6 of type "Blazorise.Table"
10:14:51.623 Debug - Disposing component 7 of type "Blazorise.DataGrid.DataGridColumn`1[WebApplication1.Data.WeatherForecast]"
10:14:51.623 Debug - Disposing component 8 of type "Blazorise.DataGrid.DataGridColumn`1[WebApplication1.Data.WeatherForecast]"
10:14:51.623 Debug - Disposing component 9 of type "Microsoft.AspNetCore.Components.CascadingValue`1[Blazorise.Table]"
10:14:51.623 Debug - Disposing component 10 of type "Blazorise.TableHeader"
10:14:51.623 Debug - Disposing component 11 of type "Blazorise.TableBody"
10:14:51.623 Debug - Disposing component 12 of type "Blazorise.TableRow"
10:14:51.623 Debug - Disposing component 13 of type "Blazorise.DataGrid._DataGridRow`1[WebApplication1.Data.WeatherForecast]"
10:14:51.623 Debug - Disposing component 14 of type "Blazorise.TableHeaderCell"
10:14:51.623 Debug - Disposing component 15 of type "Blazorise.TableHeaderCell"
10:14:51.623 Debug - Disposing component 16 of type "Blazorise.TableRow"
10:14:51.623 Debug - Disposing component 17 of type "Blazorise.TableRowCell"
10:14:51.623 Debug - Disposing component 18 of type "Blazorise.TableRowCell"

The +'s added is my attempt at highlighting the parts of the log that relates to the Click.

@andz-gl
Copy link

andz-gl commented Sep 21, 2021

thank you! just shared it on Blazorise discussion thread(about this issue): Megabit/Blazorise#2412

@David-Moreira
Copy link

David-Moreira commented Sep 26, 2021

Hello,
From Blazorise side, we've debugged the code. It successfully triggers our internal click. However the event arguments for the click brings a Detail of 0, while Blazorise expects that the Detail equals the number of clicks the user has issued. (Detect single click vs double click)

image
We didn't find any issue with this behaviour on the browsers.

Any idea why the behaviour is different with BUnit? @egil can you help?

Edit: Adding Blazorise's branch test for this issue if you guys need/want to fork/test/debug blazorise.
Branch : https://github.com/Megabit/Blazorise/tree/rel-0.9.4-BUnit-DataGrid-RowClickTrigger

Thanks!

@egil
Copy link
Member

egil commented Sep 27, 2021

Hello,
From Blazorise side, we've debugged the code. It successfully triggers our internal click. However the event arguments for the click brings a Detail of 0, while Blazorise expects that the Detail equals the number of clicks the user has issued. (Detect single click vs double click)

That explains it. bUnit doesn't set that value by default, but I think it should set it to one when you call Click and two when you call DoubleClick.

Luckily that's an easy fix, e.g. see the Click
extension method here, I just need to set the detail argument to 1 instead of default in this case.

@David-Moreira would you be so kind to call the method like this to verify that this will work: cut.Find("button").Click(detail: 1).

And thank you for your help debugging this. You and the other people over at the Blazorise team are doing great work!

@David-Moreira
Copy link

David-Moreira commented Sep 27, 2021

That's great, Thanks!

We'll let you know if it works as soon as we test it!
Also as far as I know, the expected behaviour is each click increments detail. So bunit probably should not stop at 2 but keep incrementing?
https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail

Edit: nvm there isn't repeated clicking on bunit haha 😆

@egil
Copy link
Member

egil commented Sep 28, 2021

Edit: nvm there isn't repeated clicking on bunit haha 😆

Haha yeah, well, it would be tricky to figure out when users were intending to emulate consecutive clicks or just one click followed by another.

This is mostly an issue because the users of Blazorise (and perhaps other 3rd party libs that uses the detail property) don't know that the detail property should be set.

Users testing their own libs that uses detail will know to set it and not run into trouble.

But hopefully I can make folks lives easier by setting more sane detail values. However, this scenario from your MDN link is going to be hard:

For mousedown or mouseup events, UIEvent.detail is 1 plus the current click count.

@David-Moreira
Copy link

@David-Moreira would you be so kind to call the method like this to verify that this will work: cut.Find("button").Click(detail: 1).

Tested. Works great.

@andz-gl
Copy link

andz-gl commented Sep 28, 2021

Click(detail: 1) works. Thank you!

@egil
Copy link
Member

egil commented Sep 30, 2021

The fix has been merged in to main, so it should be available as a nightly release soon (see how to get it here: #209).

@Smurf-IV
Copy link
Author

Just to confirm Click(1) makes things work..
Thanks for sticking with this

@rbillott
Copy link

rbillott commented Oct 5, 2023

if using Async version, below is what you need

cell.ClickAsync(new MouseEventArgs{Detail = 1});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants