Skip to content

Commit

Permalink
Add table row action to copy silo ID from system utilization page (#2414
Browse files Browse the repository at this point in the history
)
  • Loading branch information
charliepark authored Sep 5, 2024
1 parent 12fc862 commit c2909e7
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 49 deletions.
6 changes: 6 additions & 0 deletions app/pages/system/UtilizationPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { QueryParamTabs } from '~/components/QueryParamTabs'
import { useIntervalPicker } from '~/components/RefetchIntervalPicker'
import { SystemMetric } from '~/components/SystemMetric'
import { LinkCell } from '~/table/cells/LinkCell'
import { RowActions } from '~/table/columns/action-col'
import { Listbox } from '~/ui/lib/Listbox'
import { PageHeader, PageTitle } from '~/ui/lib/PageHeader'
import { ResourceMeter } from '~/ui/lib/ResourceMeter'
Expand Down Expand Up @@ -168,6 +169,7 @@ function UsageTab() {
<Table.HeadCell colSpan={3} data-test-ignore>
Available
</Table.HeadCell>
<Table.HeadCell data-test-ignore></Table.HeadCell>
</Table.HeaderRow>
<Table.HeaderRow>
<Table.HeadCell data-test-ignore></Table.HeadCell>
Expand All @@ -177,6 +179,7 @@ function UsageTab() {
<Table.HeadCell>CPU</Table.HeadCell>
<Table.HeadCell>Memory</Table.HeadCell>
<Table.HeadCell>Storage</Table.HeadCell>
<Table.HeadCell data-test-ignore></Table.HeadCell>
</Table.HeaderRow>
</Table.Header>
<Table.Body>
Expand Down Expand Up @@ -225,6 +228,9 @@ function UsageTab() {
unit="TiB"
/>
</Table.Cell>
<Table.Cell className="action-col w-10 children:p-0" height="large">
<RowActions id={silo.siloId} copyIdLabel="Copy silo ID" />
</Table.Cell>
</Table.Row>
))}
</Table.Body>
Expand Down
106 changes: 59 additions & 47 deletions app/table/columns/action-col.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,54 +50,66 @@ export const getActionsCol = <TData extends Record<string, unknown>>(
// TODO: control flow here has always confused me, would like to straighten it out
const actions = makeActions(row.original)
const id = typeof row.original.id === 'string' ? row.original.id : null
return (
<DropdownMenu.Root>
{/* TODO: This name should not suck; future us, make it so! */}
{/* stopPropagation prevents clicks from toggling row select in a single select table */}
<DropdownMenu.Trigger
className="flex h-full w-10 items-center justify-center"
aria-label="Row actions"
onClick={(e) => e.stopPropagation()}
>
<More12Icon className="text-tertiary" />
</DropdownMenu.Trigger>
{/* portal fixes mysterious z-index issue where menu is behind button */}
<DropdownMenu.Portal>
<DropdownMenu.Content align="end" className="-mt-3 mr-2">
{id && (
return <RowActions id={id} actions={actions} />
},
}
}

type RowActionsProps = {
/** If `id` is provided, a `Copy ID` menu item will be automatically included. */
id?: string | null
/** Use `copyIdLabel` to override the default label (`Copy ID`). */
copyIdLabel?: string
actions?: MenuAction[]
}

export const RowActions = ({ id, copyIdLabel = 'Copy ID', actions }: RowActionsProps) => {
return (
<DropdownMenu.Root>
{/* TODO: This name should not suck; future us, make it so! */}
{/* stopPropagation prevents clicks from toggling row select in a single select table */}
<DropdownMenu.Trigger
className="flex h-full w-10 items-center justify-center"
aria-label="Row actions"
onClick={(e) => e.stopPropagation()}
>
<More12Icon className="text-tertiary" />
</DropdownMenu.Trigger>
{/* portal fixes mysterious z-index issue where menu is behind button */}
<DropdownMenu.Portal>
<DropdownMenu.Content align="end" className="-mt-3 mr-2">
{id && (
<DropdownMenu.Item
onSelect={() => {
window.navigator.clipboard.writeText(id)
}}
>
{copyIdLabel}
</DropdownMenu.Item>
)}
{actions?.map((action) => {
// TODO: Tooltip on disabled button broke, probably due to portal
return (
<Wrap
when={!!action.disabled}
with={<Tooltip content={action.disabled} />}
key={kebabCase(`action-${action.label}`)}
>
<DropdownMenu.Item
onSelect={() => {
window.navigator.clipboard.writeText(id)
}}
className={cn(action.className, {
destructive:
action.label.toLowerCase() === 'delete' && !action.disabled,
})}
onSelect={action.onActivate}
disabled={!!action.disabled}
>
Copy ID
{action.label}
</DropdownMenu.Item>
)}
{actions.map((action) => {
// TODO: Tooltip on disabled button broke, probably due to portal
return (
<Wrap
when={!!action.disabled}
with={<Tooltip content={action.disabled} />}
key={kebabCase(`action-${action.label}`)}
>
<DropdownMenu.Item
className={cn(action.className, {
destructive:
action.label.toLowerCase() === 'delete' && !action.disabled,
})}
onSelect={action.onActivate}
disabled={!!action.disabled}
>
{action.label}
</DropdownMenu.Item>
</Wrap>
)
})}
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>
)
},
}
</Wrap>
)
})}
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>
)
}
4 changes: 2 additions & 2 deletions test/e2e/images.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ test('can promote an image from project', async ({ page }) => {
test('can copy an image ID to clipboard', async ({ page, browserName }) => {
// eslint-disable-next-line playwright/no-skipped-test
test.skip(
browserName === 'firefox' || browserName === 'webkit',
'navigator.clipboard.readText() not supported in FF. Works locally in Safari but not in CI.'
browserName === 'webkit',
'navigator.clipboard.readText() works locally in Safari but not in CI.'
)

await page.goto('/images')
Expand Down
12 changes: 12 additions & 0 deletions test/e2e/utilization.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/
import {
clickRowAction,
clipboardText,
closeToast,
expect,
expectRowVisible,
Expand Down Expand Up @@ -43,6 +44,17 @@ test.describe('System utilization', () => {
await page.getByRole('tab', { name: 'Metrics' }).click()
})

test('can copy silo ID', async ({ page, browserName }) => {
// eslint-disable-next-line playwright/no-skipped-test
test.skip(
browserName === 'webkit',
'navigator.clipboard.readText() works locally in Safari but not in CI.'
)
await page.goto('/system/utilization')
await clickRowAction(page, 'maze-war', 'Copy silo ID')
expect(await clipboardText(page)).toEqual('6d3a9c06-475e-4f75-b272-c0d0e3f980fa')
})

test('does not appear for dev user', async ({ browser }) => {
const page = await getPageAsUser(browser, 'Hans Jonas')
await page.goto('/system/utilization')
Expand Down

0 comments on commit c2909e7

Please sign in to comment.