Skip to content

Commit 6b8e991

Browse files
javiercnCopilot
andauthored
[Blazor] Add OnRowClick EventCallback to QuickGrid (#64602)
* Add OnRowClick EventCallback to QuickGrid This change adds an OnRowClick parameter to QuickGrid that allows users to handle row click events. When the OnRowClick delegate is set: - The row receives a 'row-clickable' CSS class - A cursor: pointer style is applied via scoped CSS - Clicking the row invokes the callback with the row item Fixes #44899 * Add OnRowClick EventCallback to QuickGrid (#64606) * Initial plan * Fix RowClassApplied test and consolidate RenderRow logic Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com> * Fix trailing space in combinedClass when rowClass is null Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com> * Simplify QuickGrid OnRowClick rendering by removing conditional duplication (#64621) * Initial plan * Simplify RenderRow by removing if/else and coalescing OnRowClick callback Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com> --------- Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
1 parent 7b05316 commit 6b8e991

File tree

6 files changed

+84
-4
lines changed

6 files changed

+84
-4
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
#nullable enable
2+
Microsoft.AspNetCore.Components.QuickGrid.QuickGrid<TGridItem>.OnRowClick.get -> Microsoft.AspNetCore.Components.EventCallback<TGridItem>
3+
Microsoft.AspNetCore.Components.QuickGrid.QuickGrid<TGridItem>.OnRowClick.set -> void

src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/src/QuickGrid.razor

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,15 @@
6666
private void RenderRow(RenderTreeBuilder __builder, int rowIndex, TGridItem item)
6767
{
6868
var rowClass = RowClass?.Invoke(item);
69-
<tr @key="@(ItemKey(item))" aria-rowindex="@rowIndex" class="@rowClass">
69+
var combinedClass = OnRowClick.HasDelegate
70+
? string.IsNullOrEmpty(rowClass) ? "row-clickable" : $"row-clickable {rowClass}"
71+
: rowClass;
72+
var rowClick = OnRowClick.HasDelegate ? EventCallback.Factory.Create<MouseEventArgs>(this, () => OnRowClick.InvokeAsync(item)) : default;
73+
74+
<tr @key="@(ItemKey(item))"
75+
aria-rowindex="@rowIndex"
76+
class="@combinedClass"
77+
@onclick="@rowClick">
7078
@foreach (var col in _columns)
7179
{
7280
<td class="@ColumnClass(col)" @key="@col">@{ col.CellContent(__builder, item); }</td>

src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/src/QuickGrid.razor.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@ public partial class QuickGrid<TGridItem> : IAsyncDisposable
109109
/// </summary>
110110
[Parameter] public Func<TGridItem, string?>? RowClass { get; set; }
111111

112+
/// <summary>
113+
/// Optional. A callback that is invoked when a row is clicked.
114+
/// </summary>
115+
[Parameter] public EventCallback<TGridItem> OnRowClick { get; set; }
116+
112117
[Inject] private IServiceProvider Services { get; set; } = default!;
113118
[Inject] private IJSRuntime JS { get; set; } = default!;
114119

src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/src/QuickGrid.razor.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,8 @@ html[dir=rtl] .col-justify-end .col-options {
116116
right: unset;
117117
left: 0;
118118
}
119+
120+
/* Clickable rows when OnRowClick is set */
121+
tr.row-clickable {
122+
cursor: pointer;
123+
}

src/Components/test/E2ETest/Tests/QuickGridTest.cs

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,11 +136,11 @@ public void RowClassApplied()
136136
if (firstName == "Julie")
137137
{
138138
isJulieRowFound = true;
139-
Assert.Equal("highlight", row.GetDomAttribute("class"));
139+
Assert.Equal("row-clickable highlight", row.GetDomAttribute("class"));
140140
}
141141
else
142142
{
143-
Assert.Null(row.GetDomAttribute("class"));
143+
Assert.Equal("row-clickable", row.GetDomAttribute("class"));
144144
}
145145
}
146146

@@ -215,4 +215,43 @@ public void ItemsProviderCalledOnceWithVirtualize()
215215
app = Browser.MountTestComponent<QuickGridVirtualizeComponent>();
216216
Browser.Equal("1", () => app.FindElement(By.Id("items-provider-call-count")).Text);
217217
}
218+
219+
[Fact]
220+
public void OnRowClickTriggersCallback()
221+
{
222+
var grid = app.FindElement(By.CssSelector("#grid > table"));
223+
224+
// Verify no row has been clicked yet
225+
Browser.Exists(By.Id("no-click"));
226+
227+
// Click on the first row (Julie Smith)
228+
var firstRow = grid.FindElement(By.CssSelector("tbody > tr:nth-child(1)"));
229+
firstRow.Click();
230+
231+
// Verify the callback was triggered with correct data
232+
Browser.Equal("PersonId: 11203", () => app.FindElement(By.Id("clicked-person-id")).Text);
233+
Browser.Equal("Name: Julie Smith", () => app.FindElement(By.Id("clicked-person-name")).Text);
234+
Browser.Equal("Click count: 1", () => app.FindElement(By.Id("click-count")).Text);
235+
236+
// Click on another row (Jose Hernandez - 3rd row)
237+
var thirdRow = grid.FindElement(By.CssSelector("tbody > tr:nth-child(3)"));
238+
thirdRow.Click();
239+
240+
// Verify the callback was triggered with the new row's data
241+
Browser.Equal("PersonId: 11898", () => app.FindElement(By.Id("clicked-person-id")).Text);
242+
Browser.Equal("Name: Jose Hernandez", () => app.FindElement(By.Id("clicked-person-name")).Text);
243+
Browser.Equal("Click count: 2", () => app.FindElement(By.Id("click-count")).Text);
244+
}
245+
246+
[Fact]
247+
public void OnRowClickAppliesCursorPointerStyle()
248+
{
249+
var grid = app.FindElement(By.CssSelector("#grid > table"));
250+
251+
// Verify the row has cursor: pointer style via the row-clickable class
252+
var cursorStyle = Browser.ExecuteJavaScript<string>(@"
253+
const row = document.querySelector('#grid > table > tbody > tr:nth-child(1)');
254+
return row ? getComputedStyle(row).cursor : null;");
255+
Assert.Equal("pointer", cursorStyle);
256+
}
218257
}

src/Components/test/testassets/BasicTestApp/QuickGridTest/SampleQuickGridComponent.razor

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<h3>Sample QuickGrid Component</h3>
44

55
<div id="grid">
6-
<QuickGrid @ref="@quickGridRef" Items="@FilteredPeople" Pagination="@pagination" RowClass="HighlightJulie" custom-attrib="somevalue" class="custom-class-attrib">
6+
<QuickGrid @ref="@quickGridRef" Items="@FilteredPeople" Pagination="@pagination" RowClass="HighlightJulie" OnRowClick="@((Person p) => HandleRowClick(p))" custom-attrib="somevalue" class="custom-class-attrib">
77
<PropertyColumn Property="@(p => p.PersonId)" Sortable="true" />
88
<PropertyColumn Property="@(p => p.FirstName)" Sortable="true">
99
<ColumnOptions>
@@ -20,11 +20,32 @@
2020
</div>
2121
<Paginator State="@pagination" />
2222

23+
<div id="clicked-row-info">
24+
@if (clickedPerson is not null)
25+
{
26+
<p id="clicked-person-id">PersonId: @clickedPerson.PersonId</p>
27+
<p id="clicked-person-name">Name: @clickedPerson.FirstName @clickedPerson.LastName</p>
28+
<p id="click-count">Click count: @clickCount</p>
29+
}
30+
else
31+
{
32+
<p id="no-click">No row clicked yet</p>
33+
}
34+
</div>
35+
2336
@code {
2437
record Person(int PersonId, string FirstName, string LastName, DateOnly BirthDate);
2538
PaginationState pagination = new PaginationState { ItemsPerPage = 10 };
2639
string firstNameFilter;
2740
QuickGrid<Person> quickGridRef;
41+
Person clickedPerson;
42+
int clickCount;
43+
44+
void HandleRowClick(Person person)
45+
{
46+
clickedPerson = person;
47+
clickCount++;
48+
}
2849

2950
int ComputeAge(DateOnly birthDate)
3051
=> DateTime.Now.Year - birthDate.Year - (birthDate.DayOfYear < DateTime.Now.DayOfYear ? 0 : 1);

0 commit comments

Comments
 (0)