Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
#nullable enable
Microsoft.AspNetCore.Components.QuickGrid.QuickGrid<TGridItem>.OnRowClick.get -> Microsoft.AspNetCore.Components.EventCallback<TGridItem>
Microsoft.AspNetCore.Components.QuickGrid.QuickGrid<TGridItem>.OnRowClick.set -> void
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,15 @@
private void RenderRow(RenderTreeBuilder __builder, int rowIndex, TGridItem item)
{
var rowClass = RowClass?.Invoke(item);
<tr @key="@(ItemKey(item))" aria-rowindex="@rowIndex" class="@rowClass">
var combinedClass = OnRowClick.HasDelegate
? string.IsNullOrEmpty(rowClass) ? "row-clickable" : $"row-clickable {rowClass}"
: rowClass;
var rowClick = OnRowClick.HasDelegate ? EventCallback.Factory.Create<MouseEventArgs>(this, () => OnRowClick.InvokeAsync(item)) : default;

<tr @key="@(ItemKey(item))"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does the row click behave when it comes to propagation? Will a click on the element bubble up to its ancestor DOM element? Do we have to support stopPropagation?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blazor uses event delegation for handling events. All events bubble up to the root where the handler intercepts them and then dispatches to the appropriate Blazor code. We normally don't stop event propagation unless we have good reasons to.

https://learn.microsoft.com/en-us/aspnet/core/blazor/components/event-handling?view=aspnetcore-10.0#stop-event-propagation

aria-rowindex="@rowIndex"
class="@combinedClass"
@onclick="@rowClick">
@foreach (var col in _columns)
{
<td class="@ColumnClass(col)" @key="@col">@{ col.CellContent(__builder, item); }</td>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ public partial class QuickGrid<TGridItem> : IAsyncDisposable
/// </summary>
[Parameter] public Func<TGridItem, string?>? RowClass { get; set; }

/// <summary>
/// Optional. A callback that is invoked when a row is clicked.
/// </summary>
[Parameter] public EventCallback<TGridItem> OnRowClick { get; set; }

[Inject] private IServiceProvider Services { get; set; } = default!;
[Inject] private IJSRuntime JS { get; set; } = default!;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,8 @@ html[dir=rtl] .col-justify-end .col-options {
right: unset;
left: 0;
}

/* Clickable rows when OnRowClick is set */
tr.row-clickable {
cursor: pointer;
}
43 changes: 41 additions & 2 deletions src/Components/test/E2ETest/Tests/QuickGridTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,11 +136,11 @@ public void RowClassApplied()
if (firstName == "Julie")
{
isJulieRowFound = true;
Assert.Equal("highlight", row.GetDomAttribute("class"));
Assert.Equal("row-clickable highlight", row.GetDomAttribute("class"));
}
else
{
Assert.Null(row.GetDomAttribute("class"));
Assert.Equal("row-clickable", row.GetDomAttribute("class"));
}
}

Expand Down Expand Up @@ -215,4 +215,43 @@ public void ItemsProviderCalledOnceWithVirtualize()
app = Browser.MountTestComponent<QuickGridVirtualizeComponent>();
Browser.Equal("1", () => app.FindElement(By.Id("items-provider-call-count")).Text);
}

[Fact]
public void OnRowClickTriggersCallback()
{
var grid = app.FindElement(By.CssSelector("#grid > table"));

// Verify no row has been clicked yet
Browser.Exists(By.Id("no-click"));

// Click on the first row (Julie Smith)
var firstRow = grid.FindElement(By.CssSelector("tbody > tr:nth-child(1)"));
firstRow.Click();

// Verify the callback was triggered with correct data
Browser.Equal("PersonId: 11203", () => app.FindElement(By.Id("clicked-person-id")).Text);
Browser.Equal("Name: Julie Smith", () => app.FindElement(By.Id("clicked-person-name")).Text);
Browser.Equal("Click count: 1", () => app.FindElement(By.Id("click-count")).Text);

// Click on another row (Jose Hernandez - 3rd row)
var thirdRow = grid.FindElement(By.CssSelector("tbody > tr:nth-child(3)"));
thirdRow.Click();

// Verify the callback was triggered with the new row's data
Browser.Equal("PersonId: 11898", () => app.FindElement(By.Id("clicked-person-id")).Text);
Browser.Equal("Name: Jose Hernandez", () => app.FindElement(By.Id("clicked-person-name")).Text);
Browser.Equal("Click count: 2", () => app.FindElement(By.Id("click-count")).Text);
}

[Fact]
public void OnRowClickAppliesCursorPointerStyle()
{
var grid = app.FindElement(By.CssSelector("#grid > table"));

// Verify the row has cursor: pointer style via the row-clickable class
var cursorStyle = Browser.ExecuteJavaScript<string>(@"
const row = document.querySelector('#grid > table > tbody > tr:nth-child(1)');
return row ? getComputedStyle(row).cursor : null;");
Assert.Equal("pointer", cursorStyle);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<h3>Sample QuickGrid Component</h3>

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

<div id="clicked-row-info">
@if (clickedPerson is not null)
{
<p id="clicked-person-id">PersonId: @clickedPerson.PersonId</p>
<p id="clicked-person-name">Name: @clickedPerson.FirstName @clickedPerson.LastName</p>
<p id="click-count">Click count: @clickCount</p>
}
else
{
<p id="no-click">No row clicked yet</p>
}
</div>

@code {
record Person(int PersonId, string FirstName, string LastName, DateOnly BirthDate);
PaginationState pagination = new PaginationState { ItemsPerPage = 10 };
string firstNameFilter;
QuickGrid<Person> quickGridRef;
Person clickedPerson;
int clickCount;

void HandleRowClick(Person person)
{
clickedPerson = person;
clickCount++;
}

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