Skip to content

chore: cleaning up workspaces table code #2765

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions site/src/components/Tooltips/HelpTooltip/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./HelpTooltip"
33 changes: 33 additions & 0 deletions site/src/components/Tooltips/OutdatedHelpTooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import RefreshIcon from "@material-ui/icons/Refresh"
import { FC } from "react"
import {
HelpTooltip,
HelpTooltipAction,
HelpTooltipLinksGroup,
HelpTooltipText,
HelpTooltipTitle,
} from "./HelpTooltip"

const Language = {
outdatedLabel: "Outdated",
versionTooltipText: "This workspace version is outdated and a newer version is available.",
updateVersionLabel: "Update version",
}

interface TooltipProps {
onUpdateVersion: () => void
}

export const OutdatedHelpTooltip: FC<TooltipProps> = ({ onUpdateVersion }) => {
return (
<HelpTooltip size="small">
<HelpTooltipTitle>{Language.outdatedLabel}</HelpTooltipTitle>
<HelpTooltipText>{Language.versionTooltipText}</HelpTooltipText>
<HelpTooltipLinksGroup>
<HelpTooltipAction icon={RefreshIcon} onClick={onUpdateVersion}>
{Language.updateVersionLabel}
</HelpTooltipAction>
</HelpTooltipLinksGroup>
</HelpTooltip>
)
}
37 changes: 37 additions & 0 deletions site/src/components/Tooltips/WorkspaceHelpTooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { FC } from "react"
import {
HelpTooltip,
HelpTooltipLink,
HelpTooltipLinksGroup,
HelpTooltipText,
HelpTooltipTitle,
} from "./HelpTooltip"

const Language = {
workspaceTooltipTitle: "What is a workspace?",
workspaceTooltipText:
"A workspace is your development environment in the cloud. It includes the infrastructure and tools you need to work on your project.",
workspaceTooltipLink1: "Create workspaces",
workspaceTooltipLink2: "Connect with SSH",
workspaceTooltipLink3: "Editors and IDEs",
}

export const WorkspaceHelpTooltip: FC = () => {
return (
<HelpTooltip>
<HelpTooltipTitle>{Language.workspaceTooltipTitle}</HelpTooltipTitle>
<HelpTooltipText>{Language.workspaceTooltipText}</HelpTooltipText>
<HelpTooltipLinksGroup>
<HelpTooltipLink href="https://github.com/coder/coder/blob/main/docs/workspaces.md#create-workspaces">
{Language.workspaceTooltipLink1}
</HelpTooltipLink>
<HelpTooltipLink href="https://github.com/coder/coder/blob/main/docs/workspaces.md#connect-with-ssh">
{Language.workspaceTooltipLink2}
</HelpTooltipLink>
<HelpTooltipLink href="https://github.com/coder/coder/blob/main/docs/workspaces.md#editors-and-ides">
{Language.workspaceTooltipLink3}
</HelpTooltipLink>
</HelpTooltipLinksGroup>
</HelpTooltip>
)
}
4 changes: 4 additions & 0 deletions site/src/components/Tooltips/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { AgentHelpTooltip } from "./AgentHelpTooltip"
export { OutdatedHelpTooltip } from "./OutdatedHelpTooltip"
export { ResourcesHelpTooltip } from "./ResourcesHelpTooltip"
export { WorkspaceHelpTooltip } from "./WorkspaceHelpTooltip"
2 changes: 1 addition & 1 deletion site/src/components/UsersTable/UsersTableBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export const UsersTableBody: FC<UsersTableBodyProps> = ({
return <TableLoader />
}

if (!users || users.length === 0) {
if (!users || !users.length) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice 👍🏼!

return (
<TableRow>
<TableCell colSpan={999}>
Expand Down
107 changes: 107 additions & 0 deletions site/src/components/WorkspacesTable/WorkspacesRow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { fade, makeStyles, Theme } from "@material-ui/core/styles"
import TableRow from "@material-ui/core/TableRow"
import KeyboardArrowRight from "@material-ui/icons/KeyboardArrowRight"
import useTheme from "@material-ui/styles/useTheme"
import { useActor } from "@xstate/react"
import dayjs from "dayjs"
import relativeTime from "dayjs/plugin/relativeTime"
import { FC } from "react"
import { useNavigate } from "react-router-dom"
import { getDisplayStatus } from "../../util/workspace"
import { WorkspaceItemMachineRef } from "../../xServices/workspaces/workspacesXService"
import { AvatarData } from "../AvatarData/AvatarData"
import { TableCellLink } from "../TableCellLink/TableCellLink"
import { OutdatedHelpTooltip } from "../Tooltips"

dayjs.extend(relativeTime)

const Language = {
upToDateLabel: "Up to date",
outdatedLabel: "Outdated",
}

export const WorkspacesRow: FC<{ workspaceRef: WorkspaceItemMachineRef }> = ({ workspaceRef }) => {
const styles = useStyles()
const navigate = useNavigate()
const theme: Theme = useTheme()
const [workspaceState, send] = useActor(workspaceRef)
const { data: workspace } = workspaceState.context
const status = getDisplayStatus(theme, workspace.latest_build)
const workspacePageLink = `/@${workspace.owner_name}/${workspace.name}`

return (
<TableRow
hover
data-testid={`workspace-${workspace.id}`}
tabIndex={0}
onKeyDown={(event) => {
if (event.key === "Enter") {
navigate(workspacePageLink)
}
}}
className={styles.clickableTableRow}
>
<TableCellLink to={workspacePageLink}>
<AvatarData title={workspace.name} subtitle={workspace.owner_name} />
</TableCellLink>
<TableCellLink to={workspacePageLink}>{workspace.template_name}</TableCellLink>
<TableCellLink to={workspacePageLink}>
{workspace.outdated ? (
<span className={styles.outdatedLabel}>
{Language.outdatedLabel}
<OutdatedHelpTooltip
onUpdateVersion={() => {
send("UPDATE_VERSION")
}}
/>
</span>
) : (
<span style={{ color: theme.palette.text.secondary }}>{Language.upToDateLabel}</span>
)}
</TableCellLink>
<TableCellLink to={workspacePageLink}>
<span data-chromatic="ignore" style={{ color: theme.palette.text.secondary }}>
{dayjs().to(dayjs(workspace.latest_build.created_at))}
</span>
</TableCellLink>
<TableCellLink to={workspacePageLink}>
<span style={{ color: status.color }}>{status.status}</span>
</TableCellLink>
<TableCellLink to={workspacePageLink}>
<div className={styles.arrowCell}>
<KeyboardArrowRight className={styles.arrowRight} />
</div>
</TableCellLink>
</TableRow>
)
}

const useStyles = makeStyles((theme) => ({
clickableTableRow: {
"&:hover td": {
backgroundColor: fade(theme.palette.primary.light, 0.1),
},

"&:focus": {
outline: `1px solid ${theme.palette.secondary.dark}`,
},

"& .MuiTableCell-root:last-child": {
paddingRight: theme.spacing(2),
},
},
arrowRight: {
color: fade(theme.palette.primary.contrastText, 0.7),
width: 20,
height: 20,
},
arrowCell: {
display: "flex",
},
outdatedLabel: {
color: theme.palette.error.main,
display: "flex",
alignItems: "center",
gap: theme.spacing(0.5),
},
}))
42 changes: 42 additions & 0 deletions site/src/components/WorkspacesTable/WorkspacesTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import Table from "@material-ui/core/Table"
import TableBody from "@material-ui/core/TableBody"
import TableCell from "@material-ui/core/TableCell"
import TableHead from "@material-ui/core/TableHead"
import TableRow from "@material-ui/core/TableRow"
import { FC } from "react"
import { WorkspaceItemMachineRef } from "../../xServices/workspaces/workspacesXService"
import { WorkspacesTableBody } from "./WorkspacesTableBody"

const Language = {
name: "Name",
template: "Template",
version: "Version",
lastBuilt: "Last Built",
status: "Status",
}

export interface WorkspacesTableProps {
isLoading?: boolean
workspaceRefs?: WorkspaceItemMachineRef[]
filter?: string
}

export const WorkspacesTable: FC<WorkspacesTableProps> = ({ isLoading, workspaceRefs, filter }) => {
return (
<Table>
<TableHead>
<TableRow>
<TableCell width="35%">{Language.name}</TableCell>
<TableCell width="15%">{Language.template}</TableCell>
<TableCell width="15%">{Language.version}</TableCell>
<TableCell width="20%">{Language.lastBuilt}</TableCell>
<TableCell width="15%">{Language.status}</TableCell>
<TableCell width="1%"></TableCell>
</TableRow>
</TableHead>
<TableBody>
<WorkspacesTableBody isLoading={isLoading} workspaceRefs={workspaceRefs} filter={filter} />
</TableBody>
</Table>
)
}
69 changes: 69 additions & 0 deletions site/src/components/WorkspacesTable/WorkspacesTableBody.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import Button from "@material-ui/core/Button"
import Link from "@material-ui/core/Link"
import TableCell from "@material-ui/core/TableCell"
import TableRow from "@material-ui/core/TableRow"
import AddCircleOutline from "@material-ui/icons/AddCircleOutline"
import { FC } from "react"
import { Link as RouterLink } from "react-router-dom"
import { workspaceFilterQuery } from "../../util/filters"
import { WorkspaceItemMachineRef } from "../../xServices/workspaces/workspacesXService"
import { EmptyState } from "../EmptyState/EmptyState"
import { TableLoader } from "../TableLoader/TableLoader"
import { WorkspacesRow } from "./WorkspacesRow"

export const Language = {
emptyCreateWorkspaceMessage: "Create your first workspace",
emptyCreateWorkspaceDescription: "Start editing your source code and building your software.",
createFromTemplateButton: "Create from template",
emptyResultsMessage: "No results matched your search",
}

interface TableBodyProps {
isLoading?: boolean
workspaceRefs?: WorkspaceItemMachineRef[]
filter?: string
}

export const WorkspacesTableBody: FC<TableBodyProps> = ({ isLoading, workspaceRefs, filter }) => {
if (isLoading) {
return <TableLoader />
}

if (!workspaceRefs || !workspaceRefs.length) {
return (
<>
{filter === workspaceFilterQuery.me || filter === workspaceFilterQuery.all ? (
<TableRow>
<TableCell colSpan={999}>
<EmptyState
message={Language.emptyCreateWorkspaceMessage}
description={Language.emptyCreateWorkspaceDescription}
cta={
<Link underline="none" component={RouterLink} to="/templates">
<Button startIcon={<AddCircleOutline />}>
{Language.createFromTemplateButton}
</Button>
</Link>
}
/>
</TableCell>
</TableRow>
) : (
<TableRow>
<TableCell colSpan={999}>
<EmptyState message={Language.emptyResultsMessage} />
</TableCell>
</TableRow>
)}
</>
)
}

return (
<>
{workspaceRefs.map((workspaceRef) => (
<WorkspacesRow workspaceRef={workspaceRef} key={workspaceRef.id} />
))}
</>
)
}
2 changes: 1 addition & 1 deletion site/src/pages/UsersPage/UsersPageView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export const UsersPageView: FC<UsersPageViewProps> = ({
) : undefined
}
>
<PageHeaderTitle>Users</PageHeaderTitle>
<PageHeaderTitle>{Language.pageTitle}</PageHeaderTitle>
</PageHeader>

<SearchBarWithFilter
Expand Down
4 changes: 2 additions & 2 deletions site/src/pages/WorkspacesPage/WorkspacesPage.test.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { screen } from "@testing-library/react"
import { rest } from "msw"
import { Language as WorkspacesTableBodyLanguage } from "../../components/WorkspacesTable/WorkspacesTableBody"
import { MockWorkspace } from "../../testHelpers/entities"
import { history, render } from "../../testHelpers/renderHelpers"
import { server } from "../../testHelpers/server"
import WorkspacesPage from "./WorkspacesPage"
import { Language } from "./WorkspacesPageView"

describe("WorkspacesPage", () => {
beforeEach(() => {
Expand All @@ -23,7 +23,7 @@ describe("WorkspacesPage", () => {
render(<WorkspacesPage />)

// Then
await screen.findByText(Language.emptyCreateWorkspaceMessage)
await screen.findByText(WorkspacesTableBodyLanguage.emptyCreateWorkspaceMessage)
})

it("renders a filled workspaces page", async () => {
Expand Down
2 changes: 1 addition & 1 deletion site/src/pages/WorkspacesPage/WorkspacesPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const WorkspacesPage: FC = () => {

<WorkspacesPageView
filter={workspacesState.context.filter}
loading={workspacesState.hasTag("loading")}
isLoading={workspacesState.hasTag("loading")}
workspaceRefs={workspaceRefs}
onFilter={(query) => {
setSearchParams({ filter: query })
Expand Down
Loading