diff --git a/SUPERDATAGRID.md b/SUPERDATAGRID.md index 5325e98..c4949fb 100644 --- a/SUPERDATAGRID.md +++ b/SUPERDATAGRID.md @@ -266,7 +266,7 @@ When your model implements `IDataItem`: | Parameter | Type | Default | Description | |---|---|---|---| -| `AllowColumnReorder` | `bool` | `true` | Allow drag-and-drop column reordering | +| `AllowColumnReorder` | `bool` | `true` | Allow column reordering with up/down buttons in the Columns menu | | `AllowColumnResize` | `bool` | `true` | Allow column resize via drag handles | | `AllowSorting` | `bool` | `true` | Enable column sorting on header click | | `AllowFiltering` | `bool` | `true` | Display filter controls in the header row | diff --git a/src/DemoWebSite/_temp/data-grid-exports/e4afaf7eca362f7d1afc0ae8a250468641e73f93cf3f118548ca7a15fe169100.xlsx b/src/DemoWebSite/_temp/data-grid-exports/e4afaf7eca362f7d1afc0ae8a250468641e73f93cf3f118548ca7a15fe169100.xlsx deleted file mode 100644 index d2c1d87..0000000 Binary files a/src/DemoWebSite/_temp/data-grid-exports/e4afaf7eca362f7d1afc0ae8a250468641e73f93cf3f118548ca7a15fe169100.xlsx and /dev/null differ diff --git a/src/SuperBlazorComponents.DataGridExporter/SuperBlazorComponents.DataGridExporter.csproj b/src/SuperBlazorComponents.DataGridExporter/SuperBlazorComponents.DataGridExporter.csproj index 5975cbf..383e526 100644 --- a/src/SuperBlazorComponents.DataGridExporter/SuperBlazorComponents.DataGridExporter.csproj +++ b/src/SuperBlazorComponents.DataGridExporter/SuperBlazorComponents.DataGridExporter.csproj @@ -5,7 +5,7 @@ enable SuperBlazorComponents.DataGridExporter SuperBlazorComponents.DataGridExporter - 2.0.12 + 2.0.13 SuperBlazorComponents.DataGridExporter SuperBlazorComponents DataGrid Exporter Appliman diff --git a/src/SuperBlazorComponents/Components/Buttons/SuperSplitButton.razor b/src/SuperBlazorComponents/Components/Buttons/SuperSplitButton.razor index c233765..854eb6a 100644 --- a/src/SuperBlazorComponents/Components/Buttons/SuperSplitButton.razor +++ b/src/SuperBlazorComponents/Components/Buttons/SuperSplitButton.razor @@ -3,7 +3,10 @@ @implements IDisposable
- - + @if (Click.HasDelegate) + { + + } diff --git a/src/SuperBlazorComponents/Components/SuperDataGrid/Tools/SuperDatagridColumnVisibilityButton.razor.css b/src/SuperBlazorComponents/Components/SuperDataGrid/Tools/SuperDatagridColumnVisibilityButton.razor.css index 334ae74..c732a34 100644 --- a/src/SuperBlazorComponents/Components/SuperDataGrid/Tools/SuperDatagridColumnVisibilityButton.razor.css +++ b/src/SuperBlazorComponents/Components/SuperDataGrid/Tools/SuperDatagridColumnVisibilityButton.razor.css @@ -4,7 +4,8 @@ } .super-datagrid-columns-menu { - min-width: 220px; + min-width: min(300px, calc(100vw - 1rem)); + max-width: calc(100vw - 1rem); max-height: 320px; overflow-y: auto; padding: 0.35rem; @@ -25,3 +26,32 @@ .super-datagrid-column-item .form-check-input { margin-top: 0; } + +.super-datagrid-column-row { + display: flex; + align-items: center; +} + +.super-datagrid-column-item { + flex: 1; + min-width: 0; + margin-bottom: 0; + white-space: normal; + overflow-wrap: anywhere; +} + +.super-datagrid-column-item .form-check-input { + flex-shrink: 0; +} + +.super-datagrid-column-move { + display: flex; + flex-shrink: 0; + gap: 0.25rem; + margin-left: auto; +} + +.super-datagrid-column-move .btn { + min-width: 28px; + min-height: 28px; +} diff --git a/src/SuperBlazorComponents/Localization/Resources/SuperBlazorComponents.en.json b/src/SuperBlazorComponents/Localization/Resources/SuperBlazorComponents.en.json index a507a50..38f8c11 100644 --- a/src/SuperBlazorComponents/Localization/Resources/SuperBlazorComponents.en.json +++ b/src/SuperBlazorComponents/Localization/Resources/SuperBlazorComponents.en.json @@ -1,4 +1,8 @@ { + "DataGrid.Columns.Reset": "Reset columns", + "DataGrid.Columns.MoveUp": "Move column {0} up", + "DataGrid.Columns.MoveDown": "Move column {0} down", + "DataGrid.Refresh.Tooltip": "Refresh list contents", "DataGrid.Reset.Tooltip": "Reset grid configuration and remove saved preferences", "DataGrid.Columns.Label": "Columns", diff --git a/src/SuperBlazorComponents/Localization/Resources/SuperBlazorComponents.fr.json b/src/SuperBlazorComponents/Localization/Resources/SuperBlazorComponents.fr.json index 9f800b2..d8692a1 100644 --- a/src/SuperBlazorComponents/Localization/Resources/SuperBlazorComponents.fr.json +++ b/src/SuperBlazorComponents/Localization/Resources/SuperBlazorComponents.fr.json @@ -1,4 +1,8 @@ { + "DataGrid.Columns.Reset": "Réinitialiser les colonnes", + "DataGrid.Columns.MoveUp": "Monter la colonne {0}", + "DataGrid.Columns.MoveDown": "Descendre la colonne {0}", + "DataGrid.Refresh.Tooltip": "Rafraîchir le contenu de la liste", "DataGrid.Reset.Tooltip": "Réinitialiser la configuration de la grille et supprimer les préférences enregistrées", "DataGrid.Columns.Label": "Colonnes", diff --git a/src/SuperBlazorComponents/SuperBlazorComponents.csproj b/src/SuperBlazorComponents/SuperBlazorComponents.csproj index 19d3388..e8cd61a 100644 --- a/src/SuperBlazorComponents/SuperBlazorComponents.csproj +++ b/src/SuperBlazorComponents/SuperBlazorComponents.csproj @@ -4,7 +4,7 @@ net10.0 enable enable - 2.0.9 + 2.0.10 false SuperBlazorComponents SuperBlazorComponents diff --git a/tests/SuperBlazorComponents.Tests/SuperDataGridColumnOrderTests.cs b/tests/SuperBlazorComponents.Tests/SuperDataGridColumnOrderTests.cs new file mode 100644 index 0000000..37605b4 --- /dev/null +++ b/tests/SuperBlazorComponents.Tests/SuperDataGridColumnOrderTests.cs @@ -0,0 +1,123 @@ +using Bunit; +using Microsoft.AspNetCore.Components; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using SuperBlazorComponents.Components.SuperDataGrid; + +namespace SuperBlazorComponents.Tests; + +#pragma warning disable BL0005 // Tests deliberately change the column parameter to verify guards. + +[TestClass] +public sealed class SuperDataGridColumnOrderTests +{ + [TestMethod] + public void MenuMovesColumnsAndResetRestoresDeclaredOrder() + { + using var context = CreateContext(); + var storage = new MemoryStorage(); + var grid = RenderGrid(context, storage); + Assert.AreEqual(0, grid.FindAll("[draggable], .sdg-drag-handle").Count); + Assert.IsTrue(grid.FindAll(".super-datagrid-column-move button")[0].HasAttribute("disabled")); + Assert.IsTrue(grid.FindAll(".super-datagrid-column-move button")[5].HasAttribute("disabled")); + + grid.FindAll(".super-datagrid-column-move button")[1].Click(); + CollectionAssert.AreEqual(new[] { "B", "A", "C" }, Order(grid)); + CollectionAssert.AreEqual(new[] { "B", "A", "C" }, storage.Saved!.Select(c => c.PropertyName).ToArray()); + Assert.AreEqual("B", grid.Find(".super-datagrid-column-item span").TextContent); + grid.FindAll(".super-datagrid-column-move button")[2].Click(); + CollectionAssert.AreEqual(new[] { "A", "B", "C" }, Order(grid)); + grid.FindAll(".super-datagrid-column-move button")[1].Click(); + grid.Find(".super-datagrid-columns-menu > li > button").Click(); + CollectionAssert.AreEqual(new[] { "A", "B", "C" }, Order(grid)); + Assert.IsTrue(storage.Cleared); + } + + [TestMethod] + public async Task ReorderRespectsDisabledSettingAndFixedColumns() + { + using var context = CreateContext(); + var grid = RenderGrid(context, new MemoryStorage(), allowReorder: false); + Assert.AreEqual(0, grid.FindAll(".super-datagrid-column-move").Count); + await grid.InvokeAsync(() => grid.Instance.MoveColumnAsync(0, 1)); + CollectionAssert.AreEqual(new[] { "A", "B", "C" }, Order(grid)); + grid.Render(p => p.Add(g => g.AllowColumnReorder, true)); + await grid.InvokeAsync(() => grid.Instance.ColumnsCollection[1].Reorderable = false); + Assert.IsFalse(grid.Instance.CanMoveColumn(0, 1)); + Assert.IsFalse(grid.Instance.CanMoveColumn(1, -1)); + Assert.IsFalse(grid.Instance.CanMoveColumn(2, -1)); + Assert.IsFalse(grid.Instance.CanMoveColumn(0, 2)); + await grid.InvokeAsync(() => grid.Instance.MoveColumnAsync(0, 1)); + CollectionAssert.AreEqual(new[] { "A", "B", "C" }, Order(grid)); + } + + [TestMethod] + public void ResetRestoresDeclaredOrderAfterLoadingSavedPreferences() + { + using var context = CreateContext(); + var storage = new MemoryStorage + { + Saved = new[] { "C", "B", "A" }.Select((name, index) => + new SuperDataGridColumnSettings { PropertyName = name, Order = index, IsVisible = true }).ToList() + }; + var grid = RenderGrid(context, storage); + CollectionAssert.AreEqual(new[] { "C", "B", "A" }, Order(grid)); + grid.Find(".super-datagrid-columns-menu > li > button").Click(); + CollectionAssert.AreEqual(new[] { "A", "B", "C" }, Order(grid)); + } + + private static string[] Order(IRenderedComponent> grid) + => grid.Instance.ColumnsCollection.Select(c => c.Property).ToArray(); + + private static BunitContext CreateContext() + { + var context = new BunitContext(); + context.JSInterop.Mode = JSRuntimeMode.Loose; + context.Services.AddLogging(); + context.Services.AddSuperComponents(); + return context; + } + + private static IRenderedComponent> RenderGrid( + BunitContext context, MemoryStorage storage, bool allowReorder = true) + { + context.Services.AddSingleton(storage); + return context.Render>(p => p + .Add(g => g.GridId, "column-order") + .Add(g => g.ItemsProvider, _ => ValueTask.FromResult(GridItemsProviderResult.Empty())) + .Add(g => g.AllowColumnReorder, allowReorder) + .AddChildContent(builder => + { + foreach (var name in new[] { "A", "B", "C" }) + { + builder.OpenComponent>(0); + builder.SetKey(name); + builder.AddAttribute(1, "Property", name); + builder.AddAttribute(2, "Title", name); + builder.CloseComponent(); + } + })); + } + + private sealed class MemoryStorage : ISuperDataGridSettingsStorage + { + public List? Saved { get; set; } + public bool Cleared { get; private set; } + + public Task?> GetSettingsAsync(string gridId, CancellationToken cancellationToken = default) + => Task.FromResult?>(Saved); + + public Task SaveSettingsAsync(string gridId, IEnumerable settings, CancellationToken cancellationToken = default) + { + Saved = settings.ToList(); + return Task.CompletedTask; + } + + public Task ClearSettingsAsync(string gridId, CancellationToken cancellationToken = default) + { + Saved = null; + Cleared = true; + return Task.CompletedTask; + } + } +}