-
-
+
+
-
+
...
@@ -64,16 +60,17 @@ property `--fluent-data-grid-pinned-background`:
## Notes
* Column resizing interacts correctly with sticky offsets — the JavaScript in
- `FluentDataGrid.razor.ts` recalculates `left` / `right` values after every resize step via
- `UpdatePinnedColumnOffsets`.
+ `FluentDataGrid.razor.ts` recalculates `inset-inline-start` / `inset-inline-end` values after
+ every resize step via `UpdatePinnedColumnOffsets`.
* Virtualization and paging are fully compatible because each rendered row's cells carry the
same `position: sticky` styling regardless of which page or scroll position is active.
-* In RTL layouts the browser interprets `left` / `right` according to the document direction, so
- pinned columns behave correctly without additional configuration.
+* RTL layouts are fully supported: the CSS logical properties `inset-inline-start` and
+ `inset-inline-end` automatically map to the correct physical direction based on the document's
+ writing mode.
## Example
-Demonstrates pinned (frozen) columns using `Pin="DataGridColumnPin.Left"` and `Pin="DataGridColumnPin.Right"`.
+Demonstrates pinned (frozen) columns using `Pin="DataGridColumnPin.Start"` and `Pin="DataGridColumnPin.End"`.
The two leftmost columns and the Actions column remain visible while the rest scroll horizontally.
Wrap the grid in a `
` container and give the grid a `Style="min-width: max-content;"`
diff --git "a/src/Core.Scripts/obj\\Debug/\\package.g.props" "b/src/Core.Scripts/obj\\Debug/\\package.g.props"
new file mode 100644
index 0000000000..4b7b3b41a5
--- /dev/null
+++ "b/src/Core.Scripts/obj\\Debug/\\package.g.props"
@@ -0,0 +1,29 @@
+
+
+
+ true
+ microsoft.fluentui.aspnetcore.components.assets
+ src/index.ts
+ dist/Microsoft.FluentUI.AspNetCore.Components.lib.module.js
+ ../Core/Components/**/*.css
+ dist/Microsoft.FluentUI.AspNetCore.Components.bundle.scp.css
+ node ./esbuild.config.mjs
+ rimraf ./dist
+ []
+
+ ISC
+ module
+ ^1.15.9
+ 8.57.1
+ 8.57.1
+ 0.27.4
+ 0.0.1
+ 10.0.3
+ ^13.0.6
+ ^7.6.1
+ 6.1.3
+ 6.4.0
+ 5.9.3
+ ^3.0.0-rc.9
+
+
\ No newline at end of file
diff --git a/src/Core/Components/DataGrid/Columns/ColumnBase.razor.cs b/src/Core/Components/DataGrid/Columns/ColumnBase.razor.cs
index d5e53fa542..4f9f1ad86f 100644
--- a/src/Core/Components/DataGrid/Columns/ColumnBase.razor.cs
+++ b/src/Core/Components/DataGrid/Columns/ColumnBase.razor.cs
@@ -188,21 +188,21 @@ public abstract partial class ColumnBase
public string? Width { get; set; }
///
- /// Gets or sets whether this column is pinned (frozen) to the left or right edge of the grid,
+ /// Gets or sets whether this column is pinned (frozen) to the inline-start or inline-end edge of the grid,
/// so it remains visible when the user scrolls horizontally.
/// Pinned columns require an explicit in pixels (e.g., "150px").
- /// Left-pinned columns must be contiguous at the start of the column list;
- /// right-pinned columns must be contiguous at the end.
+ /// Start-pinned columns must be contiguous at the start of the column list;
+ /// end-pinned columns must be contiguous at the end.
///
[Parameter]
public DataGridColumnPin Pin { get; set; } = DataGridColumnPin.None;
///
- /// The sticky left or right CSS offset (in pixels) computed by
+ /// The sticky inset-inline-start or inset-inline-end CSS offset computed by
/// when columns are collected.
/// Not intended for direct use by consumers.
///
- internal double PinOffsetPx { get; set; }
+ internal string PinOffset { get; set; } = "0px";
///
/// Gets or sets the minimal width of the column.
diff --git a/src/Core/Components/DataGrid/FluentDataGrid.razor.cs b/src/Core/Components/DataGrid/FluentDataGrid.razor.cs
index e1fa32b112..f5cac8f869 100644
--- a/src/Core/Components/DataGrid/FluentDataGrid.razor.cs
+++ b/src/Core/Components/DataGrid/FluentDataGrid.razor.cs
@@ -624,12 +624,12 @@ private void FinishCollectingColumns()
}
///
- /// Validates the pinned-column configuration and computes the sticky pixel offsets for each
+ /// Validates the pinned-column configuration and computes the sticky offsets for each
/// pinned column. Rules enforced:
///
/// - Pinned columns must specify an explicit pixel Width (e.g., "150px").
- /// - Left-pinned columns must be contiguous at the beginning of the column list.
- /// - Right-pinned columns must be contiguous at the end of the column list.
+ /// - Start-pinned columns must be contiguous at the beginning of the column list.
+ /// - End-pinned columns must be contiguous at the end of the column list.
///
///
private void ValidateAndComputePinnedColumns()
@@ -642,20 +642,20 @@ private void ValidateAndComputePinnedColumns()
ValidatePinnedColumnConstraints();
- // Compute left-pin sticky offsets (cumulative left-to-right).
- var leftOffset = 0.0;
- foreach (var col in _columns.Where(c => c.Pin == DataGridColumnPin.Left))
+ // Compute start-pin sticky offsets (cumulative inline-start to inline-end).
+ var startOffset = 0.0;
+ foreach (var col in _columns.Where(c => c.Pin == DataGridColumnPin.Start))
{
- col.PinOffsetPx = leftOffset;
- leftOffset += ParsePixelWidth(col.Width);
+ col.PinOffset = $"{startOffset.ToString(CultureInfo.InvariantCulture)}px";
+ startOffset += ParsePixelWidth(col.Width);
}
- // Compute right-pin sticky offsets (cumulative right-to-left).
- var rightOffset = 0.0;
- foreach (var col in _columns.Where(c => c.Pin == DataGridColumnPin.Right).Reverse())
+ // Compute end-pin sticky offsets (cumulative inline-end to inline-start).
+ var endOffset = 0.0;
+ foreach (var col in _columns.Where(c => c.Pin == DataGridColumnPin.End).Reverse())
{
- col.PinOffsetPx = rightOffset;
- rightOffset += ParsePixelWidth(col.Width);
+ col.PinOffset = $"{endOffset.ToString(CultureInfo.InvariantCulture)}px";
+ endOffset += ParsePixelWidth(col.Width);
}
}
@@ -683,27 +683,27 @@ private void ValidatePinnedColumnConstraints()
}
}
- // Left-pinned columns must be contiguous at the start: each one must be preceded by
- // another left-pinned column (or be the very first column).
+ // Start-pinned columns must be contiguous at the start: each one must be preceded by
+ // another start-pinned column (or be the very first column).
for (var i = 0; i < _columns.Count; i++)
{
- if (_columns[i].Pin == DataGridColumnPin.Left && i > 0 && _columns[i - 1].Pin != DataGridColumnPin.Left)
+ if (_columns[i].Pin == DataGridColumnPin.Start && i > 0 && _columns[i - 1].Pin != DataGridColumnPin.Start)
{
throw new ArgumentException(
- $"Column '{_columns[i].Title ?? _columns[i].Index.ToString(CultureInfo.InvariantCulture)}' is left-pinned but the preceding column is not. " +
- "Left-pinned columns must be contiguous at the start of the column list.");
+ $"Column '{_columns[i].Title ?? _columns[i].Index.ToString(CultureInfo.InvariantCulture)}' is start-pinned but the preceding column is not. " +
+ "Start-pinned columns must be contiguous at the start of the column list.");
}
}
- // Right-pinned columns must be contiguous at the end: each one must be followed by
- // another right-pinned column (or be the very last column).
+ // End-pinned columns must be contiguous at the end: each one must be followed by
+ // another end-pinned column (or be the very last column).
for (var i = 0; i < _columns.Count; i++)
{
- if (_columns[i].Pin == DataGridColumnPin.Right && i < _columns.Count - 1 && _columns[i + 1].Pin != DataGridColumnPin.Right)
+ if (_columns[i].Pin == DataGridColumnPin.End && i < _columns.Count - 1 && _columns[i + 1].Pin != DataGridColumnPin.End)
{
throw new ArgumentException(
- $"Column '{_columns[i].Title ?? _columns[i].Index.ToString(CultureInfo.InvariantCulture)}' is right-pinned but the following column is not. " +
- "Right-pinned columns must be contiguous at the end of the column list.");
+ $"Column '{_columns[i].Title ?? _columns[i].Index.ToString(CultureInfo.InvariantCulture)}' is end-pinned but the following column is not. " +
+ "End-pinned columns must be contiguous at the end of the column list.");
}
}
}
diff --git a/src/Core/Components/DataGrid/FluentDataGrid.razor.css b/src/Core/Components/DataGrid/FluentDataGrid.razor.css
index c86445a2bf..d03de008ea 100644
--- a/src/Core/Components/DataGrid/FluentDataGrid.razor.css
+++ b/src/Core/Components/DataGrid/FluentDataGrid.razor.css
@@ -100,23 +100,23 @@
/* ---- Pinned (sticky) columns ---- */
/* Background: keeps content from showing through when scrolling. Override --fluent-data-grid-pinned-background to theme. */
-.fluent-data-grid th.col-pinned-left,
-.fluent-data-grid th.col-pinned-right {
+.fluent-data-grid th.col-pinned-start,
+.fluent-data-grid th.col-pinned-end {
background-color: var(--colorNeutralBackground1);
}
-.fluent-data-grid td.col-pinned-left,
-.fluent-data-grid td.col-pinned-right {
+.fluent-data-grid td.col-pinned-start,
+.fluent-data-grid td.col-pinned-end {
background-color: var(--fluent-data-grid-pinned-background);
}
-/* Visual separator on the trailing edge of the last left-pinned column */
-.fluent-data-grid td:nth-last-child(1 of .col-pinned-left) {
+/* Visual separator on the trailing edge of the last start-pinned column */
+.fluent-data-grid td:nth-last-child(1 of .col-pinned-start) {
border-inline-end: var(--strokeWidthThin) solid var(--colorNeutralStroke1);
}
-/* Visual separator on the leading edge of the first right-pinned column */
-.fluent-data-grid td:nth-child(1 of .col-pinned-right) {
+/* Visual separator on the leading edge of the first end-pinned column */
+.fluent-data-grid td:nth-child(1 of .col-pinned-end) {
border-inline-start: var(--strokeWidthThin) solid var(--colorNeutralStroke1);
}
@@ -124,13 +124,13 @@
For UseMenuService=false the inline z-index: 5 is absent, so we provide a floor here.
The sticky-header row needs the highest value to beat both the sticky-header row z-index (2)
and the pinned data cell z-index (1). */
-.fluent-data-grid th.col-pinned-left,
-.fluent-data-grid th.col-pinned-right {
+.fluent-data-grid th.col-pinned-start,
+.fluent-data-grid th.col-pinned-end {
z-index: 2;
}
-.fluent-data-grid tr[row-type='sticky-header'] > th.col-pinned-left,
-.fluent-data-grid tr[row-type='sticky-header'] > th.col-pinned-right {
+.fluent-data-grid tr[row-type='sticky-header'] > th.col-pinned-start,
+.fluent-data-grid tr[row-type='sticky-header'] > th.col-pinned-end {
background-color: var(--colorNeutralBackground4);
z-index: 4;
}
diff --git a/src/Core/Components/DataGrid/FluentDataGrid.razor.ts b/src/Core/Components/DataGrid/FluentDataGrid.razor.ts
index 2cb37b0ca9..395f90313f 100644
--- a/src/Core/Components/DataGrid/FluentDataGrid.razor.ts
+++ b/src/Core/Components/DataGrid/FluentDataGrid.razor.ts
@@ -564,10 +564,10 @@ export namespace Microsoft.FluentUI.Blazor.DataGrid {
* pinned column so that columns stack correctly against the grid edge after the initial
* render or after a column is resized.
*
- * Left-pinned columns are processed left-to-right; each column's offset is the sum of
- * the widths of all left-pinned columns to its left.
- * Right-pinned columns are processed right-to-left; each column's offset is the sum of
- * the widths of all right-pinned columns to its right.
+ * Start-pinned columns are processed in DOM order; each column's offset is the sum of
+ * the widths of all start-pinned columns before it.
+ * End-pinned columns are processed in reverse DOM order; each column's offset is the sum of
+ * the widths of all end-pinned columns after it.
*
* The function reads the actual rendered header-cell width so it handles both Grid mode
* (CSS grid layout) and Table mode (standard table layout). Grid mode uses `offsetWidth`
@@ -592,7 +592,7 @@ export namespace Microsoft.FluentUI.Blazor.DataGrid {
* Applies a cumulative sticky offset to all cells in a column and returns the new
* running total to be used by the next column in the sequence.
*/
- function applyOffset(header: HTMLElement, offset: number, side: 'left' | 'right'): number {
+ function applyOffset(header: HTMLElement, offset: number, side: 'insetInlineStart' | 'insetInlineEnd'): number {
const colIndex = header.getAttribute('col-index');
if (!colIndex) { return offset; }
@@ -602,24 +602,24 @@ export namespace Microsoft.FluentUI.Blazor.DataGrid {
return offset + headerWidth(header);
}
- // Left-pinned columns: process in DOM (left-to-right) order.
- const leftPinnedHeaders = Array.from(
- gridElement.querySelectorAll('th.col-pinned-left')
+ // Start-pinned columns: process in DOM order.
+ const startPinnedHeaders = Array.from(
+ gridElement.querySelectorAll('th.col-pinned-start')
) as HTMLElement[];
- let leftOffset = 0;
- for (const header of leftPinnedHeaders) {
- leftOffset = applyOffset(header, leftOffset, 'left');
+ let startOffset = 0;
+ for (const header of startPinnedHeaders) {
+ startOffset = applyOffset(header, startOffset, 'insetInlineStart');
}
- // Right-pinned columns: process in reverse DOM (right-to-left) order.
- const rightPinnedHeaders = Array.from(
- gridElement.querySelectorAll('th.col-pinned-right')
+ // End-pinned columns: process in reverse DOM order.
+ const endPinnedHeaders = Array.from(
+ gridElement.querySelectorAll('th.col-pinned-end')
) as HTMLElement[];
- let rightOffset = 0;
- for (let i = rightPinnedHeaders.length - 1; i >= 0; i--) {
- rightOffset = applyOffset(rightPinnedHeaders[i], rightOffset, 'right');
+ let endOffset = 0;
+ for (let i = endPinnedHeaders.length - 1; i >= 0; i--) {
+ endOffset = applyOffset(endPinnedHeaders[i], endOffset, 'insetInlineEnd');
}
}
}
diff --git a/src/Core/Components/DataGrid/FluentDataGridCell.razor.cs b/src/Core/Components/DataGrid/FluentDataGridCell.razor.cs
index 22b1173456..14dc98280a 100644
--- a/src/Core/Components/DataGrid/FluentDataGridCell.razor.cs
+++ b/src/Core/Components/DataGrid/FluentDataGridCell.razor.cs
@@ -31,8 +31,8 @@ public FluentDataGridCell(LibraryConfiguration configuration) : base(configurati
.AddClass("column-header", when: CellType == DataGridCellType.ColumnHeader)
.AddClass("select-all", when: CellType == DataGridCellType.ColumnHeader && Column is SelectColumn)
.AddClass("multiline-text", when: Grid.MultiLine && (Grid.Items is not null || Grid.ItemsProvider is not null) && CellType != DataGridCellType.ColumnHeader)
- .AddClass("col-pinned-left", Column?.Pin == DataGridColumnPin.Left)
- .AddClass("col-pinned-right", Column?.Pin == DataGridColumnPin.Right)
+ .AddClass("col-pinned-start", Column?.Pin == DataGridColumnPin.Start)
+ .AddClass("col-pinned-end", Column?.Pin == DataGridColumnPin.End)
.AddClass(Owner.Class)
.Build();
@@ -50,8 +50,8 @@ public FluentDataGridCell(LibraryConfiguration configuration) : base(configurati
.AddStyle("height", "100%", Grid.MultiLine)
.AddStyle("min-height", "40px", Owner.RowType != DataGridRowType.Default)
.AddStyle("position", "sticky", Column != null && Column.Pin != DataGridColumnPin.None)
- .AddStyle("left", $"{(Column?.PinOffsetPx ?? 0).ToString(CultureInfo.InvariantCulture)}px", Column?.Pin == DataGridColumnPin.Left)
- .AddStyle("right", $"{(Column?.PinOffsetPx ?? 0).ToString(CultureInfo.InvariantCulture)}px", Column?.Pin == DataGridColumnPin.Right)
+ .AddStyle("inset-inline-start", Column?.PinOffset ?? "0px", Column?.Pin == DataGridColumnPin.Start)
+ .AddStyle("inset-inline-end", Column?.PinOffset ?? "0px", Column?.Pin == DataGridColumnPin.End)
.AddStyle("z-index", "1", Column != null && Column.Pin != DataGridColumnPin.None && CellType == DataGridCellType.Default)
.AddStyle(Owner.Style)
.Build();
diff --git a/src/Core/Enums/DataGridColumnPin.cs b/src/Core/Enums/DataGridColumnPin.cs
index 4aa90cf098..7862eb9779 100644
--- a/src/Core/Enums/DataGridColumnPin.cs
+++ b/src/Core/Enums/DataGridColumnPin.cs
@@ -16,14 +16,14 @@ public enum DataGridColumnPin
None,
///
- /// The column is pinned to the left edge of the grid.
- /// The column will remain visible when the user scrolls right.
+ /// The column is pinned to the inline-start edge of the grid (left in LTR, right in RTL).
+ /// The column will remain visible when the user scrolls toward the inline-end.
///
- Left,
+ Start,
///
- /// The column is pinned to the right edge of the grid.
- /// The column will remain visible when the user scrolls left.
+ /// The column is pinned to the inline-end edge of the grid (right in LTR, left in RTL).
+ /// The column will remain visible when the user scrolls toward the inline-start.
///
- Right,
+ End,
}
diff --git a/tests/Core/Components/DataGrid/FluentDataGridPinnedColumnTests.FluentDataGrid_PinnedColumn_Snapshot.verified.razor.html b/tests/Core/Components/DataGrid/FluentDataGridPinnedColumnTests.FluentDataGrid_PinnedColumn_Snapshot.verified.razor.html
index 90d91a2865..656413b651 100644
--- a/tests/Core/Components/DataGrid/FluentDataGridPinnedColumnTests.FluentDataGrid_PinnedColumn_Snapshot.verified.razor.html
+++ b/tests/Core/Components/DataGrid/FluentDataGridPinnedColumnTests.FluentDataGrid_PinnedColumn_Snapshot.verified.razor.html
@@ -4,14 +4,14 @@
- |
+ |
|
-
+ |
|
-
+ |
Action
@@ -36,22 +36,22 @@
|
- | 1 |
- Denis Voituron |
+ 1 |
+ Denis Voituron |
Brussels |
- Edit |
+ Edit |
- | 2 |
- Vincent Baaij |
+ 2 |
+ Vincent Baaij |
Amsterdam |
- Edit |
+ Edit |
- | 3 |
- Bill Gates |
+ 3 |
+ Bill Gates |
Medina |
- Edit |
+ Edit |
diff --git a/tests/Core/Components/DataGrid/FluentDataGridPinnedColumnTests.razor b/tests/Core/Components/DataGrid/FluentDataGridPinnedColumnTests.razor
index 0435aeaaf5..e893f6174e 100644
--- a/tests/Core/Components/DataGrid/FluentDataGridPinnedColumnTests.razor
+++ b/tests/Core/Components/DataGrid/FluentDataGridPinnedColumnTests.razor
@@ -31,43 +31,43 @@
}
// -------------------------------------------------------------------------
- // Single pinned-left column: correct CSS class and sticky style
+ // Single pinned-start column: correct CSS class and sticky style
// -------------------------------------------------------------------------
[Fact]
- public void FluentDataGrid_PinnedColumn_Left_Has_Sticky_Class_And_Style()
+ public void FluentDataGrid_PinnedColumn_Start_Has_Sticky_Class_And_Style()
{
// Arrange && Act
var cut = Render>(
@
-
+
);
- // Assert — every cell in the column carries col-pinned-left and position:sticky;left:0px
+ // Assert — every cell in the column carries col-pinned-start and position:sticky;inset-inline-start:0px
var cells = cut.FindAll("[col-index='1']");
Assert.NotEmpty(cells);
Assert.All(cells, cell =>
{
- Assert.Contains("col-pinned-left", cell.ClassName);
+ Assert.Contains("col-pinned-start", cell.ClassName);
Assert.Contains("position: sticky", cell.GetAttribute("style") ?? "");
- Assert.Contains("left: 0px", cell.GetAttribute("style") ?? "");
+ Assert.Contains("inset-inline-start: 0px", cell.GetAttribute("style") ?? "");
});
}
// -------------------------------------------------------------------------
- // Single pinned-right column: correct CSS class and sticky style
+ // Single pinned-end column: correct CSS class and sticky style
// -------------------------------------------------------------------------
[Fact]
- public void FluentDataGrid_PinnedColumn_Right_Has_Sticky_Class_And_Style()
+ public void FluentDataGrid_PinnedColumn_End_Has_Sticky_Class_And_Style()
{
// Arrange && Act
var cut = Render>(
@
-
+
);
@@ -75,9 +75,9 @@
Assert.NotEmpty(cells);
Assert.All(cells, cell =>
{
- Assert.Contains("col-pinned-right", cell.ClassName);
+ Assert.Contains("col-pinned-end", cell.ClassName);
Assert.Contains("position: sticky", cell.GetAttribute("style") ?? "");
- Assert.Contains("right: 0px", cell.GetAttribute("style") ?? "");
+ Assert.Contains("inset-inline-end: 0px", cell.GetAttribute("style") ?? "");
});
}
@@ -87,7 +87,7 @@
var cut = Render>(
@
-
+
);
@@ -97,53 +97,53 @@
}
// -------------------------------------------------------------------------
- // Two left-pinned columns: cumulative offsets
+ // Two start-pinned columns: cumulative offsets
// -------------------------------------------------------------------------
[Fact]
- public void FluentDataGrid_PinnedColumn_Left_Multiple_Cumulative_Offsets()
+ public void FluentDataGrid_PinnedColumn_Start_Multiple_Cumulative_Offsets()
{
- // Arrange && Act — col1: 100px, col2: 120px → col1 left:0px, col2 left:100px
+ // Arrange && Act — col1: 100px, col2: 120px → col1 inset-inline-start:0px, col2 inset-inline-start:100px
var cut = Render>(
@
-
-
+
+
);
Assert.All(cut.FindAll("[col-index='1']"), cell =>
- Assert.Contains("left: 0px", cell.GetAttribute("style") ?? ""));
+ Assert.Contains("inset-inline-start: 0px", cell.GetAttribute("style") ?? ""));
Assert.All(cut.FindAll("[col-index='2']"), cell =>
- Assert.Contains("left: 100px", cell.GetAttribute("style") ?? ""));
+ Assert.Contains("inset-inline-start: 100px", cell.GetAttribute("style") ?? ""));
}
// -------------------------------------------------------------------------
- // Two right-pinned columns: cumulative offsets
+ // Two end-pinned columns: cumulative offsets
// -------------------------------------------------------------------------
[Fact]
- public void FluentDataGrid_PinnedColumn_Right_Multiple_Cumulative_Offsets()
+ public void FluentDataGrid_PinnedColumn_End_Multiple_Cumulative_Offsets()
{
- // col1 (normal 1fr) | col2: 80px right-pinned | col3: 60px right-pinned
- // col3 (rightmost) → right:0px | col2 → right:60px
+ // col1 (normal 1fr) | col2: 80px end-pinned | col3: 60px end-pinned
+ // col3 (rightmost) → inset-inline-end:0px | col2 → inset-inline-end:60px
var cut = Render>(
@
-
-
+
+
);
- // col3 is index 3 — rightmost right-pinned → right: 0px
+ // col3 is index 3 — rightmost end-pinned → inset-inline-end: 0px
Assert.All(cut.FindAll("[col-index='3']"), cell =>
- Assert.Contains("right: 0px", cell.GetAttribute("style") ?? ""));
+ Assert.Contains("inset-inline-end: 0px", cell.GetAttribute("style") ?? ""));
- // col2 is index 2 — next-to-last right-pinned → right: 60px (width of col3)
+ // col2 is index 2 — next-to-last end-pinned → inset-inline-end: 60px (width of col3)
Assert.All(cut.FindAll("[col-index='2']"), cell =>
- Assert.Contains("right: 60px", cell.GetAttribute("style") ?? ""));
+ Assert.Contains("inset-inline-end: 60px", cell.GetAttribute("style") ?? ""));
}
// -------------------------------------------------------------------------
@@ -158,7 +158,7 @@
Render>(
@
-
+
);
});
@@ -176,67 +176,67 @@
Render>(
@
-
+
);
});
}
// -------------------------------------------------------------------------
- // Ordering validation: isolated left-pinned column (not at the start) throws
+ // Ordering validation: isolated start-pinned column (not at the start) throws
// -------------------------------------------------------------------------
[Fact]
- public void FluentDataGrid_PinnedColumn_Left_Not_At_Start_Throws()
+ public void FluentDataGrid_PinnedColumn_Start_Not_At_Start_Throws()
{
- // col1 is unpinned, col2 is left-pinned → invalid
+ // col1 is unpinned, col2 is start-pinned → invalid
Assert.Throws(() =>
{
Render>(
@
-
+
);
});
}
// -------------------------------------------------------------------------
- // Ordering validation: left-pinned columns split by a normal column throws
+ // Ordering validation: start-pinned columns split by a normal column throws
// -------------------------------------------------------------------------
[Fact]
- public void FluentDataGrid_PinnedColumn_Left_NonContiguous_Throws()
+ public void FluentDataGrid_PinnedColumn_Start_NonContiguous_Throws()
{
- // col1 left-pinned, col2 normal, col3 left-pinned → not contiguous
+ // col1 start-pinned, col2 normal, col3 start-pinned → not contiguous
Assert.Throws(() =>
{
Render>(
@
-
+
-
+
);
});
}
// -------------------------------------------------------------------------
- // Ordering validation: isolated right-pinned column (not at the end) throws
+ // Ordering validation: isolated end-pinned column (not at the end) throws
// -------------------------------------------------------------------------
[Fact]
- public void FluentDataGrid_PinnedColumn_Right_Not_At_End_Throws()
+ public void FluentDataGrid_PinnedColumn_End_Not_At_End_Throws()
{
- // col1 right-pinned, col2 normal → invalid
+ // col1 end-pinned, col2 normal → invalid
Assert.Throws(() =>
{
Render>(
@
-
+
);
@@ -244,60 +244,60 @@
}
// -------------------------------------------------------------------------
- // Ordering validation: right-pinned columns split by a normal column throws
+ // Ordering validation: end-pinned columns split by a normal column throws
// -------------------------------------------------------------------------
[Fact]
- public void FluentDataGrid_PinnedColumn_Right_NonContiguous_Throws()
+ public void FluentDataGrid_PinnedColumn_End_NonContiguous_Throws()
{
- // col1 normal, col2 right-pinned, col3 normal, col4 right-pinned → not contiguous
+ // col1 normal, col2 end-pinned, col3 normal, col4 end-pinned → not contiguous
Assert.Throws(() =>
{
Render>(
@
-
+
-
+
);
});
}
// -------------------------------------------------------------------------
- // Valid mixed layout: left-pinned + normal + right-pinned
+ // Valid mixed layout: start-pinned + normal + end-pinned
// -------------------------------------------------------------------------
[Fact]
- public void FluentDataGrid_PinnedColumn_LeftAndRight_Valid_Layout()
+ public void FluentDataGrid_PinnedColumn_StartAndEnd_Valid_Layout()
{
// Should not throw; verify offsets are correct
var cut = Render>(
@
-
-
+
+
-
+
);
- // col1 (Id) left-pinned → left: 0px
+ // col1 (Id) start-pinned → inset-inline-start: 0px
Assert.All(cut.FindAll("[col-index='1']"), c =>
- Assert.Contains("left: 0px", c.GetAttribute("style") ?? ""));
+ Assert.Contains("inset-inline-start: 0px", c.GetAttribute("style") ?? ""));
- // col2 (Name) left-pinned → left: 50px
+ // col2 (Name) start-pinned → inset-inline-start: 50px
Assert.All(cut.FindAll("[col-index='2']"), c =>
- Assert.Contains("left: 50px", c.GetAttribute("style") ?? ""));
+ Assert.Contains("inset-inline-start: 50px", c.GetAttribute("style") ?? ""));
// col3 (City) not pinned → no sticky style
Assert.All(cut.FindAll("[col-index='3']"), c =>
Assert.DoesNotContain("position: sticky", c.GetAttribute("style") ?? ""));
- // col4 (Action) right-pinned → right: 0px
+ // col4 (Action) end-pinned → inset-inline-end: 0px
Assert.All(cut.FindAll("[col-index='4']"), c =>
- Assert.Contains("right: 0px", c.GetAttribute("style") ?? ""));
+ Assert.Contains("inset-inline-end: 0px", c.GetAttribute("style") ?? ""));
}
// -------------------------------------------------------------------------
@@ -310,10 +310,10 @@
var cut = Render>(
@
-
-
+
+
-
+
);
From 23b3c4f96fed575c8d2910005a294871ee9454a7 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 4 Apr 2026 09:43:05 +0000
Subject: [PATCH 02/10] chore: remove accidentally committed build artifact and
add obj/ to .gitignore
Agent-Logs-Url: https://github.com/microsoft/fluentui-blazor/sessions/705a5b7c-1768-42a0-ba5f-faa99daf78d4
Co-authored-by: vnbaaij <1761079+vnbaaij@users.noreply.github.com>
---
.gitignore | 1 +
.../obj\\Debug/\\package.g.props" | 29 -------------------
2 files changed, 1 insertion(+), 29 deletions(-)
delete mode 100644 "src/Core.Scripts/obj\\Debug/\\package.g.props"
diff --git a/.gitignore b/.gitignore
index 4be8a1c616..61f81fb7a7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -424,3 +424,4 @@ all-icons.json
all-emojis.json
/global.json
/src/Core.Scripts/src/BuildConstants.ts
+/src/Core.Scripts/obj/
diff --git "a/src/Core.Scripts/obj\\Debug/\\package.g.props" "b/src/Core.Scripts/obj\\Debug/\\package.g.props"
deleted file mode 100644
index 4b7b3b41a5..0000000000
--- "a/src/Core.Scripts/obj\\Debug/\\package.g.props"
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
- true
- microsoft.fluentui.aspnetcore.components.assets
- src/index.ts
- dist/Microsoft.FluentUI.AspNetCore.Components.lib.module.js
- ../Core/Components/**/*.css
- dist/Microsoft.FluentUI.AspNetCore.Components.bundle.scp.css
- node ./esbuild.config.mjs
- rimraf ./dist
- []
-
- ISC
- module
- ^1.15.9
- 8.57.1
- 8.57.1
- 0.27.4
- 0.0.1
- 10.0.3
- ^13.0.6
- ^7.6.1
- 6.1.3
- 6.4.0
- 5.9.3
- ^3.0.0-rc.9
-
-
\ No newline at end of file
From c76e2cb39963e3e4482e348d6abfd4ec1edbb32c Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 4 Apr 2026 15:29:42 +0000
Subject: [PATCH 03/10] chore: plan wording updates for start/end docs and
comments
Agent-Logs-Url: https://github.com/microsoft/fluentui-blazor/sessions/e324508b-d7f0-40e2-bfec-a2707d42806c
Co-authored-by: vnbaaij <1761079+vnbaaij@users.noreply.github.com>
---
.../obj\\Debug/\\package.g.props" | 29 +++++++++++++++++++
1 file changed, 29 insertions(+)
create mode 100644 "src/Core.Scripts/obj\\Debug/\\package.g.props"
diff --git "a/src/Core.Scripts/obj\\Debug/\\package.g.props" "b/src/Core.Scripts/obj\\Debug/\\package.g.props"
new file mode 100644
index 0000000000..4b7b3b41a5
--- /dev/null
+++ "b/src/Core.Scripts/obj\\Debug/\\package.g.props"
@@ -0,0 +1,29 @@
+
+
+
+ true
+ microsoft.fluentui.aspnetcore.components.assets
+ src/index.ts
+ dist/Microsoft.FluentUI.AspNetCore.Components.lib.module.js
+ ../Core/Components/**/*.css
+ dist/Microsoft.FluentUI.AspNetCore.Components.bundle.scp.css
+ node ./esbuild.config.mjs
+ rimraf ./dist
+ []
+
+ ISC
+ module
+ ^1.15.9
+ 8.57.1
+ 8.57.1
+ 0.27.4
+ 0.0.1
+ 10.0.3
+ ^13.0.6
+ ^7.6.1
+ 6.1.3
+ 6.4.0
+ 5.9.3
+ ^3.0.0-rc.9
+
+
\ No newline at end of file
From 1b4c295af697bd6cffafb7e6728f5dc2f25cf114 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 4 Apr 2026 15:30:43 +0000
Subject: [PATCH 04/10] docs: simplify DataGrid start/end wording
Agent-Logs-Url: https://github.com/microsoft/fluentui-blazor/sessions/e324508b-d7f0-40e2-bfec-a2707d42806c
Co-authored-by: vnbaaij <1761079+vnbaaij@users.noreply.github.com>
---
.../Pages/DataGridPinnedColumnsPage.md | 20 +++++++++----------
.../DataGrid/Columns/ColumnBase.razor.cs | 4 ++--
.../DataGrid/FluentDataGrid.razor.cs | 5 ++---
src/Core/Enums/DataGridColumnPin.cs | 8 ++++----
.../FluentDataGridPinnedColumnTests.razor | 16 +++++++--------
5 files changed, 25 insertions(+), 28 deletions(-)
diff --git a/examples/Demo/FluentUI.Demo.Client/Documentation/Components/DataGrid/Pages/DataGridPinnedColumnsPage.md b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/DataGrid/Pages/DataGridPinnedColumnsPage.md
index 49f9e7c9cc..d82c480ab4 100644
--- a/examples/Demo/FluentUI.Demo.Client/Documentation/Components/DataGrid/Pages/DataGridPinnedColumnsPage.md
+++ b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/DataGrid/Pages/DataGridPinnedColumnsPage.md
@@ -1,9 +1,8 @@
# Pinned columns
-Columns can be pinned (frozen) to the inline-start or inline-end edge of the grid so that they remain visible
-while the user scrolls horizontally through wider datasets. Using logical-property directions (`Start`/`End`)
-instead of physical ones (`Left`/`Right`) means pinned columns automatically work correctly in both
-LTR and RTL layouts.
+Columns can be pinned (frozen) to the start or end edge of the grid so that they remain visible
+while the user scrolls horizontally through wider datasets. Using `Start`/`End` instead of
+`Left`/`Right` means pinned columns automatically work correctly in both LTR and RTL layouts.
## Parameters
@@ -12,8 +11,8 @@ Set the `Pin` parameter on any `PropertyColumn` or `TemplateColumn`:
| Value | Behavior |
|---|---|
| `DataGridColumnPin.None` | Default — column scrolls normally |
-| `DataGridColumnPin.Start` | Column stays anchored to the inline-start edge (left in LTR, right in RTL) |
-| `DataGridColumnPin.End` | Column stays anchored to the inline-end edge (right in LTR, left in RTL) |
+| `DataGridColumnPin.Start` | Column stays anchored to the start edge (left in LTR, right in RTL) |
+| `DataGridColumnPin.End` | Column stays anchored to the end edge (right in LTR, left in RTL) |
## Rules
@@ -60,13 +59,12 @@ property `--fluent-data-grid-pinned-background`:
## Notes
* Column resizing interacts correctly with sticky offsets — the JavaScript in
- `FluentDataGrid.razor.ts` recalculates `inset-inline-start` / `inset-inline-end` values after
- every resize step via `UpdatePinnedColumnOffsets`.
+ `FluentDataGrid.razor.ts` recalculates start and end offset values after every resize step via
+ `UpdatePinnedColumnOffsets`.
* Virtualization and paging are fully compatible because each rendered row's cells carry the
same `position: sticky` styling regardless of which page or scroll position is active.
-* RTL layouts are fully supported: the CSS logical properties `inset-inline-start` and
- `inset-inline-end` automatically map to the correct physical direction based on the document's
- writing mode.
+* RTL layouts are fully supported: start and end automatically map to the correct physical
+ direction based on the document's writing mode.
## Example
diff --git a/src/Core/Components/DataGrid/Columns/ColumnBase.razor.cs b/src/Core/Components/DataGrid/Columns/ColumnBase.razor.cs
index 4f9f1ad86f..7c8adff08f 100644
--- a/src/Core/Components/DataGrid/Columns/ColumnBase.razor.cs
+++ b/src/Core/Components/DataGrid/Columns/ColumnBase.razor.cs
@@ -188,7 +188,7 @@ public abstract partial class ColumnBase
public string? Width { get; set; }
///
- /// Gets or sets whether this column is pinned (frozen) to the inline-start or inline-end edge of the grid,
+ /// Gets or sets whether this column is pinned (frozen) to the start or end edge of the grid,
/// so it remains visible when the user scrolls horizontally.
/// Pinned columns require an explicit in pixels (e.g., "150px").
/// Start-pinned columns must be contiguous at the start of the column list;
@@ -198,7 +198,7 @@ public abstract partial class ColumnBase
public DataGridColumnPin Pin { get; set; } = DataGridColumnPin.None;
///
- /// The sticky inset-inline-start or inset-inline-end CSS offset computed by
+ /// The sticky start or end CSS offset computed by
/// when columns are collected.
/// Not intended for direct use by consumers.
///
diff --git a/src/Core/Components/DataGrid/FluentDataGrid.razor.cs b/src/Core/Components/DataGrid/FluentDataGrid.razor.cs
index f5cac8f869..c96c70e82f 100644
--- a/src/Core/Components/DataGrid/FluentDataGrid.razor.cs
+++ b/src/Core/Components/DataGrid/FluentDataGrid.razor.cs
@@ -642,7 +642,7 @@ private void ValidateAndComputePinnedColumns()
ValidatePinnedColumnConstraints();
- // Compute start-pin sticky offsets (cumulative inline-start to inline-end).
+ // Compute start-pin sticky offsets in display order.
var startOffset = 0.0;
foreach (var col in _columns.Where(c => c.Pin == DataGridColumnPin.Start))
{
@@ -650,7 +650,7 @@ private void ValidateAndComputePinnedColumns()
startOffset += ParsePixelWidth(col.Width);
}
- // Compute end-pin sticky offsets (cumulative inline-end to inline-start).
+ // Compute end-pin sticky offsets in reverse display order.
var endOffset = 0.0;
foreach (var col in _columns.Where(c => c.Pin == DataGridColumnPin.End).Reverse())
{
@@ -1445,4 +1445,3 @@ private async Task ToggleExpandedAsync(TGridItem item)
}
}
}
-
diff --git a/src/Core/Enums/DataGridColumnPin.cs b/src/Core/Enums/DataGridColumnPin.cs
index 7862eb9779..f666060c24 100644
--- a/src/Core/Enums/DataGridColumnPin.cs
+++ b/src/Core/Enums/DataGridColumnPin.cs
@@ -16,14 +16,14 @@ public enum DataGridColumnPin
None,
///
- /// The column is pinned to the inline-start edge of the grid (left in LTR, right in RTL).
- /// The column will remain visible when the user scrolls toward the inline-end.
+ /// The column is pinned to the start edge of the grid.
+ /// The column will remain visible when the user scrolls toward the end.
///
Start,
///
- /// The column is pinned to the inline-end edge of the grid (right in LTR, left in RTL).
- /// The column will remain visible when the user scrolls toward the inline-start.
+ /// The column is pinned to the end edge of the grid.
+ /// The column will remain visible when the user scrolls toward the start.
///
End,
}
diff --git a/tests/Core/Components/DataGrid/FluentDataGridPinnedColumnTests.razor b/tests/Core/Components/DataGrid/FluentDataGridPinnedColumnTests.razor
index e893f6174e..c4ad7669ad 100644
--- a/tests/Core/Components/DataGrid/FluentDataGridPinnedColumnTests.razor
+++ b/tests/Core/Components/DataGrid/FluentDataGridPinnedColumnTests.razor
@@ -45,7 +45,7 @@
);
- // Assert — every cell in the column carries col-pinned-start and position:sticky;inset-inline-start:0px
+ // Assert — every cell in the column carries col-pinned-start and a start offset of 0px
var cells = cut.FindAll("[col-index='1']");
Assert.NotEmpty(cells);
Assert.All(cells, cell =>
@@ -103,7 +103,7 @@
[Fact]
public void FluentDataGrid_PinnedColumn_Start_Multiple_Cumulative_Offsets()
{
- // Arrange && Act — col1: 100px, col2: 120px → col1 inset-inline-start:0px, col2 inset-inline-start:100px
+ // Arrange && Act — col1: 100px, col2: 120px → col1 start offset 0px, col2 start offset 100px
var cut = Render>(
@
@@ -127,7 +127,7 @@
public void FluentDataGrid_PinnedColumn_End_Multiple_Cumulative_Offsets()
{
// col1 (normal 1fr) | col2: 80px end-pinned | col3: 60px end-pinned
- // col3 (rightmost) → inset-inline-end:0px | col2 → inset-inline-end:60px
+ // col3 (rightmost) → end offset 0px | col2 → end offset 60px
var cut = Render>(
@
@@ -137,11 +137,11 @@
);
- // col3 is index 3 — rightmost end-pinned → inset-inline-end: 0px
+ // col3 is index 3 — rightmost end-pinned → end offset 0px
Assert.All(cut.FindAll("[col-index='3']"), cell =>
Assert.Contains("inset-inline-end: 0px", cell.GetAttribute("style") ?? ""));
- // col2 is index 2 — next-to-last end-pinned → inset-inline-end: 60px (width of col3)
+ // col2 is index 2 — next-to-last end-pinned → end offset 60px (width of col3)
Assert.All(cut.FindAll("[col-index='2']"), cell =>
Assert.Contains("inset-inline-end: 60px", cell.GetAttribute("style") ?? ""));
}
@@ -283,11 +283,11 @@
);
- // col1 (Id) start-pinned → inset-inline-start: 0px
+ // col1 (Id) start-pinned → start offset 0px
Assert.All(cut.FindAll("[col-index='1']"), c =>
Assert.Contains("inset-inline-start: 0px", c.GetAttribute("style") ?? ""));
- // col2 (Name) start-pinned → inset-inline-start: 50px
+ // col2 (Name) start-pinned → start offset 50px
Assert.All(cut.FindAll("[col-index='2']"), c =>
Assert.Contains("inset-inline-start: 50px", c.GetAttribute("style") ?? ""));
@@ -295,7 +295,7 @@
Assert.All(cut.FindAll("[col-index='3']"), c =>
Assert.DoesNotContain("position: sticky", c.GetAttribute("style") ?? ""));
- // col4 (Action) end-pinned → inset-inline-end: 0px
+ // col4 (Action) end-pinned → end offset 0px
Assert.All(cut.FindAll("[col-index='4']"), c =>
Assert.Contains("inset-inline-end: 0px", c.GetAttribute("style") ?? ""));
}
From 6e4243da07acc1bfaa332f6b77d880444251fe08 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 4 Apr 2026 15:34:50 +0000
Subject: [PATCH 05/10] chore: remove tracked generated props file
Agent-Logs-Url: https://github.com/microsoft/fluentui-blazor/sessions/9d4563a7-5f41-4b62-ab85-0d78d50060b1
Co-authored-by: vnbaaij <1761079+vnbaaij@users.noreply.github.com>
---
.../obj\\Debug/\\package.g.props" | 29 -------------------
1 file changed, 29 deletions(-)
delete mode 100644 "src/Core.Scripts/obj\\Debug/\\package.g.props"
diff --git "a/src/Core.Scripts/obj\\Debug/\\package.g.props" "b/src/Core.Scripts/obj\\Debug/\\package.g.props"
deleted file mode 100644
index 4b7b3b41a5..0000000000
--- "a/src/Core.Scripts/obj\\Debug/\\package.g.props"
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
- true
- microsoft.fluentui.aspnetcore.components.assets
- src/index.ts
- dist/Microsoft.FluentUI.AspNetCore.Components.lib.module.js
- ../Core/Components/**/*.css
- dist/Microsoft.FluentUI.AspNetCore.Components.bundle.scp.css
- node ./esbuild.config.mjs
- rimraf ./dist
- []
-
- ISC
- module
- ^1.15.9
- 8.57.1
- 8.57.1
- 0.27.4
- 0.0.1
- 10.0.3
- ^13.0.6
- ^7.6.1
- 6.1.3
- 6.4.0
- 5.9.3
- ^3.0.0-rc.9
-
-
\ No newline at end of file
From a0f46eb2f61ed12c721d9e143f909e08a30bb15a Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 4 Apr 2026 15:53:21 +0000
Subject: [PATCH 06/10] docs: restore pinned columns page metadata
Agent-Logs-Url: https://github.com/microsoft/fluentui-blazor/sessions/bbc46172-3499-4123-a119-cc10c435de7f
Co-authored-by: vnbaaij <1761079+vnbaaij@users.noreply.github.com>
---
.../Pages/DataGridPinnedColumnsPage.md | 9 ++++--
.../obj\\Debug/\\package.g.props" | 29 +++++++++++++++++++
2 files changed, 35 insertions(+), 3 deletions(-)
create mode 100644 "src/Core.Scripts/obj\\Debug/\\package.g.props"
diff --git a/examples/Demo/FluentUI.Demo.Client/Documentation/Components/DataGrid/Pages/DataGridPinnedColumnsPage.md b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/DataGrid/Pages/DataGridPinnedColumnsPage.md
index d82c480ab4..b97c848fce 100644
--- a/examples/Demo/FluentUI.Demo.Client/Documentation/Components/DataGrid/Pages/DataGridPinnedColumnsPage.md
+++ b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/DataGrid/Pages/DataGridPinnedColumnsPage.md
@@ -1,3 +1,8 @@
+---
+title: Pinned columns
+route: /DataGrid/PinnedColumns
+---
+
# Pinned columns
Columns can be pinned (frozen) to the start or end edge of the grid so that they remain visible
@@ -58,9 +63,7 @@ property `--fluent-data-grid-pinned-background`:
## Notes
-* Column resizing interacts correctly with sticky offsets — the JavaScript in
- `FluentDataGrid.razor.ts` recalculates start and end offset values after every resize step via
- `UpdatePinnedColumnOffsets`.
+* Column resizing keeps pinned columns aligned as widths change.
* Virtualization and paging are fully compatible because each rendered row's cells carry the
same `position: sticky` styling regardless of which page or scroll position is active.
* RTL layouts are fully supported: start and end automatically map to the correct physical
diff --git "a/src/Core.Scripts/obj\\Debug/\\package.g.props" "b/src/Core.Scripts/obj\\Debug/\\package.g.props"
new file mode 100644
index 0000000000..4b7b3b41a5
--- /dev/null
+++ "b/src/Core.Scripts/obj\\Debug/\\package.g.props"
@@ -0,0 +1,29 @@
+
+
+
+ true
+ microsoft.fluentui.aspnetcore.components.assets
+ src/index.ts
+ dist/Microsoft.FluentUI.AspNetCore.Components.lib.module.js
+ ../Core/Components/**/*.css
+ dist/Microsoft.FluentUI.AspNetCore.Components.bundle.scp.css
+ node ./esbuild.config.mjs
+ rimraf ./dist
+ []
+
+ ISC
+ module
+ ^1.15.9
+ 8.57.1
+ 8.57.1
+ 0.27.4
+ 0.0.1
+ 10.0.3
+ ^13.0.6
+ ^7.6.1
+ 6.1.3
+ 6.4.0
+ 5.9.3
+ ^3.0.0-rc.9
+
+
\ No newline at end of file
From b2175fe1acadd9289778ffce73deb70a461dffee Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 4 Apr 2026 15:53:37 +0000
Subject: [PATCH 07/10] chore: remove regenerated props file
Agent-Logs-Url: https://github.com/microsoft/fluentui-blazor/sessions/bbc46172-3499-4123-a119-cc10c435de7f
Co-authored-by: vnbaaij <1761079+vnbaaij@users.noreply.github.com>
---
.../obj\\Debug/\\package.g.props" | 29 -------------------
1 file changed, 29 deletions(-)
delete mode 100644 "src/Core.Scripts/obj\\Debug/\\package.g.props"
diff --git "a/src/Core.Scripts/obj\\Debug/\\package.g.props" "b/src/Core.Scripts/obj\\Debug/\\package.g.props"
deleted file mode 100644
index 4b7b3b41a5..0000000000
--- "a/src/Core.Scripts/obj\\Debug/\\package.g.props"
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
- true
- microsoft.fluentui.aspnetcore.components.assets
- src/index.ts
- dist/Microsoft.FluentUI.AspNetCore.Components.lib.module.js
- ../Core/Components/**/*.css
- dist/Microsoft.FluentUI.AspNetCore.Components.bundle.scp.css
- node ./esbuild.config.mjs
- rimraf ./dist
- []
-
- ISC
- module
- ^1.15.9
- 8.57.1
- 8.57.1
- 0.27.4
- 0.0.1
- 10.0.3
- ^13.0.6
- ^7.6.1
- 6.1.3
- 6.4.0
- 5.9.3
- ^3.0.0-rc.9
-
-
\ No newline at end of file
From 2403bfd902f218dc0c81f4f01945464e221a32ad Mon Sep 17 00:00:00 2001
From: Vincent Baaij
Date: Sat, 4 Apr 2026 17:58:10 +0200
Subject: [PATCH 08/10] Fix formatting in DataGridPinnedColumnsPage.md
---
.../Components/DataGrid/Pages/DataGridPinnedColumnsPage.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/examples/Demo/FluentUI.Demo.Client/Documentation/Components/DataGrid/Pages/DataGridPinnedColumnsPage.md b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/DataGrid/Pages/DataGridPinnedColumnsPage.md
index b97c848fce..12dec08f05 100644
--- a/examples/Demo/FluentUI.Demo.Client/Documentation/Components/DataGrid/Pages/DataGridPinnedColumnsPage.md
+++ b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/DataGrid/Pages/DataGridPinnedColumnsPage.md
@@ -16,8 +16,8 @@ Set the `Pin` parameter on any `PropertyColumn` or `TemplateColumn`:
| Value | Behavior |
|---|---|
| `DataGridColumnPin.None` | Default — column scrolls normally |
-| `DataGridColumnPin.Start` | Column stays anchored to the start edge (left in LTR, right in RTL) |
-| `DataGridColumnPin.End` | Column stays anchored to the end edge (right in LTR, left in RTL) |
+| `DataGridColumnPin.Start` | Column stays anchored to the start edge |
+| `DataGridColumnPin.End` | Column stays anchored to the end edge |
## Rules
From c0b87b30d363dc63bfe00ca83395ce287a77b2f4 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 7 Apr 2026 08:53:08 +0000
Subject: [PATCH 09/10] fix: allow non-pixel pinned column widths
Agent-Logs-Url: https://github.com/microsoft/fluentui-blazor/sessions/4c32bc45-2c71-462f-8fad-b3b8abd4a55a
Co-authored-by: vnbaaij <1761079+vnbaaij@users.noreply.github.com>
---
.../Pages/DataGridPinnedColumnsPage.md | 10 ++++----
.../DataGrid/Columns/ColumnBase.razor.cs | 8 ++++---
.../DataGrid/FluentDataGrid.razor.cs | 23 +++++++-----------
.../DataGrid/FluentDataGrid.razor.ts | 2 ++
.../FluentDataGridPinnedColumnTests.razor | 24 ++++++++++++-------
5 files changed, 36 insertions(+), 31 deletions(-)
diff --git a/examples/Demo/FluentUI.Demo.Client/Documentation/Components/DataGrid/Pages/DataGridPinnedColumnsPage.md b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/DataGrid/Pages/DataGridPinnedColumnsPage.md
index 12dec08f05..caa36cffb2 100644
--- a/examples/Demo/FluentUI.Demo.Client/Documentation/Components/DataGrid/Pages/DataGridPinnedColumnsPage.md
+++ b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/DataGrid/Pages/DataGridPinnedColumnsPage.md
@@ -21,14 +21,14 @@ Set the `Pin` parameter on any `PropertyColumn` or `TemplateColumn`:
## Rules
-* **Explicit pixel width required.** Every pinned column must declare a `Width` in pixels
- (e.g. `Width="150px"`). Relative units (`fr`, `%`) are not supported because the browser cannot
- determine a fixed sticky offset from them at render time.
+* **Explicit width required.** Every pinned column must declare a `Width`.
+ Pixel and non-pixel CSS units are supported. After the grid renders, sticky offsets are
+ recomputed from the rendered header widths so pinned columns stay aligned.
* **Start-pinned columns must be contiguous at the start.** Each start-pinned column must
immediately follow another start-pinned column, or be the very first column.
* **End-pinned columns must be contiguous at the end.** Each end-pinned column must
immediately precede another end-pinned column, or be the very last column.
-* Violating any of these rules throws an `ArgumentException` with a descriptive message.
+* Violating the missing-width or ordering rules throws an `ArgumentException` with a descriptive message.
## Scrollable container
@@ -77,6 +77,6 @@ The two leftmost columns and the Actions column remain visible while the rest sc
Wrap the grid in a `` container and give the grid a `Style="min-width: max-content;"`
so that the horizontal scroll bar appears.
-Pinned columns require an explicit pixel `Width`.
+Pinned columns require an explicit `Width`.
{{ DataGridPinnedColumns }}
diff --git a/src/Core/Components/DataGrid/Columns/ColumnBase.razor.cs b/src/Core/Components/DataGrid/Columns/ColumnBase.razor.cs
index 7c8adff08f..3a1441c6b1 100644
--- a/src/Core/Components/DataGrid/Columns/ColumnBase.razor.cs
+++ b/src/Core/Components/DataGrid/Columns/ColumnBase.razor.cs
@@ -190,7 +190,8 @@ public abstract partial class ColumnBase
///
/// Gets or sets whether this column is pinned (frozen) to the start or end edge of the grid,
/// so it remains visible when the user scrolls horizontally.
- /// Pinned columns require an explicit in pixels (e.g., "150px").
+ /// Pinned columns require an explicit .
+ /// Sticky offsets are recomputed from rendered header widths after the grid is rendered.
/// Start-pinned columns must be contiguous at the start of the column list;
/// end-pinned columns must be contiguous at the end.
///
@@ -198,8 +199,9 @@ public abstract partial class ColumnBase
public DataGridColumnPin Pin { get; set; } = DataGridColumnPin.None;
///
- /// The sticky start or end CSS offset computed by
- /// when columns are collected.
+ /// The sticky start or end CSS offset seeded by
+ /// when columns are collected and later updated from
+ /// rendered widths by JavaScript.
/// Not intended for direct use by consumers.
///
internal string PinOffset { get; set; } = "0px";
diff --git a/src/Core/Components/DataGrid/FluentDataGrid.razor.cs b/src/Core/Components/DataGrid/FluentDataGrid.razor.cs
index c96c70e82f..bc8b596b84 100644
--- a/src/Core/Components/DataGrid/FluentDataGrid.razor.cs
+++ b/src/Core/Components/DataGrid/FluentDataGrid.razor.cs
@@ -600,7 +600,7 @@ private void FinishCollectingColumns()
throw new ArgumentException("The 'HierarchicalToggle' parameter can only be set on the first column of the grid.");
}
- // Validate and compute offsets for pinned columns.
+ // Validate pinned columns and seed their initial sticky offsets.
ValidateAndComputePinnedColumns();
// Always re-evaluate after collecting columns when using displaymode grid. A column might be added or hidden and the _internalGridTemplateColumns needs to reflect that.
@@ -624,10 +624,11 @@ private void FinishCollectingColumns()
}
///
- /// Validates the pinned-column configuration and computes the sticky offsets for each
- /// pinned column. Rules enforced:
+ /// Validates the pinned-column configuration and seeds initial sticky offsets for each
+ /// pinned column before JavaScript recomputes them from rendered widths after first render.
+ /// Rules enforced:
///
- /// - Pinned columns must specify an explicit pixel Width (e.g., "150px").
+ /// - Pinned columns must specify an explicit Width.
/// - Start-pinned columns must be contiguous at the beginning of the column list.
/// - End-pinned columns must be contiguous at the end of the column list.
///
@@ -665,21 +666,14 @@ private void ValidateAndComputePinnedColumns()
///
private void ValidatePinnedColumnConstraints()
{
- // Width must be an explicit pixel value.
+ // Width must be explicitly provided for pinned columns.
foreach (var col in _columns.Where(c => c.Pin != DataGridColumnPin.None))
{
if (string.IsNullOrWhiteSpace(col.Width))
{
throw new ArgumentException(
$"Column '{col.Title ?? col.Index.ToString(CultureInfo.InvariantCulture)}' has Pin set but no Width. " +
- "Pinned columns require an explicit Width in pixels (e.g., '150px').");
- }
-
- if (!col.Width!.Trim().EndsWith("px", StringComparison.OrdinalIgnoreCase))
- {
- throw new ArgumentException(
- $"Column '{col.Title ?? col.Index.ToString(CultureInfo.InvariantCulture)}' has Pin set but Width '{col.Width}' is not in pixels. " +
- "Pinned columns require an explicit Width in pixels (e.g., '150px').");
+ "Pinned columns require an explicit Width.");
}
}
@@ -710,7 +704,8 @@ private void ValidatePinnedColumnConstraints()
///
/// Parses a CSS pixel value string such as "150px" and returns the numeric value.
- /// Returns 0 if the string is null, empty, or not a valid pixel value.
+ /// Returns 0 if the string is null, empty, or not a valid pixel value so JavaScript
+ /// can recompute the final sticky offsets from rendered widths after first render.
///
private static double ParsePixelWidth(string? width)
{
diff --git a/src/Core/Components/DataGrid/FluentDataGrid.razor.ts b/src/Core/Components/DataGrid/FluentDataGrid.razor.ts
index 395f90313f..ce16cc692c 100644
--- a/src/Core/Components/DataGrid/FluentDataGrid.razor.ts
+++ b/src/Core/Components/DataGrid/FluentDataGrid.razor.ts
@@ -570,6 +570,8 @@ export namespace Microsoft.FluentUI.Blazor.DataGrid {
* the widths of all end-pinned columns after it.
*
* The function reads the actual rendered header-cell width so it handles both Grid mode
+ * and pinned columns whose configured widths use non-pixel CSS units.
+ * It also handles both Grid mode
* (CSS grid layout) and Table mode (standard table layout). Grid mode uses `offsetWidth`
* (includes borders, matches the grid-track width) while Table mode uses `clientWidth`
* (excludes borders, matches the CSS column width), consistent with how existing resize
diff --git a/tests/Core/Components/DataGrid/FluentDataGridPinnedColumnTests.razor b/tests/Core/Components/DataGrid/FluentDataGridPinnedColumnTests.razor
index c4ad7669ad..301fd79076 100644
--- a/tests/Core/Components/DataGrid/FluentDataGridPinnedColumnTests.razor
+++ b/tests/Core/Components/DataGrid/FluentDataGridPinnedColumnTests.razor
@@ -165,20 +165,26 @@
}
// -------------------------------------------------------------------------
- // Pinned column with non-px Width throws
+ // Pinned column with non-px Width renders
// -------------------------------------------------------------------------
[Fact]
- public void FluentDataGrid_PinnedColumn_With_NonPixel_Width_Throws()
+ public void FluentDataGrid_PinnedColumn_With_NonPixel_Width_Renders()
{
- Assert.Throws(() =>
+ var cut = Render>(
+ @
+
+
+
+ );
+
+ var cells = cut.FindAll("[col-index='1']");
+ Assert.NotEmpty(cells);
+ Assert.All(cells, cell =>
{
- Render>(
- @
-
-
-
- );
+ Assert.Contains("col-pinned-start", cell.ClassName);
+ Assert.Contains("position: sticky", cell.GetAttribute("style") ?? "");
+ Assert.Contains("inset-inline-start: 0px", cell.GetAttribute("style") ?? "");
});
}
From 41021db49ab463067ce8c8b95e0877ad80c63040 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 7 Apr 2026 08:55:48 +0000
Subject: [PATCH 10/10] docs: clarify pinned offset recomputation comments
Agent-Logs-Url: https://github.com/microsoft/fluentui-blazor/sessions/4c32bc45-2c71-462f-8fad-b3b8abd4a55a
Co-authored-by: vnbaaij <1761079+vnbaaij@users.noreply.github.com>
---
src/Core/Components/DataGrid/FluentDataGrid.razor.ts | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/src/Core/Components/DataGrid/FluentDataGrid.razor.ts b/src/Core/Components/DataGrid/FluentDataGrid.razor.ts
index ce16cc692c..f029e1290d 100644
--- a/src/Core/Components/DataGrid/FluentDataGrid.razor.ts
+++ b/src/Core/Components/DataGrid/FluentDataGrid.razor.ts
@@ -569,9 +569,8 @@ export namespace Microsoft.FluentUI.Blazor.DataGrid {
* End-pinned columns are processed in reverse DOM order; each column's offset is the sum of
* the widths of all end-pinned columns after it.
*
- * The function reads the actual rendered header-cell width so it handles both Grid mode
- * and pinned columns whose configured widths use non-pixel CSS units.
- * It also handles both Grid mode
+ * The function reads the actual rendered header-cell width so it handles pinned columns whose
+ * configured widths use non-pixel CSS units as well as both Grid mode
* (CSS grid layout) and Table mode (standard table layout). Grid mode uses `offsetWidth`
* (includes borders, matches the grid-track width) while Table mode uses `clientWidth`
* (excludes borders, matches the CSS column width), consistent with how existing resize