From 9b3c636fb0723e38951c4432a7194c8bf3c528bf Mon Sep 17 00:00:00 2001 From: logonoff Date: Wed, 7 Oct 2026 10:42:00 -0400 Subject: [PATCH] feat(ColumnManagement): Add sticky column ordering Assisted-by: Codex GPT-6 --- .../ColumnManagementModal.md | 2 +- .../ColumnManagementModalDragDropExample.tsx | 5 ++-- .../ColumnManagementModal.tsx | 25 ++++++++++++++++--- .../module/src/ListManager/ListManager.tsx | 15 +++++++++-- 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/packages/module/patternfly-docs/content/extensions/component-groups/examples/ColumnManagementModal/ColumnManagementModal.md b/packages/module/patternfly-docs/content/extensions/component-groups/examples/ColumnManagementModal/ColumnManagementModal.md index 902d53c3..c794ff33 100644 --- a/packages/module/patternfly-docs/content/extensions/component-groups/examples/ColumnManagementModal/ColumnManagementModal.md +++ b/packages/module/patternfly-docs/content/extensions/component-groups/examples/ColumnManagementModal/ColumnManagementModal.md @@ -32,7 +32,7 @@ Clicking the "Manage columns" button will open the column management modal. The ### With drag and drop reordering -When `enableDragDrop` is set to `true`, users can drag and drop columns to reorder them. The order changes are reflected both in the modal and in the table when applied. This is useful when column order matters for the user experience. +When `enableDragDrop` is set to `true`, users can drag and drop columns to reorder them. The order changes are reflected both in the modal and in the table when applied. Select a column's **Sticky** checkbox to keep it at its current position; other columns cannot be moved into that position. The `isSticky` column property reflects this selection. In this example, the first `ID` column is sticky. ```js file="./ColumnManagementModalDragDropExample.tsx" diff --git a/packages/module/patternfly-docs/content/extensions/component-groups/examples/ColumnManagementModal/ColumnManagementModalDragDropExample.tsx b/packages/module/patternfly-docs/content/extensions/component-groups/examples/ColumnManagementModal/ColumnManagementModalDragDropExample.tsx index 5c45ef61..268db6c9 100644 --- a/packages/module/patternfly-docs/content/extensions/component-groups/examples/ColumnManagementModal/ColumnManagementModalDragDropExample.tsx +++ b/packages/module/patternfly-docs/content/extensions/component-groups/examples/ColumnManagementModal/ColumnManagementModalDragDropExample.tsx @@ -12,7 +12,8 @@ const DEFAULT_COLUMNS: ColumnManagementModalColumn[] = [ key: 'id', isShownByDefault: true, isShown: true, - isUntoggleable: true + isUntoggleable: true, + isSticky: true }, { title: 'Publish date', @@ -128,4 +129,4 @@ export const ColumnManagementModalDragDropExample: FunctionComponent = () => { ); -}; \ No newline at end of file +}; diff --git a/packages/module/src/ColumnManagementModal/ColumnManagementModal.tsx b/packages/module/src/ColumnManagementModal/ColumnManagementModal.tsx index 006cb6c3..2e1262a5 100644 --- a/packages/module/src/ColumnManagementModal/ColumnManagementModal.tsx +++ b/packages/module/src/ColumnManagementModal/ColumnManagementModal.tsx @@ -20,6 +20,8 @@ export interface ColumnManagementModalColumn { isShownByDefault: boolean; /** The checkbox will be disabled, this is applicable to columns which should not be toggleable by user */ isUntoggleable?: boolean; + /** Keep this column at its current position when columns are reordered */ + isSticky?: boolean; } /** extends ModalProps */ @@ -74,7 +76,8 @@ const ColumnManagementModal: FunctionComponent = ( title: column.title, isSelected: column.isShown, isShownByDefault: column.isShownByDefault, - isUntoggleable: column.isUntoggleable + isUntoggleable: column.isUntoggleable, + isSticky: column.isSticky })); const resetToDefault = () => { @@ -87,7 +90,11 @@ const ColumnManagementModal: FunctionComponent = ( const newColumns = currentColumns.map(column => { const matchingItem = items.find(item => item.key === column.key); return matchingItem - ? { ...column, isShown: matchingItem.isSelected ?? column.isShownByDefault } + ? { + ...column, + isShown: matchingItem.isSelected ?? column.isShownByDefault, + isSticky: matchingItem.isSticky ?? column.isSticky + } : column; }); setCurrentColumns(newColumns); @@ -97,6 +104,10 @@ const ColumnManagementModal: FunctionComponent = ( updateColumns([ item ]); }; + const handleStickyChange = (item: ListManagerItem) => { + updateColumns([ item ]); + }; + const handleSelectAll = (items: ListManagerItem[]) => { updateColumns(items); }; @@ -108,7 +119,11 @@ const ColumnManagementModal: FunctionComponent = ( if (!originalColumn) { throw new Error(`Column with key ${item.key} not found`); } - return { ...originalColumn, isShown: item.isSelected ?? originalColumn.isShownByDefault }; + return { + ...originalColumn, + isShown: item.isSelected ?? originalColumn.isShownByDefault, + isSticky: item.isSticky ?? originalColumn.isSticky + }; }); setCurrentColumns(newColumns); }; @@ -119,7 +134,8 @@ const ColumnManagementModal: FunctionComponent = ( title: item.title, isShown: item.isSelected, isShownByDefault: item.isShownByDefault, - isUntoggleable: item.isUntoggleable + isUntoggleable: item.isUntoggleable, + isSticky: item.isSticky })); applyColumns(updatedColumns); onClose({} as KeyboardEvent); @@ -150,6 +166,7 @@ const ColumnManagementModal: FunctionComponent = ( columns={listManagerItems} ouiaId={ouiaId} onSelect={handleSelect} + onStickyChange={handleStickyChange} onSelectAll={handleSelectAll} onOrderChange={handleOrderChange} onSave={handleSave} diff --git a/packages/module/src/ListManager/ListManager.tsx b/packages/module/src/ListManager/ListManager.tsx index a2fbf9f3..2ce87295 100644 --- a/packages/module/src/ListManager/ListManager.tsx +++ b/packages/module/src/ListManager/ListManager.tsx @@ -27,6 +27,8 @@ export interface ListManagerItem { isShownByDefault: boolean; /** The checkbox will be disabled, this is applicable to columns which should not be toggleable by user */ isUntoggleable?: boolean; + /** Keep this column at its current position when columns are reordered */ + isSticky?: boolean; } export interface ListManagerProps { @@ -36,6 +38,8 @@ export interface ListManagerProps { ouiaId?: string | number; /** Callback when a column is selected or deselected */ onSelect?: (column: ListManagerItem) => void; + /** Callback when a column's sticky state changes */ + onStickyChange?: (column: ListManagerItem) => void; /** Callback when all columns are selected or deselected */ onSelectAll?: (columns: ListManagerItem[]) => void; /** Callback when the column order changes */ @@ -75,7 +79,7 @@ const ListManager: FunctionComponent = ( const newColumns = [ ...currentColumns ]; const index = newColumns.findIndex(col => col.key === columnKey); if (index === -1) {return;} - + const changedColumn = { ...newColumns[index] }; changedColumn.isSelected = !changedColumn.isSelected; newColumns[index] = changedColumn; @@ -85,13 +89,20 @@ const ListManager: FunctionComponent = ( }; const onDrag = (_event: unknown, newOrder: DraggableObject[]) => { - const newColumns = newOrder.map((item: DraggableObject) => { + const reorderedColumns = newOrder.map((item: DraggableObject) => { const found = currentColumns.find(c => c.key === String(item.id)); if (!found) { throw new Error(`Column with key ${item.id} not found`); } return found; }); + + // Sticky columns reserve their current positions. Reorder only the remaining columns. + const reorderedNonStickyColumns = reorderedColumns.filter(column => !column.isSticky); + let nonStickyIndex = 0; + const newColumns = currentColumns.map(column => ( + column.isSticky ? column : reorderedNonStickyColumns[nonStickyIndex++] + )); setCurrentColumns(newColumns); onOrderChange?.(newColumns); };