Skip to content

Commit

Permalink
Merge pull request #504 from open-rpc/fix/add-transport-dropdown
Browse files Browse the repository at this point in the history
Fix/add transport dropdown
  • Loading branch information
shanejonas authored Nov 14, 2020
2 parents a6ab1bf + 331232a commit 2d40a73
Show file tree
Hide file tree
Showing 11 changed files with 1,306 additions and 1,207 deletions.
1,987 changes: 847 additions & 1,140 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
"react-json-view": "^1.19.1",
"react-markdown": "^4.1.0",
"react-split-pane": "^0.1.87",
"reusable": "^1.0.0-alpha.12"
"reusable": "^1.0.0-alpha.12",
"use-debounce": "^5.0.4"
},
"scripts": {
"start": "rescripts start",
Expand Down Expand Up @@ -70,7 +71,7 @@
"monaco-editor-webpack-plugin": "^1.7.0",
"react-scripts": "^3.4.3",
"tslint": "^5.18.0",
"typescript": "^3.5.2"
"typescript": "^3.7.3"
},
"rescripts": [
"rescript-monaco"
Expand Down
73 changes: 70 additions & 3 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,26 @@ import useMonacoVimMode from "./hooks/useMonacoVimMode";
import { IExample } from "./ExampleDocumentsDropdown/ExampleDocumentsDropdown";
import Inspector from "@open-rpc/inspector";
import useInspectorActionStore from "./stores/inspectorActionStore";
import { useTransport, defaultTransports, ITransport } from "./hooks/useTransport";
import fetchUrlSchemaFile from "./fetchUrlSchemaFile";
import queryParamsStore from "./stores/queryParamsStore";
import { useDebounce } from "use-debounce";

const App: React.FC = () => {
const [defaultValue] = useDefaultEditorValue();
const [defaultValue, setDefaultValue] = useDefaultEditorValue();
const [markers, setMarkers] = useState<monaco.editor.IMarker[]>([] as monaco.editor.IMarker[]);
const [searchUrl, { results, error }, setSearchUrl] = searchBarStore();
const [searchUrl, setSearchUrl] = searchBarStore();
const [searchUrlDebounced] = useDebounce(searchUrl, 1000);
const [results, setResults] = useState<any>();
const [error, setError] = useState<string | undefined>();
const [notification, setNotification] = useState<ISnackBarNotification | undefined>();
const [UISchema, setUISchemaBySection]: [IUISchema, any] = UISchemaStore();
const [editor, setEditor]: [any, Dispatch<{}>] = useState();
const [horizontalSplit, privateSetHorizontalSplit] = useState(false);
const [parsedSchema, setParsedSchema] = useParsedSchema(
defaultValue ? JSON.parse(defaultValue) : null,
);
const [query] = queryParamsStore();
const setHorizontalSplit = (val: boolean) => {
if (editor) {
setTimeout(() => {
Expand Down Expand Up @@ -104,7 +112,54 @@ const App: React.FC = () => {
indentWidth: 2,
name: false,
});
const [transportList, setTransportList] = useState(defaultTransports);
const getQueryTransport = () => {
if (!query.transport) {
return transportList[0];
}
const queryTransport = transportList.find((item) => item.type === query.transport);
return queryTransport || transportList[0];
};
const currentTheme = UISchema.appBar["ui:darkMode"] ? darkTheme : lightTheme;
const [transport, selectedTransportType, setTransportType] = useTransport(
transportList,
searchUrlDebounced,
getQueryTransport(),
);
const refreshOpenRpcDocument = async () => {
// handle .json urls
if (searchUrlDebounced && searchUrlDebounced.includes(".json")) {
const rd = await fetchUrlSchemaFile(searchUrlDebounced);
setDefaultValue(rd);
return setResults(rd);
}
try {
const d = await transport?.sendData({
internalID: 999999,
request: {
jsonrpc: "2.0",
params: [],
id: 999999,
method: "rpc.discover",
},
});
const rd = JSON.stringify(d, null, 2);
if (rd) {
setDefaultValue(rd);
setResults(rd);
}
} catch (e) {
setError(e.message);
}
};

useEffect(() => {
if (searchUrlDebounced && transport) {
refreshOpenRpcDocument();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [searchUrlDebounced, transport]);

useEffect(() => {
if (inspectorContents) {
setHorizontalSplit(true);
Expand All @@ -119,6 +174,17 @@ const App: React.FC = () => {
uiSchema={UISchema}
examples={examples as IExample[]}
onExampleDocumentsDropdownChange={(example: IExample) => setSearchUrl(example.url)}
selectedTransport={selectedTransportType}
transportList={transportList}
onTransportChange={(changedTransport) => setTransportType(changedTransport)}
onTransportAdd={(addedTransport: ITransport) => {
setTransportList((oldList) => {
return [
...oldList,
addedTransport,
];
});
}}
onSplitViewChange={(value) => {
setUISchemaBySection({
value,
Expand Down Expand Up @@ -147,8 +213,9 @@ const App: React.FC = () => {
right={
<>
<Inspector hideToggleTheme={true} url={
searchUrl && searchUrl.includes(".json") ? null : searchUrl
searchUrlDebounced && searchUrlDebounced.includes(".json") ? null : searchUrlDebounced
}
transport={selectedTransportType.type !== "plugin" ? selectedTransportType.type : undefined}
request={inspectorContents && inspectorContents.request}
openrpcDocument={parsedSchema}
/>
Expand Down
27 changes: 22 additions & 5 deletions src/AppBar/AppBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import EditIcon from "@material-ui/icons/Edit";
import { IUISchema } from "../UISchema";
import SearchBar from "../SearchBar/SearchBar";
import ExampleDocumentsDropdown, { IExample } from "../ExampleDocumentsDropdown/ExampleDocumentsDropdown";
import { ITransport } from "../hooks/useTransport";
import TransportDropdown from "../components/TransportDropdown";

const styles = (theme: Theme) => ({
title: {
Expand All @@ -31,11 +33,15 @@ const styles = (theme: Theme) => ({
interface IProps extends WithStyles<typeof styles> {
uiSchema?: IUISchema;
examples?: IExample[];
transportList?: ITransport[];
selectedTransport?: ITransport;
searchBarUrl?: string | undefined;
onChangeUrl?: any;
onDarkModeChange?: any;
onSplitViewChange?: (split: boolean) => any;
onExampleDocumentsDropdownChange?: (example: IExample) => any;
onTransportAdd: (transport: ITransport) => any;
onTransportChange: (transport: ITransport) => any;
}

class ApplicationBar extends Component<IProps> {
Expand All @@ -52,7 +58,7 @@ class ApplicationBar extends Component<IProps> {
<AppBar position="fixed" color="default" elevation={0} className={classes.appBar}>
<Toolbar>
<Grid alignItems="center" container>
<Grid item xs={6} sm={6} md={3} direction="row" container>
<Grid item xs={6} sm={6} md={2} direction="row" container>
{this.props.uiSchema && this.props.uiSchema.appBar && this.props.uiSchema.appBar["ui:logoUrl"] &&
<Grid>
<img
Expand All @@ -68,8 +74,19 @@ class ApplicationBar extends Component<IProps> {
</Grid>
</Grid>
<Hidden smDown>
<Grid item container justify="center" alignItems="center" sm={6} >
<Grid item sm={9}>
<Grid item container justify="center" alignItems="center" sm={8} >
<Grid item>
<TransportDropdown
transports={this.props.transportList}
onAddTransport={this.props.onTransportAdd}
selectedTransport={this.props.selectedTransport}
onChange={this.props.onTransportChange}
style={{
marginLeft: "10px",
}}
/>
</Grid>
<Grid item sm={6}>
{this.props.uiSchema && this.props.uiSchema.appBar && this.props.uiSchema.appBar["ui:input"] &&
<Paper style={{
background: "rgba(0, 0, 0, 0.1)",
Expand All @@ -85,12 +102,12 @@ class ApplicationBar extends Component<IProps> {
}
</Grid>
{this.props.uiSchema && this.props.uiSchema.appBar &&
this.props.uiSchema.appBar["ui:examplesDropdown"] &&
this.props.uiSchema.appBar["ui:examplesDropdown"] &&
<ExampleDocumentsDropdown examples={examples} onChange={onExampleDocumentsDropdownChange} />
}
</Grid>
</Hidden>
<Grid item xs={6} sm={6} md={3} container justify="flex-end" alignItems="center">
<Grid item xs={6} sm={6} md={2} container justify="flex-end" alignItems="center">
{uiSchema && uiSchema.appBar["ui:splitView"] ?
<Tooltip title={"Full Screen"}>
<IconButton onClick={() => {
Expand Down
170 changes: 170 additions & 0 deletions src/components/TransportDropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import React, { useState, ChangeEvent } from "react";
import { Button, Menu, MenuItem, Typography, Dialog, Container, Grid, InputBase } from "@material-ui/core";
import { CSSProperties } from "@material-ui/core/styles/withStyles";
import PlusIcon from "@material-ui/icons/Add";
import DropdownArrowIcon from "@material-ui/icons/ArrowDropDown";
import { ITransport } from "../hooks/useTransport";

interface IProps {
transports?: ITransport[];
selectedTransport?: ITransport;
onChange?: (changedTransport: ITransport) => void;
onAddTransport?: (addedTransport: ITransport) => void;
style?: CSSProperties;
}

const TransportDropdown: React.FC<IProps> = ({ selectedTransport, transports, onChange, style, onAddTransport }) => {
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const handleMenuItemClick = (transport: ITransport) => {
setAnchorEl(null);
if (onChange) {
onChange(transport);
}
};

const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const [dialogOpen, setDialogOpen] = useState<boolean>(false);

const [selectedCustomTransport, setSelectedCustomTransport] = useState<ITransport | undefined>();
const [customTransportName, setCustomTransportName] = useState<string | undefined>();
const [customTransportUri, setCustomTransportUri] = useState<string | undefined>();

const [dialogMenuAnchorEl, setDialogMenuAnchorEl] = useState<null | HTMLElement>(null);

const handleDialogAnchorClose = () => {
setDialogMenuAnchorEl(null);
};
const handleDialogCustomTransportClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setDialogMenuAnchorEl(event.currentTarget);
};

const handleCustomTransportDialogMenuItemClick = (transport: ITransport) => {
setDialogMenuAnchorEl(null);
setSelectedCustomTransport(transport);
};

const handleSubmitCustomTransport = () => {
setDialogMenuAnchorEl(null);
if (selectedCustomTransport && customTransportName && customTransportUri) {
const t: ITransport = {
type: "plugin",
transport: selectedCustomTransport,
name: customTransportName,
uri: customTransportUri,
};
if (onAddTransport) {
onAddTransport(t);
}
setDialogOpen(false);
}
};
return (
<div style={style}>
<Dialog onClose={() => setDialogOpen(false)} aria-labelledby="simple-dialog-title" open={dialogOpen}>
<Container maxWidth="sm">
<Grid
container
justify="space-between"
alignItems="center"
style={{ padding: "30px", paddingTop: "10px", paddingBottom: "10px", marginTop: "10px" }}>
<Typography variant="h6">Custom Transport Plugin</Typography>
<Typography variant="caption" gutterBottom>
Transport plugins are created by implementing the "connect",
"sendData", and "close" methods over JSON-RPC.
</Typography>
<Grid container direction="column" spacing={1}>
<Grid item>
<InputBase placeholder="Plugin Name"
onChange={
(event: ChangeEvent<HTMLInputElement>) => {
setCustomTransportName(event.target.value);
}
}
style={{
display: "block",
background: "rgba(0,0,0,0.1)",
borderRadius: "4px",
padding: "0px 10px",
marginRight: "5px",
}}
/>
</Grid>
<Grid item>
<InputBase placeholder="Plugin URI"
onChange={
(event: ChangeEvent<HTMLInputElement>) => {
setCustomTransportUri(event.target.value);
}
}
style={{
display: "block",
background: "rgba(0,0,0,0.1)",
borderRadius: "4px",
padding: "0px 10px",
marginRight: "5px",
}}
/>
</Grid>
<Grid item>
<Button
variant="outlined"
onClick={handleDialogCustomTransportClick}>
{selectedCustomTransport ? selectedCustomTransport.name : "Select A Transport"}
</Button>
</Grid>
</Grid>
<Menu
id="transport-menu"
anchorEl={dialogMenuAnchorEl}
keepMounted
open={Boolean(dialogMenuAnchorEl)}
onClose={handleDialogAnchorClose}
>
{transports && transports.filter((value) => value.type !== "plugin").map((transport, i) => (
<MenuItem
onClick={() => handleCustomTransportDialogMenuItemClick(transport)}
>{transport.name}</MenuItem>
))}
</Menu>
<Button
style={{ marginTop: "10px", marginBottom: "10px" }}
onClick={handleSubmitCustomTransport}
disabled={!customTransportName || !customTransportUri || !selectedCustomTransport}
variant="contained">
Add Transport
</Button>
</Grid>
</Container>
</Dialog>
<Button
style={{
marginRight: "10px",
marginLeft: "5px",
}}
variant="outlined"
onClick={handleClick} endIcon={<DropdownArrowIcon />}
>{selectedTransport && selectedTransport.name}</Button>
<Menu
id="transport-menu"
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={handleClose}
>
{transports && transports.map((transport, i) => (
<MenuItem onClick={() => handleMenuItemClick(transport)}>{transport.name}</MenuItem>
))}
<MenuItem onClick={() => setDialogOpen(true)}>
<PlusIcon style={{ marginRight: "5px" }} /><Typography variant="caption">Add Transport</Typography>
</MenuItem>
</Menu>
</div>
);
};

export default TransportDropdown;
18 changes: 0 additions & 18 deletions src/fetchSchemaFromRpcDiscover.tsx

This file was deleted.

Loading

0 comments on commit 2d40a73

Please sign in to comment.