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 <div
className={classNames( className={classNames(
'custom-node-column-container', 'custom-node-column-container',
isColumnTraced && 'custom-node-header-tracing', isColumnTraced && 'custom-node-header-tracing'
showDataObservabilitySummary && 'p-r-md'
)} )}
data-testid={`column-${fullyQualifiedName}`} data-testid={`column-${fullyQualifiedName}`}
key={fullyQualifiedName} key={fullyQualifiedName}
@ -152,16 +151,16 @@ export const getColumnContent = (
'lineage-column-node-handle', 'lineage-column-node-handle',
encodeLineageHandles(fullyQualifiedName ?? '') encodeLineageHandles(fullyQualifiedName ?? '')
)} )}
<Row gutter={24}> <Row className="d-flex items-center" gutter={12}>
<Col <Col className="custom-node-name-container" flex="1">
className="custom-node-name-container" {column.dataType && (
span={showDataObservabilitySummary ? 8 : 12}> <div className="custom-node-name-icon">
<div className="custom-node-name-icon"> {getColumnDataTypeIcon({
{getColumnDataTypeIcon({ dataType: column.dataType,
dataType: column.dataType, width: '14px',
width: '14px', })}
})} </div>
</div> )}
<Typography.Text <Typography.Text
className="custom-node-column-label" className="custom-node-column-label"
ellipsis={{ tooltip: true }}> ellipsis={{ tooltip: true }}>
@ -169,16 +168,18 @@ export const getColumnContent = (
</Typography.Text> </Typography.Text>
</Col> </Col>
<Col {column.constraint && (
className={classNames( <Col
'custom-node-constraint', className={classNames(
showDataObservabilitySummary ? 'text-left' : 'text-right' 'custom-node-constraint',
)} showDataObservabilitySummary ? 'text-left' : 'text-right'
span={showDataObservabilitySummary ? 8 : 12}> )}
{column.constraint} flex="80px">
</Col> {column.constraint}
</Col>
)}
{showDataObservabilitySummary && ( {showDataObservabilitySummary && (
<Col span={8}> <Col flex="80px">
<TestSuiteSummaryWidget <TestSuiteSummaryWidget
isLoading={isLoading} isLoading={isLoading}
size="small" 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()) getEntityName(column).toLowerCase().includes(value.toLowerCase())
); );
setFilteredColumns(filtered); setFilteredColumns(filtered);
setShowAllColumns(true);
} }
}, },
[children] [children]
@ -151,7 +152,7 @@ const NodeChildren = ({ node, isConnectable }: NodeChildrenProps) => {
try { try {
const response = await getTestCaseExecutionSummary(testSuite.id); const response = await getTestCaseExecutionSummary(testSuite.id);
setSummary(response); setSummary(response);
} catch (error) { } catch {
setSummary(undefined); setSummary(undefined);
} finally { } finally {
setIsLoading(false); setIsLoading(false);
@ -286,7 +287,9 @@ const NodeChildren = ({ node, isConnectable }: NodeChildrenProps) => {
// Pre-render column data outside of the return statement // Pre-render column data outside of the return statement
const renderedColumns = useMemo(() => { const renderedColumns = useMemo(() => {
return filteredColumns.map((column) => renderColumnsData(column as Column)); return filteredColumns
.map((column) => renderColumnsData(column as Column))
.filter(Boolean);
}, [filteredColumns, renderColumnsData]); }, [filteredColumns, renderColumnsData]);
// Memoize the expand/collapse icon to prevent unnecessary re-renders // Memoize the expand/collapse icon to prevent unnecessary re-renders
@ -303,6 +306,15 @@ const NodeChildren = ({ node, isConnectable }: NodeChildrenProps) => {
return searchClassBase.getEntityIcon(node.entityType ?? ''); return searchClassBase.getEntityIcon(node.entityType ?? '');
}, [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 // Memoize the expand/collapse click handler
const handleExpandCollapseClick = useCallback((e: React.MouseEvent) => { const handleExpandCollapseClick = useCallback((e: React.MouseEvent) => {
e.stopPropagation(); e.stopPropagation();
@ -346,18 +358,21 @@ const NodeChildren = ({ node, isConnectable }: NodeChildrenProps) => {
/> />
</div> </div>
<section className="m-t-md" id="table-columns"> {!isEmpty(renderedColumns) && (
<div <section className="m-t-md" id="table-columns">
className={classNames('rounded-4 overflow-hidden', { <div
border: !showAllColumns, className={classNames('rounded-4 overflow-hidden', {
})}> border: !showAllColumns,
{renderedColumns} })}>
</div> {renderedColumns}
</section> </div>
</section>
)}
{!showAllColumns && ( {shouldShowMoreButton && (
<Button <Button
className="m-t-xs text-primary" className="m-t-xs text-primary"
data-testid="show-more-columns-btn"
type="text" type="text"
onClick={handleShowMoreClick}> onClick={handleShowMoreClick}>
{t('label.show-more-entity', { {t('label.show-more-entity', {

View File

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

View File

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