Skip to content

Commit e9c58b8

Browse files
authored
Merge pull request #12 from open-rpc/fix/add-links
fix: add links support
2 parents acb1ba3 + 57b578d commit e9c58b8

File tree

11 files changed

+279
-39
lines changed

11 files changed

+279
-39
lines changed

.editorconfig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# EditorConfig is awesome: http://EditorConfig.org
2+
3+
# top-most EditorConfig file
4+
root = true
5+
6+
# Unix-style newlines with a newline ending every file
7+
[*]
8+
indent_style = space
9+
indent_size = 2
10+
tab_width = 2
11+
end_of_line = lf
12+
charset = utf-8
13+
trim_trailing_whitespace = true
14+
insert_final_newline = true

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
node_modules
22
build
3+
coverage

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/ContentDescriptor/ContentDescriptor.tsx

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,6 @@ const styles = (theme: Theme) => ({
1414
flexShrink: 0,
1515
fontSize: theme.typography.pxToRem(15),
1616
},
17-
root: {
18-
marginBottom: theme.spacing.unit,
19-
marginTop: theme.spacing.unit * 3,
20-
width: "100%",
21-
},
2217
secondaryHeading: {
2318
alignSelf: "end",
2419
color: theme.palette.text.secondary,

src/Documentation.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export default class Documentation extends React.Component<IProps> {
2323
return (
2424
<>
2525
<Info schema={schema} />
26-
<Servers schema={schema} />
26+
<Servers servers={schema.servers} />
2727
<Methods schema={schema} uiSchema={uiSchema} reactJsonOptions={reactJsonOptions}/>
2828
<ContentDescriptors schema={schema} uiSchema={uiSchema}></ContentDescriptors>
2929
</>
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import React, { Component } from "react";
2+
import Typography from "@material-ui/core/Typography";
3+
import Chip from "@material-ui/core/Chip";
4+
import Button from "@material-ui/core/Button";
5+
import Table from "@material-ui/core/Table";
6+
import TableHead from "@material-ui/core/TableHead";
7+
import TableBody from "@material-ui/core/TableBody";
8+
import TableRow from "@material-ui/core/TableRow";
9+
import TableCell from "@material-ui/core/TableCell";
10+
import { withStyles, Theme, WithStyles } from "@material-ui/core/styles";
11+
import ReactMarkdown from "react-markdown";
12+
import { types } from "@open-rpc/meta-schema";
13+
import { ExpansionPanel, ExpansionPanelSummary, ExpansionPanelDetails } from "@material-ui/core";
14+
15+
const styles = (theme: Theme) => ({
16+
heading: {
17+
flexBasis: "33.33%",
18+
flexShrink: 0,
19+
fontSize: theme.typography.pxToRem(15),
20+
},
21+
root: {
22+
marginBottom: theme.spacing.unit,
23+
marginTop: theme.spacing.unit * 3,
24+
width: "100%",
25+
},
26+
secondaryHeading: {
27+
alignSelf: "end",
28+
color: theme.palette.text.secondary,
29+
fontSize: theme.typography.pxToRem(15),
30+
},
31+
});
32+
33+
interface IProps extends WithStyles<typeof styles> {
34+
headers?: string[];
35+
children: any;
36+
}
37+
38+
class ExpansionTable extends Component<IProps> {
39+
public render() {
40+
const { headers, children, classes } = this.props;
41+
if (!headers || headers.length === 0) { return null; }
42+
return (
43+
<Table>
44+
<TableHead>
45+
<TableRow>
46+
{headers.map((header, i) => {
47+
return (
48+
<TableCell key={i} align={i === 0 ? undefined : "right"}>{header}</TableCell>
49+
);
50+
})}
51+
</TableRow>
52+
</TableHead>
53+
<TableBody>
54+
{children}
55+
</TableBody>
56+
</Table>
57+
);
58+
}
59+
}
60+
61+
export default withStyles(styles)(ExpansionTable);

src/Links/Links.test.tsx

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import React from "react";
2+
import ReactDOM from "react-dom";
3+
import Links from "./Links";
4+
import { types } from "@open-rpc/meta-schema";
5+
6+
it("renders without crashing", () => {
7+
const div = document.createElement("div");
8+
ReactDOM.render(<Links />, div);
9+
ReactDOM.unmountComponentAtNode(div);
10+
});
11+
12+
it("renders empty with no schema", () => {
13+
const div = document.createElement("div");
14+
ReactDOM.render(<Links />, div);
15+
expect(div.innerHTML).toBe("");
16+
ReactDOM.unmountComponentAtNode(div);
17+
});
18+
19+
it("renders empty with empty links", () => {
20+
const div = document.createElement("div");
21+
ReactDOM.render(<Links links={[]}/>, div);
22+
expect(div.innerHTML).toBe("");
23+
ReactDOM.unmountComponentAtNode(div);
24+
});
25+
26+
it("renders a link method for a given schema link", () => {
27+
const div = document.createElement("div");
28+
const link = {
29+
method: "get_user_address",
30+
};
31+
ReactDOM.render(<Links links={[link]} />, div);
32+
expect(div.innerHTML.includes("get_user_address")).toBe(true);
33+
ReactDOM.unmountComponentAtNode(div);
34+
});
35+
36+
it("renders a link params for a given schema link", () => {
37+
const div = document.createElement("div");
38+
const link = {
39+
params: {
40+
foo: "$params.id",
41+
},
42+
};
43+
ReactDOM.render(<Links links={[link]} />, div);
44+
expect(div.innerHTML.includes("foo")).toBe(true);
45+
expect(div.innerHTML.includes("$params.id")).toBe(true);
46+
ReactDOM.unmountComponentAtNode(div);
47+
});
48+
49+
it("renders a link description for a given schema link", () => {
50+
const div = document.createElement("div");
51+
const link = {
52+
description: "my description",
53+
};
54+
ReactDOM.render(<Links links={[link]} />, div);
55+
expect(div.innerHTML.includes("my description")).toBe(true);
56+
ReactDOM.unmountComponentAtNode(div);
57+
});
58+
59+
it("renders a link server url for a given schema link", () => {
60+
const div = document.createElement("div");
61+
const link = {
62+
server: {
63+
url: "http://localhost:9210",
64+
},
65+
};
66+
ReactDOM.render(<Links links={[link]} />, div);
67+
expect(div.innerHTML.includes("localhost:9210")).toBe(true);
68+
ReactDOM.unmountComponentAtNode(div);
69+
});
70+
71+
it("renders a link server url for a given schema link", () => {
72+
const div = document.createElement("div");
73+
const link = {
74+
server: {
75+
url: "http://localhost:9210",
76+
},
77+
};
78+
ReactDOM.render(<Links links={[link]} />, div);
79+
expect(div.innerHTML.includes("localhost:9210")).toBe(true);
80+
ReactDOM.unmountComponentAtNode(div);
81+
});

src/Links/Links.tsx

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import React, { Component } from "react";
2+
import Typography from "@material-ui/core/Typography";
3+
import Chip from "@material-ui/core/Chip";
4+
import Button from "@material-ui/core/Button";
5+
import Table from "@material-ui/core/Table";
6+
import TableHead from "@material-ui/core/TableHead";
7+
import TableBody from "@material-ui/core/TableBody";
8+
import TableRow from "@material-ui/core/TableRow";
9+
import TableCell from "@material-ui/core/TableCell";
10+
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
11+
import { withStyles, Theme, WithStyles } from "@material-ui/core/styles";
12+
import ReactMarkdown from "react-markdown";
13+
import { types } from "@open-rpc/meta-schema";
14+
import { ExpansionPanel, ExpansionPanelSummary, ExpansionPanelDetails } from "@material-ui/core";
15+
import ExpansionTable from "../ExpansionTable/ExpansionTable";
16+
import Servers from "../Servers/Servers";
17+
import ReactJson from "react-json-view";
18+
19+
const styles = (theme: Theme) => ({
20+
heading: {
21+
flexBasis: "33.33%",
22+
flexShrink: 0,
23+
fontSize: theme.typography.pxToRem(15),
24+
},
25+
paramsMargin: {
26+
marginTop: theme.spacing.unit,
27+
},
28+
secondaryHeading: {
29+
alignSelf: "end",
30+
color: theme.palette.text.secondary,
31+
fontSize: theme.typography.pxToRem(15),
32+
},
33+
});
34+
35+
interface IProps extends WithStyles<typeof styles> {
36+
links?: types.LinkObject[];
37+
uiSchema?: any;
38+
reactJsonOptions?: any;
39+
}
40+
41+
class Links extends Component<IProps> {
42+
public render() {
43+
const { links, uiSchema, reactJsonOptions, classes } = this.props;
44+
if (!links || links.length === 0) { return null; }
45+
return (
46+
<ExpansionTable headers={["Method", "Summary"]}>
47+
<TableRow>
48+
<TableCell colSpan={6}>
49+
{links.map((link, i) => (
50+
<div style={{ width: "100%" }}>
51+
<ExpansionPanel
52+
style={{ width: "100%" }} defaultExpanded={uiSchema && uiSchema.links["ui:defaultExpanded"]} key={i}>
53+
<ExpansionPanelSummary
54+
style={{ justifyContent: "space-between" }} key="links-header" expandIcon={<ExpandMoreIcon />}>
55+
<div style={{ display: "flex", justifyContent: "space-between", width: "100%", height: "100%" }}>
56+
<Typography className={classes.heading}>{link.method}</Typography>
57+
<Typography className={classes.secondaryHeading}>{link.summary}</Typography>
58+
</div>
59+
</ExpansionPanelSummary>
60+
<ExpansionPanelDetails style={{ display: "block" }} key="links-body">
61+
{link.description && <ReactMarkdown source={link.description} />}
62+
{link.params && <Typography variant="h6" gutterBottom>Params</Typography>}
63+
{link.params && <ReactJson src={link.params} {...reactJsonOptions} />}
64+
{link.server &&
65+
<Typography variant="h6" gutterBottom className={classes.paramsMargin}>Server</Typography>}
66+
{link.server && <Servers servers={[link.server]} noTitle={true}/>}
67+
</ExpansionPanelDetails>
68+
</ExpansionPanel>
69+
</div>
70+
))}
71+
</TableCell>
72+
</TableRow>
73+
</ExpansionTable>
74+
);
75+
}
76+
}
77+
78+
export default withStyles(styles)(Links);

src/Methods/Methods.tsx

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import ContentDescriptor from "../ContentDescriptor/ContentDescriptor";
1111
import ExamplePairings from "../ExamplePairings/ExamplePairings";
1212
import Errors from "../Errors/Errors";
1313
import { types } from "@open-rpc/meta-schema";
14+
import Links from "../Links/Links";
1415

1516
const styles = (theme: Theme) => ({
1617
heading: {
@@ -65,7 +66,7 @@ class Methods extends Component<IProps> {
6566
}
6667
{method.params &&
6768
<ExpansionPanelDetails key="params">
68-
<Params params={method.params as types.ContentDescriptorObject[]} uiSchema={uiSchema}/>
69+
<Params params={method.params as types.ContentDescriptorObject[]} uiSchema={uiSchema} />
6970
</ExpansionPanelDetails>
7071
}
7172
{method.result &&
@@ -75,22 +76,32 @@ class Methods extends Component<IProps> {
7576
}
7677
{method.result && (method.result as types.ContentDescriptorObject).schema &&
7778
<ExpansionPanelDetails key="result">
78-
<ContentDescriptor
79-
contentDescriptor={method.result as types.ContentDescriptorObject}
80-
hideRequired={true} uiSchema={uiSchema} />
79+
<ContentDescriptor
80+
contentDescriptor={method.result as types.ContentDescriptorObject}
81+
hideRequired={true} uiSchema={uiSchema} />
8182
</ExpansionPanelDetails>
8283
}
8384
{method.errors && method.errors.length > 0 &&
8485
<ExpansionPanelDetails key="result">
85-
<Errors errors={method.errors as types.ErrorObject[]} reactJsonOptions={this.props.reactJsonOptions}/>
86+
<Errors errors={method.errors as types.ErrorObject[]} reactJsonOptions={this.props.reactJsonOptions} />
8687
</ExpansionPanelDetails>
8788
}
8889
{method.examples && method.examples.length > 0 &&
8990
<ExpansionPanelDetails key="examples">
90-
<ExamplePairings
91-
examples={method.examples as types.ExamplePairingObject[]}
92-
method={method}
93-
reactJsonOptions={this.props.reactJsonOptions} />
91+
<ExamplePairings
92+
examples={method.examples as types.ExamplePairingObject[]}
93+
method={method}
94+
reactJsonOptions={this.props.reactJsonOptions} />
95+
</ExpansionPanelDetails>
96+
}
97+
{method.links && method.links.length > 0 &&
98+
<ExpansionPanelDetails key="result-title">
99+
<Typography variant="h5">Links</Typography>
100+
</ExpansionPanelDetails>
101+
}
102+
{method.links && method.links.length > 0 &&
103+
<ExpansionPanelDetails key="links">
104+
<Links links={method.links} reactJsonOptions={this.props.reactJsonOptions} />
94105
</ExpansionPanelDetails>
95106
}
96107
</ExpansionPanel>

src/Servers/Servers.test.tsx

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,39 +8,37 @@ it("renders without crashing", () => {
88
ReactDOM.unmountComponentAtNode(div);
99
});
1010

11-
it("renders empty with no schema", () => {
11+
it("renders empty with no servers", () => {
1212
const div = document.createElement("div");
1313
ReactDOM.render(<Servers />, div);
1414
expect(div.innerHTML).toBe("");
1515
ReactDOM.unmountComponentAtNode(div);
1616
});
1717

18-
it("renders empty with empty schema", () => {
18+
it("renders empty with empty servers", () => {
1919
const div = document.createElement("div");
20-
ReactDOM.render(<Servers schema={{} as any}/>, div);
20+
ReactDOM.render(<Servers servers={[]} />, div);
2121
expect(div.innerHTML).toBe("");
2222
ReactDOM.unmountComponentAtNode(div);
2323
});
2424

2525
it("renders empty with empty schema servers", () => {
2626
const div = document.createElement("div");
27-
ReactDOM.render(<Servers schema={{ servers: [] } as any}/>, div);
27+
ReactDOM.render(<Servers servers={[]} />, div);
2828
expect(div.innerHTML).toBe("");
2929
ReactDOM.unmountComponentAtNode(div);
3030
});
3131

3232
it("renders schema servers", () => {
3333
const div = document.createElement("div");
34-
const schema = {
35-
servers: [
36-
{
37-
description: "foobar",
38-
name: "Pet Store",
39-
url: "http://petstore.openrpc.io/api",
40-
},
41-
],
42-
};
43-
ReactDOM.render(<Servers schema={schema as any}/>, div);
34+
const servers = [
35+
{
36+
description: "foobar",
37+
name: "Pet Store",
38+
url: "http://petstore.openrpc.io/api",
39+
},
40+
]
41+
ReactDOM.render(<Servers servers={servers} />, div);
4442
expect(div.innerHTML.includes("Pet Store")).toBe(true);
4543
expect(div.innerHTML.includes('href="http://petstore.openrpc.io/api"')).toBe(true);
4644
expect(div.innerHTML.includes("foobar")).toBe(true);

0 commit comments

Comments
 (0)