diff --git a/openmetadata-ui/src/main/resources/ui/src/components/EntityLineage/CustomEdge.component.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/EntityLineage/CustomEdge.component.test.tsx new file mode 100644 index 00000000000..c86a9bc504f --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/EntityLineage/CustomEdge.component.test.tsx @@ -0,0 +1,82 @@ +/* + * Copyright 2021 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 { findByTestId, queryByTestId, render } from '@testing-library/react'; +import React from 'react'; +import { ArrowHeadType, EdgeProps, Position } from 'react-flow-renderer'; +import { MemoryRouter } from 'react-router-dom'; +import { CustomEdge } from './CustomEdge.component'; + +jest.mock('../../constants/Lineage.constants', () => ({ + foreignObjectSize: 40, +})); + +const mockCustomEdgeProp = { + id: 'id1', + sourceX: 20, + sourceY: 20, + targetX: 20, + targetY: 20, + sourcePosition: Position.Left, + targetPosition: Position.Right, + style: {}, + arrowHeadType: ArrowHeadType.ArrowClosed, + markerEndId: '', + data: { + source: 'node1', + target: 'node2', + onEdgeClick: jest.fn(), + selectedNode: { + id: 'node1', + }, + }, +} as EdgeProps; + +describe('Test CustomEdge Component', () => { + it('Check if CustomeEdge has all child elements', async () => { + const { container } = render(, { + wrapper: MemoryRouter, + }); + + const deleteButton = await findByTestId(container, 'delete-button'); + const edgePathElement = await findByTestId( + container, + 'react-flow-edge-path' + ); + + expect(deleteButton).toBeInTheDocument(); + expect(edgePathElement).toBeInTheDocument(); + }); + + it('Check if CustomeEdge has selectedNode as empty object', async () => { + const { container } = render( + , + { + wrapper: MemoryRouter, + } + ); + + const edgePathElement = await findByTestId( + container, + 'react-flow-edge-path' + ); + + const deleteButton = queryByTestId(container, 'delete-button'); + + expect(deleteButton).not.toBeInTheDocument(); + expect(edgePathElement).toBeInTheDocument(); + }); +}); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/EntityLineage/CustomEdge.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/EntityLineage/CustomEdge.component.tsx index a734cd132f4..99401e4aea1 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/EntityLineage/CustomEdge.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/EntityLineage/CustomEdge.component.tsx @@ -35,7 +35,8 @@ export const CustomEdge = ({ markerEndId, data, }: EdgeProps) => { - const { onEdgeClick, ...rest } = data; + const { onEdgeClick, selectedNode, ...rest } = data; + const edgePath = getBezierPath({ sourceX, sourceY, @@ -57,23 +58,31 @@ export const CustomEdge = ({ - - - + {(rest as CustomEdgeData)?.source?.includes(selectedNode?.id) || + (rest as CustomEdgeData)?.target?.includes(selectedNode?.id) ? ( + + + + ) : null} ); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/EntityLineage/EntityLineage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/EntityLineage/EntityLineage.component.tsx index 356137c8d88..f8b565226c4 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/EntityLineage/EntityLineage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/EntityLineage/EntityLineage.component.tsx @@ -215,6 +215,9 @@ const Entitylineage: FunctionComponent = ({ // eslint-disable-next-line @typescript-eslint/no-use-before-define setElements((es) => es.filter((e) => e.id !== data.id)); + /** + * Get new downstreamEdges + */ const newDownStreamEdges = lineageData.downstreamEdges?.filter( (dn) => !lineageData.downstreamEdges?.find( @@ -222,6 +225,10 @@ const Entitylineage: FunctionComponent = ({ edgeData.fromId === dn.fromEntity && edgeData.toId === dn.toEntity ) ); + + /** + * Get new upstreamEdges + */ const newUpStreamEdges = lineageData.upstreamEdges?.filter( (up) => !lineageData.upstreamEdges?.find( @@ -230,10 +237,20 @@ const Entitylineage: FunctionComponent = ({ ) ); + /** + * Get new nodes that have either downstreamEdge or upstreamEdge + */ + const newNodes = lineageData.nodes?.filter( + (n) => + !isUndefined(newDownStreamEdges?.find((d) => d.toEntity === n.id)) || + !isUndefined(newUpStreamEdges?.find((u) => u.fromEntity === n.id)) + ); + setNewAddedNode({} as FlowElement); setSelectedEntity({} as EntityReference); entityLineageHandler({ ...lineageData, + nodes: newNodes, downstreamEdges: newDownStreamEdges, upstreamEdges: newUpStreamEdges, }); @@ -698,6 +715,9 @@ const Entitylineage: FunctionComponent = ({ if (!isEmpty(selectedNode)) { setExpandNode(undefined); } + setElements((pre) => { + return pre.map((el) => ({ ...el, data: { ...el.data, selectedNode } })); + }); }, [selectedNode]); useEffect(() => {