From 70d55590d3ba2106f58b4ca0802cc74a3bb81a90 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Mon, 7 Jul 2025 17:46:42 -0400 Subject: [PATCH 01/98] add reorder components --- .../advanced-table/th-reorder-drop-target.hbs | 12 ++ .../advanced-table/th-reorder-drop-target.ts | 149 ++++++++++++++++++ .../hds/advanced-table/th-reorder-handle.hbs | 8 + .../hds/advanced-table/th-reorder-handle.ts | 83 ++++++++++ 4 files changed, 252 insertions(+) create mode 100644 packages/components/src/components/hds/advanced-table/th-reorder-drop-target.hbs create mode 100644 packages/components/src/components/hds/advanced-table/th-reorder-drop-target.ts create mode 100644 packages/components/src/components/hds/advanced-table/th-reorder-handle.hbs create mode 100644 packages/components/src/components/hds/advanced-table/th-reorder-handle.ts diff --git a/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.hbs b/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.hbs new file mode 100644 index 00000000000..4cdba6c12c9 --- /dev/null +++ b/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.hbs @@ -0,0 +1,12 @@ + - + --}} \ No newline at end of file From 4ed5a9f582a74aa86618c2589fe52f963232f66c Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Tue, 8 Jul 2025 18:43:07 -0400 Subject: [PATCH 05/98] working on styling --- .../hds/advanced-table/th-reorder-handle.hbs | 4 +- .../src/components/hds/advanced-table/th.hbs | 43 +++++++++++++------ .../src/styles/components/advanced-table.scss | 9 +++- 3 files changed, 40 insertions(+), 16 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/th-reorder-handle.hbs b/packages/components/src/components/hds/advanced-table/th-reorder-handle.hbs index 750c69cf9ea..e734be29a99 100644 --- a/packages/components/src/components/hds/advanced-table/th-reorder-handle.hbs +++ b/packages/components/src/components/hds/advanced-table/th-reorder-handle.hbs @@ -5,4 +5,6 @@ {{on "dragstart" this.handleDragStart}} {{on "dragend" this.handleDragEnd}} ...attributes -/> \ No newline at end of file +> + + \ 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 fda609af243..9703886d3b9 100644 --- a/packages/components/src/components/hds/advanced-table/th.hbs +++ b/packages/components/src/components/hds/advanced-table/th.hbs @@ -66,24 +66,39 @@ {{/if}} {{#if @column}} - {{#if this.showContextMenu}} - + {{#if (or @hasReorderableColumns this.showContextMenu)}} + + {{#if @hasReorderableColumns}} + + {{/if}} + + {{#if this.showContextMenu}} + + {{/if}} + {{/if}} - {{#if @hasReorderableColumns}} - {{/if}} diff --git a/packages/components/src/styles/components/advanced-table.scss b/packages/components/src/styles/components/advanced-table.scss index 791e452706f..b91f32548f7 100644 --- a/packages/components/src/styles/components/advanced-table.scss +++ b/packages/components/src/styles/components/advanced-table.scss @@ -177,7 +177,14 @@ $hds-advanced-table-drag-preview-background-color: rgba(204, 227, 254, 30%); background-color: var(--token-color-surface-primary); border: 1px solid $hds-advanced-table-border-color; border-radius: var(--token-border-radius-x-small); - transform: translateX(-50%); + + &:hover { + cursor: grab; + } + + &:active { + cursor: grabbing; + } } .hds-advanced-table__th-resize-handle { From adcc00dc7a234c7447666b4e6d3b58415f26d0f2 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Wed, 9 Jul 2025 08:47:27 -0400 Subject: [PATCH 06/98] adding tests --- .../hds/advanced-table/index-test.js | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) 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 8a0e8c912e7..cb4469ecefa 100644 --- a/showcase/tests/integration/components/hds/advanced-table/index-test.js +++ b/showcase/tests/integration/components/hds/advanced-table/index-test.js @@ -43,6 +43,18 @@ async function simulateRightPointerDrag(handle) { await triggerEvent(window, 'pointerup'); } +async function simulateColumnReorderDrag(handle, target) { + await triggerEvent(handle, 'pointerdown'); + + // get coordinates of the target element + const targetRect = target.getBoundingClientRect(); + const clientX = targetRect.left + targetRect.width / 2; + const clientY = targetRect.top + targetRect.height / 2; + + await triggerEvent(handle, 'pointermove', { clientX, clientY }); + 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', [ @@ -160,6 +172,19 @@ const setNestedTableData = (context) => { ]); }; +const setReorderableColumnsTableData = (context) => { + context.set('model', [ + { id: '1', artist: 'Nick Drake', album: 'Pink Moon', year: '1972' }, + { id: '2', artist: 'The Beatles', album: 'Abbey Road', year: '1969' }, + { id: '3', artist: 'Melanie', album: 'Candles in the Rain', year: '1971' }, + ]); + context.set('columns', [ + { key: 'artist', label: 'Artist' }, + { key: 'album', label: 'Album' }, + { key: 'year', label: 'Year' }, + ]); +}; + const setResizableColumnsTableData = (context) => { context.set('model', [ { id: '1', col1: 'A', col2: 'B' }, @@ -243,6 +268,55 @@ const hbsResizableColumnsAdvancedTable = hbs``, + ); + + assert + .dom('.hds-advanced-table__th-reorder-handle') + .doesNotExist( + 'No reorder handles are rendered when reordering is disabled', + ); + + this.set('hasReorderableColumns', true); + + assert + .dom('.hds-advanced-table__th-reorder-handle') + .exists({ count: 3 }, 'All columns have a reorder handle'); + }); + + test('dragging a column renders its drop target as a placeholder', async function (assert) { + await render( + hbs``, + ); + + const reorderHandle = find('.hds-advanced-table__th-reorder-handle'); + const targetColumn = find('.hds-advanced-table__th:nth-of-type(2)'); + + await simulateColumnReorderDrag(reorderHandle, targetColumn); + + await this.pauseTest(); + }); + }); + test('it should render the component with a CSS class that matches the component name', async function (assert) { setSortableTableData(this); From cdf739b5896bd8b5da747839b2adc923bcb42c01 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Wed, 9 Jul 2025 18:32:50 -0400 Subject: [PATCH 07/98] working on adding tests for drag/drop behavior --- .../hds/advanced-table/index-test.js | 227 +++++++++++++++++- 1 file changed, 215 insertions(+), 12 deletions(-) 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 cb4469ecefa..baaebb4ad98 100644 --- a/showcase/tests/integration/components/hds/advanced-table/index-test.js +++ b/showcase/tests/integration/components/hds/advanced-table/index-test.js @@ -11,6 +11,7 @@ import { focus, setupOnerror, find, + findAll, triggerEvent, triggerKeyEvent, } from '@ember/test-helpers'; @@ -43,16 +44,100 @@ async function simulateRightPointerDrag(handle) { await triggerEvent(window, 'pointerup'); } -async function simulateColumnReorderDrag(handle, target) { - await triggerEvent(handle, 'pointerdown'); +function getColumnByLabel(columns, label) { + return columns.find((col) => col.label === label); +} - // get coordinates of the target element - const targetRect = target.getBoundingClientRect(); - const clientX = targetRect.left + targetRect.width / 2; - const clientY = targetRect.top + targetRect.height / 2; +async function getColumnOrder(columns) { + const thElements = await findAll('.hds-advanced-table__th'); - await triggerEvent(handle, 'pointermove', { clientX, clientY }); - await triggerEvent(window, 'pointerup'); + return thElements.map((th) => { + const column = getColumnByLabel(columns, th.textContent.trim()); + + return column ? column.key : null; + }); +} + +async function startReorderDrag(handleElement) { + return triggerEvent(handleElement, 'dragstart'); +} + +function getTargetElementFromColumnIndex(index) { + const dropTargets = findAll('.hds-advanced-table__th-reorder-drop-target'); + const target = dropTargets[index]; + + if (target === null) { + throw new Error( + `Target column at index ${index} not found after drag started.`, + ); + } + + return target; +} + +function getDragTargetPosition(targetElement, targetPosition) { + const targetRect = targetElement.getBoundingClientRect(); + let clientX; + + switch (targetPosition) { + case 'left': + clientX = targetRect.left + 1; + break; + case 'right': + clientX = targetRect.right - 1; + break; + default: + throw new Error( + `Invalid targetPosition: ${targetPosition}. Use 'left' or 'right'.`, + ); + } + + return { clientX, clientY: targetRect.top + targetRect.height / 2 }; +} + +async function dragOverTarget(target, { clientX, clientY }) { + await triggerEvent(target, 'dragenter', { clientX, clientY }); + await triggerEvent(target, 'dragover', { clientX, clientY }); +} + +async function simulateColumnReorderDrag({ + handleElement, + targetIndex, + targetPosition = 'left', +}) { + await startReorderDrag(handleElement); + + const target = getTargetElementFromColumnIndex(targetIndex); + const { clientX, clientY } = getDragTargetPosition(target, targetPosition); + + const eventOptions = { clientX, clientY }; + + await dragOverTarget(target, eventOptions); + + // return the target event options for further use, if needed + return { target, eventOptions }; +} + +async function simulateColumnReorderDrop({ + target, + handleElement, + eventOptions, +}) { + await triggerEvent(target, 'drop', eventOptions); + await triggerEvent(handleElement, 'dragend'); +} + +async function simulateColumnReorderDragAndDrop({ + handleElement, + targetIndex, + targetPosition = 'left', +}) { + const { target, eventOptions } = await simulateColumnReorderDrag({ + handleElement, + targetIndex, + targetPosition, + }); + await simulateColumnReorderDrop({ target, handleElement, eventOptions }); } // we're using this for multiple tests so we'll declare context once and use it when we need it. @@ -298,7 +383,77 @@ module('Integration | Component | hds/advanced-table/index', function (hooks) { .exists({ count: 3 }, 'All columns have a reorder handle'); }); - test('dragging a column renders its drop target as a placeholder', async function (assert) { + test('columns can be reordered by dragging and dropping', async function (assert) { + await render( + hbs``, + ); + + let columnOrder = await getColumnOrder(this.columns); + assert.deepEqual( + columnOrder, + this.columns.map((col) => col.key), + 'Initial column order is correct', + ); + + const expectedDropTargetIndex = 2; + const expectedDropTargetDropSide = 'right'; + + // get the first reorder handle + const reorderHandle = find('.hds-advanced-table__th-reorder-handle'); + + // drag to the right side of the last column + const { target, eventOptions } = await simulateColumnReorderDrag({ + handleElement: reorderHandle, + targetIndex: expectedDropTargetIndex, + targetPosition: expectedDropTargetDropSide, + }); + + // get all drop targets for test reference + const dropTargets = findAll( + '.hds-advanced-table__th-reorder-drop-target', + ); + const originDropTarget = dropTargets[0]; + const destinationDropTarget = dropTargets[expectedDropTargetIndex]; + + assert + .dom(originDropTarget) + .hasClass( + 'hds-advanced-table__th-reorder-drop-target--is-being-dragged', + 'First column is being dragged', + ); + assert + .dom(destinationDropTarget) + .hasClass( + 'hds-advanced-table__th-reorder-drop-target--is-dragging-over', + ) + .hasClass( + `hds-advanced-table__th-reorder-drop-target--is-dragging-over--${expectedDropTargetDropSide}`, + ); + + await simulateColumnReorderDrop({ + target, + handleElement: reorderHandle, + eventOptions, + }); + + columnOrder = await getColumnOrder(this.columns); + + assert + .dom('.hds-advanced-table__th-reorder-drop-target') + .doesNotExist('Drop targets are removed after drop'); + assert.deepEqual( + columnOrder, + [this.columns[1].key, this.columns[2].key, this.columns[0].key], + 'Columns are reordered correctly after drag and drop', + ); + }); + + test('dropping a target on the nearest side of the next sibling does not reorder columns', async function (assert) { await render( hbs``, ); + const initialColumnOrder = this.columns.map((col) => col.key); + + let columnOrder = await getColumnOrder(this.columns); + assert.deepEqual( + columnOrder, + initialColumnOrder, + 'Initial column order is correct', + ); + const reorderHandle = find('.hds-advanced-table__th-reorder-handle'); - const targetColumn = find('.hds-advanced-table__th:nth-of-type(2)'); - await simulateColumnReorderDrag(reorderHandle, targetColumn); + const { target, eventOptions } = await simulateColumnReorderDrag({ + handleElement: reorderHandle, + targetIndex: 1, + targetPosition: 'left', + }); + + const dropTargets = findAll( + '.hds-advanced-table__th-reorder-drop-target', + ); + const originDropTarget = dropTargets[0]; + const destinationDropTarget = dropTargets[1]; + + assert + .dom(originDropTarget) + .hasClass( + 'hds-advanced-table__th-reorder-drop-target--is-being-dragged', + 'First column is being dragged', + ); + assert + .dom(destinationDropTarget) + .doesNotHaveClass( + 'hds-advanced-table__th-reorder-drop-target--is-dragging-over', + ) + .doesNotHaveClass( + 'hds-advanced-table__th-reorder-drop-target--is-dragging-over--left', + ); - await this.pauseTest(); + await simulateColumnReorderDrop({ + target, + handleElement: reorderHandle, + eventOptions, + }); + + columnOrder = await getColumnOrder(this.columns); + + assert + .dom('.hds-advanced-table__th-reorder-drop-target') + .doesNotExist('Drop targets are removed after drop'); + assert.deepEqual( + columnOrder, + initialColumnOrder, + 'Columns order is unchanged after drop on the nearest side', + ); }); }); From 00ace6f8272d62302de7075fccaf3830e697f9a7 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Fri, 11 Jul 2025 13:45:43 -0600 Subject: [PATCH 08/98] cleaning up visuals --- .../components/hds/advanced-table/th-reorder-drop-target.hbs | 2 +- packages/components/src/styles/components/advanced-table.scss | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.hbs b/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.hbs index 4cdba6c12c9..591f360b4e5 100644 --- a/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.hbs +++ b/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.hbs @@ -2,7 +2,7 @@ class={{this.classNames}} aria-hidden="true" role="presentation" - {{style height=(if @tableHeight (concat (sub @tableHeight 3.5) "px"))}} + {{style height=(if @tableHeight (concat (sub @tableHeight 2) "px"))}} {{this._registerElement}} {{on "dragover" this.handleDragOver}} {{on "dragenter" this.handleDragEnter}} diff --git a/packages/components/src/styles/components/advanced-table.scss b/packages/components/src/styles/components/advanced-table.scss index b91f32548f7..d4c89a6d70a 100644 --- a/packages/components/src/styles/components/advanced-table.scss +++ b/packages/components/src/styles/components/advanced-table.scss @@ -148,7 +148,7 @@ $hds-advanced-table-drag-preview-background-color: rgba(204, 227, 254, 30%); // dragging over left .hds-advanced-table__th-reorder-drop-target--is-dragging-over--left::after { left: 0; - transform: translateX(-3px); + transform: translateX(-2.5px); } // dragging over left and is first @@ -159,7 +159,7 @@ $hds-advanced-table-drag-preview-background-color: rgba(204, 227, 254, 30%); // dragging over right .hds-advanced-table__th-reorder-drop-target--is-dragging-over--right::after { right: 0; - transform: translateX(3px); + transform: translateX(2.5px); } // dragging over right and is last From 433e9f4bf3d0c55d162e58eda00453a21d15d443 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Fri, 11 Jul 2025 14:25:54 -0600 Subject: [PATCH 09/98] styling the reorder handle --- .../src/styles/components/advanced-table.scss | 46 +++++++++++-------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/packages/components/src/styles/components/advanced-table.scss b/packages/components/src/styles/components/advanced-table.scss index d4c89a6d70a..133865ea119 100644 --- a/packages/components/src/styles/components/advanced-table.scss +++ b/packages/components/src/styles/components/advanced-table.scss @@ -89,6 +89,27 @@ $hds-advanced-table-drag-preview-background-color: rgba(204, 227, 254, 30%); .hds-advanced-table__tr { display: contents; + .hds-advanced-table__th-reorder-handle { + z-index: 2; + display: none; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + margin: -2px 0; + background-color: var(--token-color-surface-primary); + border: 1px solid $hds-advanced-table-border-color; + border-radius: var(--token-border-radius-x-small); + + &:hover { + cursor: grab; + } + + &:active { + cursor: grabbing; + } + } + .hds-advanced-table__th { position: relative; align-content: center; @@ -98,6 +119,11 @@ $hds-advanced-table-drag-preview-background-color: rgba(204, 227, 254, 30%); text-align: left; background-color: var(--token-color-surface-strong); + &:hover .hds-advanced-table__th-reorder-handle, + &.mock-hover .hds-advanced-table__th-reorder-handle { + display: flex; + } + &:focus, &.mock-focus { // the box shadow is 3px wide, so offsetting by 3px so the focus ring doesn't go outside the cell @@ -167,26 +193,6 @@ $hds-advanced-table-drag-preview-background-color: rgba(204, 227, 254, 30%); transform: translateX(0); } - .hds-advanced-table__th-reorder-handle { - position: absolute; - top: 0; - left: 50%; - z-index: 2; - width: 24px; - height: 24px; - background-color: var(--token-color-surface-primary); - border: 1px solid $hds-advanced-table-border-color; - border-radius: var(--token-border-radius-x-small); - - &:hover { - cursor: grab; - } - - &:active { - cursor: grabbing; - } - } - .hds-advanced-table__th-resize-handle { @include hds-focus-ring-with-pseudo-element($position: absolute); From 77a4e10e0490b559b6284e28d1a4c1af62c17c70 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Mon, 14 Jul 2025 19:58:23 -0700 Subject: [PATCH 10/98] styling reorderable columns --- .../src/components/hds/advanced-table/th.hbs | 40 +++++++++---------- .../src/styles/components/advanced-table.scss | 17 ++++++-- 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/th.hbs b/packages/components/src/components/hds/advanced-table/th.hbs index 9703886d3b9..80be0be1054 100644 --- a/packages/components/src/components/hds/advanced-table/th.hbs +++ b/packages/components/src/components/hds/advanced-table/th.hbs @@ -66,29 +66,25 @@ {{/if}} {{#if @column}} - {{#if (or @hasReorderableColumns this.showContextMenu)}} - - {{#if @hasReorderableColumns}} - - {{/if}} + {{#if @hasReorderableColumns}} + + {{/if}} - {{#if this.showContextMenu}} - - {{/if}} - + {{#if this.showContextMenu}} + {{/if}} {{#if @hasResizableColumns}} diff --git a/packages/components/src/styles/components/advanced-table.scss b/packages/components/src/styles/components/advanced-table.scss index 133865ea119..2b933b2a949 100644 --- a/packages/components/src/styles/components/advanced-table.scss +++ b/packages/components/src/styles/components/advanced-table.scss @@ -90,16 +90,24 @@ $hds-advanced-table-drag-preview-background-color: rgba(204, 227, 254, 30%); display: contents; .hds-advanced-table__th-reorder-handle { + position: absolute; + top: -1px; + left: 50%; z-index: 2; display: none; align-items: center; justify-content: center; width: 24px; - height: 24px; - margin: -2px 0; + height: 16px; background-color: var(--token-color-surface-primary); border: 1px solid $hds-advanced-table-border-color; - border-radius: var(--token-border-radius-x-small); + border-radius: 0 0 var(--token-border-radius-small) var(--token-border-radius-small); + transform: translateX(-50%); + + .hds-icon { + width: 12px; + height: 12px; + } &:hover { cursor: grab; @@ -150,7 +158,8 @@ $hds-advanced-table-drag-preview-background-color: rgba(204, 227, 254, 30%); // is being dragged .hds-advanced-table__th-reorder-drop-target--is-being-dragged { - background-color: var(--token-color-surface-strong); + background-color: var(--token-color-surface-primary); + opacity: 0.5; } // is dragging over From f9c04c74735d339d0ddd2578914f6b37a8e67122 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Mon, 14 Jul 2025 20:37:11 -0700 Subject: [PATCH 11/98] adding dropdown context options for reordering --- .../hds/advanced-table/th-context-menu.hbs | 18 +-- .../hds/advanced-table/th-context-menu.ts | 110 ++++++++++++++---- .../src/components/hds/advanced-table/th.hbs | 12 +- 3 files changed, 102 insertions(+), 38 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 40ed5fc84d0..05ade16f23d 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 @@ -11,12 +11,16 @@ /> {{#each this._options as |option|}} - - {{option.label}} - + {{#if (eq option.key "separator")}} + + {{else if option.action}} + + {{option.label}} + + {{/if}} {{/each}} \ No newline at end of file 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 ab0f1093678..e90488fdfac 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 @@ -15,9 +15,9 @@ import type { HdsAdvancedTableThResizeHandleSignature } from './th-resize-handle interface HdsAdvancedTableThContextMenuOption { key: string; - label: string; - icon: HdsDropdownToggleIconSignature['Args']['icon']; - action: ( + label?: string; + icon?: HdsDropdownToggleIconSignature['Args']['icon']; + action?: ( column: HdsAdvancedTableColumn, previousColumn?: HdsAdvancedTableColumn, nextColumn?: HdsAdvancedTableColumn, @@ -31,6 +31,7 @@ export interface HdsAdvancedTableThContextMenuSignature { previousColumn?: HdsAdvancedTableColumn; nextColumn?: HdsAdvancedTableColumn; hasResizableColumns?: boolean; + hasReorderableColumns?: boolean; resizeHandleElement?: HdsAdvancedTableThResizeHandleSignature['Element']; onColumnResize?: HdsAdvancedTableSignature['Args']['onColumnResize']; }; @@ -41,29 +42,98 @@ export default class HdsAdvancedTableThContextMenu extends Component { + if (index > 0) { + return [{ key: 'separator' }, ...group]; + } + return group; + }); + + return optionsGroups.flat(); + } + + @action + reorderColumn( + _column: HdsAdvancedTableColumn, + _previousColumn?: HdsAdvancedTableColumn, + _nextColumn?: HdsAdvancedTableColumn, + dropdownCloseCallback?: () => void + ) { + console.log('Reorder column action triggered'); + dropdownCloseCallback?.(); + } + + @action + moveColumnToStart( + _column: HdsAdvancedTableColumn, + _previousColumn?: HdsAdvancedTableColumn, + _nextColumn?: HdsAdvancedTableColumn, + dropdownCloseCallback?: () => void + ) { + console.log('Move column to start action triggered'); + dropdownCloseCallback?.(); + } + + @action + moveColumnToEnd( + _column: HdsAdvancedTableColumn, + _previousColumn?: HdsAdvancedTableColumn, + _nextColumn?: HdsAdvancedTableColumn, + dropdownCloseCallback?: () => void + ) { + console.log('Move column to end action triggered'); + dropdownCloseCallback?.(); } @action diff --git a/packages/components/src/components/hds/advanced-table/th.hbs b/packages/components/src/components/hds/advanced-table/th.hbs index 80be0be1054..2f2ea9617ac 100644 --- a/packages/components/src/components/hds/advanced-table/th.hbs +++ b/packages/components/src/components/hds/advanced-table/th.hbs @@ -81,23 +81,13 @@ @column={{@column}} @previousColumn={{@previousColumn}} @nextColumn={{@nextColumn}} + @hasReorderableColumns={{@hasReorderableColumns}} @hasResizableColumns={{@hasResizableColumns}} @resizeHandleElement={{this._resizeHandleElement}} @onColumnResize={{@onColumnResize}} /> {{/if}} - {{#if @hasResizableColumns}} - - {{/if}} - {{#if (and @hasResizableColumns (not @isLastColumn))}} Date: Mon, 14 Jul 2025 21:42:49 -0700 Subject: [PATCH 12/98] working on context menu options for reordering --- .../hds/advanced-table/th-context-menu.ts | 21 ++++++++++--------- .../hds/advanced-table/th-reorder-handle.hbs | 2 ++ .../src/components/hds/advanced-table/th.hbs | 2 ++ .../src/components/hds/advanced-table/th.ts | 9 ++++++++ .../src/styles/components/advanced-table.scss | 4 +++- 5 files changed, 27 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 e90488fdfac..39de0cee3f5 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,14 +4,15 @@ */ import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; 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 type { HdsAdvancedTableSignature } from './index.ts'; -import { tracked } from '@glimmer/tracking'; import type { HdsAdvancedTableThResizeHandleSignature } from './th-resize-handle.ts'; +import type { HdsAdvancedTableThReorderHandleSignature } from './th-reorder-handle.ts'; interface HdsAdvancedTableThContextMenuOption { key: string; @@ -32,7 +33,12 @@ export interface HdsAdvancedTableThContextMenuSignature { nextColumn?: HdsAdvancedTableColumn; hasResizableColumns?: boolean; hasReorderableColumns?: boolean; + reorderHandleElement?: HdsAdvancedTableThReorderHandleSignature['Element']; resizeHandleElement?: HdsAdvancedTableThResizeHandleSignature['Element']; + onMoveColumnToPosition?: ( + column: HdsAdvancedTableColumn, + position: 'start' | 'end' + ) => void; onColumnResize?: HdsAdvancedTableSignature['Args']['onColumnResize']; }; Element: HdsDropdownSignature['Element']; @@ -74,7 +80,7 @@ export default class HdsAdvancedTableThContextMenu extends Component void - ) { - console.log('Reorder column action triggered'); - dropdownCloseCallback?.(); + moveColumn() { + console.log(this.args.reorderHandleElement); + this.args.reorderHandleElement?.focus(); } @action diff --git a/packages/components/src/components/hds/advanced-table/th-reorder-handle.hbs b/packages/components/src/components/hds/advanced-table/th-reorder-handle.hbs index e734be29a99..8771e3c1ea5 100644 --- a/packages/components/src/components/hds/advanced-table/th-reorder-handle.hbs +++ b/packages/components/src/components/hds/advanced-table/th-reorder-handle.hbs @@ -2,6 +2,8 @@
{{/if}} @@ -83,6 +84,7 @@ @nextColumn={{@nextColumn}} @hasReorderableColumns={{@hasReorderableColumns}} @hasResizableColumns={{@hasResizableColumns}} + @reorderHandleElement={{this._reorderHandleElement}} @resizeHandleElement={{this._resizeHandleElement}} @onColumnResize={{@onColumnResize}} /> diff --git a/packages/components/src/components/hds/advanced-table/th.ts b/packages/components/src/components/hds/advanced-table/th.ts index 91e911045c1..e406b10833c 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 { HdsAdvancedTableThReorderHandleSignature } from './th-reorder-handle.ts'; import type { HdsAdvancedTableThResizeHandleSignature } from './th-resize-handle.ts'; import type { HdsAdvancedTableSignature } from './index.ts'; @@ -74,6 +75,8 @@ export default class HdsAdvancedTableTh extends Component { + this._reorderHandleElement = element; + } + ); + private _registerResizeHandleElement = modifier( (element: HdsAdvancedTableThResizeHandleSignature['Element']) => { this._resizeHandleElement = element; diff --git a/packages/components/src/styles/components/advanced-table.scss b/packages/components/src/styles/components/advanced-table.scss index 2b933b2a949..079b435dd0e 100644 --- a/packages/components/src/styles/components/advanced-table.scss +++ b/packages/components/src/styles/components/advanced-table.scss @@ -128,7 +128,9 @@ $hds-advanced-table-drag-preview-background-color: rgba(204, 227, 254, 30%); background-color: var(--token-color-surface-strong); &:hover .hds-advanced-table__th-reorder-handle, - &.mock-hover .hds-advanced-table__th-reorder-handle { + &.mock-hover .hds-advanced-table__th-reorder-handle, + .hds-advanced-table__th-reorder-handle:focus, + .hds-advanced-table__th-reorder-handle.mock-focus { display: flex; } From 66c79d27d8b6e475f1eb63de7cdec2f7d962dec8 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Tue, 15 Jul 2025 17:52:03 -0700 Subject: [PATCH 13/98] can send column to first/last --- .../components/hds/advanced-table/index.hbs | 2 +- .../hds/advanced-table/models/table.ts | 60 ++++++++- .../hds/advanced-table/th-context-menu.hbs | 2 +- .../hds/advanced-table/th-context-menu.ts | 121 ++++++++---------- 4 files changed, 109 insertions(+), 76 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.hbs b/packages/components/src/components/hds/advanced-table/index.hbs index 28549fc53f8..499976b4c3d 100644 --- a/packages/components/src/components/hds/advanced-table/index.hbs +++ b/packages/components/src/components/hds/advanced-table/index.hbs @@ -85,7 +85,7 @@ @onColumnResize={{@onColumnResize}} @onReorderDragEnd={{fn (mut this._tableModel.reorderDraggedColumn) null}} @onReorderDragStart={{fn (mut this._tableModel.reorderDraggedColumn)}} - @onReorderDrop={{this._tableModel.reorderColumn}} + @onReorderDrop={{this._tableModel.moveColumnToDropTarget}} {{this._setColumnWidth column}} > {{column.label}} 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 589428b3d02..989d948b41c 100644 --- a/packages/components/src/components/hds/advanced-table/models/table.ts +++ b/packages/components/src/components/hds/advanced-table/models/table.ts @@ -17,6 +17,14 @@ import type { HdsAdvancedTableSortingFunction, } from '../types'; +enum HdsAdvancedTableColumnReorderSideValues { + Left = 'left', + Right = 'right', +} + +type HdsAdvancedTableColumnReorderSide = + `${HdsAdvancedTableColumnReorderSideValues}`; + type HdsAdvancedTableTableArgs = Pick< HdsAdvancedTableSignature['Args'], | 'model' @@ -276,13 +284,41 @@ export default class HdsAdvancedTableTableModel { } @action - reorderColumn(targetColumn: HdsAdvancedTableColumn, side: 'left' | 'right') { - const sourceColumn = this.reorderDraggedColumn; - - if (sourceColumn == null || sourceColumn === targetColumn) { + moveColumnToTerminalPosition( + column: HdsAdvancedTableColumn, + position: 'start' | 'end' + ): void { + const { + targetColumn, + side, + }: { + targetColumn?: HdsAdvancedTableColumn; + side: HdsAdvancedTableColumnReorderSide; + } = + position === 'start' + ? { + targetColumn: this.orderedColumns[0], + side: HdsAdvancedTableColumnReorderSideValues.Left, + } + : { + targetColumn: this.orderedColumns[this.orderedColumns.length - 1], + side: HdsAdvancedTableColumnReorderSideValues.Right, + }; + + if (targetColumn === undefined) { return; } + // Move the column to the target position + this.moveColumnToTarget(column, targetColumn, side); + } + + @action + moveColumnToTarget( + sourceColumn: HdsAdvancedTableColumn, + targetColumn: HdsAdvancedTableColumn, + side: HdsAdvancedTableColumnReorderSide + ): void { const oldIndex = this.orderedColumns.indexOf(sourceColumn); const newIndex = this.orderedColumns.indexOf(targetColumn); @@ -296,7 +332,7 @@ export default class HdsAdvancedTableTableModel { // If dropping to the left of the target, insert before the target // Adjust for the shift in indices caused by removing the source column const adjustedIndex = - side === 'right' + side === HdsAdvancedTableColumnReorderSideValues.Right ? newIndex > oldIndex ? newIndex : newIndex + 1 @@ -317,4 +353,18 @@ export default class HdsAdvancedTableTableModel { this.onColumnReorder?.(updated); } } + + @action + moveColumnToDropTarget( + targetColumn: HdsAdvancedTableColumn, + side: HdsAdvancedTableColumnReorderSide + ) { + const sourceColumn = this.reorderDraggedColumn; + + if (sourceColumn == null || sourceColumn === targetColumn) { + return; + } + + this.moveColumnToTarget(sourceColumn, targetColumn, side); + } } 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 05ade16f23d..f7aaf9741a0 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 @@ -17,7 +17,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 39de0cee3f5..5c4395309d6 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 @@ -13,19 +13,12 @@ import type { HdsDropdownToggleIconSignature } from '../dropdown/toggle/icon.ts' import type { HdsAdvancedTableSignature } from './index.ts'; import type { HdsAdvancedTableThResizeHandleSignature } from './th-resize-handle.ts'; import type { HdsAdvancedTableThReorderHandleSignature } from './th-reorder-handle.ts'; - interface HdsAdvancedTableThContextMenuOption { key: string; label?: string; icon?: HdsDropdownToggleIconSignature['Args']['icon']; - action?: ( - column: HdsAdvancedTableColumn, - previousColumn?: HdsAdvancedTableColumn, - nextColumn?: HdsAdvancedTableColumn, - dropdownCloseCallback?: () => void - ) => void; + action?: (dropdownCloseCallback?: () => void) => void; } - export interface HdsAdvancedTableThContextMenuSignature { Args: { column: HdsAdvancedTableColumn; @@ -35,10 +28,6 @@ export interface HdsAdvancedTableThContextMenuSignature { hasReorderableColumns?: boolean; reorderHandleElement?: HdsAdvancedTableThReorderHandleSignature['Element']; resizeHandleElement?: HdsAdvancedTableThResizeHandleSignature['Element']; - onMoveColumnToPosition?: ( - column: HdsAdvancedTableColumn, - position: 'start' | 'end' - ) => void; onColumnResize?: HdsAdvancedTableSignature['Args']['onColumnResize']; }; Element: HdsDropdownSignature['Element']; @@ -48,13 +37,12 @@ export default class HdsAdvancedTableThContextMenu extends Component this.moveColumnToPosition('start', close), }, + ]; + } + + if (!column.isLast) { + reorderableGroup = [ + ...reorderableGroup, { - key: 'reorder-column', + key: 'move-column-to-end', label: 'Move column to end', icon: 'end', - action: this.moveColumnToEnd.bind(this), + action: (close) => this.moveColumnToPosition('end', close), }, - ], - ]; - } - - // add a seperator between each group - optionsGroups = optionsGroups.map((group, index) => { - if (index > 0) { - return [{ key: 'separator' }, ...group]; + ]; } - return group; - }); - return optionsGroups.flat(); + allGroups = [...allGroups, reorderableGroup]; + } + + return allGroups.reduce( + (options, group, index) => { + // Add a separator before each group except the first + if (index > 0) { + return [...options, { key: 'separator' }, ...group]; + } + return [...options, ...group]; + }, + [] + ); } @action - moveColumn() { - console.log(this.args.reorderHandleElement); + private moveColumn() { this.args.reorderHandleElement?.focus(); } @action - moveColumnToStart( - _column: HdsAdvancedTableColumn, - _previousColumn?: HdsAdvancedTableColumn, - _nextColumn?: HdsAdvancedTableColumn, - dropdownCloseCallback?: () => void - ) { - console.log('Move column to start action triggered'); - dropdownCloseCallback?.(); + private resizeColumn() { + this.args.resizeHandleElement?.focus(); } @action - moveColumnToEnd( - _column: HdsAdvancedTableColumn, - _previousColumn?: HdsAdvancedTableColumn, - _nextColumn?: HdsAdvancedTableColumn, + private moveColumnToPosition( + position: 'start' | 'end', dropdownCloseCallback?: () => void - ) { - console.log('Move column to end action triggered'); - dropdownCloseCallback?.(); - } + ): void { + const { column } = this.args; - @action - resizeColumn() { - this.args.resizeHandleElement?.focus(); + column.table.moveColumnToTerminalPosition(column, position); + + dropdownCloseCallback?.(); } @action - resetColumnWidth( - column: HdsAdvancedTableColumn, - previousColumn?: HdsAdvancedTableColumn, - nextColumn?: HdsAdvancedTableColumn, - dropdownCloseCallback?: () => void - ): void { - const { onColumnResize } = this.args; + private resetColumnWidth(dropdownCloseCallback?: () => void): void { + const { onColumnResize, column, previousColumn, nextColumn } = this.args; previousColumn?.onNextColumnWidthRestored(column.imposedWidthDelta); nextColumn?.onPreviousColumnWidthRestored(); From 8238e273863126f1440576d9552452dd87210bce Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Tue, 15 Jul 2025 20:24:30 -0700 Subject: [PATCH 14/98] applying new reorder handle styles --- .../src/styles/components/advanced-table.scss | 26 +++++++++---------- .../page-components/advanced-table.hbs | 1 + 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/packages/components/src/styles/components/advanced-table.scss b/packages/components/src/styles/components/advanced-table.scss index 079b435dd0e..90a6c5394e7 100644 --- a/packages/components/src/styles/components/advanced-table.scss +++ b/packages/components/src/styles/components/advanced-table.scss @@ -91,31 +91,38 @@ $hds-advanced-table-drag-preview-background-color: rgba(204, 227, 254, 30%); .hds-advanced-table__th-reorder-handle { position: absolute; - top: -1px; + bottom: 0; left: 50%; z-index: 2; - display: none; + display: flex; align-items: center; justify-content: center; width: 24px; height: 16px; background-color: var(--token-color-surface-primary); border: 1px solid $hds-advanced-table-border-color; - border-radius: 0 0 var(--token-border-radius-small) var(--token-border-radius-small); - transform: translateX(-50%); + border-radius: var(--token-border-radius-small); + transform: translateX(-50%) translateY(50%); .hds-icon { width: 12px; height: 12px; } - &:hover { + &:hover, + &.mock-hover { cursor: grab; } - &:active { + &:active, + &.mock-active { cursor: grabbing; } + + &:focus, + &.mock-focus { + @include hds-focus-ring-with-pseudo-element($position: absolute); + } } .hds-advanced-table__th { @@ -127,13 +134,6 @@ $hds-advanced-table-drag-preview-background-color: rgba(204, 227, 254, 30%); text-align: left; background-color: var(--token-color-surface-strong); - &:hover .hds-advanced-table__th-reorder-handle, - &.mock-hover .hds-advanced-table__th-reorder-handle, - .hds-advanced-table__th-reorder-handle:focus, - .hds-advanced-table__th-reorder-handle.mock-focus { - display: flex; - } - &:focus, &.mock-focus { // the box shadow is 3px wide, so offsetting by 3px so the focus ring doesn't go outside the cell diff --git a/showcase/app/templates/page-components/advanced-table.hbs b/showcase/app/templates/page-components/advanced-table.hbs index f03d8fb13e5..7fbc8d8181e 100644 --- a/showcase/app/templates/page-components/advanced-table.hbs +++ b/showcase/app/templates/page-components/advanced-table.hbs @@ -12,6 +12,7 @@ @model={{this.model.music}} @columnOrder={{array "artist" "album" "year" "other"}} @hasReorderableColumns={{true}} + @hasResizableColumns={{true}} @columns={{array (hash key="artist" label="Artist" tooltip="More information.") (hash key="album" label="Album" tooltip="More information.") From 6787efac0798e33d2c03b783d4a374bffd8ddb22 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Tue, 15 Jul 2025 20:37:27 -0700 Subject: [PATCH 15/98] can step column with keyboard --- .../hds/advanced-table/models/table.ts | 26 +++++++++++++++++++ .../hds/advanced-table/th-reorder-handle.hbs | 1 + .../hds/advanced-table/th-reorder-handle.ts | 11 ++++++++ .../page-components/advanced-table.hbs | 1 - 4 files changed, 38 insertions(+), 1 deletion(-) 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 989d948b41c..9391d411e73 100644 --- a/packages/components/src/components/hds/advanced-table/models/table.ts +++ b/packages/components/src/components/hds/advanced-table/models/table.ts @@ -283,6 +283,32 @@ export default class HdsAdvancedTableTableModel { } } + @action + stepColumn(column: HdsAdvancedTableColumn, step: number): void { + const { table } = column; + const oldIndex = table.orderedColumns.indexOf(column); + const newIndex = oldIndex + step; + + // Check if the new position is within the array bounds. + if (newIndex < 0 || newIndex >= table.orderedColumns.length) { + return; + } + + const targetColumn = table.orderedColumns[newIndex]; + + if (targetColumn === undefined) { + return; + } + + // Determine the side based on the step direction. + const side: HdsAdvancedTableColumnReorderSide = + step > 0 + ? HdsAdvancedTableColumnReorderSideValues.Right + : HdsAdvancedTableColumnReorderSideValues.Left; + + table.moveColumnToTarget(column, targetColumn, side); + } + @action moveColumnToTerminalPosition( column: HdsAdvancedTableColumn, diff --git a/packages/components/src/components/hds/advanced-table/th-reorder-handle.hbs b/packages/components/src/components/hds/advanced-table/th-reorder-handle.hbs index 8771e3c1ea5..87c96782538 100644 --- a/packages/components/src/components/hds/advanced-table/th-reorder-handle.hbs +++ b/packages/components/src/components/hds/advanced-table/th-reorder-handle.hbs @@ -6,6 +6,7 @@ aria-label="Reorder {{@column.label}} column" {{on "dragstart" this.handleDragStart}} {{on "dragend" this.handleDragEnd}} + {{on "keydown" this.handleKeydown}} ...attributes > diff --git a/packages/components/src/components/hds/advanced-table/th-reorder-handle.ts b/packages/components/src/components/hds/advanced-table/th-reorder-handle.ts index 0baeae3439c..7949339ea21 100644 --- a/packages/components/src/components/hds/advanced-table/th-reorder-handle.ts +++ b/packages/components/src/components/hds/advanced-table/th-reorder-handle.ts @@ -80,4 +80,15 @@ export default class HdsAdvancedTableThReorderHandle extends Component Date: Tue, 15 Jul 2025 21:39:13 -0700 Subject: [PATCH 16/98] keyboard movement works --- .../hds/advanced-table/models/column.ts | 4 ++++ .../hds/advanced-table/th-reorder-handle.hbs | 1 + .../hds/advanced-table/th-reorder-handle.ts | 16 +++++++++++++++- 3 files changed, 20 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 eab94613acd..431d14a4775 100644 --- a/packages/components/src/components/hds/advanced-table/models/column.ts +++ b/packages/components/src/components/hds/advanced-table/models/column.ts @@ -6,6 +6,7 @@ import { tracked } from '@glimmer/tracking'; import { action } from '@ember/object'; +import type { HdsAdvancedTableThReorderHandleSignature } from '../th-reorder-handle.ts'; import type HdsAdvancedTableModel from './table.ts'; import type { HdsAdvancedTableCell, @@ -43,6 +44,9 @@ export default class HdsAdvancedTableColumn { @tracked imposedWidthDelta: number = 0; // used to track the width change imposed by the previous column @tracked isBeingDragged: boolean = false; + @tracked + reorderHandleElement?: HdsAdvancedTableThReorderHandleSignature['Element'] = + undefined; @tracked sortingFunction?: (a: unknown, b: unknown) => number = undefined; table: HdsAdvancedTableModel; diff --git a/packages/components/src/components/hds/advanced-table/th-reorder-handle.hbs b/packages/components/src/components/hds/advanced-table/th-reorder-handle.hbs index 87c96782538..43cdd0f9465 100644 --- a/packages/components/src/components/hds/advanced-table/th-reorder-handle.hbs +++ b/packages/components/src/components/hds/advanced-table/th-reorder-handle.hbs @@ -4,6 +4,7 @@ class={{this.classNames}} tabindex="0" aria-label="Reorder {{@column.label}} column" + {{this._registerElement}} {{on "dragstart" this.handleDragStart}} {{on "dragend" this.handleDragEnd}} {{on "keydown" this.handleKeydown}} diff --git a/packages/components/src/components/hds/advanced-table/th-reorder-handle.ts b/packages/components/src/components/hds/advanced-table/th-reorder-handle.ts index 7949339ea21..dbc2f3291a2 100644 --- a/packages/components/src/components/hds/advanced-table/th-reorder-handle.ts +++ b/packages/components/src/components/hds/advanced-table/th-reorder-handle.ts @@ -4,9 +4,11 @@ */ import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { modifier } from 'ember-modifier'; +import { scheduleOnce } from '@ember/runloop'; import type HdsAdvancedTableColumn from './models/column.ts'; -import { action } from '@ember/object'; export interface HdsAdvancedTableThReorderHandleSignature { Args: { @@ -39,12 +41,20 @@ function constructDragPreview(width: number, height?: number): HTMLDivElement { } export default class HdsAdvancedTableThReorderHandle extends Component { + private _registerElement = modifier((element: HTMLDivElement) => { + this.args.column.reorderHandleElement = element; + }); + get classNames(): string { const classes = ['hds-advanced-table__th-reorder-handle']; return classes.join(' '); } + private _focusReorderHandle(): void { + this.args.column.reorderHandleElement?.focus(); + } + @action handleDragStart(event: DragEvent): void { const { column, columnWidth, tableHeight, onReorderDragStart } = this.args; @@ -90,5 +100,9 @@ export default class HdsAdvancedTableThReorderHandle extends Component Date: Tue, 15 Jul 2025 21:51:29 -0700 Subject: [PATCH 17/98] added tests for context menu opptions --- .../hds/advanced-table/index-test.js | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) 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 baaebb4ad98..7551debb38a 100644 --- a/showcase/tests/integration/components/hds/advanced-table/index-test.js +++ b/showcase/tests/integration/components/hds/advanced-table/index-test.js @@ -518,6 +518,60 @@ module('Integration | Component | hds/advanced-table/index', function (hooks) { 'Columns order is unchanged after drop on the nearest side', ); }); + + test('it should show the context menu with the correct options when reordering is enabled', async function (assert) { + await render( + hbs``, + ); + + const thElements = findAll('.hds-advanced-table__th'); // find all header cells + + assert.ok( + thElements[0].querySelector('.hds-advanced-table__th-context-menu'), + 'context menu exists', + ); + + const firstContextMenuToggle = thElements[0].querySelector( + '.hds-dropdown-toggle-icon', + ); + await click(firstContextMenuToggle); + assert.dom('[data-test-context-option-key="reorder-column"]').exists(); + assert + .dom('[data-test-context-option-key="move-column-to-start"]') + .doesNotExist(); + assert + .dom('[data-test-context-option-key="move-column-to-end"]') + .exists(); + + const secondContextMenuToggle = thElements[1].querySelector( + '.hds-dropdown-toggle-icon', + ); + await click(secondContextMenuToggle); + assert.dom('[data-test-context-option-key="reorder-column"]').exists(); + assert + .dom('[data-test-context-option-key="move-column-to-start"]') + .exists(); + assert + .dom('[data-test-context-option-key="move-column-to-end"]') + .exists(); + + const lastContextMenuToggle = thElements[ + thElements.length - 1 + ].querySelector('.hds-dropdown-toggle-icon'); + await click(lastContextMenuToggle); + assert.dom('[data-test-context-option-key="reorder-column"]').exists(); + assert + .dom('[data-test-context-option-key="move-column-to-start"]') + .exists(); + assert + .dom('[data-test-context-option-key="move-column-to-end"]') + .doesNotExist(); + }); }); test('it should render the component with a CSS class that matches the component name', async function (assert) { From 00a31dbc0a819d3c9aa573d441545bca13cf16af Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Tue, 15 Jul 2025 22:08:15 -0700 Subject: [PATCH 18/98] adding tests for context menu action execution --- .../hds/advanced-table/index-test.js | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) 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 7551debb38a..15f2877cbf6 100644 --- a/showcase/tests/integration/components/hds/advanced-table/index-test.js +++ b/showcase/tests/integration/components/hds/advanced-table/index-test.js @@ -572,6 +572,83 @@ module('Integration | Component | hds/advanced-table/index', function (hooks) { .dom('[data-test-context-option-key="move-column-to-end"]') .doesNotExist(); }); + + test('clicking the "Move column" context menu option focuses the reorder handle', async function (assert) { + await render( + hbs``, + ); + + const thElements = findAll('.hds-advanced-table__th'); + + const firstContextMenuToggle = thElements[0].querySelector( + '.hds-dropdown-toggle-icon', + ); + await click(firstContextMenuToggle); + await click('[data-test-context-option-key="reorder-column"]'); + + const firstReorderHandle = thElements[0].querySelector( + '.hds-advanced-table__th-reorder-handle', + ); + + assert.dom(firstReorderHandle).isFocused(); + }); + + test('clicking the "Move column to start" context menu option moves the column to the start', async function (assert) { + await render( + hbs``, + ); + + const thElements = findAll('.hds-advanced-table__th'); + + const secondContextMenuToggle = thElements[1].querySelector( + '.hds-dropdown-toggle-icon', + ); + await click(secondContextMenuToggle); + await click('[data-test-context-option-key="move-column-to-start"]'); + + const columnOrder = await getColumnOrder(this.columns); + assert.deepEqual( + columnOrder, + [this.columns[1].key, this.columns[0].key, this.columns[2].key], + 'The second column is moved to the start', + ); + }); + + test('clicking the "Move column to end" context menu option moves the column to the end', async function (assert) { + await render( + hbs``, + ); + + const thElements = findAll('.hds-advanced-table__th'); + + const secondContextMenuToggle = thElements[1].querySelector( + '.hds-dropdown-toggle-icon', + ); + await click(secondContextMenuToggle); + await click('[data-test-context-option-key="move-column-to-end"]'); + + const columnOrder = await getColumnOrder(this.columns); + assert.deepEqual( + columnOrder, + [this.columns[0].key, this.columns[2].key, this.columns[1].key], + 'The second column is moved to the end', + ); + }); }); test('it should render the component with a CSS class that matches the component name', async function (assert) { From 67da9d3013923500e6676ac21cf35c94d8136df3 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Tue, 15 Jul 2025 22:13:09 -0700 Subject: [PATCH 19/98] added tests for reordering columns with focus/arrowkeys --- .../hds/advanced-table/index-test.js | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) 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 15f2877cbf6..d6edbb12a5a 100644 --- a/showcase/tests/integration/components/hds/advanced-table/index-test.js +++ b/showcase/tests/integration/components/hds/advanced-table/index-test.js @@ -649,6 +649,51 @@ module('Integration | Component | hds/advanced-table/index', function (hooks) { 'The second column is moved to the end', ); }); + + test('pressing "Left Arrow" and "Right Arrow" keys when the reorder handle is focused moves the column', async function (assert) { + await render( + hbs``, + ); + + const thElements = findAll('.hds-advanced-table__th'); + const firstReorderHandle = thElements[0].querySelector( + '.hds-advanced-table__th-reorder-handle', + ); + await focus(firstReorderHandle); + assert.dom(firstReorderHandle).isFocused(); + + await triggerKeyEvent(firstReorderHandle, 'keydown', 'ArrowRight'); + let columnOrder = await getColumnOrder(this.columns); + assert.deepEqual( + columnOrder, + [this.columns[1].key, this.columns[0].key, this.columns[2].key], + 'The first column is moved to the right', + ); + assert.dom(firstReorderHandle).isFocused(); + + await triggerKeyEvent(firstReorderHandle, 'keydown', 'ArrowRight'); + columnOrder = await getColumnOrder(this.columns); + assert.deepEqual( + columnOrder, + [this.columns[1].key, this.columns[2].key, this.columns[0].key], + 'The second column is moved to the right', + ); + assert.dom(firstReorderHandle).isFocused(); + + await triggerKeyEvent(firstReorderHandle, 'keydown', 'ArrowLeft'); + columnOrder = await getColumnOrder(this.columns); + assert.deepEqual( + columnOrder, + [this.columns[1].key, this.columns[0].key, this.columns[2].key], + 'The third column is moved back to the left', + ); + assert.dom(firstReorderHandle).isFocused(); + }); }); test('it should render the component with a CSS class that matches the component name', async function (assert) { From b168775acd377d2ab0ac3acc38ba157ba9b6ec01 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Wed, 16 Jul 2025 15:31:40 -0700 Subject: [PATCH 20/98] styling srag target --- .../src/components/hds/advanced-table/th.ts | 4 ++++ .../src/styles/components/advanced-table.scss | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/packages/components/src/components/hds/advanced-table/th.ts b/packages/components/src/components/hds/advanced-table/th.ts index e406b10833c..30bfafca33e 100644 --- a/packages/components/src/components/hds/advanced-table/th.ts +++ b/packages/components/src/components/hds/advanced-table/th.ts @@ -152,6 +152,10 @@ export default class HdsAdvancedTableTh extends Component Date: Wed, 16 Jul 2025 20:06:08 -0700 Subject: [PATCH 21/98] fixed reorder handle styles --- .../src/styles/components/advanced-table.scss | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/components/src/styles/components/advanced-table.scss b/packages/components/src/styles/components/advanced-table.scss index 826790fd58c..ad3c7af67f5 100644 --- a/packages/components/src/styles/components/advanced-table.scss +++ b/packages/components/src/styles/components/advanced-table.scss @@ -99,9 +99,11 @@ $hds-advanced-table-drag-preview-background-color: rgba(204, 227, 254, 30%); justify-content: center; width: 24px; height: 16px; - background-color: var(--token-color-surface-primary); - border: 1px solid $hds-advanced-table-border-color; + color: var(--token-color-foreground-faint); + background-color: var(--token-color-surface-interactive); + border: 1px solid var(--token-color-border-primary); border-radius: var(--token-border-radius-small); + box-shadow: var(--token-elevation-low-box-shadow); transform: translateX(-50%) translateY(50%); visibility: hidden; opacity: 0; @@ -113,17 +115,21 @@ $hds-advanced-table-drag-preview-background-color: rgba(204, 227, 254, 30%); &:hover, &.mock-hover { + background-color: var(--token-color-surface-interactive-hover); cursor: grab; } &:active, &.mock-active { + background-color: var(--token-color-surface-interactive-active); cursor: grabbing; } &:focus, &.mock-focus { @include hds-focus-ring-with-pseudo-element($position: absolute); + + background-color: var(--token-color-surface-interactive); } } From 9cbefdd893106f0fd110375eca6ba115c8bb6384 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Wed, 16 Jul 2025 20:25:02 -0700 Subject: [PATCH 22/98] added styles for focus-within --- packages/components/src/styles/components/advanced-table.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/components/src/styles/components/advanced-table.scss b/packages/components/src/styles/components/advanced-table.scss index ad3c7af67f5..2e702a3bcdf 100644 --- a/packages/components/src/styles/components/advanced-table.scss +++ b/packages/components/src/styles/components/advanced-table.scss @@ -158,7 +158,8 @@ $hds-advanced-table-drag-preview-background-color: rgba(204, 227, 254, 30%); } &:hover, - &.mock-hover { + &.mock-hover, + &:focus-within { .hds-advanced-table__th-reorder-handle { visibility: visible; opacity: 1; From c5f4f7809a02d32a6a5d03d39f03cc0166b30dc2 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Thu, 17 Jul 2025 16:29:29 -0700 Subject: [PATCH 23/98] working on programatic focus for reorder handle --- .../hds/advanced-table/models/column.ts | 17 +++++++++++++++++ .../hds/advanced-table/th-context-menu.ts | 10 ++++++++-- .../hds/advanced-table/th-reorder-handle.ts | 10 +++++----- .../src/components/hds/advanced-table/th.ts | 4 ++++ 4 files changed, 34 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 431d14a4775..c019a441572 100644 --- a/packages/components/src/components/hds/advanced-table/models/column.ts +++ b/packages/components/src/components/hds/advanced-table/models/column.ts @@ -44,6 +44,7 @@ export default class HdsAdvancedTableColumn { @tracked imposedWidthDelta: number = 0; // used to track the width change imposed by the previous column @tracked isBeingDragged: boolean = false; + @tracked thElement?: HTMLDivElement = undefined; @tracked reorderHandleElement?: HdsAdvancedTableThReorderHandleSignature['Element'] = undefined; @@ -157,6 +158,22 @@ export default class HdsAdvancedTableColumn { this.maxWidth = maxWidth ?? DEFAULT_MAX_WIDTH; } + @action focusReorderHandle(): void { + if (this.thElement === undefined) { + return; + } + + // focus the th element first (parent) to ensure the handle is visible + this.thElement.focus({ preventScroll: true }); + + if (this.reorderHandleElement === undefined) { + return; + } + + // then focus the reorder handle element + this.reorderHandleElement.focus(); + } + // Sets the column width in pixels, ensuring it respects the min and max width constraints. @action setPxWidth(newPxWidth: number): void { 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 5c4395309d6..a09cf1baae4 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 { tracked } from '@glimmer/tracking'; import { action } from '@ember/object'; +import { scheduleOnce } from '@ember/runloop'; import type HdsAdvancedTableColumn from './models/column.ts'; import type { HdsDropdownSignature } from '../dropdown/index.ts'; @@ -65,7 +66,7 @@ export default class HdsAdvancedTableThContextMenu extends Component this.moveColumn(), }, ]; @@ -110,7 +111,12 @@ export default class HdsAdvancedTableThContextMenu extends Component Date: Sun, 20 Jul 2025 18:34:47 -0700 Subject: [PATCH 24/98] added reorder capabilities to sortable columns --- .../components/hds/advanced-table/index.hbs | 3 ++ .../components/hds/advanced-table/th-sort.hbs | 21 ++++++++ .../components/hds/advanced-table/th-sort.ts | 49 +++++++++++++++++-- .../src/components/hds/advanced-table/th.ts | 4 +- .../page-components/advanced-table.hbs | 44 ++++++++++++++++- 5 files changed, 115 insertions(+), 6 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.hbs b/packages/components/src/components/hds/advanced-table/index.hbs index 499976b4c3d..d47e4419814 100644 --- a/packages/components/src/components/hds/advanced-table/index.hbs +++ b/packages/components/src/components/hds/advanced-table/index.hbs @@ -60,6 +60,9 @@ @nextColumn={{get this._tableModel.columns (add index 1)}} @tableHeight={{this._tableHeight}} @onColumnResize={{@onColumnResize}} + @onReorderDragEnd={{fn (mut this._tableModel.reorderDraggedColumn) null}} + @onReorderDragStart={{fn (mut this._tableModel.reorderDraggedColumn)}} + @onReorderDrop={{this._tableModel.moveColumnToDropTarget}} {{this._setColumnWidth column}} > {{column.label}} 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 caeff239a9a..148ac9ced1d 100644 --- a/packages/components/src/components/hds/advanced-table/th-sort.hbs +++ b/packages/components/src/components/hds/advanced-table/th-sort.hbs @@ -44,12 +44,25 @@ /> {{#if @column}} + {{#if @hasReorderableColumns}} + + {{/if}} + {{#if this.showContextMenu}} @@ -68,4 +81,12 @@ {{/if}} + + {{#if @column.table.hasColumnBeingDragged}} + + {{/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 e01c0f4a731..7c0962ab447 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 { HdsAdvancedTableThSortOrder, HdsAdvancedTableThSortOrderLabels, } from './types.ts'; +import type { HdsAdvancedTableThReorderHandleSignature } from './th-reorder-handle.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'; @@ -38,7 +39,8 @@ export interface HdsAdvancedTableThSortSignature { Args: { column?: HdsAdvancedTableThSignature['Args']['column']; align?: HdsAdvancedTableHorizontalAlignment; - hasResizableColumns: HdsAdvancedTableSignature['Args']['hasResizableColumns']; + hasReorderableColumns?: HdsAdvancedTableSignature['Args']['hasReorderableColumns']; + hasResizableColumns?: HdsAdvancedTableSignature['Args']['hasResizableColumns']; onClickSort?: HdsAdvancedTableThButtonSortSignature['Args']['onClick']; sortOrder?: HdsAdvancedTableThSortOrder; tooltip?: string; @@ -51,6 +53,12 @@ export interface HdsAdvancedTableThSortSignature { isStickyColumn?: boolean; isStickyColumnPinned?: boolean; onColumnResize?: HdsAdvancedTableSignature['Args']['onColumnResize']; + onReorderDragEnd?: () => void; + onReorderDragStart?: (column: HdsAdvancedTableColumn) => void; + onReorderDrop?: ( + column: HdsAdvancedTableColumn, + side: 'left' | 'right' + ) => void; }; Blocks: { default?: []; @@ -64,6 +72,8 @@ export default class HdsAdvancedTableThSort extends Component { + this._reorderHandleElement = element; + } + ); + private _registerResizeHandleElement = modifier( (element: HdsAdvancedTableThResizeHandleSignature['Element']) => { this._resizeHandleElement = element; diff --git a/packages/components/src/components/hds/advanced-table/th.ts b/packages/components/src/components/hds/advanced-table/th.ts index dd8a03b1d9c..e09c65c5498 100644 --- a/packages/components/src/components/hds/advanced-table/th.ts +++ b/packages/components/src/components/hds/advanced-table/th.ts @@ -36,8 +36,8 @@ export interface HdsAdvancedTableThSignature { colspan?: number; depth?: number; hasExpandAllButton?: boolean; - hasReorderableColumns?: boolean; - hasResizableColumns?: boolean; + hasReorderableColumns?: HdsAdvancedTableSignature['Args']['hasReorderableColumns']; + hasResizableColumns?: HdsAdvancedTableSignature['Args']['hasResizableColumns']; isExpanded?: HdsAdvancedTableExpandState; isExpandable?: boolean; isLastColumn?: boolean; diff --git a/showcase/app/templates/page-components/advanced-table.hbs b/showcase/app/templates/page-components/advanced-table.hbs index f03d8fb13e5..74d3a437abf 100644 --- a/showcase/app/templates/page-components/advanced-table.hbs +++ b/showcase/app/templates/page-components/advanced-table.hbs @@ -8,7 +8,7 @@ AdvancedTable
- {{else if (eq cell.columnKey "year")}} + + + + {{else if (eq cell.columnKey "other")}} + + + + + + + + {{/if}} + {{/each}} + + + --}} + + + <:body as |B|> + + {{#each R.orderedCells as |cell|}} + {{#if (eq cell.columnKey "artist")}} + {{B.data.artist}} + {{else if (eq cell.columnKey "album")}} + +
+ + {{B.data.album}} +
+
+ {{else if (eq cell.columnKey "year")}} + From 19108ba3d9fa02b4ec3760353140582bcea0260a Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Sun, 20 Jul 2025 18:43:12 -0700 Subject: [PATCH 25/98] reordering resized columns preserves the column size --- .../src/components/hds/advanced-table/index.ts | 10 +++++----- .../app/templates/page-components/advanced-table.hbs | 1 + 2 files changed, 6 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 269630a4991..b243f7b2bd3 100644 --- a/packages/components/src/components/hds/advanced-table/index.ts +++ b/packages/components/src/components/hds/advanced-table/index.ts @@ -356,25 +356,25 @@ export default class HdsAdvancedTable extends Component 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 < columns.length; i++) { - style += ` ${columns[i]!.width ?? DEFAULT_COLUMN_WIDTH}`; + for (let i = 0; i < orderedColumns.length; i++) { + style += ` ${orderedColumns[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})`; + style += `repeat(${orderedColumns.length}, ${DEFAULT_COLUMN_WIDTH})`; } return style; diff --git a/showcase/app/templates/page-components/advanced-table.hbs b/showcase/app/templates/page-components/advanced-table.hbs index 74d3a437abf..8afcf2d887c 100644 --- a/showcase/app/templates/page-components/advanced-table.hbs +++ b/showcase/app/templates/page-components/advanced-table.hbs @@ -53,6 +53,7 @@ Date: Sun, 20 Jul 2025 19:09:54 -0700 Subject: [PATCH 26/98] resizing reordered columns works --- .../src/components/hds/advanced-table/index.hbs | 12 ++++++------ .../app/templates/page-components/advanced-table.hbs | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.hbs b/packages/components/src/components/hds/advanced-table/index.hbs index d47e4419814..a09cf94f678 100644 --- a/packages/components/src/components/hds/advanced-table/index.hbs +++ b/packages/components/src/components/hds/advanced-table/index.hbs @@ -53,11 +53,11 @@ @tooltip={{column.tooltip}} @hasReorderableColumns={{@hasReorderableColumns}} @hasResizableColumns={{@hasResizableColumns}} - @isLastColumn={{eq index (sub this._tableModel.columns.length 1)}} + @isLastColumn={{eq index (sub this._tableModel.orderedColumns.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)}} + @previousColumn={{get this._tableModel.orderedColumns (sub index 1)}} + @nextColumn={{get this._tableModel.orderedColumns (add index 1)}} @tableHeight={{this._tableHeight}} @onColumnResize={{@onColumnResize}} @onReorderDragEnd={{fn (mut this._tableModel.reorderDraggedColumn) null}} @@ -76,12 +76,12 @@ @hasResizableColumns={{@hasResizableColumns}} @isExpanded={{this._tableModel.expandState}} @isExpandable={{column.isExpandable}} - @isLastColumn={{eq index (sub this._tableModel.columns.length 1)}} + @isLastColumn={{eq index (sub this._tableModel.orderedColumns.length 1)}} @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)}} + @previousColumn={{get this._tableModel.orderedColumns (sub index 1)}} + @nextColumn={{get this._tableModel.orderedColumns (add index 1)}} @tableHeight={{this._tableHeight}} @tooltip={{column.tooltip}} @onClickToggle={{this._tableModel.toggleAll}} diff --git a/showcase/app/templates/page-components/advanced-table.hbs b/showcase/app/templates/page-components/advanced-table.hbs index 8afcf2d887c..ab5783cfce9 100644 --- a/showcase/app/templates/page-components/advanced-table.hbs +++ b/showcase/app/templates/page-components/advanced-table.hbs @@ -56,9 +56,9 @@ @hasResizableColumns={{true}} @hasReorderableColumns={{true}} @columns={{array - (hash key="artist" label="Artist" tooltip="More information." isSortable=true) - (hash key="album" label="Album" tooltip="More information." isSortable=true) - (hash key="year" label="Release Year" tooltip="More information." isSortable=true) + (hash key="artist" label="Artist" tooltip="More information.") + (hash key="album" label="Album" tooltip="More information.") + (hash key="year" label="Release Year" tooltip="More information.") (hash key="other" label="Additional Actions") }} > From f92dbf03fb8525af48daf6111e6c74ef11157648 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Sun, 20 Jul 2025 20:03:54 -0700 Subject: [PATCH 27/98] making resizing and reordering work together --- .../src/components/hds/advanced-table/models/table.ts | 2 ++ packages/components/src/components/hds/advanced-table/th.hbs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) 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 9391d411e73..c1b97d19e39 100644 --- a/packages/components/src/components/hds/advanced-table/models/table.ts +++ b/packages/components/src/components/hds/advanced-table/models/table.ts @@ -375,6 +375,8 @@ export default class HdsAdvancedTableTableModel { } sourceColumn.isBeingDragged = false; + // when a column is moved, reset the imposed width delta + sourceColumn.imposedWidthDelta = 0; this.onColumnReorder?.(updated); } diff --git a/packages/components/src/components/hds/advanced-table/th.hbs b/packages/components/src/components/hds/advanced-table/th.hbs index c0d31cd675d..91849e43ffb 100644 --- a/packages/components/src/components/hds/advanced-table/th.hbs +++ b/packages/components/src/components/hds/advanced-table/th.hbs @@ -90,7 +90,7 @@ /> {{/if}} - {{#if (and @hasResizableColumns (not @isLastColumn))}} + {{#if (and @hasResizableColumns (not @isLastColumn) (not @column.table.hasColumnBeingDragged))}} Date: Mon, 21 Jul 2025 21:31:45 -0700 Subject: [PATCH 28/98] working on width ledger --- .../hds/advanced-table/models/column.ts | 59 ++++++++++++------- .../hds/advanced-table/models/table.ts | 5 ++ .../hds/advanced-table/th-context-menu.ts | 4 +- .../hds/advanced-table/th-resize-handle.ts | 38 +++++++----- 4 files changed, 67 insertions(+), 39 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 c019a441572..8c609df2912 100644 --- a/packages/components/src/components/hds/advanced-table/models/column.ts +++ b/packages/components/src/components/hds/advanced-table/models/column.ts @@ -42,6 +42,7 @@ export default class HdsAdvancedTableColumn { @tracked width?: string = undefined; @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 widthDebts: Record = {}; // used to track width changes imposed by other columns @tracked isBeingDragged: boolean = false; @tracked thElement?: HTMLDivElement = undefined; @@ -158,6 +159,43 @@ export default class HdsAdvancedTableColumn { this.maxWidth = maxWidth ?? DEFAULT_MAX_WIDTH; } + private payWidthDebts(): void { + Object.entries(this.widthDebts).forEach(([lenderKey, amount]) => { + const lender = this.table.getColumnByKey(lenderKey); + + if (lender) { + // Give the width back to the column that lent it to us + lender.setPxWidth((lender.pxWidth ?? 0) + amount); + } + }); + + // Clear our own debt ledger, as we've paid everyone back + this.widthDebts = {}; + } + + private collectWidthDebts(): void { + const { key: thisKey, table } = this; + if (thisKey === undefined) { + return; + } + + table.columns.forEach((otherColumn) => { + const debtToCollect = otherColumn.widthDebts[thisKey] ?? 0; + + if (debtToCollect > 0) { + // Take the width back from the column that owes us + otherColumn.setPxWidth((otherColumn.pxWidth ?? 0) - debtToCollect); + // Clear the debt from their ledger + delete otherColumn.widthDebts[thisKey]; + } + }); + } + + private settleWidthDebts(): void { + this.payWidthDebts(); + this.collectWidthDebts(); + } + @action focusReorderHandle(): void { if (this.thElement === undefined) { return; @@ -190,29 +228,10 @@ export default class HdsAdvancedTableColumn { } } - // This method is called when the column width is changed by the previous column. - @action - onPreviousColumnWidthRestored(): void { - const restoredWidth = (this.pxWidth ?? 0) + this.imposedWidthDelta; - - this.setPxWidth(restoredWidth); - - 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); - } - @action restoreWidth(): void { this.width = this.originalWidth; - this.imposedWidthDelta = 0; - if (this.key === undefined) { - return; - } + this.settleWidthDebts(); } } 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 c1b97d19e39..ed2eb258c79 100644 --- a/packages/components/src/components/hds/advanced-table/models/table.ts +++ b/packages/components/src/components/hds/advanced-table/models/table.ts @@ -209,6 +209,10 @@ export default class HdsAdvancedTableTableModel { } } + getColumnByKey(key: string): HdsAdvancedTableColumn | undefined { + return this.columns.find((column) => column.key === key); + } + @action setupData( args: Pick< @@ -376,6 +380,7 @@ export default class HdsAdvancedTableTableModel { sourceColumn.isBeingDragged = false; // when a column is moved, reset the imposed width delta + // TODO: this should be handled in a more robust way sourceColumn.imposedWidthDelta = 0; this.onColumnReorder?.(updated); 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 a09cf1baae4..4b54395aa04 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 @@ -138,10 +138,8 @@ export default class HdsAdvancedTableThContextMenu extends Component void): void { - const { onColumnResize, column, previousColumn, nextColumn } = this.args; + const { onColumnResize, column } = this.args; - previousColumn?.onNextColumnWidthRestored(column.imposedWidthDelta); - nextColumn?.onPreviousColumnWidthRestored(); column.restoreWidth(); if (typeof onColumnResize === 'function' && column.key !== undefined) { 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 4d8ee19cea5..f50afbd09c5 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 @@ -69,7 +69,8 @@ export default class HdsAdvancedTableThResizeHandle extends Component void; @@ -151,10 +152,12 @@ export default class HdsAdvancedTableThResizeHandle extends Component Date: Mon, 21 Jul 2025 22:31:15 -0700 Subject: [PATCH 29/98] resizing works again but with a ledger system now --- .../hds/advanced-table/models/column.ts | 1 + .../hds/advanced-table/th-resize-handle.ts | 42 ++++++++++++++----- .../src/components/hds/advanced-table/th.hbs | 1 + 3 files changed, 33 insertions(+), 11 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 8c609df2912..980cd5d276e 100644 --- a/packages/components/src/components/hds/advanced-table/models/column.ts +++ b/packages/components/src/components/hds/advanced-table/models/column.ts @@ -175,6 +175,7 @@ export default class HdsAdvancedTableColumn { private collectWidthDebts(): void { const { key: thisKey, table } = this; + if (thisKey === undefined) { return; } 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 f50afbd09c5..d94ff6f4d02 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 @@ -70,7 +70,7 @@ export default class HdsAdvancedTableThResizeHandle extends Component void; @@ -156,7 +156,7 @@ export default class HdsAdvancedTableThResizeHandle extends Component 0) { + this._addDebt(column, nextColumn.key, delta); + } else { + const amountBorrowed = -delta; - if (nextColumn !== undefined && nextColumn.key !== undefined) { - column.widthDebts = { - ...column.widthDebts, - [nextColumn.key]: - (column.widthDebts[nextColumn.key] ?? 0) + - this._inProgressBorrowedWidth, - }; + this._addDebt(nextColumn, column.key, amountBorrowed); } } } diff --git a/packages/components/src/components/hds/advanced-table/th.hbs b/packages/components/src/components/hds/advanced-table/th.hbs index 91849e43ffb..9b1a5891ae4 100644 --- a/packages/components/src/components/hds/advanced-table/th.hbs +++ b/packages/components/src/components/hds/advanced-table/th.hbs @@ -24,6 +24,7 @@ ...attributes > + {{@column.width}} {{#if @column.isVisuallyHidden}} {{yield}} {{else}} From 688ca3ef7285af244cbd91fe8eeafd987563e10a Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Mon, 21 Jul 2025 22:34:51 -0700 Subject: [PATCH 30/98] reordering and resizing work together --- packages/components/src/components/hds/advanced-table/th.hbs | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/components/src/components/hds/advanced-table/th.hbs b/packages/components/src/components/hds/advanced-table/th.hbs index 9b1a5891ae4..91849e43ffb 100644 --- a/packages/components/src/components/hds/advanced-table/th.hbs +++ b/packages/components/src/components/hds/advanced-table/th.hbs @@ -24,7 +24,6 @@ ...attributes > - {{@column.width}} {{#if @column.isVisuallyHidden}} {{yield}} {{else}} From 7488912f7e76cf614793463cb5635205e0b37a79 Mon Sep 17 00:00:00 2001 From: shleewhite Date: Fri, 1 Aug 2025 15:44:27 -0400 Subject: [PATCH 31/98] fix: reorder handle focus bug --- .../src/styles/components/advanced-table.scss | 2 +- .../src/styles/mixins/_focus-ring.scss | 43 +++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/packages/components/src/styles/components/advanced-table.scss b/packages/components/src/styles/components/advanced-table.scss index 2e702a3bcdf..29b6f871a63 100644 --- a/packages/components/src/styles/components/advanced-table.scss +++ b/packages/components/src/styles/components/advanced-table.scss @@ -127,7 +127,7 @@ $hds-advanced-table-drag-preview-background-color: rgba(204, 227, 254, 30%); &:focus, &.mock-focus { - @include hds-focus-ring-with-pseudo-element($position: absolute); + @include hds-focus-ring-with-pseudo-element-focus-always-visible($position: absolute); background-color: var(--token-color-surface-interactive); } diff --git a/packages/components/src/styles/mixins/_focus-ring.scss b/packages/components/src/styles/mixins/_focus-ring.scss index 330f7225676..911e654adb6 100644 --- a/packages/components/src/styles/mixins/_focus-ring.scss +++ b/packages/components/src/styles/mixins/_focus-ring.scss @@ -83,3 +83,46 @@ } } } + +// This mixin is used for the _rare_ cases where we want the focus ring to be visible when the user clicks the element (not just if they focus it with a keyboard). +@mixin hds-focus-ring-with-pseudo-element-focus-always-visible( + $top: 0, + $right: 0, + $bottom: 0, + $left: 0, + $radius: 5px, + $color: action, + $position: relative +) { + position: $position; + outline-style: solid; // used to avoid double outline+focus-ring in Safari (see https://github.com/hashicorp/design-system-components/issues/161#issuecomment-1031548656) + outline-color: transparent; + isolation: isolate; // used to create a new stacking context (needed to have the pseudo element below text/icon but not the parent container) + + &::before { + position: absolute; + top: $top; + right: $right; + bottom: $bottom; + left: $left; + z-index: -1; + border-radius: $radius; + content: ""; + } + + // default focus for browsers that still rely on ":focus" + &:focus, + &.mock-focus { + &::before { + box-shadow: var(--token-focus-ring-#{$color}-box-shadow); + } + } + + // remove the focus ring on "active + focused" state (by design) + &:focus:active, + &.mock-focus.mock-active { + &::before { + box-shadow: none; + } + } +} From 0b02296a999bf01c0c0a3709c09c2a4538aa72cc Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Fri, 8 Aug 2025 15:51:57 -0400 Subject: [PATCH 32/98] fixing ts error --- .../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 965c2c3d01d..e0312c8b480 100644 --- a/packages/components/src/components/hds/advanced-table/models/column.ts +++ b/packages/components/src/components/hds/advanced-table/models/column.ts @@ -55,7 +55,7 @@ export default class HdsAdvancedTableColumn { get cells(): HdsAdvancedTableCell[] { return this.table.flattenedVisibleRows.map((row) => { - const cell = row.cells.find((cell) => cell!.columnKey === this.key); + const cell = row.cells.find((cell) => cell.columnKey === this.key); return cell!; }); From 5b80f06c70f1d9dc4b98b48f951e905e9df16eec Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Fri, 8 Aug 2025 17:59:12 -0400 Subject: [PATCH 33/98] fixed scroll stutter --- .../advanced-table/th-reorder-drop-target.hbs | 2 +- .../advanced-table/th-reorder-drop-target.ts | 45 ++++++++++++------- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.hbs b/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.hbs index 6092caaf0d4..1e8103d10d7 100644 --- a/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.hbs +++ b/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.hbs @@ -2,7 +2,7 @@ class={{this.classNames}} aria-hidden="true" role="presentation" - {{style height=(if @tableHeight (concat @tableHeight "px"))}} + {{style height=this._height}} {{this._registerElement}} {{on "dragover" this.handleDragOver}} {{on "dragenter" this.handleDragEnter}} diff --git a/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.ts b/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.ts index 204045fd833..862fa7f5250 100644 --- a/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.ts +++ b/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.ts @@ -32,6 +32,27 @@ export default class HdsAdvancedTableThReorderDropTarget extends Component { + this._element = element; + } + ); + + private _resetDragState(): void { + this._dragCount = 0; + this._isDraggingOver = false; + this._dragSide = null; + } + + // determines whether the drag event is occurring on the left or right side of the element + private _getDragSide(event: DragEvent): 'left' | 'right' { + const rect = this._element.getBoundingClientRect(); + const mouseX = event.clientX; + const elementMiddleX = rect.left + rect.width / 2; + + return mouseX < elementMiddleX ? 'left' : 'right'; + } + get classNames(): string { const { column } = this.args; @@ -59,25 +80,17 @@ export default class HdsAdvancedTableThReorderDropTarget extends Component { - this._element = element; - } - ); + private get _height(): `${number}px` | undefined { + const { tableHeight } = this.args; - private _resetDragState(): void { - this._dragCount = 0; - this._isDraggingOver = false; - this._dragSide = null; - } + if (tableHeight === undefined) { + return undefined; + } - // determines whether the drag event is occurring on the left or right side of the element - private _getDragSide(event: DragEvent): 'left' | 'right' { - const rect = this._element.getBoundingClientRect(); - const mouseX = event.clientX; - const elementMiddleX = rect.left + rect.width / 2; + // subtract 2px to account for borders + const height = tableHeight - 4; - return mouseX < elementMiddleX ? 'left' : 'right'; + return `${height}px`; } @action From 0c119a3bf73d5f471ba63e646704f55864d95f0c Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Mon, 11 Aug 2025 13:47:13 -0400 Subject: [PATCH 34/98] updated nontranslated string --- .../src/components/hds/advanced-table/th-reorder-handle.hbs | 6 +++++- .../components/advanced-table/th-reorder-handle/en-us.yaml | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 packages/components/translations/hds/components/advanced-table/th-reorder-handle/en-us.yaml diff --git a/packages/components/src/components/hds/advanced-table/th-reorder-handle.hbs b/packages/components/src/components/hds/advanced-table/th-reorder-handle.hbs index 43cdd0f9465..e1c9833b604 100644 --- a/packages/components/src/components/hds/advanced-table/th-reorder-handle.hbs +++ b/packages/components/src/components/hds/advanced-table/th-reorder-handle.hbs @@ -3,7 +3,11 @@ draggable="true" class={{this.classNames}} tabindex="0" - aria-label="Reorder {{@column.label}} column" + aria-label={{hds-t + "hds.components.advanced-table.th-reorder-handle.aria-label" + columnLabel=@column.label + default=(concat "Reorder " @column.label " column") + }} {{this._registerElement}} {{on "dragstart" this.handleDragStart}} {{on "dragend" this.handleDragEnd}} diff --git a/packages/components/translations/hds/components/advanced-table/th-reorder-handle/en-us.yaml b/packages/components/translations/hds/components/advanced-table/th-reorder-handle/en-us.yaml new file mode 100644 index 00000000000..48e2da66913 --- /dev/null +++ b/packages/components/translations/hds/components/advanced-table/th-reorder-handle/en-us.yaml @@ -0,0 +1 @@ +aria-label: "Reorder {{columnLabel}} column" From 1b78bac645c2ee4748aedca4d44a7e004ad005bd Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Mon, 11 Aug 2025 14:31:20 -0400 Subject: [PATCH 35/98] correctly calculating column width at drag start in order to construct the preview --- .../hds/advanced-table/th-reorder-handle.ts | 20 +++++++++++++------ .../components/hds/advanced-table/th-sort.hbs | 1 - .../src/components/hds/advanced-table/th.hbs | 1 - 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/th-reorder-handle.ts b/packages/components/src/components/hds/advanced-table/th-reorder-handle.ts index a342a1789e5..ebeb30d0253 100644 --- a/packages/components/src/components/hds/advanced-table/th-reorder-handle.ts +++ b/packages/components/src/components/hds/advanced-table/th-reorder-handle.ts @@ -13,7 +13,6 @@ import type HdsAdvancedTableColumn from './models/column.ts'; export interface HdsAdvancedTableThReorderHandleSignature { Args: { column: HdsAdvancedTableColumn; - columnWidth: number; tableHeight?: number; onReorderDragStart: (column: HdsAdvancedTableColumn) => void; onReorderDragEnd?: () => void; @@ -53,23 +52,32 @@ export default class HdsAdvancedTableThReorderHandle extends Component document.body.removeChild(dragPreview), 0); 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 79e1f8adf08..9c0f0b6a789 100644 --- a/packages/components/src/components/hds/advanced-table/th-sort.hbs +++ b/packages/components/src/components/hds/advanced-table/th-sort.hbs @@ -47,7 +47,6 @@ {{#if @hasReorderableColumns}} Date: Mon, 11 Aug 2025 16:15:59 -0400 Subject: [PATCH 36/98] fixing drop target styles --- .../hds/advanced-table/th-reorder-drop-target.hbs | 2 +- .../components/hds/advanced-table/th-reorder-drop-target.ts | 2 +- .../components/src/styles/components/advanced-table.scss | 6 ++++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.hbs b/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.hbs index 1e8103d10d7..28c12f6f1c7 100644 --- a/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.hbs +++ b/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.hbs @@ -2,7 +2,7 @@ class={{this.classNames}} aria-hidden="true" role="presentation" - {{style height=this._height}} + {{style height=this.height}} {{this._registerElement}} {{on "dragover" this.handleDragOver}} {{on "dragenter" this.handleDragEnter}} diff --git a/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.ts b/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.ts index 862fa7f5250..318642b05e7 100644 --- a/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.ts +++ b/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.ts @@ -80,7 +80,7 @@ export default class HdsAdvancedTableThReorderDropTarget extends Component Date: Mon, 11 Aug 2025 17:04:51 -0400 Subject: [PATCH 37/98] fixed an issue with setting in nested rows --- .../hds/advanced-table/models/row.ts | 10 +- .../th-reorder-handle/en-us.yaml | 2 +- .../page-components/advanced-table.hbs | 97 ++----------------- 3 files changed, 15 insertions(+), 94 deletions(-) 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 c78a7193ae6..5c4a824f8ea 100644 --- a/packages/components/src/components/hds/advanced-table/models/row.ts +++ b/packages/components/src/components/hds/advanced-table/models/row.ts @@ -53,6 +53,8 @@ export default class HdsAdvancedTableRow { constructor(args: HdsAdvancedTableRowArgs) { const { columns } = args; + console.log(columns); + this.cells = columns.map((column) => { const cell = args[column.key ?? '']; @@ -74,8 +76,12 @@ export default class HdsAdvancedTableRow { if (Array.isArray(childModels)) { this.children = childModels.map( - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - (child) => new HdsAdvancedTableRow(child) + (child) => + new HdsAdvancedTableRow({ + ...(child as HdsAdvancedTableRowArgs), + columns: args.columns, + childrenKey: this.childrenKey, + }) ); } } diff --git a/packages/components/translations/hds/components/advanced-table/th-reorder-handle/en-us.yaml b/packages/components/translations/hds/components/advanced-table/th-reorder-handle/en-us.yaml index 48e2da66913..0ca32b34371 100644 --- a/packages/components/translations/hds/components/advanced-table/th-reorder-handle/en-us.yaml +++ b/packages/components/translations/hds/components/advanced-table/th-reorder-handle/en-us.yaml @@ -1 +1 @@ -aria-label: "Reorder {{columnLabel}} column" +aria-label: "Reorder {columnLabel} column" diff --git a/showcase/app/templates/page-components/advanced-table.hbs b/showcase/app/templates/page-components/advanced-table.hbs index ab675deeeee..8b1d674af17 100644 --- a/showcase/app/templates/page-components/advanced-table.hbs +++ b/showcase/app/templates/page-components/advanced-table.hbs @@ -8,92 +8,7 @@ AdvancedTable
- {{!-- - <:body as |B|> - - {{#each R.orderedCells as |cell|}} - {{#if (eq cell.columnKey "artist")}} - {{B.data.artist}} - {{else if (eq cell.columnKey "album")}} - -
- - {{B.data.album}} -
-
- {{else if (eq cell.columnKey "year")}} - - - - - {{else if (eq cell.columnKey "other")}} - - - - - - - - {{/if}} - {{/each}} -
- -
--}} - - - <:body as |B|> - - {{#each R.orderedCells as |cell|}} - {{#if (eq cell.columnKey "artist")}} - {{B.data.artist}} - {{else if (eq cell.columnKey "album")}} - -
- - {{B.data.album}} -
-
- {{else if (eq cell.columnKey "year")}} - - - - - {{else if (eq cell.columnKey "other")}} - - - - - - - - {{/if}} - {{/each}} -
- -
- - {{!-- +
Lorem Ipsum Dolor - Sit amet + Sit amet @@ -1803,7 +1718,7 @@ Lorem Ipsum Dolor - Sit amet + Sit amet @@ -1818,7 +1733,7 @@ Lorem Ipsum Dolor - Sit amet + Sit amet @@ -2357,7 +2272,7 @@ @@ -2371,6 +2286,6 @@ - --}} +
\ No newline at end of file From a40220ff14bf812d6ebec0549428171c06491fec Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Mon, 11 Aug 2025 17:35:31 -0400 Subject: [PATCH 38/98] adding a default column key (generated) --- .../components/hds/advanced-table/models/column.ts | 3 ++- .../src/components/hds/advanced-table/models/row.ts | 2 -- .../components/hds/advanced-table/models/table.ts | 12 +++++------- 3 files changed, 7 insertions(+), 10 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 e0312c8b480..18b65c53ea7 100644 --- a/packages/components/src/components/hds/advanced-table/models/column.ts +++ b/packages/components/src/components/hds/advanced-table/models/column.ts @@ -5,6 +5,7 @@ import { tracked } from '@glimmer/tracking'; import { action } from '@ember/object'; +import { guidFor } from '@ember/object/internals'; import type { HdsAdvancedTableThReorderHandleSignature } from '../th-reorder-handle.ts'; import type HdsAdvancedTableModel from './table.ts'; @@ -35,7 +36,7 @@ export default class HdsAdvancedTableColumn { @tracked isExpandable?: boolean = false; @tracked isSortable?: boolean = false; @tracked isVisuallyHidden?: boolean = false; - @tracked key?: string = undefined; + @tracked key: string = guidFor(this); @tracked minWidth?: `${number}px` = DEFAULT_MIN_WIDTH; @tracked maxWidth?: `${number}px` = DEFAULT_MAX_WIDTH; @tracked tooltip?: string = undefined; 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 5c4a824f8ea..cad545d7ab1 100644 --- a/packages/components/src/components/hds/advanced-table/models/row.ts +++ b/packages/components/src/components/hds/advanced-table/models/row.ts @@ -53,8 +53,6 @@ export default class HdsAdvancedTableRow { constructor(args: HdsAdvancedTableRowArgs) { const { columns } = args; - console.log(columns); - this.cells = columns.map((column) => { const cell = args[column.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 ed2eb258c79..ef296143791 100644 --- a/packages/components/src/components/hds/advanced-table/models/table.ts +++ b/packages/components/src/components/hds/advanced-table/models/table.ts @@ -52,7 +52,10 @@ function getVisibleRows(rows: HdsAdvancedTableRow[]): HdsAdvancedTableRow[] { } function getChildrenCount(rows: HdsAdvancedTableRow[]): number { - return rows.reduce((acc, row) => acc + 1 + getChildrenCount(row.children), 0); + return rows.reduce( + (acc, row) => acc + 1 + getChildrenCount(row.children ?? []), + 0 + ); } export default class HdsAdvancedTableTableModel { @@ -88,12 +91,7 @@ export default class HdsAdvancedTableTableModel { this.setupData({ model, columns, sortBy, sortOrder }); - this.columnOrder = - columnOrder ?? - this.columns.map((column) => { - // todo: make this work without column keys correctly - return column.key ?? ''; - }); + this.columnOrder = columnOrder ?? this.columns.map((column) => column.key); this.onColumnReorder = onColumnReorder; } From 3aa9d5319eeafc8feced7ab7d4bc4ce008b37191 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Mon, 11 Aug 2025 20:45:05 -0400 Subject: [PATCH 39/98] added an assertion for trying to use reorderable columns with nested rows --- .../components/hds/advanced-table/index.ts | 6 ++++ .../hds/advanced-table/index-test.js | 28 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/packages/components/src/components/hds/advanced-table/index.ts b/packages/components/src/components/hds/advanced-table/index.ts index b243f7b2bd3..9cacc3d9d9d 100644 --- a/packages/components/src/components/hds/advanced-table/index.ts +++ b/packages/components/src/components/hds/advanced-table/index.ts @@ -211,6 +211,7 @@ export default class HdsAdvancedTable extends Component column.label ); + assert( + 'Cannot have reorderable columns if there are nested rows.', + !hasReorderableColumns + ); + assert( `Cannot have sortable columns if there are nested rows. Sortable columns are ${sortableColumnLabels.toString()}`, sortableColumns.length === 0 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 d6edbb12a5a..320a77504a6 100644 --- a/showcase/tests/integration/components/hds/advanced-table/index-test.js +++ b/showcase/tests/integration/components/hds/advanced-table/index-test.js @@ -793,6 +793,34 @@ module('Integration | Component | hds/advanced-table/index', function (hooks) { .hasClass('hds-advanced-table--valign-top'); }); + test('it throws an assertion if @hasReorderableColumns and has nested rows', async function (assert) { + const errorMessage = + 'Cannot have reorderable 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 throws an assertion if @isStriped and has nested rows', async function (assert) { const errorMessage = '@isStriped must not be true if there are nested rows.'; From 8ac2f356dc64fa60ea96462b07fddd41f84785df Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Tue, 12 Aug 2025 17:11:29 -0400 Subject: [PATCH 40/98] got reorderable columns working with sticky first column --- .../hds/advanced-table/models/column.ts | 4 ++-- .../hds/advanced-table/models/table.ts | 2 +- .../components/hds/advanced-table/th-sort.hbs | 4 ++-- .../src/components/hds/advanced-table/th.hbs | 4 ++-- .../page-components/advanced-table.hbs | 18 ++++++++++-------- 5 files changed, 17 insertions(+), 15 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 18b65c53ea7..91bb44b8ba3 100644 --- a/packages/components/src/components/hds/advanced-table/models/column.ts +++ b/packages/components/src/components/hds/advanced-table/models/column.ts @@ -36,7 +36,7 @@ export default class HdsAdvancedTableColumn { @tracked isExpandable?: boolean = false; @tracked isSortable?: boolean = false; @tracked isVisuallyHidden?: boolean = false; - @tracked key: string = guidFor(this); + @tracked key: string; @tracked minWidth?: `${number}px` = DEFAULT_MIN_WIDTH; @tracked maxWidth?: `${number}px` = DEFAULT_MAX_WIDTH; @tracked tooltip?: string = undefined; @@ -135,7 +135,7 @@ export default class HdsAdvancedTableColumn { this.isExpandable = 'isExpandable' in column ? column.isExpandable : false; this.isSortable = column.isSortable ?? false; this.isVisuallyHidden = column.isVisuallyHidden ?? false; - this.key = column.key; + this.key = column.key ?? guidFor(this); this.tooltip = column.tooltip; this._setWidthValues(column); this.sortingFunction = column.sortingFunction; 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 ef296143791..b524f84b3bd 100644 --- a/packages/components/src/components/hds/advanced-table/models/table.ts +++ b/packages/components/src/components/hds/advanced-table/models/table.ts @@ -368,7 +368,7 @@ export default class HdsAdvancedTableTableModel { ? newIndex - 1 : newIndex; - updated.splice(adjustedIndex, 0, sourceColumn.key as string); // Insert at new position + updated.splice(adjustedIndex, 0, sourceColumn.key); // Insert at new position this.columnOrder = updated; 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 9c0f0b6a789..6d084da0988 100644 --- a/packages/components/src/components/hds/advanced-table/th-sort.hbs +++ b/packages/components/src/components/hds/advanced-table/th-sort.hbs @@ -44,7 +44,7 @@ /> {{#if @column}} - {{#if @hasReorderableColumns}} + {{#if (and @hasReorderableColumns (not @isStickyColumn))}} - {{#if @column.table.hasColumnBeingDragged}} + {{#if (and @column.table.hasColumnBeingDragged (not @isStickyColumn))}} - {{#if @column.table.hasColumnBeingDragged}} + {{#if (and @column.table.hasColumnBeingDragged (not @isStickyColumn))}} - {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} - {{B.data.id}} - {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} - {{B.data.name}} - {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} - {{B.data.email}} - {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} - {{B.data.role}} + {{#each R.orderedCells as |C|}} + {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} + {{#if (eq C.columnKey "id")}} + {{C.content}} + {{else}} + {{C.content}} + {{/if}} + {{/each}} From 59758cd70a1e68122f76be9317fbc26d5c0e20ae Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Tue, 12 Aug 2025 17:41:59 -0400 Subject: [PATCH 41/98] made the demo reorderable --- .../components/hds/advanced-table/th-sort.hbs | 14 +- .../src/components/hds/advanced-table/th.hbs | 14 +- .../mock/app/main/generic-advanced-table.gts | 167 ++++++++++-------- .../page-components/advanced-table.hbs | 18 +- 4 files changed, 117 insertions(+), 96 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 6d084da0988..fb1454e0e57 100644 --- a/packages/components/src/components/hds/advanced-table/th-sort.hbs +++ b/packages/components/src/components/hds/advanced-table/th-sort.hbs @@ -78,11 +78,13 @@ - {{#if (and @column.table.hasColumnBeingDragged (not @isStickyColumn))}} - + {{#if @column}} + {{#if (and @column.table.hasColumnBeingDragged (not @isStickyColumn))}} + + {{/if}} {{/if}} \ 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 b8e8b1f5024..d39aa5e25ee 100644 --- a/packages/components/src/components/hds/advanced-table/th.hbs +++ b/packages/components/src/components/hds/advanced-table/th.hbs @@ -99,11 +99,13 @@ {{/if}} - {{#if (and @column.table.hasColumnBeingDragged (not @isStickyColumn))}} - + {{#if @column}} + {{#if (and @column.table.hasColumnBeingDragged (not @isStickyColumn))}} + + {{/if}} {{/if}} \ No newline at end of file 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..4e1f3c003ca 100644 --- a/showcase/app/components/mock/app/main/generic-advanced-table.gts +++ b/showcase/app/components/mock/app/main/generic-advanced-table.gts @@ -6,6 +6,7 @@ import Component from '@glimmer/component'; import { action } from '@ember/object'; import { deepTracked } from 'ember-deep-tracked'; import { get } from '@ember/helper'; +import { eq } from 'ember-truth-helpers'; // HDS components import { @@ -524,6 +525,7 @@ export default class MockAppMainGenericAdvancedTable extends Component <:body as |B|> {{! @glint-expect-error }} - - - - {{! @glint-expect-error }} - {{get B.data "name"}} - - - - - {{! @glint-expect-error }} - {{get B.data "project-name"}} - - - - - {{! @glint-expect-error }} - {{get B.data "current-run-id"}} - - - - - - - {{! @glint-expect-error }} - {{get B.data "current-run-applied"}} - - - {{! @glint-expect-error }} - {{get B.data "vcs-repo"}} - - - - {{! @glint-expect-error }} - {{get B.data "module-count"}} - - - - {{! @glint-expect-error }} - {{get B.data "modules"}} - - - - {{! @glint-expect-error }} - {{get B.data "provider-count"}} - - - - {{! @glint-expect-error }} - {{get B.data "providers"}} - - - {{! @glint-expect-error }} - {{get B.data "terraform-version"}} - - - - {{! @glint-expect-error }} - {{get B.data "state-terraform-version"}} - - - - {{! @glint-expect-error }} - {{get B.data "created"}} - - - {{! @glint-expect-error }} - {{get B.data "updated"}} - + + {{#each R.orderedCells as |C|}} + {{#if (eq C.columnKey "name")}} + + + {{! @glint-expect-error }} + {{get B.data "name"}} + + + {{else if (eq C.columnKey "project-name")}} + + + {{! @glint-expect-error }} + {{get B.data "project-name"}} + + + {{else if (eq C.columnKey "current-run-id")}} + + + {{! @glint-expect-error }} + {{get B.data "current-run-id"}} + + + {{else if (eq C.columnKey "run-status")}} + + + + {{else if (eq C.columnKey "current-run-applied")}} + + {{! @glint-expect-error }} + {{get B.data "current-run-applied"}} + + {{else if (eq C.columnKey "vcs-repo")}} + + {{! @glint-expect-error }} + {{get B.data "vcs-repo"}} + + {{else if (eq C.columnKey "module-count")}} + + + {{! @glint-expect-error }} + {{get B.data "module-count"}} + + + {{else if (eq C.columnKey "modules")}} + + {{! @glint-expect-error }} + {{get B.data "modules"}} + + {{else if (eq C.columnKey "provider-count")}} + + + {{! @glint-expect-error }} + {{get B.data "provider-count"}} + + + {{else if (eq C.columnKey "providers")}} + + {{! @glint-expect-error }} + {{get B.data "providers"}} + + {{else if (eq C.columnKey "terraform-version")}} + + {{! @glint-expect-error }} + {{get B.data "terraform-version"}} + + {{else if (eq C.columnKey "state-terraform-version")}} + + + {{! @glint-expect-error }} + {{get B.data "state-terraform-version"}} + + + {{else if (eq C.columnKey "created")}} + + {{! @glint-expect-error }} + {{get B.data "created"}} + + {{else if (eq C.columnKey "updated")}} + + {{! @glint-expect-error }} + {{get B.data "updated"}} + + {{/if}} + {{/each}} diff --git a/showcase/app/templates/page-components/advanced-table.hbs b/showcase/app/templates/page-components/advanced-table.hbs index f7b8fcdd56d..8b1d674af17 100644 --- a/showcase/app/templates/page-components/advanced-table.hbs +++ b/showcase/app/templates/page-components/advanced-table.hbs @@ -220,7 +220,6 @@ {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} @model={{this.model.userData}} @maxHeight="400px" - @hasReorderableColumns={{true}} @hasStickyHeader={{false}} @columns={{array (hash key="id" label="ID" width="auto") @@ -239,16 +238,15 @@ @isSelected={{B.data.isSelected}} {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} @selectionAriaLabelSuffix="row #{{B.data.id}}" - as |R| > - {{#each R.orderedCells as |C|}} - {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} - {{#if (eq C.columnKey "id")}} - {{C.content}} - {{else}} - {{C.content}} - {{/if}} - {{/each}} + {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} + {{B.data.id}} + {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} + {{B.data.name}} + {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} + {{B.data.email}} + {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} + {{B.data.role}} From ad622034da99c38a90f92aa988cd5b03c6e723f9 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Tue, 12 Aug 2025 19:46:26 -0400 Subject: [PATCH 42/98] adding showcase examples --- .../hds/advanced-table/th-resize-handle.ts | 1 - .../components/hds/advanced-table/th-sort.hbs | 1 - .../src/components/hds/advanced-table/th.hbs | 1 - .../src/styles/components/advanced-table.scss | 88 +++++++++---------- .../styles/showcase-pages/advanced-table.scss | 6 ++ .../page-components/advanced-table.hbs | 19 +++- 6 files changed, 68 insertions(+), 48 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 bde7a76fcd1..4be85fb2453 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 @@ -52,7 +52,6 @@ function calculateEffectiveDelta( export interface HdsAdvancedTableThResizeHandleSignature { Args: { column: HdsAdvancedTableColumn; - hasResizableColumns: HdsAdvancedTableSignature['Args']['hasResizableColumns']; tableHeight?: number; onColumnResize?: HdsAdvancedTableSignature['Args']['onColumnResize']; }; 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 fb1454e0e57..082e34625d5 100644 --- a/packages/components/src/components/hds/advanced-table/th-sort.hbs +++ b/packages/components/src/components/hds/advanced-table/th-sort.hbs @@ -68,7 +68,6 @@ {{#if (and @hasResizableColumns (not @column.isLast))}} @@ -2288,4 +2287,22 @@ + + + ThReorderHandle + + + {{#each @model.STATES as |state|}} + + + + {{/each}} + +
\ No newline at end of file From d5959bc0d0888a817d6f28b50107c797e4729e6b Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Tue, 12 Aug 2025 20:54:31 -0400 Subject: [PATCH 43/98] adding showcase examples --- .../page-components/advanced-table.ts | 58 +++++---- .../page-components/advanced-table.hbs | 121 ++++++++++++++++++ 2 files changed, 157 insertions(+), 22 deletions(-) diff --git a/showcase/app/controllers/page-components/advanced-table.ts b/showcase/app/controllers/page-components/advanced-table.ts index 719a180f71d..cc231b08c9c 100644 --- a/showcase/app/controllers/page-components/advanced-table.ts +++ b/showcase/app/controllers/page-components/advanced-table.ts @@ -28,6 +28,29 @@ const customSortingCriteriaArray = [ 'pending', ]; +const musicColumns = [ + { + key: 'artist', + label: 'Artist', + tooltip: 'More information.', + }, + { + key: 'album', + label: 'Album', + tooltip: 'More information.', + width: '350px', + }, + { + key: 'year', + label: 'Release Year', + tooltip: 'More information.', + }, + { + key: 'other', + label: 'Additional Actions', + }, +]; + const updateModelWithSelectAllState = ( modelData: SelectableItem[] | User[], selectAllState: boolean, @@ -447,28 +470,7 @@ export default class PageComponentsAdvancedTableController extends Controller { } // COLUMN RESIZING DEMO - columnResizeColumns = [ - { - key: 'artist', - label: 'Artist', - tooltip: 'More information.', - }, - { - key: 'album', - label: 'Album', - tooltip: 'More information.', - width: '350px', - }, - { - key: 'year', - label: 'Release Year', - tooltip: 'More information.', - }, - { - key: 'other', - label: 'Additional Actions', - }, - ]; + columnResizeColumns = musicColumns; columnResizeColumnsWithSorting = this.columnResizeColumns.map( (column, index) => { @@ -479,6 +481,18 @@ export default class PageComponentsAdvancedTableController extends Controller { }, ); + // COLUMN REORDERING DEMO + columnReorderColumns = musicColumns; + + columnReorderColumnsWithSorting = this.columnReorderColumns.map( + (column, index) => { + return { + ...column, + isSortable: index !== this.columnReorderColumns.length - 1, // last column is not sortable + }; + }, + ); + @action noop() { // no-op diff --git a/showcase/app/templates/page-components/advanced-table.hbs b/showcase/app/templates/page-components/advanced-table.hbs index eaf00cb8096..ce5c310530b 100644 --- a/showcase/app/templates/page-components/advanced-table.hbs +++ b/showcase/app/templates/page-components/advanced-table.hbs @@ -633,6 +633,87 @@ + Reorderable columns + + {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} + + <:body as |B|> + {{! @glint-expect-error - this argument shouldn't be required, will be fixed by https://hashicorp.atlassian.net/browse/HDS-5167}} + + {{#each R.orderedCells as |C|}} + {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} + {{#if (eq C.columnKey "artist")}} + {{B.data.artist}} + {{else}} + + {{#if (eq C.columnKey "album")}} +
+ {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} + + {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} + {{B.data.album}} +
+ {{else if (eq C.columnKey "year")}} + + {{else}} + + + + + + {{/if}} +
+ {{/if}} + {{/each}} +
+ +
+ + Reorderable columns with sorting + + + <:body as |B|> + {{! @glint-expect-error - this argument shouldn't be required, will be fixed by https://hashicorp.atlassian.net/browse/HDS-5167}} + + {{#each R.orderedCells as |C|}} + {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} + {{#if (eq C.columnKey "artist")}} + {{B.data.artist}} + {{else}} + + {{#if (eq C.columnKey "album")}} +
+ {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} + + {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} + {{B.data.album}} +
+ {{else if (eq C.columnKey "year")}} + + {{else}} + + + + + + {{/if}} +
+ {{/if}} + {{/each}} +
+ +
+ + + Resizable columns {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} @@ -736,6 +817,46 @@ + Resizable and reorderable columns + + + <:body as |B|> + {{! @glint-expect-error - this argument shouldn't be required, will be fixed by https://hashicorp.atlassian.net/browse/HDS-5167}} + + {{#each R.orderedCells as |C|}} + {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} + {{#if (eq C.columnKey "artist")}} + {{B.data.artist}} + {{else}} + + {{#if (eq C.columnKey "album")}} +
+ {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} + + {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} + {{B.data.album}} +
+ {{else if (eq C.columnKey "year")}} + + {{else}} + + + + + + {{/if}} +
+ {{/if}} + {{/each}} +
+ +
+ Functional examples From 89a05686705196df2cc39335cfa0ca1e46e36a43 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Wed, 13 Aug 2025 18:04:56 -0400 Subject: [PATCH 44/98] fixed firefox reorder bug --- .../components/hds/advanced-table/index.hbs | 7 +- .../hds/advanced-table/models/table.ts | 1 + .../advanced-table/th-reorder-drop-target.hbs | 2 - .../advanced-table/th-reorder-drop-target.ts | 88 ++++++++----------- .../src/styles/components/advanced-table.scss | 1 + 5 files changed, 47 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 48a2d5a45ab..1ed6fb9e5b4 100644 --- a/packages/components/src/components/hds/advanced-table/index.hbs +++ b/packages/components/src/components/hds/advanced-table/index.hbs @@ -30,7 +30,12 @@ {{this._setUpScrollWrapper}} > {{! Header }} -
+
\ No newline at end of file diff --git a/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.ts b/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.ts index 318642b05e7..052d7e6212c 100644 --- a/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.ts +++ b/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.ts @@ -27,8 +27,7 @@ export interface HdsAdvancedTableThReorderDropTargetSignature { export default class HdsAdvancedTableThReorderDropTarget extends Component { @tracked private _dragSide: 'left' | 'right' | null = null; - @tracked private _isDraggingOver = false; - @tracked private _dragCount = 0; + @tracked private _isUpdateQueued: boolean = false; private _element!: HdsAdvancedTableThReorderDropTargetSignature['Element']; @@ -38,12 +37,6 @@ export default class HdsAdvancedTableThReorderDropTarget extends Component { + const { column } = this.args; + const { table } = column; + + if (table.reorderDraggedColumn !== null) { + if (table.reorderDraggedColumn.key === column.key) { + table.reorderHoveredColumn = null; + } else { + table.reorderHoveredColumn = column; + + const { next, previous } = table.reorderDraggedColumn.siblings; + const dragSide = this._getDragSide(event); + + if ( + (column === previous && dragSide === 'left') || + (column === next && dragSide === 'right') || + (column !== previous && column !== next) + ) { + this._dragSide = dragSide; + } + } + } + + this._isUpdateQueued = false; + }); } @action @@ -146,9 +136,6 @@ export default class HdsAdvancedTableThReorderDropTarget extends Component Date: Thu, 14 Aug 2025 15:58:11 -0400 Subject: [PATCH 45/98] wip target styles --- .../src/components/hds/advanced-table/index.ts | 9 +++++---- .../hds/advanced-table/th-reorder-drop-target.ts | 10 ++++------ .../components/hds/advanced-table/th-resize-handle.ts | 4 ++-- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.ts b/packages/components/src/components/hds/advanced-table/index.ts index 9cacc3d9d9d..bdd9da128f4 100644 --- a/packages/components/src/components/hds/advanced-table/index.ts +++ b/packages/components/src/components/hds/advanced-table/index.ts @@ -7,14 +7,13 @@ import Component from '@glimmer/component'; import { action } from '@ember/object'; import { assert } from '@ember/debug'; import { tracked } from '@glimmer/tracking'; -import type { WithBoundArgs } 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'; +import type Owner from '@ember/owner'; +import type { WithBoundArgs } from '@glint/template'; import { HdsAdvancedTableDensityValues, HdsAdvancedTableVerticalAlignmentValues, @@ -47,6 +46,8 @@ export const VALIGNMENTS: HdsAdvancedTableVerticalAlignment[] = Object.values( ); export const DEFAULT_VALIGN = HdsAdvancedTableVerticalAlignmentValues.Top; +export const BORDER_WIDTH = 1; + const DEFAULT_SCROLL_DIMENSIONS = { bottom: '0px', height: '0px', @@ -495,7 +496,7 @@ export default class HdsAdvancedTable extends Component { - this._tableHeight = element.clientHeight; + this._tableHeight = element.offsetHeight; this.scrollIndicatorDimensions = getScrollIndicatorDimensions( element, diff --git a/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.ts b/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.ts index 052d7e6212c..65f5f733079 100644 --- a/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.ts +++ b/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.ts @@ -7,6 +7,7 @@ import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; import { action } from '@ember/object'; import { modifier } from 'ember-modifier'; +import { BORDER_WIDTH } from './index.ts'; import type HdsAdvancedTableColumn from './models/column.ts'; @@ -79,17 +80,14 @@ export default class HdsAdvancedTableThReorderDropTarget extends Component Date: Thu, 14 Aug 2025 16:49:47 -0400 Subject: [PATCH 46/98] cleaning up PR and adding more tests --- .../components/hds/advanced-table/index.hbs | 2 +- .../components/hds/advanced-table/index.ts | 10 ++- .../hds/advanced-table/models/column.ts | 5 +- .../hds/advanced-table/models/row.ts | 4 -- .../hds/advanced-table/models/table.ts | 11 ++-- .../src/styles/components/advanced-table.scss | 1 + .../hds/advanced-table/index-test.js | 61 +++++++++++++++---- 7 files changed, 64 insertions(+), 30 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.hbs b/packages/components/src/components/hds/advanced-table/index.hbs index 1ed6fb9e5b4..e5b83043b7c 100644 --- a/packages/components/src/components/hds/advanced-table/index.hbs +++ b/packages/components/src/components/hds/advanced-table/index.hbs @@ -8,7 +8,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 bdd9da128f4..5d30fae7b26 100644 --- a/packages/components/src/components/hds/advanced-table/index.ts +++ b/packages/components/src/components/hds/advanced-table/index.ts @@ -580,9 +580,15 @@ export default class HdsAdvancedTable extends Component = {}; // used to track width changes imposed by other columns @tracked isBeingDragged: boolean = false; @@ -98,9 +97,7 @@ export default class HdsAdvancedTableColumn { } get isLast(): boolean { - return ( - this.index !== -1 && this.index === this.table.orderedColumns.length - 1 - ); + return this.index !== -1 && this.index === this.table.columns.length - 1; } get siblings(): { 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 cad545d7ab1..c2986d62495 100644 --- a/packages/components/src/components/hds/advanced-table/models/row.ts +++ b/packages/components/src/components/hds/advanced-table/models/row.ts @@ -87,10 +87,6 @@ export default class HdsAdvancedTableRow { @action updateColumnOrder(columnOrder: string[]) { this.columnOrder = columnOrder; - - for (const child of this.children) { - child.updateColumnOrder(columnOrder); - } } @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 a618ea2276f..38cf86d8e68 100644 --- a/packages/components/src/components/hds/advanced-table/models/table.ts +++ b/packages/components/src/components/hds/advanced-table/models/table.ts @@ -90,7 +90,7 @@ export default class HdsAdvancedTableTableModel { this.hasResizableColumns = hasResizableColumns; this.onSort = onSort; - this.setupData({ model, columns, sortBy, sortOrder }); + this.setupData({ model, columns, columnOrder, sortBy, sortOrder }); this.columnOrder = columnOrder ?? this.columns.map((column) => column.key); @@ -216,10 +216,10 @@ export default class HdsAdvancedTableTableModel { setupData( args: Pick< HdsAdvancedTableTableArgs, - 'model' | 'columns' | 'sortBy' | 'sortOrder' + 'model' | 'columns' | 'columnOrder' | 'sortBy' | 'sortOrder' > ) { - const { model, columns, sortBy, sortOrder } = args; + const { model, columns, columnOrder, sortBy, sortOrder } = args; this.sortBy = sortBy; this.sortOrder = sortOrder ?? HdsAdvancedTableThSortOrderValues.Asc; @@ -232,6 +232,8 @@ export default class HdsAdvancedTableTableModel { }) ); + this.columnOrder = columnOrder ?? []; + this.rows = model.map((row) => { return new HdsAdvancedTableRow({ ...row, @@ -378,9 +380,6 @@ export default class HdsAdvancedTableTableModel { } sourceColumn.isBeingDragged = false; - // when a column is moved, reset the imposed width delta - // TODO: this should be handled in a more robust way - sourceColumn.imposedWidthDelta = 0; this.onColumnReorder?.(updated); } diff --git a/packages/components/src/styles/components/advanced-table.scss b/packages/components/src/styles/components/advanced-table.scss index c54b059b57a..2164316dad0 100644 --- a/packages/components/src/styles/components/advanced-table.scss +++ b/packages/components/src/styles/components/advanced-table.scss @@ -719,4 +719,5 @@ $hds-advanced-table-drag-preview-background-color: rgba(204, 227, 254, 30%); background-color: $hds-advanced-table-drag-preview-background-color; border: 5px solid var(--token-color-focus-action-external); border-radius: var(--token-border-radius-medium); + box-shadow: var(--token-elevation-mid-box-shadow); } 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 320a77504a6..6d06c93eb02 100644 --- a/showcase/tests/integration/components/hds/advanced-table/index-test.js +++ b/showcase/tests/integration/components/hds/advanced-table/index-test.js @@ -127,19 +127,6 @@ async function simulateColumnReorderDrop({ await triggerEvent(handleElement, 'dragend'); } -async function simulateColumnReorderDragAndDrop({ - handleElement, - targetIndex, - targetPosition = 'left', -}) { - const { target, eventOptions } = await simulateColumnReorderDrag({ - handleElement, - targetIndex, - targetPosition, - }); - await simulateColumnReorderDrop({ target, handleElement, eventOptions }); -} - // 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', [ @@ -694,6 +681,54 @@ module('Integration | Component | hds/advanced-table/index', function (hooks) { ); assert.dom(firstReorderHandle).isFocused(); }); + + test('passing in columnOrder sets the initial order of the table columns', async function (assert) { + await render( + hbs``, + ); + + const columnOrder = await getColumnOrder(this.columns); + assert.deepEqual( + columnOrder, + ['album', 'year', 'artist'], + 'The initial column order is set correctly', + ); + }); + + test('updating columnOrder externally changes the order of the table columns', async function (assert) { + this.set('columnOrder', ['artist', 'album', 'year']); + + await render( + hbs``, + ); + + let columnOrder = await getColumnOrder(this.columns); + assert.deepEqual( + columnOrder, + ['artist', 'album', 'year'], + 'The initial column order is set correctly', + ); + + this.set('columnOrder', ['year', 'album', 'artist']); + columnOrder = await getColumnOrder(this.columns); + assert.deepEqual( + columnOrder, + ['year', 'album', 'artist'], + 'The column order is updated correctly', + ); + }); }); test('it should render the component with a CSS class that matches the component name', async function (assert) { From 4804a4d78170e6329f8eb41e4d17835b774f6ad8 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Fri, 15 Aug 2025 15:31:12 -0400 Subject: [PATCH 47/98] moving columns with sticky columns works --- .../src/components/hds/advanced-table/index.ts | 1 + .../src/components/hds/advanced-table/models/column.ts | 4 ++++ .../src/components/hds/advanced-table/models/table.ts | 10 +++++++++- .../components/hds/advanced-table/th-context-menu.ts | 8 +++++--- .../src/components/hds/advanced-table/th-sort.hbs | 1 + .../src/components/hds/advanced-table/th.hbs | 1 + 6 files changed, 21 insertions(+), 4 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.ts b/packages/components/src/components/hds/advanced-table/index.ts index 5d30fae7b26..a2d0b87e7c1 100644 --- a/packages/components/src/components/hds/advanced-table/index.ts +++ b/packages/components/src/components/hds/advanced-table/index.ts @@ -226,6 +226,7 @@ export default class HdsAdvancedTable extends Component column.isFirstNonSticky + ); + const { targetColumn, side, @@ -328,7 +336,7 @@ export default class HdsAdvancedTableTableModel { } = position === 'start' ? { - targetColumn: this.orderedColumns[0], + targetColumn: firstNonStickyColumn, side: HdsAdvancedTableColumnReorderSideValues.Left, } : { 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 eafaef39e93..7c0f6c1b201 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 @@ -25,6 +25,7 @@ export interface HdsAdvancedTableThContextMenuSignature { column: HdsAdvancedTableColumn; hasResizableColumns?: boolean; hasReorderableColumns?: boolean; + isStickyColumn?: boolean; reorderHandleElement?: HdsAdvancedTableThReorderHandleSignature['Element']; resizeHandleElement?: HdsAdvancedTableThResizeHandleSignature['Element']; onColumnResize?: HdsAdvancedTableSignature['Args']['onColumnResize']; @@ -74,7 +75,7 @@ export default class HdsAdvancedTableThContextMenu extends Component Date: Fri, 15 Aug 2025 15:43:48 -0400 Subject: [PATCH 48/98] prevent users from using keyboard nav to move column to sticky column position --- .../src/components/hds/advanced-table/models/table.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 627dac8f44a..15da57fbf43 100644 --- a/packages/components/src/components/hds/advanced-table/models/table.ts +++ b/packages/components/src/components/hds/advanced-table/models/table.ts @@ -297,9 +297,10 @@ export default class HdsAdvancedTableTableModel { const { table } = column; const oldIndex = table.orderedColumns.indexOf(column); const newIndex = oldIndex + step; + const startBoundary = table.hasStickyFirstColumn ? 1 : 0; // Check if the new position is within the array bounds. - if (newIndex < 0 || newIndex >= table.orderedColumns.length) { + if (newIndex < startBoundary || newIndex >= table.orderedColumns.length) { return; } From 8d15dc5bfa8191930495a110ee02d7f4e33a40c1 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Fri, 15 Aug 2025 16:33:43 -0400 Subject: [PATCH 49/98] adding tests around reordering and sticky columns --- .../hds/advanced-table/index-test.js | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) 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 6d06c93eb02..17d2318c702 100644 --- a/showcase/tests/integration/components/hds/advanced-table/index-test.js +++ b/showcase/tests/integration/components/hds/advanced-table/index-test.js @@ -729,6 +729,152 @@ module('Integration | Component | hds/advanced-table/index', function (hooks) { 'The column order is updated correctly', ); }); + + module('with @hasStickyFirstColumn enabled', function () { + test('dropping a column on a sticky column does not trigger a reorder', async function (assert) { + await render( + hbs``, + ); + + const initialColumnOrder = this.columns.map((col) => col.key); + + let columnOrder = await getColumnOrder(this.columns); + assert.deepEqual( + columnOrder, + initialColumnOrder, + 'Initial column order is correct', + ); + + const thirdReorderHandle = findAll( + '.hds-advanced-table__th-reorder-handle', + )[2]; + + const { target, eventOptions } = await simulateColumnReorderDrag({ + handleElement: thirdReorderHandle, + targetIndex: 0, + targetPosition: 'left', + }); + + const dropTargets = findAll( + '.hds-advanced-table__th-reorder-drop-target', + ); + const destinationDropTarget = dropTargets[0]; + + assert + .dom(thirdReorderHandle) + .hasClass( + 'hds-advanced-table__th-reorder-drop-target--is-being-dragged', + 'Third column is being dragged', + ); + assert + .dom(destinationDropTarget) + .doesNotHaveClass( + 'hds-advanced-table__th-reorder-drop-target--is-dragging-over', + ) + .doesNotHaveClass( + 'hds-advanced-table__th-reorder-drop-target--is-dragging-over--left', + ); + + await simulateColumnReorderDrop({ + target, + handleElement: thirdReorderHandle, + eventOptions, + }); + + columnOrder = await getColumnOrder(this.columns); + + assert + .dom('.hds-advanced-table__th-reorder-drop-target') + .doesNotExist('Drop targets are removed after drop'); + assert.deepEqual( + columnOrder, + initialColumnOrder, + 'Columns order is unchanged after dropping on the sticky first column', + ); + }); + + test('the context menu for the sticky column does not render when reordering is enabled but no other context menu options are available', async function (assert) { + await render( + hbs``, + ); + + assert + .dom( + '.hds-advanced-table__th:first-of-type .hds-advanced-table__th-context-menu', + ) + .doesNotExist('context menu does not render for the sticky column'); + }); + + test('clicking the "Move column to start" context menu option moves the column to the first non-sticky column position', async function (assert) { + await render( + hbs``, + ); + + const thElements = findAll('.hds-advanced-table__th'); + + const thirdContextMenuToggle = thElements[2].querySelector( + '.hds-dropdown-toggle-icon', + ); + await click(thirdContextMenuToggle); + await click('[data-test-context-option-key="move-column-to-start"]'); + + const columnOrder = await getColumnOrder(this.columns); + assert.deepEqual( + columnOrder, + [this.columns[0].key, this.columns[2].key, this.columns[1].key], + 'The third column is moved to the first non-sticky position', + ); + }); + + test('pressing "Left Arrow" when the reorder handle of the first non-sticky column is focused does not reposition the column before the sticky column', async function (assert) { + await render( + hbs``, + ); + + const thElements = findAll('.hds-advanced-table__th'); + const firstNonStickyColumnReorderHandle = thElements[1].querySelector( + '.hds-advanced-table__th-reorder-handle', + ); + await focus(firstNonStickyColumnReorderHandle); + assert.dom(firstNonStickyColumnReorderHandle).isFocused(); + + await triggerKeyEvent( + firstNonStickyColumnReorderHandle, + 'keydown', + 'ArrowLeft', + ); + let columnOrder = await getColumnOrder(this.columns); + assert.deepEqual( + columnOrder, + [this.columns[0].key, this.columns[1].key, this.columns[2].key], + 'column order is unchanged', + ); + }); + }); }); test('it should render the component with a CSS class that matches the component name', async function (assert) { From 65ea884c8858ff9f094a8a22d362c86018d6f0f1 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Fri, 15 Aug 2025 17:04:30 -0400 Subject: [PATCH 50/98] reverting a change to the gridTemplateColumns getter --- .../src/components/hds/advanced-table/index.hbs | 4 ++-- .../src/components/hds/advanced-table/index.ts | 10 +++++----- 2 files changed, 7 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 e5b83043b7c..0cb2af53db4 100644 --- a/packages/components/src/components/hds/advanced-table/index.hbs +++ b/packages/components/src/components/hds/advanced-table/index.hbs @@ -132,7 +132,7 @@ scope="row" onClickToggle=T.onClickToggle ) - Td=(component "hds/advanced-table/td" align=@align columns=this._tableModel.columns) + Td=(component "hds/advanced-table/td" align=@align) data=T.data isOpen=T.isExpanded rowIndex=T.rowIndex @@ -162,7 +162,7 @@ isStickyColumn=@hasStickyFirstColumn isStickyColumnPinned=this.isStickyColumnPinned ) - Td=(component "hds/advanced-table/td" align=@align columns=this._tableModel.columns) + Td=(component "hds/advanced-table/td" align=@align) data=record rowIndex=index ) diff --git a/packages/components/src/components/hds/advanced-table/index.ts b/packages/components/src/components/hds/advanced-table/index.ts index a2d0b87e7c1..a52c9d750db 100644 --- a/packages/components/src/components/hds/advanced-table/index.ts +++ b/packages/components/src/components/hds/advanced-table/index.ts @@ -364,25 +364,25 @@ export default class HdsAdvancedTable extends Component 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 < orderedColumns.length; i++) { - style += ` ${orderedColumns[i]!.width ?? 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(${orderedColumns.length}, ${DEFAULT_COLUMN_WIDTH})`; + style += `repeat(${columns.length}, ${DEFAULT_COLUMN_WIDTH})`; } return style; From 1d78f3c462e2ba87d3371f6ca3ad3d5ea52d6313 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Fri, 15 Aug 2025 17:11:37 -0400 Subject: [PATCH 51/98] not showing context menu if there are no reorder options --- .../src/components/hds/advanced-table/th-sort.ts | 7 +++++-- .../components/src/components/hds/advanced-table/th.ts | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) 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 9402fe55006..b26f72fb5a9 100644 --- a/packages/components/src/components/hds/advanced-table/th-sort.ts +++ b/packages/components/src/components/hds/advanced-table/th-sort.ts @@ -111,9 +111,12 @@ export default class HdsAdvancedTableThSort extends Component Date: Fri, 15 Aug 2025 17:20:58 -0400 Subject: [PATCH 52/98] cleaned up column order getter --- .../src/components/hds/advanced-table/models/row.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 c2986d62495..dbb0891a0b8 100644 --- a/packages/components/src/components/hds/advanced-table/models/row.ts +++ b/packages/components/src/components/hds/advanced-table/models/row.ts @@ -39,15 +39,15 @@ export default class HdsAdvancedTableRow { } get orderedCells(): HdsAdvancedTableCell[] { - return this.columnOrder.reduce((acc, key) => { + return this.columnOrder.map((key) => { const cell = this.cells.find((cell) => cell.columnKey === key); - if (cell) { - acc.push(cell); + if (cell === undefined) { + throw new Error(`Column with key ${key} not found`); } - return acc; - }, [] as HdsAdvancedTableCell[]); + return cell; + }); } constructor(args: HdsAdvancedTableRowArgs) { From 0b205753bf5e7140ecc60d74d85b645788332b6c Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Fri, 15 Aug 2025 18:09:59 -0400 Subject: [PATCH 53/98] fixed a bug where resizing reordered columns was resizing the wrong column --- .../src/components/hds/advanced-table/index.ts | 10 +++++----- 1 file changed, 5 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 a52c9d750db..a2d0b87e7c1 100644 --- a/packages/components/src/components/hds/advanced-table/index.ts +++ b/packages/components/src/components/hds/advanced-table/index.ts @@ -364,25 +364,25 @@ export default class HdsAdvancedTable extends Component 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 < columns.length; i++) { - style += ` ${columns[i]!.width ?? DEFAULT_COLUMN_WIDTH}`; + for (let i = 0; i < orderedColumns.length; i++) { + style += ` ${orderedColumns[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})`; + style += `repeat(${orderedColumns.length}, ${DEFAULT_COLUMN_WIDTH})`; } return style; From 93d3f03295b0c92182d18e11888ec91e47fdd1b4 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Mon, 25 Aug 2025 17:08:35 -0400 Subject: [PATCH 54/98] added some throttling to resizing to prevent lag --- .../hds/advanced-table/th-resize-handle.ts | 49 +++++++++++++------ 1 file changed, 35 insertions(+), 14 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 d95cdb43234..cccddd84f34 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 @@ -69,6 +69,8 @@ export default class HdsAdvancedTableThResizeHandle extends Component void; @@ -216,25 +218,41 @@ export default class HdsAdvancedTableThResizeHandle extends Component { + if (this.resizing === null || this._lastPointerEvent === null) { + this._isUpdateQueued = false; - this._applyResizeDelta( - deltaX, - column, - startColumnPxWidth, // Width at the start of the drag - nextColumn, - startNextColumnPxWidth // Width of next col at the start of the drag - ); + return; + } + + const event = this._lastPointerEvent; + + event.preventDefault(); + + const { column } = this.args; + const { next: nextColumn } = column.siblings; + const { startX, startColumnPxWidth, startNextColumnPxWidth } = + this.resizing!; + const deltaX = event.clientX - startX; + + this._applyResizeDelta( + deltaX, + column, + startColumnPxWidth, // Width at the start of the drag + nextColumn, + startNextColumnPxWidth // Width of next col at the start of the drag + ); + + this._isUpdateQueued = false; + }); } private _stopResize(): void { @@ -243,6 +261,9 @@ export default class HdsAdvancedTableThResizeHandle extends Component Date: Mon, 25 Aug 2025 19:39:49 -0400 Subject: [PATCH 55/98] getting width repayment working with column reordering --- .../src/components/hds/advanced-table/index.ts | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.ts b/packages/components/src/components/hds/advanced-table/index.ts index fca617036e2..e2d5ce8c9e7 100644 --- a/packages/components/src/components/hds/advanced-table/index.ts +++ b/packages/components/src/components/hds/advanced-table/index.ts @@ -57,8 +57,6 @@ const DEFAULT_SCROLL_DIMENSIONS = { width: '0px', }; -const DEFAULT_COLUMN_WIDTH = '1fr'; - const getScrollIndicatorDimensions = ( scrollWrapper: HTMLDivElement, theadElement: HTMLDivElement, @@ -403,18 +401,8 @@ export default class HdsAdvancedTable extends Component 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 < orderedColumns.length; i++) { - style += ` ${orderedColumns[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(${orderedColumns.length}, ${DEFAULT_COLUMN_WIDTH})`; + for (let i = 0; i < orderedColumns.length; i++) { + style += ` ${orderedColumns[i]!.appliedWidth}`; } return style; From aaca8f05d4360fe8fe7d265209388e23885bc763 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Tue, 26 Aug 2025 12:08:45 -0400 Subject: [PATCH 56/98] fixing width reset --- .../src/components/hds/advanced-table/models/column.ts | 4 ++-- 1 file changed, 2 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 ad510d72bd2..b0c1c9bbbcd 100644 --- a/packages/components/src/components/hds/advanced-table/models/column.ts +++ b/packages/components/src/components/hds/advanced-table/models/column.ts @@ -312,8 +312,8 @@ export default class HdsAdvancedTableColumn { @action restoreWidth(): void { - this.width = this.originalWidth; - this.settleWidthDebts(); + + this.width = this.originalWidth; } } From 2566e2ab9cd74f035b1aa199961908aad1e44c63 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Tue, 26 Aug 2025 12:10:39 -0400 Subject: [PATCH 57/98] remove glint disable comment --- packages/components/src/components/hds/advanced-table/index.hbs | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.hbs b/packages/components/src/components/hds/advanced-table/index.hbs index ef0b7965d75..5d0aafd5d3e 100644 --- a/packages/components/src/components/hds/advanced-table/index.hbs +++ b/packages/components/src/components/hds/advanced-table/index.hbs @@ -2,8 +2,6 @@ Copyright (c) HashiCorp, Inc. SPDX-License-Identifier: MPL-2.0 }} -{{! TODO: Remove this when ready }} -{{! @glint-nocheck }}
{{#each R.orderedCells as |C|}} - {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} {{#if (eq C.columnKey "artist")}} + {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} {{B.data.artist}} {{else}} @@ -645,6 +646,7 @@ {{B.data.album}}
{{else if (eq C.columnKey "year")}} + {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} {{else}} @@ -663,6 +665,7 @@ Reorderable columns with sorting {{#each R.orderedCells as |C|}} - {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} {{#if (eq C.columnKey "artist")}} + {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} {{B.data.artist}} {{else}} @@ -684,6 +687,7 @@ {{B.data.album}}
{{else if (eq C.columnKey "year")}} + {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} {{else}} @@ -804,6 +808,7 @@ Resizable and reorderable columns {{#each R.orderedCells as |C|}} - {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} {{#if (eq C.columnKey "artist")}} + {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} {{B.data.artist}} {{else}} @@ -826,6 +831,7 @@ {{B.data.album}}
{{else if (eq C.columnKey "year")}} + {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} {{else}} From 4e95dfbe4114800cf22c9965095e964a71741cf5 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Tue, 26 Aug 2025 16:17:18 -0400 Subject: [PATCH 61/98] fixing linting errors --- packages/components/src/styles/components/advanced-table.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/styles/components/advanced-table.scss b/packages/components/src/styles/components/advanced-table.scss index 2164316dad0..649b9d240a1 100644 --- a/packages/components/src/styles/components/advanced-table.scss +++ b/packages/components/src/styles/components/advanced-table.scss @@ -151,12 +151,12 @@ $hds-advanced-table-drag-preview-background-color: rgba(204, 227, 254, 30%); &::after { position: absolute; - pointer-events: none; top: 0; bottom: 0; width: 4px; background-color: var(--token-color-palette-blue-300); content: ""; + pointer-events: none; } } From ba38777b8bc477255b8cb32eb278d371de12888e Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Tue, 26 Aug 2025 16:29:23 -0400 Subject: [PATCH 62/98] removing unnecessary type assertion --- .../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 31f59d3230a..e79fcdb0491 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 @@ -286,7 +286,7 @@ export default class HdsAdvancedTableThResizeHandle extends Component Date: Tue, 26 Aug 2025 17:20:20 -0400 Subject: [PATCH 63/98] fixing a11y violation --- .../src/components/hds/advanced-table/th-reorder-handle.hbs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/components/src/components/hds/advanced-table/th-reorder-handle.hbs b/packages/components/src/components/hds/advanced-table/th-reorder-handle.hbs index e1c9833b604..40b039c0225 100644 --- a/packages/components/src/components/hds/advanced-table/th-reorder-handle.hbs +++ b/packages/components/src/components/hds/advanced-table/th-reorder-handle.hbs @@ -1,5 +1,7 @@ {{! template-lint-disable no-pointer-down-event-binding }} +{{! template-lint-disable no-invalid-role require-context-role require-presentational-children }}
Date: Tue, 26 Aug 2025 18:17:40 -0400 Subject: [PATCH 64/98] fixing tests --- .../hds/advanced-table/models/table.ts | 13 ++++-- .../hds/advanced-table/index-test.js | 43 +++++++++---------- 2 files changed, 31 insertions(+), 25 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 f0b2099b4d6..13910b587ca 100644 --- a/packages/components/src/components/hds/advanced-table/models/table.ts +++ b/packages/components/src/components/hds/advanced-table/models/table.ts @@ -6,6 +6,7 @@ import HdsAdvancedTableRow from './row.ts'; import { action } from '@ember/object'; import { tracked } from '@glimmer/tracking'; +import { isEmpty } from '@ember/utils'; import HdsAdvancedTableColumn from './column.ts'; import { HdsAdvancedTableThSortOrderValues } from '../types.ts'; @@ -97,7 +98,7 @@ export default class HdsAdvancedTableTableModel { this.setupData({ model, columns, columnOrder, sortBy, sortOrder }); - this.columnOrder = columnOrder ?? this.columns.map((column) => column.key); + this.setColumnOrder(columnOrder); this.onColumnReorder = onColumnReorder; } @@ -233,6 +234,12 @@ export default class HdsAdvancedTableTableModel { return this.columns.find((column) => column.key === key); } + setColumnOrder(columnOrder?: string[]): void { + this.columnOrder = isEmpty(columnOrder) + ? this.columns.map((column) => column.key) + : columnOrder!; // ensured non-empty + } + @action setupData( args: Pick< @@ -253,7 +260,7 @@ export default class HdsAdvancedTableTableModel { }) ); - this.columnOrder = columnOrder ?? []; + this.setColumnOrder(columnOrder); this.rows = model.map((row) => { return new HdsAdvancedTableRow({ @@ -399,7 +406,7 @@ export default class HdsAdvancedTableTableModel { updated.splice(adjustedIndex, 0, sourceColumn.key); // Insert at new position - this.columnOrder = updated; + this.setColumnOrder(updated); for (const row of this.rows) { row.updateColumnOrder(updated); 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 2b6fd3ff3d5..82754b00ba0 100644 --- a/showcase/tests/integration/components/hds/advanced-table/index-test.js +++ b/showcase/tests/integration/components/hds/advanced-table/index-test.js @@ -113,12 +113,13 @@ async function dragOverTarget(target, { clientX, clientY }) { async function simulateColumnReorderDrag({ handleElement, + targetElement, targetIndex, targetPosition = 'left', }) { await startReorderDrag(handleElement); - const target = getTargetElementFromColumnIndex(targetIndex); + const target = targetElement ?? getTargetElementFromColumnIndex(targetIndex); const { clientX, clientY } = getDragTargetPosition(target, targetPosition); const eventOptions = { clientX, clientY }; @@ -687,9 +688,11 @@ module('Integration | Component | hds/advanced-table/index', function (hooks) { ); const thElements = findAll('.hds-advanced-table__th'); + const firstThElement = thElements[0]; const firstReorderHandle = thElements[0].querySelector( '.hds-advanced-table__th-reorder-handle', ); + await focus(firstThElement); await focus(firstReorderHandle); assert.dom(firstReorderHandle).isFocused(); @@ -770,7 +773,7 @@ module('Integration | Component | hds/advanced-table/index', function (hooks) { }); module('with @hasStickyFirstColumn enabled', function () { - test('dropping a column on a sticky column does not trigger a reorder', async function (assert) { + test('when dragging a column, the sticky column does not have a drop target', async function (assert) { await render( hbs` Date: Wed, 27 Aug 2025 13:18:27 -0400 Subject: [PATCH 65/98] green tests --- .../hds/advanced-table/th-context-menu.ts | 51 +++++++++---------- .../hds/advanced-table/th-reorder-handle.hbs | 1 - .../components/hds/advanced-table/th-sort.ts | 9 ++-- .../src/components/hds/advanced-table/th.ts | 9 ++-- .../hds/advanced-table/index-test.js | 18 ------- 5 files changed, 36 insertions(+), 52 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 bf5808fa229..cccc5c30831 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 @@ -134,37 +134,34 @@ export default class HdsAdvancedTableThContextMenu extends Component`, - ); - - assert - .dom( - '.hds-advanced-table__th:first-of-type .hds-advanced-table__th-context-menu', - ) - .doesNotExist('context menu does not render for the sticky column'); - }); - test('clicking the "Move column to start" context menu option moves the column to the first non-sticky column position', async function (assert) { await render( hbs` Date: Wed, 27 Aug 2025 15:25:46 -0400 Subject: [PATCH 66/98] added test waiter --- packages/components/package.json | 1 + .../hds/advanced-table/th-reorder-drop-target.ts | 3 ++- .../hds/advanced-table/th-resize-handle.ts | 3 ++- .../src/components/hds/advanced-table/utils.ts | 16 ++++++++++++++++ 4 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 packages/components/src/components/hds/advanced-table/utils.ts diff --git a/packages/components/package.json b/packages/components/package.json index 1143d002cfb..721bc23b673 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -161,6 +161,7 @@ "./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", "./components/hds/advanced-table/tr.js": "./dist/_app_/components/hds/advanced-table/tr.js", + "./components/hds/advanced-table/utils.js": "./dist/_app_/components/hds/advanced-table/utils.js", "./components/hds/alert/description.js": "./dist/_app_/components/hds/alert/description.js", "./components/hds/alert/index.js": "./dist/_app_/components/hds/alert/index.js", "./components/hds/alert/title.js": "./dist/_app_/components/hds/alert/title.js", diff --git a/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.ts b/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.ts index 65f5f733079..0035e8fc2bc 100644 --- a/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.ts +++ b/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.ts @@ -8,6 +8,7 @@ import { tracked } from '@glimmer/tracking'; import { action } from '@ember/object'; import { modifier } from 'ember-modifier'; import { BORDER_WIDTH } from './index.ts'; +import { requestAnimationFrameWaiter } from './utils.ts'; import type HdsAdvancedTableColumn from './models/column.ts'; @@ -100,7 +101,7 @@ export default class HdsAdvancedTableThReorderDropTarget extends Component { + requestAnimationFrameWaiter(() => { const { column } = this.args; const { table } = column; 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 e79fcdb0491..696187189b2 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 { modifier } from 'ember-modifier'; +import { requestAnimationFrameWaiter } from './utils.ts'; import { BORDER_WIDTH } from './index.ts'; import type HdsAdvancedTableColumn from './models/column.ts'; @@ -272,7 +273,7 @@ export default class HdsAdvancedTableThResizeHandle extends Component { + requestAnimationFrameWaiter(() => { if (this.resizing === null || this._lastPointerEvent === null) { this._isUpdateQueued = false; diff --git a/packages/components/src/components/hds/advanced-table/utils.ts b/packages/components/src/components/hds/advanced-table/utils.ts new file mode 100644 index 00000000000..4bb8046e5e0 --- /dev/null +++ b/packages/components/src/components/hds/advanced-table/utils.ts @@ -0,0 +1,16 @@ +import { buildWaiter } from '@ember/test-waiters'; + +const waiter = buildWaiter('raf-waiter'); + +// a utility that wraps requestAnimationFrame and integrates with Ember's test waiters +export function requestAnimationFrameWaiter(callback: () => void) { + const token = waiter.beginAsync(); + + return requestAnimationFrame(() => { + try { + callback(); + } finally { + waiter.endAsync(token); + } + }); +} From 2d273a22108b011bd9c43aaf2ca3e723ac804723 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Wed, 27 Aug 2025 16:16:53 -0400 Subject: [PATCH 67/98] fixing broken unit tests --- .../hds/advanced-table/models/column-test.js | 148 ++++++++++-------- 1 file changed, 80 insertions(+), 68 deletions(-) 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 f867437a7a6..5854adfd993 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 @@ -260,22 +260,24 @@ module('Unit | Component | hds/advanced-table/models/column', function () { }); test('index getter returns correct column position', function (assert) { + const mockColumns = [ + new HdsAdvancedTableColumn({ + column: { label: 'First', key: 'first' }, + table: null, + }), + new HdsAdvancedTableColumn({ + column: { label: 'Second', key: 'second' }, + table: null, + }), + new HdsAdvancedTableColumn({ + column: { label: 'Third', key: 'third' }, + table: null, + }), + ]; // Create mock table with multiple columns const mockTable = { - columns: [ - new HdsAdvancedTableColumn({ - column: { label: 'First', key: 'first' }, - table: null, - }), - new HdsAdvancedTableColumn({ - column: { label: 'Second', key: 'second' }, - table: null, - }), - new HdsAdvancedTableColumn({ - column: { label: 'Third', key: 'third' }, - table: null, - }), - ], + columns: mockColumns, + orderedColumns: mockColumns, }; // Set table reference for each column @@ -299,7 +301,7 @@ module('Unit | Component | hds/advanced-table/models/column', function () { }); test('index getter returns -1 when table has no columns', function (assert) { - const mockTable = { columns: [] }; + const mockTable = { columns: [], orderedColumns: [] }; const column = new HdsAdvancedTableColumn({ column: { label: 'Test', key: 'test' }, table: mockTable, @@ -309,13 +311,15 @@ module('Unit | Component | hds/advanced-table/models/column', function () { }); test('index getter returns -1 when column key does not exist in table', function (assert) { + const columns = [ + new HdsAdvancedTableColumn({ + column: { label: 'Other', key: 'other' }, + table: null, + }), + ]; const mockTable = { - columns: [ - new HdsAdvancedTableColumn({ - column: { label: 'Other', key: 'other' }, - table: null, - }), - ], + columns, + orderedColumns: columns, }; mockTable.columns[0].table = mockTable; @@ -332,17 +336,19 @@ module('Unit | Component | hds/advanced-table/models/column', function () { }); test('isFirst getter identifies first column correctly', function (assert) { + const columns = [ + new HdsAdvancedTableColumn({ + column: { label: 'First', key: 'first' }, + table: null, + }), + new HdsAdvancedTableColumn({ + column: { label: 'Second', key: 'second' }, + table: null, + }), + ]; const mockTable = { - columns: [ - new HdsAdvancedTableColumn({ - column: { label: 'First', key: 'first' }, - table: null, - }), - new HdsAdvancedTableColumn({ - column: { label: 'Second', key: 'second' }, - table: null, - }), - ], + columns, + orderedColumns: columns, }; mockTable.columns.forEach((col) => (col.table = mockTable)); @@ -358,7 +364,7 @@ module('Unit | Component | hds/advanced-table/models/column', function () { }); test('isFirst getter returns false when index is -1', function (assert) { - const mockTable = { columns: [] }; + const mockTable = { columns: [], orderedColumns: [] }; const column = new HdsAdvancedTableColumn({ column: { label: 'Test', key: 'test' }, table: mockTable, @@ -368,21 +374,23 @@ module('Unit | Component | hds/advanced-table/models/column', function () { }); test('isLast getter identifies last column correctly', function (assert) { + const columns = [ + new HdsAdvancedTableColumn({ + column: { label: 'First', key: 'first' }, + table: null, + }), + new HdsAdvancedTableColumn({ + column: { label: 'Second', key: 'second' }, + table: null, + }), + new HdsAdvancedTableColumn({ + column: { label: 'Third', key: 'third' }, + table: null, + }), + ]; const mockTable = { - columns: [ - new HdsAdvancedTableColumn({ - column: { label: 'First', key: 'first' }, - table: null, - }), - new HdsAdvancedTableColumn({ - column: { label: 'Second', key: 'second' }, - table: null, - }), - new HdsAdvancedTableColumn({ - column: { label: 'Third', key: 'third' }, - table: null, - }), - ], + columns, + orderedColumns: columns, }; mockTable.columns.forEach((col) => (col.table = mockTable)); @@ -402,7 +410,7 @@ module('Unit | Component | hds/advanced-table/models/column', function () { }); test('isLast getter returns false when index is -1', function (assert) { - const mockTable = { columns: [] }; + const mockTable = { columns: [], orderedColumns: [] }; const column = new HdsAdvancedTableColumn({ column: { label: 'Test', key: 'test' }, table: mockTable, @@ -412,21 +420,23 @@ module('Unit | Component | hds/advanced-table/models/column', function () { }); test('siblings getter returns correct previous and next columns', function (assert) { + const columns = [ + new HdsAdvancedTableColumn({ + column: { label: 'First', key: 'first' }, + table: null, + }), + new HdsAdvancedTableColumn({ + column: { label: 'Second', key: 'second' }, + table: null, + }), + new HdsAdvancedTableColumn({ + column: { label: 'Third', key: 'third' }, + table: null, + }), + ]; const mockTable = { - columns: [ - new HdsAdvancedTableColumn({ - column: { label: 'First', key: 'first' }, - table: null, - }), - new HdsAdvancedTableColumn({ - column: { label: 'Second', key: 'second' }, - table: null, - }), - new HdsAdvancedTableColumn({ - column: { label: 'Third', key: 'third' }, - table: null, - }), - ], + columns, + orderedColumns: columns, }; mockTable.columns.forEach((col) => (col.table = mockTable)); @@ -472,7 +482,7 @@ module('Unit | Component | hds/advanced-table/models/column', function () { }); test('siblings getter returns empty object when index is -1', function (assert) { - const mockTable = { columns: [] }; + const mockTable = { columns: [], orderedColumns: [] }; const column = new HdsAdvancedTableColumn({ column: { label: 'Test', key: 'test' }, table: mockTable, @@ -483,13 +493,15 @@ module('Unit | Component | hds/advanced-table/models/column', function () { }); test('siblings getter works with single column', function (assert) { + const columns = [ + new HdsAdvancedTableColumn({ + column: { label: 'Only', key: 'only' }, + table: null, + }), + ]; const mockTable = { - columns: [ - new HdsAdvancedTableColumn({ - column: { label: 'Only', key: 'only' }, - table: null, - }), - ], + columns, + orderedColumns: columns, }; mockTable.columns[0].table = mockTable; From 6afab7755e1da66c91200a3496b2a0420c8322b0 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Thu, 28 Aug 2025 17:45:06 -0400 Subject: [PATCH 68/98] increased the target for the reorder handle --- .../hds/advanced-table/th-reorder-handle.hbs | 6 ++-- .../src/styles/components/advanced-table.scss | 35 +++++++++++++------ 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/th-reorder-handle.hbs b/packages/components/src/components/hds/advanced-table/th-reorder-handle.hbs index 787fd3a90df..d3de92f5b9e 100644 --- a/packages/components/src/components/hds/advanced-table/th-reorder-handle.hbs +++ b/packages/components/src/components/hds/advanced-table/th-reorder-handle.hbs @@ -2,7 +2,7 @@
- +
+ +
\ 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 649b9d240a1..64a34ec9b87 100644 --- a/packages/components/src/styles/components/advanced-table.scss +++ b/packages/components/src/styles/components/advanced-table.scss @@ -347,19 +347,28 @@ $hds-advanced-table-drag-preview-background-color: rgba(204, 227, 254, 30%); left: 50%; z-index: 2; display: flex; + flex-direction: column; align-items: center; justify-content: center; width: 24px; - height: 16px; - color: var(--token-color-foreground-faint); - background-color: var(--token-color-surface-interactive); - border: 1px solid var(--token-color-border-primary); - border-radius: var(--token-border-radius-small); - box-shadow: var(--token-elevation-low-box-shadow); + height: 24px; transform: translateX(-50%) translateY(50%); visibility: hidden; opacity: 0; + .hds-advanced-table__th-reorder-handle__inner { + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 16px; + color: var(--token-color-foreground-faint); + background-color: var(--token-color-surface-interactive); + border: 1px solid var(--token-color-border-primary); + border-radius: var(--token-border-radius-small); + box-shadow: var(--token-elevation-low-box-shadow); + } + .hds-icon { width: 12px; height: 12px; @@ -367,18 +376,24 @@ $hds-advanced-table-drag-preview-background-color: rgba(204, 227, 254, 30%); &:hover, &.mock-hover { - background-color: var(--token-color-surface-interactive-hover); cursor: grab; + + .hds-advanced-table__th-reorder-handle__inner { + background-color: var(--token-color-surface-interactive-hover); + } } &:active, &.mock-active { - background-color: var(--token-color-surface-interactive-active); cursor: grabbing; + + .hds-advanced-table__th-reorder-handle__inner { + background-color: var(--token-color-surface-interactive-hover); + } } - &:focus, - &.mock-focus { + &:focus .hds-advanced-table__th-reorder-handle__inner, + &.mock-focus .hds-advanced-table__th-reorder-handle__inner { @include hds-focus-ring-with-pseudo-element-focus-always-visible($position: absolute); background-color: var(--token-color-surface-interactive); From 70a7cc87cbfcab74a009cd45744fed4f891e4d8c Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Thu, 28 Aug 2025 17:47:28 -0400 Subject: [PATCH 69/98] changed order of th components --- .../components/hds/advanced-table/th-sort.hbs | 20 +++++++++---------- .../src/components/hds/advanced-table/th.hbs | 20 +++++++++---------- 2 files changed, 20 insertions(+), 20 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 d001542075a..d13c1dc4168 100644 --- a/packages/components/src/components/hds/advanced-table/th-sort.hbs +++ b/packages/components/src/components/hds/advanced-table/th-sort.hbs @@ -44,16 +44,6 @@ /> {{#if @column}} - {{#if (and @hasReorderableColumns (not @isStickyColumn))}} - - {{/if}} - {{#if this.showContextMenu}} {{/if}} + + {{#if (and @hasReorderableColumns (not @isStickyColumn))}} + + {{/if}} {{#if (and @hasResizableColumns (not @column.isLast))}} - {{/if}} - {{#if this.showContextMenu}} {{/if}} + {{#if (and @hasReorderableColumns (not @isStickyColumn))}} + + {{/if}} + {{#if (and @hasResizableColumns (not @column.isLast) (not @column.table.hasColumnBeingDragged))}} Date: Fri, 29 Aug 2025 11:06:04 -0400 Subject: [PATCH 70/98] fixing linting error --- .../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 d13c1dc4168..d4092162ae8 100644 --- a/packages/components/src/components/hds/advanced-table/th-sort.hbs +++ b/packages/components/src/components/hds/advanced-table/th-sort.hbs @@ -57,7 +57,7 @@ @onPinFirstColumn={{@onPinFirstColumn}} /> {{/if}} - + {{#if (and @hasReorderableColumns (not @isStickyColumn))}} Date: Fri, 29 Aug 2025 13:27:26 -0400 Subject: [PATCH 71/98] responding to pr feedback --- .../hds/advanced-table/th-reorder-handle.ts | 9 +++------ .../src/components/hds/advanced-table/th-sort.ts | 16 +--------------- .../src/components/hds/advanced-table/th.ts | 16 +--------------- 3 files changed, 5 insertions(+), 36 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/th-reorder-handle.ts b/packages/components/src/components/hds/advanced-table/th-reorder-handle.ts index ebeb30d0253..0bcccd4c865 100644 --- a/packages/components/src/components/hds/advanced-table/th-reorder-handle.ts +++ b/packages/components/src/components/hds/advanced-table/th-reorder-handle.ts @@ -53,17 +53,14 @@ export default class HdsAdvancedTableThReorderHandle extends Component Date: Fri, 29 Aug 2025 15:01:44 -0400 Subject: [PATCH 72/98] responding to pr feedback --- .../hds/advanced-table/models/table.ts | 14 ++- .../hds/advanced-table/th-context-menu.ts | 28 +++--- .../advanced-table/th-reorder-drop-target.hbs | 1 - .../advanced-table/th-reorder-drop-target.ts | 18 ++-- .../hds/advanced-table/th-reorder-handle.hbs | 6 +- .../components/hds/advanced-table/types.ts | 8 ++ .../th-reorder-handle/en-us.yaml | 1 + .../page-components/advanced-table.hbs | 88 ++++++++++++++++++- 8 files changed, 128 insertions(+), 36 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 13910b587ca..9b71b3f7228 100644 --- a/packages/components/src/components/hds/advanced-table/models/table.ts +++ b/packages/components/src/components/hds/advanced-table/models/table.ts @@ -8,24 +8,20 @@ import { action } from '@ember/object'; import { tracked } from '@glimmer/tracking'; import { isEmpty } from '@ember/utils'; import HdsAdvancedTableColumn from './column.ts'; -import { HdsAdvancedTableThSortOrderValues } from '../types.ts'; +import { + HdsAdvancedTableColumnReorderSideValues, + HdsAdvancedTableThSortOrderValues, +} from '../types.ts'; import type { HdsAdvancedTableSignature } from '../index.ts'; import type { HdsAdvancedTableExpandState, HdsAdvancedTableCell, HdsAdvancedTableColumnReorderCallback, + HdsAdvancedTableColumnReorderSide, HdsAdvancedTableSortingFunction, } from '../types'; -enum HdsAdvancedTableColumnReorderSideValues { - Left = 'left', - Right = 'right', -} - -type HdsAdvancedTableColumnReorderSide = - `${HdsAdvancedTableColumnReorderSideValues}`; - type HdsAdvancedTableTableArgs = Pick< HdsAdvancedTableSignature['Args'], | 'model' 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 cccc5c30831..dddbff5d283 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,7 +4,6 @@ */ import Component from '@glimmer/component'; -import { action } from '@ember/object'; import { service } from '@ember/service'; import { tracked } from '@glimmer/tracking'; import { scheduleOnce } from '@ember/runloop'; @@ -57,7 +56,7 @@ export default class HdsAdvancedTableThContextMenu extends Component this.moveColumn(), + action: () => this._moveColumn(), }, ]; @@ -109,7 +108,7 @@ export default class HdsAdvancedTableThContextMenu extends Component this.moveColumnToPosition('start', close), + action: (close) => this._moveColumnToPosition('start', close), }, ]; } @@ -125,7 +124,7 @@ export default class HdsAdvancedTableThContextMenu extends Component this.moveColumnToPosition('end', close), + action: (close) => this._moveColumnToPosition('end', close), }, ]; } @@ -150,7 +149,7 @@ export default class HdsAdvancedTableThContextMenu extends Component void): void { + private _resetColumnWidth(dropdownCloseCallback: () => void): void { const { column, onColumnResize } = this.args; column.restoreWidth(); @@ -207,8 +204,7 @@ export default class HdsAdvancedTableThContextMenu extends Component void ): void { @@ -229,8 +224,7 @@ export default class HdsAdvancedTableThContextMenu extends Component void): void { + private _pinFirstColumn(dropdownCloseCallback: () => void): void { const { onPinFirstColumn } = this.args; if (typeof onPinFirstColumn === 'function') { diff --git a/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.hbs b/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.hbs index b53d1bd676b..82879861538 100644 --- a/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.hbs +++ b/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.hbs @@ -1,7 +1,6 @@ + Reorderable columns with selectable rows + +
+ + <:body as |B|> + + {{#each R.orderedCells as |C|}} + + {{C.content}} + + {{/each}} + + + +
+ Resizable columns From 3bb1222e349c64378825e9f3e6f6d0b15db22128 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Tue, 2 Sep 2025 18:09:27 -0400 Subject: [PATCH 76/98] addressing PR feedback --- showcase/app/styles/showcase-pages/advanced-table.scss | 4 ++++ showcase/app/templates/page-components/advanced-table.hbs | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/showcase/app/styles/showcase-pages/advanced-table.scss b/showcase/app/styles/showcase-pages/advanced-table.scss index 6c78a7976bc..9f6bbef0307 100644 --- a/showcase/app/styles/showcase-pages/advanced-table.scss +++ b/showcase/app/styles/showcase-pages/advanced-table.scss @@ -41,6 +41,10 @@ body.page-components-advanced-table { width: 400px; } + .shw-component-advanced-table-fixed-width-wrapper--wide { + width: 600px; + } + .shw-component-advanced-table-with-pagination-demo-wrapper { .hds-table + .hds-pagination { margin-top: 16px; diff --git a/showcase/app/templates/page-components/advanced-table.hbs b/showcase/app/templates/page-components/advanced-table.hbs index 571e814977f..bb443397da7 100644 --- a/showcase/app/templates/page-components/advanced-table.hbs +++ b/showcase/app/templates/page-components/advanced-table.hbs @@ -746,7 +746,7 @@ Reorderable columns with sticky first column -
+
{{#each R.orderedCells as |C|}} + {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} {{C.content}} {{/each}} From b6bf61a4bac3af763b3df77a5aaf7a12176215d8 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Tue, 2 Sep 2025 18:14:51 -0400 Subject: [PATCH 77/98] fixed dynamic segment in translation --- .../translations/hds/components/advanced-table/en-us.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/translations/hds/components/advanced-table/en-us.yaml b/packages/components/translations/hds/components/advanced-table/en-us.yaml index f9e48bdc27a..b6074f30b8b 100644 --- a/packages/components/translations/hds/components/advanced-table/en-us.yaml +++ b/packages/components/translations/hds/components/advanced-table/en-us.yaml @@ -1 +1 @@ -reordered-message: Moved {{columnLabel}} column to position {{newPosition}} +reordered-message: Moved {columnLabel} column to position {newPosition} From 539f54af7b048dcdbf689e02c1b54f5f9c8ce38a Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Mon, 8 Sep 2025 12:59:00 -0400 Subject: [PATCH 78/98] correctly updates the column order when the value changes both internally and externally --- .../components/hds/advanced-table/index.hbs | 1 + .../components/hds/advanced-table/index.ts | 9 ++++++++ .../hds/advanced-table/models/table.ts | 21 +++++++------------ 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.hbs b/packages/components/src/components/hds/advanced-table/index.hbs index 0a1e446294a..f3619abbea2 100644 --- a/packages/components/src/components/hds/advanced-table/index.hbs +++ b/packages/components/src/components/hds/advanced-table/index.hbs @@ -7,6 +7,7 @@ class="hds-advanced-table__container {{(if this.isStickyHeaderPinned 'hds-advanced-table__container--header-is-pinned')}}" {{did-update this.setupTableModelData @columns @model @sortBy @sortOrder @columnOrder}} + {{did-update this.updateTableModelColumnOrder @columnOrder}} ...attributes > {{! Caption }} diff --git a/packages/components/src/components/hds/advanced-table/index.ts b/packages/components/src/components/hds/advanced-table/index.ts index bdf30b28163..206118ff104 100644 --- a/packages/components/src/components/hds/advanced-table/index.ts +++ b/packages/components/src/components/hds/advanced-table/index.ts @@ -612,6 +612,15 @@ export default class HdsAdvancedTable extends Component { 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 32f0789b709..a47dfcedad3 100644 --- a/packages/components/src/components/hds/advanced-table/models/table.ts +++ b/packages/components/src/components/hds/advanced-table/models/table.ts @@ -92,9 +92,12 @@ export default class HdsAdvancedTableTableModel { this.hasStickyFirstColumn = hasStickyFirstColumn; this.onSort = onSort; - this.setupData({ model, columns, columnOrder, sortBy, sortOrder }); + this.setupData({ model, columns, sortBy, sortOrder }); - this.setColumnOrder(columnOrder); + // set initial column order + this.columnOrder = isEmpty(columnOrder) + ? this.columns.map((column) => column.key) + : columnOrder!; // ensured non-empty this.onColumnReorder = onColumnReorder; } @@ -230,20 +233,14 @@ export default class HdsAdvancedTableTableModel { return this.columns.find((column) => column.key === key); } - setColumnOrder(columnOrder?: string[]): void { - this.columnOrder = isEmpty(columnOrder) - ? this.columns.map((column) => column.key) - : columnOrder!; // ensured non-empty - } - @action setupData( args: Pick< HdsAdvancedTableTableArgs, - 'model' | 'columns' | 'columnOrder' | 'sortBy' | 'sortOrder' + 'model' | 'columns' | 'sortBy' | 'sortOrder' > ) { - const { model, columns, columnOrder, sortBy, sortOrder } = args; + const { model, columns, sortBy, sortOrder } = args; this.sortBy = sortBy; this.sortOrder = sortOrder ?? HdsAdvancedTableThSortOrderValues.Asc; @@ -256,8 +253,6 @@ export default class HdsAdvancedTableTableModel { }) ); - this.setColumnOrder(columnOrder); - this.rows = model.map((row) => { return new HdsAdvancedTableRow({ ...row, @@ -402,7 +397,7 @@ export default class HdsAdvancedTableTableModel { updated.splice(adjustedIndex, 0, sourceColumn.key); // Insert at new position - this.setColumnOrder(updated); + this.columnOrder = updated; for (const row of this.rows) { row.columnOrder = updated; From d40df908d16150873a54684a1692fa462eb1dae1 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Mon, 8 Sep 2025 14:51:51 -0400 Subject: [PATCH 79/98] not showing pin/unpin option if columns are reorderable --- .../components/hds/advanced-table/index.ts | 67 +++++++++++-------- .../hds/advanced-table/th-context-menu.hbs | 44 ++++++------ .../hds/advanced-table/th-context-menu.ts | 7 +- .../components/hds/advanced-table/th-sort.hbs | 24 +++---- .../components/hds/advanced-table/th-sort.ts | 12 ---- .../src/components/hds/advanced-table/th.hbs | 24 +++---- .../src/components/hds/advanced-table/th.ts | 12 ---- 7 files changed, 89 insertions(+), 101 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.ts b/packages/components/src/components/hds/advanced-table/index.ts index 206118ff104..57fc5df3541 100644 --- a/packages/components/src/components/hds/advanced-table/index.ts +++ b/packages/components/src/components/hds/advanced-table/index.ts @@ -234,7 +234,6 @@ export default class HdsAdvancedTable extends Component column.isSortable); - const sortableColumnLabels = sortableColumns.map( - (column) => column.label - ); - - assert( - 'Cannot have reorderable columns if there are nested rows.', - !hasReorderableColumns - ); - - assert( - `Cannot have sortable columns if there are nested rows. Sortable columns are ${sortableColumnLabels.toString()}`, - sortableColumns.length === 0 - ); - - assert( - 'Cannot have a sticky first column if there are nested rows.', - !hasStickyFirstColumn - ); - - assert( - `Cannot have resizable columns if there are nested rows.`, - !hasResizableColumns - ); - } + this._runAssertions(); if (hasStickyFirstColumn) { this.hasPinnedFirstColumn = true; @@ -532,6 +506,42 @@ export default class HdsAdvancedTable extends Component column.isSortable); + const sortableColumnLabels = sortableColumns.map( + (column) => column.label + ); + + assert( + 'Cannot have reorderable columns if there are nested rows.', + !hasReorderableColumns + ); + + assert( + `Cannot have sortable columns if there are nested rows. Sortable columns are ${sortableColumnLabels.toString()}`, + sortableColumns.length === 0 + ); + + assert( + 'Cannot have a sticky first column if there are nested rows.', + !hasStickyFirstColumn + ); + + assert( + `Cannot have resizable columns if there are nested rows.`, + !hasResizableColumns + ); + } + } + private _setUpThead = modifier((element: HTMLDivElement) => { this._theadElement = element; }); @@ -601,11 +611,10 @@ export default class HdsAdvancedTable extends Component - +{{#if (gt this._options.length 0)}} + + - {{#each this._options as |option|}} - {{#if (eq option.key "separator")}} - - {{else if option.action}} - - {{option.label}} - - {{/if}} - {{/each}} - \ No newline at end of file + {{#each this._options as |option|}} + {{#if (eq option.key "separator")}} + + {{else if option.action}} + + {{option.label}} + + {{/if}} + {{/each}} + +{{/if}} \ No newline at end of file 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 a4458217170..b1bebbae2e8 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 @@ -172,7 +172,12 @@ export default class HdsAdvancedTableThContextMenu extends Component {{#if @column}} - {{#if this.showContextMenu}} - - {{/if}} + {{#if (and @hasReorderableColumns (not @isStickyColumn))}} - {{/if}} + {{#if (and @hasReorderableColumns (not @isStickyColumn))}} Date: Mon, 8 Sep 2025 15:11:36 -0400 Subject: [PATCH 80/98] no longer showing 'move to first' option in context menu if there is a sticky column --- .../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 b1bebbae2e8..032af0a910f 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 @@ -81,7 +81,7 @@ export default class HdsAdvancedTableThContextMenu extends Component Date: Mon, 8 Sep 2025 15:57:32 -0400 Subject: [PATCH 81/98] ammending tests --- .../hds/advanced-table/index-test.js | 38 ++++++++----------- 1 file changed, 16 insertions(+), 22 deletions(-) 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 b473a3d0b2f..d4bcd9ce322 100644 --- a/showcase/tests/integration/components/hds/advanced-table/index-test.js +++ b/showcase/tests/integration/components/hds/advanced-table/index-test.js @@ -773,7 +773,7 @@ module('Integration | Component | hds/advanced-table/index', function (hooks) { }); module('with @hasStickyFirstColumn enabled', function () { - test('the sticky first column does not have context menu options for column reordering', async function (assert) { + test('the sticky first column does not show the context menu if no other options with context menu items are enabled', async function (assert) { await render( hbs` { + const contextMenuToggle = element.querySelector( + '.hds-dropdown-toggle-icon', + ); - const columnOrder = await getColumnOrder(this.columns); - assert.deepEqual( - columnOrder, - [this.columns[0].key, this.columns[2].key, this.columns[1].key], - 'The third column is moved to the first non-sticky position', - ); + if (contextMenuToggle !== null) { + await click(contextMenuToggle); + + assert + .dom('[data-test-context-option-key="move-column-to-start"]') + .doesNotExist('no "Move to start" option is rendered'); + } + }); }); test('pressing "Left Arrow" when the reorder handle of the first non-sticky column is focused does not reposition the column before the sticky column', async function (assert) { From d6445861f8732fa96b81a3eb0f9f38c2ed50927e Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Mon, 8 Sep 2025 16:22:11 -0400 Subject: [PATCH 82/98] fixed the focus ring style on the reorder handle --- .../src/styles/components/advanced-table.scss | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/components/src/styles/components/advanced-table.scss b/packages/components/src/styles/components/advanced-table.scss index 64a34ec9b87..3e9e89e5e4a 100644 --- a/packages/components/src/styles/components/advanced-table.scss +++ b/packages/components/src/styles/components/advanced-table.scss @@ -392,11 +392,13 @@ $hds-advanced-table-drag-preview-background-color: rgba(204, 227, 254, 30%); } } - &:focus .hds-advanced-table__th-reorder-handle__inner, - &.mock-focus .hds-advanced-table__th-reorder-handle__inner { - @include hds-focus-ring-with-pseudo-element-focus-always-visible($position: absolute); + &:focus, + &.mock-focus { + @include hds-focus-ring-with-pseudo-element-focus-always-visible($position: absolute, $top: 3px, $bottom: 3px); - background-color: var(--token-color-surface-interactive); + .hds-advanced-table__th-reorder-handle__inner { + background-color: var(--token-color-surface-interactive); + } } } From be759d503b2e7a080b3348c51320e52779d7251d Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Tue, 9 Sep 2025 11:09:21 -0400 Subject: [PATCH 83/98] made sure the drop target had the correct styles when dropping on tables with selectable rows --- .../components/src/components/hds/advanced-table/index.hbs | 4 +++- .../components/hds/advanced-table/th-reorder-drop-target.ts | 4 +++- .../components/src/components/hds/advanced-table/th-sort.hbs | 1 + .../components/src/components/hds/advanced-table/th-sort.ts | 1 + packages/components/src/components/hds/advanced-table/th.hbs | 1 + packages/components/src/components/hds/advanced-table/th.ts | 1 + 6 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.hbs b/packages/components/src/components/hds/advanced-table/index.hbs index f3619abbea2..66f5e1ffc61 100644 --- a/packages/components/src/components/hds/advanced-table/index.hbs +++ b/packages/components/src/components/hds/advanced-table/index.hbs @@ -6,7 +6,7 @@
@@ -59,6 +59,7 @@ @tooltip={{column.tooltip}} @hasReorderableColumns={{@hasReorderableColumns}} @hasResizableColumns={{@hasResizableColumns}} + @hasSelectableRows={{this.isSelectable}} @hasStickyFirstColumn={{@hasStickyFirstColumn}} @isStickyColumn={{this._isStickyColumn column}} @isStickyColumnPinned={{this.isStickyColumnPinned}} @@ -79,6 +80,7 @@ @hasExpandAllButton={{this._tableModel.hasRowsWithChildren}} @hasReorderableColumns={{@hasReorderableColumns}} @hasResizableColumns={{@hasResizableColumns}} + @hasSelectableRows={{this.isSelectable}} @hasStickyFirstColumn={{@hasStickyFirstColumn}} @isExpanded={{this._tableModel.expandState}} @isExpandable={{column.isExpandable}} diff --git a/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.ts b/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.ts index 09a9f9597dc..a07bb042372 100644 --- a/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.ts +++ b/packages/components/src/components/hds/advanced-table/th-reorder-drop-target.ts @@ -13,10 +13,12 @@ import { HdsAdvancedTableColumnReorderSideValues } from './types.ts'; import type HdsAdvancedTableColumn from './models/column.ts'; import type { HdsAdvancedTableColumnReorderSide } from './types.ts'; +import type { HdsAdvancedTableSignature } from './index.ts'; export interface HdsAdvancedTableThReorderDropTargetSignature { Args: { column: HdsAdvancedTableColumn; + hasSelectableRows?: HdsAdvancedTableSignature['Args']['isSelectable']; tableHeight?: number; onReorderDrop?: ( column: HdsAdvancedTableColumn, @@ -63,7 +65,7 @@ export default class HdsAdvancedTableThReorderDropTarget extends Component 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 d4fe415d6a2..730225352fd 100644 --- a/packages/components/src/components/hds/advanced-table/th-sort.ts +++ b/packages/components/src/components/hds/advanced-table/th-sort.ts @@ -42,6 +42,7 @@ export interface HdsAdvancedTableThSortSignature { align?: HdsAdvancedTableHorizontalAlignment; hasReorderableColumns?: HdsAdvancedTableSignature['Args']['hasReorderableColumns']; hasResizableColumns?: HdsAdvancedTableSignature['Args']['hasResizableColumns']; + hasSelectableRows?: HdsAdvancedTableSignature['Args']['isSelectable']; hasStickyFirstColumn?: HdsAdvancedTableSignature['Args']['hasStickyFirstColumn']; 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 22fc81c66fb..026b9a0f5a5 100644 --- a/packages/components/src/components/hds/advanced-table/th.hbs +++ b/packages/components/src/components/hds/advanced-table/th.hbs @@ -103,6 +103,7 @@ {{#if (and @column.table.hasColumnBeingDragged (not @isStickyColumn))}} diff --git a/packages/components/src/components/hds/advanced-table/th.ts b/packages/components/src/components/hds/advanced-table/th.ts index 3a492bbe0af..d987f525de8 100644 --- a/packages/components/src/components/hds/advanced-table/th.ts +++ b/packages/components/src/components/hds/advanced-table/th.ts @@ -39,6 +39,7 @@ export interface HdsAdvancedTableThSignature { hasExpandAllButton?: boolean; hasReorderableColumns?: HdsAdvancedTableSignature['Args']['hasReorderableColumns']; hasResizableColumns?: HdsAdvancedTableSignature['Args']['hasResizableColumns']; + hasSelectableRows?: HdsAdvancedTableSignature['Args']['isSelectable']; hasStickyFirstColumn?: HdsAdvancedTableSignature['Args']['hasStickyFirstColumn']; isExpanded?: HdsAdvancedTableExpandState; isExpandable?: boolean; From 9d4c01169ddd3174e8aa2946d6600b4d15741e11 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Wed, 10 Sep 2025 10:03:59 -0400 Subject: [PATCH 84/98] removed support for hasStickyFirstColumn together with hasReorderableColumns --- .../components/hds/advanced-table/index.ts | 7 + .../page-components/advanced-table.hbs | 43 ----- .../hds/advanced-table/index-test.js | 156 ++---------------- 3 files changed, 23 insertions(+), 183 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.ts b/packages/components/src/components/hds/advanced-table/index.ts index 57fc5df3541..2c3cd4b42b4 100644 --- a/packages/components/src/components/hds/advanced-table/index.ts +++ b/packages/components/src/components/hds/advanced-table/index.ts @@ -540,6 +540,13 @@ export default class HdsAdvancedTable extends Component { diff --git a/showcase/app/templates/page-components/advanced-table.hbs b/showcase/app/templates/page-components/advanced-table.hbs index bb443397da7..8414c9dab40 100644 --- a/showcase/app/templates/page-components/advanced-table.hbs +++ b/showcase/app/templates/page-components/advanced-table.hbs @@ -744,49 +744,6 @@ - Reorderable columns with sticky first column - -
- - <:body as |B|> - - {{#each R.orderedCells as |C|}} - {{#if (eq C.columnKey "artist")}} - {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} - {{B.data.artist}} - {{else}} - - {{#if (eq C.columnKey "album")}} -
- {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} - - {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} - {{B.data.album}} -
- {{else if (eq C.columnKey "year")}} - {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} - - {{else}} - - - - - - {{/if}} -
- {{/if}} - {{/each}} -
- -
-
- Reorderable columns with selectable rows
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 d4bcd9ce322..f9257f22f63 100644 --- a/showcase/tests/integration/components/hds/advanced-table/index-test.js +++ b/showcase/tests/integration/components/hds/advanced-table/index-test.js @@ -772,150 +772,26 @@ module('Integration | Component | hds/advanced-table/index', function (hooks) { ); }); - module('with @hasStickyFirstColumn enabled', function () { - test('the sticky first column does not show the context menu if no other options with context menu items are enabled', async function (assert) { - await render( - hbs``, - ); - - const thElements = findAll('.hds-advanced-table__th'); - - const firstContextMenuToggle = thElements[0].querySelector( - '.hds-dropdown-toggle-icon', - ); - - assert - .dom(firstContextMenuToggle) - .doesNotExist('the context menu is not rendered'); - }); - - test('when dragging a column, the sticky column does not have a drop target', async function (assert) { - await render( - hbs``, - ); - - const initialColumnOrder = this.columns.map((col) => col.key); - - let columnOrder = await getColumnOrder(this.columns); - assert.deepEqual( - columnOrder, - initialColumnOrder, - 'Initial column order is correct', - ); - - const secondReorderHandle = findAll( - '.hds-advanced-table__th-reorder-handle', - )[1]; - - const { target, eventOptions } = await simulateColumnReorderDrag({ - handleElement: secondReorderHandle, - targetElement: findAll('.hds-advanced-table__th')[0], - targetPosition: 'left', - }); - - assert - .dom(findAll('.hds-advanced-table__th-reorder-drop-target')[1]) - .hasClass( - 'hds-advanced-table__th-reorder-drop-target--is-being-dragged', - 'Third column is being dragged', - ); - - assert - .dom( - '.hds-advanced-table__th:first-of-type .hds-advanced-table__th-reorder-drop-target', - ) - .doesNotExist('Drop target is not rendered on sticky column'); - - await simulateColumnReorderDrop({ - target, - handleElement: secondReorderHandle, - eventOptions, - }); - - columnOrder = await getColumnOrder(this.columns); - - assert - .dom('.hds-advanced-table__th-reorder-drop-target') - .doesNotExist('Drop targets are removed after drop'); - assert.deepEqual( - columnOrder, - initialColumnOrder, - 'Columns order is unchanged after dropping on the sticky first column', - ); - }); - - test('if `@hasStickyFirstColumn` is enabled, columns do not have a "Move to start" option', async function (assert) { - await render( - hbs``, - ); + test('it throws an assertion if @hasStickyFirstColumn is true and @hasReorderableColumns is true', async function (assert) { + const errorMessage = + 'Cannot have both reorderable columns and a sticky first column.'; - const thElements = findAll('.hds-advanced-table__th'); - - thElements.forEach(async (element) => { - const contextMenuToggle = element.querySelector( - '.hds-dropdown-toggle-icon', - ); - - if (contextMenuToggle !== null) { - await click(contextMenuToggle); + await render( + hbs``, + ); - assert - .dom('[data-test-context-option-key="move-column-to-start"]') - .doesNotExist('no "Move to start" option is rendered'); - } - }); + setupOnerror(function (error) { + assert.strictEqual(error.message, `Assertion Failed: ${errorMessage}`); }); - test('pressing "Left Arrow" when the reorder handle of the first non-sticky column is focused does not reposition the column before the sticky column', async function (assert) { - await render( - hbs``, - ); - - const thElements = findAll('.hds-advanced-table__th'); - const firstNonStickyThElement = thElements[1]; - const firstNonStickyColumnReorderHandle = - firstNonStickyThElement.querySelector( - '.hds-advanced-table__th-reorder-handle', - ); - await focus(firstNonStickyThElement); - await focus(firstNonStickyColumnReorderHandle); - assert.dom(firstNonStickyColumnReorderHandle).isFocused(); - - await triggerKeyEvent( - firstNonStickyColumnReorderHandle, - 'keydown', - 'ArrowLeft', - ); - let columnOrder = await getColumnOrder(this.columns); - assert.deepEqual( - columnOrder, - [this.columns[0].key, this.columns[1].key, this.columns[2].key], - 'column order is unchanged', - ); + assert.throws(function () { + throw new Error(errorMessage); }); }); }); From 4e5cc9d65579118c0db7f643b665aa929b5b446f Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Wed, 10 Sep 2025 10:35:17 -0400 Subject: [PATCH 85/98] fixed assertion test --- .../components/hds/advanced-table/index-test.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 f9257f22f63..b7a5ab81265 100644 --- a/showcase/tests/integration/components/hds/advanced-table/index-test.js +++ b/showcase/tests/integration/components/hds/advanced-table/index-test.js @@ -776,6 +776,10 @@ module('Integration | Component | hds/advanced-table/index', function (hooks) { const errorMessage = 'Cannot have both reorderable columns and a sticky first column.'; + setupOnerror(function (error) { + assert.strictEqual(error.message, `Assertion Failed: ${errorMessage}`); + }); + await render( hbs``, ); - setupOnerror(function (error) { - assert.strictEqual(error.message, `Assertion Failed: ${errorMessage}`); - }); - assert.throws(function () { throw new Error(errorMessage); }); From a3b35ee1099b92d8b2704af3defca965cb89a4da Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Wed, 10 Sep 2025 10:40:19 -0400 Subject: [PATCH 86/98] removed column reordering from the generic advanced table example --- .../mock/app/main/generic-advanced-table.gts | 167 ++++++++---------- 1 file changed, 74 insertions(+), 93 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 4e1f3c003ca..c238ae7606e 100644 --- a/showcase/app/components/mock/app/main/generic-advanced-table.gts +++ b/showcase/app/components/mock/app/main/generic-advanced-table.gts @@ -6,7 +6,6 @@ import Component from '@glimmer/component'; import { action } from '@ember/object'; import { deepTracked } from 'ember-deep-tracked'; import { get } from '@ember/helper'; -import { eq } from 'ember-truth-helpers'; // HDS components import { @@ -525,7 +524,6 @@ export default class MockAppMainGenericAdvancedTable extends Component <:body as |B|> {{! @glint-expect-error }} - - {{#each R.orderedCells as |C|}} - {{#if (eq C.columnKey "name")}} - - - {{! @glint-expect-error }} - {{get B.data "name"}} - - - {{else if (eq C.columnKey "project-name")}} - - - {{! @glint-expect-error }} - {{get B.data "project-name"}} - - - {{else if (eq C.columnKey "current-run-id")}} - - - {{! @glint-expect-error }} - {{get B.data "current-run-id"}} - - - {{else if (eq C.columnKey "run-status")}} - - - - {{else if (eq C.columnKey "current-run-applied")}} - - {{! @glint-expect-error }} - {{get B.data "current-run-applied"}} - - {{else if (eq C.columnKey "vcs-repo")}} - - {{! @glint-expect-error }} - {{get B.data "vcs-repo"}} - - {{else if (eq C.columnKey "module-count")}} - - - {{! @glint-expect-error }} - {{get B.data "module-count"}} - - - {{else if (eq C.columnKey "modules")}} - - {{! @glint-expect-error }} - {{get B.data "modules"}} - - {{else if (eq C.columnKey "provider-count")}} - - - {{! @glint-expect-error }} - {{get B.data "provider-count"}} - - - {{else if (eq C.columnKey "providers")}} - - {{! @glint-expect-error }} - {{get B.data "providers"}} - - {{else if (eq C.columnKey "terraform-version")}} - - {{! @glint-expect-error }} - {{get B.data "terraform-version"}} - - {{else if (eq C.columnKey "state-terraform-version")}} - - - {{! @glint-expect-error }} - {{get B.data "state-terraform-version"}} - - - {{else if (eq C.columnKey "created")}} - - {{! @glint-expect-error }} - {{get B.data "created"}} - - {{else if (eq C.columnKey "updated")}} - - {{! @glint-expect-error }} - {{get B.data "updated"}} - - {{/if}} - {{/each}} + + + + {{! @glint-expect-error }} + {{get B.data "name"}} + + + + + {{! @glint-expect-error }} + {{get B.data "project-name"}} + + + + + {{! @glint-expect-error }} + {{get B.data "current-run-id"}} + + + + + + + {{! @glint-expect-error }} + {{get B.data "current-run-applied"}} + + + {{! @glint-expect-error }} + {{get B.data "vcs-repo"}} + + + + {{! @glint-expect-error }} + {{get B.data "module-count"}} + + + + {{! @glint-expect-error }} + {{get B.data "modules"}} + + + + {{! @glint-expect-error }} + {{get B.data "provider-count"}} + + + + {{! @glint-expect-error }} + {{get B.data "providers"}} + + + {{! @glint-expect-error }} + {{get B.data "terraform-version"}} + + + + {{! @glint-expect-error }} + {{get B.data "state-terraform-version"}} + + + + {{! @glint-expect-error }} + {{get B.data "created"}} + + + {{! @glint-expect-error }} + {{get B.data "updated"}} + From f044514b191b8aa24836c1c6bf0575da034c55be Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Wed, 10 Sep 2025 14:29:03 -0400 Subject: [PATCH 87/98] set up overflowing table for sortable demo --- .../hds/advanced-table/models/table.ts | 6 +- showcase/app/mocks/infrastructure-data.ts | 131 ++++++++++++++++++ .../routes/page-components/advanced-table.ts | 2 + .../page-components/advanced-table.hbs | 74 ++++++++++ 4 files changed, 208 insertions(+), 5 deletions(-) create mode 100644 showcase/app/mocks/infrastructure-data.ts 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 a47dfcedad3..a729d790692 100644 --- a/packages/components/src/components/hds/advanced-table/models/table.ts +++ b/packages/components/src/components/hds/advanced-table/models/table.ts @@ -404,11 +404,7 @@ export default class HdsAdvancedTableTableModel { } queueMicrotask(() => { - sourceColumn.thElement?.scrollIntoView({ - behavior: 'smooth', - block: 'center', - inline: 'center', - }); + // TODO: Scroll into view, but horizontally sourceColumn.isBeingDragged = false; diff --git a/showcase/app/mocks/infrastructure-data.ts b/showcase/app/mocks/infrastructure-data.ts new file mode 100644 index 00000000000..1895fe90f29 --- /dev/null +++ b/showcase/app/mocks/infrastructure-data.ts @@ -0,0 +1,131 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import type { HdsIconSignature } from '@hashicorp/design-system-components/components/hds/icon/index'; + +export interface InfrastructureResource { + resource_id: string; + status: 'active' | 'pending' | 'failing' | 'establishing'; + namespace: string; + provider_name: 'aws' | 'gcp' | 'azure' | 'kubernetes'; + created_at: Date; + last_run_time: Date; + lease_duration: string; + workspace: string; + datacenter: string; + job_spec_version: number; + attached_policies: string[]; + target_endpoint: string; + audit_device_path: string; + tags: string[]; + icon: HdsIconSignature['Args']['name']; +} + +const infrastructureResources: InfrastructureResource[] = [ + { + resource_id: 'd9b1a2c3-f4e5-6a7b-8c9d-0e1f2a3b4c5d', + status: 'active', + namespace: 'admin/secrets', + provider_name: 'aws', + created_at: new Date('2025-08-15T14:30:00Z'), + last_run_time: new Date('2025-09-10T11:55:12Z'), + lease_duration: '720h', + workspace: 'prod-us-east-1', + datacenter: 'us-east-1', + job_spec_version: 2, + attached_policies: ['root-access', 'audit-writer'], + target_endpoint: 'pki_int.vault.internal:8200', + audit_device_path: '/var/log/vault_audit.log', + tags: ['pki', 'production', 'vault'], + icon: 'vault-color', + }, + { + resource_id: 'e8c2b3d4-a5f6-7b8c-9d0e-1f2a3b4c5d6e', + status: 'establishing', + namespace: 'default', + provider_name: 'azure', + created_at: new Date('2025-09-01T10:00:00Z'), + last_run_time: new Date('2025-09-01T10:05:30Z'), + lease_duration: 'N/A', + workspace: 'dev-network-staging', + datacenter: 'eastus2', + job_spec_version: 5, + attached_policies: ['network-contributor'], + target_endpoint: 'resource-group:rg-dev-net', + audit_device_path: 'N/A', + tags: ['networking', 'terraform-cloud'], + icon: 'terraform-color', + }, + { + resource_id: 'f7d3c4e5-b6a7-8c9d-0e1f-2a3b4c5d6e7f', + status: 'active', + namespace: 'api-gateway', + provider_name: 'gcp', + created_at: new Date('2025-07-20T08:00:00Z'), + last_run_time: new Date('2025-09-10T12:30:00Z'), + lease_duration: '24h', + workspace: 'prod-api', + datacenter: 'us-central1-a', + job_spec_version: 8, + attached_policies: ['service-reader'], + target_endpoint: 'payments-api.service.consul', + audit_device_path: '/var/log/consul_audit.log', + tags: ['api', 'service-mesh', 'consul'], + icon: 'consul-color', + }, + { + resource_id: 'a6e4d5f6-c7b8-9d0e-1f2a-3b4c5d6e7f8a', + status: 'failing', + namespace: 'batch-processing', + provider_name: 'kubernetes', + created_at: new Date('2025-09-09T18:00:00Z'), + last_run_time: new Date('2025-09-09T18:15:00Z'), + lease_duration: '1h', + workspace: 'analytics-jobs', + datacenter: 'on-prem-dc1', + job_spec_version: 12, + attached_policies: ['job-runner', 'logs-reader'], + target_endpoint: 'task:redis-cache', + audit_device_path: 'N/A', + tags: ['batch', 'analytics', 'nomad'], + icon: 'nomad-color', + }, + { + resource_id: 'b5f5e6a7-d8c9-0e1f-2a3b-4c5d6e7f8a9b', + status: 'active', + namespace: 'ssh-targets', + provider_name: 'aws', + created_at: new Date('2025-06-01T00:00:00Z'), + last_run_time: new Date('2025-09-10T09:45:21Z'), + lease_duration: '8h', + workspace: 'corp-ssh-access', + datacenter: 'us-west-2', + job_spec_version: 1, + attached_policies: ['dba-access-prod'], + target_endpoint: 'tcp://db-prod-1.rds.amazonaws.com:5432', + audit_device_path: '/var/log/boundary_session.log', + tags: ['database', 'ssh', 'rds', 'boundary'], + icon: 'boundary-color', + }, + { + resource_id: 'c4a6f7b8-e9d0-1f2a-3b4c-5d6e7f8a9b0c', + status: 'pending', + namespace: 'web-app', + provider_name: 'gcp', + created_at: new Date('2025-09-10T12:51:00Z'), + last_run_time: new Date('2025-09-10T12:51:00Z'), + lease_duration: 'N/A', + workspace: 'webapp-staging', + datacenter: 'us-west1-b', + job_spec_version: 3, + attached_policies: ['deployer-role'], + target_endpoint: 'cloud-run:frontend-staging-app', + audit_device_path: 'N/A', + tags: ['frontend', 'waypoint', 'cicd'], + icon: 'waypoint-color', + }, +]; + +export default infrastructureResources; diff --git a/showcase/app/routes/page-components/advanced-table.ts b/showcase/app/routes/page-components/advanced-table.ts index e718f7ca5e0..c876457ba81 100644 --- a/showcase/app/routes/page-components/advanced-table.ts +++ b/showcase/app/routes/page-components/advanced-table.ts @@ -15,6 +15,7 @@ import policies from 'showcase/mocks/policy-data'; import selectableItems from 'showcase/mocks/selectable-item-data'; import spanningCells from 'showcase/mocks/spanning-cell-data'; import users from 'showcase/mocks/user-data'; +import infrastructureResources from 'showcase/mocks/infrastructure-data'; export type PageComponentsAdvancedTableModel = ModelFrom; @@ -28,6 +29,7 @@ export default class PageComponentsAdvancedTableRoute extends Route { userDataShort: structuredClone(users.slice(0, 5)), clusters, spanningManualData: spanningCells, + infrastructureResources, selectableData: selectableItems, selectableDataDemo1: structuredClone(selectableItems), selectableDataDemo2: structuredClone(selectableItems), diff --git a/showcase/app/templates/page-components/advanced-table.hbs b/showcase/app/templates/page-components/advanced-table.hbs index 8414c9dab40..528eb1db122 100644 --- a/showcase/app/templates/page-components/advanced-table.hbs +++ b/showcase/app/templates/page-components/advanced-table.hbs @@ -744,6 +744,80 @@ + Reorderable columns with horizontal overflow + +
+ + <:body as |B|> + + {{#each R.orderedCells as |C|}} + + {{#if (eq C.columnKey "status")}} + {{#if (eq (get B.data "status") "failing")}} + + {{else if (eq (get B.data "status") "active")}} + + {{else if (eq (get B.data "status") "pending")}} + + {{else if (eq (get B.data "status") "establishing")}} + + {{/if}} + {{else if (or (eq C.columnKey "created_at") (eq C.columnKey "last_run_time"))}} + {{#if (eq C.columnKey "created_at")}} + {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} + {{format-date (get B.data "created_at")}} + {{else}} + {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} + {{format-date (get B.data "last_run_time")}} + {{/if}} + {{else if (or (eq C.columnKey "attached_policies") (eq C.columnKey "tags"))}} + + {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} + {{#each C.content as |content|}} + + {{/each}} + + {{else}} + {{! @glint-expect-error - will be fixed by https://hashicorp.atlassian.net/browse/HDS-5090}} + {{C.content}} + {{/if}} + + {{/each}} + + + +
+ Reorderable columns with selectable rows
From b6ec224af8602ee59537a5a7f86649d3be508c3c Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Wed, 10 Sep 2025 15:49:50 -0400 Subject: [PATCH 88/98] scrollIntoView works when reordering --- .../src/components/hds/advanced-table/models/table.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 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 a729d790692..705cc9fbb62 100644 --- a/packages/components/src/components/hds/advanced-table/models/table.ts +++ b/packages/components/src/components/hds/advanced-table/models/table.ts @@ -403,8 +403,12 @@ export default class HdsAdvancedTableTableModel { row.columnOrder = updated; } - queueMicrotask(() => { - // TODO: Scroll into view, but horizontally + requestAnimationFrame(() => { + sourceColumn.thElement?.scrollIntoView({ + behavior: 'smooth', + block: 'nearest', + inline: 'center', + }); sourceColumn.isBeingDragged = false; From 809e9c34c39851c7baaff6647edd3c74ad2a723e Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Wed, 10 Sep 2025 17:42:45 -0400 Subject: [PATCH 89/98] registering context menu toggle with the column class --- .../src/components/hds/advanced-table/models/column.ts | 4 ++++ .../src/components/hds/advanced-table/models/table.ts | 1 + .../src/components/hds/advanced-table/th-context-menu.ts | 8 ++++++++ 3 files changed, 13 insertions(+) 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 b0c1c9bbbcd..888afbaad73 100644 --- a/packages/components/src/components/hds/advanced-table/models/column.ts +++ b/packages/components/src/components/hds/advanced-table/models/column.ts @@ -9,6 +9,7 @@ import { guidFor } from '@ember/object/internals'; import type { HdsAdvancedTableThReorderHandleSignature } from '../th-reorder-handle.ts'; import type HdsAdvancedTableModel from './table.ts'; +import type { HdsDropdownToggleButtonSignature } from '../../dropdown/toggle/button.ts'; import type { HdsAdvancedTableCell, HdsAdvancedTableHorizontalAlignment, @@ -47,6 +48,9 @@ export default class HdsAdvancedTableColumn { @tracked reorderHandleElement?: HdsAdvancedTableThReorderHandleSignature['Element'] = undefined; + @tracked + thContextMenuToggleElement?: HdsDropdownToggleButtonSignature['Element'] = + undefined; // width properties @tracked transientWidth?: `${number}px` = undefined; // used for transient width changes 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 705cc9fbb62..e96ed06540b 100644 --- a/packages/components/src/components/hds/advanced-table/models/table.ts +++ b/packages/components/src/components/hds/advanced-table/models/table.ts @@ -403,6 +403,7 @@ export default class HdsAdvancedTableTableModel { row.columnOrder = updated; } + // we need to wait until the reposition has finished requestAnimationFrame(() => { sourceColumn.thElement?.scrollIntoView({ behavior: 'smooth', 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 032af0a910f..45dad0afed7 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 @@ -7,6 +7,7 @@ import Component from '@glimmer/component'; import { service } from '@ember/service'; import { tracked } from '@glimmer/tracking'; import { scheduleOnce } from '@ember/runloop'; +import { modifier } from 'ember-modifier'; import type HdsAdvancedTableColumn from './models/column.ts'; import type { HdsDropdownSignature } from '../dropdown/index.ts'; @@ -14,6 +15,7 @@ import type { HdsDropdownToggleIconSignature } from '../dropdown/toggle/icon.ts' import type { HdsAdvancedTableSignature } from './index.ts'; import type { HdsAdvancedTableThReorderHandleSignature } from './th-reorder-handle.ts'; import type { HdsAdvancedTableThResizeHandleSignature } from './th-resize-handle.ts'; +import type { HdsDropdownToggleButtonSignature } from '../dropdown/toggle/button.ts'; import type HdsIntlService from '../../../services/hds-intl.ts'; interface HdsAdvancedTableThContextMenuOption { @@ -193,6 +195,12 @@ export default class HdsAdvancedTableThContextMenu extends Component { + this.args.column.thContextMenuToggleElement = element; + } + ); + private _resizeColumn() { this.args.resizeHandleElement?.focus(); } From e3715a694eb62d9ef37da58323601fe5f6d0e418 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Thu, 11 Sep 2025 10:54:39 -0400 Subject: [PATCH 90/98] refocusing the dropdown toggle when using the context menu to reorder --- .../src/components/hds/advanced-table/th-context-menu.hbs | 1 + .../src/components/hds/advanced-table/th-context-menu.ts | 8 +++++++- 2 files changed, 8 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 876e737030d..03841bf0b42 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,6 +9,7 @@ @text="Additional actions for {{@column.label}}" @hasChevron={{false}} @size="small" + {{this._registerDropdownToggleElement}} /> {{#each this._options as |option|}} 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 45dad0afed7..c7a5c25ff1f 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 @@ -234,7 +234,13 @@ export default class HdsAdvancedTableThContextMenu extends Component { + dropdownCloseCallback?.(); + + console.log(column.thContextMenuToggleElement); + + column.thContextMenuToggleElement?.focus(); + }); } private _pinFirstColumn(dropdownCloseCallback: () => void): void { From 76bba42319177901505b22f43588c33be5cf3292 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Thu, 11 Sep 2025 10:56:01 -0400 Subject: [PATCH 91/98] removed the console log --- .../src/components/hds/advanced-table/th-context-menu.ts | 2 -- 1 file changed, 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 c7a5c25ff1f..38dca54ff72 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 @@ -237,8 +237,6 @@ export default class HdsAdvancedTableThContextMenu extends Component { dropdownCloseCallback?.(); - console.log(column.thContextMenuToggleElement); - column.thContextMenuToggleElement?.focus(); }); } From 7d2967a79acd2944d8e276b68447563b2dadda2c Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Thu, 11 Sep 2025 14:00:31 -0400 Subject: [PATCH 92/98] responding to PR feedback --- .../components/hds/advanced-table/index.hbs | 2 -- .../components/hds/advanced-table/index.ts | 5 ++--- .../hds/advanced-table/models/column.ts | 4 ---- .../hds/advanced-table/models/table.ts | 13 +++-------- .../hds/advanced-table/th-context-menu.ts | 5 ++--- .../hds/advanced-table/th-reorder-handle.ts | 6 ----- .../components/hds/advanced-table/th-sort.hbs | 3 +-- .../components/hds/advanced-table/th-sort.ts | 1 - .../src/components/hds/advanced-table/th.hbs | 1 - .../src/components/hds/advanced-table/th.ts | 1 - .../th-reorder-handle/en-us.yaml | 2 +- .../page-components/advanced-table.hbs | 1 - .../hds/advanced-table/index-test.js | 22 +++++++++++++++++++ 13 files changed, 31 insertions(+), 35 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.hbs b/packages/components/src/components/hds/advanced-table/index.hbs index 66f5e1ffc61..6af13b3d0cf 100644 --- a/packages/components/src/components/hds/advanced-table/index.hbs +++ b/packages/components/src/components/hds/advanced-table/index.hbs @@ -60,7 +60,6 @@ @hasReorderableColumns={{@hasReorderableColumns}} @hasResizableColumns={{@hasResizableColumns}} @hasSelectableRows={{this.isSelectable}} - @hasStickyFirstColumn={{@hasStickyFirstColumn}} @isStickyColumn={{this._isStickyColumn column}} @isStickyColumnPinned={{this.isStickyColumnPinned}} @tableHeight={{this._tableHeight}} @@ -81,7 +80,6 @@ @hasReorderableColumns={{@hasReorderableColumns}} @hasResizableColumns={{@hasResizableColumns}} @hasSelectableRows={{this.isSelectable}} - @hasStickyFirstColumn={{@hasStickyFirstColumn}} @isExpanded={{this._tableModel.expandState}} @isExpandable={{column.isExpandable}} @isStickyColumn={{this._isStickyColumn column}} diff --git a/packages/components/src/components/hds/advanced-table/index.ts b/packages/components/src/components/hds/advanced-table/index.ts index 2c3cd4b42b4..2ea74760a2a 100644 --- a/packages/components/src/components/hds/advanced-table/index.ts +++ b/packages/components/src/components/hds/advanced-table/index.ts @@ -247,7 +247,6 @@ export default class HdsAdvancedTable extends Component= table.orderedColumns.length) { + if (newIndex < 0 || newIndex >= table.orderedColumns.length) { return; } @@ -339,9 +334,7 @@ export default class HdsAdvancedTableTableModel { column: HdsAdvancedTableColumn, position: 'start' | 'end' ): void { - const firstNonStickyColumn = this.orderedColumns.find( - (column) => column.isFirstNonSticky - ); + const firstColumn = this.orderedColumns.find((column) => column.isFirst); const { targetColumn, @@ -352,7 +345,7 @@ export default class HdsAdvancedTableTableModel { } = position === 'start' ? { - targetColumn: firstNonStickyColumn, + targetColumn: firstColumn, side: HdsAdvancedTableColumnReorderSideValues.Left, } : { 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 38dca54ff72..629753f9a96 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 @@ -30,7 +30,6 @@ export interface HdsAdvancedTableThContextMenuSignature { column: HdsAdvancedTableColumn; hasResizableColumns?: boolean; hasReorderableColumns?: boolean; - hasStickyFirstColumn?: boolean; isStickyColumn?: boolean; reorderHandleElement?: HdsAdvancedTableThReorderHandleSignature['Element']; resizeHandleElement?: HdsAdvancedTableThResizeHandleSignature['Element']; @@ -83,7 +82,7 @@ export default class HdsAdvancedTableThContextMenu extends Component - {{#if (and @hasReorderableColumns (not @isStickyColumn))}} + {{#if (and @hasReorderableColumns (not @isStickyColumn) (not @column.table.hasColumnBeingDragged))}} `, + ); + + const selectAllThSelector = + '[role="columnheader"].hds-advanced-table__th--is-selectable'; + const reorderHandleSelector = '.hds-advanced-table__th-reorder-handle'; + + assert + .dom(`${selectAllThSelector} ${reorderHandleSelector}`) + .doesNotExist( + 'No reorder handle is rendered on the row selection column', + ); + }); + test('columns can be reordered by dragging and dropping', async function (assert) { await render( hbs` Date: Thu, 11 Sep 2025 15:11:57 -0400 Subject: [PATCH 93/98] fixed an issue with reordering on sortable columns --- .../components/src/components/hds/advanced-table/th-sort.hbs | 4 ++-- 1 file changed, 2 insertions(+), 2 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 a3027815d1e..6606f130bc7 100644 --- a/packages/components/src/components/hds/advanced-table/th-sort.hbs +++ b/packages/components/src/components/hds/advanced-table/th-sort.hbs @@ -55,7 +55,7 @@ @onPinFirstColumn={{@onPinFirstColumn}} /> - {{#if (and @hasReorderableColumns (not @isStickyColumn) (not @column.table.hasColumnBeingDragged))}} + {{#if (and @hasReorderableColumns (not @isStickyColumn))}} {{/if}} - {{#if (and @hasResizableColumns (not @column.isLast))}} + {{#if (and @hasResizableColumns (not @column.isLast) (not @column.table.hasColumnBeingDragged))}} Date: Thu, 11 Sep 2025 16:39:00 -0400 Subject: [PATCH 94/98] added a changeset --- .changeset/wet-files-joke.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .changeset/wet-files-joke.md diff --git a/.changeset/wet-files-joke.md b/.changeset/wet-files-joke.md new file mode 100644 index 00000000000..bb30a2ce5b4 --- /dev/null +++ b/.changeset/wet-files-joke.md @@ -0,0 +1,11 @@ +--- +"@hashicorp/design-system-components": minor +--- + + + +`AdvancedTable` - Added support for column reordering. +- Added `@hasReorderableColumns` argument. When set to `true`, enables column reordering. +- Added optional `@columnOrder` argument for setting the initial order of columns by their keys. +- Added optional `@onColumnReorder` argument which accepts a callback function that is called when reordering is completed. + From 04c1ca138ea77c61f73d957cf1fabd649f94b002 Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Thu, 11 Sep 2025 16:50:55 -0400 Subject: [PATCH 95/98] updated changeset --- .changeset/wet-files-joke.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.changeset/wet-files-joke.md b/.changeset/wet-files-joke.md index bb30a2ce5b4..89655e738c4 100644 --- a/.changeset/wet-files-joke.md +++ b/.changeset/wet-files-joke.md @@ -8,4 +8,5 @@ - Added `@hasReorderableColumns` argument. When set to `true`, enables column reordering. - Added optional `@columnOrder` argument for setting the initial order of columns by their keys. - Added optional `@onColumnReorder` argument which accepts a callback function that is called when reordering is completed. +- Added optional `@reorderedMessageText` which overrides the default message text that is rendered in the table caption when a column is reordered. From bf3817b935581c44c010243e2c0d12790ba377fe Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Fri, 12 Sep 2025 18:09:38 -0400 Subject: [PATCH 96/98] Apply suggestions from code review Co-authored-by: Lee White --- .changeset/wet-files-joke.md | 1 - .../components/src/components/hds/advanced-table/models/row.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.changeset/wet-files-joke.md b/.changeset/wet-files-joke.md index 89655e738c4..1aeb5e22a37 100644 --- a/.changeset/wet-files-joke.md +++ b/.changeset/wet-files-joke.md @@ -3,7 +3,6 @@ --- - `AdvancedTable` - Added support for column reordering. - Added `@hasReorderableColumns` argument. When set to `true`, enables column reordering. - Added optional `@columnOrder` argument for setting the initial order of columns by their keys. 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 60b9e2ad5bf..45a2e977ef0 100644 --- a/packages/components/src/components/hds/advanced-table/models/row.ts +++ b/packages/components/src/components/hds/advanced-table/models/row.ts @@ -43,7 +43,7 @@ export default class HdsAdvancedTableRow { const cell = this.cells.find((cell) => cell.columnKey === key); if (cell === undefined) { - throw new Error(`Column with key ${key} not found`); + throw new Error(`Cell in the column with key ${key} not found for the row.`); } return cell; From 3fe499a13b6893b580078911d3b6b1b57d1ab72b Mon Sep 17 00:00:00 2001 From: Zack Moore Date: Sun, 14 Sep 2025 22:48:30 -0400 Subject: [PATCH 97/98] change where the top scroll indicator renders --- .../components/hds/advanced-table/index.hbs | 11 ++++------- .../components/hds/advanced-table/index.ts | 4 ---- .../src/styles/components/advanced-table.scss | 19 +++++++++++++------ 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/components/src/components/hds/advanced-table/index.hbs b/packages/components/src/components/hds/advanced-table/index.hbs index 6af13b3d0cf..8f8bae33e58 100644 --- a/packages/components/src/components/hds/advanced-table/index.hbs +++ b/packages/components/src/components/hds/advanced-table/index.hbs @@ -100,6 +100,10 @@ {{/if}} {{/each}} + + {{#if this.showScrollIndicatorTop}} +
+ {{/if}}
{{! Body }} @@ -192,13 +196,6 @@ /> {{/if}} - {{#if this.showScrollIndicatorTop}} -
- {{/if}} - {{#if this.showScrollIndicatorBottom}}
Date: Mon, 15 Sep 2025 11:08:50 -0400 Subject: [PATCH 98/98] fixed a linting error --- .../src/components/hds/advanced-table/models/row.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 45a2e977ef0..7a56621526d 100644 --- a/packages/components/src/components/hds/advanced-table/models/row.ts +++ b/packages/components/src/components/hds/advanced-table/models/row.ts @@ -43,7 +43,9 @@ export default class HdsAdvancedTableRow { const cell = this.cells.find((cell) => cell.columnKey === key); if (cell === undefined) { - throw new Error(`Cell in the column with key ${key} not found for the row.`); + throw new Error( + `Cell in the column with key ${key} not found for the row.` + ); } return cell;