From e1fcc53159e13108462f144e6dc3236ee56c9330 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Thu, 1 May 2025 16:31:45 -0400 Subject: [PATCH 01/89] added column model --- .../hds/advanced-table/models/column.ts | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 packages/components/src/components/hds/advanced-table/models/column.ts diff --git a/packages/components/src/components/hds/advanced-table/models/column.ts b/packages/components/src/components/hds/advanced-table/models/column.ts new file mode 100644 index 00000000000..982b8bcb20b --- /dev/null +++ b/packages/components/src/components/hds/advanced-table/models/column.ts @@ -0,0 +1,33 @@ +import { tracked } from '@glimmer/tracking'; + +import type { HdsAdvancedTableHorizontalAlignment } from "../types"; + +export default class HdsAdvancedTableColumn { + @tracked label: string = ''; + @tracked align?: HdsAdvancedTableHorizontalAlignment = 'left'; + @tracked key?: string = undefined; + @tracked isExpandable?: boolean = false; + @tracked isReorderable?: boolean = false; + @tracked isResizable?: boolean = false; + @tracked isSortable?: boolean = false; + @tracked isVisuallyHidden?: boolean = false; + @tracked tooltip?: string = undefined; + @tracked width?: string = undefined; + + @tracked sortingFunction?: (a: unknown, b: unknown) => number = undefined; + + constructor(args: HdsAdvancedTableColumn) { + this.label = args.label; + this.align = args.align; + this.key = args.key; + this.isExpandable = args.isExpandable; + this.isReorderable = args.isReorderable; + this.isResizable = args.isResizable; + this.isSortable = args.isSortable; + this.isVisuallyHidden = args.isVisuallyHidden; + this.tooltip = args.tooltip; + this.width = args.width; + + this.sortingFunction = args.sortingFunction; + } +} \ No newline at end of file From c64cbb971fca56704138b265b0efc5767128a1c1 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Thu, 1 May 2025 19:44:20 -0400 Subject: [PATCH 02/89] column resizing works --- packages/components/package.json | 2 + .../components/hds/advanced-table/index.hbs | 5 +- .../components/hds/advanced-table/index.ts | 38 ++++------- .../hds/advanced-table/models/column.ts | 9 ++- .../hds/advanced-table/models/row.ts | 15 +++++ .../hds/advanced-table/models/table.ts | 30 +++++++-- .../src/components/hds/advanced-table/td.ts | 1 + .../hds/advanced-table/th-resize-handle.hbs | 1 + .../hds/advanced-table/th-resize-handle.ts | 66 +++++++++++++++++++ .../components/hds/advanced-table/th-sort.ts | 2 + .../src/components/hds/advanced-table/th.hbs | 5 ++ .../src/components/hds/advanced-table/th.ts | 8 ++- .../components/hds/advanced-table/types.ts | 6 ++ .../src/styles/components/advanced-table.scss | 24 +++++++ packages/components/src/template-registry.ts | 3 + .../controllers/components/advanced-table.js | 26 ++++++++ .../templates/components/advanced-table.hbs | 28 +++++++- 17 files changed, 227 insertions(+), 42 deletions(-) create mode 100644 packages/components/src/components/hds/advanced-table/th-resize-handle.hbs create mode 100644 packages/components/src/components/hds/advanced-table/th-resize-handle.ts diff --git a/packages/components/package.json b/packages/components/package.json index cccbb3cafda..8e9290eb8de 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -139,12 +139,14 @@ "./components/hds/accordion/item/index.js": "./dist/_app_/components/hds/accordion/item/index.js", "./components/hds/advanced-table/expandable-tr-group.js": "./dist/_app_/components/hds/advanced-table/expandable-tr-group.js", "./components/hds/advanced-table/index.js": "./dist/_app_/components/hds/advanced-table/index.js", + "./components/hds/advanced-table/models/column.js": "./dist/_app_/components/hds/advanced-table/models/column.js", "./components/hds/advanced-table/models/row.js": "./dist/_app_/components/hds/advanced-table/models/row.js", "./components/hds/advanced-table/models/table.js": "./dist/_app_/components/hds/advanced-table/models/table.js", "./components/hds/advanced-table/td.js": "./dist/_app_/components/hds/advanced-table/td.js", "./components/hds/advanced-table/th-button-expand.js": "./dist/_app_/components/hds/advanced-table/th-button-expand.js", "./components/hds/advanced-table/th-button-sort.js": "./dist/_app_/components/hds/advanced-table/th-button-sort.js", "./components/hds/advanced-table/th-button-tooltip.js": "./dist/_app_/components/hds/advanced-table/th-button-tooltip.js", + "./components/hds/advanced-table/th-resize-handle.js": "./dist/_app_/components/hds/advanced-table/th-resize-handle.js", "./components/hds/advanced-table/th-selectable.js": "./dist/_app_/components/hds/advanced-table/th-selectable.js", "./components/hds/advanced-table/th-sort.js": "./dist/_app_/components/hds/advanced-table/th-sort.js", "./components/hds/advanced-table/th.js": "./dist/_app_/components/hds/advanced-table/th.js", diff --git a/packages/components/src/components/hds/advanced-table/index.hbs b/packages/components/src/components/hds/advanced-table/index.hbs index ce87402455f..69745111dc6 100644 --- a/packages/components/src/components/hds/advanced-table/index.hbs +++ b/packages/components/src/components/hds/advanced-table/index.hbs @@ -40,11 +40,11 @@ @hasStickyColumn={{@hasStickyFirstColumn}} @isStickyColumnPinned={{this.isStickyColumnPinned}} > - {{#each @columns as |column index|}} + {{#each this._tableModel.columns as |column index|}} {{#if column.isSortable}} column.width !== undefined + ); + + if (hasCustomColumnWidths) { // check the custom column widths, if the current column has a custom width use the custom width. otherwise take the available space. - for (let i = 0; i < this.columnWidths.length; i++) { - style += ` ${this.columnWidths[i] ? this.columnWidths[i] : DEFAULT_COLUMN_WIDTH}`; + for (let i = 0; i < columns.length; i++) { + style += ` ${columns[i]?.width ?? DEFAULT_COLUMN_WIDTH}`; } + } else { + // if there are no custom column widths, each column is the same width and they take up the available space + style += `repeat(${columns.length}, ${DEFAULT_COLUMN_WIDTH})`; } return style; diff --git a/packages/components/src/components/hds/advanced-table/models/column.ts b/packages/components/src/components/hds/advanced-table/models/column.ts index 982b8bcb20b..2f7c1ef23f9 100644 --- a/packages/components/src/components/hds/advanced-table/models/column.ts +++ b/packages/components/src/components/hds/advanced-table/models/column.ts @@ -1,6 +1,7 @@ import { tracked } from '@glimmer/tracking'; -import type { HdsAdvancedTableHorizontalAlignment } from "../types"; +import type { HdsAdvancedTableColumn as HdsAdvancedTableColumnType } from '../types'; +import type { HdsAdvancedTableHorizontalAlignment } from '../types'; export default class HdsAdvancedTableColumn { @tracked label: string = ''; @@ -13,15 +14,13 @@ export default class HdsAdvancedTableColumn { @tracked isVisuallyHidden?: boolean = false; @tracked tooltip?: string = undefined; @tracked width?: string = undefined; - + @tracked sortingFunction?: (a: unknown, b: unknown) => number = undefined; - constructor(args: HdsAdvancedTableColumn) { + constructor(args: HdsAdvancedTableColumnType) { this.label = args.label; this.align = args.align; this.key = args.key; - this.isExpandable = args.isExpandable; - this.isReorderable = args.isReorderable; this.isResizable = args.isResizable; this.isSortable = args.isSortable; this.isVisuallyHidden = args.isVisuallyHidden; diff --git a/packages/components/src/components/hds/advanced-table/models/row.ts b/packages/components/src/components/hds/advanced-table/models/row.ts index d1dababaf10..4ecd8f27c41 100644 --- a/packages/components/src/components/hds/advanced-table/models/row.ts +++ b/packages/components/src/components/hds/advanced-table/models/row.ts @@ -7,8 +7,11 @@ import { tracked } from '@glimmer/tracking'; import { action } from '@ember/object'; import { guidFor } from '@ember/object/internals'; +import type { HdsAdvancedTableColumn, HdsAdvancedTableCell } from '../types'; + interface HdsAdvancedTableRowArgs { [key: string]: unknown; + columns: HdsAdvancedTableColumn[]; id?: string; childrenKey?: string; } @@ -20,6 +23,7 @@ export default class HdsAdvancedTableRow { [key: string]: unknown; @tracked isOpen: boolean = false; + @tracked cells: HdsAdvancedTableCell[] = []; children: HdsAdvancedTableRow[] = []; childrenKey: string; @@ -33,6 +37,17 @@ export default class HdsAdvancedTableRow { } constructor(args: HdsAdvancedTableRowArgs) { + const { columns } = args; + + this.cells = columns.map((column) => { + const cell = args[column.key ?? '']; + + return { + columnKey: column.key, + value: cell, + }; + }); + // set row data Object.assign(this, args); diff --git a/packages/components/src/components/hds/advanced-table/models/table.ts b/packages/components/src/components/hds/advanced-table/models/table.ts index fa50988e565..0fb4f3bb7c9 100644 --- a/packages/components/src/components/hds/advanced-table/models/table.ts +++ b/packages/components/src/components/hds/advanced-table/models/table.ts @@ -7,13 +7,17 @@ import HdsAdvancedTableRow from './row.ts'; import { action } from '@ember/object'; import { tracked } from '@glimmer/tracking'; +import HdsAdvancedTableColumn from './column.ts'; + import type { + HdsAdvancedTableColumn as HdsAdvancedTableColumnType, HdsAdvancedTableExpandState, HdsAdvancedTableModel, } from '../types'; interface HdsAdvancedTableTableArgs { model: HdsAdvancedTableModel; + columns: HdsAdvancedTableColumnType[]; childrenKey?: string; } @@ -34,15 +38,15 @@ function getChildrenCount(rows: HdsAdvancedTableRow[]): number { } export default class HdsAdvancedTableTableModel { - @tracked rows: HdsAdvancedTableRow[] = []; + @tracked columns: HdsAdvancedTableColumn[] = []; - childrenKey?: string; + rows: HdsAdvancedTableRow[] = []; constructor(args: HdsAdvancedTableTableArgs) { - const { model, childrenKey } = args; + const { model, columns, childrenKey } = args; - this.childrenKey = childrenKey; - this.updateModel(model); + this._setupColumns({ columns }); + this._setupRows({ model, columns, childrenKey }); } get totalRowCount(): number { @@ -73,6 +77,22 @@ export default class HdsAdvancedTableTableModel { } } + private _setupColumns({ + columns, + }: Pick) { + this.columns = columns.map((column) => new HdsAdvancedTableColumn(column)); + } + + private _setupRows({ + model, + columns, + childrenKey, + }: Pick) { + this.rows = model.map((row) => { + return new HdsAdvancedTableRow({ ...row, childrenKey, columns }); + }); + } + @action updateModel(model: HdsAdvancedTableModel) { this.rows = model.map((row) => { diff --git a/packages/components/src/components/hds/advanced-table/td.ts b/packages/components/src/components/hds/advanced-table/td.ts index c985dbeb5ad..502e13b90dd 100644 --- a/packages/components/src/components/hds/advanced-table/td.ts +++ b/packages/components/src/components/hds/advanced-table/td.ts @@ -22,6 +22,7 @@ export interface HdsAdvancedTableTdSignature { Args: { align?: HdsAdvancedTableHorizontalAlignment; rowspan?: number; + columnKey?: string; colspan?: number; }; Blocks: { diff --git a/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs b/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs new file mode 100644 index 00000000000..853eae8da06 --- /dev/null +++ b/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/packages/components/src/components/hds/advanced-table/th-resize-handle.ts b/packages/components/src/components/hds/advanced-table/th-resize-handle.ts new file mode 100644 index 00000000000..578b75f35d4 --- /dev/null +++ b/packages/components/src/components/hds/advanced-table/th-resize-handle.ts @@ -0,0 +1,66 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; +import { action } from '@ember/object'; + +import type HdsAdvancedTableColumn from './models/column'; + +export interface HdsAdvancedTableThResizeHandleSignature { + Args: { + column: HdsAdvancedTableColumn; + }; + Blocks: { + default?: []; + }; + Element: HTMLDivElement; +} + +export default class HdsAdvancedTableThResizeHandle extends Component { + @tracked resizing: { startX: number; startW: number } | null = null; + + private _resize(event: PointerEvent): void { + const { column } = this.args ?? {}; + + if (this.resizing === null || column === undefined) { + return; + } + + const { startX, startW } = this.resizing; + + const newW = Math.max(startW + (event.clientX - startX), 50); + + column.width = `${newW}px`; + } + + @action + startResize(event: PointerEvent): void { + event.preventDefault(); + + const target = event.target as HTMLDivElement; + const parent = target.parentElement as HTMLDivElement; + const parentWidth = parent.clientWidth; + + const { column } = this.args ?? {}; + + column.width = column.width ?? `${parentWidth}px`; + + // width is in px like `100px` + const numericalWidth = parseInt(column.width, 10); + + this.resizing = { startX: event.clientX, startW: numericalWidth }; + + window.addEventListener('pointermove', this._resize.bind(this)); + window.addEventListener('pointerup', this._stopResize.bind(this)); + } + + private _stopResize() { + window.removeEventListener('pointermove', this._resize.bind(this)); + window.removeEventListener('pointerup', this._stopResize.bind(this)); + + this.resizing = null; + } +} \ No newline at end of file diff --git a/packages/components/src/components/hds/advanced-table/th-sort.ts b/packages/components/src/components/hds/advanced-table/th-sort.ts index 53c10bc7a08..7ad09f9cbdc 100644 --- a/packages/components/src/components/hds/advanced-table/th-sort.ts +++ b/packages/components/src/components/hds/advanced-table/th-sort.ts @@ -23,6 +23,7 @@ import type { } from './types.ts'; import type { HdsAdvancedTableThButtonSortSignature } from './th-button-sort.ts'; import { onFocusTrapDeactivate } from '../../../modifiers/hds-advanced-table-cell/dom-management.ts'; +import type { HdsAdvancedTableThSignature } from './th.ts'; export const ALIGNMENTS: string[] = Object.values( HdsAdvancedTableHorizontalAlignmentValues @@ -31,6 +32,7 @@ export const DEFAULT_ALIGN = HdsAdvancedTableHorizontalAlignmentValues.Left; export interface HdsAdvancedTableThSortSignature { Args: { + column?: HdsAdvancedTableThSignature['Args']['column']; align?: HdsAdvancedTableHorizontalAlignment; onClickSort?: HdsAdvancedTableThButtonSortSignature['Args']['onClick']; sortOrder?: HdsAdvancedTableThSortOrder; diff --git a/packages/components/src/components/hds/advanced-table/th.hbs b/packages/components/src/components/hds/advanced-table/th.hbs index fa58c9c821d..357f2930c03 100644 --- a/packages/components/src/components/hds/advanced-table/th.hbs +++ b/packages/components/src/components/hds/advanced-table/th.hbs @@ -23,6 +23,7 @@ }} ...attributes > + {{@column.width}} {{#if @isVisuallyHidden}} {{yield}} {{else}} @@ -55,4 +56,8 @@
{{/if}} {{/if}} + + {{#if @column.isResizable}} + + {{/if}} \ No newline at end of file diff --git a/packages/components/src/components/hds/advanced-table/th.ts b/packages/components/src/components/hds/advanced-table/th.ts index 2b2a5747ec3..db8fcc4e67d 100644 --- a/packages/components/src/components/hds/advanced-table/th.ts +++ b/packages/components/src/components/hds/advanced-table/th.ts @@ -10,15 +10,16 @@ import { action } from '@ember/object'; import { tracked } from '@glimmer/tracking'; import { focusable, type FocusableElement } from 'tabbable'; import { modifier } from 'ember-modifier'; -import type Owner from '@ember/owner'; +import HdsAdvancedTableColumn from './models/column.ts'; +import { onFocusTrapDeactivate } from '../../../modifiers/hds-advanced-table-cell/dom-management.ts'; +import { HdsAdvancedTableHorizontalAlignmentValues } from './types.ts'; +import type Owner from '@ember/owner'; import type { HdsAdvancedTableHorizontalAlignment, HdsAdvancedTableScope, HdsAdvancedTableExpandState, } from './types.ts'; -import { HdsAdvancedTableHorizontalAlignmentValues } from './types.ts'; -import { onFocusTrapDeactivate } from '../../../modifiers/hds-advanced-table-cell/dom-management.ts'; export const ALIGNMENTS: string[] = Object.values( HdsAdvancedTableHorizontalAlignmentValues @@ -27,6 +28,7 @@ export const DEFAULT_ALIGN = HdsAdvancedTableHorizontalAlignmentValues.Left; export interface HdsAdvancedTableThSignature { Args: { + column?: HdsAdvancedTableColumn; align?: HdsAdvancedTableHorizontalAlignment; isVisuallyHidden?: boolean; scope?: HdsAdvancedTableScope; diff --git a/packages/components/src/components/hds/advanced-table/types.ts b/packages/components/src/components/hds/advanced-table/types.ts index 1d8a39dc09c..04bf54c60da 100644 --- a/packages/components/src/components/hds/advanced-table/types.ts +++ b/packages/components/src/components/hds/advanced-table/types.ts @@ -76,6 +76,7 @@ export type HdsAdvancedTableExpandState = boolean; interface BaseHdsAdvancedTableColumn { align?: HdsAdvancedTableHorizontalAlignment; + isResizable?: boolean; isVisuallyHidden?: boolean; label: string; sortingFunction?: HdsAdvancedTableSortingFunction; @@ -111,3 +112,8 @@ export interface HdsAdvancedTableOnSelectionChangeSignature { } export type HdsAdvancedTableModel = Array>; + +export interface HdsAdvancedTableCell { + value: unknown; + columnKey?: string; +} diff --git a/packages/components/src/styles/components/advanced-table.scss b/packages/components/src/styles/components/advanced-table.scss index cca85342a97..2d8d441fe58 100644 --- a/packages/components/src/styles/components/advanced-table.scss +++ b/packages/components/src/styles/components/advanced-table.scss @@ -86,6 +86,7 @@ $hds-advanced-table-cell-padding-tall: 22px 16px 21px 16px; // the 1px differenc display: contents; .hds-advanced-table__th { + position: relative; align-content: center; height: 100%; padding: $hds-advanced-table-cell-padding-medium; @@ -109,6 +110,29 @@ $hds-advanced-table-cell-padding-tall: 22px 16px 21px 16px; // the 1px differenc } } + .hds-advanced-table__th-resize-handle { + position: absolute; + top: 0; + right: 0; + bottom: 0; + z-index: 1; + width: 16px; + transform: translateX(50%); + cursor: col-resize; + + &::after { + position: absolute; + top: 0; + right: 0; + bottom: 0; + z-index: 1; + width: 1px; + background-color: red; + transform: translateX(-7.5px); + content: ""; + } + } + // horizontal alignment .hds-advanced-table__th--align-center, diff --git a/packages/components/src/template-registry.ts b/packages/components/src/template-registry.ts index 17ee75bce38..0a8b54aa479 100644 --- a/packages/components/src/template-registry.ts +++ b/packages/components/src/template-registry.ts @@ -13,6 +13,7 @@ import type HdsAdvancedTableThButtonExpandComponent from './components/hds/advan import type HdsAdvancedTableThButtonSortComponent from './components/hds/advanced-table/th-button-sort'; import type HdsAdvancedTableThComponent from './components/hds/advanced-table/th'; import type HdsAdvancedTableThButtonTooltipComponent from './components/hds/advanced-table/th-button-tooltip'; +import type HdsAdvancedTableThResizeHandle from './components/hds/advanced-table/th-resize-handle.ts'; import type HdsAdvancedTableThSortComponent from './components/hds/advanced-table/th-sort'; import type HdsAdvancedTableThSelectableComponent from './components/hds/advanced-table/th-selectable'; import type HdsAdvancedTableTrComponent from './components/hds/advanced-table/tr'; @@ -257,6 +258,8 @@ export default interface HdsComponentsRegistry { 'Hds::AdvancedTable::ThButtonSort': typeof HdsAdvancedTableThButtonSortComponent; 'hds/advanced-table/th-button-sort': typeof HdsAdvancedTableThButtonSortComponent; 'Hds::AdvancedTable::ThButtonTooltip': typeof HdsAdvancedTableThButtonTooltipComponent; + 'Hds::AdvancedTable::ThResizeHandle': typeof HdsAdvancedTableThResizeHandle; + 'hds/advanced-table/th-resize-handle': typeof HdsAdvancedTableThResizeHandle; 'hds/advanced-table/th-button-tooltip': typeof HdsAdvancedTableThButtonTooltipComponent; 'Hds::AdvancedTable::ThSort': typeof HdsAdvancedTableThSortComponent; 'hds/advanced-table/th-sort': typeof HdsAdvancedTableThSortComponent; diff --git a/showcase/app/controllers/components/advanced-table.js b/showcase/app/controllers/components/advanced-table.js index 873fda40f00..85ae8f536a9 100644 --- a/showcase/app/controllers/components/advanced-table.js +++ b/showcase/app/controllers/components/advanced-table.js @@ -358,6 +358,32 @@ export default class ComponentsTableController extends Controller { this.focusableElementsVisible = !this.focusableElementsVisible; } + // COLUMN RESIZING DEMO + columnResizeColumns = [ + { + key: 'artist', + label: 'Artist', + tooltip: 'More information.', + isResizable: true, + }, + { + key: 'album', + label: 'Album', + tooltip: 'More information.', + isResizable: true, + }, + { + key: 'year', + label: 'Release Year', + tooltip: 'More information.', + isResizable: true, + }, + { + key: 'other', + label: 'Additional Actions', + }, + ]; + @action noop() { // no-op diff --git a/showcase/app/templates/components/advanced-table.hbs b/showcase/app/templates/components/advanced-table.hbs index fc2e9656bdb..c6ff0e49e56 100644 --- a/showcase/app/templates/components/advanced-table.hbs +++ b/showcase/app/templates/components/advanced-table.hbs @@ -8,7 +8,31 @@ AdvancedTable
- + + <:body as |B|> + + {{B.data.artist}} + +
+ + {{B.data.album}} +
+
+ + + + + + + + + + +
+ +
+ + {{!-- {{/each}} - + --}}
\ No newline at end of file From f4c9f0547a2c962978b4a2e8fe82141a6538c8d1 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Thu, 1 May 2025 21:33:10 -0400 Subject: [PATCH 03/89] resize handle extends to full height of table --- .../src/components/hds/advanced-table/index.hbs | 1 + .../src/components/hds/advanced-table/index.ts | 4 +++- .../components/hds/advanced-table/models/table.ts | 2 +- .../hds/advanced-table/th-resize-handle.hbs | 7 ++++++- .../hds/advanced-table/th-resize-handle.ts | 13 +++++++++++++ .../src/components/hds/advanced-table/th.hbs | 3 +-- .../src/components/hds/advanced-table/th.ts | 1 + .../src/styles/components/advanced-table.scss | 4 ++-- 8 files changed, 28 insertions(+), 7 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.hbs b/packages/components/src/components/hds/advanced-table/index.hbs index 69745111dc6..bd565540e4c 100644 --- a/packages/components/src/components/hds/advanced-table/index.hbs +++ b/packages/components/src/components/hds/advanced-table/index.hbs @@ -64,6 +64,7 @@ @hasExpandAllButton={{this._tableModel.hasRowsWithChildren}} @isStickyColumn={{if (and (eq index 0) @hasStickyFirstColumn) true}} @isStickyColumnPinned={{this.isStickyColumnPinned}} + @tableHeight={{this.tableHeight}} > {{column.label}} diff --git a/packages/components/src/components/hds/advanced-table/index.ts b/packages/components/src/components/hds/advanced-table/index.ts index ef5f85bcffa..496e2d5b244 100644 --- a/packages/components/src/components/hds/advanced-table/index.ts +++ b/packages/components/src/components/hds/advanced-table/index.ts @@ -158,7 +158,7 @@ export default class HdsAdvancedTable extends Component { + this.tableHeight = element.clientHeight; + this.scrollIndicatorDimensions = getScrollIndicatorDimensions( element, this._theadElement, diff --git a/packages/components/src/components/hds/advanced-table/models/table.ts b/packages/components/src/components/hds/advanced-table/models/table.ts index 0fb4f3bb7c9..24c053ae415 100644 --- a/packages/components/src/components/hds/advanced-table/models/table.ts +++ b/packages/components/src/components/hds/advanced-table/models/table.ts @@ -5,7 +5,7 @@ import HdsAdvancedTableRow from './row.ts'; import { action } from '@ember/object'; -import { tracked } from '@glimmer/tracking'; +import { modifier } from 'ember-modifier'; import HdsAdvancedTableColumn from './column.ts'; diff --git a/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs b/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs index 853eae8da06..9985575406f 100644 --- a/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs +++ b/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs @@ -1 +1,6 @@ -
\ No newline at end of file +
\ No newline at end of file diff --git a/packages/components/src/components/hds/advanced-table/th-resize-handle.ts b/packages/components/src/components/hds/advanced-table/th-resize-handle.ts index 578b75f35d4..bc39f4991fc 100644 --- a/packages/components/src/components/hds/advanced-table/th-resize-handle.ts +++ b/packages/components/src/components/hds/advanced-table/th-resize-handle.ts @@ -9,9 +9,12 @@ import { action } from '@ember/object'; import type HdsAdvancedTableColumn from './models/column'; +const TABLE_BORDER_WIDTH = 1; + export interface HdsAdvancedTableThResizeHandleSignature { Args: { column: HdsAdvancedTableColumn; + tableHeight?: number; }; Blocks: { default?: []; @@ -63,4 +66,14 @@ export default class HdsAdvancedTableThResizeHandle extends Component - {{@column.width}} {{#if @isVisuallyHidden}} {{yield}} {{else}} @@ -58,6 +57,6 @@ {{/if}} {{#if @column.isResizable}} - + {{/if}}
\ No newline at end of file diff --git a/packages/components/src/components/hds/advanced-table/th.ts b/packages/components/src/components/hds/advanced-table/th.ts index db8fcc4e67d..4cb1b2d310e 100644 --- a/packages/components/src/components/hds/advanced-table/th.ts +++ b/packages/components/src/components/hds/advanced-table/th.ts @@ -28,6 +28,7 @@ export const DEFAULT_ALIGN = HdsAdvancedTableHorizontalAlignmentValues.Left; export interface HdsAdvancedTableThSignature { Args: { + tableHeight?: number; column?: HdsAdvancedTableColumn; align?: HdsAdvancedTableHorizontalAlignment; isVisuallyHidden?: boolean; diff --git a/packages/components/src/styles/components/advanced-table.scss b/packages/components/src/styles/components/advanced-table.scss index 2d8d441fe58..ae5083686c5 100644 --- a/packages/components/src/styles/components/advanced-table.scss +++ b/packages/components/src/styles/components/advanced-table.scss @@ -115,7 +115,7 @@ $hds-advanced-table-cell-padding-tall: 22px 16px 21px 16px; // the 1px differenc top: 0; right: 0; bottom: 0; - z-index: 1; + z-index: 2; width: 16px; transform: translateX(50%); cursor: col-resize; @@ -125,7 +125,7 @@ $hds-advanced-table-cell-padding-tall: 22px 16px 21px 16px; // the 1px differenc top: 0; right: 0; bottom: 0; - z-index: 1; + z-index: 2; width: 1px; background-color: red; transform: translateX(-7.5px); From 2a226502fb584057d29f3ceb2a8d57405d7d97e4 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Fri, 2 May 2025 09:56:12 -0400 Subject: [PATCH 04/89] adding a11y improvements --- .../src/components/hds/advanced-table/models/column.ts | 4 ++++ .../components/hds/advanced-table/th-resize-handle.hbs | 10 +++++++++- .../src/components/hds/advanced-table/types.ts | 2 ++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/components/src/components/hds/advanced-table/models/column.ts b/packages/components/src/components/hds/advanced-table/models/column.ts index 2f7c1ef23f9..d3ac1265bce 100644 --- a/packages/components/src/components/hds/advanced-table/models/column.ts +++ b/packages/components/src/components/hds/advanced-table/models/column.ts @@ -14,6 +14,8 @@ export default class HdsAdvancedTableColumn { @tracked isVisuallyHidden?: boolean = false; @tracked tooltip?: string = undefined; @tracked width?: string = undefined; + @tracked minWidth?: string = undefined; + @tracked maxWidth?: string = undefined; @tracked sortingFunction?: (a: unknown, b: unknown) => number = undefined; @@ -26,6 +28,8 @@ export default class HdsAdvancedTableColumn { this.isVisuallyHidden = args.isVisuallyHidden; this.tooltip = args.tooltip; this.width = args.width; + this.minWidth = args.minWidth; + this.maxWidth = args.maxWidth; this.sortingFunction = args.sortingFunction; } diff --git a/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs b/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs index 9985575406f..e3bbc2386b9 100644 --- a/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs +++ b/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs @@ -1,6 +1,14 @@ \ No newline at end of file diff --git a/packages/components/src/template-registry.ts b/packages/components/src/template-registry.ts index 0a8b54aa479..94689348a2c 100644 --- a/packages/components/src/template-registry.ts +++ b/packages/components/src/template-registry.ts @@ -12,6 +12,7 @@ import type HdsAdvancedTableTdComponent from './components/hds/advanced-table/td import type HdsAdvancedTableThButtonExpandComponent from './components/hds/advanced-table/th-button-expand'; import type HdsAdvancedTableThButtonSortComponent from './components/hds/advanced-table/th-button-sort'; import type HdsAdvancedTableThComponent from './components/hds/advanced-table/th'; +import type HdsAdvancedTableThMenuComponent from './components/hds/advanced-table/th-menu'; import type HdsAdvancedTableThButtonTooltipComponent from './components/hds/advanced-table/th-button-tooltip'; import type HdsAdvancedTableThResizeHandle from './components/hds/advanced-table/th-resize-handle.ts'; import type HdsAdvancedTableThSortComponent from './components/hds/advanced-table/th-sort'; @@ -251,6 +252,8 @@ export default interface HdsComponentsRegistry { 'hds/advanced-table/td': typeof HdsAdvancedTableTdComponent; 'Hds::AdvancedTable::Th': typeof HdsAdvancedTableThComponent; 'hds/advanced-table/th': typeof HdsAdvancedTableThComponent; + 'Hds::AdvancedTable::ThMenu': typeof HdsAdvancedTableThMenuComponent; + 'hds/advanced-table/th-menu': typeof HdsAdvancedTableThMenuComponent; 'Hds::AdvancedTable::Tr': typeof HdsAdvancedTableTrComponent; 'hds/advanced-table/tr': typeof HdsAdvancedTableTrComponent; 'Hds::AdvancedTable::ThButtonExpand': typeof HdsAdvancedTableThButtonExpandComponent; From ca8309bca64cd3c94fd4b0ef4ed5c87b86d61cd1 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Mon, 5 May 2025 16:35:40 -0400 Subject: [PATCH 06/89] comleted inital styling on range input --- packages/components/package.json | 1 + .../components/hds/form/range-slider/base.hbs | 11 ++++ .../components/hds/form/range-slider/base.ts | 65 +++++++++++++++++++ .../src/styles/components/form/index.scss | 1 + .../styles/components/form/range-slider.scss | 41 ++++++++++++ packages/components/src/template-registry.ts | 5 ++ .../components/form/base-elements.hbs | 7 ++ 7 files changed, 131 insertions(+) create mode 100644 packages/components/src/components/hds/form/range-slider/base.hbs create mode 100644 packages/components/src/components/hds/form/range-slider/base.ts create mode 100644 packages/components/src/styles/components/form/range-slider.scss diff --git a/packages/components/package.json b/packages/components/package.json index 10df78cb95b..c41edba7d46 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -254,6 +254,7 @@ "./components/hds/form/radio/base.js": "./dist/_app_/components/hds/form/radio/base.js", "./components/hds/form/radio/field.js": "./dist/_app_/components/hds/form/radio/field.js", "./components/hds/form/radio/group.js": "./dist/_app_/components/hds/form/radio/group.js", + "./components/hds/form/range-slider/base.js": "./dist/_app_/components/hds/form/range-slider/base.js", "./components/hds/form/select/base.js": "./dist/_app_/components/hds/form/select/base.js", "./components/hds/form/select/field.js": "./dist/_app_/components/hds/form/select/field.js", "./components/hds/form/super-select/after-options.js": "./dist/_app_/components/hds/form/super-select/after-options.js", diff --git a/packages/components/src/components/hds/form/range-slider/base.hbs b/packages/components/src/components/hds/form/range-slider/base.hbs new file mode 100644 index 00000000000..ff3a448725d --- /dev/null +++ b/packages/components/src/components/hds/form/range-slider/base.hbs @@ -0,0 +1,11 @@ + \ No newline at end of file diff --git a/packages/components/src/components/hds/form/range-slider/base.ts b/packages/components/src/components/hds/form/range-slider/base.ts new file mode 100644 index 00000000000..fdcbc523c4a --- /dev/null +++ b/packages/components/src/components/hds/form/range-slider/base.ts @@ -0,0 +1,65 @@ +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; + +interface AccessibleRangeSliderArgs { + min?: number; + max?: number; + step?: number; + value?: number; + onChange?: ( + values: { value: number } | { startValue: number; endValue: number } + ) => void; +} + +interface AccessibleRangeSliderSignature { + Args: AccessibleRangeSliderArgs; + Element: HTMLInputElement; +} + +export default class AccessibleRangeSlider extends Component { + @tracked _value: number; + + constructor(owner: unknown, args: AccessibleRangeSliderArgs) { + super(owner, args); + + this._value = args.value ?? 0; + } + + get min(): number { + return this.args.min ?? 0; + } + get max(): number { + return this.args.max ?? 100; + } + get step(): number { + return this.args.step ?? 1; + } + + get classNames(): string { + return 'hds-form-range-slider'; + } + get inlineStyles(): Record { + const percent = ((this._value - this.min) / (this.max - this.min)) * 100; + + return { + '--progress': `${percent}%`, + background: `linear-gradient( + to right, + var(--token-color-palette-blue-200) 0%, + var(--token-color-palette-blue-200) ${percent}%, + var(--token-color-palette-neutral-200) ${percent}%, + var(--token-color-palette-neutral-200) 100% + )`, + }; + } + + @action + handleInput(event: Event): void { + const target = event.target as HTMLInputElement; + const value = Number(target.value); + + this._value = value; + this.args.onChange?.({ value }); + } +} diff --git a/packages/components/src/styles/components/form/index.scss b/packages/components/src/styles/components/form/index.scss index ddbafe1d28d..e6dce3d521b 100644 --- a/packages/components/src/styles/components/form/index.scss +++ b/packages/components/src/styles/components/form/index.scss @@ -20,6 +20,7 @@ @use "./masked-input"; @use "./radio"; @use "./radio-card"; +@use "./range-slider"; @use "./select"; @use "./super-select"; @use "./text-input"; diff --git a/packages/components/src/styles/components/form/range-slider.scss b/packages/components/src/styles/components/form/range-slider.scss new file mode 100644 index 00000000000..e3326bec951 --- /dev/null +++ b/packages/components/src/styles/components/form/range-slider.scss @@ -0,0 +1,41 @@ +input[type="range"].hds-form-range-slider { + width: 100%; + height: 4px; + border-radius: 2px; + cursor: pointer; + // stylelint-disable-next-line property-no-vendor-prefix + -webkit-appearance: none; + appearance: none; +} + +input[type="range"].hds-form-range-slider::-webkit-slider-runnable-track { + height: 4px; + background: transparent; + border-radius: 2px; +} + +input[type="range"].hds-form-range-slider::-moz-range-track { + height: 4px; + background: transparent; + border-radius: 2px; +} + +input[type="range"].hds-form-range-slider::-webkit-slider-thumb { + width: 16px; + height: 16px; + margin-top: -6px; + background-color: var(--token-color-surface-interactive); + border: 1px solid var(--token-color-palette-neutral-400); + border-radius: 8px; + // stylelint-disable-next-line property-no-vendor-prefix + -webkit-appearance: none; + appearance: none; +} + +input[type="range"].hds-form-range-slider::-moz-range-thumb { + width: 1rem; + height: 2rem; + background-color: var(--token-color-surface-interactive); + border: 1px solid var(--token-color-palette-neutral-400); + border-radius: 8px; +} diff --git a/packages/components/src/template-registry.ts b/packages/components/src/template-registry.ts index 94689348a2c..9a4d2c1f6d8 100644 --- a/packages/components/src/template-registry.ts +++ b/packages/components/src/template-registry.ts @@ -123,6 +123,7 @@ import type HdsFormRadioCardComponent from './components/hds/form/radio-card'; import type HdsFormRadioCardDescriptionComponent from './components/hds/form/radio-card/description'; import type HdsFormRadioCardGroupComponent from './components/hds/form/radio-card/group'; import type HdsFormRadioCardLabelComponent from './components/hds/form/radio-card/label'; +import type HdsFormRangeSliderBaseComponent from './components/hds/form/range-slider/base'; import type HdsFormSelectBaseComponent from './components/hds/form/select/base'; import type HdsFormSelectFieldComponent from './components/hds/form/select/field'; import type HdsFormSuperSelectAfterOptionsComponent from './components/hds/form/super-select/after-options'; @@ -640,6 +641,10 @@ export default interface HdsComponentsRegistry { 'Hds::Form::RadioCard::Label': typeof HdsFormRadioCardLabelComponent; 'hds/form/radio-card/label': typeof HdsFormRadioCardLabelComponent; + // Form RangeSlider + 'Hds::Form::RangeSlider::Base': typeof HdsFormRangeSliderBaseComponent; + 'hds/form/range-slider/base': typeof HdsFormRangeSliderBaseComponent; + // Form Select 'Hds::Form::Select::Base': typeof HdsFormSelectBaseComponent; 'hds/form/select/base': typeof HdsFormSelectBaseComponent; diff --git a/showcase/app/templates/components/form/base-elements.hbs b/showcase/app/templates/components/form/base-elements.hbs index 2b340f1d692..43e13ae52c6 100644 --- a/showcase/app/templates/components/form/base-elements.hbs +++ b/showcase/app/templates/components/form/base-elements.hbs @@ -8,6 +8,13 @@ Form / Base elements
+ Label + + + + + + Label From e55a36f721e74e9d614d2584d8e04072dd9e66c9 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Wed, 7 May 2025 09:03:04 -0400 Subject: [PATCH 07/89] resizing columns works with both the slider and the column border --- .../hds/advanced-table/models/column.ts | 10 +++++--- .../hds/advanced-table/models/table.ts | 1 - .../hds/advanced-table/th-menu/index.hbs | 12 ++++------ .../hds/advanced-table/th-menu/index.ts | 14 +++++++++-- .../src/components/hds/advanced-table/th.hbs | 14 ++++++++++- .../src/components/hds/advanced-table/th.ts | 11 +++++++++ .../components/hds/form/range-slider/base.hbs | 2 +- .../components/hds/form/range-slider/base.ts | 23 ++++++------------- .../src/styles/components/advanced-table.scss | 14 +++++++++++ 9 files changed, 69 insertions(+), 32 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/models/column.ts b/packages/components/src/components/hds/advanced-table/models/column.ts index 436eceb0e7f..8e4d1194521 100644 --- a/packages/components/src/components/hds/advanced-table/models/column.ts +++ b/packages/components/src/components/hds/advanced-table/models/column.ts @@ -30,6 +30,9 @@ export default class HdsAdvancedTableColumn { get numericalWidth(): number { return getNumericalWidth(this.width); } + set numericalWidth(value: number) { + this.width = `${value}px`; + } get numericalMinWidth(): number { return getNumericalWidth(this.minWidth); @@ -47,10 +50,11 @@ export default class HdsAdvancedTableColumn { this.isSortable = args.isSortable; this.isVisuallyHidden = args.isVisuallyHidden; this.tooltip = args.tooltip; + this.width = args.width; - this.minWidth = args.minWidth; - this.maxWidth = args.maxWidth; + this.minWidth = args.minWidth ?? `100px`; // default min width + this.maxWidth = args.maxWidth ?? `500px`; // default max width this.sortingFunction = args.sortingFunction; } -} \ No newline at end of file +} diff --git a/packages/components/src/components/hds/advanced-table/models/table.ts b/packages/components/src/components/hds/advanced-table/models/table.ts index 24c053ae415..8364cd8de13 100644 --- a/packages/components/src/components/hds/advanced-table/models/table.ts +++ b/packages/components/src/components/hds/advanced-table/models/table.ts @@ -5,7 +5,6 @@ import HdsAdvancedTableRow from './row.ts'; import { action } from '@ember/object'; -import { modifier } from 'ember-modifier'; import HdsAdvancedTableColumn from './column.ts'; diff --git a/packages/components/src/components/hds/advanced-table/th-menu/index.hbs b/packages/components/src/components/hds/advanced-table/th-menu/index.hbs index 6bd442fdd90..af984493523 100644 --- a/packages/components/src/components/hds/advanced-table/th-menu/index.hbs +++ b/packages/components/src/components/hds/advanced-table/th-menu/index.hbs @@ -1,17 +1,13 @@
- + {{#if @column.isResizable}} - + Resize column - + Reset column width {{/if}} - - - {{#if this.isResizing}} - Resizing - {{/if}} +
\ No newline at end of file diff --git a/packages/components/src/components/hds/advanced-table/th-menu/index.ts b/packages/components/src/components/hds/advanced-table/th-menu/index.ts index 800c78cbe31..1e396c3e28d 100644 --- a/packages/components/src/components/hds/advanced-table/th-menu/index.ts +++ b/packages/components/src/components/hds/advanced-table/th-menu/index.ts @@ -11,6 +11,7 @@ import { action } from '@ember/object'; export interface HdsAdvancedTableThMenuSignature { Args: { column: HdsAdvancedTableColumn; + onStartResize: () => void; }; Blocks: { default?: []; @@ -22,11 +23,20 @@ export default class HdsAdvancedTableThMenu extends Component void) | undefined): void { + this.args.onStartResize(); + + callback?.(); + } + + @action + resetColumnWidth(callback: (() => void) | undefined): void { const { column } = this.args ?? {}; if (column) { column.width = undefined; } + + callback?.(); } -} \ No newline at end of file +} diff --git a/packages/components/src/components/hds/advanced-table/th.hbs b/packages/components/src/components/hds/advanced-table/th.hbs index 82bbff8fbc0..3c85d96b26f 100644 --- a/packages/components/src/components/hds/advanced-table/th.hbs +++ b/packages/components/src/components/hds/advanced-table/th.hbs @@ -58,8 +58,20 @@ {{/if}} {{#if @column.isResizable}} - + {{/if}} + + {{#if this.isResizeSliderVisible}} +
+ {{@column.numericalWidth}} + +
+ {{/if}}
\ No newline at end of file diff --git a/packages/components/src/components/hds/advanced-table/th.ts b/packages/components/src/components/hds/advanced-table/th.ts index 4cb1b2d310e..463ecc729da 100644 --- a/packages/components/src/components/hds/advanced-table/th.ts +++ b/packages/components/src/components/hds/advanced-table/th.ts @@ -59,6 +59,8 @@ export default class HdsAdvancedTableTh extends Component void; + value: number; + onChange?: (value: number) => void; } interface AccessibleRangeSliderSignature { @@ -18,14 +15,6 @@ interface AccessibleRangeSliderSignature { } export default class AccessibleRangeSlider extends Component { - @tracked _value: number; - - constructor(owner: unknown, args: AccessibleRangeSliderArgs) { - super(owner, args); - - this._value = args.value ?? 0; - } - get min(): number { return this.args.min ?? 0; } @@ -35,12 +24,15 @@ export default class AccessibleRangeSlider extends Component { - const percent = ((this._value - this.min) / (this.max - this.min)) * 100; + const percent = ((this.value - this.min) / (this.max - this.min)) * 100; return { '--progress': `${percent}%`, @@ -59,7 +51,6 @@ export default class AccessibleRangeSlider extends Component Date: Wed, 7 May 2025 16:31:19 -0400 Subject: [PATCH 08/89] requiring widths for resize --- .../hds/advanced-table/models/column.ts | 16 ++++++++++++++-- .../src/components/hds/advanced-table/th.hbs | 6 +++++- .../src/styles/components/form/range-slider.scss | 1 + .../app/controllers/components/advanced-table.js | 3 +++ 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/models/column.ts b/packages/components/src/components/hds/advanced-table/models/column.ts index 8e4d1194521..76c14e14a33 100644 --- a/packages/components/src/components/hds/advanced-table/models/column.ts +++ b/packages/components/src/components/hds/advanced-table/models/column.ts @@ -2,6 +2,7 @@ import { tracked } from '@glimmer/tracking'; import type { HdsAdvancedTableColumn as HdsAdvancedTableColumnType } from '../types'; import type { HdsAdvancedTableHorizontalAlignment } from '../types'; +import { assert } from '@ember/debug'; function getNumericalWidth(width: string | undefined): number { if (width === undefined) { @@ -13,11 +14,12 @@ function getNumericalWidth(width: string | undefined): number { } export default class HdsAdvancedTableColumn { @tracked label: string = ''; + @tracked _isResizable: boolean = false; + @tracked align?: HdsAdvancedTableHorizontalAlignment = 'left'; @tracked key?: string = undefined; @tracked isExpandable?: boolean = false; @tracked isReorderable?: boolean = false; - @tracked isResizable?: boolean = false; @tracked isSortable?: boolean = false; @tracked isVisuallyHidden?: boolean = false; @tracked tooltip?: string = undefined; @@ -27,6 +29,16 @@ export default class HdsAdvancedTableColumn { @tracked sortingFunction?: (a: unknown, b: unknown) => number = undefined; + get isResizable(): boolean | undefined { + if (!this._isResizable) { + return false; + } + + assert('width must be set to use isResizable', this.width !== undefined); + + return this._isResizable; + } + get numericalWidth(): number { return getNumericalWidth(this.width); } @@ -46,7 +58,7 @@ export default class HdsAdvancedTableColumn { this.label = args.label; this.align = args.align; this.key = args.key; - this.isResizable = args.isResizable; + this._isResizable = args.isResizable ?? false; this.isSortable = args.isSortable; this.isVisuallyHidden = args.isVisuallyHidden; this.tooltip = args.tooltip; diff --git a/packages/components/src/components/hds/advanced-table/th.hbs b/packages/components/src/components/hds/advanced-table/th.hbs index 3c85d96b26f..00ae344b7f6 100644 --- a/packages/components/src/components/hds/advanced-table/th.hbs +++ b/packages/components/src/components/hds/advanced-table/th.hbs @@ -65,8 +65,12 @@ {{#if this.isResizeSliderVisible}}
- {{@column.numericalWidth}} + + {{@column.label}} + column width + Date: Wed, 7 May 2025 17:58:48 -0400 Subject: [PATCH 09/89] reset width works --- packages/components/package.json | 1 + .../hds/advanced-table/models/column.ts | 11 ++++ .../hds/advanced-table/th-menu/index.ts | 2 +- .../src/components/hds/advanced-table/th.hbs | 2 +- .../src/components/hds/advanced-table/th.ts | 5 ++ .../components/hds/form/range-slider/base.ts | 2 +- .../src/modifiers/hds-on-click-outside.ts | 50 +++++++++++++++++++ packages/components/src/template-registry.ts | 4 ++ 8 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 packages/components/src/modifiers/hds-on-click-outside.ts diff --git a/packages/components/package.json b/packages/components/package.json index c41edba7d46..082f5e763c0 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -367,6 +367,7 @@ "./modifiers/hds-code-editor/palettes/hds-dark-palette.js": "./dist/_app_/modifiers/hds-code-editor/palettes/hds-dark-palette.js", "./modifiers/hds-code-editor/themes/hds-dark-theme.js": "./dist/_app_/modifiers/hds-code-editor/themes/hds-dark-theme.js", "./modifiers/hds-code-editor/types.js": "./dist/_app_/modifiers/hds-code-editor/types.js", + "./modifiers/hds-on-click-outside.js": "./dist/_app_/modifiers/hds-on-click-outside.js", "./modifiers/hds-register-event.js": "./dist/_app_/modifiers/hds-register-event.js", "./modifiers/hds-tooltip.js": "./dist/_app_/modifiers/hds-tooltip.js", "./services/hds-time.js": "./dist/_app_/services/hds-time.js" diff --git a/packages/components/src/components/hds/advanced-table/models/column.ts b/packages/components/src/components/hds/advanced-table/models/column.ts index 76c14e14a33..47983c41ab7 100644 --- a/packages/components/src/components/hds/advanced-table/models/column.ts +++ b/packages/components/src/components/hds/advanced-table/models/column.ts @@ -1,4 +1,5 @@ import { tracked } from '@glimmer/tracking'; +import { action } from '@ember/object'; import type { HdsAdvancedTableColumn as HdsAdvancedTableColumnType } from '../types'; import type { HdsAdvancedTableHorizontalAlignment } from '../types'; @@ -29,6 +30,8 @@ export default class HdsAdvancedTableColumn { @tracked sortingFunction?: (a: unknown, b: unknown) => number = undefined; + private _originalWidth?: string = undefined; + get isResizable(): boolean | undefined { if (!this._isResizable) { return false; @@ -64,9 +67,17 @@ export default class HdsAdvancedTableColumn { this.tooltip = args.tooltip; this.width = args.width; + this._originalWidth = args.width; this.minWidth = args.minWidth ?? `100px`; // default min width this.maxWidth = args.maxWidth ?? `500px`; // default max width this.sortingFunction = args.sortingFunction; } + + @action + restoreWidth(): void { + if (this._originalWidth !== undefined) { + this.width = this._originalWidth; + } + } } diff --git a/packages/components/src/components/hds/advanced-table/th-menu/index.ts b/packages/components/src/components/hds/advanced-table/th-menu/index.ts index 1e396c3e28d..4b31a1eba2c 100644 --- a/packages/components/src/components/hds/advanced-table/th-menu/index.ts +++ b/packages/components/src/components/hds/advanced-table/th-menu/index.ts @@ -34,7 +34,7 @@ export default class HdsAdvancedTableThMenu extends Component {{#if this.isResizeSliderVisible}} -
+
{{@column.label}} column width diff --git a/packages/components/src/components/hds/advanced-table/th.ts b/packages/components/src/components/hds/advanced-table/th.ts index 463ecc729da..aa1dbe7eede 100644 --- a/packages/components/src/components/hds/advanced-table/th.ts +++ b/packages/components/src/components/hds/advanced-table/th.ts @@ -136,6 +136,11 @@ export default class HdsAdvancedTableTh extends Component void; } diff --git a/packages/components/src/modifiers/hds-on-click-outside.ts b/packages/components/src/modifiers/hds-on-click-outside.ts new file mode 100644 index 00000000000..f3954b97d76 --- /dev/null +++ b/packages/components/src/modifiers/hds-on-click-outside.ts @@ -0,0 +1,50 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import Modifier from 'ember-modifier'; +import { registerDestructor } from '@ember/destroyable'; +import { next } from '@ember/runloop'; + +import type Owner from '@ember/owner'; +import type { ArgsFor, PositionalArgs } from 'ember-modifier'; + +export interface HdsOnClickOutsideSignature { + Args: { + Positional: [() => unknown]; + }; + Element: HTMLDivElement; +} + +export default class HdsOnClickOutsideModifier extends Modifier { + private _element?: HTMLElement = undefined; + + clickOutsideHandler!: (event: MouseEvent) => void; + + constructor(owner: Owner, args: ArgsFor) { + super(owner, args); + + registerDestructor(this, () => { + document.removeEventListener('click', this.clickOutsideHandler); + }); + } + + modify( + element: HTMLDivElement, + positional: PositionalArgs + ): void { + const [callback] = positional; + + this._element = element; + + this.clickOutsideHandler = (event: MouseEvent) => { + if (this._element && !this._element.contains(event.target as Node)) { + callback(); + } + }; + + // eslint-disable-next-line ember/no-runloop + next(() => document.addEventListener('click', this.clickOutsideHandler)); + } +} diff --git a/packages/components/src/template-registry.ts b/packages/components/src/template-registry.ts index 9a4d2c1f6d8..1f0d50a4720 100644 --- a/packages/components/src/template-registry.ts +++ b/packages/components/src/template-registry.ts @@ -232,6 +232,7 @@ import type HdsClipboardModifier from './modifiers/hds-clipboard.ts'; import type HdsRegisterEventModifier from './modifiers/hds-register-event.ts'; import type HdsTooltipModifier from './modifiers/hds-tooltip.ts'; import type HdsAdvancedTableCellModifier from './modifiers/hds-advanced-table-cell.ts'; +import type HdsOnClickOutsideModifier from './modifiers/hds-on-click-outside.ts'; export default interface HdsComponentsRegistry { // ----- COMPONENTS --------------------------------------------------- @@ -996,4 +997,7 @@ export default interface HdsComponentsRegistry { // hds-advanced-table-cell 'hds-advanced-table-cell': typeof HdsAdvancedTableCellModifier; + + // hds-on-click-outside + 'hds-on-click-outside': typeof HdsOnClickOutsideModifier; } From 38bf40a810ea2b0583d9d5e8cc8074d218e03571 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Fri, 9 May 2025 13:07:12 -0400 Subject: [PATCH 10/89] column resize in progress --- packages/components/package.json | 1 + .../hds/advanced-table/models/column.ts | 95 +++++++++++++++---- .../hds/advanced-table/models/table.ts | 7 +- .../hds/advanced-table/th-resize-form.hbs | 28 ++++++ .../hds/advanced-table/th-resize-form.ts | 67 +++++++++++++ .../hds/advanced-table/th-resize-handle.ts | 24 ++--- .../src/components/hds/advanced-table/th.hbs | 18 ++-- .../src/components/hds/advanced-table/th.ts | 11 +-- .../components/hds/advanced-table/types.ts | 14 ++- .../src/modifiers/hds-on-click-outside.ts | 6 +- .../styles/components/form/range-slider.scss | 2 +- packages/components/src/template-registry.ts | 3 + 12 files changed, 215 insertions(+), 61 deletions(-) create mode 100644 packages/components/src/components/hds/advanced-table/th-resize-form.hbs create mode 100644 packages/components/src/components/hds/advanced-table/th-resize-form.ts diff --git a/packages/components/package.json b/packages/components/package.json index 082f5e763c0..0f796555696 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -147,6 +147,7 @@ "./components/hds/advanced-table/th-button-sort.js": "./dist/_app_/components/hds/advanced-table/th-button-sort.js", "./components/hds/advanced-table/th-button-tooltip.js": "./dist/_app_/components/hds/advanced-table/th-button-tooltip.js", "./components/hds/advanced-table/th-menu/index.js": "./dist/_app_/components/hds/advanced-table/th-menu/index.js", + "./components/hds/advanced-table/th-resize-form.js": "./dist/_app_/components/hds/advanced-table/th-resize-form.js", "./components/hds/advanced-table/th-resize-handle.js": "./dist/_app_/components/hds/advanced-table/th-resize-handle.js", "./components/hds/advanced-table/th-selectable.js": "./dist/_app_/components/hds/advanced-table/th-selectable.js", "./components/hds/advanced-table/th-sort.js": "./dist/_app_/components/hds/advanced-table/th-sort.js", diff --git a/packages/components/src/components/hds/advanced-table/models/column.ts b/packages/components/src/components/hds/advanced-table/models/column.ts index 47983c41ab7..3b0aa1e7e12 100644 --- a/packages/components/src/components/hds/advanced-table/models/column.ts +++ b/packages/components/src/components/hds/advanced-table/models/column.ts @@ -1,13 +1,28 @@ import { tracked } from '@glimmer/tracking'; import { action } from '@ember/object'; +import { assert } from '@ember/debug'; +import { CssSizeUnitValues } from '../types.ts'; import type { HdsAdvancedTableColumn as HdsAdvancedTableColumnType } from '../types'; import type { HdsAdvancedTableHorizontalAlignment } from '../types'; -import { assert } from '@ember/debug'; +import type { CssSize, CssSizeUnit } from '../types'; +import type HdsAdvancedTableTableModel from './table.ts'; + +function getCssUnit(cssString?: string): CssSizeUnit | undefined { + if (cssString === undefined) { + return undefined; + } + + const match = cssString.match(/([a-zA-Z%]+)/); + + if (match) { + return match[0] as CssSizeUnit; + } +} -function getNumericalWidth(width: string | undefined): number { +function getNumericalWidth(width?: CssSize, defaultWidth?: number): number { if (width === undefined) { - return 0; + return defaultWidth ?? 0; } // width is a css string @@ -24,13 +39,14 @@ export default class HdsAdvancedTableColumn { @tracked isSortable?: boolean = false; @tracked isVisuallyHidden?: boolean = false; @tracked tooltip?: string = undefined; - @tracked width?: string = undefined; // css width like `100px` - @tracked minWidth?: string = undefined; // css width like `100px` - @tracked maxWidth?: string = undefined; // css width like `100px` + @tracked width?: CssSize = undefined; + @tracked minWidth?: CssSize = undefined; + @tracked maxWidth?: CssSize = undefined; @tracked sortingFunction?: (a: unknown, b: unknown) => number = undefined; - private _originalWidth?: string = undefined; + private _originalWidth?: CssSize = undefined; + private _cssWidthUnit?: CssSizeUnit = CssSizeUnitValues.Px; get isResizable(): boolean | undefined { if (!this._isResizable) { @@ -42,11 +58,32 @@ export default class HdsAdvancedTableColumn { return this._isResizable; } + get pixelWidth(): number | undefined { + const numericalWidth = getNumericalWidth(this.width); + + if (this._cssWidthUnit === CssSizeUnitValues.Px) { + return numericalWidth; + } + + if (this._cssWidthUnit === CssSizeUnitValues.Percent) { + return 0; + } + } + get numericalWidth(): number { return getNumericalWidth(this.width); } set numericalWidth(value: number) { - this.width = `${value}px`; + if (this._cssWidthUnit === CssSizeUnitValues.Px) { + this.width = `${value}${CssSizeUnitValues.Px}`; + console.log({ width: this.width }); + return; + } + if (this._cssWidthUnit === CssSizeUnitValues.Percent) { + // TODO: actually calculate the width based on the table width + this.width = `${value}${CssSizeUnitValues.Percent}`; + return; + } } get numericalMinWidth(): number { @@ -57,7 +94,9 @@ export default class HdsAdvancedTableColumn { return getNumericalWidth(this.maxWidth); } - constructor(args: HdsAdvancedTableColumnType) { + constructor( + args: HdsAdvancedTableColumnType & { table: HdsAdvancedTableTableModel } + ) { this.label = args.label; this.align = args.align; this.key = args.key; @@ -66,18 +105,42 @@ export default class HdsAdvancedTableColumn { this.isVisuallyHidden = args.isVisuallyHidden; this.tooltip = args.tooltip; - this.width = args.width; - this._originalWidth = args.width; - this.minWidth = args.minWidth ?? `100px`; // default min width - this.maxWidth = args.maxWidth ?? `500px`; // default max width - this.sortingFunction = args.sortingFunction; + + this.setWidthValues(args); + } + + setWidthValues({ + width, + minWidth, + maxWidth, + }: HdsAdvancedTableColumnType): void { + this.width = width; + // set default minWidth and maxWidth if width is set + this.minWidth = (minWidth ?? this.width !== undefined) ? '50px' : undefined; + this.maxWidth = + (maxWidth ?? this.width !== undefined) ? '800px' : undefined; + + // capture the width at the time of instantiation so it can be restored + this._originalWidth = width; + + this._cssWidthUnit = getCssUnit(this.width); + } + + @action + setNumericalWidth(newNumericalWidth: number): void { + this.numericalWidth = Math.min( + Math.max(newNumericalWidth, this.numericalMinWidth), + this.numericalMaxWidth + ); } @action restoreWidth(): void { - if (this._originalWidth !== undefined) { - this.width = this._originalWidth; + if (this._originalWidth === undefined) { + return; } + + this.width = this._originalWidth; } } diff --git a/packages/components/src/components/hds/advanced-table/models/table.ts b/packages/components/src/components/hds/advanced-table/models/table.ts index 8364cd8de13..d0fccafffae 100644 --- a/packages/components/src/components/hds/advanced-table/models/table.ts +++ b/packages/components/src/components/hds/advanced-table/models/table.ts @@ -39,6 +39,9 @@ function getChildrenCount(rows: HdsAdvancedTableRow[]): number { export default class HdsAdvancedTableTableModel { @tracked columns: HdsAdvancedTableColumn[] = []; + @tracked pixelWidth: number = 0; + @tracked pixelHeight: number = 0; + rows: HdsAdvancedTableRow[] = []; constructor(args: HdsAdvancedTableTableArgs) { @@ -79,7 +82,9 @@ export default class HdsAdvancedTableTableModel { private _setupColumns({ columns, }: Pick) { - this.columns = columns.map((column) => new HdsAdvancedTableColumn(column)); + this.columns = columns.map((column) => { + return new HdsAdvancedTableColumn({ ...column, table: this }); + }); } private _setupRows({ diff --git a/packages/components/src/components/hds/advanced-table/th-resize-form.hbs b/packages/components/src/components/hds/advanced-table/th-resize-form.hbs new file mode 100644 index 00000000000..0ce3ac85538 --- /dev/null +++ b/packages/components/src/components/hds/advanced-table/th-resize-form.hbs @@ -0,0 +1,28 @@ +
+ + {{@column.label}} + column width + + + + + + + + + \ No newline at end of file diff --git a/packages/components/src/components/hds/advanced-table/th-resize-form.ts b/packages/components/src/components/hds/advanced-table/th-resize-form.ts new file mode 100644 index 00000000000..25d2d6a0603 --- /dev/null +++ b/packages/components/src/components/hds/advanced-table/th-resize-form.ts @@ -0,0 +1,67 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import Component from '@glimmer/component'; +import { action } from '@ember/object'; + +import type HdsAdvancedTableColumn from './models/column'; +import type Owner from '@ember/owner'; + +export interface HdsAdvancedTableThResizeFormSignature { + Args: { + column: HdsAdvancedTableColumn; + labelId: string; + onClose: () => void; + }; + Blocks: { + default?: []; + }; + Element: HTMLFormElement; +} + +export default class HdsAdvancedTableThResizeForm extends Component { + originalColumnNumericalWidth: number; + + constructor( + owner: Owner, + args: HdsAdvancedTableThResizeFormSignature['Args'] + ) { + super(owner, args); + + const { column } = this.args; + + this.originalColumnNumericalWidth = column.numericalWidth; + } + + @action + resizeColumn(width: number): void { + const { column } = this.args; + + column.setNumericalWidth(width); + } + + @action + handleKeyup(event: KeyboardEvent): void { + if (event.key === 'Escape') { + this.handleCancel(); + } + } + + @action + handleCancel(): void { + const { column } = this.args; + + column.setNumericalWidth(this.originalColumnNumericalWidth); + + this.args.onClose(); + } + + @action + handleSubmit(event: Event): void { + event.preventDefault(); + + this.args.onClose(); + } +} diff --git a/packages/components/src/components/hds/advanced-table/th-resize-handle.ts b/packages/components/src/components/hds/advanced-table/th-resize-handle.ts index bc39f4991fc..09c59884782 100644 --- a/packages/components/src/components/hds/advanced-table/th-resize-handle.ts +++ b/packages/components/src/components/hds/advanced-table/th-resize-handle.ts @@ -26,7 +26,7 @@ export default class HdsAdvancedTableThResizeHandle extends Component {{#if this.isResizeSliderVisible}} -
- - {{@column.label}} - column width - - -
+ {{/if}} {{/if}}
\ No newline at end of file diff --git a/packages/components/src/components/hds/advanced-table/th.ts b/packages/components/src/components/hds/advanced-table/th.ts index aa1dbe7eede..e919300c76b 100644 --- a/packages/components/src/components/hds/advanced-table/th.ts +++ b/packages/components/src/components/hds/advanced-table/th.ts @@ -59,7 +59,7 @@ export default class HdsAdvancedTableTh extends Component; tooltip?: string; - width?: string; - minWidth?: string; - maxWidth?: string; + width?: CssSize; + minWidth?: CssSize; + maxWidth?: CssSize; } interface SortableHdsAdvancedTableColumn extends BaseHdsAdvancedTableColumn { diff --git a/packages/components/src/modifiers/hds-on-click-outside.ts b/packages/components/src/modifiers/hds-on-click-outside.ts index f3954b97d76..e0818caab96 100644 --- a/packages/components/src/modifiers/hds-on-click-outside.ts +++ b/packages/components/src/modifiers/hds-on-click-outside.ts @@ -12,9 +12,9 @@ import type { ArgsFor, PositionalArgs } from 'ember-modifier'; export interface HdsOnClickOutsideSignature { Args: { - Positional: [() => unknown]; + Positional: [() => void]; }; - Element: HTMLDivElement; + Element: HTMLElement; } export default class HdsOnClickOutsideModifier extends Modifier { @@ -31,7 +31,7 @@ export default class HdsOnClickOutsideModifier extends Modifier ): void { const [callback] = positional; diff --git a/packages/components/src/styles/components/form/range-slider.scss b/packages/components/src/styles/components/form/range-slider.scss index c898a66b2c6..08c5a2fb5c3 100644 --- a/packages/components/src/styles/components/form/range-slider.scss +++ b/packages/components/src/styles/components/form/range-slider.scss @@ -1,7 +1,7 @@ input[type="range"].hds-form-range-slider { width: 100%; height: 4px; - margin-top: 16px; + margin: 16px 0; border-radius: 2px; cursor: pointer; // stylelint-disable-next-line property-no-vendor-prefix diff --git a/packages/components/src/template-registry.ts b/packages/components/src/template-registry.ts index 1f0d50a4720..57f99dddfdc 100644 --- a/packages/components/src/template-registry.ts +++ b/packages/components/src/template-registry.ts @@ -14,6 +14,7 @@ import type HdsAdvancedTableThButtonSortComponent from './components/hds/advance import type HdsAdvancedTableThComponent from './components/hds/advanced-table/th'; import type HdsAdvancedTableThMenuComponent from './components/hds/advanced-table/th-menu'; import type HdsAdvancedTableThButtonTooltipComponent from './components/hds/advanced-table/th-button-tooltip'; +import type HdsAdvancedTableThResizeForm from './components/hds/advanced-table/th-resize-form.ts'; import type HdsAdvancedTableThResizeHandle from './components/hds/advanced-table/th-resize-handle.ts'; import type HdsAdvancedTableThSortComponent from './components/hds/advanced-table/th-sort'; import type HdsAdvancedTableThSelectableComponent from './components/hds/advanced-table/th-selectable'; @@ -265,6 +266,8 @@ export default interface HdsComponentsRegistry { 'Hds::AdvancedTable::ThButtonTooltip': typeof HdsAdvancedTableThButtonTooltipComponent; 'Hds::AdvancedTable::ThResizeHandle': typeof HdsAdvancedTableThResizeHandle; 'hds/advanced-table/th-resize-handle': typeof HdsAdvancedTableThResizeHandle; + 'Hds::AdvancedTable::ThResizeForm': typeof HdsAdvancedTableThResizeForm; + 'hds/advanced-table/th-resize-form': typeof HdsAdvancedTableThResizeForm; 'hds/advanced-table/th-button-tooltip': typeof HdsAdvancedTableThButtonTooltipComponent; 'Hds::AdvancedTable::ThSort': typeof HdsAdvancedTableThSortComponent; 'hds/advanced-table/th-sort': typeof HdsAdvancedTableThSortComponent; From a92d46f3a3f5f756f510cff7a722375509c3f141 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Fri, 9 May 2025 14:01:17 -0400 Subject: [PATCH 11/89] calculating percentage based on table width --- .../hds/advanced-table/models/column.ts | 19 ++++++++++++++++--- .../hds/advanced-table/models/table.ts | 1 - .../hds/advanced-table/th-resize-handle.ts | 2 -- .../controllers/components/advanced-table.js | 2 +- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/models/column.ts b/packages/components/src/components/hds/advanced-table/models/column.ts index 3b0aa1e7e12..305d47c177c 100644 --- a/packages/components/src/components/hds/advanced-table/models/column.ts +++ b/packages/components/src/components/hds/advanced-table/models/column.ts @@ -48,6 +48,8 @@ export default class HdsAdvancedTableColumn { private _originalWidth?: CssSize = undefined; private _cssWidthUnit?: CssSizeUnit = CssSizeUnitValues.Px; + table: HdsAdvancedTableTableModel; + get isResizable(): boolean | undefined { if (!this._isResizable) { return false; @@ -76,12 +78,21 @@ export default class HdsAdvancedTableColumn { set numericalWidth(value: number) { if (this._cssWidthUnit === CssSizeUnitValues.Px) { this.width = `${value}${CssSizeUnitValues.Px}`; - console.log({ width: this.width }); + return; } + if (this._cssWidthUnit === CssSizeUnitValues.Percent) { - // TODO: actually calculate the width based on the table width - this.width = `${value}${CssSizeUnitValues.Percent}`; + const tableWidth = this.table.pixelWidth; + + if (tableWidth === 0) { + return; + } + + const percentage = (value / tableWidth) * 100; + + this.width = `${percentage}${CssSizeUnitValues.Percent}`; + return; } } @@ -97,6 +108,8 @@ export default class HdsAdvancedTableColumn { constructor( args: HdsAdvancedTableColumnType & { table: HdsAdvancedTableTableModel } ) { + this.table = args.table; + this.label = args.label; this.align = args.align; this.key = args.key; diff --git a/packages/components/src/components/hds/advanced-table/models/table.ts b/packages/components/src/components/hds/advanced-table/models/table.ts index d0fccafffae..4247d2555e6 100644 --- a/packages/components/src/components/hds/advanced-table/models/table.ts +++ b/packages/components/src/components/hds/advanced-table/models/table.ts @@ -40,7 +40,6 @@ export default class HdsAdvancedTableTableModel { @tracked columns: HdsAdvancedTableColumn[] = []; @tracked pixelWidth: number = 0; - @tracked pixelHeight: number = 0; rows: HdsAdvancedTableRow[] = []; diff --git a/packages/components/src/components/hds/advanced-table/th-resize-handle.ts b/packages/components/src/components/hds/advanced-table/th-resize-handle.ts index 09c59884782..1091ccf3c4b 100644 --- a/packages/components/src/components/hds/advanced-table/th-resize-handle.ts +++ b/packages/components/src/components/hds/advanced-table/th-resize-handle.ts @@ -37,8 +37,6 @@ export default class HdsAdvancedTableThResizeHandle extends Component Date: Fri, 9 May 2025 16:02:49 -0400 Subject: [PATCH 12/89] mixed unit math works --- .../components/hds/advanced-table/index.ts | 1 + .../hds/advanced-table/models/column.ts | 78 ++++++++++--------- .../hds/advanced-table/th-resize-form.hbs | 6 +- .../hds/advanced-table/th-resize-form.ts | 8 +- .../hds/advanced-table/th-resize-handle.hbs | 6 +- .../hds/advanced-table/th-resize-handle.ts | 4 +- .../controllers/components/advanced-table.js | 3 +- 7 files changed, 56 insertions(+), 50 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.ts b/packages/components/src/components/hds/advanced-table/index.ts index aef8253d3b5..66e68a49213 100644 --- a/packages/components/src/components/hds/advanced-table/index.ts +++ b/packages/components/src/components/hds/advanced-table/index.ts @@ -443,6 +443,7 @@ export default class HdsAdvancedTable extends Component { this.tableHeight = element.clientHeight; + this._tableModel.pixelWidth = element.clientWidth; this.scrollIndicatorDimensions = getScrollIndicatorDimensions( element, diff --git a/packages/components/src/components/hds/advanced-table/models/column.ts b/packages/components/src/components/hds/advanced-table/models/column.ts index 305d47c177c..37a3dfa1cfd 100644 --- a/packages/components/src/components/hds/advanced-table/models/column.ts +++ b/packages/components/src/components/hds/advanced-table/models/column.ts @@ -20,14 +20,6 @@ function getCssUnit(cssString?: string): CssSizeUnit | undefined { } } -function getNumericalWidth(width?: CssSize, defaultWidth?: number): number { - if (width === undefined) { - return defaultWidth ?? 0; - } - - // width is a css string - return parseInt(width, 10); -} export default class HdsAdvancedTableColumn { @tracked label: string = ''; @tracked _isResizable: boolean = false; @@ -47,6 +39,8 @@ export default class HdsAdvancedTableColumn { private _originalWidth?: CssSize = undefined; private _cssWidthUnit?: CssSizeUnit = CssSizeUnitValues.Px; + private _cssMinWidthUnit?: CssSizeUnit = CssSizeUnitValues.Px; + private _cssMaxWidthUnit?: CssSizeUnit = CssSizeUnitValues.Px; table: HdsAdvancedTableTableModel; @@ -60,22 +54,10 @@ export default class HdsAdvancedTableColumn { return this._isResizable; } - get pixelWidth(): number | undefined { - const numericalWidth = getNumericalWidth(this.width); - - if (this._cssWidthUnit === CssSizeUnitValues.Px) { - return numericalWidth; - } - - if (this._cssWidthUnit === CssSizeUnitValues.Percent) { - return 0; - } - } - - get numericalWidth(): number { - return getNumericalWidth(this.width); + get pixelWidth(): number { + return this._getPixelWidth(this.width) ?? 0; } - set numericalWidth(value: number) { + set pixelWidth(value: number) { if (this._cssWidthUnit === CssSizeUnitValues.Px) { this.width = `${value}${CssSizeUnitValues.Px}`; @@ -97,12 +79,12 @@ export default class HdsAdvancedTableColumn { } } - get numericalMinWidth(): number { - return getNumericalWidth(this.minWidth); + get pixelMinWidth(): number { + return this._getPixelWidth(this.minWidth) ?? 0; } - get numericalMaxWidth(): number { - return getNumericalWidth(this.maxWidth); + get pixelMaxWidth(): number { + return this._getPixelWidth(this.maxWidth) ?? 0; } constructor( @@ -120,31 +102,53 @@ export default class HdsAdvancedTableColumn { this.sortingFunction = args.sortingFunction; - this.setWidthValues(args); + this._setWidthValues(args); + } + + private _getPixelWidth(width?: CssSize): number | undefined { + if (width === undefined) { + return; + } + + const cssUnit = getCssUnit(width); + const numericalWidth = parseInt(width, 10); + + if (cssUnit === CssSizeUnitValues.Px) { + return numericalWidth; + } + + if (cssUnit === CssSizeUnitValues.Percent) { + const tableWidth = this.table.pixelWidth; + + return (numericalWidth / 100) * tableWidth; + } } - setWidthValues({ + private _setWidthValues({ width, minWidth, maxWidth, }: HdsAdvancedTableColumnType): void { + if (width === undefined) { + return; + } + this.width = width; - // set default minWidth and maxWidth if width is set - this.minWidth = (minWidth ?? this.width !== undefined) ? '50px' : undefined; - this.maxWidth = - (maxWidth ?? this.width !== undefined) ? '800px' : undefined; // capture the width at the time of instantiation so it can be restored this._originalWidth = width; + this.minWidth = minWidth ?? '50px'; + this.maxWidth = maxWidth ?? '800px'; + this._cssWidthUnit = getCssUnit(this.width); } @action - setNumericalWidth(newNumericalWidth: number): void { - this.numericalWidth = Math.min( - Math.max(newNumericalWidth, this.numericalMinWidth), - this.numericalMaxWidth + setPixelWidth(newPixelWidth: number): void { + this.pixelWidth = Math.min( + Math.max(newPixelWidth, this.pixelMinWidth), + this.pixelMaxWidth ); } diff --git a/packages/components/src/components/hds/advanced-table/th-resize-form.hbs b/packages/components/src/components/hds/advanced-table/th-resize-form.hbs index 0ce3ac85538..0f4efc8bd01 100644 --- a/packages/components/src/components/hds/advanced-table/th-resize-form.hbs +++ b/packages/components/src/components/hds/advanced-table/th-resize-form.hbs @@ -15,9 +15,9 @@ diff --git a/packages/components/src/components/hds/advanced-table/th-resize-form.ts b/packages/components/src/components/hds/advanced-table/th-resize-form.ts index 25d2d6a0603..26808b083e4 100644 --- a/packages/components/src/components/hds/advanced-table/th-resize-form.ts +++ b/packages/components/src/components/hds/advanced-table/th-resize-form.ts @@ -22,7 +22,7 @@ export interface HdsAdvancedTableThResizeFormSignature { } export default class HdsAdvancedTableThResizeForm extends Component { - originalColumnNumericalWidth: number; + originalColumnPixelWidth: number; constructor( owner: Owner, @@ -32,14 +32,14 @@ export default class HdsAdvancedTableThResizeForm extends Component Date: Fri, 9 May 2025 17:20:46 -0400 Subject: [PATCH 13/89] cleaned up the column model --- .../hds/advanced-table/models/column.ts | 53 +++++++++---------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/models/column.ts b/packages/components/src/components/hds/advanced-table/models/column.ts index 37a3dfa1cfd..0fca6c852da 100644 --- a/packages/components/src/components/hds/advanced-table/models/column.ts +++ b/packages/components/src/components/hds/advanced-table/models/column.ts @@ -22,38 +22,25 @@ function getCssUnit(cssString?: string): CssSizeUnit | undefined { export default class HdsAdvancedTableColumn { @tracked label: string = ''; - @tracked _isResizable: boolean = false; - @tracked align?: HdsAdvancedTableHorizontalAlignment = 'left'; - @tracked key?: string = undefined; @tracked isExpandable?: boolean = false; @tracked isReorderable?: boolean = false; + @tracked isResizable?: boolean = false; @tracked isSortable?: boolean = false; @tracked isVisuallyHidden?: boolean = false; - @tracked tooltip?: string = undefined; - @tracked width?: CssSize = undefined; + @tracked key?: string = undefined; @tracked minWidth?: CssSize = undefined; @tracked maxWidth?: CssSize = undefined; + @tracked tooltip?: string = undefined; + @tracked width?: CssSize = undefined; @tracked sortingFunction?: (a: unknown, b: unknown) => number = undefined; - private _originalWidth?: CssSize = undefined; private _cssWidthUnit?: CssSizeUnit = CssSizeUnitValues.Px; - private _cssMinWidthUnit?: CssSizeUnit = CssSizeUnitValues.Px; - private _cssMaxWidthUnit?: CssSizeUnit = CssSizeUnitValues.Px; - - table: HdsAdvancedTableTableModel; - - get isResizable(): boolean | undefined { - if (!this._isResizable) { - return false; - } - - assert('width must be set to use isResizable', this.width !== undefined); - - return this._isResizable; - } + private _originalWidth?: CssSize = undefined; + private _table: HdsAdvancedTableTableModel; + // setting the pixelWidth property will set the width property in the correct unit get pixelWidth(): number { return this._getPixelWidth(this.width) ?? 0; } @@ -65,7 +52,7 @@ export default class HdsAdvancedTableColumn { } if (this._cssWidthUnit === CssSizeUnitValues.Percent) { - const tableWidth = this.table.pixelWidth; + const tableWidth = this._table.pixelWidth; if (tableWidth === 0) { return; @@ -90,19 +77,19 @@ export default class HdsAdvancedTableColumn { constructor( args: HdsAdvancedTableColumnType & { table: HdsAdvancedTableTableModel } ) { - this.table = args.table; + // set reference to parent table model + this._table = args.table; + // set column properties this.label = args.label; this.align = args.align; - this.key = args.key; - this._isResizable = args.isResizable ?? false; this.isSortable = args.isSortable; this.isVisuallyHidden = args.isVisuallyHidden; + this.key = args.key; this.tooltip = args.tooltip; - - this.sortingFunction = args.sortingFunction; - + this._setResizableValues(args); this._setWidthValues(args); + this.sortingFunction = args.sortingFunction; } private _getPixelWidth(width?: CssSize): number | undefined { @@ -118,12 +105,22 @@ export default class HdsAdvancedTableColumn { } if (cssUnit === CssSizeUnitValues.Percent) { - const tableWidth = this.table.pixelWidth; + const tableWidth = this._table.pixelWidth; return (numericalWidth / 100) * tableWidth; } } + private _setResizableValues({ + isResizable, + }: HdsAdvancedTableColumnType): void { + this.isResizable = isResizable ?? false; + + if (this.isResizable) { + assert('width must be set to use isResizable', this.width !== undefined); + } + } + private _setWidthValues({ width, minWidth, From 404bb47d22cd46118e7c1cb071562a26b37359be Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Mon, 12 May 2025 16:33:01 -0400 Subject: [PATCH 14/89] correctly works with pixel vals --- .../components/hds/advanced-table/index.ts | 1 - .../hds/advanced-table/models/column.ts | 138 +++++++----------- .../hds/advanced-table/models/table.ts | 6 +- .../hds/advanced-table/th-resize-form.hbs | 6 +- .../hds/advanced-table/th-resize-form.ts | 14 +- .../hds/advanced-table/th-resize-handle.hbs | 6 +- .../hds/advanced-table/th-resize-handle.ts | 11 +- .../components/hds/advanced-table/types.ts | 6 +- .../controllers/components/advanced-table.js | 4 +- 9 files changed, 81 insertions(+), 111 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.ts b/packages/components/src/components/hds/advanced-table/index.ts index 66e68a49213..aef8253d3b5 100644 --- a/packages/components/src/components/hds/advanced-table/index.ts +++ b/packages/components/src/components/hds/advanced-table/index.ts @@ -443,7 +443,6 @@ export default class HdsAdvancedTable extends Component { this.tableHeight = element.clientHeight; - this._tableModel.pixelWidth = element.clientWidth; this.scrollIndicatorDimensions = getScrollIndicatorDimensions( element, diff --git a/packages/components/src/components/hds/advanced-table/models/column.ts b/packages/components/src/components/hds/advanced-table/models/column.ts index 0fca6c852da..f78a76da43a 100644 --- a/packages/components/src/components/hds/advanced-table/models/column.ts +++ b/packages/components/src/components/hds/advanced-table/models/column.ts @@ -1,23 +1,21 @@ import { tracked } from '@glimmer/tracking'; import { action } from '@ember/object'; import { assert } from '@ember/debug'; -import { CssSizeUnitValues } from '../types.ts'; import type { HdsAdvancedTableColumn as HdsAdvancedTableColumnType } from '../types'; import type { HdsAdvancedTableHorizontalAlignment } from '../types'; -import type { CssSize, CssSizeUnit } from '../types'; -import type HdsAdvancedTableTableModel from './table.ts'; +import type { PixelSize } from '../types'; -function getCssUnit(cssString?: string): CssSizeUnit | undefined { - if (cssString === undefined) { - return undefined; +function isPxSize(value?: string): boolean { + if (value === undefined) { + return false; } - const match = cssString.match(/([a-zA-Z%]+)/); + return /^-?\d+(\.\d+)?px$/.test(value); +} - if (match) { - return match[0] as CssSizeUnit; - } +function pxToNumber(pxString: string): number { + return parseFloat(pxString.slice(0, -2)); } export default class HdsAdvancedTableColumn { @@ -29,57 +27,37 @@ export default class HdsAdvancedTableColumn { @tracked isSortable?: boolean = false; @tracked isVisuallyHidden?: boolean = false; @tracked key?: string = undefined; - @tracked minWidth?: CssSize = undefined; - @tracked maxWidth?: CssSize = undefined; + @tracked minWidth?: PixelSize = undefined; + @tracked maxWidth?: PixelSize = undefined; @tracked tooltip?: string = undefined; - @tracked width?: CssSize = undefined; + @tracked width?: string = undefined; @tracked sortingFunction?: (a: unknown, b: unknown) => number = undefined; - private _cssWidthUnit?: CssSizeUnit = CssSizeUnitValues.Px; - private _originalWidth?: CssSize = undefined; - private _table: HdsAdvancedTableTableModel; - - // setting the pixelWidth property will set the width property in the correct unit - get pixelWidth(): number { - return this._getPixelWidth(this.width) ?? 0; - } - set pixelWidth(value: number) { - if (this._cssWidthUnit === CssSizeUnitValues.Px) { - this.width = `${value}${CssSizeUnitValues.Px}`; - - return; - } - - if (this._cssWidthUnit === CssSizeUnitValues.Percent) { - const tableWidth = this._table.pixelWidth; - - if (tableWidth === 0) { - return; - } + private _originalWidth?: string = undefined; - const percentage = (value / tableWidth) * 100; - - this.width = `${percentage}${CssSizeUnitValues.Percent}`; - - return; + get pxWidth(): number | undefined { + if (isPxSize(this.width)) { + return pxToNumber(this.width!); } } - - get pixelMinWidth(): number { - return this._getPixelWidth(this.minWidth) ?? 0; + set pxWidth(value: number) { + this.width = `${value}px`; } - get pixelMaxWidth(): number { - return this._getPixelWidth(this.maxWidth) ?? 0; + get pxMinWidth(): number | undefined { + if (isPxSize(this.minWidth)) { + return pxToNumber(this.minWidth!); + } } - constructor( - args: HdsAdvancedTableColumnType & { table: HdsAdvancedTableTableModel } - ) { - // set reference to parent table model - this._table = args.table; + get pxMaxWidth(): number | undefined { + if (isPxSize(this.maxWidth)) { + return pxToNumber(this.maxWidth!); + } + } + constructor(args: HdsAdvancedTableColumnType) { // set column properties this.label = args.label; this.align = args.align; @@ -87,40 +65,11 @@ export default class HdsAdvancedTableColumn { this.isVisuallyHidden = args.isVisuallyHidden; this.key = args.key; this.tooltip = args.tooltip; - this._setResizableValues(args); this._setWidthValues(args); + this._setResizableValues(args); this.sortingFunction = args.sortingFunction; } - private _getPixelWidth(width?: CssSize): number | undefined { - if (width === undefined) { - return; - } - - const cssUnit = getCssUnit(width); - const numericalWidth = parseInt(width, 10); - - if (cssUnit === CssSizeUnitValues.Px) { - return numericalWidth; - } - - if (cssUnit === CssSizeUnitValues.Percent) { - const tableWidth = this._table.pixelWidth; - - return (numericalWidth / 100) * tableWidth; - } - } - - private _setResizableValues({ - isResizable, - }: HdsAdvancedTableColumnType): void { - this.isResizable = isResizable ?? false; - - if (this.isResizable) { - assert('width must be set to use isResizable', this.width !== undefined); - } - } - private _setWidthValues({ width, minWidth, @@ -135,26 +84,37 @@ export default class HdsAdvancedTableColumn { // capture the width at the time of instantiation so it can be restored this._originalWidth = width; + // TODO: discuss sensible defaults for minWidth and maxWidth this.minWidth = minWidth ?? '50px'; this.maxWidth = maxWidth ?? '800px'; + } - this._cssWidthUnit = getCssUnit(this.width); + private _setResizableValues({ + isResizable, + }: HdsAdvancedTableColumnType): void { + if (isResizable) { + assert( + 'width must be set a px value to use isResizable', + isPxSize(this.width) + ); + } + + this.isResizable = isResizable ?? false; } @action - setPixelWidth(newPixelWidth: number): void { - this.pixelWidth = Math.min( - Math.max(newPixelWidth, this.pixelMinWidth), - this.pixelMaxWidth - ); + setPxWidth(newPxWidth: number): void { + const pxMinWidth = this.pxMinWidth ?? 1; + const minLimitedPxWidth = Math.max(newPxWidth, pxMinWidth); + + this.pxWidth = + this.pxMaxWidth !== undefined + ? Math.min(minLimitedPxWidth, this.pxMaxWidth) + : minLimitedPxWidth; } @action restoreWidth(): void { - if (this._originalWidth === undefined) { - return; - } - this.width = this._originalWidth; } } diff --git a/packages/components/src/components/hds/advanced-table/models/table.ts b/packages/components/src/components/hds/advanced-table/models/table.ts index 4247d2555e6..8364cd8de13 100644 --- a/packages/components/src/components/hds/advanced-table/models/table.ts +++ b/packages/components/src/components/hds/advanced-table/models/table.ts @@ -39,8 +39,6 @@ function getChildrenCount(rows: HdsAdvancedTableRow[]): number { export default class HdsAdvancedTableTableModel { @tracked columns: HdsAdvancedTableColumn[] = []; - @tracked pixelWidth: number = 0; - rows: HdsAdvancedTableRow[] = []; constructor(args: HdsAdvancedTableTableArgs) { @@ -81,9 +79,7 @@ export default class HdsAdvancedTableTableModel { private _setupColumns({ columns, }: Pick) { - this.columns = columns.map((column) => { - return new HdsAdvancedTableColumn({ ...column, table: this }); - }); + this.columns = columns.map((column) => new HdsAdvancedTableColumn(column)); } private _setupRows({ diff --git a/packages/components/src/components/hds/advanced-table/th-resize-form.hbs b/packages/components/src/components/hds/advanced-table/th-resize-form.hbs index 0f4efc8bd01..55acf696f63 100644 --- a/packages/components/src/components/hds/advanced-table/th-resize-form.hbs +++ b/packages/components/src/components/hds/advanced-table/th-resize-form.hbs @@ -15,9 +15,9 @@ diff --git a/packages/components/src/components/hds/advanced-table/th-resize-form.ts b/packages/components/src/components/hds/advanced-table/th-resize-form.ts index 26808b083e4..5ae99c07cc4 100644 --- a/packages/components/src/components/hds/advanced-table/th-resize-form.ts +++ b/packages/components/src/components/hds/advanced-table/th-resize-form.ts @@ -5,6 +5,7 @@ import Component from '@glimmer/component'; import { action } from '@ember/object'; +import { assert } from '@ember/debug'; import type HdsAdvancedTableColumn from './models/column'; import type Owner from '@ember/owner'; @@ -22,7 +23,7 @@ export interface HdsAdvancedTableThResizeFormSignature { } export default class HdsAdvancedTableThResizeForm extends Component { - originalColumnPixelWidth: number; + originalColumnPxWidth: number; constructor( owner: Owner, @@ -32,14 +33,19 @@ export default class HdsAdvancedTableThResizeForm extends Component; tooltip?: string; width?: CssSize; - minWidth?: CssSize; - maxWidth?: CssSize; + minWidth?: PixelSize; + maxWidth?: PixelSize; } interface SortableHdsAdvancedTableColumn extends BaseHdsAdvancedTableColumn { diff --git a/showcase/app/controllers/components/advanced-table.js b/showcase/app/controllers/components/advanced-table.js index 24f71293d60..61214ea4f87 100644 --- a/showcase/app/controllers/components/advanced-table.js +++ b/showcase/app/controllers/components/advanced-table.js @@ -364,8 +364,8 @@ export default class ComponentsTableController extends Controller { key: 'artist', label: 'Artist', tooltip: 'More information.', - width: '35%', - maxWidth: '50%', + width: '200px', + maxWidth: '400px', isResizable: true, }, { From c6621f2c3632a5ddccd6663075fa456b47641770 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Tue, 13 May 2025 13:41:53 -0400 Subject: [PATCH 15/89] cleaning up --- .../components/hds/advanced-table/index.hbs | 1 + .../hds/advanced-table/th-resize-handle.ts | 125 ++++++++++++++---- .../src/components/hds/advanced-table/th.hbs | 8 +- .../src/components/hds/advanced-table/th.ts | 1 + 4 files changed, 111 insertions(+), 24 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.hbs b/packages/components/src/components/hds/advanced-table/index.hbs index bd565540e4c..dad83256391 100644 --- a/packages/components/src/components/hds/advanced-table/index.hbs +++ b/packages/components/src/components/hds/advanced-table/index.hbs @@ -56,6 +56,7 @@ 0) { + const maxCanExpandCol = colMax - startColW; + const maxCanShrinkNext = startNextColW - nextMin; + + effectiveDelta = Math.min(deltaX, maxCanExpandCol, maxCanShrinkNext); + effectiveDelta = Math.max(0, effectiveDelta); + } + // shrinking col, expanding nextCol + else if (deltaX < 0) { + const absDeltaX = -deltaX; + const maxCanShrinkCol = startColW - colMin; + const maxCanExpandNext = nextMax - startNextColW; + + effectiveDelta = -Math.min(absDeltaX, maxCanShrinkCol, maxCanExpandNext); + effectiveDelta = Math.min(0, effectiveDelta); + } + + return effectiveDelta; +} + export interface HdsAdvancedTableThResizeHandleSignature { Args: { column: HdsAdvancedTableColumn; @@ -25,43 +60,87 @@ export interface HdsAdvancedTableThResizeHandleSignature { } export default class HdsAdvancedTableThResizeHandle extends Component { - @tracked resizing: { startX: number; startW: number } | null = null; + @tracked resizing: { + startX: number; + startColumnPxWidth: number; + startNextColumnPxWidth?: number; + } | null = null; + + private boundResize: (event: PointerEvent) => void; + private boundStopResize: () => void; + + constructor( + owner: unknown, + args: HdsAdvancedTableThResizeHandleSignature['Args'] + ) { + super(owner, args); + + this.boundResize = this._resize.bind(this); + this.boundStopResize = this._stopResize.bind(this); + } - private _resize(event: PointerEvent): void { - const { column } = this.args; + @action + startResize(event: PointerEvent): void { + event.preventDefault(); + event.stopPropagation(); - if (this.resizing === null || column === undefined) { - return; - } + const { column, nextColumn } = this.args; - const { startX, startW } = this.resizing; + assert( + 'HdsAdvancedTableThResizeHandle: column pxWidth must be a number to start resize.', + typeof column.pxWidth === 'number' + ); - const deltaX = event.clientX - startX; - const newW = startW + deltaX; + this.resizing = { + startX: event.clientX, + startColumnPxWidth: column.pxWidth, + startNextColumnPxWidth: nextColumn?.pxWidth, + }; - column.setPxWidth(newW); + window.addEventListener('pointermove', this.boundResize); + window.addEventListener('pointerup', this.boundStopResize); } - @action - startResize(event: PointerEvent): void { + private _resize(event: PointerEvent): void { + if (this.resizing === null) { + return; + } + event.preventDefault(); - const { column } = this.args; + const { column, nextColumn } = this.args; + const { startX, startColumnPxWidth, startNextColumnPxWidth } = + this.resizing; + const deltaX = event.clientX - startX; - assert( - 'HdsAdvancedTableThResizeForm: column width must be set', - column.pxWidth !== undefined - ); + const canResizeNeighbor = + nextColumn !== undefined && + nextColumn.isResizable && + startNextColumnPxWidth !== undefined; - this.resizing = { startX: event.clientX, startW: column.pxWidth }; + if (canResizeNeighbor) { + const effectiveDelta = calculateEffectiveDelta( + deltaX, + column, + startColumnPxWidth, + nextColumn, // Known to be valid and resizable here + startNextColumnPxWidth // Known to be a number here + ); - window.addEventListener('pointermove', this._resize.bind(this)); - window.addEventListener('pointerup', this._stopResize.bind(this)); + column.setPxWidth(startColumnPxWidth + effectiveDelta); + + const actualNewColumnWidth = column.pxWidth ?? startColumnPxWidth; + const actualAppliedDelta = actualNewColumnWidth - startColumnPxWidth; + + nextColumn.setPxWidth(startNextColumnPxWidth - actualAppliedDelta); + } else { + column.setPxWidth(startColumnPxWidth + deltaX); + } } - private _stopResize() { - window.removeEventListener('pointermove', this._resize.bind(this)); - window.removeEventListener('pointerup', this._stopResize.bind(this)); + private _stopResize(): void { + window.removeEventListener('pointermove', this.boundResize); + window.removeEventListener('pointerup', this.boundStopResize); this.resizing = null; } diff --git a/packages/components/src/components/hds/advanced-table/th.hbs b/packages/components/src/components/hds/advanced-table/th.hbs index 36a2e38ef0a..77a5285667b 100644 --- a/packages/components/src/components/hds/advanced-table/th.hbs +++ b/packages/components/src/components/hds/advanced-table/th.hbs @@ -57,9 +57,15 @@ {{/if}} {{/if}} + {{@column.width}} + {{#if @column.isResizable}} - + {{/if}} diff --git a/packages/components/src/components/hds/advanced-table/th.ts b/packages/components/src/components/hds/advanced-table/th.ts index e919300c76b..d15396100bc 100644 --- a/packages/components/src/components/hds/advanced-table/th.ts +++ b/packages/components/src/components/hds/advanced-table/th.ts @@ -30,6 +30,7 @@ export interface HdsAdvancedTableThSignature { Args: { tableHeight?: number; column?: HdsAdvancedTableColumn; + nextColumn?: HdsAdvancedTableColumn; align?: HdsAdvancedTableHorizontalAlignment; isVisuallyHidden?: boolean; scope?: HdsAdvancedTableScope; From 9c3f21f20ad98dab46c9cf1d3b79d6945d11b5ba Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Tue, 13 May 2025 14:46:12 -0400 Subject: [PATCH 16/89] simplified the th api --- .../components/hds/advanced-table/index.hbs | 10 +++---- .../src/components/hds/advanced-table/th.hbs | 10 +++---- .../src/components/hds/advanced-table/th.ts | 26 ++++++++----------- 3 files changed, 19 insertions(+), 27 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.hbs b/packages/components/src/components/hds/advanced-table/index.hbs index dad83256391..86cf54191c4 100644 --- a/packages/components/src/components/hds/advanced-table/index.hbs +++ b/packages/components/src/components/hds/advanced-table/index.hbs @@ -54,18 +54,14 @@ {{else}} {{column.label}} diff --git a/packages/components/src/components/hds/advanced-table/th.hbs b/packages/components/src/components/hds/advanced-table/th.hbs index 77a5285667b..09ca531a40d 100644 --- a/packages/components/src/components/hds/advanced-table/th.hbs +++ b/packages/components/src/components/hds/advanced-table/th.hbs @@ -24,12 +24,12 @@ ...attributes > - {{#if @isVisuallyHidden}} + {{#if @column.isVisuallyHidden}} {{yield}} {{else}} - {{#if @tooltip}} + {{#if @column.tooltip}}
- {{#if @isExpandable}} + {{#if @column.isExpandable}} {{/if}} {{yield}} - +
{{else}}
- {{#if @isExpandable}} + {{#if @column.isExpandable}} void; - isExpanded?: HdsAdvancedTableExpandState; depth?: number; - didInsertExpandButton?: (button: HTMLButtonElement) => void; - willDestroyExpandButton?: (button: HTMLButtonElement) => void; hasExpandAllButton?: boolean; + isExpanded?: HdsAdvancedTableExpandState; isStickyColumn?: boolean; isStickyColumnPinned?: boolean; + newLabel?: string; + nextColumn?: HdsAdvancedTableColumn; + parentId?: string; + rowspan?: number; + scope?: HdsAdvancedTableScope; + tableHeight?: number; + didInsertExpandButton?: (button: HTMLButtonElement) => void; + onClickToggle?: () => void; + willDestroyExpandButton?: (button: HTMLButtonElement) => void; }; Blocks: { default?: []; @@ -86,7 +82,7 @@ export default class HdsAdvancedTableTh extends Component Date: Tue, 13 May 2025 15:05:30 -0400 Subject: [PATCH 17/89] removing slider component --- packages/components/package.json | 2 - .../components/hds/advanced-table/index.hbs | 1 + .../hds/advanced-table/th-menu/index.hbs | 13 ---- .../hds/advanced-table/th-menu/index.ts | 42 ----------- .../hds/advanced-table/th-resize-form.hbs | 28 ------- .../hds/advanced-table/th-resize-form.ts | 73 ------------------- .../src/components/hds/advanced-table/th.hbs | 15 +--- .../src/components/hds/advanced-table/th.ts | 8 +- 8 files changed, 3 insertions(+), 179 deletions(-) delete mode 100644 packages/components/src/components/hds/advanced-table/th-menu/index.hbs delete mode 100644 packages/components/src/components/hds/advanced-table/th-menu/index.ts delete mode 100644 packages/components/src/components/hds/advanced-table/th-resize-form.hbs delete mode 100644 packages/components/src/components/hds/advanced-table/th-resize-form.ts diff --git a/packages/components/package.json b/packages/components/package.json index 0f796555696..1a56549cdec 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -146,8 +146,6 @@ "./components/hds/advanced-table/th-button-expand.js": "./dist/_app_/components/hds/advanced-table/th-button-expand.js", "./components/hds/advanced-table/th-button-sort.js": "./dist/_app_/components/hds/advanced-table/th-button-sort.js", "./components/hds/advanced-table/th-button-tooltip.js": "./dist/_app_/components/hds/advanced-table/th-button-tooltip.js", - "./components/hds/advanced-table/th-menu/index.js": "./dist/_app_/components/hds/advanced-table/th-menu/index.js", - "./components/hds/advanced-table/th-resize-form.js": "./dist/_app_/components/hds/advanced-table/th-resize-form.js", "./components/hds/advanced-table/th-resize-handle.js": "./dist/_app_/components/hds/advanced-table/th-resize-handle.js", "./components/hds/advanced-table/th-selectable.js": "./dist/_app_/components/hds/advanced-table/th-selectable.js", "./components/hds/advanced-table/th-sort.js": "./dist/_app_/components/hds/advanced-table/th-sort.js", diff --git a/packages/components/src/components/hds/advanced-table/index.hbs b/packages/components/src/components/hds/advanced-table/index.hbs index 86cf54191c4..29d5a4de01e 100644 --- a/packages/components/src/components/hds/advanced-table/index.hbs +++ b/packages/components/src/components/hds/advanced-table/index.hbs @@ -57,6 +57,7 @@ @column={{column}} @hasExpandAllButton={{this._tableModel.hasRowsWithChildren}} @isExpanded={{this._tableModel.expandState}} + @isLastColumn={{eq index (sub this._tableModel.columns.length 1)}} @isStickyColumn={{if (and (eq index 0) @hasStickyFirstColumn) true}} @isStickyColumnPinned={{this.isStickyColumnPinned}} @nextColumn={{get this._tableModel.columns (add index 1)}} diff --git a/packages/components/src/components/hds/advanced-table/th-menu/index.hbs b/packages/components/src/components/hds/advanced-table/th-menu/index.hbs deleted file mode 100644 index af984493523..00000000000 --- a/packages/components/src/components/hds/advanced-table/th-menu/index.hbs +++ /dev/null @@ -1,13 +0,0 @@ -
- - - {{#if @column.isResizable}} - - Resize column - - - Reset column width - - {{/if}} - -
\ No newline at end of file diff --git a/packages/components/src/components/hds/advanced-table/th-menu/index.ts b/packages/components/src/components/hds/advanced-table/th-menu/index.ts deleted file mode 100644 index 4b31a1eba2c..00000000000 --- a/packages/components/src/components/hds/advanced-table/th-menu/index.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: MPL-2.0 - */ - -import Component from '@glimmer/component'; -import { tracked } from '@glimmer/tracking'; -import HdsAdvancedTableColumn from '../models/column.ts'; -import { action } from '@ember/object'; - -export interface HdsAdvancedTableThMenuSignature { - Args: { - column: HdsAdvancedTableColumn; - onStartResize: () => void; - }; - Blocks: { - default?: []; - }; - Element: HTMLDivElement; -} - -export default class HdsAdvancedTableThMenu extends Component { - @tracked isResizing = false; - - @action - enableResizing(callback: (() => void) | undefined): void { - this.args.onStartResize(); - - callback?.(); - } - - @action - resetColumnWidth(callback: (() => void) | undefined): void { - const { column } = this.args ?? {}; - - if (column) { - column.restoreWidth(); - } - - callback?.(); - } -} diff --git a/packages/components/src/components/hds/advanced-table/th-resize-form.hbs b/packages/components/src/components/hds/advanced-table/th-resize-form.hbs deleted file mode 100644 index 55acf696f63..00000000000 --- a/packages/components/src/components/hds/advanced-table/th-resize-form.hbs +++ /dev/null @@ -1,28 +0,0 @@ -
- - {{@column.label}} - column width - - - - - - - - - \ No newline at end of file diff --git a/packages/components/src/components/hds/advanced-table/th-resize-form.ts b/packages/components/src/components/hds/advanced-table/th-resize-form.ts deleted file mode 100644 index 5ae99c07cc4..00000000000 --- a/packages/components/src/components/hds/advanced-table/th-resize-form.ts +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: MPL-2.0 - */ - -import Component from '@glimmer/component'; -import { action } from '@ember/object'; -import { assert } from '@ember/debug'; - -import type HdsAdvancedTableColumn from './models/column'; -import type Owner from '@ember/owner'; - -export interface HdsAdvancedTableThResizeFormSignature { - Args: { - column: HdsAdvancedTableColumn; - labelId: string; - onClose: () => void; - }; - Blocks: { - default?: []; - }; - Element: HTMLFormElement; -} - -export default class HdsAdvancedTableThResizeForm extends Component { - originalColumnPxWidth: number; - - constructor( - owner: Owner, - args: HdsAdvancedTableThResizeFormSignature['Args'] - ) { - super(owner, args); - - const { column } = this.args; - - assert( - 'HdsAdvancedTableThResizeForm: column width must be set', - column.pxWidth !== undefined - ); - - this.originalColumnPxWidth = column.pxWidth; - } - - @action - resizeColumn(width: number): void { - const { column } = this.args; - - column.setPxWidth(width); - } - - @action - handleKeyup(event: KeyboardEvent): void { - if (event.key === 'Escape') { - this.handleCancel(); - } - } - - @action - handleCancel(): void { - const { column } = this.args; - - column.setPxWidth(this.originalColumnPxWidth); - - this.args.onClose(); - } - - @action - handleSubmit(event: Event): void { - event.preventDefault(); - - this.args.onClose(); - } -} diff --git a/packages/components/src/components/hds/advanced-table/th.hbs b/packages/components/src/components/hds/advanced-table/th.hbs index 09ca531a40d..73266195fd3 100644 --- a/packages/components/src/components/hds/advanced-table/th.hbs +++ b/packages/components/src/components/hds/advanced-table/th.hbs @@ -57,10 +57,7 @@ {{/if}} {{/if}} - {{@column.width}} - - {{#if @column.isResizable}} - + {{#if (and @column.isResizable (not @isLastColumn))}} {{/if}} - - {{#if this.isResizeSliderVisible}} - {{#if @column.width}} - - {{/if}} - {{/if}}
\ No newline at end of file diff --git a/packages/components/src/components/hds/advanced-table/th.ts b/packages/components/src/components/hds/advanced-table/th.ts index 902694297b1..277c6dfdb80 100644 --- a/packages/components/src/components/hds/advanced-table/th.ts +++ b/packages/components/src/components/hds/advanced-table/th.ts @@ -33,6 +33,7 @@ export interface HdsAdvancedTableThSignature { depth?: number; hasExpandAllButton?: boolean; isExpanded?: HdsAdvancedTableExpandState; + isLastColumn?: boolean; isStickyColumn?: boolean; isStickyColumnPinned?: boolean; newLabel?: string; @@ -56,8 +57,6 @@ export default class HdsAdvancedTableTh extends Component Date: Tue, 13 May 2025 17:53:24 -0400 Subject: [PATCH 18/89] styling the resize handle --- packages/components/package.json | 1 + .../components/hds/advanced-table/index.hbs | 1 + .../hds/advanced-table/th-resize-handle.hbs | 2 +- .../hds/advanced-table/th-resize-handle.ts | 10 +++++ .../src/components/hds/advanced-table/th.hbs | 14 ++++--- .../src/styles/components/advanced-table.scss | 38 +++++++++++-------- packages/components/src/template-registry.ts | 6 --- .../unpublished-development-types/global.d.ts | 2 + pnpm-lock.yaml | 4 +- 9 files changed, 48 insertions(+), 30 deletions(-) diff --git a/packages/components/package.json b/packages/components/package.json index 1a56549cdec..abf133ee24f 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -97,6 +97,7 @@ "concurrently": "^9.1.2", "ember-basic-dropdown": "^8.6.1", "ember-source": "^6.4.0", + "ember-math-helpers": "^5.0.0", "ember-template-lint": "^7.0.2", "ember-template-lint-plugin-prettier": "^5.0.0", "eslint": "^9.23.0", diff --git a/packages/components/src/components/hds/advanced-table/index.hbs b/packages/components/src/components/hds/advanced-table/index.hbs index 29d5a4de01e..6001935eaf8 100644 --- a/packages/components/src/components/hds/advanced-table/index.hbs +++ b/packages/components/src/components/hds/advanced-table/index.hbs @@ -145,6 +145,7 @@ {{/each}}
+ {{#if this.showScrollIndicatorLeft}} \ No newline at end of file diff --git a/packages/components/src/styles/components/advanced-table.scss b/packages/components/src/styles/components/advanced-table.scss index 85755c587f6..addf2836d33 100644 --- a/packages/components/src/styles/components/advanced-table.scss +++ b/packages/components/src/styles/components/advanced-table.scss @@ -16,6 +16,7 @@ $hds-advanced-table-inner-border-radius: calc( #{$hds-advanced-table-border-radius} - #{$hds-advanced-table-border-width} ); $hds-advanced-table-border-color: var(--token-color-border-primary); +$hds-advanced-table-border-resize-handle-hover-color: #b1b1b5; $hds-advanced-table-header-height: 48px; $hds-advanced-table-cell-padding-medium: 14px 16px 13px 16px; // the 1px difference is to account for the bottom border $hds-advanced-table-cell-padding-short: 6px 16px 5px 16px; // the 1px difference is to account for the bottom border @@ -111,13 +112,14 @@ $hds-advanced-table-cell-padding-tall: 22px 16px 21px 16px; // the 1px differenc } .hds-advanced-table__th-resize-handle { - position: absolute; + @include hds-focus-ring-with-pseudo-element($position: absolute); + top: 0; right: 0; bottom: 0; z-index: 2; width: 16px; - transform: translateX(50%); + transform: translateX(8.5px); cursor: col-resize; &::after { @@ -127,24 +129,28 @@ $hds-advanced-table-cell-padding-tall: 22px 16px 21px 16px; // the 1px differenc bottom: 0; z-index: 2; width: 1px; - background-color: red; + background-color: $hds-advanced-table-border-color; transform: translateX(-7.5px); content: ""; } - } - .hds-advanced-table__th-resize-slider { - position: absolute; - right: 16px; - bottom: -8px; - left: 0; - z-index: 2; - width: 280px; - padding: 16px; - background-color: var(--token-color-surface-primary); - border: 1px solid var(--token-color-foreground-primary); - border-radius: var(--token-border-radius-medium); - transform: translateY(100%); + &:focus::after, + &.mock-focus, + &:hover::after, + &.hds-advanced-table__th-resize-handle--resizing::after { + width: 3px; + transform: translateX(-6.5px); + } + + &:hover::after { + background-color: $hds-advanced-table-border-resize-handle-hover-color; + } + + &:focus::after, + &.mock-focus, + &.hds-advanced-table__th-resize-handle--resizing::after { + background-color: var(--token-color-palette-blue-300); + } } // horizontal alignment diff --git a/packages/components/src/template-registry.ts b/packages/components/src/template-registry.ts index 57f99dddfdc..c78b33576ac 100644 --- a/packages/components/src/template-registry.ts +++ b/packages/components/src/template-registry.ts @@ -12,9 +12,7 @@ import type HdsAdvancedTableTdComponent from './components/hds/advanced-table/td import type HdsAdvancedTableThButtonExpandComponent from './components/hds/advanced-table/th-button-expand'; import type HdsAdvancedTableThButtonSortComponent from './components/hds/advanced-table/th-button-sort'; import type HdsAdvancedTableThComponent from './components/hds/advanced-table/th'; -import type HdsAdvancedTableThMenuComponent from './components/hds/advanced-table/th-menu'; import type HdsAdvancedTableThButtonTooltipComponent from './components/hds/advanced-table/th-button-tooltip'; -import type HdsAdvancedTableThResizeForm from './components/hds/advanced-table/th-resize-form.ts'; import type HdsAdvancedTableThResizeHandle from './components/hds/advanced-table/th-resize-handle.ts'; import type HdsAdvancedTableThSortComponent from './components/hds/advanced-table/th-sort'; import type HdsAdvancedTableThSelectableComponent from './components/hds/advanced-table/th-selectable'; @@ -255,8 +253,6 @@ export default interface HdsComponentsRegistry { 'hds/advanced-table/td': typeof HdsAdvancedTableTdComponent; 'Hds::AdvancedTable::Th': typeof HdsAdvancedTableThComponent; 'hds/advanced-table/th': typeof HdsAdvancedTableThComponent; - 'Hds::AdvancedTable::ThMenu': typeof HdsAdvancedTableThMenuComponent; - 'hds/advanced-table/th-menu': typeof HdsAdvancedTableThMenuComponent; 'Hds::AdvancedTable::Tr': typeof HdsAdvancedTableTrComponent; 'hds/advanced-table/tr': typeof HdsAdvancedTableTrComponent; 'Hds::AdvancedTable::ThButtonExpand': typeof HdsAdvancedTableThButtonExpandComponent; @@ -266,8 +262,6 @@ export default interface HdsComponentsRegistry { 'Hds::AdvancedTable::ThButtonTooltip': typeof HdsAdvancedTableThButtonTooltipComponent; 'Hds::AdvancedTable::ThResizeHandle': typeof HdsAdvancedTableThResizeHandle; 'hds/advanced-table/th-resize-handle': typeof HdsAdvancedTableThResizeHandle; - 'Hds::AdvancedTable::ThResizeForm': typeof HdsAdvancedTableThResizeForm; - 'hds/advanced-table/th-resize-form': typeof HdsAdvancedTableThResizeForm; 'hds/advanced-table/th-button-tooltip': typeof HdsAdvancedTableThButtonTooltipComponent; 'Hds::AdvancedTable::ThSort': typeof HdsAdvancedTableThSortComponent; 'hds/advanced-table/th-sort': typeof HdsAdvancedTableThSortComponent; diff --git a/packages/components/unpublished-development-types/global.d.ts b/packages/components/unpublished-development-types/global.d.ts index 44ba51c1e86..ca3873a1b15 100644 --- a/packages/components/unpublished-development-types/global.d.ts +++ b/packages/components/unpublished-development-types/global.d.ts @@ -5,6 +5,7 @@ import { Portal, PortalTarget } from 'ember-stargate'; import type HdsComponentsRegistry from '../src/template-registry'; import type EmberElementHelperRegistry from 'ember-element-helper/template-registry'; +import type EmberMathHelpersRegistry from 'ember-math-helpers/template-registry'; import type EmberStargateRegistry from 'ember-stargate/template-registry'; import type EmberStyleModifierRegistry from 'ember-style-modifier/template-registry'; import type EmberTruthRegistry from 'ember-truth-helpers/template-registry'; @@ -18,6 +19,7 @@ declare module '@glint/environment-ember-loose/registry' { export default interface Registry extends HdsComponentsRegistry, EmberElementHelperRegistry, + EmberMathHelpersRegistry, EmberStargateRegistry, EmberStyleModifierRegistry, EmberTruthRegistry, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ddc82db0810..f09302283bc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -256,6 +256,9 @@ importers: ember-basic-dropdown: specifier: ^8.6.1 version: 8.6.2(@babel/core@7.27.1)(@ember/string@3.1.1)(@ember/test-helpers@4.0.4(@babel/core@7.27.1)(@glint/template@1.5.2)(ember-source@6.4.0(@glimmer/component@1.1.2(@babel/core@7.27.1))(rsvp@4.8.5)))(@glimmer/component@1.1.2(@babel/core@7.27.1))(@glint/environment-ember-loose@1.5.2(patch_hash=d8f52aa753a983edc93b91e5564e4bb736c9bdeb66a6fa65d7d406ec20174b9c)(@glimmer/component@1.1.2(@babel/core@7.27.1))(@glint/template@1.5.2)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.2.2(@babel/core@7.27.1)))(@glint/template@1.5.2)(ember-source@6.4.0(@glimmer/component@1.1.2(@babel/core@7.27.1))(rsvp@4.8.5)) + ember-math-helpers: + specifier: ^5.0.0 + version: 5.0.0(@babel/core@7.27.1)(ember-source@6.4.0(@glimmer/component@1.1.2(@babel/core@7.27.1))(rsvp@4.8.5)) ember-source: specifier: ^6.4.0 version: 6.4.0(@glimmer/component@1.1.2(@babel/core@7.27.1))(rsvp@4.8.5) @@ -9798,7 +9801,6 @@ packages: engines: {node: '>=0.6.0', teleport: '>=0.2.0'} deprecated: |- You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other. - (For a CapTP with native promises, see @endo/eventual-send and @endo/captp) qs@6.13.0: From eb6388da2315356fc8f44a5f3c9e7b33f71ee09a Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Tue, 13 May 2025 18:05:00 -0400 Subject: [PATCH 19/89] resizing with both the keyboard and mouse works --- .../hds/advanced-table/th-resize-handle.hbs | 1 + .../hds/advanced-table/th-resize-handle.ts | 103 ++++++++++++++---- 2 files changed, 80 insertions(+), 24 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs b/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs index 3979f7cf476..03c188ef91d 100644 --- a/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs +++ b/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs @@ -9,6 +9,7 @@ tabindex="0" aria-label="Resize {{@column.label}} column" {{on "pointerdown" this.startResize}} + {{on "keydown" this.handleKeydown}} {{style height=this.height}} ...attributes /> \ No newline at end of file diff --git a/packages/components/src/components/hds/advanced-table/th-resize-handle.ts b/packages/components/src/components/hds/advanced-table/th-resize-handle.ts index eaed87bf6b4..89453f41934 100644 --- a/packages/components/src/components/hds/advanced-table/th-resize-handle.ts +++ b/packages/components/src/components/hds/advanced-table/th-resize-handle.ts @@ -11,6 +11,7 @@ import { assert } from '@ember/debug'; import type HdsAdvancedTableColumn from './models/column'; const TABLE_BORDER_WIDTH = 1; +const KEYBOARD_RESIZE_STEP = 10; function calculateEffectiveDelta( deltaX: number, @@ -79,6 +80,16 @@ export default class HdsAdvancedTableThResizeHandle extends Component Date: Tue, 13 May 2025 18:13:38 -0400 Subject: [PATCH 20/89] cleaning up PR --- .../src/modifiers/hds-on-click-outside.ts | 50 ------------------- packages/components/src/template-registry.ts | 4 -- .../components/form/base-elements.hbs | 8 --- 3 files changed, 62 deletions(-) delete mode 100644 packages/components/src/modifiers/hds-on-click-outside.ts diff --git a/packages/components/src/modifiers/hds-on-click-outside.ts b/packages/components/src/modifiers/hds-on-click-outside.ts deleted file mode 100644 index e0818caab96..00000000000 --- a/packages/components/src/modifiers/hds-on-click-outside.ts +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: MPL-2.0 - */ - -import Modifier from 'ember-modifier'; -import { registerDestructor } from '@ember/destroyable'; -import { next } from '@ember/runloop'; - -import type Owner from '@ember/owner'; -import type { ArgsFor, PositionalArgs } from 'ember-modifier'; - -export interface HdsOnClickOutsideSignature { - Args: { - Positional: [() => void]; - }; - Element: HTMLElement; -} - -export default class HdsOnClickOutsideModifier extends Modifier { - private _element?: HTMLElement = undefined; - - clickOutsideHandler!: (event: MouseEvent) => void; - - constructor(owner: Owner, args: ArgsFor) { - super(owner, args); - - registerDestructor(this, () => { - document.removeEventListener('click', this.clickOutsideHandler); - }); - } - - modify( - element: HTMLElement, - positional: PositionalArgs - ): void { - const [callback] = positional; - - this._element = element; - - this.clickOutsideHandler = (event: MouseEvent) => { - if (this._element && !this._element.contains(event.target as Node)) { - callback(); - } - }; - - // eslint-disable-next-line ember/no-runloop - next(() => document.addEventListener('click', this.clickOutsideHandler)); - } -} diff --git a/packages/components/src/template-registry.ts b/packages/components/src/template-registry.ts index c78b33576ac..4f143dfa1ad 100644 --- a/packages/components/src/template-registry.ts +++ b/packages/components/src/template-registry.ts @@ -231,7 +231,6 @@ import type HdsClipboardModifier from './modifiers/hds-clipboard.ts'; import type HdsRegisterEventModifier from './modifiers/hds-register-event.ts'; import type HdsTooltipModifier from './modifiers/hds-tooltip.ts'; import type HdsAdvancedTableCellModifier from './modifiers/hds-advanced-table-cell.ts'; -import type HdsOnClickOutsideModifier from './modifiers/hds-on-click-outside.ts'; export default interface HdsComponentsRegistry { // ----- COMPONENTS --------------------------------------------------- @@ -994,7 +993,4 @@ export default interface HdsComponentsRegistry { // hds-advanced-table-cell 'hds-advanced-table-cell': typeof HdsAdvancedTableCellModifier; - - // hds-on-click-outside - 'hds-on-click-outside': typeof HdsOnClickOutsideModifier; } diff --git a/showcase/app/templates/components/form/base-elements.hbs b/showcase/app/templates/components/form/base-elements.hbs index 43e13ae52c6..afa82ee797b 100644 --- a/showcase/app/templates/components/form/base-elements.hbs +++ b/showcase/app/templates/components/form/base-elements.hbs @@ -10,14 +10,6 @@
Label - - - - - - - Label - This is a simple label From 937837c7ca51a503cad59b141d27e6d44920527e Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Wed, 14 May 2025 14:23:37 -0400 Subject: [PATCH 21/89] cleaned up PR for review --- .../templates/components/advanced-table.hbs | 58 ++++++++++--------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/showcase/app/templates/components/advanced-table.hbs b/showcase/app/templates/components/advanced-table.hbs index c6ff0e49e56..b1b7ad85cd1 100644 --- a/showcase/app/templates/components/advanced-table.hbs +++ b/showcase/app/templates/components/advanced-table.hbs @@ -6,33 +6,9 @@ {{page-title "AdvancedTable Component"}} AdvancedTable -
- - <:body as |B|> - - {{B.data.artist}} - -
- - {{B.data.album}} -
-
- - - - - - - - - - -
- -
- - {{!-- +
+ + Resizable columns + + + <:body as |B|> + + {{B.data.artist}} + +
+ + {{B.data.album}} +
+
+ + + + + + + + + + +
+ +
+ + + Functional examples With dynamic focusable content in cells @@ -1967,6 +1971,6 @@ {{/each}} - --}} +
\ No newline at end of file From 06e56cda9f3389fdbeaf8d7dcdc9c07b90fae516 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Wed, 14 May 2025 14:25:07 -0400 Subject: [PATCH 22/89] fixing nested rows --- packages/components/package.json | 1 - .../components/hds/advanced-table/models/row.ts | 14 +------------- .../src/components/hds/advanced-table/types.ts | 5 ----- .../src/styles/components/advanced-table.scss | 8 ++++---- 4 files changed, 5 insertions(+), 23 deletions(-) diff --git a/packages/components/package.json b/packages/components/package.json index abf133ee24f..799a059e68b 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -367,7 +367,6 @@ "./modifiers/hds-code-editor/palettes/hds-dark-palette.js": "./dist/_app_/modifiers/hds-code-editor/palettes/hds-dark-palette.js", "./modifiers/hds-code-editor/themes/hds-dark-theme.js": "./dist/_app_/modifiers/hds-code-editor/themes/hds-dark-theme.js", "./modifiers/hds-code-editor/types.js": "./dist/_app_/modifiers/hds-code-editor/types.js", - "./modifiers/hds-on-click-outside.js": "./dist/_app_/modifiers/hds-on-click-outside.js", "./modifiers/hds-register-event.js": "./dist/_app_/modifiers/hds-register-event.js", "./modifiers/hds-tooltip.js": "./dist/_app_/modifiers/hds-tooltip.js", "./services/hds-time.js": "./dist/_app_/services/hds-time.js" diff --git a/packages/components/src/components/hds/advanced-table/models/row.ts b/packages/components/src/components/hds/advanced-table/models/row.ts index 4ecd8f27c41..8fd16051d6b 100644 --- a/packages/components/src/components/hds/advanced-table/models/row.ts +++ b/packages/components/src/components/hds/advanced-table/models/row.ts @@ -7,7 +7,7 @@ import { tracked } from '@glimmer/tracking'; import { action } from '@ember/object'; import { guidFor } from '@ember/object/internals'; -import type { HdsAdvancedTableColumn, HdsAdvancedTableCell } from '../types'; +import type { HdsAdvancedTableColumn } from '../types'; interface HdsAdvancedTableRowArgs { [key: string]: unknown; @@ -23,7 +23,6 @@ export default class HdsAdvancedTableRow { [key: string]: unknown; @tracked isOpen: boolean = false; - @tracked cells: HdsAdvancedTableCell[] = []; children: HdsAdvancedTableRow[] = []; childrenKey: string; @@ -37,17 +36,6 @@ export default class HdsAdvancedTableRow { } constructor(args: HdsAdvancedTableRowArgs) { - const { columns } = args; - - this.cells = columns.map((column) => { - const cell = args[column.key ?? '']; - - return { - columnKey: column.key, - value: cell, - }; - }); - // set row data Object.assign(this, args); diff --git a/packages/components/src/components/hds/advanced-table/types.ts b/packages/components/src/components/hds/advanced-table/types.ts index 1dd7aa112fa..323f92333b5 100644 --- a/packages/components/src/components/hds/advanced-table/types.ts +++ b/packages/components/src/components/hds/advanced-table/types.ts @@ -124,8 +124,3 @@ export interface HdsAdvancedTableOnSelectionChangeSignature { } export type HdsAdvancedTableModel = Array>; - -export interface HdsAdvancedTableCell { - value: unknown; - columnKey?: string; -} diff --git a/packages/components/src/styles/components/advanced-table.scss b/packages/components/src/styles/components/advanced-table.scss index addf2836d33..9f11a618b00 100644 --- a/packages/components/src/styles/components/advanced-table.scss +++ b/packages/components/src/styles/components/advanced-table.scss @@ -118,8 +118,8 @@ $hds-advanced-table-cell-padding-tall: 22px 16px 21px 16px; // the 1px differenc right: 0; bottom: 0; z-index: 2; - width: 16px; - transform: translateX(8.5px); + width: 24px; + transform: translateX(12.5px); cursor: col-resize; &::after { @@ -130,7 +130,7 @@ $hds-advanced-table-cell-padding-tall: 22px 16px 21px 16px; // the 1px differenc z-index: 2; width: 1px; background-color: $hds-advanced-table-border-color; - transform: translateX(-7.5px); + transform: translateX(-11.5px); content: ""; } @@ -139,7 +139,7 @@ $hds-advanced-table-cell-padding-tall: 22px 16px 21px 16px; // the 1px differenc &:hover::after, &.hds-advanced-table__th-resize-handle--resizing::after { width: 3px; - transform: translateX(-6.5px); + transform: translateX(-10.5px); } &:hover::after { From 49a729ef12e280e5224013cc83b8a766aecc5fa9 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Wed, 14 May 2025 14:39:07 -0400 Subject: [PATCH 23/89] removed range slider styles --- .../src/styles/components/form/index.scss | 1 - .../styles/components/form/range-slider.scss | 42 ------------------- 2 files changed, 43 deletions(-) delete mode 100644 packages/components/src/styles/components/form/range-slider.scss diff --git a/packages/components/src/styles/components/form/index.scss b/packages/components/src/styles/components/form/index.scss index e6dce3d521b..ddbafe1d28d 100644 --- a/packages/components/src/styles/components/form/index.scss +++ b/packages/components/src/styles/components/form/index.scss @@ -20,7 +20,6 @@ @use "./masked-input"; @use "./radio"; @use "./radio-card"; -@use "./range-slider"; @use "./select"; @use "./super-select"; @use "./text-input"; diff --git a/packages/components/src/styles/components/form/range-slider.scss b/packages/components/src/styles/components/form/range-slider.scss deleted file mode 100644 index 08c5a2fb5c3..00000000000 --- a/packages/components/src/styles/components/form/range-slider.scss +++ /dev/null @@ -1,42 +0,0 @@ -input[type="range"].hds-form-range-slider { - width: 100%; - height: 4px; - margin: 16px 0; - border-radius: 2px; - cursor: pointer; - // stylelint-disable-next-line property-no-vendor-prefix - -webkit-appearance: none; - appearance: none; -} - -input[type="range"].hds-form-range-slider::-webkit-slider-runnable-track { - height: 4px; - background: transparent; - border-radius: 2px; -} - -input[type="range"].hds-form-range-slider::-moz-range-track { - height: 4px; - background: transparent; - border-radius: 2px; -} - -input[type="range"].hds-form-range-slider::-webkit-slider-thumb { - width: 16px; - height: 16px; - margin-top: -6px; - background-color: var(--token-color-surface-interactive); - border: 1px solid var(--token-color-palette-neutral-400); - border-radius: 8px; - // stylelint-disable-next-line property-no-vendor-prefix - -webkit-appearance: none; - appearance: none; -} - -input[type="range"].hds-form-range-slider::-moz-range-thumb { - width: 1rem; - height: 2rem; - background-color: var(--token-color-surface-interactive); - border: 1px solid var(--token-color-palette-neutral-400); - border-radius: 8px; -} From 540f7097f9a77a7564fd28082efaec8c7466ff9e Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Wed, 14 May 2025 14:50:17 -0400 Subject: [PATCH 24/89] pr cleanup --- .../components/hds/advanced-table/types.ts | 16 +----- .../components/hds/form/range-slider/base.hbs | 11 ---- .../components/hds/form/range-slider/base.ts | 56 ------------------- 3 files changed, 3 insertions(+), 80 deletions(-) delete mode 100644 packages/components/src/components/hds/form/range-slider/base.hbs delete mode 100644 packages/components/src/components/hds/form/range-slider/base.ts diff --git a/packages/components/src/components/hds/advanced-table/types.ts b/packages/components/src/components/hds/advanced-table/types.ts index 323f92333b5..b4c820c737f 100644 --- a/packages/components/src/components/hds/advanced-table/types.ts +++ b/packages/components/src/components/hds/advanced-table/types.ts @@ -5,16 +5,6 @@ import type { HdsFormCheckboxBaseSignature } from '../form/checkbox/base.ts'; -export enum CssSizeUnitValues { - Px = 'px', - Percent = '%', -} - -export type CssSizeUnit = `${CssSizeUnitValues}`; -export type CssSize = `${number}${CssSizeUnit}`; - -export type PixelSize = `${number}${CssSizeUnitValues.Px}`; - export enum HdsAdvancedTableDensityValues { Default = 'default', Medium = 'medium', @@ -91,9 +81,9 @@ interface BaseHdsAdvancedTableColumn { label: string; sortingFunction?: HdsAdvancedTableSortingFunction; tooltip?: string; - width?: CssSize; - minWidth?: PixelSize; - maxWidth?: PixelSize; + width?: string; + minWidth?: `${number}px`; + maxWidth?: `${number}px`; } interface SortableHdsAdvancedTableColumn extends BaseHdsAdvancedTableColumn { diff --git a/packages/components/src/components/hds/form/range-slider/base.hbs b/packages/components/src/components/hds/form/range-slider/base.hbs deleted file mode 100644 index 6e8cf87fda4..00000000000 --- a/packages/components/src/components/hds/form/range-slider/base.hbs +++ /dev/null @@ -1,11 +0,0 @@ - \ No newline at end of file diff --git a/packages/components/src/components/hds/form/range-slider/base.ts b/packages/components/src/components/hds/form/range-slider/base.ts deleted file mode 100644 index ad6a8e852ea..00000000000 --- a/packages/components/src/components/hds/form/range-slider/base.ts +++ /dev/null @@ -1,56 +0,0 @@ -import Component from '@glimmer/component'; -import { action } from '@ember/object'; - -interface AccessibleRangeSliderArgs { - min?: number; - max?: number; - step?: number; - value?: number; - onChange?: (value: number) => void; -} - -interface AccessibleRangeSliderSignature { - Args: AccessibleRangeSliderArgs; - Element: HTMLInputElement; -} - -export default class AccessibleRangeSlider extends Component { - get min(): number { - return this.args.min ?? 0; - } - get max(): number { - return this.args.max ?? 100; - } - get step(): number { - return this.args.step ?? 1; - } - get value(): number { - return this.args.value ?? this.min; - } - - get classNames(): string { - return 'hds-form-range-slider'; - } - get inlineStyles(): Record { - const percent = ((this.value - this.min) / (this.max - this.min)) * 100; - - return { - '--progress': `${percent}%`, - background: `linear-gradient( - to right, - var(--token-color-palette-blue-200) 0%, - var(--token-color-palette-blue-200) ${percent}%, - var(--token-color-palette-neutral-200) ${percent}%, - var(--token-color-palette-neutral-200) 100% - )`, - }; - } - - @action - handleInput(event: Event): void { - const target = event.target as HTMLInputElement; - const value = Number(target.value); - - this.args.onChange?.(value); - } -} From 227747fc78214edd5e42ac742ab65c335e0bc1e0 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Wed, 14 May 2025 15:01:30 -0400 Subject: [PATCH 25/89] fixing build issues --- packages/components/package.json | 1 - .../src/components/hds/advanced-table/models/column.ts | 5 ++--- packages/components/src/template-registry.ts | 5 ----- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/components/package.json b/packages/components/package.json index 799a059e68b..b74d5e94702 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -254,7 +254,6 @@ "./components/hds/form/radio/base.js": "./dist/_app_/components/hds/form/radio/base.js", "./components/hds/form/radio/field.js": "./dist/_app_/components/hds/form/radio/field.js", "./components/hds/form/radio/group.js": "./dist/_app_/components/hds/form/radio/group.js", - "./components/hds/form/range-slider/base.js": "./dist/_app_/components/hds/form/range-slider/base.js", "./components/hds/form/select/base.js": "./dist/_app_/components/hds/form/select/base.js", "./components/hds/form/select/field.js": "./dist/_app_/components/hds/form/select/field.js", "./components/hds/form/super-select/after-options.js": "./dist/_app_/components/hds/form/super-select/after-options.js", diff --git a/packages/components/src/components/hds/advanced-table/models/column.ts b/packages/components/src/components/hds/advanced-table/models/column.ts index f78a76da43a..31f971bb65a 100644 --- a/packages/components/src/components/hds/advanced-table/models/column.ts +++ b/packages/components/src/components/hds/advanced-table/models/column.ts @@ -4,7 +4,6 @@ import { assert } from '@ember/debug'; import type { HdsAdvancedTableColumn as HdsAdvancedTableColumnType } from '../types'; import type { HdsAdvancedTableHorizontalAlignment } from '../types'; -import type { PixelSize } from '../types'; function isPxSize(value?: string): boolean { if (value === undefined) { @@ -27,8 +26,8 @@ export default class HdsAdvancedTableColumn { @tracked isSortable?: boolean = false; @tracked isVisuallyHidden?: boolean = false; @tracked key?: string = undefined; - @tracked minWidth?: PixelSize = undefined; - @tracked maxWidth?: PixelSize = undefined; + @tracked minWidth?: `${number}px` = undefined; + @tracked maxWidth?: `${number}px` = undefined; @tracked tooltip?: string = undefined; @tracked width?: string = undefined; diff --git a/packages/components/src/template-registry.ts b/packages/components/src/template-registry.ts index 4f143dfa1ad..0a8b54aa479 100644 --- a/packages/components/src/template-registry.ts +++ b/packages/components/src/template-registry.ts @@ -122,7 +122,6 @@ import type HdsFormRadioCardComponent from './components/hds/form/radio-card'; import type HdsFormRadioCardDescriptionComponent from './components/hds/form/radio-card/description'; import type HdsFormRadioCardGroupComponent from './components/hds/form/radio-card/group'; import type HdsFormRadioCardLabelComponent from './components/hds/form/radio-card/label'; -import type HdsFormRangeSliderBaseComponent from './components/hds/form/range-slider/base'; import type HdsFormSelectBaseComponent from './components/hds/form/select/base'; import type HdsFormSelectFieldComponent from './components/hds/form/select/field'; import type HdsFormSuperSelectAfterOptionsComponent from './components/hds/form/super-select/after-options'; @@ -638,10 +637,6 @@ export default interface HdsComponentsRegistry { 'Hds::Form::RadioCard::Label': typeof HdsFormRadioCardLabelComponent; 'hds/form/radio-card/label': typeof HdsFormRadioCardLabelComponent; - // Form RangeSlider - 'Hds::Form::RangeSlider::Base': typeof HdsFormRangeSliderBaseComponent; - 'hds/form/range-slider/base': typeof HdsFormRangeSliderBaseComponent; - // Form Select 'Hds::Form::Select::Base': typeof HdsFormSelectBaseComponent; 'hds/form/select/base': typeof HdsFormSelectBaseComponent; From 03663778f67f29ce05a24fe977a8164b0d410ebd Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Thu, 22 May 2025 18:17:02 -0400 Subject: [PATCH 26/89] responding to PR feedback --- .../components/src/components/hds/advanced-table/index.hbs | 2 +- .../components/src/components/hds/advanced-table/index.ts | 4 ++-- packages/components/src/components/hds/advanced-table/td.ts | 1 - .../src/components/hds/advanced-table/th-resize-handle.hbs | 2 +- .../components/src/styles/components/advanced-table.scss | 6 +++--- 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.hbs b/packages/components/src/components/hds/advanced-table/index.hbs index 6001935eaf8..7538ba81c29 100644 --- a/packages/components/src/components/hds/advanced-table/index.hbs +++ b/packages/components/src/components/hds/advanced-table/index.hbs @@ -61,7 +61,7 @@ @isStickyColumn={{if (and (eq index 0) @hasStickyFirstColumn) true}} @isStickyColumnPinned={{this.isStickyColumnPinned}} @nextColumn={{get this._tableModel.columns (add index 1)}} - @tableHeight={{this.tableHeight}} + @tableHeight={{this._tableHeight}} @onClickToggle={{this._tableModel.toggleAll}} > {{column.label}} diff --git a/packages/components/src/components/hds/advanced-table/index.ts b/packages/components/src/components/hds/advanced-table/index.ts index aef8253d3b5..4b71fbd232f 100644 --- a/packages/components/src/components/hds/advanced-table/index.ts +++ b/packages/components/src/components/hds/advanced-table/index.ts @@ -158,7 +158,7 @@ export default class HdsAdvancedTable extends Component { - this.tableHeight = element.clientHeight; + this._tableHeight = element.clientHeight; this.scrollIndicatorDimensions = getScrollIndicatorDimensions( element, diff --git a/packages/components/src/components/hds/advanced-table/td.ts b/packages/components/src/components/hds/advanced-table/td.ts index 502e13b90dd..c985dbeb5ad 100644 --- a/packages/components/src/components/hds/advanced-table/td.ts +++ b/packages/components/src/components/hds/advanced-table/td.ts @@ -22,7 +22,6 @@ export interface HdsAdvancedTableTdSignature { Args: { align?: HdsAdvancedTableHorizontalAlignment; rowspan?: number; - columnKey?: string; colspan?: number; }; Blocks: { diff --git a/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs b/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs index 03c188ef91d..3221c4ca906 100644 --- a/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs +++ b/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs @@ -1,7 +1,7 @@ \ No newline at end of file diff --git a/packages/components/src/components/hds/advanced-table/th-sort.ts b/packages/components/src/components/hds/advanced-table/th-sort.ts index 7ad09f9cbdc..6e8f16ff6f1 100644 --- a/packages/components/src/components/hds/advanced-table/th-sort.ts +++ b/packages/components/src/components/hds/advanced-table/th-sort.ts @@ -9,6 +9,7 @@ import { assert } from '@ember/debug'; import { action } from '@ember/object'; import { tracked } from '@glimmer/tracking'; import { focusable, type FocusableElement } from 'tabbable'; +import HdsAdvancedTableColumn from './models/column.ts'; import type Owner from '@ember/owner'; import { @@ -39,6 +40,9 @@ export interface HdsAdvancedTableThSortSignature { tooltip?: string; rowspan?: number; colspan?: number; + nextColumn?: HdsAdvancedTableColumn; + tableHeight?: number; + isLastColumn?: boolean; isStickyColumn?: boolean; isStickyColumnPinned?: boolean; }; @@ -90,6 +94,16 @@ export default class HdsAdvancedTableThSort extends Component {{/if}} - {{yield}} + {{yield}} {{else}} diff --git a/packages/components/src/components/hds/advanced-table/th.ts b/packages/components/src/components/hds/advanced-table/th.ts index bd10d1c3aeb..c474ebf0973 100644 --- a/packages/components/src/components/hds/advanced-table/th.ts +++ b/packages/components/src/components/hds/advanced-table/th.ts @@ -134,6 +134,10 @@ export default class HdsAdvancedTableTh extends Component value.trim()); + + return gridValues; +} + // we're using this for multiple tests so we'll declare context once and use it when we need it. const setSortableTableData = (context) => { context.set('model', [ @@ -151,10 +163,6 @@ const setResizableColumnsTableData = (context) => { { key: 'col2', label: 'Col 2', - isResizable: true, - width: '120px', - minWidth: '60px', - maxWidth: '300px', }, ]); }; @@ -1341,48 +1349,55 @@ module('Integration | Component | hds/advanced-table/index', function (hooks) { setResizableColumnsTableData(this); await render(hbsResizableColumnsAdvancedTable); + const table = find('.hds-advanced-table'); + const originalGridValues = getTableGridValues(table); + assert .dom('.hds-advanced-table__th-resize-handle') .exists({ count: 1 }, 'There is one resize handle (not on last column)'); - const handle = find('.hds-advanced-table__th-resize-handle'); - const th = handle.closest('.hds-advanced-table__th'); - const initialWidth = th.style.width || getComputedStyle(th).width; + const handle = find('.hds-advanced-table__th-resize-handle'); // get the first handle // Simulate pointer drag to the right (increase width) await triggerEvent(handle, 'pointerdown', { clientX: 100 }); await triggerEvent(handle, 'pointermove', { clientX: 130 }); await triggerEvent(window, 'pointerup'); - const newWidth = th.style.width || getComputedStyle(th).width; - assert.notEqual(newWidth, initialWidth, 'Column width changed after drag'); + const newGridValues = getTableGridValues(table); + assert.notEqual( + newGridValues, + originalGridValues, + 'Grid values changed after drag', + ); }); test('it should allow resizing columns with the resize handle (keyboard events)', async function (assert) { setResizableColumnsTableData(this); await render(hbsResizableColumnsAdvancedTable); + const table = find('.hds-advanced-table'); + const originalGridValues = getTableGridValues(table); + const handle = find('.hds-advanced-table__th-resize-handle'); - const th = handle.closest('.hds-advanced-table__th'); - const initialWidth = th.style.width || getComputedStyle(th).width; // Focus and send ArrowRight key await focus(handle); await triggerKeyEvent(handle, 'keydown', 'ArrowRight'); - const widthAfterRight = th.style.width || getComputedStyle(th).width; - assert.notEqual( - widthAfterRight, - initialWidth, - 'Column width increased after ArrowRight', + + let newGridValues = getTableGridValues(table); + assert.notDeepEqual( + newGridValues, + originalGridValues, + 'Grid values changed after ArrowRight', ); // Send ArrowLeft key await triggerKeyEvent(handle, 'keydown', 'ArrowLeft'); - const widthAfterLeft = th.style.width || getComputedStyle(th).width; - assert.notEqual( - widthAfterLeft, - widthAfterRight, - 'Column width decreased after ArrowLeft', + newGridValues = getTableGridValues(table); + assert.deepEqual( + newGridValues, + originalGridValues, + 'Grid values reverted after ArrowLeft', ); }); @@ -1390,20 +1405,28 @@ module('Integration | Component | hds/advanced-table/index', function (hooks) { setResizableColumnsTableData(this); await render(hbsResizableColumnsAdvancedTable); + const table = find('.hds-advanced-table'); + const originalGridValues = getTableGridValues(table); + const handle = find('.hds-advanced-table__th-resize-handle'); - const th = handle.closest('.hds-advanced-table__th'); // Try to resize column to a very small width (well below minWidth of 60px) await triggerEvent(handle, 'pointerdown', { clientX: 100 }); await triggerEvent(window, 'pointermove', { clientX: 1 }); await triggerEvent(window, 'pointerup'); - const newWidthString = th.style.width || getComputedStyle(th).width; - const newWidth = parseFloat(newWidthString) + 1; // adding a pixel to account for border + const newGridValues = getTableGridValues(table); + assert.notEqual( + newGridValues, + originalGridValues, + 'Grid values changed after pointer drag', + ); + + const firstColumnGridValue = newGridValues[0]; assert.ok( - newWidth >= 60, - `Column width respects minimum width constraint (actual: ${newWidth}px, min: 60px)`, + parseInt(firstColumnGridValue, 10) >= 60, + `Column width respects minimum width constraint (actual: ${firstColumnGridValue}, min: 60px)`, ); }); @@ -1411,8 +1434,10 @@ module('Integration | Component | hds/advanced-table/index', function (hooks) { setResizableColumnsTableData(this); await render(hbsResizableColumnsAdvancedTable); + const table = find('.hds-advanced-table'); + const originalGridValues = getTableGridValues(table); + const handle = find('.hds-advanced-table__th-resize-handle'); - const th = handle.closest('.hds-advanced-table__th'); // Try to resize column to a very large width (well below minWidth of 60px) await triggerEvent(handle, 'pointerdown', { clientX: 100 }); @@ -1420,12 +1445,18 @@ module('Integration | Component | hds/advanced-table/index', function (hooks) { await triggerEvent(window, 'pointerup'); // Check the new width - const newWidthString = th.style.width || getComputedStyle(th).width; - const newWidth = parseFloat(newWidthString) + 1; // adding a pixel to account for border + const newGridValues = getTableGridValues(table); + assert.notEqual( + newGridValues, + originalGridValues, + 'Grid values changed after pointer drag', + ); + + const firstColumnGridValue = newGridValues[0]; assert.ok( - newWidth <= 300, - `Column width respects maximum width constraint (actual: ${newWidth}px, max: 300px)`, + parseInt(firstColumnGridValue, 10) <= 300, + `Column width respects maximum width constraint (actual: ${firstColumnGridValue}px, max: 300px)`, ); }); @@ -1433,8 +1464,10 @@ module('Integration | Component | hds/advanced-table/index', function (hooks) { setResizableColumnsTableData(this); await render(hbsResizableColumnsAdvancedTable); + const table = find('.hds-advanced-table'); + const originalGridValues = getTableGridValues(table); + const handle = find('.hds-advanced-table__th-resize-handle'); - const th = handle.closest('.hds-advanced-table__th'); // Focus handle and press ArrowLeft multiple times to try going below min width await focus(handle); @@ -1444,12 +1477,18 @@ module('Integration | Component | hds/advanced-table/index', function (hooks) { await triggerKeyEvent(handle, 'keydown', 'ArrowLeft'); } - const newWidthString = th.style.width || getComputedStyle(th).width; - const newWidth = parseFloat(newWidthString) + 1; // adding a pixel to account for border + const newGridValues = getTableGridValues(table); + assert.notEqual( + newGridValues, + originalGridValues, + 'Grid values changed after ArrowLeft', + ); + + const firstColumnGridValue = newGridValues[0]; assert.ok( - newWidth >= 60, - `Column width respects minimum width constraint with keyboard events (actual: ${newWidth}px, min: 60px)`, + parseInt(firstColumnGridValue, 10) >= 60, + `Column width respects minimum width constraint with keyboard events (actual: ${firstColumnGridValue}, min: 60px)`, ); }); @@ -1457,8 +1496,10 @@ module('Integration | Component | hds/advanced-table/index', function (hooks) { setResizableColumnsTableData(this); await render(hbsResizableColumnsAdvancedTable); + const table = find('.hds-advanced-table'); + const originalGridValues = getTableGridValues(table); + const handle = find('.hds-advanced-table__th-resize-handle'); - const th = handle.closest('.hds-advanced-table__th'); // Focus handle and press ArrowLeft multiple times to try going below min width await focus(handle); @@ -1468,12 +1509,70 @@ module('Integration | Component | hds/advanced-table/index', function (hooks) { await triggerKeyEvent(handle, 'keydown', 'ArrowRight'); } - const newWidthString = th.style.width || getComputedStyle(th).width; - const newWidth = parseFloat(newWidthString) + 1; // adding a pixel to account for border + const newGridValues = getTableGridValues(table); + assert.notEqual( + newGridValues, + originalGridValues, + 'Grid values changed after ArrowRight', + ); + + const firstColumnGridValue = newGridValues[0]; + + assert.ok( + parseInt(firstColumnGridValue, 10) <= 300, + `Column width respects maximum width constraint with keyboard events (actual: ${firstColumnGridValue}px, max: 300px)`, + ); + }); + + test('it should show the context menu when resizing is enabled', async function (assert) { + setResizableColumnsTableData(this); + await render(hbsResizableColumnsAdvancedTable); + + const th = find('.hds-advanced-table__th'); // find the first header cell assert.ok( - newWidth <= 300, - `Column width respects maximum width constraint with keyboard events (actual: ${newWidth}px, max: 300px)`, + th.querySelector('.hds-advanced-table__th-context-menu'), + 'context menu exists', + ); + + const contextMenuToggle = th.querySelector('.hds-dropdown-toggle-icon'); + await click(contextMenuToggle); + + assert.dom('[data-test-context-option-key="reset-column-width"]').exists(); + }); + + test('it should resize the column to the initial width when resetting column width', async function (assert) { + setResizableColumnsTableData(this); + await render(hbsResizableColumnsAdvancedTable); + + const table = find('.hds-advanced-table'); + const originalGridValues = getTableGridValues(table); + + const handle = find('.hds-advanced-table__th-resize-handle'); + const th = handle.closest('.hds-advanced-table__th'); + + // Simulate pointer drag to the right (increase width) + await triggerEvent(handle, 'pointerdown', { clientX: 100 }); + await triggerEvent(handle, 'pointermove', { clientX: 130 }); + await triggerEvent(window, 'pointerup'); + + let newGridValues = getTableGridValues(table); + + assert.notEqual( + newGridValues, + originalGridValues, + 'Grid values changed after drag', + ); + + const contextMenuToggle = th.querySelector('.hds-dropdown-toggle-icon'); + await click(contextMenuToggle); + await click('[data-test-context-option-key="reset-column-width"]'); + + newGridValues = getTableGridValues(table); + assert.deepEqual( + newGridValues, + originalGridValues, + 'Grid values reset to initial state after resetting column width', ); }); }); From 59edd1365a021e30de9101a13e02ead1180788af Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Tue, 27 May 2025 20:29:36 -0400 Subject: [PATCH 34/89] updated styling on th-sort component --- .../components/hds/advanced-table/th-sort.hbs | 49 ++++++++++++------- .../components/hds/advanced-table/th-sort.ts | 4 ++ .../src/components/hds/advanced-table/th.hbs | 4 +- 3 files changed, 39 insertions(+), 18 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/th-sort.hbs b/packages/components/src/components/hds/advanced-table/th-sort.hbs index 6b61cd1e6a1..0047b67091f 100644 --- a/packages/components/src/components/hds/advanced-table/th-sort.hbs +++ b/packages/components/src/components/hds/advanced-table/th-sort.hbs @@ -22,25 +22,40 @@ }} ...attributes > -
- {{yield}} - {{#if @tooltip}} - - {{/if}} - + +
+ + {{yield}} + - {{#if @column}} - {{#if this.showContextMenu}} - + {{#if @tooltip}} + {{/if}} +
- {{#if (and @column.isResizable (not @isLastColumn))}} - + + + + {{#if @column}} + {{#if this.showContextMenu}} + + {{/if}} + + {{#if (and @column.isResizable (not @isLastColumn))}} + + {{/if}} {{/if}} - {{/if}} -
+ + \ No newline at end of file diff --git a/packages/components/src/components/hds/advanced-table/th-sort.ts b/packages/components/src/components/hds/advanced-table/th-sort.ts index 6e8f16ff6f1..8b21b68706b 100644 --- a/packages/components/src/components/hds/advanced-table/th-sort.ts +++ b/packages/components/src/components/hds/advanced-table/th-sort.ts @@ -120,6 +120,10 @@ export default class HdsAdvancedTableThSort extends Component{{yield}} + > + {{yield}} + {{else}} From 9734ff8cc5450733ef7c498632b9c1d239f7021a Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Tue, 27 May 2025 20:46:47 -0400 Subject: [PATCH 35/89] styles wip --- .../components/src/components/hds/advanced-table/th-sort.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/components/hds/advanced-table/th-sort.hbs b/packages/components/src/components/hds/advanced-table/th-sort.hbs index 0047b67091f..84a44070c57 100644 --- a/packages/components/src/components/hds/advanced-table/th-sort.hbs +++ b/packages/components/src/components/hds/advanced-table/th-sort.hbs @@ -36,7 +36,7 @@ {{/if}} - + Date: Tue, 27 May 2025 21:14:03 -0400 Subject: [PATCH 36/89] playing around with minimum widths --- .../src/components/hds/advanced-table/models/column.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/components/hds/advanced-table/models/column.ts b/packages/components/src/components/hds/advanced-table/models/column.ts index 5b0e9a368d0..9d04fcaa8d7 100644 --- a/packages/components/src/components/hds/advanced-table/models/column.ts +++ b/packages/components/src/components/hds/advanced-table/models/column.ts @@ -85,7 +85,7 @@ export default class HdsAdvancedTableColumn { this._originalWidth = width; // TODO: discuss sensible defaults for minWidth and maxWidth - this.minWidth = minWidth ?? '50px'; + this.minWidth = minWidth ?? '150px'; this.maxWidth = maxWidth ?? '800px'; } From ef6ba037efbb8404cf4649901a8c292149e1d299 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Wed, 28 May 2025 10:51:34 -0400 Subject: [PATCH 37/89] working on styling th elements --- .../components/src/components/hds/advanced-table/th-sort.hbs | 4 ++-- packages/components/src/components/hds/advanced-table/th.hbs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/th-sort.hbs b/packages/components/src/components/hds/advanced-table/th-sort.hbs index 84a44070c57..189aceb198b 100644 --- a/packages/components/src/components/hds/advanced-table/th-sort.hbs +++ b/packages/components/src/components/hds/advanced-table/th-sort.hbs @@ -22,7 +22,7 @@ }} ...attributes > - +
- + - + {{#if @column.isVisuallyHidden}} {{yield}} {{else}} From fbbb0db4073d8078876241d860c0310918659cad Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Wed, 28 May 2025 11:34:53 -0400 Subject: [PATCH 38/89] added sorting and nonsorting examples --- .../controllers/components/advanced-table.js | 12 ++++++--- .../templates/components/advanced-table.hbs | 26 +++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/showcase/app/controllers/components/advanced-table.js b/showcase/app/controllers/components/advanced-table.js index a14557b45ac..1b3797f14ce 100644 --- a/showcase/app/controllers/components/advanced-table.js +++ b/showcase/app/controllers/components/advanced-table.js @@ -367,7 +367,6 @@ export default class ComponentsTableController extends Controller { width: '200px', maxWidth: '400px', isResizable: true, - isSortable: true, }, { key: 'album', @@ -375,7 +374,6 @@ export default class ComponentsTableController extends Controller { tooltip: 'More information.', width: '300px', isResizable: true, - isSortable: true, }, { key: 'year', @@ -383,7 +381,6 @@ export default class ComponentsTableController extends Controller { tooltip: 'More information.', width: '200px', isResizable: true, - isSortable: true, }, { key: 'other', @@ -391,6 +388,15 @@ export default class ComponentsTableController extends Controller { }, ]; + columnResizeColumnsWithSorting = this.columnResizeColumns.map( + (column, index) => { + return { + ...column, + isSortable: index !== this.columnResizeColumns.length - 1, // last column is not sortable + }; + }, + ); + @action noop() { // no-op diff --git a/showcase/app/templates/components/advanced-table.hbs b/showcase/app/templates/components/advanced-table.hbs index b1b7ad85cd1..5a8b2f48c13 100644 --- a/showcase/app/templates/components/advanced-table.hbs +++ b/showcase/app/templates/components/advanced-table.hbs @@ -569,6 +569,32 @@ + Resizable columns with sorting + + + <:body as |B|> + + {{B.data.artist}} + +
+ + {{B.data.album}} +
+
+ + + + + + + + + + +
+ +
+ Functional examples From 4ba8e7d12cf12818d76445e3c355ea293abec01a Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Wed, 28 May 2025 17:05:14 -0400 Subject: [PATCH 39/89] added more showcase examples and fixed the incorrect icon --- .../hds/advanced-table/th-context-menu.hbs | 2 +- .../controllers/components/advanced-table.js | 5 ++ .../templates/components/advanced-table.hbs | 64 +++++++++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/packages/components/src/components/hds/advanced-table/th-context-menu.hbs b/packages/components/src/components/hds/advanced-table/th-context-menu.hbs index 594af76aa41..62fa6a9ec7f 100644 --- a/packages/components/src/components/hds/advanced-table/th-context-menu.hbs +++ b/packages/components/src/components/hds/advanced-table/th-context-menu.hbs @@ -3,7 +3,7 @@ SPDX-License-Identifier: MPL-2.0 }} - + {{#each this._options as |option|}} + ThResizeHandle + + + +
+
+
+
+ +
+ + Lorem + +
+ +
+
+
+ +
+ + Ipsum + +
+ +
+
+
+ +
+ + Dolor + +
+ +
+
+ Sit +
+
+
+
+
+
+
+ + + Td From 2ba74a5f9c3d720246934a39097bfd5defdebfb4 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Wed, 28 May 2025 17:17:53 -0400 Subject: [PATCH 40/89] added resizable columns in the complex example --- .../mock/app/main/generic-advanced-table.gts | 39 ++++++++++++------- .../templates/components/advanced-table.hbs | 2 - 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/showcase/app/components/mock/app/main/generic-advanced-table.gts b/showcase/app/components/mock/app/main/generic-advanced-table.gts index c238ae7606e..f7e9c066dd9 100644 --- a/showcase/app/components/mock/app/main/generic-advanced-table.gts +++ b/showcase/app/components/mock/app/main/generic-advanced-table.gts @@ -27,79 +27,92 @@ const SAMPLE_COLUMNS = [ isSortable: true, label: 'Name', key: 'name', - width: 'max-content', + isResizable: true, + width: '300px', }, { label: 'Project name', key: 'project-name', isSortable: true, - width: 'max-content', + isResizable: true, + width: '300px', }, { label: 'Current run ID', key: 'current-run-id', isSortable: true, - width: 'max-content', + isResizable: true, + width: '300px', }, { label: 'Run status', key: 'run-status', isSortable: true, - width: 'max-content', + isResizable: true, + width: '300px', }, { label: 'Current run applied', key: 'current-run-applied', isSortable: true, - width: 'max-content', + isResizable: true, + width: '300px', }, { label: 'VCS repo', key: 'vcs-repo', isSortable: true, - width: 'max-content', + isResizable: true, + width: '300px', }, { label: 'Module count', key: 'module-count', isSortable: true, - width: 'max-content', + isResizable: true, + width: '300px', }, { label: 'Modules', key: 'modules', isSortable: true, - width: 'max-content', + isResizable: true, + width: '300px', }, { label: 'Provider count', key: 'provider-count', isSortable: true, - width: 'max-content', + isResizable: true, + width: '300px', }, { label: 'Providers', key: 'providers', isSortable: true, - width: 'max-content', + isResizable: true, + width: '300px', }, { label: 'Terraform version', key: 'terraform-version', isSortable: true, - width: 'max-content', + isResizable: true, + width: '300px', }, { label: 'State terraform version', key: 'state-terraform-version', isSortable: true, - width: 'max-content', + isResizable: true, + width: '300px', }, { label: 'Created', key: 'created', isSortable: true, - width: 'max-content', + isResizable: true, + width: '300px', }, { label: 'Updated', diff --git a/showcase/app/templates/components/advanced-table.hbs b/showcase/app/templates/components/advanced-table.hbs index 8650e766cfa..3a240ed6028 100644 --- a/showcase/app/templates/components/advanced-table.hbs +++ b/showcase/app/templates/components/advanced-table.hbs @@ -1720,8 +1720,6 @@ Sit
-
-
From 03c964b6cec635ea73aa458bd4f80f694259e211 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Wed, 28 May 2025 17:46:35 -0400 Subject: [PATCH 41/89] fixed dropdown context menu toggle button styles --- .../components/hds/advanced-table/th-context-menu.hbs | 8 +++++++- .../src/styles/components/advanced-table.scss | 10 ++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/th-context-menu.hbs b/packages/components/src/components/hds/advanced-table/th-context-menu.hbs index 62fa6a9ec7f..3d8af2d38df 100644 --- a/packages/components/src/components/hds/advanced-table/th-context-menu.hbs +++ b/packages/components/src/components/hds/advanced-table/th-context-menu.hbs @@ -3,7 +3,13 @@ SPDX-License-Identifier: MPL-2.0 }} - + {{#each this._options as |option|}} Date: Wed, 28 May 2025 18:22:41 -0400 Subject: [PATCH 42/89] added callback function for column resizing and accompanying test --- .../components/hds/advanced-table/index.ts | 10 ++++- .../hds/advanced-table/models/column.ts | 38 +++++++++++++------ .../hds/advanced-table/models/table.ts | 19 ++++++++-- .../hds/advanced-table/index-test.js | 31 +++++++++++++++ 4 files changed, 82 insertions(+), 16 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.ts b/packages/components/src/components/hds/advanced-table/index.ts index 4b71fbd232f..f7217379e8f 100644 --- a/packages/components/src/components/hds/advanced-table/index.ts +++ b/packages/components/src/components/hds/advanced-table/index.ts @@ -134,6 +134,7 @@ export interface HdsAdvancedTableSignature { hasStickyFirstColumn?: boolean; childrenKey?: string; maxHeight?: string; + onColumnResize?: (columnKey: string, newWidth: string) => void; }; Blocks: { body?: [ @@ -178,12 +179,19 @@ export default class HdsAdvancedTable extends Component number = undefined; private _originalWidth?: string = undefined; + private _onColumnResize?: HdsAdvancedTableTableColumnResizeCallback; get pxWidth(): number | undefined { if (isPxSize(this.width)) { @@ -56,18 +58,26 @@ export default class HdsAdvancedTableColumn { } } - constructor(args: HdsAdvancedTableColumnType) { + constructor(args: { + column: HdsAdvancedTableColumnType; + onColumnResize?: HdsAdvancedTableTableColumnResizeCallback; + }) { + const { column, onColumnResize } = args; + // set column properties - this.label = args.label; - this.align = args.align; - this.isExpandable = 'isExpandable' in args ? args.isExpandable : false; - this.isSortable = args.isSortable; - this.isVisuallyHidden = args.isVisuallyHidden; - this.key = args.key; - this.tooltip = args.tooltip; - this._setWidthValues(args); - this._setResizableValues(args); - this.sortingFunction = args.sortingFunction; + this.label = column.label; + this.align = column.align; + this.isExpandable = 'isExpandable' in column ? column.isExpandable : false; + this.isSortable = column.isSortable; + this.isVisuallyHidden = column.isVisuallyHidden; + this.key = column.key; + this.tooltip = column.tooltip; + this._setWidthValues(column); + this._setResizableValues(column); + this.sortingFunction = column.sortingFunction; + + // set resize callback + this._onColumnResize = onColumnResize; } private _setWidthValues({ @@ -111,6 +121,12 @@ export default class HdsAdvancedTableColumn { this.pxMaxWidth !== undefined ? Math.min(minLimitedPxWidth, this.pxMaxWidth) : minLimitedPxWidth; + + if (this.key === undefined || this.width === undefined) { + return; + } + + this._onColumnResize?.(this.key, this.width); } @action diff --git a/packages/components/src/components/hds/advanced-table/models/table.ts b/packages/components/src/components/hds/advanced-table/models/table.ts index 8364cd8de13..e3a02d77e80 100644 --- a/packages/components/src/components/hds/advanced-table/models/table.ts +++ b/packages/components/src/components/hds/advanced-table/models/table.ts @@ -14,10 +14,16 @@ import type { HdsAdvancedTableModel, } from '../types'; +export type HdsAdvancedTableTableColumnResizeCallback = ( + columnKey: string, + newWidth: string +) => void; + interface HdsAdvancedTableTableArgs { model: HdsAdvancedTableModel; columns: HdsAdvancedTableColumnType[]; childrenKey?: string; + onColumnResize?: HdsAdvancedTableTableColumnResizeCallback; } function getVisibleRows(rows: HdsAdvancedTableRow[]): HdsAdvancedTableRow[] { @@ -42,9 +48,9 @@ export default class HdsAdvancedTableTableModel { rows: HdsAdvancedTableRow[] = []; constructor(args: HdsAdvancedTableTableArgs) { - const { model, columns, childrenKey } = args; + const { model, columns, childrenKey, onColumnResize } = args; - this._setupColumns({ columns }); + this._setupColumns({ columns, onColumnResize }); this._setupRows({ model, columns, childrenKey }); } @@ -78,8 +84,13 @@ export default class HdsAdvancedTableTableModel { private _setupColumns({ columns, - }: Pick) { - this.columns = columns.map((column) => new HdsAdvancedTableColumn(column)); + onColumnResize, + }: Pick & { + onColumnResize?: HdsAdvancedTableTableColumnResizeCallback; + }) { + this.columns = columns.map( + (column) => new HdsAdvancedTableColumn({ column, onColumnResize }) + ); } private _setupRows({ diff --git a/showcase/tests/integration/components/hds/advanced-table/index-test.js b/showcase/tests/integration/components/hds/advanced-table/index-test.js index 8d2fa4c13c3..66512314bae 100644 --- a/showcase/tests/integration/components/hds/advanced-table/index-test.js +++ b/showcase/tests/integration/components/hds/advanced-table/index-test.js @@ -1575,4 +1575,35 @@ module('Integration | Component | hds/advanced-table/index', function (hooks) { 'Grid values reset to initial state after resetting column width', ); }); + + test('it should call `onColumnResize` when a column is resized', async function (assert) { + setResizableColumnsTableData(this); + const onColumnResizeSpy = sinon.spy(); + this.set('onColumnResize', onColumnResizeSpy); + + await render(hbs` + + <:body as |B|> + + {{B.data.col1}} + {{B.data.col2}} + + + + `); + + const handle = find('.hds-advanced-table__th-resize-handle'); + + // Simulate pointer drag to the right (increase width) + await triggerEvent(handle, 'pointerdown', { clientX: 100 }); + await triggerEvent(handle, 'pointermove', { clientX: 130 }); + await triggerEvent(window, 'pointerup'); + + assert.ok(onColumnResizeSpy.calledOnce, 'onColumnResize was called'); + }); }); From 66297bf1f60b1b1234b1dc487eacd062f0fe17cf Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Thu, 29 May 2025 10:31:13 -0400 Subject: [PATCH 43/89] added a changeset --- .changeset/chilly-cheetahs-rescue.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/chilly-cheetahs-rescue.md diff --git a/.changeset/chilly-cheetahs-rescue.md b/.changeset/chilly-cheetahs-rescue.md new file mode 100644 index 00000000000..2296be5be8a --- /dev/null +++ b/.changeset/chilly-cheetahs-rescue.md @@ -0,0 +1,7 @@ +--- +"@hashicorp/design-system-components": minor +--- + +`AdvancedTable` - Added `isResizable` option to column object. When `true`, allows the column to be resized with both a click-and-drag and a keyboard interface. + +Added `ember-math-helpers` dependency. From bf7548410cb54d74b7a4013c1801a7e16e0e44b3 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Thu, 29 May 2025 13:46:03 -0400 Subject: [PATCH 44/89] added unit test for column class --- .../hds/advanced-table/models/column.ts | 2 +- .../hds/advanced-table/models/column-test.js | 267 ++++++++++++++++++ 2 files changed, 268 insertions(+), 1 deletion(-) create mode 100644 showcase/tests/unit/components/hds/advanced-table/models/column-test.js diff --git a/packages/components/src/components/hds/advanced-table/models/column.ts b/packages/components/src/components/hds/advanced-table/models/column.ts index 81ed5e04629..1cea0295f7a 100644 --- a/packages/components/src/components/hds/advanced-table/models/column.ts +++ b/packages/components/src/components/hds/advanced-table/models/column.ts @@ -66,7 +66,7 @@ export default class HdsAdvancedTableColumn { // set column properties this.label = column.label; - this.align = column.align; + this.align = column.align ?? 'left'; this.isExpandable = 'isExpandable' in column ? column.isExpandable : false; this.isSortable = column.isSortable; this.isVisuallyHidden = column.isVisuallyHidden; diff --git a/showcase/tests/unit/components/hds/advanced-table/models/column-test.js b/showcase/tests/unit/components/hds/advanced-table/models/column-test.js new file mode 100644 index 00000000000..68e2354b154 --- /dev/null +++ b/showcase/tests/unit/components/hds/advanced-table/models/column-test.js @@ -0,0 +1,267 @@ +import { module, test } from 'qunit'; +import sinon from 'sinon'; +import HdsAdvancedTableColumn from '@hashicorp/design-system-components/components/hds/advanced-table/models/column'; + +module('Unit | Component | hds/advanced-table/models/column', function () { + test('initializes with default properties when minimal args provided', function (assert) { + const column = new HdsAdvancedTableColumn({ + column: { label: 'Test Column' }, + }); + + assert.strictEqual(column.label, 'Test Column', 'sets the label property'); + assert.strictEqual(column.align, 'left', 'defaults to left alignment'); + assert.false(column.isExpandable, 'defaults isExpandable to false'); + assert.false(column.isResizable, 'defaults isResizable to false'); + assert.strictEqual( + column.isSortable, + undefined, + 'isSortable is undefined when not provided', + ); + assert.strictEqual( + column.isVisuallyHidden, + undefined, + 'isVisuallyHidden is undefined when not provided', + ); + assert.strictEqual( + column.key, + undefined, + 'key is undefined when not provided', + ); + assert.strictEqual( + column.minWidth, + undefined, + 'minWidth is undefined when width is not provided', + ); + assert.strictEqual( + column.maxWidth, + undefined, + 'maxWidth is undefined when width is not provided', + ); + assert.strictEqual( + column.tooltip, + undefined, + 'tooltip is undefined when not provided', + ); + assert.strictEqual( + column.width, + undefined, + 'width is undefined when not provided', + ); + }); + + test('initializes with all provided properties', function (assert) { + const sortFn = (a, b) => a - b; + const column = new HdsAdvancedTableColumn({ + column: { + label: 'Full Column', + align: 'center', + isExpandable: true, + isResizable: false, + isSortable: true, + isVisuallyHidden: true, + key: 'test-column', + tooltip: 'Column tooltip', + sortingFunction: sortFn, + }, + }); + + assert.strictEqual(column.label, 'Full Column', 'sets the label property'); + assert.strictEqual(column.align, 'center', 'sets the alignment'); + assert.true(column.isExpandable, 'sets isExpandable'); + assert.false(column.isResizable, 'sets isResizable'); + assert.true(column.isSortable, 'sets isSortable'); + assert.true(column.isVisuallyHidden, 'sets isVisuallyHidden'); + assert.strictEqual(column.key, 'test-column', 'sets the key'); + assert.strictEqual(column.tooltip, 'Column tooltip', 'sets the tooltip'); + assert.strictEqual( + column.sortingFunction, + sortFn, + 'sets the sorting function', + ); + }); + + test('handles width values properly', function (assert) { + const column = new HdsAdvancedTableColumn({ + column: { + label: 'Width Column', + width: '200px', + minWidth: '100px', + maxWidth: '300px', + }, + }); + + assert.strictEqual(column.width, '200px', 'sets the width'); + assert.strictEqual(column.minWidth, '100px', 'sets the minWidth'); + assert.strictEqual(column.maxWidth, '300px', 'sets the maxWidth'); + assert.strictEqual(column.pxWidth, 200, 'converts width to number'); + assert.strictEqual(column.pxMinWidth, 100, 'converts minWidth to number'); + assert.strictEqual(column.pxMaxWidth, 300, 'converts maxWidth to number'); + }); + + test('uses default min/max width when only width is provided', function (assert) { + const column = new HdsAdvancedTableColumn({ + column: { + label: 'Width Only', + width: '200px', + }, + }); + + assert.strictEqual(column.width, '200px', 'sets the width'); + assert.strictEqual(column.minWidth, '150px', 'sets default minWidth'); + assert.strictEqual(column.maxWidth, '800px', 'sets default maxWidth'); + }); + + test('pxWidth setter updates width property', function (assert) { + const column = new HdsAdvancedTableColumn({ + column: { + label: 'Width Column', + width: '200px', + }, + }); + + column.pxWidth = 250; + assert.strictEqual(column.width, '250px', 'updates width with px suffix'); + assert.strictEqual( + column.pxWidth, + 250, + 'pxWidth getter returns updated value', + ); + }); + + test('isPxSize utility function works correctly', function (assert) { + const column = new HdsAdvancedTableColumn({ + column: { label: 'Test' }, + }); + + // Setting valid px values + column.width = '100px'; + assert.strictEqual( + column.pxWidth, + 100, + 'returns number for valid px value', + ); + + column.width = '100.5px'; + assert.strictEqual(column.pxWidth, 100.5, 'handles decimal px values'); + + column.width = '-50px'; + assert.strictEqual(column.pxWidth, -50, 'handles negative px values'); + + // Setting non-px values + column.width = '100%'; + assert.strictEqual( + column.pxWidth, + undefined, + 'returns undefined for percentage values', + ); + + column.width = '10em'; + assert.strictEqual( + column.pxWidth, + undefined, + 'returns undefined for em values', + ); + + column.width = 'auto'; + assert.strictEqual(column.pxWidth, undefined, 'returns undefined for auto'); + + column.width = undefined; + assert.strictEqual( + column.pxWidth, + undefined, + 'returns undefined when width is undefined', + ); + }); + + test('setPxWidth respects min/max constraints', function (assert) { + const column = new HdsAdvancedTableColumn({ + column: { + label: 'Constrained Width', + width: '200px', + minWidth: '150px', + maxWidth: '250px', + }, + }); + + column.setPxWidth(100); + assert.strictEqual( + column.width, + '150px', + 'respects minimum width constraint', + ); + + column.setPxWidth(300); + assert.strictEqual( + column.width, + '250px', + 'respects maximum width constraint', + ); + + column.setPxWidth(200); + assert.strictEqual( + column.width, + '200px', + 'sets exact width when within constraints', + ); + }); + + test('setPxWidth calls resize callback when key is present', function (assert) { + const resizeSpy = sinon.spy(); + + const column = new HdsAdvancedTableColumn({ + column: { + label: 'Callback Test', + width: '150px', + key: 'test-key', + }, + onColumnResize: resizeSpy, + }); + + column.setPxWidth(200); + + assert.ok( + resizeSpy.calledOnceWith('test-key', '200px'), + 'resize callback was called when key is present', + ); + }); + + test('setPxWidth does not call resize callback when key is missing', function (assert) { + const resizeSpy = sinon.spy(); + + const column = new HdsAdvancedTableColumn({ + column: { + label: 'No Key', + width: '150px', + }, + onColumnResize: resizeSpy, + }); + + column.setPxWidth(200); + + assert.false( + resizeSpy.called, + 'resize callback was not called when key is missing', + ); + }); + + test('restoreWidth sets width back to original value', function (assert) { + const column = new HdsAdvancedTableColumn({ + column: { + label: 'Restore Test', + width: '200px', + }, + }); + + assert.strictEqual(column.width, '200px', 'initial width is set'); + + column.setPxWidth(300); + assert.strictEqual(column.width, '300px', 'width is updated'); + + column.restoreWidth(); + assert.strictEqual( + column.width, + '200px', + 'width is restored to original value', + ); + }); +}); From 7bddb7d146ea378304e4a4dde66386010f922930 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Thu, 29 May 2025 14:52:00 -0400 Subject: [PATCH 45/89] pr cleanup --- .../hds/advanced-table/models/column.ts | 4 ++-- .../hds/advanced-table/th-context-menu.ts | 3 ++- .../src/styles/components/advanced-table.scss | 16 +++++++--------- packages/components/src/template-registry.ts | 4 ++-- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/models/column.ts b/packages/components/src/components/hds/advanced-table/models/column.ts index 1cea0295f7a..6c923a100e3 100644 --- a/packages/components/src/components/hds/advanced-table/models/column.ts +++ b/packages/components/src/components/hds/advanced-table/models/column.ts @@ -68,8 +68,8 @@ export default class HdsAdvancedTableColumn { this.label = column.label; this.align = column.align ?? 'left'; this.isExpandable = 'isExpandable' in column ? column.isExpandable : false; - this.isSortable = column.isSortable; - this.isVisuallyHidden = column.isVisuallyHidden; + this.isSortable = column.isSortable ?? false; + this.isVisuallyHidden = column.isVisuallyHidden ?? false; this.key = column.key; this.tooltip = column.tooltip; this._setWidthValues(column); diff --git a/packages/components/src/components/hds/advanced-table/th-context-menu.ts b/packages/components/src/components/hds/advanced-table/th-context-menu.ts index e314c172281..9d9abe92478 100644 --- a/packages/components/src/components/hds/advanced-table/th-context-menu.ts +++ b/packages/components/src/components/hds/advanced-table/th-context-menu.ts @@ -6,6 +6,7 @@ import Component from '@glimmer/component'; import type HdsAdvancedTableColumn from './models/column.ts'; +import type { HdsDropdownSignature } from '../dropdown/index.ts'; import type { HdsDropdownToggleIconSignature } from '../dropdown/toggle/icon.ts'; interface HdsAdvancedTableThContextMenuOption { @@ -22,7 +23,7 @@ export interface HdsAdvancedTableThContextMenuSignature { Args: { column: HdsAdvancedTableColumn; }; - Element: HTMLDivElement; + Element: HdsDropdownSignature['Element']; } export default class HdsAdvancedTableThContextMenu extends Component { diff --git a/packages/components/src/styles/components/advanced-table.scss b/packages/components/src/styles/components/advanced-table.scss index 823c1f83383..19358e68c9b 100644 --- a/packages/components/src/styles/components/advanced-table.scss +++ b/packages/components/src/styles/components/advanced-table.scss @@ -110,15 +110,13 @@ $hds-advanced-table-cell-padding-tall: 22px 16px 21px 16px; // the 1px differenc isolation: isolate; } - &.hds-advanced-table__th--is-resizable { - .hds-advanced-table__th-content-text { - display: block; - width: 100%; - min-width: 30px; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } + &.hds-advanced-table__th--is-resizable .hds-advanced-table__th-content-text { + display: block; + width: 100%; + min-width: 30px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; } } diff --git a/packages/components/src/template-registry.ts b/packages/components/src/template-registry.ts index 4c0742ae61c..27090a4a407 100644 --- a/packages/components/src/template-registry.ts +++ b/packages/components/src/template-registry.ts @@ -13,8 +13,8 @@ import type HdsAdvancedTableThButtonExpandComponent from './components/hds/advan import type HdsAdvancedTableThButtonSortComponent from './components/hds/advanced-table/th-button-sort'; import type HdsAdvancedTableThComponent from './components/hds/advanced-table/th'; import type HdsAdvancedTableThButtonTooltipComponent from './components/hds/advanced-table/th-button-tooltip'; -import type HdsAdvancedTableThContextMenu from './components/hds/advanced-table/th-context-menu.ts'; -import type HdsAdvancedTableThResizeHandle from './components/hds/advanced-table/th-resize-handle.ts'; +import type HdsAdvancedTableThContextMenu from './components/hds/advanced-table/th-context-menu'; +import type HdsAdvancedTableThResizeHandle from './components/hds/advanced-table/th-resize-handle'; import type HdsAdvancedTableThSortComponent from './components/hds/advanced-table/th-sort'; import type HdsAdvancedTableThSelectableComponent from './components/hds/advanced-table/th-selectable'; import type HdsAdvancedTableTrComponent from './components/hds/advanced-table/tr'; From 2d64712ecd44821ddb5f51d906e41ab469f0b9fa Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Thu, 29 May 2025 15:27:50 -0400 Subject: [PATCH 46/89] responding to PR feedback --- .../src/components/hds/advanced-table/th-resize-handle.hbs | 2 +- .../src/components/hds/advanced-table/th-resize-handle.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs b/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs index 0229c885e5c..e94dc657ea0 100644 --- a/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs +++ b/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs @@ -1,6 +1,6 @@ {{! template-lint-disable no-pointer-down-event-binding }}
Date: Thu, 29 May 2025 15:58:51 -0400 Subject: [PATCH 47/89] fixing failing tests --- showcase/app/templates/components/advanced-table.hbs | 6 +++--- .../components/hds/advanced-table/models/column-test.js | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/showcase/app/templates/components/advanced-table.hbs b/showcase/app/templates/components/advanced-table.hbs index 3a240ed6028..993e627f087 100644 --- a/showcase/app/templates/components/advanced-table.hbs +++ b/showcase/app/templates/components/advanced-table.hbs @@ -1675,7 +1675,7 @@ >
-
+
@@ -1689,7 +1689,7 @@ />
-
+
@@ -1703,7 +1703,7 @@ />
-
+
diff --git a/showcase/tests/unit/components/hds/advanced-table/models/column-test.js b/showcase/tests/unit/components/hds/advanced-table/models/column-test.js index 68e2354b154..dcf99ae681a 100644 --- a/showcase/tests/unit/components/hds/advanced-table/models/column-test.js +++ b/showcase/tests/unit/components/hds/advanced-table/models/column-test.js @@ -14,13 +14,13 @@ module('Unit | Component | hds/advanced-table/models/column', function () { assert.false(column.isResizable, 'defaults isResizable to false'); assert.strictEqual( column.isSortable, - undefined, - 'isSortable is undefined when not provided', + false, + 'isSortable is false when not provided', ); assert.strictEqual( column.isVisuallyHidden, - undefined, - 'isVisuallyHidden is undefined when not provided', + false, + 'isVisuallyHidden is false when not provided', ); assert.strictEqual( column.key, From 845b424d76f1ca0947f735c5a828634d8fc90630 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Thu, 29 May 2025 17:16:51 -0400 Subject: [PATCH 48/89] disallow resizable columns in combo with stickyFirstColumn --- .../components/hds/advanced-table/index.ts | 7 ++++ .../hds/advanced-table/models/table.ts | 4 ++ .../mock/app/main/generic-advanced-table.gts | 39 +++++++------------ 3 files changed, 24 insertions(+), 26 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.ts b/packages/components/src/components/hds/advanced-table/index.ts index f7217379e8f..956e5c47ed2 100644 --- a/packages/components/src/components/hds/advanced-table/index.ts +++ b/packages/components/src/components/hds/advanced-table/index.ts @@ -194,6 +194,13 @@ export default class HdsAdvancedTable extends Component column.isSortable); const sortableColumnLabels = sortableColumns.map( diff --git a/packages/components/src/components/hds/advanced-table/models/table.ts b/packages/components/src/components/hds/advanced-table/models/table.ts index e3a02d77e80..f342458b9ef 100644 --- a/packages/components/src/components/hds/advanced-table/models/table.ts +++ b/packages/components/src/components/hds/advanced-table/models/table.ts @@ -82,6 +82,10 @@ export default class HdsAdvancedTableTableModel { } } + get hasResizableColumns(): boolean { + return this.columns.some((column) => column.isResizable); + } + private _setupColumns({ columns, onColumnResize, diff --git a/showcase/app/components/mock/app/main/generic-advanced-table.gts b/showcase/app/components/mock/app/main/generic-advanced-table.gts index f7e9c066dd9..c238ae7606e 100644 --- a/showcase/app/components/mock/app/main/generic-advanced-table.gts +++ b/showcase/app/components/mock/app/main/generic-advanced-table.gts @@ -27,92 +27,79 @@ const SAMPLE_COLUMNS = [ isSortable: true, label: 'Name', key: 'name', - isResizable: true, - width: '300px', + width: 'max-content', }, { label: 'Project name', key: 'project-name', isSortable: true, - isResizable: true, - width: '300px', + width: 'max-content', }, { label: 'Current run ID', key: 'current-run-id', isSortable: true, - isResizable: true, - width: '300px', + width: 'max-content', }, { label: 'Run status', key: 'run-status', isSortable: true, - isResizable: true, - width: '300px', + width: 'max-content', }, { label: 'Current run applied', key: 'current-run-applied', isSortable: true, - isResizable: true, - width: '300px', + width: 'max-content', }, { label: 'VCS repo', key: 'vcs-repo', isSortable: true, - isResizable: true, - width: '300px', + width: 'max-content', }, { label: 'Module count', key: 'module-count', isSortable: true, - isResizable: true, - width: '300px', + width: 'max-content', }, { label: 'Modules', key: 'modules', isSortable: true, - isResizable: true, - width: '300px', + width: 'max-content', }, { label: 'Provider count', key: 'provider-count', isSortable: true, - isResizable: true, - width: '300px', + width: 'max-content', }, { label: 'Providers', key: 'providers', isSortable: true, - isResizable: true, - width: '300px', + width: 'max-content', }, { label: 'Terraform version', key: 'terraform-version', isSortable: true, - isResizable: true, - width: '300px', + width: 'max-content', }, { label: 'State terraform version', key: 'state-terraform-version', isSortable: true, - isResizable: true, - width: '300px', + width: 'max-content', }, { label: 'Created', key: 'created', isSortable: true, - isResizable: true, - width: '300px', + width: 'max-content', }, { label: 'Updated', From 4389dcaccac7d7cb1b55cdfb9fd271ecef2fb513 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Thu, 29 May 2025 17:32:44 -0400 Subject: [PATCH 49/89] adding additional tests --- .../components/hds/advanced-table/index.ts | 2 +- .../hds/advanced-table/models/column.ts | 18 +++-- .../hds/advanced-table/models/table.ts | 12 ++-- .../components/hds/advanced-table/types.ts | 5 ++ .../hds/advanced-table/index-test.js | 65 +++++++++++++++---- 5 files changed, 75 insertions(+), 27 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.ts b/packages/components/src/components/hds/advanced-table/index.ts index 956e5c47ed2..d67f8e3e872 100644 --- a/packages/components/src/components/hds/advanced-table/index.ts +++ b/packages/components/src/components/hds/advanced-table/index.ts @@ -134,7 +134,7 @@ export interface HdsAdvancedTableSignature { hasStickyFirstColumn?: boolean; childrenKey?: string; maxHeight?: string; - onColumnResize?: (columnKey: string, newWidth: string) => void; + onColumnResize?: (columnKey: string, newWidth?: string) => void; }; Blocks: { body?: [ diff --git a/packages/components/src/components/hds/advanced-table/models/column.ts b/packages/components/src/components/hds/advanced-table/models/column.ts index 6c923a100e3..80c6d387f5e 100644 --- a/packages/components/src/components/hds/advanced-table/models/column.ts +++ b/packages/components/src/components/hds/advanced-table/models/column.ts @@ -3,8 +3,10 @@ import { action } from '@ember/object'; import { assert } from '@ember/debug'; import type { HdsAdvancedTableColumn as HdsAdvancedTableColumnType } from '../types'; -import type { HdsAdvancedTableHorizontalAlignment } from '../types'; -import type { HdsAdvancedTableTableColumnResizeCallback } from './table'; +import type { + HdsAdvancedTableHorizontalAlignment, + HdsAdvancedTableColumnResizeCallback, +} from '../types'; function isPxSize(value?: string): boolean { if (value === undefined) { @@ -35,7 +37,7 @@ export default class HdsAdvancedTableColumn { @tracked sortingFunction?: (a: unknown, b: unknown) => number = undefined; private _originalWidth?: string = undefined; - private _onColumnResize?: HdsAdvancedTableTableColumnResizeCallback; + private _onColumnResize?: HdsAdvancedTableColumnResizeCallback; get pxWidth(): number | undefined { if (isPxSize(this.width)) { @@ -60,7 +62,7 @@ export default class HdsAdvancedTableColumn { constructor(args: { column: HdsAdvancedTableColumnType; - onColumnResize?: HdsAdvancedTableTableColumnResizeCallback; + onColumnResize?: HdsAdvancedTableColumnResizeCallback; }) { const { column, onColumnResize } = args; @@ -122,7 +124,7 @@ export default class HdsAdvancedTableColumn { ? Math.min(minLimitedPxWidth, this.pxMaxWidth) : minLimitedPxWidth; - if (this.key === undefined || this.width === undefined) { + if (this.key === undefined) { return; } @@ -132,5 +134,11 @@ export default class HdsAdvancedTableColumn { @action restoreWidth(): void { this.width = this._originalWidth; + + if (this.key === undefined) { + return; + } + + this._onColumnResize?.(this.key, this.width); } } diff --git a/packages/components/src/components/hds/advanced-table/models/table.ts b/packages/components/src/components/hds/advanced-table/models/table.ts index f342458b9ef..919e615c285 100644 --- a/packages/components/src/components/hds/advanced-table/models/table.ts +++ b/packages/components/src/components/hds/advanced-table/models/table.ts @@ -5,25 +5,21 @@ import HdsAdvancedTableRow from './row.ts'; import { action } from '@ember/object'; - +import { tracked } from '@glimmer/tracking'; import HdsAdvancedTableColumn from './column.ts'; import type { HdsAdvancedTableColumn as HdsAdvancedTableColumnType, HdsAdvancedTableExpandState, HdsAdvancedTableModel, + HdsAdvancedTableColumnResizeCallback, } from '../types'; -export type HdsAdvancedTableTableColumnResizeCallback = ( - columnKey: string, - newWidth: string -) => void; - interface HdsAdvancedTableTableArgs { model: HdsAdvancedTableModel; columns: HdsAdvancedTableColumnType[]; childrenKey?: string; - onColumnResize?: HdsAdvancedTableTableColumnResizeCallback; + onColumnResize?: HdsAdvancedTableColumnResizeCallback; } function getVisibleRows(rows: HdsAdvancedTableRow[]): HdsAdvancedTableRow[] { @@ -90,7 +86,7 @@ export default class HdsAdvancedTableTableModel { columns, onColumnResize, }: Pick & { - onColumnResize?: HdsAdvancedTableTableColumnResizeCallback; + onColumnResize?: HdsAdvancedTableColumnResizeCallback; }) { this.columns = columns.map( (column) => new HdsAdvancedTableColumn({ column, onColumnResize }) diff --git a/packages/components/src/components/hds/advanced-table/types.ts b/packages/components/src/components/hds/advanced-table/types.ts index b4c820c737f..54f19de9b95 100644 --- a/packages/components/src/components/hds/advanced-table/types.ts +++ b/packages/components/src/components/hds/advanced-table/types.ts @@ -114,3 +114,8 @@ export interface HdsAdvancedTableOnSelectionChangeSignature { } export type HdsAdvancedTableModel = Array>; + +export type HdsAdvancedTableColumnResizeCallback = ( + columnKey: string, + newWidth?: string +) => void; diff --git a/showcase/tests/integration/components/hds/advanced-table/index-test.js b/showcase/tests/integration/components/hds/advanced-table/index-test.js index 66512314bae..508c466c368 100644 --- a/showcase/tests/integration/components/hds/advanced-table/index-test.js +++ b/showcase/tests/integration/components/hds/advanced-table/index-test.js @@ -29,6 +29,20 @@ function getTableGridValues(tableElement) { return gridValues; } +async function resetColumnWidth(th) { + const contextMenuToggle = th.querySelector('.hds-dropdown-toggle-icon'); + + await click(contextMenuToggle); + + return click('[data-test-context-option-key="reset-column-width"]'); +} + +async function simulateRightPointerDrag(handle) { + await triggerEvent(handle, 'pointerdown', { clientX: 100 }); + await triggerEvent(handle, 'pointermove', { clientX: 130 }); + await triggerEvent(window, 'pointerup'); +} + // we're using this for multiple tests so we'll declare context once and use it when we need it. const setSortableTableData = (context) => { context.set('model', [ @@ -1359,9 +1373,7 @@ module('Integration | Component | hds/advanced-table/index', function (hooks) { const handle = find('.hds-advanced-table__th-resize-handle'); // get the first handle // Simulate pointer drag to the right (increase width) - await triggerEvent(handle, 'pointerdown', { clientX: 100 }); - await triggerEvent(handle, 'pointermove', { clientX: 130 }); - await triggerEvent(window, 'pointerup'); + await simulateRightPointerDrag(handle); const newGridValues = getTableGridValues(table); assert.notEqual( @@ -1551,10 +1563,7 @@ module('Integration | Component | hds/advanced-table/index', function (hooks) { const handle = find('.hds-advanced-table__th-resize-handle'); const th = handle.closest('.hds-advanced-table__th'); - // Simulate pointer drag to the right (increase width) - await triggerEvent(handle, 'pointerdown', { clientX: 100 }); - await triggerEvent(handle, 'pointermove', { clientX: 130 }); - await triggerEvent(window, 'pointerup'); + await simulateRightPointerDrag(handle); let newGridValues = getTableGridValues(table); @@ -1564,9 +1573,7 @@ module('Integration | Component | hds/advanced-table/index', function (hooks) { 'Grid values changed after drag', ); - const contextMenuToggle = th.querySelector('.hds-dropdown-toggle-icon'); - await click(contextMenuToggle); - await click('[data-test-context-option-key="reset-column-width"]'); + await resetColumnWidth(th); newGridValues = getTableGridValues(table); assert.deepEqual( @@ -1600,10 +1607,42 @@ module('Integration | Component | hds/advanced-table/index', function (hooks) { const handle = find('.hds-advanced-table__th-resize-handle'); // Simulate pointer drag to the right (increase width) - await triggerEvent(handle, 'pointerdown', { clientX: 100 }); - await triggerEvent(handle, 'pointermove', { clientX: 130 }); - await triggerEvent(window, 'pointerup'); + await simulateRightPointerDrag(handle); + + assert.ok(onColumnResizeSpy.calledOnce, 'onColumnResize was called'); + }); + + test('it should call `onColumnResize` when a column width is reset', async function (assert) { + setResizableColumnsTableData(this); + const onColumnResizeSpy = sinon.spy(); + this.set('onColumnResize', onColumnResizeSpy); + + await render(hbs` + + <:body as |B|> + + {{B.data.col1}} + {{B.data.col2}} + + + + `); + + const handle = find('.hds-advanced-table__th-resize-handle'); + + await simulateRightPointerDrag(handle); assert.ok(onColumnResizeSpy.calledOnce, 'onColumnResize was called'); + + await resetColumnWidth(handle.closest('.hds-advanced-table__th')); + assert.ok( + onColumnResizeSpy.calledTwice, + 'onColumnResize was called again after resetting column width', + ); }); }); From 526e702b49ecacb0bf39311f4b68ce2df11d469e Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Sun, 1 Jun 2025 11:02:54 -0400 Subject: [PATCH 50/89] guarding against expandable rows if there are resizable columns --- .../src/components/hds/advanced-table/index.ts | 12 +++++++++++- .../components/hds/advanced-table/models/table.ts | 4 ---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.ts b/packages/components/src/components/hds/advanced-table/index.ts index d67f8e3e872..282bd6f4beb 100644 --- a/packages/components/src/components/hds/advanced-table/index.ts +++ b/packages/components/src/components/hds/advanced-table/index.ts @@ -194,11 +194,21 @@ export default class HdsAdvancedTable extends Component column.isResizable); + const resizableColumnLabels = resizableColumns.map( + (column) => column.label + ); + + if (resizableColumns.length > 0) { assert( 'Cannot have a sticky first column if there are resizable columns.', !hasStickyFirstColumn ); + + assert( + `Cannot have resizable columns if there are nested rows. Resizable columns are ${resizableColumnLabels.toString()}`, + !this._tableModel.hasRowsWithChildren + ); } if (this._tableModel.hasRowsWithChildren) { diff --git a/packages/components/src/components/hds/advanced-table/models/table.ts b/packages/components/src/components/hds/advanced-table/models/table.ts index 919e615c285..25cf4509ebf 100644 --- a/packages/components/src/components/hds/advanced-table/models/table.ts +++ b/packages/components/src/components/hds/advanced-table/models/table.ts @@ -78,10 +78,6 @@ export default class HdsAdvancedTableTableModel { } } - get hasResizableColumns(): boolean { - return this.columns.some((column) => column.isResizable); - } - private _setupColumns({ columns, onColumnResize, From 2410d6c5505f703bc8ebe1b02a4ea1de7ba817ae Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Mon, 2 Jun 2025 17:58:22 -0400 Subject: [PATCH 51/89] respond to changes in the column or data structure --- .../components/hds/advanced-table/index.hbs | 2 +- .../components/hds/advanced-table/index.ts | 17 +++++++- .../hds/advanced-table/models/table.ts | 39 +++++++++++-------- 3 files changed, 38 insertions(+), 20 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.hbs b/packages/components/src/components/hds/advanced-table/index.hbs index 3a95623d504..a9613642ef7 100644 --- a/packages/components/src/components/hds/advanced-table/index.hbs +++ b/packages/components/src/components/hds/advanced-table/index.hbs @@ -5,7 +5,7 @@
{{! Caption }} diff --git a/packages/components/src/components/hds/advanced-table/index.ts b/packages/components/src/components/hds/advanced-table/index.ts index 282bd6f4beb..bef96b44875 100644 --- a/packages/components/src/components/hds/advanced-table/index.ts +++ b/packages/components/src/components/hds/advanced-table/index.ts @@ -11,6 +11,7 @@ import type { ComponentLike } from '@glint/template'; import { guidFor } from '@ember/object/internals'; import { modifier } from 'ember-modifier'; import type Owner from '@ember/owner'; +import { next } from '@ember/runloop'; import HdsAdvancedTableTableModel from './models/table.ts'; @@ -175,6 +176,8 @@ export default class HdsAdvancedTable extends Component { - this._tableModel.updateModel(this.args.model); + private _onUpdateContent = modifier(() => { + const { columns, model } = this.args; + + if (this.lastModel !== model || this.lastColumns !== columns) { + this._tableModel.setupData(this.args.model, this.args.columns); + + // eslint-disable-next-line ember/no-runloop + next(() => { + this.lastModel = model; + this.lastColumns = columns; + }); + } }); private _setUpScrollWrapper = modifier((element: HTMLDivElement) => { diff --git a/packages/components/src/components/hds/advanced-table/models/table.ts b/packages/components/src/components/hds/advanced-table/models/table.ts index 25cf4509ebf..58a8eb5bee6 100644 --- a/packages/components/src/components/hds/advanced-table/models/table.ts +++ b/packages/components/src/components/hds/advanced-table/models/table.ts @@ -40,14 +40,18 @@ function getChildrenCount(rows: HdsAdvancedTableRow[]): number { export default class HdsAdvancedTableTableModel { @tracked columns: HdsAdvancedTableColumn[] = []; + @tracked rows: HdsAdvancedTableRow[] = []; - rows: HdsAdvancedTableRow[] = []; + childrenKey?: string; + onColumnResize?: HdsAdvancedTableColumnResizeCallback; constructor(args: HdsAdvancedTableTableArgs) { const { model, columns, childrenKey, onColumnResize } = args; - this._setupColumns({ columns, onColumnResize }); - this._setupRows({ model, columns, childrenKey }); + this.childrenKey = childrenKey; + this.onColumnResize = onColumnResize; + + this.setupData(model, columns); } get totalRowCount(): number { @@ -78,24 +82,25 @@ export default class HdsAdvancedTableTableModel { } } - private _setupColumns({ - columns, - onColumnResize, - }: Pick & { - onColumnResize?: HdsAdvancedTableColumnResizeCallback; - }) { + @action + setupData( + model: HdsAdvancedTableModel, + columns: HdsAdvancedTableColumnType[] + ) { this.columns = columns.map( - (column) => new HdsAdvancedTableColumn({ column, onColumnResize }) + (column) => + new HdsAdvancedTableColumn({ + column, + onColumnResize: this.onColumnResize, + }) ); - } - private _setupRows({ - model, - columns, - childrenKey, - }: Pick) { this.rows = model.map((row) => { - return new HdsAdvancedTableRow({ ...row, childrenKey, columns }); + return new HdsAdvancedTableRow({ + ...row, + childrenKey: this.childrenKey, + columns, + }); }); } From 886bacdea49bede6429c7e74c2856143efdaedde Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Mon, 2 Jun 2025 18:37:29 -0400 Subject: [PATCH 52/89] addressing PR feedback --- .../src/components/hds/advanced-table/th-context-menu.ts | 2 +- .../components/src/styles/components/advanced-table.scss | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/th-context-menu.ts b/packages/components/src/components/hds/advanced-table/th-context-menu.ts index 9d9abe92478..7c9f1a1b6bc 100644 --- a/packages/components/src/components/hds/advanced-table/th-context-menu.ts +++ b/packages/components/src/components/hds/advanced-table/th-context-menu.ts @@ -44,7 +44,7 @@ export default class HdsAdvancedTableThContextMenu extends Component void diff --git a/packages/components/src/styles/components/advanced-table.scss b/packages/components/src/styles/components/advanced-table.scss index 19358e68c9b..65b6289983c 100644 --- a/packages/components/src/styles/components/advanced-table.scss +++ b/packages/components/src/styles/components/advanced-table.scss @@ -128,7 +128,7 @@ $hds-advanced-table-cell-padding-tall: 22px 16px 21px 16px; // the 1px differenc bottom: 0; z-index: 2; width: 24px; - transform: translateX(50%); + transform: translateX(12.5px); // (width of the handler (24px) + border width (1px)) / 2 == 12.5px cursor: col-resize; &::after { @@ -137,9 +137,6 @@ $hds-advanced-table-cell-padding-tall: 22px 16px 21px 16px; // the 1px differenc right: 0; bottom: 0; z-index: 2; - width: 1px; - background-color: $hds-advanced-table-border-color; - transform: translateX(-11.5px); // (width of the handle (24px) - width of the visual handle (1px)) / 2 == 11.5px content: ""; } From 963921c0b1ffdd618a9a2b08cc0d5fd8ec65442b Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Mon, 2 Jun 2025 21:01:05 -0400 Subject: [PATCH 53/89] fix resize handle height --- .../src/components/hds/advanced-table/th-resize-handle.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/components/hds/advanced-table/th-resize-handle.ts b/packages/components/src/components/hds/advanced-table/th-resize-handle.ts index 57770e2ecd3..77e2d58cb43 100644 --- a/packages/components/src/components/hds/advanced-table/th-resize-handle.ts +++ b/packages/components/src/components/hds/advanced-table/th-resize-handle.ts @@ -87,7 +87,7 @@ export default class HdsAdvancedTableThResizeHandle extends Component Date: Tue, 3 Jun 2025 22:43:32 -0400 Subject: [PATCH 54/89] styling context menu --- .../hds/advanced-table/th-context-menu.hbs | 8 +----- .../src/styles/components/advanced-table.scss | 27 ++++++++++++++++++- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/th-context-menu.hbs b/packages/components/src/components/hds/advanced-table/th-context-menu.hbs index 3d8af2d38df..62fa6a9ec7f 100644 --- a/packages/components/src/components/hds/advanced-table/th-context-menu.hbs +++ b/packages/components/src/components/hds/advanced-table/th-context-menu.hbs @@ -3,13 +3,7 @@ SPDX-License-Identifier: MPL-2.0 }} - + {{#each this._options as |option|}} Date: Tue, 3 Jun 2025 22:57:55 -0400 Subject: [PATCH 55/89] keeps resize handle in view when resizing with the keyboard --- .../hds/advanced-table/th-resize-handle.hbs | 1 + .../hds/advanced-table/th-resize-handle.ts | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs b/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs index e94dc657ea0..b50264e2744 100644 --- a/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs +++ b/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs @@ -9,6 +9,7 @@ aria-valuemax={{@column.pxMaxWidth}} tabindex="0" aria-label="Resize {{@column.label}} column" + {{this.registerHandleElement}} {{on "pointerdown" this.startResize}} {{on "keydown" this.handleKeydown}} {{style height=this.height}} diff --git a/packages/components/src/components/hds/advanced-table/th-resize-handle.ts b/packages/components/src/components/hds/advanced-table/th-resize-handle.ts index 77e2d58cb43..99cccca5a66 100644 --- a/packages/components/src/components/hds/advanced-table/th-resize-handle.ts +++ b/packages/components/src/components/hds/advanced-table/th-resize-handle.ts @@ -7,6 +7,7 @@ import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; import { action } from '@ember/object'; import { assert } from '@ember/debug'; +import { modifier } from 'ember-modifier'; import type HdsAdvancedTableColumn from './models/column'; @@ -67,9 +68,16 @@ export default class HdsAdvancedTableThResizeHandle extends Component void; private boundStopResize: () => void; + private registerHandleElement = modifier( + (element: HdsAdvancedTableThResizeHandleSignature['Element']) => { + this.handleElement = element; + } + ); + constructor( owner: unknown, args: HdsAdvancedTableThResizeHandleSignature['Args'] @@ -135,6 +143,12 @@ export default class HdsAdvancedTableThResizeHandle extends Component Date: Wed, 4 Jun 2025 15:13:40 -0400 Subject: [PATCH 56/89] fixed dropdown toggle styles --- .../hds/advanced-table/th-resize-handle.ts | 2 +- .../src/styles/components/advanced-table.scss | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/th-resize-handle.ts b/packages/components/src/components/hds/advanced-table/th-resize-handle.ts index 99cccca5a66..0ce3d378ec6 100644 --- a/packages/components/src/components/hds/advanced-table/th-resize-handle.ts +++ b/packages/components/src/components/hds/advanced-table/th-resize-handle.ts @@ -95,7 +95,7 @@ export default class HdsAdvancedTableThResizeHandle extends Component Date: Wed, 4 Jun 2025 17:04:58 -0400 Subject: [PATCH 57/89] working on adding resizable columns when working with sticky columns --- .../components/hds/advanced-table/index.ts | 27 +++++++------------ .../mock/app/main/generic-advanced-table.gts | 9 ++++--- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.ts b/packages/components/src/components/hds/advanced-table/index.ts index bef96b44875..fd1ad829569 100644 --- a/packages/components/src/components/hds/advanced-table/index.ts +++ b/packages/components/src/components/hds/advanced-table/index.ts @@ -197,23 +197,6 @@ export default class HdsAdvancedTable extends Component column.isResizable); - const resizableColumnLabels = resizableColumns.map( - (column) => column.label - ); - - if (resizableColumns.length > 0) { - assert( - 'Cannot have a sticky first column if there are resizable columns.', - !hasStickyFirstColumn - ); - - assert( - `Cannot have resizable columns if there are nested rows. Resizable columns are ${resizableColumnLabels.toString()}`, - !this._tableModel.hasRowsWithChildren - ); - } - if (this._tableModel.hasRowsWithChildren) { const sortableColumns = columns.filter((column) => column.isSortable); const sortableColumnLabels = sortableColumns.map( @@ -229,6 +212,16 @@ export default class HdsAdvancedTable extends Component column.isResizable); + const resizableColumnLabels = resizableColumns.map( + (column) => column.label + ); + + assert( + `Cannot have resizable columns if there are nested rows. Resizable columns are ${resizableColumnLabels.toString()}`, + resizableColumns.length === 0 + ); } } diff --git a/showcase/app/components/mock/app/main/generic-advanced-table.gts b/showcase/app/components/mock/app/main/generic-advanced-table.gts index c238ae7606e..ed2b90de879 100644 --- a/showcase/app/components/mock/app/main/generic-advanced-table.gts +++ b/showcase/app/components/mock/app/main/generic-advanced-table.gts @@ -33,19 +33,22 @@ const SAMPLE_COLUMNS = [ label: 'Project name', key: 'project-name', isSortable: true, - width: 'max-content', + isResizable: true, + width: '300px', }, { label: 'Current run ID', key: 'current-run-id', isSortable: true, - width: 'max-content', + isResizable: true, + width: '300px', }, { label: 'Run status', key: 'run-status', isSortable: true, - width: 'max-content', + isResizable: true, + width: '200px', }, { label: 'Current run applied', From e36097268f22867b542dfd802bf1eee5ca8a27ce Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Mon, 9 Jun 2025 16:22:00 -0400 Subject: [PATCH 58/89] responding to PR feedback --- .../src/components/hds/advanced-table/th-context-menu.ts | 6 ------ .../src/components/hds/advanced-table/th-resize-handle.hbs | 2 +- .../components/src/styles/components/advanced-table.scss | 4 ++-- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/th-context-menu.ts b/packages/components/src/components/hds/advanced-table/th-context-menu.ts index 7c9f1a1b6bc..c1ab698a287 100644 --- a/packages/components/src/components/hds/advanced-table/th-context-menu.ts +++ b/packages/components/src/components/hds/advanced-table/th-context-menu.ts @@ -29,12 +29,6 @@ export interface HdsAdvancedTableThContextMenuSignature { export default class HdsAdvancedTableThContextMenu extends Component { originalColumnWidth?: string = this.args.column.width; - get classNames() { - const classes = ['hds-advanced-table__th-context-menu']; - - return classes.join(' '); - } - get _options(): HdsAdvancedTableThContextMenuOption[] { const { column } = this.args; diff --git a/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs b/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs index b50264e2744..022e32d8028 100644 --- a/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs +++ b/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs @@ -1,6 +1,6 @@ {{! template-lint-disable no-pointer-down-event-binding }}
Date: Mon, 9 Jun 2025 16:26:45 -0400 Subject: [PATCH 59/89] responding to PR feedback --- .../components/src/styles/components/advanced-table.scss | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/components/src/styles/components/advanced-table.scss b/packages/components/src/styles/components/advanced-table.scss index fe5c44c012d..e6eb83e4d36 100644 --- a/packages/components/src/styles/components/advanced-table.scss +++ b/packages/components/src/styles/components/advanced-table.scss @@ -22,6 +22,7 @@ $hds-advanced-table-header-height: 48px; $hds-advanced-table-cell-padding-medium: 14px 16px 13px 16px; // the 1px difference is to account for the bottom border $hds-advanced-table-cell-padding-short: 6px 16px 5px 16px; // the 1px difference is to account for the bottom border $hds-advanced-table-cell-padding-tall: 22px 16px 21px 16px; // the 1px difference is to account for the bottom border +$hds-advanced-table-button-size: 24px; // the size of the buttons and dropdown triggers in the header cell // ADVANCED TABLE @@ -261,8 +262,8 @@ $hds-advanced-table-cell-padding-tall: 22px 16px 21px 16px; // the 1px differenc } .hds-advanced-table__th-context-menu .hds-dropdown-toggle-icon { - width: 24px; - height: 24px; + width: $hds-advanced-table-button-size; + height: $hds-advanced-table-button-size; margin: -2px 0; padding: 0; color: var(--token-color-foreground-faint); @@ -300,8 +301,8 @@ $hds-advanced-table-cell-padding-tall: 22px 16px 21px 16px; // the 1px differenc flex: none; align-items: center; justify-content: center; - width: 24px; - height: 24px; + width: $hds-advanced-table-button-size; + height: $hds-advanced-table-button-size; margin: -2px 0; // this is necessary to compensate for the height of the button being 24px while the line height of the text is 20px (the overall height of the cell shoud be 48px and we want to keep the cell's padding as is for consistency with Figma) padding: 0; color: var(--token-color-foreground-faint); From 4bfa65bccb28cce3fd958b137399eac8724c2f71 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Mon, 9 Jun 2025 21:42:35 -0400 Subject: [PATCH 60/89] fixing after rebase --- .../src/components/hds/advanced-table/models/table.ts | 7 ------- .../src/components/hds/advanced-table/th-context-menu.hbs | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/models/table.ts b/packages/components/src/components/hds/advanced-table/models/table.ts index 58a8eb5bee6..7c248fdbe96 100644 --- a/packages/components/src/components/hds/advanced-table/models/table.ts +++ b/packages/components/src/components/hds/advanced-table/models/table.ts @@ -104,13 +104,6 @@ export default class HdsAdvancedTableTableModel { }); } - @action - updateModel(model: HdsAdvancedTableModel) { - this.rows = model.map((row) => { - return new HdsAdvancedTableRow({ ...row, childrenKey: this.childrenKey }); - }); - } - @action openAll() { this.rows.forEach((row) => row.openAll()); diff --git a/packages/components/src/components/hds/advanced-table/th-context-menu.hbs b/packages/components/src/components/hds/advanced-table/th-context-menu.hbs index 62fa6a9ec7f..75d84823cfe 100644 --- a/packages/components/src/components/hds/advanced-table/th-context-menu.hbs +++ b/packages/components/src/components/hds/advanced-table/th-context-menu.hbs @@ -2,7 +2,7 @@ Copyright (c) HashiCorp, Inc. SPDX-License-Identifier: MPL-2.0 }} - + {{#each this._options as |option|}} From 951c7d532ad88a41b222a4acd575ebc0c3da7b05 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Mon, 9 Jun 2025 21:47:21 -0400 Subject: [PATCH 61/89] fixing bad rebase --- .../src/components/hds/advanced-table/th-resize-handle.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs b/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs index 022e32d8028..b50264e2744 100644 --- a/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs +++ b/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs @@ -1,6 +1,6 @@ {{! template-lint-disable no-pointer-down-event-binding }}
Date: Mon, 9 Jun 2025 22:29:09 -0400 Subject: [PATCH 62/89] applying ellipsis style --- .../src/components/hds/advanced-table/index.ts | 4 ++++ .../components/hds/advanced-table/models/table.ts | 4 ++++ .../src/components/hds/advanced-table/th-sort.ts | 4 ---- .../src/components/hds/advanced-table/th.hbs | 5 ++++- .../src/components/hds/advanced-table/th.ts | 4 ---- .../src/styles/components/advanced-table.scss | 12 ++++++++++++ 6 files changed, 24 insertions(+), 9 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.ts b/packages/components/src/components/hds/advanced-table/index.ts index fd1ad829569..14ad358f14b 100644 --- a/packages/components/src/components/hds/advanced-table/index.ts +++ b/packages/components/src/components/hds/advanced-table/index.ts @@ -401,6 +401,10 @@ export default class HdsAdvancedTable extends Component column.isResizable); + } + get hasRowsWithChildren(): boolean { return this.rows.some((row) => row.hasChildren); } diff --git a/packages/components/src/components/hds/advanced-table/th-sort.ts b/packages/components/src/components/hds/advanced-table/th-sort.ts index 8b21b68706b..6e8f16ff6f1 100644 --- a/packages/components/src/components/hds/advanced-table/th-sort.ts +++ b/packages/components/src/components/hds/advanced-table/th-sort.ts @@ -120,10 +120,6 @@ export default class HdsAdvancedTableThSort extends Component {{/if}} - {{yield}} + {{yield}}
{{/if}} {{/if}} diff --git a/packages/components/src/components/hds/advanced-table/th.ts b/packages/components/src/components/hds/advanced-table/th.ts index c474ebf0973..bd10d1c3aeb 100644 --- a/packages/components/src/components/hds/advanced-table/th.ts +++ b/packages/components/src/components/hds/advanced-table/th.ts @@ -134,10 +134,6 @@ export default class HdsAdvancedTableTh extends Component Date: Tue, 10 Jun 2025 14:13:09 -0400 Subject: [PATCH 63/89] fixed the focus styles --- .../components/src/styles/components/advanced-table.scss | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/components/src/styles/components/advanced-table.scss b/packages/components/src/styles/components/advanced-table.scss index f2e61d3810f..f0d93db91b9 100644 --- a/packages/components/src/styles/components/advanced-table.scss +++ b/packages/components/src/styles/components/advanced-table.scss @@ -292,9 +292,8 @@ $hds-advanced-table-button-size: 24px; // the size of the buttons and dropdown t } &:focus, - &.mock-focus { - border-color: transparent; - + &.mock-focus, + .hds-dropdown-toggle-icon--is-open { &::before { top: 0; right: 0; @@ -302,10 +301,9 @@ $hds-advanced-table-button-size: 24px; // the size of the buttons and dropdown t left: 0; border: none; border-radius: var(--token-border-radius-x-small); + box-shadow: var(--token-focus-ring-action-box-shadow); } } - - @include hds-focus-ring-with-pseudo-element($radius: inherit); } .hds-advanced-table__th-button { From 9e59580761434107e5e76b147de8d249458973cd Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Tue, 10 Jun 2025 21:23:42 -0400 Subject: [PATCH 64/89] move sorting logic to the model --- .../components/hds/advanced-table/index.hbs | 11 +- .../components/hds/advanced-table/index.ts | 79 +++-------- .../hds/advanced-table/models/table.ts | 130 +++++++++++++++--- 3 files changed, 134 insertions(+), 86 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.hbs b/packages/components/src/components/hds/advanced-table/index.hbs index a9613642ef7..c7f468042b5 100644 --- a/packages/components/src/components/hds/advanced-table/index.hbs +++ b/packages/components/src/components/hds/advanced-table/index.hbs @@ -30,8 +30,8 @@
{ - @tracked private _sortBy = this.args.sortBy ?? undefined; - @tracked private _sortOrder = - this.args.sortOrder || HdsAdvancedTableThSortOrderValues.Asc; @tracked private _selectAllCheckbox?: HdsFormCheckboxBaseSignature['Element'] = undefined; @@ -176,25 +170,29 @@ export default class HdsAdvancedTable extends Component { - // get the current column - const currentColumn = this.args?.columns?.find( - (column) => column.key === this._sortBy - ); - - if ( - // check if there is a custom sorting function associated with the current `sortBy` column (we assume the column has `isSortable`) - currentColumn?.sortingFunction && - typeof currentColumn.sortingFunction === 'function' - ) { - return currentColumn.sortingFunction; - } else { - // otherwise fallback to the default format "sortBy:sortOrder" - return `${this._sortBy}:${this._sortOrder}`; - } - } - get identityKey(): string | undefined { // we have to provide a way for the consumer to pass undefined because Ember tries to interpret undefined as missing an arg and therefore falls back to the default if (this.args.identityKey === 'none') { @@ -267,11 +247,13 @@ export default class HdsAdvancedTable extends Component { - const { columns, model } = this.args; + const { columns, model, sortBy, sortOrder } = this.args; - if (this.lastModel !== model || this.lastColumns !== columns) { - this._tableModel.setupData(this.args.model, this.args.columns); - - // eslint-disable-next-line ember/no-runloop - next(() => { - this.lastModel = model; - this.lastColumns = columns; - }); - } + this._tableModel.setupData({ columns, model, sortBy, sortOrder }); }); private _setUpScrollWrapper = modifier((element: HTMLDivElement) => { @@ -526,27 +500,6 @@ export default class HdsAdvancedTable extends Component; function getVisibleRows(rows: HdsAdvancedTableRow[]): HdsAdvancedTableRow[] { return rows.reduce((acc, row) => { @@ -41,25 +45,91 @@ function getChildrenCount(rows: HdsAdvancedTableRow[]): number { export default class HdsAdvancedTableTableModel { @tracked columns: HdsAdvancedTableColumn[] = []; @tracked rows: HdsAdvancedTableRow[] = []; + @tracked sortBy: HdsAdvancedTableTableArgs['sortBy'] = undefined; + @tracked sortOrder: HdsAdvancedTableTableArgs['sortOrder'] = + HdsAdvancedTableThSortOrderValues.Asc; - childrenKey?: string; - onColumnResize?: HdsAdvancedTableColumnResizeCallback; + childrenKey?: HdsAdvancedTableTableArgs['childrenKey']; + onColumnResize?: HdsAdvancedTableTableArgs['onColumnResize']; + onSort?: HdsAdvancedTableSignature['Args']['onSort']; constructor(args: HdsAdvancedTableTableArgs) { - const { model, columns, childrenKey, onColumnResize } = args; + const { + model, + columns, + childrenKey, + sortBy, + sortOrder, + onColumnResize, + onSort, + } = args; this.childrenKey = childrenKey; this.onColumnResize = onColumnResize; + this.onSort = onSort; + + this.setupData({ model, columns, sortBy, sortOrder }); + } + + get sortCriteria(): string | HdsAdvancedTableSortingFunction { + // get the current column + const currentColumn = this.columns.find( + (column) => column.key === this.sortBy + ); + + if ( + // check if there is a custom sorting function associated with the current `sortBy` column (we assume the column has `isSortable`) + currentColumn?.sortingFunction && + typeof currentColumn.sortingFunction === 'function' + ) { + return currentColumn.sortingFunction; + } else { + // otherwise fallback to the default format "sortBy:sortOrder" + return `${this.sortBy}:${this.sortOrder}`; + } + } + + get sortedRows(): HdsAdvancedTableRow[] { + const criteria = this.sortCriteria; + const rows = this.rows; + + if (rows.length <= 1 || criteria === undefined) { + return rows; + } + + if (typeof criteria === 'function') { + // Use custom sort function + return [...rows].sort(criteria); + } else { + // Parse the criteria string format "sortBy:sortOrder" + const [sortBy, sortOrder] = criteria.split(':'); - this.setupData(model, columns); + if (!sortBy) { + return rows; + } + + return [...rows].sort((a, b) => { + const valueA = a[sortBy] as string | number | boolean; + const valueB = b[sortBy] as string | number | boolean; + + if (valueA < valueB) { + return sortOrder === 'asc' ? -1 : 1; + } + if (valueA > valueB) { + return sortOrder === 'asc' ? 1 : -1; + } + + return 0; + }); + } } get totalRowCount(): number { - return getChildrenCount(this.rows); + return getChildrenCount(this.sortedRows); } get flattenedVisibleRows(): HdsAdvancedTableRow[] { - return getVisibleRows(this.rows); + return getVisibleRows(this.sortedRows); } get lastVisibleRow(): HdsAdvancedTableRow | undefined { @@ -88,9 +158,16 @@ export default class HdsAdvancedTableTableModel { @action setupData( - model: HdsAdvancedTableModel, - columns: HdsAdvancedTableColumnType[] + args: Pick< + HdsAdvancedTableTableArgs, + 'model' | 'columns' | 'sortBy' | 'sortOrder' + > ) { + const { model, columns, sortBy, sortOrder } = args; + + this.sortBy = sortBy; + this.sortOrder = sortOrder ?? HdsAdvancedTableThSortOrderValues.Asc; + this.columns = columns.map( (column) => new HdsAdvancedTableColumn({ @@ -108,6 +185,25 @@ export default class HdsAdvancedTableTableModel { }); } + @action + setSortBy(column: string): void { + if (this.sortBy === column) { + // check to see if the column is already sorted and invert the sort order if so + this.sortOrder = + this.sortOrder === HdsAdvancedTableThSortOrderValues.Asc + ? HdsAdvancedTableThSortOrderValues.Desc + : HdsAdvancedTableThSortOrderValues.Asc; + } else { + // otherwise, set the sort order to ascending + this.sortBy = column; + this.sortOrder = HdsAdvancedTableThSortOrderValues.Asc; + } + + if (typeof this.onSort === 'function') { + this.onSort(this.sortBy, this.sortOrder); + } + } + @action openAll() { this.rows.forEach((row) => row.openAll()); From 80c99c5bb02cdb9f7f394f7c2559beac01ec5c04 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Tue, 10 Jun 2025 21:35:44 -0400 Subject: [PATCH 65/89] fixing modifier --- .../src/components/hds/advanced-table/index.hbs | 2 +- .../src/components/hds/advanced-table/index.ts | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.hbs b/packages/components/src/components/hds/advanced-table/index.hbs index c7f468042b5..289a97b5ab1 100644 --- a/packages/components/src/components/hds/advanced-table/index.hbs +++ b/packages/components/src/components/hds/advanced-table/index.hbs @@ -5,7 +5,7 @@
{{! Caption }} diff --git a/packages/components/src/components/hds/advanced-table/index.ts b/packages/components/src/components/hds/advanced-table/index.ts index 968784073e3..e0cb5b58737 100644 --- a/packages/components/src/components/hds/advanced-table/index.ts +++ b/packages/components/src/components/hds/advanced-table/index.ts @@ -390,12 +390,6 @@ export default class HdsAdvancedTable extends Component { - const { columns, model, sortBy, sortOrder } = this.args; - - this._tableModel.setupData({ columns, model, sortBy, sortOrder }); - }); - private _setUpScrollWrapper = modifier((element: HTMLDivElement) => { this._scrollHandler = () => { // 6px as a buffer so the shadow doesn't appear over the border radius on the edge of the table @@ -533,6 +527,13 @@ export default class HdsAdvancedTable extends Component { From b8d921e6cbbbe86b0e093bbc9918904018f8668b Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Wed, 11 Jun 2025 12:00:35 -0400 Subject: [PATCH 66/89] added further showcase examples --- .../src/styles/components/advanced-table.scss | 8 +- .../templates/components/advanced-table.hbs | 117 ++++++++---------- 2 files changed, 60 insertions(+), 65 deletions(-) diff --git a/packages/components/src/styles/components/advanced-table.scss b/packages/components/src/styles/components/advanced-table.scss index f0d93db91b9..f5c0ae1362f 100644 --- a/packages/components/src/styles/components/advanced-table.scss +++ b/packages/components/src/styles/components/advanced-table.scss @@ -143,19 +143,21 @@ $hds-advanced-table-button-size: 24px; // the size of the buttons and dropdown t } &:focus::after, - &.mock-focus, + &.mock-focus::after, &:hover::after, + &.mock-hover::after, &.hds-advanced-table__th-resize-handle--resizing::after { width: 3px; transform: translateX(-10.5px); // (width of the handle (24px) - width of the visual handle (3px)) / 2 == 10.5px } - &:hover::after { + &:hover::after, + &.mock-hover::after { background-color: $hds-advanced-table-border-resize-handle-hover-color; } &:focus::after, - &.mock-focus, + &.mock-focus::after, &.hds-advanced-table__th-resize-handle--resizing::after { background-color: var(--token-color-palette-blue-300); } diff --git a/showcase/app/templates/components/advanced-table.hbs b/showcase/app/templates/components/advanced-table.hbs index 993e627f087..72e0dd8ea5b 100644 --- a/showcase/app/templates/components/advanced-table.hbs +++ b/showcase/app/templates/components/advanced-table.hbs @@ -1664,68 +1664,6 @@ - ThResizeHandle - - - -
-
-
-
- -
- - Lorem - -
- -
-
-
- -
- - Ipsum - -
- -
-
-
- -
- - Dolor - -
- -
-
- Sit -
-
-
-
-
- - - Td @@ -2061,4 +1999,59 @@ {{/each}} + + + ThContextMenu + + + {{#each @model.STATES as |state|}} + + + + {{/each}} + + + + + ThResizeHandle + + + +
+
+
+ {{#each @model.STATES as |state|}} +
+ +
+ + {{capitalize state}} + +
+ +
+
+ {{/each}} + +
+
+
+
+
+
\ No newline at end of file From 504e4bf7a9a84c1a5aeb2faabcbaed0216e792f8 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Wed, 11 Jun 2025 16:11:33 -0400 Subject: [PATCH 67/89] setting resizable at table level works --- .../components/hds/advanced-table/index.hbs | 2 ++ .../components/hds/advanced-table/index.ts | 20 +++++++++---------- .../hds/advanced-table/models/column.ts | 16 --------------- .../hds/advanced-table/models/table.ts | 8 ++++---- .../hds/advanced-table/th-context-menu.ts | 5 +++-- .../hds/advanced-table/th-resize-handle.ts | 6 +++--- .../components/hds/advanced-table/th-sort.hbs | 3 ++- .../components/hds/advanced-table/th-sort.ts | 10 ++++------ .../src/components/hds/advanced-table/th.hbs | 3 ++- .../src/components/hds/advanced-table/th.ts | 9 +++------ .../components/hds/advanced-table/types.ts | 1 - .../templates/components/advanced-table.hbs | 2 +- 12 files changed, 33 insertions(+), 52 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.hbs b/packages/components/src/components/hds/advanced-table/index.hbs index 289a97b5ab1..08678a5dde2 100644 --- a/packages/components/src/components/hds/advanced-table/index.hbs +++ b/packages/components/src/components/hds/advanced-table/index.hbs @@ -48,6 +48,7 @@ @onClickSort={{if column.key (fn this._tableModel.setSortBy column.key)}} @align={{column.align}} @tooltip={{column.tooltip}} + @hasResizableColumns={{@hasResizableColumns}} @isLastColumn={{eq index (sub this._tableModel.columns.length 1)}} @isStickyColumn={{if (and (eq index 0) @hasStickyFirstColumn) true}} @isStickyColumnPinned={{this.isStickyColumnPinned}} @@ -61,6 +62,7 @@ @align={{column.align}} @column={{column}} @hasExpandAllButton={{this._tableModel.hasRowsWithChildren}} + @hasResizableColumns={{@hasResizableColumns}} @isExpanded={{this._tableModel.expandState}} @isExpandable={{column.isExpandable}} @isLastColumn={{eq index (sub this._tableModel.columns.length 1)}} diff --git a/packages/components/src/components/hds/advanced-table/index.ts b/packages/components/src/components/hds/advanced-table/index.ts index e0cb5b58737..c6a5ebdf69b 100644 --- a/packages/components/src/components/hds/advanced-table/index.ts +++ b/packages/components/src/components/hds/advanced-table/index.ts @@ -118,21 +118,22 @@ export interface HdsAdvancedTableSignature { isSelectable?: boolean; isStriped?: boolean; model: HdsAdvancedTableModel; - onSelectionChange?: ( - selection: HdsAdvancedTableOnSelectionChangeSignature - ) => void; - onSort?: (sortBy: string, sortOrder: HdsAdvancedTableThSortOrder) => void; selectionAriaLabelSuffix?: string; sortBy?: string; selectableColumnKey?: string; sortedMessageText?: string; sortOrder?: HdsAdvancedTableThSortOrder; valign?: HdsAdvancedTableVerticalAlignment; + hasResizableColumns?: boolean; hasStickyHeader?: boolean; hasStickyFirstColumn?: boolean; childrenKey?: string; maxHeight?: string; onColumnResize?: (columnKey: string, newWidth?: string) => void; + onSelectionChange?: ( + selection: HdsAdvancedTableOnSelectionChangeSignature + ) => void; + onSort?: (sortBy: string, sortOrder: HdsAdvancedTableThSortOrder) => void; }; Blocks: { body?: [ @@ -178,6 +179,7 @@ export default class HdsAdvancedTable extends Component column.isResizable); - const resizableColumnLabels = resizableColumns.map( - (column) => column.label - ); - assert( - `Cannot have resizable columns if there are nested rows. Resizable columns are ${resizableColumnLabels.toString()}`, - resizableColumns.length === 0 + `Cannot have resizable columns if there are nested rows.`, + !hasResizableColumns ); } } diff --git a/packages/components/src/components/hds/advanced-table/models/column.ts b/packages/components/src/components/hds/advanced-table/models/column.ts index 80c6d387f5e..8c08dd3e79a 100644 --- a/packages/components/src/components/hds/advanced-table/models/column.ts +++ b/packages/components/src/components/hds/advanced-table/models/column.ts @@ -1,6 +1,5 @@ import { tracked } from '@glimmer/tracking'; import { action } from '@ember/object'; -import { assert } from '@ember/debug'; import type { HdsAdvancedTableColumn as HdsAdvancedTableColumnType } from '../types'; import type { @@ -25,7 +24,6 @@ export default class HdsAdvancedTableColumn { @tracked align?: HdsAdvancedTableHorizontalAlignment = 'left'; @tracked isExpandable?: boolean = false; @tracked isReorderable?: boolean = false; - @tracked isResizable?: boolean = false; @tracked isSortable?: boolean = false; @tracked isVisuallyHidden?: boolean = false; @tracked key?: string = undefined; @@ -75,7 +73,6 @@ export default class HdsAdvancedTableColumn { this.key = column.key; this.tooltip = column.tooltip; this._setWidthValues(column); - this._setResizableValues(column); this.sortingFunction = column.sortingFunction; // set resize callback @@ -101,19 +98,6 @@ export default class HdsAdvancedTableColumn { this.maxWidth = maxWidth ?? '800px'; } - private _setResizableValues({ - isResizable, - }: HdsAdvancedTableColumnType): void { - if (isResizable) { - assert( - 'width must be set a px value to use isResizable', - isPxSize(this.width) - ); - } - - this.isResizable = isResizable ?? false; - } - @action setPxWidth(newPxWidth: number): void { const pxMinWidth = this.pxMinWidth ?? 1; diff --git a/packages/components/src/components/hds/advanced-table/models/table.ts b/packages/components/src/components/hds/advanced-table/models/table.ts index 843f7d8a8d7..efc4c91240e 100644 --- a/packages/components/src/components/hds/advanced-table/models/table.ts +++ b/packages/components/src/components/hds/advanced-table/models/table.ts @@ -20,6 +20,7 @@ type HdsAdvancedTableTableArgs = Pick< | 'model' | 'columns' | 'childrenKey' + | 'hasResizableColumns' | 'sortBy' | 'sortOrder' | 'onColumnResize' @@ -50,6 +51,7 @@ export default class HdsAdvancedTableTableModel { HdsAdvancedTableThSortOrderValues.Asc; childrenKey?: HdsAdvancedTableTableArgs['childrenKey']; + hasResizableColumns?: HdsAdvancedTableTableArgs['hasResizableColumns']; onColumnResize?: HdsAdvancedTableTableArgs['onColumnResize']; onSort?: HdsAdvancedTableSignature['Args']['onSort']; @@ -58,6 +60,7 @@ export default class HdsAdvancedTableTableModel { model, columns, childrenKey, + hasResizableColumns, sortBy, sortOrder, onColumnResize, @@ -65,6 +68,7 @@ export default class HdsAdvancedTableTableModel { } = args; this.childrenKey = childrenKey; + this.hasResizableColumns = hasResizableColumns; this.onColumnResize = onColumnResize; this.onSort = onSort; @@ -136,10 +140,6 @@ export default class HdsAdvancedTableTableModel { return this.flattenedVisibleRows[this.flattenedVisibleRows.length - 1]; } - get hasResizableColumns(): boolean { - return this.columns.some((column) => column.isResizable); - } - get hasRowsWithChildren(): boolean { return this.rows.some((row) => row.hasChildren); } diff --git a/packages/components/src/components/hds/advanced-table/th-context-menu.ts b/packages/components/src/components/hds/advanced-table/th-context-menu.ts index c1ab698a287..df29b182148 100644 --- a/packages/components/src/components/hds/advanced-table/th-context-menu.ts +++ b/packages/components/src/components/hds/advanced-table/th-context-menu.ts @@ -22,6 +22,7 @@ interface HdsAdvancedTableThContextMenuOption { export interface HdsAdvancedTableThContextMenuSignature { Args: { column: HdsAdvancedTableColumn; + isResizable?: boolean; }; Element: HdsDropdownSignature['Element']; } @@ -30,11 +31,11 @@ export default class HdsAdvancedTableThContextMenu extends Component {{/if}} - {{#if (and @column.isResizable (not @isLastColumn))}} + {{#if (and @hasResizableColumns (not @isLastColumn))}} {{/if}} diff --git a/packages/components/src/components/hds/advanced-table/th-sort.ts b/packages/components/src/components/hds/advanced-table/th-sort.ts index 6e8f16ff6f1..8b86edf2efa 100644 --- a/packages/components/src/components/hds/advanced-table/th-sort.ts +++ b/packages/components/src/components/hds/advanced-table/th-sort.ts @@ -25,6 +25,7 @@ import type { import type { HdsAdvancedTableThButtonSortSignature } from './th-button-sort.ts'; import { onFocusTrapDeactivate } from '../../../modifiers/hds-advanced-table-cell/dom-management.ts'; import type { HdsAdvancedTableThSignature } from './th.ts'; +import type { HdsAdvancedTableSignature } from './index.ts'; export const ALIGNMENTS: string[] = Object.values( HdsAdvancedTableHorizontalAlignmentValues @@ -35,6 +36,7 @@ export interface HdsAdvancedTableThSortSignature { Args: { column?: HdsAdvancedTableThSignature['Args']['column']; align?: HdsAdvancedTableHorizontalAlignment; + hasResizableColumns: HdsAdvancedTableSignature['Args']['hasResizableColumns']; onClickSort?: HdsAdvancedTableThButtonSortSignature['Args']['onClick']; sortOrder?: HdsAdvancedTableThSortOrder; tooltip?: string; @@ -95,13 +97,9 @@ export default class HdsAdvancedTableThSort extends Component {{/if}} - {{#if (and @column.isResizable (not @isLastColumn))}} + {{#if (and @hasResizableColumns (not @isLastColumn))}} {{/if}} diff --git a/packages/components/src/components/hds/advanced-table/th.ts b/packages/components/src/components/hds/advanced-table/th.ts index bd10d1c3aeb..3022b0d9222 100644 --- a/packages/components/src/components/hds/advanced-table/th.ts +++ b/packages/components/src/components/hds/advanced-table/th.ts @@ -33,6 +33,7 @@ export interface HdsAdvancedTableThSignature { colspan?: number; depth?: number; hasExpandAllButton?: boolean; + hasResizableColumns?: boolean; isExpanded?: HdsAdvancedTableExpandState; isExpandable?: boolean; isLastColumn?: boolean; @@ -138,13 +139,9 @@ export default class HdsAdvancedTableTh extends Component; diff --git a/showcase/app/templates/components/advanced-table.hbs b/showcase/app/templates/components/advanced-table.hbs index 72e0dd8ea5b..4b9ffb6a5d8 100644 --- a/showcase/app/templates/components/advanced-table.hbs +++ b/showcase/app/templates/components/advanced-table.hbs @@ -545,7 +545,7 @@ Resizable columns - + <:body as |B|> {{B.data.artist}} From fc075672c6fd4f76b6c6fce59f3df11ff57de1f9 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Wed, 11 Jun 2025 16:25:33 -0400 Subject: [PATCH 68/89] removing width req --- .../hds/advanced-table/th-resize-handle.ts | 19 ++++--------------- .../controllers/components/advanced-table.js | 7 ------- 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/th-resize-handle.ts b/packages/components/src/components/hds/advanced-table/th-resize-handle.ts index 0445401236d..25dc7d94c07 100644 --- a/packages/components/src/components/hds/advanced-table/th-resize-handle.ts +++ b/packages/components/src/components/hds/advanced-table/th-resize-handle.ts @@ -6,7 +6,6 @@ import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; import { action } from '@ember/object'; -import { assert } from '@ember/debug'; import { modifier } from 'ember-modifier'; import type HdsAdvancedTableColumn from './models/column'; @@ -121,11 +120,6 @@ export default class HdsAdvancedTableThResizeHandle extends Component Date: Wed, 11 Jun 2025 17:52:30 -0400 Subject: [PATCH 69/89] improvements to column resizing --- .../src/components/hds/advanced-table/index.hbs | 2 ++ .../src/components/hds/advanced-table/index.ts | 15 +++++++++++++++ .../hds/advanced-table/models/column.ts | 4 ++-- .../app/controllers/components/advanced-table.js | 1 + .../app/templates/components/advanced-table.hbs | 6 +++++- 5 files changed, 25 insertions(+), 3 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.hbs b/packages/components/src/components/hds/advanced-table/index.hbs index 08678a5dde2..4dc6b39287a 100644 --- a/packages/components/src/components/hds/advanced-table/index.hbs +++ b/packages/components/src/components/hds/advanced-table/index.hbs @@ -54,6 +54,7 @@ @isStickyColumnPinned={{this.isStickyColumnPinned}} @nextColumn={{get this._tableModel.columns (add index 1)}} @tableHeight={{this._tableHeight}} + {{this._setColumnWidth column}} > {{column.label}} @@ -73,6 +74,7 @@ @tableHeight={{this._tableHeight}} @tooltip={{column.tooltip}} @onClickToggle={{this._tableModel.toggleAll}} + {{this._setColumnWidth column}} > {{column.label}} diff --git a/packages/components/src/components/hds/advanced-table/index.ts b/packages/components/src/components/hds/advanced-table/index.ts index c6a5ebdf69b..b4cca8a1343 100644 --- a/packages/components/src/components/hds/advanced-table/index.ts +++ b/packages/components/src/components/hds/advanced-table/index.ts @@ -11,6 +11,7 @@ import type { ComponentLike } from '@glint/template'; import { guidFor } from '@ember/object/internals'; import { modifier } from 'ember-modifier'; import type Owner from '@ember/owner'; +import { schedule } from '@ember/runloop'; import HdsAdvancedTableTableModel from './models/table.ts'; @@ -29,6 +30,7 @@ import type { HdsAdvancedTableModel, HdsAdvancedTableExpandState, } from './types.ts'; +import type HdsAdvancedTableColumnType from './models/column.ts'; import type { HdsFormCheckboxBaseSignature } from '../form/checkbox/base.ts'; import type { HdsAdvancedTableTdSignature } from './td.ts'; import type { HdsAdvancedTableThSignature } from './th.ts'; @@ -388,6 +390,19 @@ export default class HdsAdvancedTable extends Component { + // eslint-disable-next-line ember/no-runloop + schedule('afterRender', () => { + const width = element.offsetWidth; + + if (column.width === undefined) { + column.setPxWidth(width); + } + }); + } + ); + private _setUpScrollWrapper = modifier((element: HTMLDivElement) => { this._scrollHandler = () => { // 6px as a buffer so the shadow doesn't appear over the border radius on the edge of the table diff --git a/packages/components/src/components/hds/advanced-table/models/column.ts b/packages/components/src/components/hds/advanced-table/models/column.ts index 8c08dd3e79a..597418c62c5 100644 --- a/packages/components/src/components/hds/advanced-table/models/column.ts +++ b/packages/components/src/components/hds/advanced-table/models/column.ts @@ -27,8 +27,8 @@ export default class HdsAdvancedTableColumn { @tracked isSortable?: boolean = false; @tracked isVisuallyHidden?: boolean = false; @tracked key?: string = undefined; - @tracked minWidth?: `${number}px` = undefined; - @tracked maxWidth?: `${number}px` = undefined; + @tracked minWidth?: `${number}px` = '150px'; + @tracked maxWidth?: `${number}px` = '800px'; @tracked tooltip?: string = undefined; @tracked width?: string = undefined; diff --git a/showcase/app/controllers/components/advanced-table.js b/showcase/app/controllers/components/advanced-table.js index 7973e420e0a..1036b7910eb 100644 --- a/showcase/app/controllers/components/advanced-table.js +++ b/showcase/app/controllers/components/advanced-table.js @@ -369,6 +369,7 @@ export default class ComponentsTableController extends Controller { key: 'album', label: 'Album', tooltip: 'More information.', + width: '350px', }, { key: 'year', diff --git a/showcase/app/templates/components/advanced-table.hbs b/showcase/app/templates/components/advanced-table.hbs index 4b9ffb6a5d8..0cf1da9b1a6 100644 --- a/showcase/app/templates/components/advanced-table.hbs +++ b/showcase/app/templates/components/advanced-table.hbs @@ -571,7 +571,11 @@ Resizable columns with sorting - + <:body as |B|> {{B.data.artist}} From bdeba952bdaa4cfe51afbfc0059f19c88a795f3c Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Wed, 11 Jun 2025 19:18:17 -0400 Subject: [PATCH 70/89] reset all column widths at once --- .../components/hds/advanced-table/index.hbs | 2 ++ .../components/hds/advanced-table/index.ts | 1 + .../hds/advanced-table/models/column.ts | 29 ++++++++++++------- .../hds/advanced-table/models/table.ts | 8 +++++ .../hds/advanced-table/th-context-menu.ts | 15 +++++----- .../components/hds/advanced-table/th-sort.hbs | 6 +++- .../components/hds/advanced-table/th-sort.ts | 1 + .../src/components/hds/advanced-table/th.hbs | 6 +++- .../src/components/hds/advanced-table/th.ts | 1 + 9 files changed, 49 insertions(+), 20 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.hbs b/packages/components/src/components/hds/advanced-table/index.hbs index 4dc6b39287a..f7230bfe6f8 100644 --- a/packages/components/src/components/hds/advanced-table/index.hbs +++ b/packages/components/src/components/hds/advanced-table/index.hbs @@ -54,6 +54,7 @@ @isStickyColumnPinned={{this.isStickyColumnPinned}} @nextColumn={{get this._tableModel.columns (add index 1)}} @tableHeight={{this._tableHeight}} + @onRestoreColumnWidths={{this._tableModel.restoreColumnWidths}} {{this._setColumnWidth column}} > {{column.label}} @@ -74,6 +75,7 @@ @tableHeight={{this._tableHeight}} @tooltip={{column.tooltip}} @onClickToggle={{this._tableModel.toggleAll}} + @onRestoreColumnWidths={{this._tableModel.restoreColumnWidths}} {{this._setColumnWidth column}} > {{column.label}} diff --git a/packages/components/src/components/hds/advanced-table/index.ts b/packages/components/src/components/hds/advanced-table/index.ts index b4cca8a1343..d053b4247f2 100644 --- a/packages/components/src/components/hds/advanced-table/index.ts +++ b/packages/components/src/components/hds/advanced-table/index.ts @@ -398,6 +398,7 @@ export default class HdsAdvancedTable extends Component number = undefined; - private _originalWidth?: string = undefined; private _onColumnResize?: HdsAdvancedTableColumnResizeCallback; + table: HdsAdvancedTableModel; + get pxWidth(): number | undefined { if (isPxSize(this.width)) { return pxToNumber(this.width!); @@ -60,9 +66,13 @@ export default class HdsAdvancedTableColumn { constructor(args: { column: HdsAdvancedTableColumnType; + table: HdsAdvancedTableModel; onColumnResize?: HdsAdvancedTableColumnResizeCallback; }) { - const { column, onColumnResize } = args; + const { column, table, onColumnResize } = args; + + // set reference to table model + this.table = table; // set column properties this.label = column.label; @@ -91,11 +101,10 @@ export default class HdsAdvancedTableColumn { this.width = width; // capture the width at the time of instantiation so it can be restored - this._originalWidth = width; + this.originalWidth = width; - // TODO: discuss sensible defaults for minWidth and maxWidth - this.minWidth = minWidth ?? '150px'; - this.maxWidth = maxWidth ?? '800px'; + this.minWidth = minWidth ?? DEFAULT_MIN_WIDTH; + this.maxWidth = maxWidth ?? DEFAULT_MAX_WIDTH; } @action @@ -117,7 +126,7 @@ export default class HdsAdvancedTableColumn { @action restoreWidth(): void { - this.width = this._originalWidth; + this.width = this.originalWidth; if (this.key === undefined) { return; diff --git a/packages/components/src/components/hds/advanced-table/models/table.ts b/packages/components/src/components/hds/advanced-table/models/table.ts index efc4c91240e..5857c46e979 100644 --- a/packages/components/src/components/hds/advanced-table/models/table.ts +++ b/packages/components/src/components/hds/advanced-table/models/table.ts @@ -172,6 +172,7 @@ export default class HdsAdvancedTableTableModel { (column) => new HdsAdvancedTableColumn({ column, + table: this, onColumnResize: this.onColumnResize, }) ); @@ -185,6 +186,13 @@ export default class HdsAdvancedTableTableModel { }); } + @action + restoreColumnWidths(): void { + this.columns.forEach((column) => { + column.width = column.originalWidth; + }); + } + @action setSortBy(column: string): void { if (this.sortBy === column) { diff --git a/packages/components/src/components/hds/advanced-table/th-context-menu.ts b/packages/components/src/components/hds/advanced-table/th-context-menu.ts index df29b182148..6bd73385c20 100644 --- a/packages/components/src/components/hds/advanced-table/th-context-menu.ts +++ b/packages/components/src/components/hds/advanced-table/th-context-menu.ts @@ -22,29 +22,28 @@ interface HdsAdvancedTableThContextMenuOption { export interface HdsAdvancedTableThContextMenuSignature { Args: { column: HdsAdvancedTableColumn; - isResizable?: boolean; + hasResizableColumns?: boolean; + onRestoreColumnWidths?: () => void; }; Element: HdsDropdownSignature['Element']; } export default class HdsAdvancedTableThContextMenu extends Component { - originalColumnWidth?: string = this.args.column.width; - get _options(): HdsAdvancedTableThContextMenuOption[] { - const { isResizable } = this.args; + const { hasResizableColumns } = this.args; const options: HdsAdvancedTableThContextMenuOption[] = []; - if (isResizable) { + if (hasResizableColumns) { options.push({ key: 'reset-column-width', - label: 'Reset column width', + label: 'Reset column widths', icon: 'rotate-ccw', action: ( - column: HdsAdvancedTableColumn, + _column: HdsAdvancedTableColumn, dropdownCloseCallback?: () => void ): void => { - column.restoreWidth(); + this.args.onRestoreColumnWidths?.(); dropdownCloseCallback?.(); }, }); diff --git a/packages/components/src/components/hds/advanced-table/th-sort.hbs b/packages/components/src/components/hds/advanced-table/th-sort.hbs index dccce67ace5..4d7d3ce9bdb 100644 --- a/packages/components/src/components/hds/advanced-table/th-sort.hbs +++ b/packages/components/src/components/hds/advanced-table/th-sort.hbs @@ -45,7 +45,11 @@ {{#if @column}} {{#if this.showContextMenu}} - + {{/if}} {{#if (and @hasResizableColumns (not @isLastColumn))}} diff --git a/packages/components/src/components/hds/advanced-table/th-sort.ts b/packages/components/src/components/hds/advanced-table/th-sort.ts index 8b86edf2efa..15fc106b324 100644 --- a/packages/components/src/components/hds/advanced-table/th-sort.ts +++ b/packages/components/src/components/hds/advanced-table/th-sort.ts @@ -47,6 +47,7 @@ export interface HdsAdvancedTableThSortSignature { isLastColumn?: boolean; isStickyColumn?: boolean; isStickyColumnPinned?: boolean; + onRestoreColumnWidths?: () => void; }; Blocks: { default?: []; diff --git a/packages/components/src/components/hds/advanced-table/th.hbs b/packages/components/src/components/hds/advanced-table/th.hbs index af74b61c8ea..de3008b9bc5 100644 --- a/packages/components/src/components/hds/advanced-table/th.hbs +++ b/packages/components/src/components/hds/advanced-table/th.hbs @@ -67,7 +67,11 @@ {{#if @column}} {{#if this.showContextMenu}} - + {{/if}} {{#if (and @hasResizableColumns (not @isLastColumn))}} diff --git a/packages/components/src/components/hds/advanced-table/th.ts b/packages/components/src/components/hds/advanced-table/th.ts index 3022b0d9222..dec836829b3 100644 --- a/packages/components/src/components/hds/advanced-table/th.ts +++ b/packages/components/src/components/hds/advanced-table/th.ts @@ -47,6 +47,7 @@ export interface HdsAdvancedTableThSignature { scope?: HdsAdvancedTableScope; tooltip?: string; tableHeight?: number; + onRestoreColumnWidths?: () => void; didInsertExpandButton?: (button: HTMLButtonElement) => void; onClickToggle?: () => void; willDestroyExpandButton?: (button: HTMLButtonElement) => void; From 83ca8881fd42ef664c30711d9352c076e2ecd59c Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Thu, 12 Jun 2025 11:33:44 -0400 Subject: [PATCH 71/89] reset width wip --- .../components/hds/advanced-table/index.hbs | 4 +-- .../hds/advanced-table/models/column.ts | 14 +++++++++ .../hds/advanced-table/th-context-menu.hbs | 2 +- .../hds/advanced-table/th-context-menu.ts | 29 +++++++++++++------ .../hds/advanced-table/th-resize-handle.hbs | 1 + .../hds/advanced-table/th-resize-handle.ts | 16 ++++++++++ .../components/hds/advanced-table/th-sort.hbs | 3 +- .../components/hds/advanced-table/th-sort.ts | 2 +- .../src/components/hds/advanced-table/th.hbs | 8 ++++- .../src/components/hds/advanced-table/th.ts | 2 +- 10 files changed, 65 insertions(+), 16 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.hbs b/packages/components/src/components/hds/advanced-table/index.hbs index f7230bfe6f8..8fe0beea5e3 100644 --- a/packages/components/src/components/hds/advanced-table/index.hbs +++ b/packages/components/src/components/hds/advanced-table/index.hbs @@ -52,9 +52,9 @@ @isLastColumn={{eq index (sub this._tableModel.columns.length 1)}} @isStickyColumn={{if (and (eq index 0) @hasStickyFirstColumn) true}} @isStickyColumnPinned={{this.isStickyColumnPinned}} + @previousColumn={{get this._tableModel.columns (sub index 1)}} @nextColumn={{get this._tableModel.columns (add index 1)}} @tableHeight={{this._tableHeight}} - @onRestoreColumnWidths={{this._tableModel.restoreColumnWidths}} {{this._setColumnWidth column}} > {{column.label}} @@ -71,11 +71,11 @@ @isStickyColumn={{if (and (eq index 0) @hasStickyFirstColumn) true}} @isStickyColumnPinned={{this.isStickyColumnPinned}} @isVisuallyHidden={{column.isVisuallyHidden}} + @previousColumn={{get this._tableModel.columns (sub index 1)}} @nextColumn={{get this._tableModel.columns (add index 1)}} @tableHeight={{this._tableHeight}} @tooltip={{column.tooltip}} @onClickToggle={{this._tableModel.toggleAll}} - @onRestoreColumnWidths={{this._tableModel.restoreColumnWidths}} {{this._setColumnWidth column}} > {{column.label}} diff --git a/packages/components/src/components/hds/advanced-table/models/column.ts b/packages/components/src/components/hds/advanced-table/models/column.ts index 30048d6cd65..443f02fd876 100644 --- a/packages/components/src/components/hds/advanced-table/models/column.ts +++ b/packages/components/src/components/hds/advanced-table/models/column.ts @@ -36,6 +36,7 @@ export default class HdsAdvancedTableColumn { @tracked tooltip?: string = undefined; @tracked width?: string = undefined; @tracked originalWidth?: string = undefined; + @tracked imposedWidthDelta: number = 0; @tracked sortingFunction?: (a: unknown, b: unknown) => number = undefined; @@ -124,6 +125,19 @@ export default class HdsAdvancedTableColumn { this._onColumnResize?.(this.key, this.width); } + @action + onPreviousColumnWidthRestored(): void { + const restoredWidth = (this.pxWidth ?? 0) + this.imposedWidthDelta; + console.log(this.pxWidth, this.imposedWidthDelta, restoredWidth); + this.setPxWidth(restoredWidth); + this.imposedWidthDelta = 0; + } + + @action + onNextColumnWidthRestored(imposedWidthDelta: number): void { + console.log({ imposedWidthDelta }); + } + @action restoreWidth(): void { this.width = this.originalWidth; diff --git a/packages/components/src/components/hds/advanced-table/th-context-menu.hbs b/packages/components/src/components/hds/advanced-table/th-context-menu.hbs index 75d84823cfe..03dbf52a1f8 100644 --- a/packages/components/src/components/hds/advanced-table/th-context-menu.hbs +++ b/packages/components/src/components/hds/advanced-table/th-context-menu.hbs @@ -9,7 +9,7 @@ {{option.label}} diff --git a/packages/components/src/components/hds/advanced-table/th-context-menu.ts b/packages/components/src/components/hds/advanced-table/th-context-menu.ts index 6bd73385c20..d4c7edaeff3 100644 --- a/packages/components/src/components/hds/advanced-table/th-context-menu.ts +++ b/packages/components/src/components/hds/advanced-table/th-context-menu.ts @@ -8,6 +8,7 @@ import Component from '@glimmer/component'; import type HdsAdvancedTableColumn from './models/column.ts'; import type { HdsDropdownSignature } from '../dropdown/index.ts'; import type { HdsDropdownToggleIconSignature } from '../dropdown/toggle/icon.ts'; +import { action } from '@ember/object'; interface HdsAdvancedTableThContextMenuOption { key: string; @@ -15,6 +16,8 @@ interface HdsAdvancedTableThContextMenuOption { icon: HdsDropdownToggleIconSignature['Args']['icon']; action: ( column: HdsAdvancedTableColumn, + previousColumn?: HdsAdvancedTableColumn, + nextColumn?: HdsAdvancedTableColumn, dropdownCloseCallback?: () => void ) => void; } @@ -22,8 +25,9 @@ interface HdsAdvancedTableThContextMenuOption { export interface HdsAdvancedTableThContextMenuSignature { Args: { column: HdsAdvancedTableColumn; + previousColumn?: HdsAdvancedTableColumn; + nextColumn?: HdsAdvancedTableColumn; hasResizableColumns?: boolean; - onRestoreColumnWidths?: () => void; }; Element: HdsDropdownSignature['Element']; } @@ -37,18 +41,25 @@ export default class HdsAdvancedTableThContextMenu extends Component void - ): void => { - this.args.onRestoreColumnWidths?.(); - dropdownCloseCallback?.(); - }, + action: this.resetColumnWidth.bind(this), }); } return options; } + + @action + resetColumnWidth( + column: HdsAdvancedTableColumn, + previousColumn?: HdsAdvancedTableColumn, + nextColumn?: HdsAdvancedTableColumn, + dropdownCloseCallback?: () => void + ): void { + column.restoreWidth(); + previousColumn?.onNextColumnWidthRestored(column.imposedWidthDelta); + nextColumn?.onPreviousColumnWidthRestored(); + dropdownCloseCallback?.(); + } } diff --git a/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs b/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs index b50264e2744..9ee06510b0f 100644 --- a/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs +++ b/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs @@ -10,6 +10,7 @@ tabindex="0" aria-label="Resize {{@column.label}} column" {{this.registerHandleElement}} + {{on "pointerup" this.stopResize}} {{on "pointerdown" this.startResize}} {{on "keydown" this.handleKeydown}} {{style height=this.height}} diff --git a/packages/components/src/components/hds/advanced-table/th-resize-handle.ts b/packages/components/src/components/hds/advanced-table/th-resize-handle.ts index 25dc7d94c07..f34c5a58771 100644 --- a/packages/components/src/components/hds/advanced-table/th-resize-handle.ts +++ b/packages/components/src/components/hds/advanced-table/th-resize-handle.ts @@ -68,6 +68,7 @@ export default class HdsAdvancedTableThResizeHandle extends Component void; @@ -109,6 +110,20 @@ export default class HdsAdvancedTableThResizeHandle extends Component {{/if}} diff --git a/packages/components/src/components/hds/advanced-table/th-sort.ts b/packages/components/src/components/hds/advanced-table/th-sort.ts index 15fc106b324..8305abcd9e3 100644 --- a/packages/components/src/components/hds/advanced-table/th-sort.ts +++ b/packages/components/src/components/hds/advanced-table/th-sort.ts @@ -42,12 +42,12 @@ export interface HdsAdvancedTableThSortSignature { tooltip?: string; rowspan?: number; colspan?: number; + previousColumn?: HdsAdvancedTableColumn; nextColumn?: HdsAdvancedTableColumn; tableHeight?: number; isLastColumn?: boolean; isStickyColumn?: boolean; isStickyColumnPinned?: boolean; - onRestoreColumnWidths?: () => void; }; Blocks: { default?: []; diff --git a/packages/components/src/components/hds/advanced-table/th.hbs b/packages/components/src/components/hds/advanced-table/th.hbs index de3008b9bc5..bffd0b6be18 100644 --- a/packages/components/src/components/hds/advanced-table/th.hbs +++ b/packages/components/src/components/hds/advanced-table/th.hbs @@ -24,6 +24,11 @@ ...attributes > + {{round @column.imposedWidthDelta}} + + + {{round @column.pxWidth}} + = + {{round (sum @column.imposedWidthDelta @column.pxWidth)}} {{#if @column.isVisuallyHidden}} {{yield}} {{else}} @@ -69,8 +74,9 @@ {{#if this.showContextMenu}} {{/if}} diff --git a/packages/components/src/components/hds/advanced-table/th.ts b/packages/components/src/components/hds/advanced-table/th.ts index dec836829b3..50b72f164c6 100644 --- a/packages/components/src/components/hds/advanced-table/th.ts +++ b/packages/components/src/components/hds/advanced-table/th.ts @@ -41,13 +41,13 @@ export interface HdsAdvancedTableThSignature { isStickyColumnPinned?: boolean; isVisuallyHidden?: boolean; newLabel?: string; + previousColumn?: HdsAdvancedTableColumn; nextColumn?: HdsAdvancedTableColumn; parentId?: string; rowspan?: number; scope?: HdsAdvancedTableScope; tooltip?: string; tableHeight?: number; - onRestoreColumnWidths?: () => void; didInsertExpandButton?: (button: HTMLButtonElement) => void; onClickToggle?: () => void; willDestroyExpandButton?: (button: HTMLButtonElement) => void; From 5dcaffefd265cf900dff3a89aff094cf44032261 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Thu, 12 Jun 2025 13:15:34 -0400 Subject: [PATCH 72/89] restoring borrowed widths to neighbor cell --- .../src/components/hds/advanced-table/models/column.ts | 2 +- packages/components/src/components/hds/advanced-table/th.hbs | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/models/column.ts b/packages/components/src/components/hds/advanced-table/models/column.ts index 443f02fd876..55488a38d71 100644 --- a/packages/components/src/components/hds/advanced-table/models/column.ts +++ b/packages/components/src/components/hds/advanced-table/models/column.ts @@ -135,7 +135,7 @@ export default class HdsAdvancedTableColumn { @action onNextColumnWidthRestored(imposedWidthDelta: number): void { - console.log({ imposedWidthDelta }); + this.setPxWidth((this.pxWidth ?? 0) - imposedWidthDelta); } @action diff --git a/packages/components/src/components/hds/advanced-table/th.hbs b/packages/components/src/components/hds/advanced-table/th.hbs index bffd0b6be18..7100f2c0617 100644 --- a/packages/components/src/components/hds/advanced-table/th.hbs +++ b/packages/components/src/components/hds/advanced-table/th.hbs @@ -24,11 +24,6 @@ ...attributes > - {{round @column.imposedWidthDelta}} - + - {{round @column.pxWidth}} - = - {{round (sum @column.imposedWidthDelta @column.pxWidth)}} {{#if @column.isVisuallyHidden}} {{yield}} {{else}} From 0b63e672ae458e628d2db0f084cf647182e398a3 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Thu, 12 Jun 2025 14:04:44 -0400 Subject: [PATCH 73/89] working on resetting size --- .../src/components/hds/advanced-table/models/column.ts | 4 +++- .../src/components/hds/advanced-table/th-context-menu.ts | 2 +- packages/components/src/components/hds/advanced-table/th.hbs | 5 +++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/models/column.ts b/packages/components/src/components/hds/advanced-table/models/column.ts index 55488a38d71..76990c7a52d 100644 --- a/packages/components/src/components/hds/advanced-table/models/column.ts +++ b/packages/components/src/components/hds/advanced-table/models/column.ts @@ -128,8 +128,9 @@ export default class HdsAdvancedTableColumn { @action onPreviousColumnWidthRestored(): void { const restoredWidth = (this.pxWidth ?? 0) + this.imposedWidthDelta; - console.log(this.pxWidth, this.imposedWidthDelta, restoredWidth); + this.setPxWidth(restoredWidth); + this.imposedWidthDelta = 0; } @@ -141,6 +142,7 @@ export default class HdsAdvancedTableColumn { @action restoreWidth(): void { this.width = this.originalWidth; + this.imposedWidthDelta = 0; if (this.key === undefined) { return; diff --git a/packages/components/src/components/hds/advanced-table/th-context-menu.ts b/packages/components/src/components/hds/advanced-table/th-context-menu.ts index d4c7edaeff3..5f759491106 100644 --- a/packages/components/src/components/hds/advanced-table/th-context-menu.ts +++ b/packages/components/src/components/hds/advanced-table/th-context-menu.ts @@ -57,9 +57,9 @@ export default class HdsAdvancedTableThContextMenu extends Component void ): void { - column.restoreWidth(); previousColumn?.onNextColumnWidthRestored(column.imposedWidthDelta); nextColumn?.onPreviousColumnWidthRestored(); + column.restoreWidth(); dropdownCloseCallback?.(); } } diff --git a/packages/components/src/components/hds/advanced-table/th.hbs b/packages/components/src/components/hds/advanced-table/th.hbs index 7100f2c0617..bffd0b6be18 100644 --- a/packages/components/src/components/hds/advanced-table/th.hbs +++ b/packages/components/src/components/hds/advanced-table/th.hbs @@ -24,6 +24,11 @@ ...attributes > + {{round @column.imposedWidthDelta}} + + + {{round @column.pxWidth}} + = + {{round (sum @column.imposedWidthDelta @column.pxWidth)}} {{#if @column.isVisuallyHidden}} {{yield}} {{else}} From f02ade178b827eafeee1b93ca30b8f18e0944fb9 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Thu, 12 Jun 2025 19:30:07 -0400 Subject: [PATCH 74/89] fixed reszing issue --- .../hds/advanced-table/th-resize-handle.hbs | 1 - .../hds/advanced-table/th-resize-handle.ts | 26 +++++++++---------- .../src/components/hds/advanced-table/th.hbs | 5 ---- 3 files changed, 12 insertions(+), 20 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs b/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs index 9ee06510b0f..b50264e2744 100644 --- a/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs +++ b/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs @@ -10,7 +10,6 @@ tabindex="0" aria-label="Resize {{@column.label}} column" {{this.registerHandleElement}} - {{on "pointerup" this.stopResize}} {{on "pointerdown" this.startResize}} {{on "keydown" this.handleKeydown}} {{style height=this.height}} diff --git a/packages/components/src/components/hds/advanced-table/th-resize-handle.ts b/packages/components/src/components/hds/advanced-table/th-resize-handle.ts index f34c5a58771..87de4b6544c 100644 --- a/packages/components/src/components/hds/advanced-table/th-resize-handle.ts +++ b/packages/components/src/components/hds/advanced-table/th-resize-handle.ts @@ -110,20 +110,6 @@ export default class HdsAdvancedTableThResizeHandle extends Component - {{round @column.imposedWidthDelta}} - + - {{round @column.pxWidth}} - = - {{round (sum @column.imposedWidthDelta @column.pxWidth)}} {{#if @column.isVisuallyHidden}} {{yield}} {{else}} From 46603d24246121343b4c671de4bf92fea9de00db Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Thu, 12 Jun 2025 20:37:12 -0400 Subject: [PATCH 75/89] added comments --- .../src/components/hds/advanced-table/models/column.ts | 7 +++++-- .../src/components/hds/advanced-table/th-context-menu.ts | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/models/column.ts b/packages/components/src/components/hds/advanced-table/models/column.ts index 76990c7a52d..9d258ea7558 100644 --- a/packages/components/src/components/hds/advanced-table/models/column.ts +++ b/packages/components/src/components/hds/advanced-table/models/column.ts @@ -35,8 +35,8 @@ export default class HdsAdvancedTableColumn { @tracked maxWidth?: `${number}px` = DEFAULT_MAX_WIDTH; @tracked tooltip?: string = undefined; @tracked width?: string = undefined; - @tracked originalWidth?: string = undefined; - @tracked imposedWidthDelta: number = 0; + @tracked originalWidth?: string = undefined; // used to restore the width when resetting + @tracked imposedWidthDelta: number = 0; // used to track the width change imposed by the previous column @tracked sortingFunction?: (a: unknown, b: unknown) => number = undefined; @@ -108,6 +108,7 @@ export default class HdsAdvancedTableColumn { this.maxWidth = maxWidth ?? DEFAULT_MAX_WIDTH; } + // Sets the column width in pixels, ensuring it respects the min and max width constraints. @action setPxWidth(newPxWidth: number): void { const pxMinWidth = this.pxMinWidth ?? 1; @@ -125,6 +126,7 @@ export default class HdsAdvancedTableColumn { this._onColumnResize?.(this.key, this.width); } + // This method is called when the column width is changed by the previous column. @action onPreviousColumnWidthRestored(): void { const restoredWidth = (this.pxWidth ?? 0) + this.imposedWidthDelta; @@ -134,6 +136,7 @@ export default class HdsAdvancedTableColumn { this.imposedWidthDelta = 0; } + // This method is called when the next column width is restored. @action onNextColumnWidthRestored(imposedWidthDelta: number): void { this.setPxWidth((this.pxWidth ?? 0) - imposedWidthDelta); diff --git a/packages/components/src/components/hds/advanced-table/th-context-menu.ts b/packages/components/src/components/hds/advanced-table/th-context-menu.ts index 5f759491106..c97b854c4be 100644 --- a/packages/components/src/components/hds/advanced-table/th-context-menu.ts +++ b/packages/components/src/components/hds/advanced-table/th-context-menu.ts @@ -60,6 +60,7 @@ export default class HdsAdvancedTableThContextMenu extends Component Date: Fri, 13 Jun 2025 15:48:35 -0400 Subject: [PATCH 76/89] fixing failing tests --- .../src/components/hds/advanced-table/index.hbs | 2 ++ .../src/components/hds/advanced-table/index.ts | 5 ++--- .../components/hds/advanced-table/models/column.ts | 12 +----------- .../components/hds/advanced-table/models/table.ts | 5 ----- .../components/hds/advanced-table/th-context-menu.ts | 11 ++++++++++- .../hds/advanced-table/th-resize-handle.ts | 8 ++++++-- .../src/components/hds/advanced-table/th-sort.hbs | 2 ++ .../src/components/hds/advanced-table/th-sort.ts | 1 + .../src/components/hds/advanced-table/th.hbs | 2 ++ .../src/components/hds/advanced-table/th.ts | 2 ++ .../components/hds/advanced-table/index-test.js | 10 +++++++--- 11 files changed, 35 insertions(+), 25 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.hbs b/packages/components/src/components/hds/advanced-table/index.hbs index 8fe0beea5e3..1c16f805685 100644 --- a/packages/components/src/components/hds/advanced-table/index.hbs +++ b/packages/components/src/components/hds/advanced-table/index.hbs @@ -55,6 +55,7 @@ @previousColumn={{get this._tableModel.columns (sub index 1)}} @nextColumn={{get this._tableModel.columns (add index 1)}} @tableHeight={{this._tableHeight}} + @onColumnResize={{@onColumnResize}} {{this._setColumnWidth column}} > {{column.label}} @@ -76,6 +77,7 @@ @tableHeight={{this._tableHeight}} @tooltip={{column.tooltip}} @onClickToggle={{this._tableModel.toggleAll}} + @onColumnResize={{@onColumnResize}} {{this._setColumnWidth column}} > {{column.label}} diff --git a/packages/components/src/components/hds/advanced-table/index.ts b/packages/components/src/components/hds/advanced-table/index.ts index d053b4247f2..f1730a4746f 100644 --- a/packages/components/src/components/hds/advanced-table/index.ts +++ b/packages/components/src/components/hds/advanced-table/index.ts @@ -185,7 +185,6 @@ export default class HdsAdvancedTable extends Component number = undefined; - private _onColumnResize?: HdsAdvancedTableColumnResizeCallback; - table: HdsAdvancedTableModel; get pxWidth(): number | undefined { @@ -68,9 +66,8 @@ export default class HdsAdvancedTableColumn { constructor(args: { column: HdsAdvancedTableColumnType; table: HdsAdvancedTableModel; - onColumnResize?: HdsAdvancedTableColumnResizeCallback; }) { - const { column, table, onColumnResize } = args; + const { column, table } = args; // set reference to table model this.table = table; @@ -85,9 +82,6 @@ export default class HdsAdvancedTableColumn { this.tooltip = column.tooltip; this._setWidthValues(column); this.sortingFunction = column.sortingFunction; - - // set resize callback - this._onColumnResize = onColumnResize; } private _setWidthValues({ @@ -122,8 +116,6 @@ export default class HdsAdvancedTableColumn { if (this.key === undefined) { return; } - - this._onColumnResize?.(this.key, this.width); } // This method is called when the column width is changed by the previous column. @@ -150,7 +142,5 @@ export default class HdsAdvancedTableColumn { if (this.key === undefined) { return; } - - this._onColumnResize?.(this.key, this.width); } } diff --git a/packages/components/src/components/hds/advanced-table/models/table.ts b/packages/components/src/components/hds/advanced-table/models/table.ts index 5857c46e979..6fb00cabf81 100644 --- a/packages/components/src/components/hds/advanced-table/models/table.ts +++ b/packages/components/src/components/hds/advanced-table/models/table.ts @@ -23,7 +23,6 @@ type HdsAdvancedTableTableArgs = Pick< | 'hasResizableColumns' | 'sortBy' | 'sortOrder' - | 'onColumnResize' | 'onSort' >; @@ -52,7 +51,6 @@ export default class HdsAdvancedTableTableModel { childrenKey?: HdsAdvancedTableTableArgs['childrenKey']; hasResizableColumns?: HdsAdvancedTableTableArgs['hasResizableColumns']; - onColumnResize?: HdsAdvancedTableTableArgs['onColumnResize']; onSort?: HdsAdvancedTableSignature['Args']['onSort']; constructor(args: HdsAdvancedTableTableArgs) { @@ -63,13 +61,11 @@ export default class HdsAdvancedTableTableModel { hasResizableColumns, sortBy, sortOrder, - onColumnResize, onSort, } = args; this.childrenKey = childrenKey; this.hasResizableColumns = hasResizableColumns; - this.onColumnResize = onColumnResize; this.onSort = onSort; this.setupData({ model, columns, sortBy, sortOrder }); @@ -173,7 +169,6 @@ export default class HdsAdvancedTableTableModel { new HdsAdvancedTableColumn({ column, table: this, - onColumnResize: this.onColumnResize, }) ); diff --git a/packages/components/src/components/hds/advanced-table/th-context-menu.ts b/packages/components/src/components/hds/advanced-table/th-context-menu.ts index c97b854c4be..81611521082 100644 --- a/packages/components/src/components/hds/advanced-table/th-context-menu.ts +++ b/packages/components/src/components/hds/advanced-table/th-context-menu.ts @@ -4,11 +4,12 @@ */ import Component from '@glimmer/component'; +import { action } from '@ember/object'; import type HdsAdvancedTableColumn from './models/column.ts'; import type { HdsDropdownSignature } from '../dropdown/index.ts'; import type { HdsDropdownToggleIconSignature } from '../dropdown/toggle/icon.ts'; -import { action } from '@ember/object'; +import type { HdsAdvancedTableSignature } from './index.ts'; interface HdsAdvancedTableThContextMenuOption { key: string; @@ -28,6 +29,7 @@ export interface HdsAdvancedTableThContextMenuSignature { previousColumn?: HdsAdvancedTableColumn; nextColumn?: HdsAdvancedTableColumn; hasResizableColumns?: boolean; + onColumnResize?: HdsAdvancedTableSignature['Args']['onColumnResize']; }; Element: HdsDropdownSignature['Element']; } @@ -57,10 +59,17 @@ export default class HdsAdvancedTableThContextMenu extends Component void ): void { + const { onColumnResize } = this.args; + previousColumn?.onNextColumnWidthRestored(column.imposedWidthDelta); nextColumn?.onPreviousColumnWidthRestored(); column.restoreWidth(); + if (typeof onColumnResize === 'function' && column.key !== undefined) { + console.log('resetting'); + onColumnResize(column.key, column.width); + } + dropdownCloseCallback?.(); } } diff --git a/packages/components/src/components/hds/advanced-table/th-resize-handle.ts b/packages/components/src/components/hds/advanced-table/th-resize-handle.ts index 87de4b6544c..1f1e84da0d3 100644 --- a/packages/components/src/components/hds/advanced-table/th-resize-handle.ts +++ b/packages/components/src/components/hds/advanced-table/th-resize-handle.ts @@ -55,6 +55,7 @@ export interface HdsAdvancedTableThResizeHandleSignature { nextColumn?: HdsAdvancedTableColumn; hasResizableColumns: HdsAdvancedTableSignature['Args']['hasResizableColumns']; tableHeight?: number; + onColumnResize?: HdsAdvancedTableSignature['Args']['onColumnResize']; }; Blocks: { default?: []; @@ -163,7 +164,6 @@ export default class HdsAdvancedTableThResizeHandle extends Component {{/if}} @@ -59,6 +60,7 @@ @nextColumn={{@nextColumn}} @hasResizableColumns={{@hasResizableColumns}} @tableHeight={{@tableHeight}} + @onColumnResize={{@onColumnResize}} /> {{/if}} {{/if}} diff --git a/packages/components/src/components/hds/advanced-table/th-sort.ts b/packages/components/src/components/hds/advanced-table/th-sort.ts index 8305abcd9e3..3e9e6334e06 100644 --- a/packages/components/src/components/hds/advanced-table/th-sort.ts +++ b/packages/components/src/components/hds/advanced-table/th-sort.ts @@ -48,6 +48,7 @@ export interface HdsAdvancedTableThSortSignature { isLastColumn?: boolean; isStickyColumn?: boolean; isStickyColumnPinned?: boolean; + onColumnResize?: HdsAdvancedTableSignature['Args']['onColumnResize']; }; Blocks: { default?: []; diff --git a/packages/components/src/components/hds/advanced-table/th.hbs b/packages/components/src/components/hds/advanced-table/th.hbs index 7100f2c0617..8e3ce1d7966 100644 --- a/packages/components/src/components/hds/advanced-table/th.hbs +++ b/packages/components/src/components/hds/advanced-table/th.hbs @@ -72,6 +72,7 @@ @previousColumn={{@previousColumn}} @nextColumn={{@nextColumn}} @hasResizableColumns={{@hasResizableColumns}} + @onColumnResize={{@onColumnResize}} /> {{/if}} @@ -81,6 +82,7 @@ @nextColumn={{@nextColumn}} @hasResizableColumns={{@hasResizableColumns}} @tableHeight={{@tableHeight}} + @onColumnResize={{@onColumnResize}} /> {{/if}} {{/if}} diff --git a/packages/components/src/components/hds/advanced-table/th.ts b/packages/components/src/components/hds/advanced-table/th.ts index 50b72f164c6..c5fa324b4a9 100644 --- a/packages/components/src/components/hds/advanced-table/th.ts +++ b/packages/components/src/components/hds/advanced-table/th.ts @@ -20,6 +20,7 @@ import type { HdsAdvancedTableScope, HdsAdvancedTableExpandState, } from './types.ts'; +import type { HdsAdvancedTableSignature } from './index.ts'; export const ALIGNMENTS: string[] = Object.values( HdsAdvancedTableHorizontalAlignmentValues @@ -50,6 +51,7 @@ export interface HdsAdvancedTableThSignature { tableHeight?: number; didInsertExpandButton?: (button: HTMLButtonElement) => void; onClickToggle?: () => void; + onColumnResize?: HdsAdvancedTableSignature['Args']['onColumnResize']; willDestroyExpandButton?: (button: HTMLButtonElement) => void; }; Blocks: { diff --git a/showcase/tests/integration/components/hds/advanced-table/index-test.js b/showcase/tests/integration/components/hds/advanced-table/index-test.js index 508c466c368..5647dd3b568 100644 --- a/showcase/tests/integration/components/hds/advanced-table/index-test.js +++ b/showcase/tests/integration/components/hds/advanced-table/index-test.js @@ -169,7 +169,6 @@ const setResizableColumnsTableData = (context) => { { key: 'col1', label: 'Col 1', - isResizable: true, width: '120px', minWidth: '60px', maxWidth: '300px', @@ -231,7 +230,7 @@ const hbsNestedAdvancedTable = hbs``; const hbsResizableColumnsAdvancedTable = hbs` <:body as |B|> @@ -777,6 +776,7 @@ module('Integration | Component | hds/advanced-table/index', function (hooks) { assert .dom('#data-test-advanced-table .hds-advanced-table__td:nth-of-type(1)') .hasText('Melanie'); + assert .dom('#data-test-advanced-table .hds-advanced-table__caption') .hasText('Sorted by artist ascending'); @@ -1592,6 +1592,7 @@ module('Integration | Component | hds/advanced-table/index', function (hooks) { @@ -1614,13 +1615,16 @@ module('Integration | Component | hds/advanced-table/index', function (hooks) { test('it should call `onColumnResize` when a column width is reset', async function (assert) { setResizableColumnsTableData(this); - const onColumnResizeSpy = sinon.spy(); + const onColumnResizeSpy = sinon.spy((key) => { + console.log('Column resized', key); + }); this.set('onColumnResize', onColumnResizeSpy); await render(hbs` From 41e6fcfef211993dd9062a91f76e2abdf5a0c528 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Fri, 13 Jun 2025 16:14:39 -0400 Subject: [PATCH 77/89] added additional onResize test --- .../hds/advanced-table/th-resize-handle.ts | 35 ++++++++++++++----- .../hds/advanced-table/index-test.js | 33 ++++++++++++++++- 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/th-resize-handle.ts b/packages/components/src/components/hds/advanced-table/th-resize-handle.ts index 1f1e84da0d3..cb21aac915a 100644 --- a/packages/components/src/components/hds/advanced-table/th-resize-handle.ts +++ b/packages/components/src/components/hds/advanced-table/th-resize-handle.ts @@ -111,6 +111,15 @@ export default class HdsAdvancedTableThResizeHandle extends Component + <:body as |B|> + + {{B.data.col1}} + {{B.data.col2}} + + + + `); + + const handle = find('.hds-advanced-table__th-resize-handle'); + + await focus(handle); + + await triggerKeyEvent(handle, 'keydown', 'ArrowRight'); + + assert.ok(onColumnResizeSpy.calledOnce, 'onColumnResize was called'); + }); + + test('it should call `onColumnResize` when a column is resized by keyboard', async function (assert) { setResizableColumnsTableData(this); const onColumnResizeSpy = sinon.spy(); this.set('onColumnResize', onColumnResizeSpy); From 58e0588703c343160dfcecac54f1888dd0454a1c Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Fri, 13 Jun 2025 16:35:32 -0400 Subject: [PATCH 78/89] responding to PR feedback --- .../templates/components/advanced-table.hbs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/showcase/app/templates/components/advanced-table.hbs b/showcase/app/templates/components/advanced-table.hbs index 0cf1da9b1a6..a3152d65306 100644 --- a/showcase/app/templates/components/advanced-table.hbs +++ b/showcase/app/templates/components/advanced-table.hbs @@ -2007,16 +2007,18 @@ ThContextMenu - + {{#each @model.STATES as |state|}} - +
+ +
{{/each}}
From 10b61884e6d37a32d64c28ca1f1304c7c2a1a79e Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Fri, 13 Jun 2025 17:07:17 -0400 Subject: [PATCH 79/89] responding to PR feedback --- .../app/components/mock/app/main/generic-advanced-table.gts | 3 --- 1 file changed, 3 deletions(-) diff --git a/showcase/app/components/mock/app/main/generic-advanced-table.gts b/showcase/app/components/mock/app/main/generic-advanced-table.gts index ed2b90de879..58b94f315f7 100644 --- a/showcase/app/components/mock/app/main/generic-advanced-table.gts +++ b/showcase/app/components/mock/app/main/generic-advanced-table.gts @@ -33,21 +33,18 @@ const SAMPLE_COLUMNS = [ label: 'Project name', key: 'project-name', isSortable: true, - isResizable: true, width: '300px', }, { label: 'Current run ID', key: 'current-run-id', isSortable: true, - isResizable: true, width: '300px', }, { label: 'Run status', key: 'run-status', isSortable: true, - isResizable: true, width: '200px', }, { From 973c0885094763645636e1d37614dedc895c3e8d Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Fri, 13 Jun 2025 17:12:58 -0400 Subject: [PATCH 80/89] fixing linting error --- .../src/components/hds/advanced-table/models/column.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/components/src/components/hds/advanced-table/models/column.ts b/packages/components/src/components/hds/advanced-table/models/column.ts index 4dbe37fe11f..a891dc23711 100644 --- a/packages/components/src/components/hds/advanced-table/models/column.ts +++ b/packages/components/src/components/hds/advanced-table/models/column.ts @@ -4,7 +4,6 @@ import { action } from '@ember/object'; import type HdsAdvancedTableModel from './table.ts'; import type { HdsAdvancedTableHorizontalAlignment, - HdsAdvancedTableColumnResizeCallback, HdsAdvancedTableColumn as HdsAdvancedTableColumnType, } from '../types'; From f02d03e6a38300f48fb4f0cfba4db9ca58c59012 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Fri, 13 Jun 2025 17:29:42 -0400 Subject: [PATCH 81/89] fixing a11y test --- .../hds/advanced-table/models/column.ts | 4 +- .../templates/components/advanced-table.hbs | 5 +- .../hds/advanced-table/models/column-test.js | 56 +++---------------- 3 files changed, 14 insertions(+), 51 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/models/column.ts b/packages/components/src/components/hds/advanced-table/models/column.ts index a891dc23711..aa44813ea11 100644 --- a/packages/components/src/components/hds/advanced-table/models/column.ts +++ b/packages/components/src/components/hds/advanced-table/models/column.ts @@ -7,8 +7,8 @@ import type { HdsAdvancedTableColumn as HdsAdvancedTableColumnType, } from '../types'; -const DEFAULT_MIN_WIDTH = '150px'; -const DEFAULT_MAX_WIDTH = '800px'; +export const DEFAULT_MIN_WIDTH = '150px'; +export const DEFAULT_MAX_WIDTH = '800px'; function isPxSize(value?: string): boolean { if (value === undefined) { diff --git a/showcase/app/templates/components/advanced-table.hbs b/showcase/app/templates/components/advanced-table.hbs index a3152d65306..cf10a2d5cd9 100644 --- a/showcase/app/templates/components/advanced-table.hbs +++ b/showcase/app/templates/components/advanced-table.hbs @@ -8,6 +8,7 @@ AdvancedTable
+ import type { ActionState } from '@ember/-internals/glimmer/lib/modifiers/action'; {{/each}} - + + Actions + diff --git a/showcase/tests/unit/components/hds/advanced-table/models/column-test.js b/showcase/tests/unit/components/hds/advanced-table/models/column-test.js index dcf99ae681a..b36f9c55757 100644 --- a/showcase/tests/unit/components/hds/advanced-table/models/column-test.js +++ b/showcase/tests/unit/components/hds/advanced-table/models/column-test.js @@ -1,6 +1,8 @@ import { module, test } from 'qunit'; -import sinon from 'sinon'; -import HdsAdvancedTableColumn from '@hashicorp/design-system-components/components/hds/advanced-table/models/column'; +import HdsAdvancedTableColumn, { + DEFAULT_MAX_WIDTH, + DEFAULT_MIN_WIDTH, +} from '@hashicorp/design-system-components/components/hds/advanced-table/models/column'; module('Unit | Component | hds/advanced-table/models/column', function () { test('initializes with default properties when minimal args provided', function (assert) { @@ -11,7 +13,6 @@ module('Unit | Component | hds/advanced-table/models/column', function () { assert.strictEqual(column.label, 'Test Column', 'sets the label property'); assert.strictEqual(column.align, 'left', 'defaults to left alignment'); assert.false(column.isExpandable, 'defaults isExpandable to false'); - assert.false(column.isResizable, 'defaults isResizable to false'); assert.strictEqual( column.isSortable, false, @@ -29,13 +30,13 @@ module('Unit | Component | hds/advanced-table/models/column', function () { ); assert.strictEqual( column.minWidth, - undefined, - 'minWidth is undefined when width is not provided', + DEFAULT_MIN_WIDTH, + 'minWidth is set to the default value when width is not provided', ); assert.strictEqual( column.maxWidth, - undefined, - 'maxWidth is undefined when width is not provided', + DEFAULT_MAX_WIDTH, + 'maxWidth is set to the default value when width is not provided', ); assert.strictEqual( column.tooltip, @@ -56,7 +57,6 @@ module('Unit | Component | hds/advanced-table/models/column', function () { label: 'Full Column', align: 'center', isExpandable: true, - isResizable: false, isSortable: true, isVisuallyHidden: true, key: 'test-column', @@ -68,7 +68,6 @@ module('Unit | Component | hds/advanced-table/models/column', function () { assert.strictEqual(column.label, 'Full Column', 'sets the label property'); assert.strictEqual(column.align, 'center', 'sets the alignment'); assert.true(column.isExpandable, 'sets isExpandable'); - assert.false(column.isResizable, 'sets isResizable'); assert.true(column.isSortable, 'sets isSortable'); assert.true(column.isVisuallyHidden, 'sets isVisuallyHidden'); assert.strictEqual(column.key, 'test-column', 'sets the key'); @@ -205,45 +204,6 @@ module('Unit | Component | hds/advanced-table/models/column', function () { ); }); - test('setPxWidth calls resize callback when key is present', function (assert) { - const resizeSpy = sinon.spy(); - - const column = new HdsAdvancedTableColumn({ - column: { - label: 'Callback Test', - width: '150px', - key: 'test-key', - }, - onColumnResize: resizeSpy, - }); - - column.setPxWidth(200); - - assert.ok( - resizeSpy.calledOnceWith('test-key', '200px'), - 'resize callback was called when key is present', - ); - }); - - test('setPxWidth does not call resize callback when key is missing', function (assert) { - const resizeSpy = sinon.spy(); - - const column = new HdsAdvancedTableColumn({ - column: { - label: 'No Key', - width: '150px', - }, - onColumnResize: resizeSpy, - }); - - column.setPxWidth(200); - - assert.false( - resizeSpy.called, - 'resize callback was not called when key is missing', - ); - }); - test('restoreWidth sets width back to original value', function (assert) { const column = new HdsAdvancedTableColumn({ column: { From b95fd2921b5df0b049639e7ffdde87b237778933 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Thu, 19 Jun 2025 15:54:29 -0400 Subject: [PATCH 82/89] cleaning up PR --- .../hds/advanced-table/th-context-menu.ts | 1 - .../hds/advanced-table/th-resize-handle.ts | 36 +++++++++---------- .../components/hds/advanced-table/types.ts | 5 --- .../templates/components/advanced-table.hbs | 1 - .../components/form/base-elements.hbs | 1 + 5 files changed, 19 insertions(+), 25 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/th-context-menu.ts b/packages/components/src/components/hds/advanced-table/th-context-menu.ts index 81611521082..1dd07caea1c 100644 --- a/packages/components/src/components/hds/advanced-table/th-context-menu.ts +++ b/packages/components/src/components/hds/advanced-table/th-context-menu.ts @@ -66,7 +66,6 @@ export default class HdsAdvancedTableThContextMenu extends Component void; - private boundStopResize: () => void; + private _handleElement!: HdsAdvancedTableThResizeHandleSignature['Element']; + private _boundResize: (event: PointerEvent) => void; + private _boundStopResize: () => void; - private registerHandleElement = modifier( + private _registerHandleElement = modifier( (element: HdsAdvancedTableThResizeHandleSignature['Element']) => { - this.handleElement = element; + this._handleElement = element; } ); @@ -87,8 +87,8 @@ export default class HdsAdvancedTableThResizeHandle extends Component>; - -export type HdsAdvancedTableColumnResizeCallback = ( - columnKey: string, - newWidth?: string -) => void; diff --git a/showcase/app/templates/components/advanced-table.hbs b/showcase/app/templates/components/advanced-table.hbs index cf10a2d5cd9..4f4dbc9ca30 100644 --- a/showcase/app/templates/components/advanced-table.hbs +++ b/showcase/app/templates/components/advanced-table.hbs @@ -8,7 +8,6 @@ AdvancedTable
- import type { ActionState } from '@ember/-internals/glimmer/lib/modifiers/action'; Form / Base elements
+ Label From 550e05fad3359aae156f83685189fdba057ddc17 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Thu, 19 Jun 2025 16:11:13 -0400 Subject: [PATCH 83/89] cleaning up PR --- .changeset/chilly-cheetahs-rescue.md | 4 ++-- .../components/hds/advanced-table/th-resize-handle.hbs | 2 +- .../components/hds/advanced-table/th-resize-handle.ts | 2 +- .../components/src/styles/components/advanced-table.scss | 9 --------- 4 files changed, 4 insertions(+), 13 deletions(-) diff --git a/.changeset/chilly-cheetahs-rescue.md b/.changeset/chilly-cheetahs-rescue.md index 2296be5be8a..ec1393d2427 100644 --- a/.changeset/chilly-cheetahs-rescue.md +++ b/.changeset/chilly-cheetahs-rescue.md @@ -2,6 +2,6 @@ "@hashicorp/design-system-components": minor --- -`AdvancedTable` - Added `isResizable` option to column object. When `true`, allows the column to be resized with both a click-and-drag and a keyboard interface. - Added `ember-math-helpers` dependency. + +`AdvancedTable` - Added `hasResizableColumns` argument. When `true`, allows the table's columns to be resized with both a click-and-drag and a keyboard interface. diff --git a/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs b/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs index b50264e2744..e9f5dee5482 100644 --- a/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs +++ b/packages/components/src/components/hds/advanced-table/th-resize-handle.hbs @@ -9,7 +9,7 @@ aria-valuemax={{@column.pxMaxWidth}} tabindex="0" aria-label="Resize {{@column.label}} column" - {{this.registerHandleElement}} + {{this._registerHandleElement}} {{on "pointerdown" this.startResize}} {{on "keydown" this.handleKeydown}} {{style height=this.height}} diff --git a/packages/components/src/components/hds/advanced-table/th-resize-handle.ts b/packages/components/src/components/hds/advanced-table/th-resize-handle.ts index 6a8f37ab68c..3cc76481f91 100644 --- a/packages/components/src/components/hds/advanced-table/th-resize-handle.ts +++ b/packages/components/src/components/hds/advanced-table/th-resize-handle.ts @@ -113,7 +113,7 @@ export default class HdsAdvancedTableThResizeHandle extends Component Date: Thu, 19 Jun 2025 16:46:20 -0400 Subject: [PATCH 84/89] cleaning up PR --- .../hds/advanced-table/th-context-menu.hbs | 2 +- .../hds/advanced-table/index-test.js | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/packages/components/src/components/hds/advanced-table/th-context-menu.hbs b/packages/components/src/components/hds/advanced-table/th-context-menu.hbs index 03dbf52a1f8..fc6aba98feb 100644 --- a/packages/components/src/components/hds/advanced-table/th-context-menu.hbs +++ b/packages/components/src/components/hds/advanced-table/th-context-menu.hbs @@ -2,7 +2,7 @@ Copyright (c) HashiCorp, Inc. SPDX-License-Identifier: MPL-2.0 }} - + {{#each this._options as |option|}} diff --git a/showcase/tests/integration/components/hds/advanced-table/index-test.js b/showcase/tests/integration/components/hds/advanced-table/index-test.js index 48d2f5fd4d6..e7fe07d6b32 100644 --- a/showcase/tests/integration/components/hds/advanced-table/index-test.js +++ b/showcase/tests/integration/components/hds/advanced-table/index-test.js @@ -371,6 +371,34 @@ module('Integration | Component | hds/advanced-table/index', function (hooks) { }); }); + test('it throws an assertion if @hasResizableColumns and has nested rows', async function (assert) { + const errorMessage = + 'Cannot have resizable columns if there are nested rows.'; + + setNestedTableData(this); + assert.expect(2); + setupOnerror(function (error) { + assert.strictEqual(error.message, `Assertion Failed: ${errorMessage}`); + }); + await render(hbs` + <:body as |B|> + + {{B.data.name}} + {{B.data.age}} + + + `); + + assert.throws(function () { + throw new Error(errorMessage); + }); + }); + test('it should support splattributes', async function (assert) { setSortableTableData(this); await render( From 4695b8ea1b81558355d48480b25288b094c6ebe6 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Thu, 19 Jun 2025 19:42:24 -0400 Subject: [PATCH 85/89] responding to PR feedback --- .../components/hds/advanced-table/index.ts | 39 ++++++++++--- .../hds/advanced-table/th-context-menu.hbs | 7 ++- .../hds/advanced-table/th-resize-handle.hbs | 2 +- .../hds/advanced-table/th-resize-handle.ts | 4 +- .../src/styles/components/advanced-table.scss | 2 + .../hds/advanced-table/index-test.js | 56 +++++++++++++++++++ 6 files changed, 99 insertions(+), 11 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.ts b/packages/components/src/components/hds/advanced-table/index.ts index f1730a4746f..1912da0cfa0 100644 --- a/packages/components/src/components/hds/advanced-table/index.ts +++ b/packages/components/src/components/hds/advanced-table/index.ts @@ -7,7 +7,7 @@ import Component from '@glimmer/component'; import { action } from '@ember/object'; import { assert } from '@ember/debug'; import { tracked } from '@glimmer/tracking'; -import type { ComponentLike } from '@glint/template'; +import type { WithBoundArgs } from '@glint/template'; import { guidFor } from '@ember/object/internals'; import { modifier } from 'ember-modifier'; import type Owner from '@ember/owner'; @@ -32,9 +32,9 @@ import type { } from './types.ts'; import type HdsAdvancedTableColumnType from './models/column.ts'; import type { HdsFormCheckboxBaseSignature } from '../form/checkbox/base.ts'; -import type { HdsAdvancedTableTdSignature } from './td.ts'; -import type { HdsAdvancedTableThSignature } from './th.ts'; -import type { HdsAdvancedTableTrSignature } from './tr.ts'; +import type HdsAdvancedTableTd from './td.ts'; +import type HdsAdvancedTableTh from './th.ts'; +import type HdsAdvancedTableTr from './tr.ts'; export const DENSITIES: HdsAdvancedTableDensities[] = Object.values( HdsAdvancedTableDensityValues @@ -140,9 +140,34 @@ export interface HdsAdvancedTableSignature { Blocks: { body?: [ { - Td?: ComponentLike; - Tr?: ComponentLike; - Th?: ComponentLike; + Td?: WithBoundArgs; + Tr?: WithBoundArgs< + typeof HdsAdvancedTableTr, + | 'selectionScope' + | 'isLastRow' + | 'isSelectable' + | 'onSelectionChange' + | 'didInsert' + | 'willDestroy' + | 'selectionAriaLabelSuffix' + | 'hasStickyColumn' + | 'isStickyColumnPinned' + | 'isParentRow' + | 'depth' + | 'displayRow' + >; + Th?: WithBoundArgs< + typeof HdsAdvancedTableTh, + | 'depth' + | 'isExpandable' + | 'isExpanded' + | 'newLabel' + | 'parentId' + | 'scope' + | 'isStickyColumn' + | 'isStickyColumnPinned' + | 'onClickToggle' + >; data?: Record; rowIndex?: number | string; isOpen?: HdsAdvancedTableExpandState; diff --git a/packages/components/src/components/hds/advanced-table/th-context-menu.hbs b/packages/components/src/components/hds/advanced-table/th-context-menu.hbs index fc6aba98feb..40ed5fc84d0 100644 --- a/packages/components/src/components/hds/advanced-table/th-context-menu.hbs +++ b/packages/components/src/components/hds/advanced-table/th-context-menu.hbs @@ -3,7 +3,12 @@ SPDX-License-Identifier: MPL-2.0 }} - + {{#each this._options as |option|}} th.textContent.trim()); + } + + const columns = [ + { key: 'name', label: 'Name' }, + { key: 'age', label: 'Age' }, + { key: 'country', label: 'Country' }, + ]; + + this.setProperties({ + columns, + model: [ + { name: 'Bob', age: 20, country: 'USA' }, + { name: 'Alice', age: 25, country: 'UK' }, + { name: 'Charlie', age: 30, country: 'Canada' }, + ], + }); + + await render(hbs` + <:body as |B|> + + {{B.data.name}} + {{B.data.age}} + {{B.data.country}} + + +`); + + assert.deepEqual(getColumnLabels(), ['Name', 'Age', 'Country']); + + this.set( + 'columns', + columns.map((column) => ({ + ...column, + label: `Updated ${column.label}`, + })), + ); + + assert.deepEqual(getColumnLabels(), [ + 'Updated Name', + 'Updated Age', + 'Updated Country', + ]); + }); + // OPTIONS // Sortable From c97c317965c5b640e396b1aa0dfa218867c472d5 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Thu, 19 Jun 2025 20:20:34 -0400 Subject: [PATCH 86/89] responding to PR feedback --- .../mock/app/main/generic-advanced-table.gts | 6 ++-- showcase/app/styles/_globals.scss | 4 +-- .../styles/showcase-pages/advanced-table.scss | 33 ++++++++++++++----- .../templates/components/advanced-table.hbs | 29 ++++++++++++++++ 4 files changed, 57 insertions(+), 15 deletions(-) diff --git a/showcase/app/components/mock/app/main/generic-advanced-table.gts b/showcase/app/components/mock/app/main/generic-advanced-table.gts index 58b94f315f7..c238ae7606e 100644 --- a/showcase/app/components/mock/app/main/generic-advanced-table.gts +++ b/showcase/app/components/mock/app/main/generic-advanced-table.gts @@ -33,19 +33,19 @@ const SAMPLE_COLUMNS = [ label: 'Project name', key: 'project-name', isSortable: true, - width: '300px', + width: 'max-content', }, { label: 'Current run ID', key: 'current-run-id', isSortable: true, - width: '300px', + width: 'max-content', }, { label: 'Run status', key: 'run-status', isSortable: true, - width: '200px', + width: 'max-content', }, { label: 'Current run applied', diff --git a/showcase/app/styles/_globals.scss b/showcase/app/styles/_globals.scss index 58a39acc92c..1447a120e7e 100644 --- a/showcase/app/styles/_globals.scss +++ b/showcase/app/styles/_globals.scss @@ -3,7 +3,7 @@ * SPDX-License-Identifier: MPL-2.0 */ - @use "./typography" as *; +@use "./typography" as *; *, *::before, @@ -223,8 +223,6 @@ body { } } - - // Percy (percySnapshot) doesn't allow to target specific DOM elements, so we have to "blacklist" the elements // that we want to exclude from the snapshots using their own "Percy-specific CSS". // see: https://docs.percy.io/docs/percy-specific-css#section-hiding-regions-with-percy-specific-css diff --git a/showcase/app/styles/showcase-pages/advanced-table.scss b/showcase/app/styles/showcase-pages/advanced-table.scss index 5bcbaf499f9..18c510c7767 100644 --- a/showcase/app/styles/showcase-pages/advanced-table.scss +++ b/showcase/app/styles/showcase-pages/advanced-table.scss @@ -10,7 +10,14 @@ body.components-advanced-table { .shw-component-advanced-table-instruction-list { font-size: 1rem; - font-family: gilmer-web, Gilmer, -apple-system, blinkmacsystemfont, "Segoe UI", roboto, sans-serif; + font-family: + gilmer-web, + Gilmer, + -apple-system, + blinkmacsystemfont, + "Segoe UI", + roboto, + sans-serif; line-height: 1.4; } @@ -18,16 +25,17 @@ body.components-advanced-table { width: fit-content; } - // align table cell content for icon plus text in cell - .shw-component-advanced-table-cell-content-div { - display: flex; - align-items: center; + // align table cell content for icon plus text in cell + .shw-component-advanced-table-cell-content-div { + display: flex; + align-items: center; + min-width: 0; - .hds-icon { - flex: none; - margin-right: 5px; - } + .hds-icon { + flex: none; + margin-right: 5px; } + } .shw-component-advanced-table-fixed-width-wrapper { width: 400px; @@ -68,4 +76,11 @@ body.components-advanced-table { animation: shw-component-table-user-bounce 1s infinite ease-in-out; } + // utility class for text truncation + .shw-component-advanced-table-text-truncate { + width: 100%; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } } diff --git a/showcase/app/templates/components/advanced-table.hbs b/showcase/app/templates/components/advanced-table.hbs index 4f4dbc9ca30..ad33a6b36ad 100644 --- a/showcase/app/templates/components/advanced-table.hbs +++ b/showcase/app/templates/components/advanced-table.hbs @@ -599,6 +599,35 @@ + Resizable columns with truncated cell content + + + <:body as |B|> + + {{B.data.artist}} + +
+ + + {{B.data.album}} + +
+
+ + {{B.data.quote}} + +
+ +
+ Functional examples From 523cd8e0b40b9b463bdbd3dc0d6077ff125ec84e Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Thu, 19 Jun 2025 20:28:15 -0400 Subject: [PATCH 87/89] removed unneeded import --- .../integration/components/hds/advanced-table/index-test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/showcase/tests/integration/components/hds/advanced-table/index-test.js b/showcase/tests/integration/components/hds/advanced-table/index-test.js index 23da4ed22c8..30cc487bcaf 100644 --- a/showcase/tests/integration/components/hds/advanced-table/index-test.js +++ b/showcase/tests/integration/components/hds/advanced-table/index-test.js @@ -13,7 +13,6 @@ import { find, triggerEvent, triggerKeyEvent, - findAll, } from '@ember/test-helpers'; import { hbs } from 'ember-cli-htmlbars'; import sinon from 'sinon'; From dcc820310503b445fee660bd644a373324d746b9 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Thu, 19 Jun 2025 21:07:03 -0400 Subject: [PATCH 88/89] added column resize option to context menu --- .../hds/advanced-table/th-context-menu.ts | 33 +++++++++++++++---- .../components/hds/advanced-table/th-sort.hbs | 2 ++ .../components/hds/advanced-table/th-sort.ts | 11 +++++++ .../src/components/hds/advanced-table/th.hbs | 2 ++ .../src/components/hds/advanced-table/th.ts | 10 ++++++ .../hds/advanced-table/index-test.js | 23 ++++++++++--- 6 files changed, 70 insertions(+), 11 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/th-context-menu.ts b/packages/components/src/components/hds/advanced-table/th-context-menu.ts index 1dd07caea1c..f4cda0fc664 100644 --- a/packages/components/src/components/hds/advanced-table/th-context-menu.ts +++ b/packages/components/src/components/hds/advanced-table/th-context-menu.ts @@ -10,6 +10,8 @@ import type HdsAdvancedTableColumn from './models/column.ts'; import type { HdsDropdownSignature } from '../dropdown/index.ts'; import type { HdsDropdownToggleIconSignature } from '../dropdown/toggle/icon.ts'; import type { HdsAdvancedTableSignature } from './index.ts'; +import { tracked } from '@glimmer/tracking'; +import type { HdsAdvancedTableThResizeHandleSignature } from './th-resize-handle.ts'; interface HdsAdvancedTableThContextMenuOption { key: string; @@ -29,29 +31,46 @@ export interface HdsAdvancedTableThContextMenuSignature { previousColumn?: HdsAdvancedTableColumn; nextColumn?: HdsAdvancedTableColumn; hasResizableColumns?: boolean; + resizeHandleElement: HdsAdvancedTableThResizeHandleSignature['Element']; onColumnResize?: HdsAdvancedTableSignature['Args']['onColumnResize']; }; Element: HdsDropdownSignature['Element']; } export default class HdsAdvancedTableThContextMenu extends Component { + @tracked private _element!: HdsDropdownSignature['Element']; + get _options(): HdsAdvancedTableThContextMenuOption[] { const { hasResizableColumns } = this.args; - const options: HdsAdvancedTableThContextMenuOption[] = []; + let options: HdsAdvancedTableThContextMenuOption[] = []; if (hasResizableColumns) { - options.push({ - key: 'reset-column-width', - label: 'Reset column width', - icon: 'rotate-ccw', - action: this.resetColumnWidth.bind(this), - }); + options = [ + ...options, + { + key: 'resize-column', + label: 'Resize column', + icon: 'rotate-ccw', + action: this.resizeColumn.bind(this), + }, + { + key: 'reset-column-width', + label: 'Reset column width', + icon: 'rotate-ccw', + action: this.resetColumnWidth.bind(this), + }, + ]; } return options; } + @action + resizeColumn() { + this.args.resizeHandleElement.focus(); + } + @action resetColumnWidth( column: HdsAdvancedTableColumn, diff --git a/packages/components/src/components/hds/advanced-table/th-sort.hbs b/packages/components/src/components/hds/advanced-table/th-sort.hbs index f04a3fa5c45..caeff239a9a 100644 --- a/packages/components/src/components/hds/advanced-table/th-sort.hbs +++ b/packages/components/src/components/hds/advanced-table/th-sort.hbs @@ -50,6 +50,7 @@ @previousColumn={{@previousColumn}} @nextColumn={{@nextColumn}} @hasResizableColumns={{@hasResizableColumns}} + @resizeHandleElement={{this._resizeHandleElement}} @onColumnResize={{@onColumnResize}} /> {{/if}} @@ -61,6 +62,7 @@ @hasResizableColumns={{@hasResizableColumns}} @tableHeight={{@tableHeight}} @onColumnResize={{@onColumnResize}} + {{this._registerResizeHandleElement}} /> {{/if}} {{/if}} diff --git a/packages/components/src/components/hds/advanced-table/th-sort.ts b/packages/components/src/components/hds/advanced-table/th-sort.ts index 3e9e6334e06..e01c0f4a731 100644 --- a/packages/components/src/components/hds/advanced-table/th-sort.ts +++ b/packages/components/src/components/hds/advanced-table/th-sort.ts @@ -11,6 +11,7 @@ import { tracked } from '@glimmer/tracking'; import { focusable, type FocusableElement } from 'tabbable'; import HdsAdvancedTableColumn from './models/column.ts'; import type Owner from '@ember/owner'; +import { modifier } from 'ember-modifier'; import { HdsAdvancedTableHorizontalAlignmentValues, @@ -26,6 +27,7 @@ import type { HdsAdvancedTableThButtonSortSignature } from './th-button-sort.ts' import { onFocusTrapDeactivate } from '../../../modifiers/hds-advanced-table-cell/dom-management.ts'; import type { HdsAdvancedTableThSignature } from './th.ts'; import type { HdsAdvancedTableSignature } from './index.ts'; +import type { HdsAdvancedTableThResizeHandleSignature } from './th-resize-handle.ts'; export const ALIGNMENTS: string[] = Object.values( HdsAdvancedTableHorizontalAlignmentValues @@ -59,7 +61,10 @@ export interface HdsAdvancedTableThSortSignature { export default class HdsAdvancedTableThSort extends Component { private _labelId = guidFor(this); private _element!: HTMLDivElement; + @tracked private _shouldTrapFocus = false; + @tracked + private _resizeHandleElement?: HdsAdvancedTableThResizeHandleSignature['Element']; constructor(owner: Owner, args: HdsAdvancedTableThSortSignature['Args']) { super(owner, args); @@ -140,4 +145,10 @@ export default class HdsAdvancedTableThSort extends Component { + this._resizeHandleElement = element; + } + ); } diff --git a/packages/components/src/components/hds/advanced-table/th.hbs b/packages/components/src/components/hds/advanced-table/th.hbs index 8e3ce1d7966..92bfbbffa99 100644 --- a/packages/components/src/components/hds/advanced-table/th.hbs +++ b/packages/components/src/components/hds/advanced-table/th.hbs @@ -72,6 +72,7 @@ @previousColumn={{@previousColumn}} @nextColumn={{@nextColumn}} @hasResizableColumns={{@hasResizableColumns}} + @resizeHandleElement={{this._resizeHandleElement}} @onColumnResize={{@onColumnResize}} /> {{/if}} @@ -83,6 +84,7 @@ @hasResizableColumns={{@hasResizableColumns}} @tableHeight={{@tableHeight}} @onColumnResize={{@onColumnResize}} + {{this._registerResizeHandleElement}} /> {{/if}} {{/if}} diff --git a/packages/components/src/components/hds/advanced-table/th.ts b/packages/components/src/components/hds/advanced-table/th.ts index c5fa324b4a9..c7f0691a26f 100644 --- a/packages/components/src/components/hds/advanced-table/th.ts +++ b/packages/components/src/components/hds/advanced-table/th.ts @@ -21,6 +21,7 @@ import type { HdsAdvancedTableExpandState, } from './types.ts'; import type { HdsAdvancedTableSignature } from './index.ts'; +import type { HdsAdvancedTableThResizeHandleSignature } from './th-resize-handle.ts'; export const ALIGNMENTS: string[] = Object.values( HdsAdvancedTableHorizontalAlignmentValues @@ -63,7 +64,10 @@ export interface HdsAdvancedTableThSignature { export default class HdsAdvancedTableTh extends Component { private _labelId = this.args.newLabel ? this.args.newLabel : guidFor(this); private _element!: HTMLDivElement; + @tracked private _shouldTrapFocus = false; + @tracked + private _resizeHandleElement?: HdsAdvancedTableThResizeHandleSignature['Element']; constructor(owner: Owner, args: HdsAdvancedTableThSignature['Args']) { super(owner, args); @@ -165,6 +169,12 @@ export default class HdsAdvancedTableTh extends Component { + this._resizeHandleElement = element; + } + ); + private _manageExpandButton = modifier((button: HTMLButtonElement) => { const { didInsertExpandButton, willDestroyExpandButton } = this.args; if (typeof didInsertExpandButton === 'function') { diff --git a/showcase/tests/integration/components/hds/advanced-table/index-test.js b/showcase/tests/integration/components/hds/advanced-table/index-test.js index 30cc487bcaf..8a0e8c912e7 100644 --- a/showcase/tests/integration/components/hds/advanced-table/index-test.js +++ b/showcase/tests/integration/components/hds/advanced-table/index-test.js @@ -29,12 +29,12 @@ function getTableGridValues(tableElement) { return gridValues; } -async function resetColumnWidth(th) { +async function performContextMenuAction(th, key) { const contextMenuToggle = th.querySelector('.hds-dropdown-toggle-icon'); await click(contextMenuToggle); - return click('[data-test-context-option-key="reset-column-width"]'); + return click(`[data-test-context-option-key="${key}"]`); } async function simulateRightPointerDrag(handle) { @@ -1656,7 +1656,7 @@ module('Integration | Component | hds/advanced-table/index', function (hooks) { 'Grid values changed after drag', ); - await resetColumnWidth(th); + await performContextMenuAction(th, 'reset-column-width'); newGridValues = getTableGridValues(table); assert.deepEqual( @@ -1666,6 +1666,18 @@ module('Integration | Component | hds/advanced-table/index', function (hooks) { ); }); + test('it should focus the resize handle when the "resize column" context menu option is clicked', async function (assert) { + setResizableColumnsTableData(this); + await render(hbsResizableColumnsAdvancedTable); + + const handle = find('.hds-advanced-table__th-resize-handle'); + const th = handle.closest('.hds-advanced-table__th'); + + await performContextMenuAction(th, 'resize-column'); + + assert.ok(handle === document.activeElement, 'Resize handle is focused'); + }); + test('it should call `onColumnResize` when a column is resized by dragging', async function (assert) { setResizableColumnsTableData(this); const onColumnResizeSpy = sinon.spy(); @@ -1757,7 +1769,10 @@ module('Integration | Component | hds/advanced-table/index', function (hooks) { assert.ok(onColumnResizeSpy.calledOnce, 'onColumnResize was called'); - await resetColumnWidth(handle.closest('.hds-advanced-table__th')); + await performContextMenuAction( + handle.closest('.hds-advanced-table__th'), + 'reset-column-width', + ); assert.ok( onColumnResizeSpy.calledTwice, 'onColumnResize was called again after resetting column width', From a1e96f7bbfd4f12842d2c9b8c74c43b4bc9b0edd Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Thu, 19 Jun 2025 21:35:02 -0400 Subject: [PATCH 89/89] fixing bug --- .../src/components/hds/advanced-table/th-context-menu.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/th-context-menu.ts b/packages/components/src/components/hds/advanced-table/th-context-menu.ts index f4cda0fc664..48a0f503098 100644 --- a/packages/components/src/components/hds/advanced-table/th-context-menu.ts +++ b/packages/components/src/components/hds/advanced-table/th-context-menu.ts @@ -31,7 +31,7 @@ export interface HdsAdvancedTableThContextMenuSignature { previousColumn?: HdsAdvancedTableColumn; nextColumn?: HdsAdvancedTableColumn; hasResizableColumns?: boolean; - resizeHandleElement: HdsAdvancedTableThResizeHandleSignature['Element']; + resizeHandleElement?: HdsAdvancedTableThResizeHandleSignature['Element']; onColumnResize?: HdsAdvancedTableSignature['Args']['onColumnResize']; }; Element: HdsDropdownSignature['Element']; @@ -68,7 +68,7 @@ export default class HdsAdvancedTableThContextMenu extends Component