Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add adjustable padding for nested nodes and add storybook story for custom nested nodes #30

Merged
merged 9 commits into from Dec 15, 2020
3 changes: 2 additions & 1 deletion src/layout/elkLayout.ts
Expand Up @@ -33,6 +33,7 @@ function mapNode(nodes: NodeData[], edges: EdgeData[], node: NodeData) {
height,
labelHeight,
labelWidth,
nodePadding,
originalText
} = formatText(node);

Expand Down Expand Up @@ -61,7 +62,7 @@ function mapNode(nodes: NodeData[], edges: EdgeData[], node: NodeData) {
}))
: [],
layoutOptions: {
'elk.padding': '[left=50, top=50, right=50, bottom=50]',
'elk.padding': `[left=${nodePadding.left}, top=${nodePadding.top}, right=${nodePadding.right}, bottom=${nodePadding.bottom}]`,
portConstraints: 'FIXED_ORDER'
},
properties: {
Expand Down
32 changes: 32 additions & 0 deletions src/layout/utils.test.ts
@@ -0,0 +1,32 @@
import { parsePadding } from './utils';
jest.disableAutomock();

test('should set all sides to input number, when a number is provided', () => {
const expectedPadding = {
top: 10,
right: 10,
bottom: 10,
left: 10
};
expect(parsePadding(10)).toEqual(expectedPadding);
});

test('should set horizontal and vertical padding, when an array with numbers is provided', () => {
const expectedPadding = {
top: 20,
right: 50,
bottom: 20,
left: 50
};
expect(parsePadding([20, 50])).toEqual(expectedPadding);
});

test('should set each padding value individually, when an array with four numbers is provided', () => {
const expectedPadding = {
top: 20,
right: 50,
bottom: 100,
left: 150
};
expect(parsePadding([20, 50, 100, 150])).toEqual(expectedPadding);
});
39 changes: 36 additions & 3 deletions src/layout/utils.ts
Expand Up @@ -21,12 +21,44 @@ export function measureText(text: string) {
return result;
}

export function parsePadding(padding: NodeData['nodePadding']) {
let top = 50;
let right = 50;
let bottom = 50;
let left = 50;

if (Array.isArray(padding)) {
if (padding.length === 2) {
top = padding[0];
bottom = padding[0];
left = padding[1];
right = padding[1];
} else if (padding.length === 4) {
top = padding[0];
right = padding[1];
bottom = padding[2];
left = padding[3];
}
} else if (padding !== undefined) {
top = padding;
right = padding;
bottom = padding;
left = padding;
}

return {
top,
right,
bottom,
left
};
}

export function formatText(node: NodeData) {
const text = node.text
? ellipsize(node.text, MAX_CHAR_COUNT)
: node.text;
const text = node.text ? ellipsize(node.text, MAX_CHAR_COUNT) : node.text;

const labelDim = measureText(text);
const nodePadding = parsePadding(node.nodePadding);

let width = node.width;
if (width === undefined) {
Expand Down Expand Up @@ -61,6 +93,7 @@ export function formatText(node: NodeData) {
originalText: node.text,
width,
height,
nodePadding,
labelHeight: labelDim.height,
labelWidth: labelDim.width
};
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Expand Up @@ -7,6 +7,7 @@ export interface NodeData<T = any> {
parent?: string;
ports?: PortData[];
icon?: IconData;
nodePadding?: number | [number, number] | [number, number, number, number];
data?: T;
className?: string;
}
Expand Down
106 changes: 105 additions & 1 deletion stories/Nested.stories.tsx
@@ -1,6 +1,6 @@
import React, { useState } from 'react';
import { Canvas } from '../src/Canvas';
import { Node, Edge, MarkerArrow, Port, Icon, Arrow, Label, Remove, Add } from '../src/symbols';
import { Node, Edge, MarkerArrow, Port, Icon, Arrow, Label, Remove, Add, NodeProps } from '../src/symbols';
import { EdgeData, NodeData } from '../src/types';

export const Simple = () => (
Expand Down Expand Up @@ -101,6 +101,110 @@ export const Linking = () => {
);
};

export const NestedEdges = () => {
const nodeDimensions : any = {
typeA: {
width: 190,
height: 150
},
typeB:{
width: 80,
height: 80
}
}
const nodes: NodeData[]= [
{
id: '1',
text: '1',
},
{
id: '2',
label: 'A',
name: 'Process XYZ',
description: 'Description of XYZ',
// describes padding for nested nodes
nodePadding: [120, 50, 50, 50],
...nodeDimensions.typeA
},
{
id: '2.1',
parent: '2',
label: 'B',
name: 'Task 1',
...nodeDimensions.typeB
},
{
id: '2.2',
parent: '2',
label: 'B',
name: 'Task 2',
...nodeDimensions.typeB
},
{
id: '3',
text: '3'
}
];
const edges: EdgeData[] = [
{
id: '1-2.1',
from: '1',
to: '2.1'
},
{
id: '2.1-2.2',
parent: '2',
from: '2.1',
to: '2.2'
},
{
id: '2.2-3',
from: '2.2',
to: '3'
},
];

function prepareNode(node){
const data = node.properties;
switch (data.label){
case 'A':
return (
<Node style={{fill: 'lightgreen', opacity: 0.8}}>
<div style={{textAlign: "center"}}>
<h4>{data.name}</h4>
<p>{data.description}</p>
</div>
</Node>
)
case 'B':
return (
<Node style={{fill: 'lightyellow'}}>
<div style={{textAlign: "center"}}>
<h4>{data.name}</h4>
</div>
</Node>
)
default:
return (
<Node/>
)
}
}

return (
<div style={{ position: 'absolute', top: 0, bottom: 0, left: 0, right: 0 }}>
<Canvas
// required to enable edges from/to nested nodes
layoutOptions={{'elk.hierarchyHandling':'INCLUDE_CHILDREN'}}
direction='RIGHT'
nodes={nodes}
edges={edges}
node={(node: NodeProps) => prepareNode(node)}
/>
</div>
);
};

export const Edges = () => (
<div style={{ position: 'absolute', top: 0, bottom: 0, left: 0, right: 0 }}>
<Canvas
Expand Down