Skip to content

fix: Wrap TableCell with Link for native browser routing #2532

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 1 commit into from
Jun 21, 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
29 changes: 12 additions & 17 deletions site/src/components/BuildsTable/BuildsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { useNavigate, useParams } from "react-router-dom"
import * as TypesGen from "../../api/typesGenerated"
import { displayWorkspaceBuildDuration, getDisplayWorkspaceBuildStatus } from "../../util/workspace"
import { EmptyState } from "../EmptyState/EmptyState"
import { TableCellLink } from "../TableCellLink/TableCellLink"
import { TableLoader } from "../TableLoader/TableLoader"

export const Language = {
Expand Down Expand Up @@ -51,42 +52,38 @@ export const BuildsTable: FC<BuildsTableProps> = ({ builds, className }) => {
{builds &&
builds.map((build) => {
const status = getDisplayWorkspaceBuildStatus(theme, build)

const navigateToBuildPage = () => {
navigate(`/@${username}/${workspaceName}/builds/${build.build_number}`)
}
const buildPageLink = `/@${username}/${workspaceName}/builds/${build.build_number}`

return (
<TableRow
hover
key={build.id}
data-testid={`build-${build.id}`}
tabIndex={0}
onClick={navigateToBuildPage}
onKeyDown={(event) => {
if (event.key === "Enter") {
navigateToBuildPage()
navigate(buildPageLink)
}
}}
className={styles.clickableTableRow}
>
<TableCell>{build.transition}</TableCell>
<TableCell>
<TableCellLink to={buildPageLink}>{build.transition}</TableCellLink>
<TableCellLink to={buildPageLink}>
<span style={{ color: theme.palette.text.secondary }}>{displayWorkspaceBuildDuration(build)}</span>
</TableCell>
<TableCell>
</TableCellLink>
<TableCellLink to={buildPageLink}>
<span style={{ color: theme.palette.text.secondary }}>
{new Date(build.created_at).toLocaleString()}
</span>
</TableCell>
<TableCell>
</TableCellLink>
<TableCellLink to={buildPageLink}>
<span style={{ color: status.color }}>{status.status}</span>
</TableCell>
<TableCell>
</TableCellLink>
<TableCellLink to={buildPageLink}>
<div className={styles.arrowCell}>
<KeyboardArrowRight className={styles.arrowRight} />
</div>
</TableCell>
</TableCellLink>
</TableRow>
)
})}
Expand All @@ -107,8 +104,6 @@ export const BuildsTable: FC<BuildsTableProps> = ({ builds, className }) => {

const useStyles = makeStyles((theme) => ({
clickableTableRow: {
cursor: "pointer",

Comment on lines -110 to -111
Copy link
Contributor

Choose a reason for hiding this comment

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

?: why are we removing this?

Copy link
Member Author

Choose a reason for hiding this comment

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

The pointer still exists, it's just provided by the "a" tag now!

"&:hover td": {
backgroundColor: fade(theme.palette.primary.light, 0.1),
},
Expand Down
47 changes: 47 additions & 0 deletions site/src/components/TableCellLink/TableCellLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import Link from "@material-ui/core/Link"
import { makeStyles } from "@material-ui/core/styles"
import TableCell, { TableCellProps } from "@material-ui/core/TableCell"
import { Link as RouterLink } from "react-router-dom"
import { combineClasses } from "../../util/combineClasses"

// TableCellLink wraps a TableCell filling the entirety with a Link.
// This allows table rows to be clickable with browser-behavior like ctrl+click.
Copy link
Contributor

Choose a reason for hiding this comment

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

👍🏼

export const TableCellLink: React.FC<
TableCellProps & {
to: string
}
> = (props) => {
const styles = useStyles()

return (
<TableCell className={styles.cell} {...props}>
<Link
component={RouterLink}
to={props.to}
className={combineClasses([styles.link, "MuiTableCell-root", "MuiTableCell-body"])}
>
{props.children}
</Link>
</TableCell>
)
}

const useStyles = makeStyles((theme) => ({
cell: {
// This must override all padding for all rules on a TableCell.
// Otherwise, the link will not cover the entire region.
// It's unfortuante to use `!important`, but this seems to be
// a reasonable use-case.
padding: "0 !important",
Copy link
Member

Choose a reason for hiding this comment

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

Why do we need !important in these styles?

Copy link
Member Author

Choose a reason for hiding this comment

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

Unfortunately the MUI styles will override, and that can't work for the padding (because this needs to fill the entire cell).

Copy link
Member

Choose a reason for hiding this comment

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

Got it. I wonder if we could use specificity in class naming to get around this. Not a blocker for this PR.

Copy link
Member Author

Choose a reason for hiding this comment

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

I shall try!

},
link: {
display: "block",
width: "100%",
border: "none",
background: "none",
paddingTop: theme.spacing(2),
paddingBottom: theme.spacing(2),
// This is required to hide all underlines for child elements!
textDecoration: "none !important",
Copy link
Contributor

Choose a reason for hiding this comment

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

!important generally is not a good pattern. I wonder if there's a way around this.

Copy link
Contributor

Choose a reason for hiding this comment

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

Could we get more specific with a selector?

},
}))
28 changes: 14 additions & 14 deletions site/src/pages/TemplatesPage/TemplatesPageView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { EmptyState } from "../../components/EmptyState/EmptyState"
import { Margins } from "../../components/Margins/Margins"
import { PageHeader, PageHeaderSubtitle, PageHeaderTitle } from "../../components/PageHeader/PageHeader"
import { Stack } from "../../components/Stack/Stack"
import { TableCellLink } from "../../components/TableCellLink/TableCellLink"
import { TableLoader } from "../../components/TableLoader/TableLoader"
import {
HelpTooltip,
Expand Down Expand Up @@ -115,36 +116,37 @@ export const TemplatesPageView: FC<TemplatesPageViewProps> = (props) => {
</TableRow>
)}
{props.templates?.map((template) => {
const navigateToTemplatePage = () => {
navigate(`/templates/${template.name}`)
}
const templatePageLink = `/templates/${template.name}`
return (
<TableRow
key={template.id}
hover
data-testid={`template-${template.id}`}
tabIndex={0}
onClick={navigateToTemplatePage}
onKeyDown={(event) => {
if (event.key === "Enter") {
navigateToTemplatePage()
navigate(templatePageLink)
}
}}
className={styles.clickableTableRow}
>
<TableCell>
<TableCellLink to={templatePageLink}>
<AvatarData title={template.name} subtitle={template.description} />
</TableCell>
</TableCellLink>

<TableCell>{Language.developerCount(template.workspace_owner_count)}</TableCell>
<TableCellLink to={templatePageLink}>
{Language.developerCount(template.workspace_owner_count)}
</TableCellLink>

<TableCell data-chromatic="ignore">{dayjs().to(dayjs(template.updated_at))}</TableCell>
<TableCell>{template.created_by_name}</TableCell>
<TableCell>
<TableCellLink data-chromatic="ignore" to={templatePageLink}>
{dayjs().to(dayjs(template.updated_at))}
</TableCellLink>
<TableCellLink to={templatePageLink}>{template.created_by_name}</TableCellLink>
<TableCellLink to={templatePageLink}>
<div className={styles.arrowCell}>
<KeyboardArrowRight className={styles.arrowRight} />
</div>
</TableCell>
</TableCellLink>
</TableRow>
)
})}
Expand All @@ -159,8 +161,6 @@ const useStyles = makeStyles((theme) => ({
maxWidth: theme.spacing(62),
},
clickableTableRow: {
cursor: "pointer",

"&:hover td": {
backgroundColor: fade(theme.palette.primary.light, 0.1),
},
Expand Down
33 changes: 15 additions & 18 deletions site/src/pages/WorkspacesPage/WorkspacesPageView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { EmptyState } from "../../components/EmptyState/EmptyState"
import { Margins } from "../../components/Margins/Margins"
import { PageHeader, PageHeaderSubtitle, PageHeaderTitle } from "../../components/PageHeader/PageHeader"
import { Stack } from "../../components/Stack/Stack"
import { TableCellLink } from "../../components/TableCellLink/TableCellLink"
import { TableLoader } from "../../components/TableLoader/TableLoader"
import {
HelpTooltip,
Expand Down Expand Up @@ -104,27 +105,25 @@ const WorkspaceRow: React.FC<{ workspaceRef: WorkspaceItemMachineRef }> = ({ wor
const [workspaceState, send] = useActor(workspaceRef)
const { data: workspace } = workspaceState.context
const status = getDisplayStatus(theme, workspace.latest_build)
const navigateToWorkspacePage = () => {
navigate(`/@${workspace.owner_name}/${workspace.name}`)
}
const workspacePageLink = `/@${workspace.owner_name}/${workspace.name}`

return (
<TableRow
hover
data-testid={`workspace-${workspace.id}`}
tabIndex={0}
onClick={navigateToWorkspacePage}
onKeyDown={(event) => {
if (event.key === "Enter") {
navigateToWorkspacePage()
navigate(workspacePageLink)
}
}}
className={styles.clickableTableRow}
>
<TableCell>
<TableCellLink to={workspacePageLink}>
<AvatarData title={workspace.name} subtitle={workspace.owner_name} />
</TableCell>
<TableCell>{workspace.template_name}</TableCell>
<TableCell>
</TableCellLink>
<TableCellLink to={workspacePageLink}>{workspace.template_name}</TableCellLink>
<TableCellLink to={workspacePageLink}>
{workspace.outdated ? (
<span className={styles.outdatedLabel}>
{Language.outdatedLabel}
Expand All @@ -137,20 +136,20 @@ const WorkspaceRow: React.FC<{ workspaceRef: WorkspaceItemMachineRef }> = ({ wor
) : (
<span style={{ color: theme.palette.text.secondary }}>{Language.upToDateLabel}</span>
)}
</TableCell>
<TableCell>
</TableCellLink>
<TableCellLink to={workspacePageLink}>
<span data-chromatic="ignore" style={{ color: theme.palette.text.secondary }}>
{dayjs().to(dayjs(workspace.latest_build.created_at))}
</span>
</TableCell>
<TableCell>
</TableCellLink>
<TableCellLink to={workspacePageLink}>
<span style={{ color: status.color }}>{status.status}</span>
</TableCell>
<TableCell>
</TableCellLink>
<TableCellLink to={workspacePageLink}>
<div className={styles.arrowCell}>
<KeyboardArrowRight className={styles.arrowRight} />
</div>
</TableCell>
</TableCellLink>
</TableRow>
)
}
Expand Down Expand Up @@ -349,8 +348,6 @@ const useStyles = makeStyles((theme) => ({
},
},
clickableTableRow: {
cursor: "pointer",

"&:hover td": {
backgroundColor: fade(theme.palette.primary.light, 0.1),
},
Expand Down
6 changes: 4 additions & 2 deletions site/src/theme/overrides.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,12 @@ export const getOverrides = (palette: PaletteOptions) => {
background: palette.background?.paper,
borderBottom: `1px solid ${palette.divider}`,
padding: 8,
"&:first-child": {
// This targets the first+last td elements, and also the first+last elements
// of a TableCellLink.
"&:not(:only-child):first-child, &:not(:only-child):first-child > a": {
paddingLeft: 32,
},
"&:last-child": {
"&:not(:only-child):last-child, &:not(:only-child):last-child > a": {
paddingRight: 32,
},
},
Expand Down