mirror of
				https://github.com/open-metadata/OpenMetadata.git
				synced 2025-11-03 20:19:31 +00:00 
			
		
		
		
	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 (cherry picked from commit bb54555daaa7684e68d45779dce5498db30d031c)
This commit is contained in:
		
							parent
							
								
									47bf9a4b9b
								
							
						
					
					
						commit
						e28e854e0f
					
				@ -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"
 | 
			
		||||
 | 
			
		||||
@ -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();
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
@ -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', {
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user