diff --git a/site/src/components/PortForwardButton/PortForwardButton.stories.tsx b/site/src/components/PortForwardButton/PortForwardButton.stories.tsx new file mode 100644 index 0000000000000..89369b42a4d22 --- /dev/null +++ b/site/src/components/PortForwardButton/PortForwardButton.stories.tsx @@ -0,0 +1,25 @@ +import { Story } from "@storybook/react" +import { MockWorkspace, MockWorkspaceAgent } from "../../testHelpers/renderHelpers" +import { PortForwardButton, PortForwardButtonProps } from "./PortForwardButton" + +export default { + title: "components/PortForwardButton", + component: PortForwardButton, +} + +const Template: Story = (args) => + +export const Closed = Template.bind({}) +Closed.args = { + username: MockWorkspace.owner_name, + workspaceName: MockWorkspace.name, + agentName: MockWorkspaceAgent.name, +} + +export const Opened = Template.bind({}) +Opened.args = { + username: MockWorkspace.owner_name, + workspaceName: MockWorkspace.name, + agentName: MockWorkspaceAgent.name, + defaultIsOpen: true, +} diff --git a/site/src/components/PortForwardButton/PortForwardButton.tsx b/site/src/components/PortForwardButton/PortForwardButton.tsx new file mode 100644 index 0000000000000..4691465092a2e --- /dev/null +++ b/site/src/components/PortForwardButton/PortForwardButton.tsx @@ -0,0 +1,131 @@ +import Button from "@material-ui/core/Button" +import Link from "@material-ui/core/Link" +import Popover from "@material-ui/core/Popover" +import { makeStyles } from "@material-ui/core/styles" +import TextField from "@material-ui/core/TextField" +import OpenInNewOutlined from "@material-ui/icons/OpenInNewOutlined" +import { Stack } from "components/Stack/Stack" +import { useRef, useState } from "react" +import { colors } from "theme/colors" +import { CodeExample } from "../CodeExample/CodeExample" +import { HelpTooltipLink, HelpTooltipLinksGroup, HelpTooltipText } from "../Tooltips/HelpTooltip" + +export interface PortForwardButtonProps { + username: string + workspaceName: string + agentName: string + defaultIsOpen?: boolean +} + +export const PortForwardButton: React.FC> = ({ + workspaceName, + agentName, + username, + defaultIsOpen = false, +}) => { + const anchorRef = useRef(null) + const [isOpen, setIsOpen] = useState(defaultIsOpen) + const id = isOpen ? "schedule-popover" : undefined + const styles = useStyles() + const [port, setPort] = useState("3000") + const { location } = window + const urlExample = + process.env.CODER_ENABLE_WILDCARD_APPS === "true" + ? `${location.protocol}//${port}--${agentName}--${workspaceName}--${username}.${location.host}` + : `${location.protocol}//${location.host}/@${username}/${workspaceName}.${agentName}/apps/${port}` + + const onClose = () => { + setIsOpen(false) + } + + return ( + <> + + + + + You can port forward this resource by typing the{" "} + port, workspace name, agent name and your username in + the URL like the example below + + + + + + Or you can use the following form to open it in a new tab. + + + + { + setPort(e.currentTarget.value) + }} + /> + + + + + + + + Port forward + + + + + + ) +} + +const useStyles = makeStyles((theme) => ({ + popoverPaper: { + padding: `${theme.spacing(2.5)}px ${theme.spacing(3.5)}px ${theme.spacing(3.5)}px`, + width: theme.spacing(46), + color: theme.palette.text.secondary, + marginTop: theme.spacing(0.25), + }, + + openUrlButton: { + flexShrink: 0, + }, + + portField: { + // The default border don't contrast well with the popover + "& .MuiOutlinedInput-root .MuiOutlinedInput-notchedOutline": { + borderColor: colors.gray[10], + }, + }, +})) diff --git a/site/src/components/Resources/Resources.tsx b/site/src/components/Resources/Resources.tsx index 21a9459cc764f..380b05c5d7a65 100644 --- a/site/src/components/Resources/Resources.tsx +++ b/site/src/components/Resources/Resources.tsx @@ -10,6 +10,7 @@ import { Skeleton } from "@material-ui/lab" import useTheme from "@material-ui/styles/useTheme" import { CloseDropdown, OpenDropdown } from "components/DropdownArrows/DropdownArrows" import { ErrorSummary } from "components/ErrorSummary/ErrorSummary" +import { PortForwardButton } from "components/PortForwardButton/PortForwardButton" import { TableCellDataPrimary } from "components/TableCellData/TableCellData" import { FC, useState } from "react" import { getDisplayAgentStatus, getDisplayVersionStatus } from "util/workspace" @@ -150,6 +151,11 @@ export const Resources: FC> = ({ {canUpdateWorkspace && agent.status === "connected" && ( <> + ({ }, link: { - display: "flex", + display: "inline-flex", alignItems: "center", + width: "fit-content", }, linkIcon: { diff --git a/site/webpack.common.ts b/site/webpack.common.ts index 6c6e662493796..22d349769767c 100644 --- a/site/webpack.common.ts +++ b/site/webpack.common.ts @@ -14,9 +14,13 @@ import { Configuration, EnvironmentPlugin } from "webpack" const environmentPlugin = new EnvironmentPlugin({ INSPECT_XSTATE: "", CODER_VERSION: "main", + CODER_ENABLE_WILDCARD_APPS: "", }) console.info(`--- Setting INSPECT_XSTATE to '${process.env.INSPECT_XSTATE || ""}'`) console.info(`--- Setting CODER_VERSION to '${process.env.CODER_VERSION || "main"}'`) +console.info( + `--- Setting CODER_ENABLE_WILDCARD_APPS to '${process.env.CODER_ENABLE_WILDCARD_APPS ?? ""}'`, +) console.info(`--- Setting NODE_ENV to '${process.env.NODE_ENV || ""}'`) /**