diff --git a/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml b/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml
index d1570ff922..166b0f6235 100644
--- a/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml
+++ b/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml
@@ -2001,7 +2001,8 @@
- Gets or sets a value indicating whether the grid is in a loading data state.
+ Gets or sets a value to indicate the grid loading data state.
+ If not set and a is present, the grid will show until the provider's first return.
@@ -4405,6 +4406,8 @@
This method requires dynamic access to code. This code may be removed by the trimmer.
+ If the assembly is not yet loaded, it will be loaded by the method `Assembly.Load`.
+ To avoid any issues, the assembly must be loaded before calling this method (e.g. adding an emoji in your code).
Raised when the is not found in predefined emojis.
@@ -4422,6 +4425,9 @@
+
+
+
FluentUI Emoji meta-data.
@@ -4992,6 +4998,8 @@
The to instantiate.
This method requires dynamic access to code. This code may be removed by the trimmer.
+ If the assembly is not yet loaded, it will be loaded by the method `Assembly.Load`.
+ To avoid any issues, the assembly must be loaded before calling this method (e.g. adding an icon in your code).
Raised when the is not found in predefined icons.
@@ -5009,6 +5017,9 @@
+
+
+
@@ -5232,6 +5243,11 @@
Gets the content type of the current file in an upload process.
+
+
+ Gets the last modified date of the current file in an upload process.
+
+
Gets the label to display in an upload process.
diff --git a/examples/Demo/Shared/Pages/DataGrid/Examples/DataGridRemoteData.razor b/examples/Demo/Shared/Pages/DataGrid/Examples/DataGridRemoteData.razor
index 8f49828f2c..00804f73af 100644
--- a/examples/Demo/Shared/Pages/DataGrid/Examples/DataGridRemoteData.razor
+++ b/examples/Demo/Shared/Pages/DataGrid/Examples/DataGridRemoteData.razor
@@ -5,7 +5,6 @@
}
- @if (Loading)
+ @if (EffectiveLoadingValue)
{
@_renderLoadingContent
}
diff --git a/src/Core/Components/DataGrid/FluentDataGrid.razor.cs b/src/Core/Components/DataGrid/FluentDataGrid.razor.cs
index f6d34a9443..9036b69197 100644
--- a/src/Core/Components/DataGrid/FluentDataGrid.razor.cs
+++ b/src/Core/Components/DataGrid/FluentDataGrid.razor.cs
@@ -239,10 +239,11 @@ public partial class FluentDataGrid : FluentComponentBase, IHandleEve
public RenderFragment? EmptyContent { get; set; }
///
- /// Gets or sets a value indicating whether the grid is in a loading data state.
+ /// Gets or sets a value to indicate the grid loading data state.
+ /// If not set and a is present, the grid will show until the provider's first return.
///
[Parameter]
- public bool Loading { get; set; }
+ public bool? Loading { get; set; }
///
/// Gets or sets the content to render when is true.
@@ -287,7 +288,11 @@ public partial class FluentDataGrid : FluentComponentBase, IHandleEve
/// Gets or sets a value indicating whether the grids' first cell should be focused.
///
[Parameter]
- public bool AutoFocus{ get; set; } = false;
+ public bool AutoFocus { get; set; } = false;
+
+ // Returns Loading if set (controlled). If not controlled,
+ // we assume the grid is loading until the next data load completes
+ internal bool EffectiveLoadingValue => Loading ?? ItemsProvider is not null;
private ElementReference? _gridReference;
private Virtualize<(int, TGridItem)>? _virtualizeComponent;
@@ -403,7 +408,7 @@ protected override Task OnParametersSetAsync()
Pagination?.ItemsPerPage != _lastRefreshedPaginationState?.ItemsPerPage
|| Pagination?.CurrentPageIndex != _lastRefreshedPaginationState?.CurrentPageIndex;
- var mustRefreshData = dataSourceHasChanged || paginationStateHasChanged;
+ var mustRefreshData = dataSourceHasChanged || paginationStateHasChanged || Loading is null;
// We don't want to trigger the first data load until we've collected the initial set of columns,
// because they might perform some action like setting the default sort order, so it would be wasteful
@@ -643,7 +648,7 @@ public Task ShowColumnResizeAsync(int index)
return (index >= 0 && index < _columns.Count) ? ShowColumnResizeAsync(_columns[index]) : Task.CompletedTask;
}
- public void SetLoadingState(bool loading)
+ public void SetLoadingState(bool? loading)
{
Loading = loading;
}
@@ -733,9 +738,10 @@ private async Task RefreshDataCoreAsync()
_internalGridContext.TotalViewItemCount = Pagination?.ItemsPerPage ?? providerResult.TotalItemCount;
Pagination?.SetTotalItemCountAsync(_internalGridContext.TotalItemCount);
- if (_internalGridContext.TotalItemCount > 0)
+ if (_internalGridContext.TotalItemCount > 0 && Loading is null)
{
Loading = false;
+ StateHasChanged();
}
// We're supplying the row _index along with each row's data because we need it for aria-rowindex, and we have to account for
@@ -757,9 +763,10 @@ private async ValueTask> ResolveItemsRequestA
if (ItemsProvider is not null)
{
var gipr = await ItemsProvider(request);
- if (gipr.Items is not null)
+ if (gipr.Items is not null && Loading is null)
{
Loading = false;
+ StateHasChanged();
}
return gipr;
}
@@ -791,7 +798,7 @@ private string AriaSortValue(ColumnBase column)
private string? StyleValue => new StyleBuilder(Style)
.AddStyle("grid-template-columns", _internalGridTemplateColumns, !string.IsNullOrWhiteSpace(_internalGridTemplateColumns))
.AddStyle("grid-template-rows", "auto 1fr", _internalGridContext.Items.Count == 0 || Items is null)
- .AddStyle("height", $"calc(100% - {(int)RowSize}px)", _internalGridContext.TotalItemCount == 0 || Loading)
+ .AddStyle("height", $"calc(100% - {(int)RowSize}px)", _internalGridContext.TotalItemCount == 0 || EffectiveLoadingValue)
.Build();
private string? ColumnHeaderClass(ColumnBase column)
diff --git a/src/Core/Components/DataGrid/FluentDataGridCell.razor.cs b/src/Core/Components/DataGrid/FluentDataGridCell.razor.cs
index 327a9339c0..fc7894c29a 100644
--- a/src/Core/Components/DataGrid/FluentDataGridCell.razor.cs
+++ b/src/Core/Components/DataGrid/FluentDataGridCell.razor.cs
@@ -68,15 +68,15 @@ public partial class FluentDataGridCell : FluentComponentBase
.Build();
protected string? StyleValue => new StyleBuilder(Style)
- .AddStyle("grid-column", GridColumn.ToString(), () => (!Grid.Loading && Grid.Items is not null) || Grid.Virtualize)
+ .AddStyle("grid-column", GridColumn.ToString(), () => (!Grid.EffectiveLoadingValue && Grid.Items is not null) || Grid.Virtualize)
.AddStyle("text-align", "center", Column is SelectColumn)
.AddStyle("align-content", "center", Column is SelectColumn)
.AddStyle("padding-inline-start", "calc(((var(--design-unit)* 3) + var(--focus-stroke-width) - var(--stroke-width))* 1px)", Column is SelectColumn && Owner.RowType == DataGridRowType.Default)
.AddStyle("padding-top", "calc(var(--design-unit) * 2.5px)", Column is SelectColumn && (Grid.RowSize == DataGridRowSize.Medium || Owner.RowType == DataGridRowType.Header))
.AddStyle("padding-top", "calc(var(--design-unit) * 1.5px)", Column is SelectColumn && Grid.RowSize == DataGridRowSize.Small && Owner.RowType == DataGridRowType.Default)
- .AddStyle("height", $"{Grid.ItemSize:0}px", () => !Grid.Loading && Grid.Virtualize && Owner.RowType == DataGridRowType.Default)
- .AddStyle("height", $"{(int)Grid.RowSize}px", () => !Grid.Loading && !Grid.Virtualize && Grid.Items is not null && !Grid.MultiLine)
- .AddStyle("height", "100%", InternalGridContext.TotalItemCount == 0 || (Grid.Loading && Grid.Items is null) || Grid.MultiLine)
+ .AddStyle("height", $"{Grid.ItemSize:0}px", () => !Grid.EffectiveLoadingValue && Grid.Virtualize && Owner.RowType == DataGridRowType.Default)
+ .AddStyle("height", $"{(int)Grid.RowSize}px", () => !Grid.EffectiveLoadingValue && !Grid.Virtualize && Grid.Items is not null && !Grid.MultiLine)
+ .AddStyle("height", "100%", InternalGridContext.TotalItemCount == 0 || (Grid.EffectiveLoadingValue && Grid.Items is null) || Grid.MultiLine)
.AddStyle("min-height", "44px", Owner.RowType != DataGridRowType.Default)
.AddStyle(Owner.Style)
.Build();
diff --git a/tests/Core/DataGrid/FluentDataGridTests.FluentDataGrid_Default.verified.razor.html b/tests/Core/DataGrid/FluentDataGridTests.FluentDataGrid_Default.verified.razor.html
new file mode 100644
index 0000000000..15f37cb1c5
--- /dev/null
+++ b/tests/Core/DataGrid/FluentDataGridTests.FluentDataGrid_Default.verified.razor.html
@@ -0,0 +1,23 @@
+
+
+
+
+ |
+
+ |
+
+
+
+
+ | Denis Voituron |
+
+
+ | Vincent Baaij |
+
+
+ | Bill Gates |
+
+
+
\ No newline at end of file
diff --git a/tests/Core/DataGrid/FluentDataGridTests.razor b/tests/Core/DataGrid/FluentDataGridTests.razor
new file mode 100644
index 0000000000..849667fe06
--- /dev/null
+++ b/tests/Core/DataGrid/FluentDataGridTests.razor
@@ -0,0 +1,156 @@
+@using FluentAssertions
+@using Xunit
+@inherits TestContext
+
+@code {
+ public FluentDataGridTests()
+ {
+ var dataGridModule = JSInterop.SetupModule("./_content/Microsoft.FluentUI.AspNetCore.Components/Components/DataGrid/FluentDataGrid.razor.js");
+ dataGridModule.SetupModule("init", _ => true);
+
+ // Register services
+ Services.AddSingleton(LibraryConfiguration.ForUnitTests);
+ Services.AddScoped(factory => new KeyCodeService());
+ }
+
+ [Fact]
+ public void FluentDataGrid_Default()
+ {
+ // Arrange && Act
+ var cut = Render>(
+ @
+
+
+
+ empty content
+ );
+
+ // Assert
+ cut.Verify();
+ }
+
+ [Fact]
+ public void FluentDataGrid_With_Empty_Items_Stays_Loading_Until_Changed()
+ {
+ // Arrange && Act
+ var cut = Render>(
+ @
+ empty content
+ loading content
+
+
+
+ );
+
+ // Assert
+ cut.Find("#loading-content").Should().NotBeNull();
+ Assert.Throws(() => cut.Find("#empty-content"));
+
+ cut.SetParametersAndRender(parameters => parameters
+ .Add(p => p.Loading, false));
+
+ Assert.Throws(() => cut.Find("#loading-content"));
+ cut.Find("#empty-content").Should().NotBeNull();
+ }
+
+ [Fact]
+ public async Task FluentDataGrid_With_ItemProvider_Stays_Loading_Until_ChangedAsync()
+ {
+ ValueTask> GetItems(GridItemsProviderRequest request)
+ {
+ return ValueTask.FromResult(GridItemsProviderResult.From(
+ Array.Empty(),
+ 0));
+ }
+
+ var cut = Render>(
+ @
+ empty content
+ loading content
+
+
+ @context.Name
+
+
+ );
+
+ // Assert
+ var dataGrid = cut.Instance;
+ cut.Find("#loading-content").Should().NotBeNull();
+
+ // should stay loading even after data refresh
+ await cut.InvokeAsync(() => dataGrid.RefreshDataAsync());
+ cut.Find("#loading-content").Should().NotBeNull();
+
+ // now not loading but still with 0 items, should render empty content
+ cut.SetParametersAndRender(parameters => parameters
+ .Add(p => p.Loading, false));
+
+ cut.Find("#empty-content").Should().NotBeNull();
+ }
+
+ [Fact]
+ public async Task FluentDataGrid_With_ItemProvider_And_Uncontrolled_Loading_Starts_Loading()
+ {
+ var tcs = new TaskCompletionSource();
+ async ValueTask> GetItems(GridItemsProviderRequest request)
+ {
+ await tcs.Task;
+ var numberOfItems = 1;
+ return GridItemsProviderResult.From(
+ GetCustomers().Take(numberOfItems).ToArray(),
+ numberOfItems);
+ }
+
+ var cut = Render>(
+ @
+ empty content
+ loading content
+
+
+ @context.Name
+
+
+ );
+
+ // Assert
+ var dataGrid = cut.Instance;
+
+ // Data is still loading, so loading content should be displayed
+ cut.Find("#loading-content").Should().NotBeNull();
+
+ tcs.SetResult();
+
+ // Data is no longer loading, so loading content should not be displayed after re-render
+ // wait for re-render here
+ cut.WaitForState(() => cut.Find("p").TextContent == GetCustomers().First().Name);
+
+ Assert.Throws(() => cut.Find("#loading-content"));
+
+ // should stay not loading even after data refresh
+ await cut.InvokeAsync(() => dataGrid.RefreshDataAsync());
+ Assert.Throws(() => cut.Find("#loading-content"));
+
+ // if we explicitly set Loading back to null, we should see the same behaviors because data should
+ // be refreshed
+ tcs = new TaskCompletionSource();
+ cut.SetParametersAndRender(parameters => parameters
+ .Add(p => p.Loading, null));
+ cut.Find("#loading-content").Should().NotBeNull();
+
+ tcs.SetResult();
+
+ cut.WaitForState(() => cut.Find("p").TextContent == GetCustomers().First().Name);
+ Assert.Throws(() => cut.Find("#loading-content"));
+ }
+
+ // Sample data...
+ private IEnumerable GetCustomers()
+ {
+ yield return new Customer(1, "Denis Voituron");
+ yield return new Customer(2, "Vincent Baaij");
+ yield return new Customer(3, "Bill Gates");
+ }
+
+ private record Customer(int Id, string Name);
+}
diff --git a/tests/Core/_ToDo/DataGrid/FluentDataGridTests.cs b/tests/Core/_ToDo/DataGrid/FluentDataGridTests.cs
deleted file mode 100644
index 1ee9ab1293..0000000000
--- a/tests/Core/_ToDo/DataGrid/FluentDataGridTests.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-using Bunit;
-using Microsoft.JSInterop;
-using Xunit;
-
-namespace Microsoft.FluentUI.AspNetCore.Components.Tests.DataGrid;
-public class FluentDataGridTests : TestBase
-{
- public FluentDataGridTests()
- {
- TestContext.JSInterop.SetupModule("./_content/Microsoft.FluentUI.AspNetCore.Components/Components/DataGrid/FluentDataGrid.razor.js");
- TestContext.JSInterop.Setup("init", _ => true);
- }
-
- [Fact(Skip = "Need to figure out how to do this test")]
- public void FluentDataGrid_Default()
- {
- //Arrange
- //Services.AddSingleton();
- var childContent = "render me";
- var emptyContent = "render me";
-
- bool virtualize = default!;
- float itemSize = default!;
- bool resizableColumns = default!;
- PaginationState pagination = default!;
- bool noTabbing = default!;
- GenerateHeaderOption? generateHeader = default!;
- string gridTemplateColumns = default!;
-
- var cut = TestContext.RenderComponent>(parameters => parameters
- .Add(p => p.Items, GetCustomers().AsQueryable())
-
- .AddChildContent(childContent)
- .Add(p => p.Virtualize, virtualize)
- .Add(p => p.ItemSize, itemSize)
- .Add(p => p.ResizableColumns, resizableColumns)
- .Add(p => p.Pagination, pagination)
- .Add(p => p.NoTabbing, noTabbing)
- .Add(p => p.GenerateHeader, generateHeader)
- .Add(p => p.GridTemplateColumns, gridTemplateColumns)
-
- .Add(p => p.EmptyContent, emptyContent)
- );
- //Act
-
- //Assert
- cut.Verify();
- }
-
- // Sample data...
- private IEnumerable GetCustomers()
- {
- yield return new Customer(1, "Denis Voituron");
- yield return new Customer(2, "Vincent Baaij");
- yield return new Customer(3, "Bill Gates");
- }
-
- private record Customer(int Id, string Name);
-}