Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@
<Typography
as="label"
className="tw:text-sm tw:font-semibold tw:text-secondary">
{t('label.description')}

Check warning on line 421 in openmetadata-ui/src/main/resources/ui/src/pages/ColumnBulkOperations/ColumnGrid/ColumnGrid.component.tsx

View workflow job for this annotation

GitHub Actions / checkstyle

Define a constant instead of duplicating this literal 3 times
</Typography>
<RichTextEditor
initialValue={currentDescription}
Expand All @@ -442,7 +442,7 @@
{t('label.tag-plural')}
</Typography>
<AsyncSelectList
autoFocus={false}

Check warning on line 445 in openmetadata-ui/src/main/resources/ui/src/pages/ColumnBulkOperations/ColumnGrid/ColumnGrid.component.tsx

View workflow job for this annotation

GitHub Actions / checkstyle

The autoFocus prop should not be used, as it can reduce usability and accessibility for users
fetchOptions={tagClassBase.getTags}
getPopupContainer={(triggerNode) => triggerNode.parentElement}
initialOptions={classificationTagOptions}
Expand Down Expand Up @@ -556,7 +556,7 @@

const ColumnGrid: React.FC<ColumnGridProps> = ({
filters: externalFilters,
}) => {

Check warning on line 559 in openmetadata-ui/src/main/resources/ui/src/pages/ColumnBulkOperations/ColumnGrid/ColumnGrid.component.tsx

View workflow job for this annotation

GitHub Actions / checkstyle

{"message":"Function has a complexity of 11 which is greater than 10 authorized.","cost":1,"secondaryLocations":[{"line":559,"column":3,"endLine":559,"endColumn":5,"message":"+1"},{"line":1816,"column":49,"endLine":1816,"endColumn":51,"message":"+1"},{"line":1830,"column":43,"endLine":1830,"endColumn":45,"message":"+1"},{"line":1963,"column":40,"endLine":1963,"endColumn":42,"message":"+1"},{"line":1970,"column":51,"endLine":1970,"endColumn":53,"message":"+1"},{"line":2381,"column":31,"endLine":2381,"endColumn":33,"message":"+1"},{"line":2443,"column":48,"endLine":2443,"endColumn":50,"message":"+1"},{"line":2518,"column":45,"endLine":2518,"endColumn":47,"message":"+1"},{"line":2533,"column":30,"endLine":2533,"endColumn":32,"message":"+1"},{"line":2556,"column":32,"endLine":2556,"endColumn":34,"message":"+1"},{"line":2572,"column":32,"endLine":2572,"endColumn":33,"message":"+1"}]}
const { t } = useTranslation();
const { socket } = useWebSocketConnector();
const [isUpdating, setIsUpdating] = useState(false);
Expand Down Expand Up @@ -751,7 +751,7 @@
items: ColumnGridItem[],
expandedRows: Set<string>,
expandedStructRows: Set<string>
): ColumnGridRowData[] => {

Check warning on line 754 in openmetadata-ui/src/main/resources/ui/src/pages/ColumnBulkOperations/ColumnGrid/ColumnGrid.component.tsx

View workflow job for this annotation

GitHub Actions / checkstyle

{"message":"Function has a complexity of 24 which is greater than 10 authorized.","cost":14,"secondaryLocations":[{"line":754,"column":27,"endLine":754,"endColumn":29,"message":"+1"},{"line":757,"column":6,"endLine":757,"endColumn":9,"message":"+1"},{"line":765,"column":8,"endLine":765,"endColumn":10,"message":"+1"},{"line":765,"column":31,"endLine":765,"endColumn":33,"message":"+1"},{"line":787,"column":10,"endLine":787,"endColumn":12,"message":"+1"},{"line":788,"column":12,"endLine":788,"endColumn":15,"message":"+1"},{"line":790,"column":31,"endLine":790,"endColumn":33,"message":"+1"},{"line":791,"column":14,"endLine":791,"endColumn":17,"message":"+1"},{"line":822,"column":16,"endLine":822,"endColumn":18,"message":"+1"},{"line":822,"column":58,"endLine":822,"endColumn":60,"message":"+1"},{"line":822,"column":37,"endLine":822,"endColumn":39,"message":"+1"},{"line":835,"column":15,"endLine":835,"endColumn":17,"message":"+1"},{"line":840,"column":28,"endLine":840,"endColumn":30,"message":"+1"},{"line":849,"column":44,"endLine":849,"endColumn":45,"message":"+1"},{"line":867,"column":10,"endLine":867,"endColumn":12,"message":"+1"},{"line":867,"column":52,"endLine":867,"endColumn":54,"message":"+1"},{"line":867,"column":31,"endLine":867,"endColumn":33,"message":"+1"},{"line":878,"column":10,"endLine":878,"endColumn":12,"message":"+1"},{"line":878,"column":25,"endLine":878,"endColumn":27,"message":"+1"},{"line":879,"column":12,"endLine":879,"endColumn":15,"message":"+1"},{"line":910,"column":28,"endLine":910,"endColumn":30,"message":"+1"},{"line":938,"column":10,"endLine":938,"endColumn":12,"message":"+1"},{"line":938,"column":52,"endLine":938,"endColumn":54,"message":"+1"},{"line":938,"column":31,"endLine":938,"endColumn":33,"message":"+1"}]}

Check warning on line 754 in openmetadata-ui/src/main/resources/ui/src/pages/ColumnBulkOperations/ColumnGrid/ColumnGrid.component.tsx

View workflow job for this annotation

GitHub Actions / checkstyle

Refactor this function to reduce its Cognitive Complexity from 47 to the 15 allowed
const rows: ColumnGridRowData[] = [];

for (const item of items) {
Expand Down Expand Up @@ -950,7 +950,7 @@

return rows;
},
[]

Check warning on line 953 in openmetadata-ui/src/main/resources/ui/src/pages/ColumnBulkOperations/ColumnGrid/ColumnGrid.component.tsx

View workflow job for this annotation

GitHub Actions / checkstyle

React Hook useCallback has missing dependencies: 'createStructChildRows' and 'getUniquePaths'. Either include them or remove the dependency array
);

// Define columns inline (similar to DomainListPage pattern)
Expand All @@ -971,7 +971,7 @@
);

// Get column link function - defined before use
const getColumnLink = useCallback((row: ColumnGridRowData) => {

Check warning on line 974 in openmetadata-ui/src/main/resources/ui/src/pages/ColumnBulkOperations/ColumnGrid/ColumnGrid.component.tsx

View workflow job for this annotation

GitHub Actions / checkstyle

{"message":"Function has a complexity of 13 which is greater than 10 authorized.","cost":3,"secondaryLocations":[{"line":974,"column":61,"endLine":974,"endColumn":63,"message":"+1"},{"line":977,"column":4,"endLine":977,"endColumn":6,"message":"+1"},{"line":979,"column":11,"endLine":979,"endColumn":13,"message":"+1"},{"line":985,"column":11,"endLine":985,"endColumn":13,"message":"+1"},{"line":985,"column":38,"endLine":985,"endColumn":40,"message":"+1"},{"line":987,"column":11,"endLine":987,"endColumn":13,"message":"+1"},{"line":989,"column":37,"endLine":989,"endColumn":39,"message":"+1"},{"line":988,"column":27,"endLine":988,"endColumn":29,"message":"+1"},{"line":995,"column":4,"endLine":995,"endColumn":6,"message":"+1"},{"line":995,"column":20,"endLine":995,"endColumn":22,"message":"+1"},{"line":1006,"column":61,"endLine":1006,"endColumn":63,"message":"+1"},{"line":1011,"column":15,"endLine":1011,"endColumn":17,"message":"+1"},{"line":1017,"column":6,"endLine":1017,"endColumn":7,"message":"+1"}]}
let occurrence: ColumnOccurrenceRef | null = null;

if (row.occurrence) {
Expand Down Expand Up @@ -1064,7 +1064,7 @@
);

const renderDescriptionCellAdapter = useCallback(
(entity: ColumnGridRowData) => {

Check warning on line 1067 in openmetadata-ui/src/main/resources/ui/src/pages/ColumnBulkOperations/ColumnGrid/ColumnGrid.component.tsx

View workflow job for this annotation

GitHub Actions / checkstyle

{"message":"Function has a complexity of 11 which is greater than 10 authorized.","cost":1,"secondaryLocations":[{"line":1067,"column":32,"endLine":1067,"endColumn":34,"message":"+1"},{"line":1070,"column":8,"endLine":1070,"endColumn":9,"message":"+1"},{"line":1070,"column":42,"endLine":1070,"endColumn":44,"message":"+1"},{"line":1071,"column":36,"endLine":1071,"endColumn":38,"message":"+1"},{"line":1073,"column":6,"endLine":1073,"endColumn":8,"message":"+1"},{"line":1073,"column":29,"endLine":1073,"endColumn":31,"message":"+1"},{"line":1076,"column":12,"endLine":1076,"endColumn":13,"message":"+1"},{"line":1075,"column":45,"endLine":1075,"endColumn":47,"message":"+1"},{"line":1092,"column":6,"endLine":1092,"endColumn":8,"message":"+1"},{"line":1100,"column":28,"endLine":1100,"endColumn":30,"message":"+1"},{"line":1110,"column":24,"endLine":1110,"endColumn":26,"message":"+1"}]}
const hasEdit = entity.editedDescription !== undefined;
const displayValue = hasEdit
? entity.editedDescriptionPreview ?? ''
Expand Down Expand Up @@ -1111,7 +1111,7 @@
</Typography>
);
},
[]

Check warning on line 1114 in openmetadata-ui/src/main/resources/ui/src/pages/ColumnBulkOperations/ColumnGrid/ColumnGrid.component.tsx

View workflow job for this annotation

GitHub Actions / checkstyle

React Hook useCallback has a missing dependency: 't'. Either include it or remove the dependency array
);

const renderTagsCellAdapter = useCallback((entity: ColumnGridRowData) => {
Expand Down Expand Up @@ -1220,7 +1220,7 @@

// Update render functions to use listing data state (with correct CellRenderer signature)
const renderColumnNameCellFinal = useCallback(
(entity: ColumnGridRowData) => {

Check warning on line 1223 in openmetadata-ui/src/main/resources/ui/src/pages/ColumnBulkOperations/ColumnGrid/ColumnGrid.component.tsx

View workflow job for this annotation

GitHub Actions / checkstyle

{"message":"Function has a complexity of 15 which is greater than 10 authorized.","cost":5,"secondaryLocations":[{"line":1223,"column":32,"endLine":1223,"endColumn":34,"message":"+1"},{"line":1229,"column":6,"endLine":1229,"endColumn":8,"message":"+1"},{"line":1229,"column":25,"endLine":1229,"endColumn":27,"message":"+1"},{"line":1263,"column":38,"endLine":1263,"endColumn":40,"message":"+1"},{"line":1284,"column":6,"endLine":1284,"endColumn":8,"message":"+1"},{"line":1285,"column":44,"endLine":1285,"endColumn":46,"message":"+1"},{"line":1286,"column":52,"endLine":1286,"endColumn":54,"message":"+1"},{"line":1289,"column":12,"endLine":1289,"endColumn":13,"message":"+1"},{"line":1321,"column":27,"endLine":1321,"endColumn":28,"message":"+1"},{"line":1328,"column":41,"endLine":1328,"endColumn":43,"message":"+1"},{"line":1350,"column":48,"endLine":1350,"endColumn":50,"message":"+1"},{"line":1351,"column":50,"endLine":1351,"endColumn":52,"message":"+1"},{"line":1354,"column":10,"endLine":1354,"endColumn":11,"message":"+1"},{"line":1384,"column":31,"endLine":1384,"endColumn":32,"message":"+1"},{"line":1391,"column":43,"endLine":1391,"endColumn":45,"message":"+1"}]}
const columnNameButtonClass = classNames(
'tw:flex tw:flex-1 tw:min-w-0 tw:items-center tw:justify-start tw:overflow-hidden tw:text-start',
'tw:*:data-text:block tw:*:data-text:min-w-0 tw:*:data-text:w-full tw:*:data-text:truncate'
Expand Down Expand Up @@ -2140,14 +2140,24 @@
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}>
<ColumnGridTableRow.Cell columnId="columnName">
{renderColumnNameCellFinal(entity)}
</ColumnGridTableRow.Cell>
<ColumnGridTableRow.Cell columnId="path">
{renderPathCellAdapter(entity)}
</ColumnGridTableRow.Cell>
<ColumnGridTableRow.Cell columnId="description">
{renderDescriptionCellAdapter(entity)}
</ColumnGridTableRow.Cell>
<ColumnGridTableRow.Cell columnId="tags">
{renderTagsCellAdapter(entity)}
</ColumnGridTableRow.Cell>
<ColumnGridTableRow.Cell columnId="glossaryTerms">
{renderGlossaryTermsCellAdapter(entity)}
</ColumnGridTableRow.Cell>
</ColumnGridTableRow>
);
}}
</Table.Body>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,39 +63,43 @@ const mockTableColumns = [
{ id: 'glossaryTerms' },
];

const renderProps = {
renderColumnNameCell: jest.fn(() => <span>name</span>),
renderPathCell: jest.fn(() => <span>path</span>),
renderDescriptionCell: jest.fn(() => <span>desc</span>),
renderTagsCell: jest.fn(() => <span>tags</span>),
renderGlossaryTermsCell: jest.fn(() => <span>glossary</span>),
};
const cellSlots = [
<ColumnGridTableRow.Cell columnId="columnName" key="columnName">
<span>name</span>
</ColumnGridTableRow.Cell>,
<ColumnGridTableRow.Cell columnId="path" key="path">
<span>path</span>
</ColumnGridTableRow.Cell>,
<ColumnGridTableRow.Cell columnId="description" key="description">
<span>desc</span>
</ColumnGridTableRow.Cell>,
<ColumnGridTableRow.Cell columnId="tags" key="tags">
<span>tags</span>
</ColumnGridTableRow.Cell>,
<ColumnGridTableRow.Cell columnId="glossaryTerms" key="glossaryTerms">
<span>glossary</span>
</ColumnGridTableRow.Cell>,
];

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(
<ColumnGridTableRow
entity={mockEntity}
isSelected={false}
tableColumns={mockTableColumns}
{...renderProps}
/>
tableColumns={mockTableColumns}>
{cellSlots}
</ColumnGridTableRow>
);

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();
});

Expand All @@ -105,9 +109,11 @@ describe('ColumnGridTableRow', () => {
isPendingRefetch
entity={mockEntity}
isSelected={false}
tableColumns={[{ id: 'columnName' }]}
{...renderProps}
/>
tableColumns={[{ id: 'columnName' }]}>
<ColumnGridTableRow.Cell columnId="columnName">
<span>name</span>
</ColumnGridTableRow.Cell>
</ColumnGridTableRow>
);

expect(screen.getByTestId('loader')).toBeInTheDocument();
Expand All @@ -118,9 +124,9 @@ describe('ColumnGridTableRow', () => {
<ColumnGridTableRow
entity={mockEntity}
isSelected={false}
tableColumns={mockTableColumns}
{...renderProps}
/>
tableColumns={mockTableColumns}>
{cellSlots}
</ColumnGridTableRow>
);

expect(screen.getByTestId('column-row-test_col')).toHaveAttribute(
Expand All @@ -133,9 +139,9 @@ describe('ColumnGridTableRow', () => {
showParentChildColors
entity={{ ...mockEntity, parentId: 'p1' } as ColumnGridRowData}
isSelected={false}
tableColumns={mockTableColumns}
{...renderProps}
/>
tableColumns={mockTableColumns}>
{cellSlots}
</ColumnGridTableRow>
);

expect(screen.getByTestId('column-row-test_col')).toHaveAttribute(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<ColumnGridTableCellProps> = () => null;

interface ColumnGridTableRowProps {
/** Width percent per column id for fixed column layout */
columnWidthPercent?: Record<string, string>;
Expand All @@ -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';
Expand All @@ -44,22 +54,32 @@ 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<ColumnGridTableRowProps> = ({
const ColumnGridTableRowBase: React.FC<ColumnGridTableRowProps> = ({
columnWidthPercent = {},
entity,
isSelected,
isPendingRefetch = false,
isRecentlyUpdated,
showParentChildColors = false,
tableColumns,
renderColumnNameCell,
renderPathCell,
renderDescriptionCell,
renderTagsCell,
renderGlossaryTermsCell,
children,
}) => {
const isChildRow = Boolean(entity.parentId || entity.isStructChild);

const cellContentById = useMemo(() => {
const map: Record<string, React.ReactNode> = {};
React.Children.forEach(children, (child) => {
if (
React.isValidElement<ColumnGridTableCellProps>(child) &&
child.props.columnId
) {
map[child.props.columnId] = child.props.children;
}
});

return map;
}, [children]);

const { rowClassName, cellClassName, rowType } = useMemo(() => {
const type = isChildRow ? 'child' : 'parent';

Expand Down Expand Up @@ -106,24 +126,12 @@ export const ColumnGridTableRow: React.FC<ColumnGridTableRowProps> = ({
]);

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;
Expand Down Expand Up @@ -188,3 +196,9 @@ export const ColumnGridTableRow: React.FC<ColumnGridTableRowProps> = ({
</Table.Row>
);
};

export const ColumnGridTableRow =
ColumnGridTableRowBase as React.FC<ColumnGridTableRowProps> & {
Cell: typeof ColumnGridTableCell;
};
ColumnGridTableRow.Cell = ColumnGridTableCell;
Loading