Skip to content

Commit

Permalink
Accessibility fixes for Table Overview (#4716)
Browse files Browse the repository at this point in the history
* web: show star on keyboard focus

* web: more consistent React component names

* web: allow keyboard focus on Table Overview name

* web: clarify tooltips for Trigger and Trigger Mode

* web: visual tweaks for Endpoints

* web: Add tooltip for Copy Pod ID

* web: Table status takes you to resource logs

* web: Table should not show Trigger Mode for Tiltfile

* web: Table headers easier to read

* web: fix failing test

* web: handle long endpoint text

* web: revert containing div for endpoints

Co-authored-by: Han Yu <han@tilt.dev>
  • Loading branch information
hyu and Han Yu committed Jul 1, 2021
1 parent 43a3e7e commit e49c9e9
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 58 deletions.
55 changes: 36 additions & 19 deletions web/src/OverviewTable.tsx
Expand Up @@ -9,10 +9,10 @@ import { ReactComponent as CopySvg } from "./assets/svg/copy.svg"
import { ReactComponent as LinkSvg } from "./assets/svg/link.svg"
import { InstrumentedButton } from "./instrumentedComponents"
import { displayURL } from "./links"
import TableStarResourceButton from "./OverviewTableStarResourceButton"
import OverviewTableStarResourceButton from "./OverviewTableStarResourceButton"
import OverviewTableStatus from "./OverviewTableStatus"
import TriggerButton from "./OverviewTableTriggerButton"
import TableTriggerModeToggle from "./OverviewTableTriggerModeToggle"
import OverviewTableTriggerButton from "./OverviewTableTriggerButton"
import OverviewTableTriggerModeToggle from "./OverviewTableTriggerModeToggle"
import { useResourceNav } from "./ResourceNav"
import { useStarredResources } from "./StarredResourcesContext"
import { buildStatus, runtimeStatus } from "./status"
Expand Down Expand Up @@ -82,7 +82,7 @@ const ResourceTableData = styled.td`
`
const ResourceTableHeader = styled(ResourceTableData)`
color: ${Color.gray7};
font-size: ${FontSize.smallester};
font-size: ${FontSize.smallest};
padding-top: ${SizeUnit(0.5)};
padding-bottom: ${SizeUnit(0.5)};
box-sizing: border-box;
Expand All @@ -105,7 +105,8 @@ const ResourceTableHeaderSortTriangle = styled.div`
transform: rotate(180deg);
}
`
const ResourceName = styled.div`
const ResourceName = styled.button`
${mixinResetButtonStyle};
color: ${Color.offWhite};
font-size: ${FontSize.small};
padding-top: ${SizeUnit(1 / 3)};
Expand All @@ -122,14 +123,20 @@ const ResourceName = styled.div`
}
`

const Endpoint = styled.a``
const StyledLinkSvg = styled(LinkSvg)``

const DetailText = styled.span`
const Endpoint = styled.a`
display: flex;
align-items: center;
max-width: 150px;
`
const DetailText = styled.div`
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-left: 10px;
`

const StyledLinkSvg = styled(LinkSvg)`
fill: ${Color.grayLight};
margin-right: ${SizeUnit(0.2)};
`

const PodId = styled.div`
Expand Down Expand Up @@ -163,7 +170,7 @@ const PodIdCopy = styled(InstrumentedButton)`
function TableStarColumn({ row }: CellProps<RowValues>) {
let ctx = useStarredResources()
return (
<TableStarResourceButton
<OverviewTableStarResourceButton
resourceName={row.values.name}
analyticsName="ui.web.overviewStarButton"
ctx={ctx}
Expand All @@ -179,7 +186,7 @@ function TableUpdateColumn({ row }: CellProps<RowValues>) {

function TableTriggerColumn({ row }: CellProps<RowValues>) {
return (
<TriggerButton
<OverviewTableTriggerButton
hasPendingChanges={row.values.trigger.hasPendingChanges}
hasBuilt={row.values.trigger.hasBuilt}
isBuilding={row.values.trigger.isBuilding}
Expand Down Expand Up @@ -214,10 +221,12 @@ function TableStatusColumn({ row }: CellProps<RowValues>) {
lastBuildDur={row.values.statusLine.lastBuildDur}
alertCount={row.values.statusLine.buildAlertCount}
isBuild={true}
resourceName={row.values.name}
/>
<OverviewTableStatus
status={row.values.statusLine.runtimeStatus}
alertCount={row.values.statusLine.runtimeAlertCount}
resourceName={row.values.name}
/>
</>
)
Expand Down Expand Up @@ -258,16 +267,19 @@ function TablePodIDColumn({ row }: CellProps<RowValues>) {
readOnly={true}
onClick={() => selectPodIdInput(row.values.podId)}
/>
<PodIdCopy onClick={copyClick} analyticsName="ui.web.overview.copyPodID">
<PodIdCopy
onClick={copyClick}
analyticsName="ui.web.overview.copyPodID"
title="Copy Pod ID"
>
{icon}
</PodIdCopy>
</PodId>
)
}

function TableEndpointColumn({ row }: CellProps<RowValues>) {
// @ts-ignore
let endpoints = row.values.endpoints.map((ep) => {
let endpoints = row.values.endpoints.map((ep: any) => {
return (
<Endpoint
onClick={() => void incr("ui.web.endpoint", { action: "click" })}
Expand All @@ -277,16 +289,21 @@ function TableEndpointColumn({ row }: CellProps<RowValues>) {
key={ep.url}
>
<StyledLinkSvg />
<DetailText>{ep.name || displayURL(ep)}</DetailText>
<DetailText title={ep.name || displayURL(ep)}>
{ep.name || displayURL(ep)}
</DetailText>
</Endpoint>
)
})
return <div>{endpoints}</div>
return <>{endpoints}</>
}

function TableTriggerModeColumn({ row }: CellProps<RowValues>) {
let isTiltfile = row.values.name == "(Tiltfile)"

if (isTiltfile) return null
return (
<TableTriggerModeToggle
<OverviewTableTriggerModeToggle
resourceName={row.values.name}
triggerMode={row.values.triggerMode}
/>
Expand Down Expand Up @@ -345,7 +362,7 @@ const columns: Column<RowValues>[] = [
{
Header: "Trigger Mode",
accessor: "triggerMode",
width: "50px",
width: "70px",
Cell: TableTriggerModeColumn,
},
]
Expand Down
3 changes: 2 additions & 1 deletion web/src/OverviewTableStarResourceButton.tsx
Expand Up @@ -31,7 +31,8 @@ let StyledStarSvg = styled(StarSvg)`
opacity: 0;
fill: ${Color.grayLight};
}
&.is-unstarred:hover {
&.is-unstarred:hover,
${StyledTableStarResourceButton}:focus &.is-unstarred {
opacity: 1;
}
`
Expand Down
61 changes: 40 additions & 21 deletions web/src/OverviewTableStatus.tsx
Expand Up @@ -3,27 +3,32 @@ import styled from "styled-components"
import { ReactComponent as CheckmarkSmallSvg } from "./assets/svg/checkmark-small.svg"
import { ReactComponent as CloseSvg } from "./assets/svg/close.svg"
import { ReactComponent as PendingSvg } from "./assets/svg/pending.svg"
import { Color, Glow, SizeUnit, spin } from "./style-helpers"
import { useResourceNav } from "./ResourceNav"
import {
Color,
Glow,
mixinResetButtonStyle,
SizeUnit,
spin,
} from "./style-helpers"
import { formatBuildDuration } from "./time"
import { ResourceStatus } from "./types"

const StyledOverviewTableStatus = styled.div``

const StatusIcon = styled.span`
const StatusMsg = styled.span``
const StyledOverviewTableStatus = styled.button`
${mixinResetButtonStyle};
color: inherit;
display: flex;
align-items: center;
margin-right: ${SizeUnit(0.2)};
width: ${SizeUnit(0.5)};
svg {
width: 100%;
& + & {
margin-top: ${SizeUnit(0.15)};
}
`
const StatusMsg = styled.span``
const StatusLine = styled.div`
display: flex;
align-items: center;
&:hover ${StatusMsg} {
text-decoration: underline;
text-underline-position: under;
}
&.is-healthy {
svg {
fill: ${Color.green};
Expand Down Expand Up @@ -52,16 +57,27 @@ const StatusLine = styled.div`
color: ${Color.grayLight};
}
`
const StatusIcon = styled.span`
display: flex;
align-items: center;
margin-right: ${SizeUnit(0.2)};
width: ${SizeUnit(0.5)};
svg {
width: 100%;
}
`

type OverviewTableStatusProps = {
status: ResourceStatus
resourceName: string
lastBuildDur?: moment.Duration | null
alertCount: number
isBuild?: boolean
}

export default function OverviewTableStatus(props: OverviewTableStatusProps) {
let { status, lastBuildDur, alertCount, isBuild } = props
let { status, lastBuildDur, alertCount, isBuild, resourceName } = props
let icon = null
let msg = ""
let classes = ""
Expand Down Expand Up @@ -102,14 +118,17 @@ export default function OverviewTableStatus(props: OverviewTableStatusProps) {
msg = ""
}

let nav = useResourceNav()

if (!msg) return null

return (
<StyledOverviewTableStatus>
{msg && (
<StatusLine className={classes}>
<StatusIcon>{icon}</StatusIcon>
<StatusMsg>{msg}</StatusMsg>
</StatusLine>
)}
<StyledOverviewTableStatus
className={classes}
onClick={() => void nav.openResource(resourceName)}
>
<StatusIcon>{icon}</StatusIcon>
<StatusMsg>{msg}</StatusMsg>
</StyledOverviewTableStatus>
)
}
10 changes: 5 additions & 5 deletions web/src/OverviewTableTriggerButton.tsx
Expand Up @@ -49,10 +49,10 @@ export let TriggerButtonRoot = styled(InstrumentedButton)`
`

export const TriggerButtonTooltip = {
AlreadyQueued: "Resource is already queued!",
NeedsManualTrigger: "Trigger an update",
UpdateInProgOrPending: "Resource is already updating!",
ClickToForce: "Force an update",
AlreadyQueued: "Resource already queued!",
NeedsManualTrigger: "Trigger update to sync changes",
UpdateInProgOrPending: "Resource already updating!",
Default: "Trigger update",
}

type TriggerButtonProps = {
Expand All @@ -76,7 +76,7 @@ const titleText = (
} else if (shouldbeClicked) {
return TriggerButtonTooltip.NeedsManualTrigger
} else {
return TriggerButtonTooltip.ClickToForce
return TriggerButtonTooltip.Default
}
}

Expand Down
4 changes: 2 additions & 2 deletions web/src/OverviewTableTriggerModeToggle.tsx
Expand Up @@ -46,8 +46,8 @@ type TriggerModeToggleProps = {
}

export const ToggleTriggerModeTooltip = {
isManual: "File changes do not trigger updates",
isAuto: "File changes trigger update",
isManual: "Manual: File changes don’t trigger updates",
isAuto: "Auto: File changes trigger update",
}

const titleText = (isManual: boolean): string => {
Expand Down
6 changes: 3 additions & 3 deletions web/src/SidebarTriggerButton.test.tsx
Expand Up @@ -177,7 +177,7 @@ describe("SidebarTriggerButton", () => {
expectClickable(b1, true)
expectManualTriggerIcon(b1, false)
expectIsQueued(b1, false)
expectWithTooltip(b1, TriggerButtonTooltip.ClickToForce)
expectWithTooltip(b1, TriggerButtonTooltip.Default)
})

it("shows selected trigger button for selected resource", () => {
Expand Down Expand Up @@ -255,7 +255,7 @@ describe("SidebarTriggerButton", () => {
expectClickable(b1, true)
expectManualTriggerIcon(b1, false)
expectIsQueued(b1, false)
expectWithTooltip(b1, TriggerButtonTooltip.ClickToForce)
expectWithTooltip(b1, TriggerButtonTooltip.Default)
})

it("trigger button not clickable if resource is building", () => {
Expand Down Expand Up @@ -363,7 +363,7 @@ describe("SidebarTriggerButton", () => {
expectClickable(button, true)
expectManualTriggerIcon(button, false)
expectIsQueued(button, false)
expectWithTooltip(button, TriggerButtonTooltip.ClickToForce)
expectWithTooltip(button, TriggerButtonTooltip.Default)
})

it("shows trigger button for Tiltfile", () => {
Expand Down
13 changes: 6 additions & 7 deletions web/src/SidebarTriggerButton.tsx
Expand Up @@ -71,13 +71,12 @@ export let SidebarTriggerButtonRoot = styled(InstrumentedButton)`
}
`

// TODO: Some of these aren't currently visible, since Trigger button is hidden when building
export const TriggerButtonTooltip = {
AlreadyQueued:
"Cannot trigger an update if resource is already queued for update.",
NeedsManualTrigger: "Click to trigger an update.",
UpdateInProgOrPending:
"Cannot trigger an update while resource is updating or update is pending.",
ClickToForce: "Force an update for this resource.",
AlreadyQueued: "Resource already queued!",
NeedsManualTrigger: "Trigger update to sync changes",
UpdateInProgOrPending: "Resource already updating!",
Default: "Trigger update",
}

type SidebarTriggerButtonProps = {
Expand All @@ -103,7 +102,7 @@ const titleText = (
} else if (clickMe) {
return TriggerButtonTooltip.NeedsManualTrigger
} else {
return TriggerButtonTooltip.ClickToForce
return TriggerButtonTooltip.Default
}
}

Expand Down

0 comments on commit e49c9e9

Please sign in to comment.