fix lineage child node column name not taking remaining space in card and showMoreButton (#21473)

* fix lineage child node column name not taking remaning space in card

* fix the showColumn button which was apperaring even if there were no columns
This commit is contained in:
Ashish Gupta 2025-06-02 20:49:09 +05:30 committed by GitHub
parent 4c96b85a47
commit bb54555daa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 196 additions and 41 deletions

View File

@ -137,8 +137,7 @@ export const getColumnContent = (
<div
className={classNames(
'custom-node-column-container',
isColumnTraced && 'custom-node-header-tracing',
showDataObservabilitySummary && 'p-r-md'
isColumnTraced && 'custom-node-header-tracing'
)}
data-testid={`column-${fullyQualifiedName}`}
key={fullyQualifiedName}
@ -152,16 +151,16 @@ export const getColumnContent = (
'lineage-column-node-handle',
encodeLineageHandles(fullyQualifiedName ?? '')
)}
<Row gutter={24}>
<Col
className="custom-node-name-container"
span={showDataObservabilitySummary ? 8 : 12}>
<div className="custom-node-name-icon">
{getColumnDataTypeIcon({
dataType: column.dataType,
width: '14px',
})}
</div>
<Row className="d-flex items-center" gutter={12}>
<Col className="custom-node-name-container" flex="1">
{column.dataType && (
<div className="custom-node-name-icon">
{getColumnDataTypeIcon({
dataType: column.dataType,
width: '14px',
})}
</div>
)}
<Typography.Text
className="custom-node-column-label"
ellipsis={{ tooltip: true }}>
@ -169,16 +168,18 @@ export const getColumnContent = (
</Typography.Text>
</Col>
<Col
className={classNames(
'custom-node-constraint',
showDataObservabilitySummary ? 'text-left' : 'text-right'
)}
span={showDataObservabilitySummary ? 8 : 12}>
{column.constraint}
</Col>
{column.constraint && (
<Col
className={classNames(
'custom-node-constraint',
showDataObservabilitySummary ? 'text-left' : 'text-right'
)}
flex="80px">
{column.constraint}
</Col>
)}
{showDataObservabilitySummary && (
<Col span={8}>
<Col flex="80px">
<TestSuiteSummaryWidget
isLoading={isLoading}
size="small"

View File

@ -0,0 +1,139 @@
/*
* Copyright 2025 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { act, fireEvent, render, screen } from '@testing-library/react';
import React from 'react';
import { useLineageProvider } from '../../../../context/LineageProvider/LineageProvider';
import { EntityType } from '../../../../enums/entity.enum';
import { LineageLayer } from '../../../../generated/settings/settings';
import NodeChildren from './NodeChildren.component';
const mockNode = {
id: 'test-id',
entityType: EntityType.TABLE,
fullyQualifiedName: 'test.fqn',
name: 'test',
columns: [
{
name: 'column1',
fullyQualifiedName: 'test.fqn.column1',
dataType: 'STRING',
},
{
name: 'column2',
fullyQualifiedName: 'test.fqn.column2',
dataType: 'STRING',
},
],
};
const mockLineageProvider = {
tracedColumns: [],
activeLayer: [LineageLayer.ColumnLevelLineage],
onColumnClick: jest.fn(),
columnsHavingLineage: ['test.fqn.column1'],
isEditMode: false,
expandAllColumns: false,
};
jest.mock('../../../../context/LineageProvider/LineageProvider', () => ({
useLineageProvider: jest.fn().mockImplementation(() => mockLineageProvider),
}));
jest.mock('../../../../rest/testAPI', () => ({
getTestCaseExecutionSummary: jest.fn(),
}));
jest.mock('../../../../utils/EntityLink', () => ({
EntityLink: jest.fn(),
}));
jest.mock('../../../../utils/SearchClassBase', () => ({
getEntityIcon: jest.fn().mockReturnValue('entityIcon'),
}));
jest.mock('../CustomNode.utils', () => ({
getColumnContent: jest
.fn()
.mockImplementation((column) => <p>{column.name}</p>),
}));
jest.mock('../TestSuiteSummaryWidget/TestSuiteSummaryWidget.component', () =>
jest.fn().mockReturnValue(<p>TestSuiteSummaryWidget</p>)
);
describe('NodeChildren Component', () => {
it('should show show more button when there are columns without lineage', () => {
render(<NodeChildren isConnectable={false} node={mockNode} />);
const showMoreButton = screen.getByTestId('show-more-columns-btn');
expect(showMoreButton).toBeInTheDocument();
});
it('should hide show more button when all columns are shown after clicking show more button', () => {
render(<NodeChildren isConnectable={false} node={mockNode} />);
const showMoreButton = screen.getByTestId('show-more-columns-btn');
fireEvent.click(showMoreButton);
expect(
screen.queryByTestId('show-more-columns-btn')
).not.toBeInTheDocument();
});
it('should hide show more button when all columns are shown', () => {
(useLineageProvider as jest.Mock).mockImplementation(() => ({
...mockLineageProvider,
columnsHavingLineage: ['test.fqn.column1', 'test.fqn.column2'],
}));
render(<NodeChildren isConnectable={false} node={mockNode} />);
expect(
screen.queryByTestId('show-more-columns-btn')
).not.toBeInTheDocument();
});
it('should show all columns when searching', () => {
render(<NodeChildren isConnectable={false} node={mockNode} />);
const searchInput = screen.getByPlaceholderText('label.search-entity');
act(() => {
fireEvent.change(searchInput, { target: { value: 'column' } });
});
expect(screen.getByText('column1')).toBeInTheDocument();
expect(screen.getByText('column2')).toBeInTheDocument();
});
it('should hide show more button when searching', () => {
render(<NodeChildren isConnectable={false} node={mockNode} />);
const searchInput = screen.getByPlaceholderText('label.search-entity');
fireEvent.change(searchInput, { target: { value: 'column' } });
expect(
screen.queryByTestId('show-more-columns-btn')
).not.toBeInTheDocument();
});
it('should filter columns based on search input', () => {
render(<NodeChildren isConnectable={false} node={mockNode} />);
const searchInput = screen.getByPlaceholderText('label.search-entity');
fireEvent.change(searchInput, { target: { value: 'column1' } });
expect(screen.getByText('column1')).toBeInTheDocument();
expect(screen.queryByText('column2')).not.toBeInTheDocument();
});
});

View File

@ -115,6 +115,7 @@ const NodeChildren = ({ node, isConnectable }: NodeChildrenProps) => {
getEntityName(column).toLowerCase().includes(value.toLowerCase())
);
setFilteredColumns(filtered);
setShowAllColumns(true);
}
},
[children]
@ -151,7 +152,7 @@ const NodeChildren = ({ node, isConnectable }: NodeChildrenProps) => {
try {
const response = await getTestCaseExecutionSummary(testSuite.id);
setSummary(response);
} catch (error) {
} catch {
setSummary(undefined);
} finally {
setIsLoading(false);
@ -286,7 +287,9 @@ const NodeChildren = ({ node, isConnectable }: NodeChildrenProps) => {
// Pre-render column data outside of the return statement
const renderedColumns = useMemo(() => {
return filteredColumns.map((column) => renderColumnsData(column as Column));
return filteredColumns
.map((column) => renderColumnsData(column as Column))
.filter(Boolean);
}, [filteredColumns, renderColumnsData]);
// Memoize the expand/collapse icon to prevent unnecessary re-renders
@ -303,6 +306,15 @@ const NodeChildren = ({ node, isConnectable }: NodeChildrenProps) => {
return searchClassBase.getEntityIcon(node.entityType ?? '');
}, [node.entityType]);
const shouldShowMoreButton = useMemo(() => {
return (
!showAllColumns &&
!isEmpty(children) &&
renderedColumns.length !== children.length &&
!searchValue
);
}, [showAllColumns, children, renderedColumns, searchValue]);
// Memoize the expand/collapse click handler
const handleExpandCollapseClick = useCallback((e: React.MouseEvent) => {
e.stopPropagation();
@ -346,18 +358,21 @@ const NodeChildren = ({ node, isConnectable }: NodeChildrenProps) => {
/>
</div>
<section className="m-t-md" id="table-columns">
<div
className={classNames('rounded-4 overflow-hidden', {
border: !showAllColumns,
})}>
{renderedColumns}
</div>
</section>
{!isEmpty(renderedColumns) && (
<section className="m-t-md" id="table-columns">
<div
className={classNames('rounded-4 overflow-hidden', {
border: !showAllColumns,
})}>
{renderedColumns}
</div>
</section>
)}
{!showAllColumns && (
{shouldShowMoreButton && (
<Button
className="m-t-xs text-primary"
data-testid="show-more-columns-btn"
type="text"
onClick={handleShowMoreClick}>
{t('label.show-more-entity', {

View File

@ -78,6 +78,7 @@
.column-container {
min-height: 48px;
max-width: 320px;
padding: 12px;
background-color: @grey-1;
@ -284,16 +285,17 @@
}
.custom-node-column-container {
padding: 6px;
padding: 6px 8px;
background: @white;
position: relative;
font-size: 12px;
font-weight: 400;
.custom-node-name-container {
min-width: 0;
display: flex;
align-items: center;
padding-left: 12px;
text-align: left;
gap: 4px;
.custom-node-name-icon {
@ -302,7 +304,8 @@
}
.custom-node-column-label {
max-width: 140px;
width: 100%;
display: block;
}
}

View File

@ -92,12 +92,6 @@
.w-11-12 {
width: 91.666667%;
}
.w-min-0 {
min-width: 0;
}
.w-max-full {
max-width: 100%;
}
.w-max-20 {
max-width: 20%;
}
@ -191,6 +185,9 @@
width: 100vw;
}
.w-min-0 {
min-width: 0;
}
.w-min-10 {
min-width: 10rem;
}