Skip to content
This repository was archived by the owner on Mar 17, 2024. It is now read-only.

Commit 0f77406

Browse files
authored
Merge pull request #323 from ts-graphviz/fix-render-function
BRAKING CHANGE: refactor render function
2 parents 824dc9e + fdaf6c6 commit 0f77406

35 files changed

+494
-286
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`renderToDot render to container subgraph test 1`] = `
4+
digraph {
5+
subgraph "test" {
6+
"a";
7+
"b";
8+
"a" -> "b";
9+
}
10+
}
11+
`;

src/__tests__/render-to-dot.spec.tsx

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,4 @@ describe('renderToDot', () => {
4545
);
4646
expect(dot).toBeValidDotAndMatchSnapshot();
4747
});
48-
49-
it('render to be blank string', () => {
50-
const dot = renderToDot(<></>);
51-
expect(dot).toBe('');
52-
});
5348
});

src/__tests__/render.test.tsx

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/* eslint-disable jest/expect-expect */
2+
import React from 'react';
3+
import 'jest-graphviz';
4+
import { digraph, toDot } from 'ts-graphviz';
5+
import { Edge } from '../components/Edge';
6+
import { Node } from '../components/Node';
7+
import { renderExpectToThrow } from '../components/__tests__/utils/renderExpectToThrow';
8+
import { NoContainerErrorMessage } from '../errors';
9+
import { render } from '../render';
10+
11+
describe('renderToDot', () => {
12+
describe('no container error', () => {
13+
test('Fragment', () => {
14+
renderExpectToThrow(<></>, NoContainerErrorMessage);
15+
});
16+
17+
test('Node', () => {
18+
renderExpectToThrow(<Node id="test" />, NoContainerErrorMessage);
19+
});
20+
21+
test('Edge', () => {
22+
renderExpectToThrow(<Edge targets={['a', 'b']} />, NoContainerErrorMessage);
23+
});
24+
});
25+
26+
it('render to container subgraph test', () => {
27+
const nodes = ['a', 'b'];
28+
const G = digraph();
29+
const subgraph = render(
30+
<>
31+
{nodes.map((id) => (
32+
<Node id={id} key={id} />
33+
))}
34+
<Edge targets={nodes} />
35+
</>,
36+
G.subgraph('test'),
37+
);
38+
expect(G.subgraph('test')).toEqual(subgraph);
39+
expect(toDot(G)).toBeValidDotAndMatchSnapshot();
40+
});
41+
});

src/components/ClusterPortal.tsx

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,25 @@
11
import React, { FC, useContext, useMemo } from 'react';
22
import PropTypes from 'prop-types';
3-
import { Cluster } from '../contexts/Cluster';
3+
import { CurrentCluster } from '../contexts/CurrentCluster';
44
import { ClusterMap } from '../contexts/ClusterMap';
5-
import { useRootCluster } from '../hooks/use-root-cluster';
6-
import { ClusterPortalComponentProps } from '../types';
5+
import { useContainerCluster } from '../hooks/use-container-cluster';
6+
import { ClusterPortalProps } from '../types';
77

8-
export const ClusterPortal: FC<ClusterPortalComponentProps> = ({ children, name }) => {
9-
const root = useRootCluster();
8+
/**
9+
* ClusterPortal component.
10+
*/
11+
export const ClusterPortal: FC<ClusterPortalProps> = ({ children, id }) => {
12+
const container = useContainerCluster();
1013
const map = useContext(ClusterMap);
11-
const cluster = useMemo(() => (name ? map.get(name) ?? root : root), [root, map, name]);
12-
return <Cluster.Provider value={cluster}>{children}</Cluster.Provider>;
14+
const cluster = useMemo(() => (id ? map.get(id) ?? container : container), [container, map, id]);
15+
return <CurrentCluster.Provider value={cluster}>{children}</CurrentCluster.Provider>;
1316
};
1417

1518
ClusterPortal.displayName = 'ClusterPortal';
1619
ClusterPortal.defaultProps = {
17-
name: undefined,
20+
id: undefined,
1821
};
1922

2023
ClusterPortal.propTypes = {
21-
name: PropTypes.string,
24+
id: PropTypes.string,
2225
};

src/components/Digraph.tsx

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,35 @@
11
import React, { FC, useEffect } from 'react';
22
import PropTypes from 'prop-types';
3-
import { RootCluster } from '../contexts/RootCluster';
4-
import { Cluster } from '../contexts/Cluster';
3+
import { ContainerCluster } from '../contexts/ContainerCluster';
4+
import { CurrentCluster } from '../contexts/CurrentCluster';
55
import { useDigraph } from '../hooks/use-digraph';
66
import { useRenderedID } from '../hooks/use-rendered-id';
7-
import { useRootCluster } from '../hooks/use-root-cluster';
7+
import { useContainerCluster } from '../hooks/use-container-cluster';
88
import { DuplicatedRootClusterErrorMessage } from '../errors';
99
import { useClusterMap } from '../hooks/use-cluster-map';
10-
import { RootClusterComponentProps } from '../types';
10+
import { RootClusterProps } from '../types';
1111

12-
export const Digraph: FC<RootClusterComponentProps> = ({ children, label, ...props }) => {
13-
const root = useRootCluster();
14-
if (root !== null) {
12+
/**
13+
* `Digraph` component.
14+
*/
15+
export const Digraph: FC<RootClusterProps> = ({ children, label, ...options }) => {
16+
const container = useContainerCluster();
17+
if (container !== null) {
1518
throw Error(DuplicatedRootClusterErrorMessage);
1619
}
1720
const renderedLabel = useRenderedID(label);
18-
if (renderedLabel !== undefined) Object.assign(props, { label: renderedLabel });
19-
const digraph = useDigraph(props);
21+
if (renderedLabel !== undefined) Object.assign(options, { label: renderedLabel });
22+
const digraph = useDigraph(options);
2023
const clusters = useClusterMap();
2124
useEffect(() => {
2225
if (digraph.id !== undefined) {
2326
clusters.set(digraph.id, digraph);
2427
}
2528
}, [clusters, digraph]);
2629
return (
27-
<RootCluster.Provider value={digraph}>
28-
<Cluster.Provider value={digraph}>{children}</Cluster.Provider>
29-
</RootCluster.Provider>
30+
<ContainerCluster.Provider value={digraph}>
31+
<CurrentCluster.Provider value={digraph}>{children}</CurrentCluster.Provider>
32+
</ContainerCluster.Provider>
3033
);
3134
};
3235

src/components/Edge.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
1-
import React, { FC } from 'react';
1+
import { VFC } from 'react';
22
import PropTypes from 'prop-types';
33
import { useEdge } from '../hooks/use-edge';
44
import { useRenderedID } from '../hooks/use-rendered-id';
5-
import { EdgeComponentProps } from '../types';
5+
import { EdgeProps } from '../types';
66

7-
export const Edge: FC<EdgeComponentProps> = ({ children, label, ...props }) => {
7+
/**
8+
* `Edge` component.
9+
*/
10+
export const Edge: VFC<EdgeProps> = ({ targets, label, ...options }) => {
811
const renderedLabel = useRenderedID(label);
9-
if (renderedLabel !== undefined) Object.assign(props, { label: renderedLabel });
10-
useEdge(props);
11-
return <>{children}</>;
12+
if (renderedLabel !== undefined) Object.assign(options, { label: renderedLabel });
13+
useEdge(targets, options);
14+
return null;
1215
};
1316

1417
Edge.displayName = 'Edge';

src/components/Graph.tsx

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,34 @@
11
import React, { FC, useEffect } from 'react';
22
import PropTypes from 'prop-types';
3-
import { RootCluster } from '../contexts/RootCluster';
4-
import { Cluster } from '../contexts/Cluster';
3+
import { ContainerCluster } from '../contexts/ContainerCluster';
4+
import { CurrentCluster } from '../contexts/CurrentCluster';
55
import { useGraph } from '../hooks/use-graph';
66
import { useRenderedID } from '../hooks/use-rendered-id';
7-
import { useRootCluster } from '../hooks/use-root-cluster';
7+
import { useContainerCluster } from '../hooks/use-container-cluster';
88
import { DuplicatedRootClusterErrorMessage } from '../errors';
99
import { useClusterMap } from '../hooks/use-cluster-map';
10-
import { RootClusterComponentProps } from '../types';
11-
12-
export const Graph: FC<RootClusterComponentProps> = ({ children, label, ...props }) => {
13-
const root = useRootCluster();
14-
if (root !== null) {
10+
import { RootClusterProps } from '../types';
11+
/**
12+
* `Graph` component.
13+
*/
14+
export const Graph: FC<RootClusterProps> = ({ children, label, ...options }) => {
15+
const container = useContainerCluster();
16+
if (container !== null) {
1517
throw Error(DuplicatedRootClusterErrorMessage);
1618
}
1719
const renderedLabel = useRenderedID(label);
18-
if (renderedLabel !== undefined) Object.assign(props, { label: renderedLabel });
19-
const graph = useGraph(props);
20+
if (renderedLabel !== undefined) Object.assign(options, { label: renderedLabel });
21+
const graph = useGraph(options);
2022
const clusters = useClusterMap();
2123
useEffect(() => {
2224
if (graph.id !== undefined) {
2325
clusters.set(graph.id, graph);
2426
}
2527
}, [clusters, graph]);
2628
return (
27-
<RootCluster.Provider value={graph}>
28-
<Cluster.Provider value={graph}>{children}</Cluster.Provider>
29-
</RootCluster.Provider>
29+
<ContainerCluster.Provider value={graph}>
30+
<CurrentCluster.Provider value={graph}>{children}</CurrentCluster.Provider>
31+
</ContainerCluster.Provider>
3032
);
3133
};
3234

src/components/Node.tsx

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
1-
import React, { FC } from 'react';
1+
import { VFC } from 'react';
22
import PropTypes from 'prop-types';
33
import { useNode } from '../hooks/use-node';
44
import { useRenderedID } from '../hooks/use-rendered-id';
5-
import { NodeComponentProps } from '../types';
5+
import { NodeProps } from '../types';
66

7-
export const Node: FC<NodeComponentProps> = ({ children, label, xlabel, ...props }) => {
7+
/**
8+
* `Node` component.
9+
*/
10+
export const Node: VFC<NodeProps> = ({ id, label, xlabel, ...options }) => {
811
const renderedLabel = useRenderedID(label);
912
const renderedXlabel = useRenderedID(xlabel);
1013

11-
if (renderedLabel !== undefined) Object.assign(props, { label: renderedLabel });
12-
if (renderedXlabel !== undefined) Object.assign(props, { xlabel: renderedXlabel });
13-
useNode(props);
14-
return <>{children}</>;
14+
if (renderedLabel !== undefined) Object.assign(options, { label: renderedLabel });
15+
if (renderedXlabel !== undefined) Object.assign(options, { xlabel: renderedXlabel });
16+
useNode(id, options);
17+
return null;
1518
};
1619

1720
Node.displayName = 'Node';

src/components/Subgraph.tsx

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,24 @@
11
import React, { FC, useEffect } from 'react';
22
import PropTypes from 'prop-types';
3-
import { Cluster } from '../contexts/Cluster';
3+
import { CurrentCluster } from '../contexts/CurrentCluster';
44
import { useSubgraph } from '../hooks/use-subgraph';
55
import { useRenderedID } from '../hooks/use-rendered-id';
66
import { useClusterMap } from '../hooks/use-cluster-map';
7-
import { SubgraphComponentProps } from '../types';
8-
9-
export const Subgraph: FC<SubgraphComponentProps> = ({ children, label, ...props }) => {
7+
import { SubgraphProps } from '../types';
8+
/**
9+
* `Subgraph` component.
10+
*/
11+
export const Subgraph: FC<SubgraphProps> = ({ children, label, ...options }) => {
1012
const renderedLabel = useRenderedID(label);
11-
if (renderedLabel !== undefined) Object.assign(props, { label: renderedLabel });
12-
const subgraph = useSubgraph(props);
13+
if (renderedLabel !== undefined) Object.assign(options, { label: renderedLabel });
14+
const subgraph = useSubgraph(options);
1315
const clusters = useClusterMap();
1416
useEffect(() => {
1517
if (subgraph.id !== undefined) {
1618
clusters.set(subgraph.id, subgraph);
1719
}
1820
}, [subgraph, clusters]);
19-
return <Cluster.Provider value={subgraph}>{children}</Cluster.Provider>;
21+
return <CurrentCluster.Provider value={subgraph}>{children}</CurrentCluster.Provider>;
2022
};
2123

2224
Subgraph.displayName = 'Subgraph';

src/components/__tests__/utils/renderExpectToThrow.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import React, { Component, ReactElement } from 'react';
44
import { render } from '../../../render';
55

6-
export function renderExpectToThrow(element: ReactElement, expectedError: string): void {
6+
export function renderExpectToThrow(element: ReactElement, ...expectedError: string[]): void {
77
const errors: Error[] = [];
88
class ErrorBoundary extends Component<Record<string, unknown>, { hasError: boolean }> {
99
constructor(props: Record<string, unknown>) {
@@ -28,11 +28,11 @@ export function renderExpectToThrow(element: ReactElement, expectedError: string
2828
}
2929

3030
try {
31-
render(<ErrorBoundary>{element}</ErrorBoundary>, {});
31+
render(<ErrorBoundary>{element}</ErrorBoundary>);
3232
} catch (e) {
3333
errors.push(e);
3434
}
35+
expect(errors.length).toBeGreaterThanOrEqual(expectedError.length);
3536

36-
expect(errors.length).toBe(1);
37-
expect(errors[0].message).toContain(expectedError);
37+
expect(errors.map((e) => e.message)).toEqual(expect.arrayContaining(expectedError));
3838
}

0 commit comments

Comments
 (0)