Skip to content

feat(ColumnManagementModal): add support for extended columns#929

Open
raswonders wants to merge 4 commits into
patternfly:mainfrom
raswonders:feat/column-management-modal-generic-columns
Open

feat(ColumnManagementModal): add support for extended columns#929
raswonders wants to merge 4 commits into
patternfly:mainfrom
raswonders:feat/column-management-modal-generic-columns

Conversation

@raswonders
Copy link
Copy Markdown
Contributor

@raswonders raswonders commented May 7, 2026

Hey,

I've extended Modal so it supports subtype of ColumnManagementModalColumn, once this is applied we'll be able to pass in our custom Column type which extends ColumnManagementModalColumn and get the same custom columns back.

What this PR do?

ColumnManagementModal now fully works with custom shape columns which are subtype of ColumnManagementModalColumn

Why we need this?

  • Modal can be passed custom column objects, but it only returns column objects with rigid structure and strips any extra properties.
  • This is introducing complexity downstream, as we basically have to maintain two column structures, one for modal and one of our own and transform between them.

How we are fixing it?

  • ColumnManagementModal becomes generic over a column type that extends ColumnManagementModalColumn.
  • Modal passes back the same column objects we passed in, with only isShown updated, instead of rebuilding rows from ListManagerItem (which only recreated a subset of fields passed in).

API compatibilty concerns

  • Change is backward compatible
  • We don't do any changes in prop names nor runtime defaults
  • Type level: There is just extra capability and un-parameterized usage still defaults to ColumnManagementModalColumn
  • applyColumns now receives full column objects. Previously, save dropped extra properties. That would be incompatible only if something relied on those extras being removed at save time for typical usage this is a bugfix and improves compatibility

@patternfly-build
Copy link
Copy Markdown

patternfly-build commented May 7, 2026

Copy link
Copy Markdown
Collaborator

@karelhala karelhala left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work overall — clean generic approach, backward compatible, and well-tested. A couple of suggestions below to strengthen the test coverage and make the feature more discoverable.

saved.forEach((col) => {
const source = EXTENDED_COLUMNS.find((aec) => aec.key === col.key);
expect(source?.dataKey).toBe(col.dataKey);
});
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Consider replacing the forEach + individual expect with a single toEqual assertion. This is more robust — it would also catch unexpected property mutations or additions on the base fields, not just verify that dataKey survived:

expect(saved).toEqual(
  EXTENDED_COLUMNS.map((col) => ({
    ...col,
    isShown: col.key === 'impact' ? !col.isShownByDefault : col.isShownByDefault
  }))
);

This covers the full shape of every column in one shot.

expect(source?.dataKey).toBe(col.dataKey);
});
});

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Would be good to also add a test for enableDragDrop={true} combined with extended columns — verifying that reordering + save still preserves the extra fields. The existing drag-drop tests only use DEFAULT_COLUMNS (base type), so the combination is currently untested.

Something like:

it('should preserve extended column fields after drag-drop reorder and save', () => {
  const applyColumnsMock = jest.fn();
  const reorderedExtended: ExtendedColumn[] = [
    EXTENDED_COLUMNS[3],
    EXTENDED_COLUMNS[0],
    EXTENDED_COLUMNS[2],
    EXTENDED_COLUMNS[1]
  ];
  render(
    <ColumnManagementModal<ExtendedColumn>
      appliedColumns={reorderedExtended}
      applyColumns={applyColumnsMock}
      enableDragDrop
      isOpen
      onClose={onClose}
    />
  );
  // reset + save triggers applyColumns
  const resetButtons = screen.getAllByText('Reset to default');
  fireEvent.click(resetButtons[resetButtons.length - 1]);
  fireEvent.click(screen.getAllByText('Save').pop()!);

  const saved = applyColumnsMock.mock.calls[0][0] as ExtendedColumn[];
  saved.forEach((col) => {
    expect(col.dataKey).toBeDefined();
  });
});

@karelhala
Copy link
Copy Markdown
Collaborator

Documentation suggestion: This generic column support is a great feature for consumers — it would be worth adding a documentation example showing the generic usage pattern. Something like:

interface MyColumn extends ColumnManagementModalColumn {
  dataKey: string;
  sortable?: boolean;
}

const columns: MyColumn[] = [
  { key: 'name', title: 'Name', isShownByDefault: true, dataKey: 'user.name', sortable: true },
  { key: 'email', title: 'Email', isShownByDefault: true, dataKey: 'user.email' },
  // ...
];

<ColumnManagementModal<MyColumn>
  appliedColumns={columns}
  applyColumns={(updated) => {
    // `updated` is MyColumn[] — dataKey, sortable, etc. are preserved
    setColumns(updated);
  }}
  isOpen={isOpen}
  onClose={handleClose}
/>

Without a docs example, consumers might not discover that the component now supports generics and continue maintaining two parallel column structures (which is the exact pain point this PR fixes). Would you be open to adding this to the component's example/demo page?

Copy link
Copy Markdown
Contributor

@nicolethoen nicolethoen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 Karel's notes
LGTM otherwise

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants