diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/ColumnBulkOperations/ColumnGrid/ColumnGrid.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/ColumnBulkOperations/ColumnGrid/ColumnGrid.component.tsx index f4f7e541dcdf..838823b5fe9d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/ColumnBulkOperations/ColumnGrid/ColumnGrid.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/ColumnBulkOperations/ColumnGrid/ColumnGrid.component.tsx @@ -2140,14 +2140,24 @@ const ColumnGrid: React.FC = ({ isRecentlyUpdated={recentlyUpdatedRowIds.has(entity.id)} isSelected={columnGridListing.isSelected(entity.id)} key={entity.id} - renderColumnNameCell={renderColumnNameCellFinal} - renderDescriptionCell={renderDescriptionCellAdapter} - renderGlossaryTermsCell={renderGlossaryTermsCellAdapter} - renderPathCell={renderPathCellAdapter} - renderTagsCell={renderTagsCellAdapter} showParentChildColors={isChildRow || isParentExpanded} - tableColumns={tableColumns} - /> + tableColumns={tableColumns}> + + {renderColumnNameCellFinal(entity)} + + + {renderPathCellAdapter(entity)} + + + {renderDescriptionCellAdapter(entity)} + + + {renderTagsCellAdapter(entity)} + + + {renderGlossaryTermsCellAdapter(entity)} + + ); }} diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/ColumnBulkOperations/ColumnGrid/components/ColumnGridTableRow.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/ColumnBulkOperations/ColumnGrid/components/ColumnGridTableRow.test.tsx index 187ad3407cc8..1db4df486707 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/ColumnBulkOperations/ColumnGrid/components/ColumnGridTableRow.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/ColumnBulkOperations/ColumnGrid/components/ColumnGridTableRow.test.tsx @@ -63,39 +63,43 @@ const mockTableColumns = [ { id: 'glossaryTerms' }, ]; -const renderProps = { - renderColumnNameCell: jest.fn(() => name), - renderPathCell: jest.fn(() => path), - renderDescriptionCell: jest.fn(() => desc), - renderTagsCell: jest.fn(() => tags), - renderGlossaryTermsCell: jest.fn(() => glossary), -}; +const cellSlots = [ + + name + , + + path + , + + desc + , + + tags + , + + glossary + , +]; describe('ColumnGridTableRow', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('renders the row and delegates each cell to its render adapter', () => { + it('places each supplied cell slot in its matching column', () => { render( + tableColumns={mockTableColumns}> + {cellSlots} + ); expect(screen.getByTestId('column-row-test_col')).toBeInTheDocument(); expect(screen.getByTestId('column-name-cell')).toBeInTheDocument(); expect(screen.getByTestId('column-description-cell')).toBeInTheDocument(); - expect(renderProps.renderColumnNameCell).toHaveBeenCalledWith(mockEntity); - expect(renderProps.renderDescriptionCell).toHaveBeenCalledWith(mockEntity); - expect(renderProps.renderTagsCell).toHaveBeenCalledWith(mockEntity); - expect(renderProps.renderGlossaryTermsCell).toHaveBeenCalledWith( - mockEntity - ); - // dataType is rendered from the entity directly, not via an adapter. + expect(screen.getByText('name')).toBeInTheDocument(); + expect(screen.getByText('desc')).toBeInTheDocument(); + expect(screen.getByText('tags')).toBeInTheDocument(); + expect(screen.getByText('glossary')).toBeInTheDocument(); + // dataType is rendered from the entity directly, not via a cell slot. expect(screen.getByText('VARCHAR')).toBeInTheDocument(); }); @@ -105,9 +109,11 @@ describe('ColumnGridTableRow', () => { isPendingRefetch entity={mockEntity} isSelected={false} - tableColumns={[{ id: 'columnName' }]} - {...renderProps} - /> + tableColumns={[{ id: 'columnName' }]}> + + name + + ); expect(screen.getByTestId('loader')).toBeInTheDocument(); @@ -118,9 +124,9 @@ describe('ColumnGridTableRow', () => { + tableColumns={mockTableColumns}> + {cellSlots} + ); expect(screen.getByTestId('column-row-test_col')).toHaveAttribute( @@ -133,9 +139,9 @@ describe('ColumnGridTableRow', () => { showParentChildColors entity={{ ...mockEntity, parentId: 'p1' } as ColumnGridRowData} isSelected={false} - tableColumns={mockTableColumns} - {...renderProps} - /> + tableColumns={mockTableColumns}> + {cellSlots} + ); expect(screen.getByTestId('column-row-test_col')).toHaveAttribute( diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/ColumnBulkOperations/ColumnGrid/components/ColumnGridTableRow.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/ColumnBulkOperations/ColumnGrid/components/ColumnGridTableRow.tsx index 89c2afa32242..e5d0b758d496 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/ColumnBulkOperations/ColumnGrid/components/ColumnGridTableRow.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/ColumnBulkOperations/ColumnGrid/components/ColumnGridTableRow.tsx @@ -17,6 +17,19 @@ import React, { useMemo } from 'react'; import Loader from '../../../../components/common/Loader/Loader'; import { ColumnGridRowData } from '../ColumnGrid.interface'; +export interface ColumnGridTableCellProps { + /** Column id this content maps to (must match a `tableColumns` id) */ + columnId: string; + children: React.ReactNode; +} + +/** + * Slot marker for `ColumnGridTableRow`. Never rendered on its own — the row + * reads its `columnId` + `children` to place the content in the matching + * `Table.Cell`. + */ +const ColumnGridTableCell: React.FC = () => null; + interface ColumnGridTableRowProps { /** Width percent per column id for fixed column layout */ columnWidthPercent?: Record; @@ -30,11 +43,8 @@ interface ColumnGridTableRowProps { showParentChildColors?: boolean; /** Column definitions for Table.Row (id only), used for core Table layout */ tableColumns: { id: string }[]; - renderColumnNameCell: (entity: ColumnGridRowData) => React.ReactNode; - renderPathCell: (entity: ColumnGridRowData) => React.ReactNode; - renderDescriptionCell: (entity: ColumnGridRowData) => React.ReactNode; - renderTagsCell: (entity: ColumnGridRowData) => React.ReactNode; - renderGlossaryTermsCell: (entity: ColumnGridRowData) => React.ReactNode; + /** `ColumnGridTableRow.Cell` slots, one per column id whose content is supplied by the parent */ + children: React.ReactNode; } const CELL_ELLIPSIS_CLASS = 'tw:min-w-0 tw:w-full tw:overflow-hidden'; @@ -44,7 +54,7 @@ const BASE_CELL_PADDING_PX = 24; const PARENT_ROW_BG_CLASS = 'tw:bg-gray-100'; const CHILD_ROW_BG_CLASS = 'tw:bg-gray-50'; -export const ColumnGridTableRow: React.FC = ({ +const ColumnGridTableRowBase: React.FC = ({ columnWidthPercent = {}, entity, isSelected, @@ -52,14 +62,24 @@ export const ColumnGridTableRow: React.FC = ({ isRecentlyUpdated, showParentChildColors = false, tableColumns, - renderColumnNameCell, - renderPathCell, - renderDescriptionCell, - renderTagsCell, - renderGlossaryTermsCell, + children, }) => { const isChildRow = Boolean(entity.parentId || entity.isStructChild); + const cellContentById = useMemo(() => { + const map: Record = {}; + React.Children.forEach(children, (child) => { + if ( + React.isValidElement(child) && + child.props.columnId + ) { + map[child.props.columnId] = child.props.children; + } + }); + + return map; + }, [children]); + const { rowClassName, cellClassName, rowType } = useMemo(() => { const type = isChildRow ? 'child' : 'parent'; @@ -106,24 +126,12 @@ export const ColumnGridTableRow: React.FC = ({ ]); const renderCellContent = (columnId: string) => { - const content = (() => { - switch (columnId) { - case 'columnName': - return renderColumnNameCell(entity); - case 'path': - return renderPathCell(entity); - case 'description': - return renderDescriptionCell(entity); - case 'dataType': - return entity.dataType || '-'; - case 'tags': - return renderTagsCell(entity); - case 'glossaryTerms': - return renderGlossaryTermsCell(entity); - default: - return null; - } - })(); + // dataType is static row data; every other cell's content is supplied by + // the parent via a ColumnGridTableRow.Cell slot. + const content = + columnId === 'dataType' + ? entity.dataType || '-' + : cellContentById[columnId] ?? null; if (content == null) { return null; @@ -188,3 +196,9 @@ export const ColumnGridTableRow: React.FC = ({ ); }; + +export const ColumnGridTableRow = + ColumnGridTableRowBase as React.FC & { + Cell: typeof ColumnGridTableCell; + }; +ColumnGridTableRow.Cell = ColumnGridTableCell;