Skip to content

Commit a6f976e

Browse files
committed
feat: Add portforward to the UI
1 parent 6826b97 commit a6f976e

File tree

4 files changed

+161
-1
lines changed

4 files changed

+161
-1
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { Story } from "@storybook/react"
2+
import { MockWorkspace, MockWorkspaceAgent } from "../../testHelpers/renderHelpers"
3+
import { PortForwardButton, PortForwardButtonProps } from "./PortForwardButton"
4+
5+
export default {
6+
title: "components/PortForwardButton",
7+
component: PortForwardButton,
8+
}
9+
10+
const Template: Story<PortForwardButtonProps> = (args) => <PortForwardButton {...args} />
11+
12+
export const Closed = Template.bind({})
13+
Closed.args = {
14+
username: MockWorkspace.owner_name,
15+
workspaceName: MockWorkspace.name,
16+
agentName: MockWorkspaceAgent.name,
17+
}
18+
19+
export const Opened = Template.bind({})
20+
Opened.args = {
21+
username: MockWorkspace.owner_name,
22+
workspaceName: MockWorkspace.name,
23+
agentName: MockWorkspaceAgent.name,
24+
defaultIsOpen: true,
25+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import Button from "@material-ui/core/Button"
2+
import Link from "@material-ui/core/Link"
3+
import Popover from "@material-ui/core/Popover"
4+
import { makeStyles } from "@material-ui/core/styles"
5+
import TextField from "@material-ui/core/TextField"
6+
import OpenInNewOutlined from "@material-ui/icons/OpenInNewOutlined"
7+
import { Stack } from "components/Stack/Stack"
8+
import { useRef, useState } from "react"
9+
import { colors } from "theme/colors"
10+
import { CodeExample } from "../CodeExample/CodeExample"
11+
import { HelpTooltipLink, HelpTooltipLinksGroup, HelpTooltipText } from "../Tooltips/HelpTooltip"
12+
13+
export interface PortForwardButtonProps {
14+
username: string
15+
workspaceName: string
16+
agentName: string
17+
defaultIsOpen?: boolean
18+
}
19+
20+
export const PortForwardButton: React.FC<React.PropsWithChildren<PortForwardButtonProps>> = ({
21+
workspaceName,
22+
agentName,
23+
username,
24+
defaultIsOpen = false,
25+
}) => {
26+
const anchorRef = useRef<HTMLButtonElement>(null)
27+
const [isOpen, setIsOpen] = useState(defaultIsOpen)
28+
const id = isOpen ? "schedule-popover" : undefined
29+
const styles = useStyles()
30+
const [port, setPort] = useState("3000")
31+
const { location } = window
32+
const urlExample = `${location.protocol}//${port}--${workspaceName}--${agentName}--${username}.${location.host}`
33+
34+
const onClose = () => {
35+
setIsOpen(false)
36+
}
37+
38+
return (
39+
<>
40+
<Button
41+
startIcon={<OpenInNewOutlined />}
42+
size="small"
43+
ref={anchorRef}
44+
onClick={() => {
45+
setIsOpen(true)
46+
}}
47+
>
48+
Port forward
49+
</Button>
50+
<Popover
51+
classes={{ paper: styles.popoverPaper }}
52+
id={id}
53+
open={isOpen}
54+
anchorEl={anchorRef.current}
55+
onClose={onClose}
56+
anchorOrigin={{
57+
vertical: "bottom",
58+
horizontal: "left",
59+
}}
60+
transformOrigin={{
61+
vertical: "top",
62+
horizontal: "left",
63+
}}
64+
>
65+
<Stack direction="column" spacing={1}>
66+
<HelpTooltipText>
67+
You can portforward this resource by typing the{" "}
68+
<strong>port, workspace name, agent name</strong> and <strong>your username</strong> in
69+
the URL like the example below
70+
</HelpTooltipText>
71+
72+
<CodeExample code={urlExample} />
73+
74+
<HelpTooltipText>
75+
Or you can use the following form to open it in a new tab.
76+
</HelpTooltipText>
77+
78+
<Stack direction="row" spacing={1} alignItems="center">
79+
<TextField
80+
label="Port"
81+
type="number"
82+
value={port}
83+
className={styles.portField}
84+
onChange={(e) => {
85+
setPort(e.currentTarget.value)
86+
}}
87+
/>
88+
<Link
89+
underline="none"
90+
href={urlExample}
91+
target="_blank"
92+
rel="noreferrer"
93+
className={styles.openUrlButton}
94+
>
95+
<Button>Open URL</Button>
96+
</Link>
97+
</Stack>
98+
99+
<HelpTooltipLinksGroup>
100+
<HelpTooltipLink href="https://coder.com/docs/coder-oss/latest/port-forward">
101+
Port forward
102+
</HelpTooltipLink>
103+
</HelpTooltipLinksGroup>
104+
</Stack>
105+
</Popover>
106+
</>
107+
)
108+
}
109+
110+
const useStyles = makeStyles((theme) => ({
111+
popoverPaper: {
112+
padding: `${theme.spacing(2.5)}px ${theme.spacing(3.5)}px ${theme.spacing(3.5)}px`,
113+
width: theme.spacing(46),
114+
color: theme.palette.text.secondary,
115+
marginTop: theme.spacing(0.25),
116+
},
117+
118+
openUrlButton: {
119+
flexShrink: 0,
120+
},
121+
122+
portField: {
123+
// The default border don't contrast well with the popover
124+
"& .MuiOutlinedInput-root .MuiOutlinedInput-notchedOutline": {
125+
borderColor: colors.gray[10],
126+
},
127+
},
128+
}))

site/src/components/Resources/Resources.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import TableHead from "@material-ui/core/TableHead"
77
import TableRow from "@material-ui/core/TableRow"
88
import useTheme from "@material-ui/styles/useTheme"
99
import { ErrorSummary } from "components/ErrorSummary/ErrorSummary"
10+
import { PortForwardButton } from "components/PortForwardButton/PortForwardButton"
1011
import { TableCellDataPrimary } from "components/TableCellData/TableCellData"
1112
import { FC } from "react"
1213
import { getDisplayAgentStatus, getDisplayVersionStatus } from "util/workspace"
@@ -135,6 +136,11 @@ export const Resources: FC<React.PropsWithChildren<ResourcesProps>> = ({
135136
{canUpdateWorkspace && agent.status === "connected" && (
136137
<>
137138
<SSHButton workspaceName={workspace.name} agentName={agent.name} />
139+
<PortForwardButton
140+
username={workspace.owner_name}
141+
workspaceName={workspace.name}
142+
agentName={agent.name}
143+
/>
138144
<TerminalLink
139145
workspaceName={workspace.name}
140146
agentName={agent.name}

site/src/components/Tooltips/HelpTooltip/HelpTooltip.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,8 +226,9 @@ const useStyles = makeStyles((theme) => ({
226226
},
227227

228228
link: {
229-
display: "flex",
229+
display: "inline-flex",
230230
alignItems: "center",
231+
width: "fit-content",
231232
},
232233

233234
linkIcon: {

0 commit comments

Comments
 (0)