diff --git a/src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/src/QuickGrid.razor b/src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/src/QuickGrid.razor index 5228ed1d2b00..3d61a1e2990f 100644 --- a/src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/src/QuickGrid.razor +++ b/src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/src/QuickGrid.razor @@ -77,9 +77,11 @@ private void RenderPlaceholderRow(RenderTreeBuilder __builder, PlaceholderContext placeholderContext) { - @foreach (var col in _columns) + @foreach (var col in _columns) { - @{ col.RenderPlaceholderContent(__builder, placeholderContext); } + var hasCustomPlaceholder = col.PlaceholderTemplate is not null; + var placeholderClass = hasCustomPlaceholder ? "grid-cell-placeholder" : "grid-cell-placeholder default"; + @{ col.RenderPlaceholderContent(__builder, placeholderContext); } } } diff --git a/src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/src/Themes/Default.css b/src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/src/Themes/Default.css index e11b358f9927..4b7313256ff8 100644 --- a/src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/src/Themes/Default.css +++ b/src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/src/Themes/Default.css @@ -75,7 +75,7 @@ border-radius: 0.3rem; } -.quickgrid[theme=default] > tbody > tr > td.grid-cell-placeholder:after { +.quickgrid[theme=default] > tbody > tr > td.grid-cell-placeholder.default:after { content: '\2026'; opacity: 0.75; } diff --git a/src/Components/test/E2ETest/Tests/VirtualizationTest.cs b/src/Components/test/E2ETest/Tests/VirtualizationTest.cs index cb1b221c117d..d45269677490 100644 --- a/src/Components/test/E2ETest/Tests/VirtualizationTest.cs +++ b/src/Components/test/E2ETest/Tests/VirtualizationTest.cs @@ -110,6 +110,39 @@ public void AlwaysFillsVisibleCapacity_Async() int GetPlaceholderCount() => Browser.FindElements(By.Id("async-placeholder")).Count; } + [Fact] + public void PlaceholdersHaveCorrectValue_Async() + { + var component = Browser.MountTestComponent(); + + var finishLoadingButton = Browser.Exists(By.Id("finish-loading-button")); + var startLoadingButton = Browser.Exists(By.Id("start-loading-button")); + //Load the initial data. + finishLoadingButton.Click(); + + Browser.True(() => GetItemCount() > 0); + Browser.Equal(0, () => GetPlaceholderCount()); + + //Start loading the second set of data to check for placeholders. + startLoadingButton.Click(); + Browser.ExecuteJavaScript("const container = document.getElementById('async-container'); container.scrollTop = container.scrollHeight * 0.5;"); + + Browser.Equal(0, () => GetItemCount()); + int placeholderCount = GetPlaceholderCount(); + Browser.True(() => GetPlaceholderCount() > 0); + + Assert.Equal("\"…\"", Browser.ExecuteJavaScript(@" + const p = document.querySelector('td.grid-cell-placeholder.default'); + return p ? getComputedStyle(p, '::after').content : null;")); + Assert.Equal("none", Browser.ExecuteJavaScript(@" + const p = document.querySelector('td.grid-cell-placeholder:not(.default)'); + return p ? getComputedStyle(p, '::after').content : null;")); + Browser.Equal("LOADING DATA", () => Browser.Exists(By.CssSelector(".grid-cell-placeholder .async-placeholder")).Text); + + int GetItemCount() => Browser.FindElements(By.CssSelector("#async-container tbody td.async-id:not(.grid-cell-placeholder)")).Count; + int GetPlaceholderCount() => Browser.FindElements(By.CssSelector("#async-container tbody .async-id.grid-cell-placeholder")).Count; + } + [Fact] public void RerendersWhenItemSizeShrinks_Sync() { diff --git a/src/Components/test/testassets/BasicTestApp/Index.razor b/src/Components/test/testassets/BasicTestApp/Index.razor index 6e8d20b391a2..23262b501bd4 100644 --- a/src/Components/test/testassets/BasicTestApp/Index.razor +++ b/src/Components/test/testassets/BasicTestApp/Index.razor @@ -117,6 +117,7 @@ + diff --git a/src/Components/test/testassets/BasicTestApp/VirtualizationQuickGrid.razor b/src/Components/test/testassets/BasicTestApp/VirtualizationQuickGrid.razor new file mode 100644 index 000000000000..58e7fc1eb7e8 --- /dev/null +++ b/src/Components/test/testassets/BasicTestApp/VirtualizationQuickGrid.razor @@ -0,0 +1,82 @@ +@using Microsoft.AspNetCore.Components.QuickGrid + + +@if (RendererInfo.IsInteractive) +{ + + + +
+ +
+ + + + + + LOADING DATA + + + +
+ + + +} + +@code { + record DataItem(int Id, int SecondNum); + QuickGrid asyncGrid; + + int asyncTotalItemCount = 200; + int asyncCancellationCount = 0; + + TaskCompletionSource asyncTcs = new TaskCompletionSource(); + + private async ValueTask> GetItemsAsync(GridItemsProviderRequest request) + { + var loadingTask = asyncTcs.Task; + var registration = request.CancellationToken.Register(() => CancelLoading(request.CancellationToken)); + + try + { + await loadingTask.WaitAsync(request.CancellationToken); + + var items = Enumerable.Range(request.StartIndex, request.Count ?? 200) + .Select(i => new DataItem(i, i * 2)) + .ToArray(); + + return GridItemsProviderResult.From(items, asyncTotalItemCount); + } + catch (OperationCanceledException) + { + throw; + } + finally + { + registration.Dispose(); + } + } + + + void StartNewAsyncLoad() + { + asyncTcs = new TaskCompletionSource(); + StateHasChanged(); + } + + void FinishLoading(int totalItemCount) + { + asyncTotalItemCount = totalItemCount; + asyncTcs.SetResult(); + StateHasChanged(); + } + + void CancelLoading(System.Threading.CancellationToken cancellationToken) + { + asyncTcs.TrySetCanceled(cancellationToken); + asyncTcs = new TaskCompletionSource(); + asyncCancellationCount++; + StateHasChanged(); + } +} diff --git a/src/Components/test/testassets/Components.TestServer/RazorComponents/ComplexValidationComponent.razor b/src/Components/test/testassets/Components.TestServer/RazorComponents/ComplexValidationComponent.razor index 2e4f4f958a69..e1f611d8d81c 100644 --- a/src/Components/test/testassets/Components.TestServer/RazorComponents/ComplexValidationComponent.razor +++ b/src/Components/test/testassets/Components.TestServer/RazorComponents/ComplexValidationComponent.razor @@ -3,7 +3,7 @@ @using Microsoft.AspNetCore.Components.Forms @if(RendererInfo.IsInteractive) { -

+

111

} @if (_invalid)