Skip to content
Open
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
6bc89b4
stash
mollykreis Mar 4, 2026
dbbb14f
working?
mollykreis Mar 4, 2026
385d887
a few improvements
mollykreis Mar 4, 2026
854200c
mostly working -- styling needs help
mollykreis Mar 6, 2026
fc68bfa
merge
hellovolcano May 20, 2026
1b99869
only make the pinned column sticky
hellovolcano May 21, 2026
c9aa786
Merge remote-tracking branch 'origin/main' into users/vgleason/pinned…
hellovolcano May 28, 2026
2d16e03
apply cell indent levels to first non-pinned column
hellovolcano May 28, 2026
355aa50
fix formatting issues
hellovolcano May 28, 2026
e2804b0
cleanup styles and stoybook
hellovolcano May 28, 2026
26e7717
fix test
hellovolcano May 28, 2026
a6ce5ba
restore accidental delition
hellovolcano May 28, 2026
62527b5
wrappers
hellovolcano May 28, 2026
e940d60
Change files
hellovolcano May 28, 2026
f474d84
add tests
hellovolcano May 28, 2026
17b823c
fix ordering of pinned column
hellovolcano May 28, 2026
f322f1c
make pinned an attribute
hellovolcano May 29, 2026
f5a00a4
fix for styling non-pinned chevron
hellovolcano May 29, 2026
4d57bc2
Merge branch 'main' into users/vgleason/pinned-column
hellovolcano May 29, 2026
22eedc5
fix hover/sticky group issue
hellovolcano Jun 1, 2026
e8ce8b0
Merge branch 'users/vgleason/pinned-column' of https://github.com/ni/…
hellovolcano Jun 1, 2026
9684cad
Merge branch 'main' into users/vgleason/pinned-column
hellovolcano Jun 1, 2026
f391277
Merge branch 'main' into users/vgleason/pinned-column
hellovolcano Jun 3, 2026
34444af
pinned to pin-location enum
hellovolcano Jun 5, 2026
fc63a79
fix blazor wrapper
hellovolcano Jun 15, 2026
869849d
adjust header layout to fix relative/sticky column position issue
hellovolcano Jun 15, 2026
28b27b2
fix storybook
hellovolcano Jun 15, 2026
5d97744
small storybook tweak
hellovolcano Jun 15, 2026
27807f2
fix storybook and add slight visual style
hellovolcano Jun 15, 2026
4781a66
Merge branch 'main' into users/vgleason/pinned-column
hellovolcano Jun 15, 2026
23de172
add more matrix tests and fix styling
hellovolcano Jun 16, 2026
e6aaf93
fix chromatic tests
hellovolcano Jun 16, 2026
0f86d08
breakup table to hopefully help load
hellovolcano Jun 16, 2026
a82664a
Change files
hellovolcano Jun 16, 2026
63fea2b
Update packages/storybook/src/nimble/table-column/base/table-column.s…
hellovolcano Jun 16, 2026
311919e
remove sticky position from group row
hellovolcano Jun 16, 2026
acb86d9
Merge branch 'users/vgleason/pinned-column' of https://github.com/ni/…
hellovolcano Jun 16, 2026
8abc316
remove not
hellovolcano Jun 16, 2026
9032a0b
rename method and add selection to matrix tests
hellovolcano Jun 16, 2026
0ba59fe
fix storybook
hellovolcano Jun 16, 2026
45390e2
add horizontal scroll tests
hellovolcano Jun 17, 2026
43ef8ad
PR feedback
hellovolcano Jun 17, 2026
f87910c
fix focus state for rows with pinned columns
hellovolcano Jun 17, 2026
f42d985
create pinnableColumn mixin
hellovolcano Jun 18, 2026
ca962bf
fix group row focus style
hellovolcano Jun 18, 2026
2e4d2e9
Change files
hellovolcano Jun 18, 2026
f45a930
Merge branch 'main' into users/vgleason/pinned-column
hellovolcano Jun 18, 2026
eb1755b
fix mixin implementation
hellovolcano Jun 18, 2026
a360a0c
PR feedback
hellovolcano Jun 18, 2026
14c26eb
lint
hellovolcano Jun 18, 2026
b9f28ce
PR feedback
hellovolcano Jun 18, 2026
8be620e
doesn't need to be a function
hellovolcano Jun 19, 2026
4e4dee9
Merge branch 'main' into users/vgleason/pinned-column
rajsite Jun 19, 2026
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
@@ -0,0 +1,7 @@
{
Comment thread
hellovolcano marked this conversation as resolved.
"type": "minor",
"comment": "add support for pinning table columns",
"packageName": "@ni/nimble-angular",
"email": "5265744+hellovolcano@users.noreply.github.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "add support for pinning table columns",
"packageName": "@ni/nimble-blazor",
"email": "5265744+hellovolcano@users.noreply.github.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
Comment thread
hellovolcano marked this conversation as resolved.
"type": "minor",
"comment": "add support for pinning table columns",
"packageName": "@ni/nimble-components",
"email": "5265744+hellovolcano@users.noreply.github.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Component, ElementRef, ViewChild } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import type { BooleanValueOrAttribute } from '@ni/nimble-angular/internal-utilities';
import { NimbleTableModule } from '../../../table/nimble-table.module';
import { NimbleTableColumnMappingModule } from '../nimble-table-column-mapping.module';
import { NimbleTableColumnMappingDirective, type TableColumnMapping, TableColumnMappingWidthMode } from '../nimble-table-column-mapping.directive';
Expand Down Expand Up @@ -30,6 +31,7 @@ describe('NimbleTableColumnMapping', () => {
action-menu-slot="my-slot"
action-menu-label="my menu"
column-hidden="true"
pinned
fractional-width="2"
min-pixel-width="40"
sort-direction="${TableColumnSortDirection.ascending}"
Expand Down Expand Up @@ -93,6 +95,11 @@ describe('NimbleTableColumnMapping', () => {
expect(nativeElement.columnHidden).toBe(true);
});

it('will use template string value for pinned', () => {
expect(directive.pinned).toBeTrue();
expect(nativeElement.pinned).toBeTrue();
});

it('will use template string values for sortDirection', () => {
expect(directive.sortDirection).toBe(TableColumnSortDirection.ascending);
expect(nativeElement.sortDirection).toBe(TableColumnSortDirection.ascending);
Expand Down Expand Up @@ -146,6 +153,7 @@ describe('NimbleTableColumnMapping', () => {
[actionMenuSlot]="actionMenuSlot"
[actionMenuLabel]="actionMenuLabel"
[column-hidden]="columnHidden"
[pinned]="pinned"
[fractional-width]="fractionalWidth"
[min-pixel-width]="minPixelWidth"
[sort-direction]="sortDirection"
Expand All @@ -170,6 +178,7 @@ describe('NimbleTableColumnMapping', () => {
public minPixelWidth: number | null = 40;
public columnId = 'my-column';
public columnHidden = true;
public pinned: BooleanValueOrAttribute = null;
public sortDirection: TableColumnSortDirection = TableColumnSortDirection.ascending;
public sortIndex: number | null = 0;
public sortingDisabled = false;
Expand Down Expand Up @@ -259,6 +268,17 @@ describe('NimbleTableColumnMapping', () => {
expect(nativeElement.columnHidden).toBe(false);
});

it('can be configured with property binding for pinned', () => {
expect(directive.pinned).toBeFalse();
expect(nativeElement.pinned).toBeFalse();

fixture.componentInstance.pinned = true;
fixture.detectChanges();

expect(directive.pinned).toBeTrue();
expect(nativeElement.pinned).toBeTrue();
});

it('can be configured with property binding for sortDirection', () => {
expect(directive.sortDirection).toBe(TableColumnSortDirection.ascending);
expect(nativeElement.sortDirection).toBe(TableColumnSortDirection.ascending);
Expand Down Expand Up @@ -404,6 +424,7 @@ describe('NimbleTableColumnMapping', () => {
[attr.action-menu-slot]="actionMenuSlot"
[attr.action-menu-label]="actionMenuLabel"
[attr.column-hidden]="columnHidden"
[attr.pinned]="pinned"
[attr.fractional-width]="fractionalWidth"
[attr.min-pixel-width]="minPixelWidth"
[attr.sort-direction]="sortDirection"
Expand All @@ -428,6 +449,7 @@ describe('NimbleTableColumnMapping', () => {
public minPixelWidth: number | null = 40;
public columnId = 'my-column';
public columnHidden = true;
public pinned: BooleanValueOrAttribute = null;
public sortDirection: TableColumnSortDirection = TableColumnSortDirection.ascending;
public sortIndex: number | null = 0;
public sortingDisabled = false;
Expand Down Expand Up @@ -517,6 +539,17 @@ describe('NimbleTableColumnMapping', () => {
expect(nativeElement.columnHidden).toBe(false);
});

it('can be configured with attribute binding for pinned', () => {
expect(directive.pinned).toBeFalse();
expect(nativeElement.pinned).toBeFalse();

fixture.componentInstance.pinned = '';
fixture.detectChanges();

expect(directive.pinned).toBeTrue();
expect(nativeElement.pinned).toBeTrue();
});

it('can be configured with attribute binding for sortDirection', () => {
expect(directive.sortDirection).toBe(TableColumnSortDirection.ascending);
expect(nativeElement.sortDirection).toBe(TableColumnSortDirection.ascending);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ export class NimbleTableColumnBaseDirective<T extends TableColumn> {
this.renderer.setProperty(this.elementRef.nativeElement, 'columnHidden', toBooleanProperty(value));
}

public get pinned(): BooleanValueOrAttribute {
return this.elementRef.nativeElement.pinned;
}

@Input('pinned') public set pinned(value: BooleanValueOrAttribute) {
this.renderer.setProperty(this.elementRef.nativeElement, 'pinned', toBooleanProperty(value));
}

public constructor(protected readonly renderer: Renderer2, protected readonly elementRef: ElementRef<T>) {}

public checkValidity(): boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,8 @@ describe('Nimble table', () => {
duplicateGroupIndex: false,
idFieldNameNotConfigured: false,
invalidColumnConfiguration: false,
invalidParentIdConfiguration: false
invalidParentIdConfiguration: false,
invalidPinnedColumnConfiguration: false
};
expect(directive.validity).toEqual(expectedValidity);
expect(nativeElement.validity).toEqual(expectedValidity);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
column-hidden="@ColumnHidden"
fractional-width="@FractionalWidthAsString"
min-pixel-width="@MinPixelWidthAsString"
pinned="@Pinned"
sort-direction="@SortDirection.ToAttributeValue()"
sort-index="@SortIndex"
sorting-disabled="@SortingDisabled"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ public partial class NimbleTableColumnMapping<TKey> : NimbleTableColumnEnumBase<
[Parameter]
public double? MinPixelWidth { get; set; }

/// <summary>
/// Indicates whether the column is pinned.
/// </summary>
[Parameter]
public bool Pinned { get; set; }

/// <summary>
/// Sets the width mode on the column.
/// </summary>
Expand Down
3 changes: 3 additions & 0 deletions packages/nimble-components/src/table-column/base/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ export abstract class TableColumn<
@attr({ attribute: 'column-hidden', mode: 'boolean' })
public columnHidden = false;

@attr({ attribute: 'pinned', mode: 'boolean' })
public pinned = false;

/** @internal */
@observable
public hasOverflow = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ describe('TableColumn', () => {
expect(element.columnInternals.currentPixelWidth).toBe(200);
});

it('has expected defaults for pinned', async () => {
await connect();

expect(element.pinned).toBeFalse();
expect(element.hasAttribute('pinned')).toBeFalse();
});

describe('with a custom constructor', () => {
// Seems subject to change how errors are handled during custom
// element construction: https://github.com/WICG/webcomponents/issues/635
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ export class TableGroupRow extends FoundationElement {
@observable
public nestingLevel = 0;

@observable
public pinnedColumnOffset = 0;

/**
* Row index in the flattened set of all regular and group header rows.
* Represents the index in table.tableData (TableRowState[]).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ import {
controlHeight,
fillHoverColor,
mediumPadding,
standardPadding
standardPadding,
tableRowBorderColor
} from '../../../theme-provider/design-tokens';
import { Theme } from '../../../theme-provider/types';
import { hexToRgbaCssColor } from '../../../utilities/style/colors';
import { themeBehavior } from '../../../utilities/style/theme';
import { userSelectNone } from '../../../utilities/style/user-select';
import { styles as expandCollapseStyles } from '../../../patterns/expand-collapse/styles';
import { focusVisible } from '../../../utilities/style/focus';
import { ZIndexLevels } from '../../../utilities/style/types';

export const styles = css`
${display('grid')}
Expand All @@ -26,6 +28,7 @@ export const styles = css`
height: calc(${controlHeight} + 2 * ${borderWidth});
border-top: calc(2 * ${borderWidth}) solid ${applicationBackgroundColor};
grid-template-columns:
calc(var(--ni-private-table-group-row-pinned-column-offset))
calc(
${controlHeight} *
(var(--ni-private-table-group-row-indent-level) + 1)
Expand All @@ -35,6 +38,7 @@ export const styles = css`

:host([selectable]) {
grid-template-columns:
calc(var(--ni-private-table-group-row-pinned-column-offset))
${controlHeight}
calc(
${controlHeight} *
Expand All @@ -61,6 +65,34 @@ export const styles = css`
outline-offset: calc(-2 * ${borderWidth});
}

.pinned-column-spacer {
height: 100%;
}

.pinned-column-spacer.has-pinned-columns {
position: sticky;
left: 0;
background: ${tableRowBorderColor};
z-index: ${ZIndexLevels.zIndex1000};
}

.expand-collapse-button-container.has-pinned-columns {
position: sticky;
left: var(--ni-private-table-group-row-pinned-column-offset);
z-index: ${ZIndexLevels.zIndex1000};
background: ${tableRowBorderColor};
}

.expand-collapse-button-container.selectable.has-pinned-columns {
left: calc(
var(--ni-private-table-group-row-pinned-column-offset) +
${controlHeight}
);
}

.expand-collapse-button-container.selectable {
}
Comment thread
hellovolcano marked this conversation as resolved.
Outdated

.expand-collapse-button {
margin-left: calc(
${mediumPadding} + ${standardPadding} * 2 *
Expand Down Expand Up @@ -90,6 +122,13 @@ export const styles = css`
display: flex;
}

.checkbox-container.has-pinned-columns {
position: sticky;
left: var(--ni-private-table-group-row-pinned-column-offset);
z-index: ${ZIndexLevels.zIndex1000};
background: ${tableRowBorderColor};
}

.selection-checkbox {
margin-left: ${standardPadding};
}
Expand All @@ -104,6 +143,16 @@ export const styles = css`
:host([allow-hover]:hover)::before {
background-color: ${hexToRgbaCssColor(White, 0.05)};
}

.pinned-column-spacer.has-pinned-columns,
.expand-collapse-button-container.has-pinned-columns,
.checkbox-container.has-pinned-columns {
background: linear-gradient(
${hexToRgbaCssColor(White, 0.1)},
${hexToRgbaCssColor(White, 0.1)}
),
${tableRowBorderColor};
}
`
),
themeBehavior(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,15 @@ export const template = html<TableGroupRow>`
role="row"
@click=${x => x.onGroupExpandToggle()}
aria-expanded=${x => x.expanded}
Comment thread
hellovolcano marked this conversation as resolved.
style="--ni-private-table-group-row-indent-level: ${x => x.nestingLevel};"
style="
--ni-private-table-group-row-indent-level: ${x => x.nestingLevel};
--ni-private-table-group-row-pinned-column-offset: ${x => x.pinnedColumnOffset}px;
"
>
<span class="pinned-column-spacer ${x => (x.pinnedColumnOffset > 0 ? 'has-pinned-columns' : '')}"></span>

${when(x => x.selectable, html<TableGroupRow>`
<span role="gridcell" class="checkbox-container">
<span role="gridcell" class="checkbox-container ${x => (x.pinnedColumnOffset > 0 ? 'has-pinned-columns' : '')}">
<${checkboxTag}
${ref('selectionCheckbox')}
class="selection-checkbox"
Expand All @@ -31,7 +36,7 @@ export const template = html<TableGroupRow>`
</span>
`)}

<span role="gridcell">
<span role="gridcell" class="expand-collapse-button-container ${x => (x.selectable ? 'selectable' : '')} ${x => (x.pinnedColumnOffset > 0 ? 'has-pinned-columns' : '')}">
<${buttonTag}
appearance="${ButtonAppearance.ghost}"
content-hidden
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -330,8 +330,9 @@ export class TableRow<
}

private updateCellIndentLevels(): void {
const firstNonPinnedIndex = this.columns.findIndex(col => !col.pinned);
this.cellIndentLevels = this.columns.map((_, i) => {
return i === 0 ? this.nestingLevel : 0;
return i === firstNonPinnedIndex ? this.nestingLevel : 0;
});
}

Expand Down
10 changes: 10 additions & 0 deletions packages/nimble-components/src/table/components/row/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { hexToRgbaCssColor } from '../../../utilities/style/colors';
import { themeBehavior } from '../../../utilities/style/theme';
import { styles as expandCollapseStyles } from '../../../patterns/expand-collapse/styles';
import { focusVisible } from '../../../utilities/style/focus';
import { ZIndexLevels } from '../../../utilities/style/types';

export const styles = css`
${display('flex')}
Expand Down Expand Up @@ -115,6 +116,15 @@ export const styles = css`
width: ${mediumPadding};
}

.pinned-cell-container {
display: grid;
grid-template-columns: var(--ni-private-table-pinned-columns-row-grid-columns);
position: sticky;
left: 0;
background: ${applicationBackgroundColor};
z-index: ${ZIndexLevels.zIndex1000};
}

.cell-container {
display: grid;
width: 100%;
Expand Down
Loading