#21232: fix the lineage export image being cropped (#21765)

* fix the lineage export image being cropped

* fix the nodes children column not being able to trace well

* minor removal as not needed

(cherry picked from commit da25cd1a3968fc9a890f79f9badf3149702b8a95)
This commit is contained in:
Ashish Gupta 2025-06-14 17:09:08 +05:30
parent 9f0ec0fd04
commit abeed00935
3 changed files with 71 additions and 45 deletions

View File

@ -670,10 +670,10 @@ describe('Test EntityLineageUtils utility', () => {
const bounds = getNodesBoundsReactFlow(nodes); const bounds = getNodesBoundsReactFlow(nodes);
expect(bounds).toEqual({ expect(bounds).toEqual({
xMin: 100, xMin: 80, // x - padding
yMin: 200, yMin: 180, // y - padding
xMax: 150, // x + width xMax: 170, // x + width + padding
yMax: 230, // y + height yMax: 250, // y + height + padding
}); });
}); });
@ -705,10 +705,10 @@ describe('Test EntityLineageUtils utility', () => {
const bounds = getNodesBoundsReactFlow(nodes); const bounds = getNodesBoundsReactFlow(nodes);
expect(bounds).toEqual({ expect(bounds).toEqual({
xMin: 0, xMin: -20, // x - padding
yMin: 0, yMin: -20, // y - padding
xMax: 260, // rightmost x (200) + width (60) xMax: 280, // rightmost x (200) + width (60) + padding
yMax: 340, // bottom y (300) + height (40) yMax: 360, // bottom y (300) + height (40) + padding
}); });
}); });
@ -729,10 +729,10 @@ describe('Test EntityLineageUtils utility', () => {
const bounds = getNodesBoundsReactFlow(nodes); const bounds = getNodesBoundsReactFlow(nodes);
expect(bounds).toEqual({ expect(bounds).toEqual({
xMin: 100, xMin: 80,
yMin: 200, yMin: 180,
xMax: 200, xMax: 220,
yMax: 300, yMax: 320,
}); });
}); });
@ -770,10 +770,10 @@ describe('Test EntityLineageUtils utility', () => {
const bounds = getNodesBoundsReactFlow(nodes); const bounds = getNodesBoundsReactFlow(nodes);
expect(bounds).toEqual({ expect(bounds).toEqual({
xMin: -100, xMin: -120, // x - padding
yMin: -200, yMin: -220, // y - padding
xMax: 140, // rightmost x (100) + width (40) xMax: 160, // rightmost x (100) + width (40) + padding
yMax: 220, // bottom y (200) + height (20) yMax: 240, // bottom y (200) + height (20) + padding
}); });
}); });
}); });
@ -796,9 +796,9 @@ describe('Test EntityLineageUtils utility', () => {
); );
expect(viewport).toEqual({ expect(viewport).toEqual({
x: 0, x: 20,
y: 0, y: 20,
zoom: 2, zoom: 1.1428571428571428,
}); });
}); });
@ -821,9 +821,9 @@ describe('Test EntityLineageUtils utility', () => {
); );
expect(viewport).toEqual({ expect(viewport).toEqual({
x: 100, x: 110,
y: 100, y: 97.5,
zoom: 1, zoom: 0.75,
}); });
}); });
@ -844,9 +844,9 @@ describe('Test EntityLineageUtils utility', () => {
); );
expect(viewport).toEqual({ expect(viewport).toEqual({
x: 200, x: 170,
y: 200, y: 170,
zoom: 2, zoom: 1.5,
}); });
}); });
@ -867,9 +867,9 @@ describe('Test EntityLineageUtils utility', () => {
); );
expect(viewport).toEqual({ expect(viewport).toEqual({
x: 0, x: 20,
y: 50, y: 56.36363636363636,
zoom: 0.5, zoom: 0.36363636363636365,
}); });
}); });
@ -890,9 +890,9 @@ describe('Test EntityLineageUtils utility', () => {
); );
expect(viewport).toEqual({ expect(viewport).toEqual({
x: NaN, x: 20,
y: NaN, y: 20,
zoom: Infinity, zoom: 4,
}); });
}); });
@ -915,9 +915,9 @@ describe('Test EntityLineageUtils utility', () => {
); );
expect(viewport).toEqual({ expect(viewport).toEqual({
x: -100, x: -60,
y: -100, y: -60,
zoom: 4, zoom: 2.2857142857142856,
}); });
}); });
}); });

View File

@ -278,12 +278,15 @@ export const getELKLayoutedElements = async (
return { return {
...node, ...node,
position: { x: layoutedNode?.x ?? 0, y: layoutedNode?.y ?? 0 }, position: { x: layoutedNode?.x ?? 0, y: layoutedNode?.y ?? 0 },
// layoutedNode contains the total height of the node including the children height
// Needed to calculate the bounds height of the nodes in the export
height: layoutedNode?.height ?? node.height,
hidden: false, hidden: false,
}; };
}); });
return { nodes: updatedNodes, edges: edges ?? [] }; return { nodes: updatedNodes, edges: edges ?? [] };
} catch (error) { } catch {
return { nodes: [], edges: [] }; return { nodes: [], edges: [] };
} }
}; };
@ -1725,10 +1728,16 @@ export const getNodesBoundsReactFlow = (nodes: Node[]) => {
nodes.forEach((node) => { nodes.forEach((node) => {
const { x, y } = node.position; const { x, y } = node.position;
bounds.xMin = Math.min(bounds.xMin, x); const width = node.width ?? 0;
bounds.yMin = Math.min(bounds.yMin, y); const height = node.height ?? 0;
bounds.xMax = Math.max(bounds.xMax, x + (node.width ?? 0));
bounds.yMax = Math.max(bounds.yMax, y + (node.height ?? 0)); // Add padding to ensure nodes are fully visible
const padding = 20;
bounds.xMin = Math.min(bounds.xMin, x - padding);
bounds.yMin = Math.min(bounds.yMin, y - padding);
bounds.xMax = Math.max(bounds.xMax, x + width + padding);
bounds.yMax = Math.max(bounds.yMax, y + height + padding);
}); });
return bounds; return bounds;
@ -1744,13 +1753,23 @@ export const getViewportForBoundsReactFlow = (
const width = bounds.xMax - bounds.xMin; const width = bounds.xMax - bounds.xMin;
const height = bounds.yMax - bounds.yMin; const height = bounds.yMax - bounds.yMin;
// Scale the image to fit the container // Add extra padding to ensure content is fully visible
const padding = 20;
const paddedWidth = width + padding * 2;
const paddedHeight = height + padding * 2;
// Scale the image to fit the container while maintaining aspect ratio
const scale = const scale =
Math.min(imageWidth / width, imageHeight / height) * scaleFactor; Math.min(
(imageWidth - padding * 2) / paddedWidth,
(imageHeight - padding * 2) / paddedHeight
) * scaleFactor;
// Calculate translation to center the flow // Calculate translation to center the flow
const translateX = (imageWidth - width * scale) / 2 - bounds.xMin * scale; const translateX =
const translateY = (imageHeight - height * scale) / 2 - bounds.yMin * scale; (imageWidth - paddedWidth * scale) / 2 - bounds.xMin * scale;
const translateY =
(imageHeight - paddedHeight * scale) / 2 - bounds.yMin * scale;
return { x: translateX, y: translateY, zoom: scale }; return { x: translateX, y: translateY, zoom: scale };
}; };
@ -1766,8 +1785,13 @@ export const getViewportForLineageExport = (
const nodesBounds = getNodesBoundsReactFlow(nodes); const nodesBounds = getNodesBoundsReactFlow(nodes);
// Calculate the viewport to fit all nodes // Calculate the viewport to fit all nodes with padding
return getViewportForBoundsReactFlow(nodesBounds, imageWidth, imageHeight); return getViewportForBoundsReactFlow(
nodesBounds,
imageWidth,
imageHeight,
0.9
); // Scale down slightly to ensure padding
}; };
export const getLineageEntityExclusionFilter = () => { export const getLineageEntityExclusionFilter = () => {

View File

@ -54,6 +54,8 @@ export const exportPNGImageFromElement = async (exportData: ExportData) => {
backgroundColor: '#ffffff', backgroundColor: '#ffffff',
width: imageWidth + padding * 2, width: imageWidth + padding * 2,
height: imageHeight + padding * 2, height: imageHeight + padding * 2,
pixelRatio: 3,
quality: 1.0,
style: { style: {
width: imageWidth.toString(), width: imageWidth.toString(),
height: imageHeight.toString(), height: imageHeight.toString(),