(cherry picked from commit aa7dbd7a69)
Co-authored-by: Andrej Ocenas <mr.ocenas@gmail.com>
This commit is contained in:
co-authored by
Andrej Ocenas
parent
ebc38afce5
commit
f5c55ffbe3
@@ -5,6 +5,15 @@ import { NodeGraph } from './NodeGraph';
|
|||||||
import { makeEdgesDataFrame, makeNodesDataFrame } from './utils';
|
import { makeEdgesDataFrame, makeNodesDataFrame } from './utils';
|
||||||
jest.mock('./layout.worker.js');
|
jest.mock('./layout.worker.js');
|
||||||
|
|
||||||
|
jest.mock('react-use/lib/useMeasure', () => {
|
||||||
|
return {
|
||||||
|
__esModule: true,
|
||||||
|
default: () => {
|
||||||
|
return [() => {}, { width: 500, height: 200 }];
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
describe('NodeGraph', () => {
|
describe('NodeGraph', () => {
|
||||||
it('doesnt fail without any data', async () => {
|
it('doesnt fail without any data', async () => {
|
||||||
render(<NodeGraph dataFrames={[]} getLinks={() => []} />);
|
render(<NodeGraph dataFrames={[]} getLinks={() => []} />);
|
||||||
@@ -39,7 +48,6 @@ describe('NodeGraph', () => {
|
|||||||
await screen.findByLabelText('Node: service:1');
|
await screen.findByLabelText('Node: service:1');
|
||||||
|
|
||||||
panView({ x: 10, y: 10 });
|
panView({ x: 10, y: 10 });
|
||||||
screen.debug(getSvg());
|
|
||||||
// Though we try to pan down 10px we are rendering in straight line 3 nodes so there are bounds preventing
|
// Though we try to pan down 10px we are rendering in straight line 3 nodes so there are bounds preventing
|
||||||
// as panning vertically
|
// as panning vertically
|
||||||
await waitFor(() => expect(getTranslate()).toEqual({ x: 10, y: 0 }));
|
await waitFor(() => expect(getTranslate()).toEqual({ x: 10, y: 0 }));
|
||||||
@@ -191,9 +199,9 @@ describe('NodeGraph', () => {
|
|||||||
const button = await screen.findByTitle(/Grid layout/);
|
const button = await screen.findByTitle(/Grid layout/);
|
||||||
userEvent.click(button);
|
userEvent.click(button);
|
||||||
|
|
||||||
await expectNodePositionCloseTo('service:0', { x: -180, y: -60 });
|
await expectNodePositionCloseTo('service:0', { x: -60, y: -60 });
|
||||||
await expectNodePositionCloseTo('service:1', { x: -60, y: -60 });
|
await expectNodePositionCloseTo('service:1', { x: 60, y: -60 });
|
||||||
await expectNodePositionCloseTo('service:2', { x: 60, y: -60 });
|
await expectNodePositionCloseTo('service:2', { x: -60, y: 80 });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -135,6 +135,7 @@ export function NodeGraph({ getLinks, dataFrames, nodeLimit }: Props) {
|
|||||||
processed.edges,
|
processed.edges,
|
||||||
config,
|
config,
|
||||||
nodeCountLimit,
|
nodeCountLimit,
|
||||||
|
width,
|
||||||
focusedNodeId
|
focusedNodeId
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -44,11 +44,9 @@ export function useLayout(
|
|||||||
rawEdges: EdgeDatum[],
|
rawEdges: EdgeDatum[],
|
||||||
config: Config = defaultConfig,
|
config: Config = defaultConfig,
|
||||||
nodeCountLimit: number,
|
nodeCountLimit: number,
|
||||||
|
width: number,
|
||||||
rootNodeId?: string
|
rootNodeId?: string
|
||||||
) {
|
) {
|
||||||
const [nodesGrid, setNodesGrid] = useState<NodeDatum[]>([]);
|
|
||||||
const [edgesGrid, setEdgesGrid] = useState<EdgeDatumLayout[]>([]);
|
|
||||||
|
|
||||||
const [nodesGraph, setNodesGraph] = useState<NodeDatum[]>([]);
|
const [nodesGraph, setNodesGraph] = useState<NodeDatum[]>([]);
|
||||||
const [edgesGraph, setEdgesGraph] = useState<EdgeDatumLayout[]>([]);
|
const [edgesGraph, setEdgesGraph] = useState<EdgeDatumLayout[]>([]);
|
||||||
|
|
||||||
@@ -70,18 +68,16 @@ export function useLayout(
|
|||||||
// happening as already visible nodes would change positions.
|
// happening as already visible nodes would change positions.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (rawNodes.length === 0) {
|
if (rawNodes.length === 0) {
|
||||||
|
setNodesGraph([]);
|
||||||
|
setEdgesGraph([]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
// d3 just modifies the nodes directly, so lets make sure we don't leak that outside
|
// This is async but as I wanted to still run the sync grid layout and you cannot return promise from effect so
|
||||||
let rawNodesCopy = rawNodes.map((n) => ({ ...n }));
|
// having callback seems ok here.
|
||||||
let rawEdgesCopy = rawEdges.map((e) => ({ ...e }));
|
defaultLayout(rawNodes, rawEdges, ({ nodes, edges }) => {
|
||||||
|
|
||||||
// This is async but as I wanted to still run the sync grid layout and you cannot return promise from effect having
|
|
||||||
// callback seem ok here.
|
|
||||||
defaultLayout(rawNodesCopy, rawEdgesCopy, ({ nodes, edges }) => {
|
|
||||||
// TODO: it would be better to cancel the worker somehow but probably not super important right now.
|
// TODO: it would be better to cancel the worker somehow but probably not super important right now.
|
||||||
if (isMounted()) {
|
if (isMounted()) {
|
||||||
setNodesGraph(nodes);
|
setNodesGraph(nodes);
|
||||||
@@ -89,14 +85,21 @@ export function useLayout(
|
|||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}, [rawNodes, rawEdges, isMounted]);
|
||||||
|
|
||||||
rawNodesCopy = rawNodes.map((n) => ({ ...n }));
|
// Compute grid separately as it is sync and do not need to be inside effect. Also it is dependant on width while
|
||||||
rawEdgesCopy = rawEdges.map((e) => ({ ...e }));
|
// default layout does not care and we don't want to recalculate that on panel resize.
|
||||||
gridLayout(rawNodesCopy, config.sort);
|
const [nodesGrid, edgesGrid] = useMemo(() => {
|
||||||
|
if (rawNodes.length === 0) {
|
||||||
|
return [[], []];
|
||||||
|
}
|
||||||
|
|
||||||
setNodesGrid(rawNodesCopy);
|
const rawNodesCopy = rawNodes.map((n) => ({ ...n }));
|
||||||
setEdgesGrid(rawEdgesCopy as EdgeDatumLayout[]);
|
const rawEdgesCopy = rawEdges.map((e) => ({ ...e }));
|
||||||
}, [config.sort, rawNodes, rawEdges, isMounted]);
|
gridLayout(rawNodesCopy, width, config.sort);
|
||||||
|
|
||||||
|
return [rawNodesCopy, rawEdgesCopy as EdgeDatumLayout[]];
|
||||||
|
}, [config.sort, rawNodes, rawEdges, width]);
|
||||||
|
|
||||||
// Limit the nodes so we don't show all for performance reasons. Here we don't compute both at the same time so
|
// Limit the nodes so we don't show all for performance reasons. Here we don't compute both at the same time so
|
||||||
// changing the layout can trash internal memoization at the moment.
|
// changing the layout can trash internal memoization at the moment.
|
||||||
@@ -159,6 +162,7 @@ function defaultLayout(
|
|||||||
*/
|
*/
|
||||||
function gridLayout(
|
function gridLayout(
|
||||||
nodes: NodeDatum[],
|
nodes: NodeDatum[],
|
||||||
|
width: number,
|
||||||
sort?: {
|
sort?: {
|
||||||
field: Field;
|
field: Field;
|
||||||
ascending: boolean;
|
ascending: boolean;
|
||||||
@@ -166,8 +170,9 @@ function gridLayout(
|
|||||||
) {
|
) {
|
||||||
const spacingVertical = 140;
|
const spacingVertical = 140;
|
||||||
const spacingHorizontal = 120;
|
const spacingHorizontal = 120;
|
||||||
// TODO probably make this based on the width of the screen
|
const padding = spacingHorizontal / 2;
|
||||||
const perRow = 4;
|
const perRow = Math.min(Math.floor((width - padding * 2) / spacingVertical), nodes.length);
|
||||||
|
const midPoint = Math.floor(((perRow - 1) * spacingHorizontal) / 2);
|
||||||
|
|
||||||
if (sort) {
|
if (sort) {
|
||||||
nodes.sort((node1, node2) => {
|
nodes.sort((node1, node2) => {
|
||||||
@@ -182,7 +187,7 @@ function gridLayout(
|
|||||||
for (const [index, node] of nodes.entries()) {
|
for (const [index, node] of nodes.entries()) {
|
||||||
const row = Math.floor(index / perRow);
|
const row = Math.floor(index / perRow);
|
||||||
const column = index % perRow;
|
const column = index % perRow;
|
||||||
node.x = -180 + column * spacingHorizontal;
|
node.x = column * spacingHorizontal - midPoint;
|
||||||
node.y = -60 + row * spacingVertical;
|
node.y = -60 + row * spacingVertical;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user