Skip to content

Feat: delete template button #3781

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 19 commits into from
Sep 1, 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
5 changes: 5 additions & 0 deletions site/src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,11 @@ export const updateTemplateMeta = async (
return response.data
}

export const deleteTemplate = async (templateId: string): Promise<TypesGen.Template> => {
const response = await axios.delete<TypesGen.Template>(`/api/v2/templates/${templateId}`)
return response.data
}

export const getWorkspace = async (
workspaceId: string,
params?: TypesGen.WorkspaceOptions,
Expand Down
9 changes: 7 additions & 2 deletions site/src/components/DropdownArrows/DropdownArrows.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,15 @@ interface ArrowProps {

export const OpenDropdown: FC<ArrowProps> = ({ margin = true }) => {
const styles = useStyles({ margin })
return <KeyboardArrowDown className={styles.arrowIcon} />
return <KeyboardArrowDown aria-label="open-dropdown" className={styles.arrowIcon} />
}

export const CloseDropdown: FC<ArrowProps> = ({ margin = true }) => {
const styles = useStyles({ margin })
return <KeyboardArrowUp className={`${styles.arrowIcon} ${styles.arrowIconUp}`} />
return (
<KeyboardArrowUp
aria-label="close-dropdown"
className={`${styles.arrowIcon} ${styles.arrowIconUp}`}
/>
)
}
30 changes: 30 additions & 0 deletions site/src/components/DropdownButton/DropdownButton.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { action } from "@storybook/addon-actions"
import { Story } from "@storybook/react"
import { WorkspaceStateEnum } from "util/workspace"
import { DeleteButton, DisabledButton, StartButton, UpdateButton } from "./ActionCtas"
import { DropdownButton, DropdownButtonProps } from "./DropdownButton"

export default {
title: "Components/DropdownButton",
component: DropdownButton,
}

const Template: Story<DropdownButtonProps> = (args) => <DropdownButton {...args} />

export const WithDropdown = Template.bind({})
WithDropdown.args = {
primaryAction: <StartButton handleAction={action("start")} />,
secondaryActions: [
{ action: "update", button: <UpdateButton handleAction={action("update")} /> },
{ action: "delete", button: <DeleteButton handleAction={action("delete")} /> },
],
canCancel: false,
}

export const WithCancel = Template.bind({})
WithCancel.args = {
primaryAction: <DisabledButton workspaceState={WorkspaceStateEnum.deleting} />,
secondaryActions: [],
canCancel: true,
handleCancel: action("cancel"),
}
104 changes: 104 additions & 0 deletions site/src/components/DropdownButton/DropdownButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import Button from "@material-ui/core/Button"
import Popover from "@material-ui/core/Popover"
import { makeStyles } from "@material-ui/core/styles"
import { CloseDropdown, OpenDropdown } from "components/DropdownArrows/DropdownArrows"
import { DropdownContent } from "components/DropdownButton/DropdownContent/DropdownContent"
import { FC, ReactNode, useRef, useState } from "react"
import { CancelButton } from "./ActionCtas"

export interface DropdownButtonProps {
primaryAction: ReactNode
secondaryActions: Array<{ action: string; button: ReactNode }>
canCancel: boolean
handleCancel?: () => void
}

export const DropdownButton: FC<DropdownButtonProps> = ({
primaryAction,
secondaryActions,
canCancel,
handleCancel,
}) => {
const styles = useStyles()
const anchorRef = useRef<HTMLButtonElement>(null)
const [isOpen, setIsOpen] = useState(false)
const id = isOpen ? "action-popover" : undefined

return (
<span className={styles.buttonContainer}>
{/* primary workspace CTA */}
<span data-testid="primary-cta" className={styles.primaryCta}>
{primaryAction}
</span>
{canCancel && handleCancel ? (
<CancelButton handleAction={handleCancel} />
) : (
<>
{/* popover toggle button */}
<Button
data-testid="workspace-actions-button"
aria-controls="workspace-actions-menu"
aria-haspopup="true"
className={styles.dropdownButton}
ref={anchorRef}
disabled={!secondaryActions.length}
onClick={() => {
setIsOpen(true)
}}
>
{isOpen ? <CloseDropdown /> : <OpenDropdown />}
</Button>
<Popover
classes={{ paper: styles.popoverPaper }}
id={id}
open={isOpen}
anchorEl={anchorRef.current}
onClose={() => setIsOpen(false)}
onBlur={() => setIsOpen(false)}
anchorOrigin={{
vertical: "bottom",
horizontal: "right",
}}
transformOrigin={{
vertical: "top",
horizontal: "right",
}}
>
{/* secondary workspace CTAs */}
<DropdownContent secondaryActions={secondaryActions} />
</Popover>
</>
)}
</span>
)
}

const useStyles = makeStyles((theme) => ({
buttonContainer: {
border: `1px solid ${theme.palette.divider}`,
borderRadius: `${theme.shape.borderRadius}px`,
display: "inline-flex",
},
dropdownButton: {
border: "none",
borderLeft: `1px solid ${theme.palette.divider}`,
borderRadius: `0px ${theme.shape.borderRadius}px ${theme.shape.borderRadius}px 0px`,
minWidth: "unset",
width: "63px", // matching cancel button so button grouping doesn't grow in size
"& .MuiButton-label": {
marginRight: "8px",
},
},
primaryCta: {
[theme.breakpoints.down("sm")]: {
width: "100%",

"& > *": {
width: "100%",
},
},
},
popoverPaper: {
padding: `${theme.spacing(1)}px ${theme.spacing(2)}px ${theme.spacing(1)}px`,
},
}))
Original file line number Diff line number Diff line change
@@ -1,24 +1,21 @@
import { makeStyles } from "@material-ui/core/styles"
import { FC } from "react"
import { ButtonMapping, ButtonTypesEnum } from "../constants"
import { FC, ReactNode } from "react"

export interface DropdownContentProps {
secondaryActions: ButtonTypesEnum[]
buttonMapping: Partial<ButtonMapping>
secondaryActions: Array<{ action: string; button: ReactNode }>
}

/* secondary workspace CTAs */
export const DropdownContent: FC<React.PropsWithChildren<DropdownContentProps>> = ({
secondaryActions,
buttonMapping,
}) => {
const styles = useStyles()

return (
<span data-testid="secondary-ctas">
{secondaryActions.map((action) => (
{secondaryActions.map(({ action, button }) => (
<div key={action} className={styles.popoverActionButton}>
{buttonMapping[action]}
{button}
</div>
))}
</span>
Expand Down
77 changes: 37 additions & 40 deletions site/src/components/Workspace/Workspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,20 +68,19 @@ export const Workspace: FC<React.PropsWithChildren<WorkspaceProps>> = ({
const styles = useStyles()
const navigate = useNavigate()

const buildError = workspaceErrors[WorkspaceErrors.BUILD_ERROR] ? (
<ErrorSummary error={workspaceErrors[WorkspaceErrors.BUILD_ERROR]} dismissible />
) : (
<></>
)
const cancellationError = workspaceErrors[WorkspaceErrors.CANCELLATION_ERROR] ? (
<ErrorSummary error={workspaceErrors[WorkspaceErrors.CANCELLATION_ERROR]} dismissible />
) : (
<></>
)

return (
<Margins>
<Stack spacing={1}>
{workspaceErrors[WorkspaceErrors.BUILD_ERROR] ? (
<ErrorSummary error={workspaceErrors[WorkspaceErrors.BUILD_ERROR]} dismissible />
) : (
<></>
)}
{workspaceErrors[WorkspaceErrors.CANCELLATION_ERROR] ? (
<ErrorSummary error={workspaceErrors[WorkspaceErrors.CANCELLATION_ERROR]} dismissible />
) : (
<></>
)}
</Stack>
<PageHeader
actions={
<Stack direction="row" spacing={1} className={styles.actions}>
Expand Down Expand Up @@ -109,39 +108,37 @@ export const Workspace: FC<React.PropsWithChildren<WorkspaceProps>> = ({
<PageHeaderSubtitle>{workspace.owner_name}</PageHeaderSubtitle>
</PageHeader>

<Stack direction="row" spacing={3}>
<Stack direction="column" className={styles.firstColumnSpacer} spacing={3}>
<WorkspaceScheduleBanner
isLoading={bannerProps.isLoading}
onExtend={bannerProps.onExtend}
workspace={workspace}
/>
<Stack direction="column" className={styles.firstColumnSpacer} spacing={2.5}>
{buildError}
{cancellationError}

<WorkspaceScheduleBanner
isLoading={bannerProps.isLoading}
onExtend={bannerProps.onExtend}
workspace={workspace}
/>

<WorkspaceDeletedBanner
<WorkspaceDeletedBanner workspace={workspace} handleClick={() => navigate(`/templates`)} />

<WorkspaceStats workspace={workspace} handleUpdate={handleUpdate} />

{!!resources && !!resources.length && (
<Resources
resources={resources}
getResourcesError={workspaceErrors[WorkspaceErrors.GET_RESOURCES_ERROR]}
workspace={workspace}
handleClick={() => navigate(`/templates`)}
canUpdateWorkspace={canUpdateWorkspace}
buildInfo={buildInfo}
/>
)}

<WorkspaceStats workspace={workspace} handleUpdate={handleUpdate} />

{!!resources && !!resources.length && (
<Resources
resources={resources}
getResourcesError={workspaceErrors[WorkspaceErrors.GET_RESOURCES_ERROR]}
workspace={workspace}
canUpdateWorkspace={canUpdateWorkspace}
buildInfo={buildInfo}
/>
<WorkspaceSection title="Logs" contentsProps={{ className: styles.timelineContents }}>
{workspaceErrors[WorkspaceErrors.GET_BUILDS_ERROR] ? (
<ErrorSummary error={workspaceErrors[WorkspaceErrors.GET_BUILDS_ERROR]} />
) : (
<BuildsTable builds={builds} className={styles.timelineTable} />
)}

<WorkspaceSection title="Logs" contentsProps={{ className: styles.timelineContents }}>
{workspaceErrors[WorkspaceErrors.GET_BUILDS_ERROR] ? (
<ErrorSummary error={workspaceErrors[WorkspaceErrors.GET_BUILDS_ERROR]} />
) : (
<BuildsTable builds={builds} className={styles.timelineTable} />
)}
</WorkspaceSection>
</Stack>
</WorkspaceSection>
</Stack>
</Margins>
)
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { fireEvent, screen } from "@testing-library/react"
import { WorkspaceStateEnum } from "util/workspace"
import * as Mocks from "../../testHelpers/entities"
import { render } from "../../testHelpers/renderHelpers"
import { Language } from "./ActionCtas"
import { Language } from "../DropdownButton/ActionCtas"
import { WorkspaceActions, WorkspaceActionsProps } from "./WorkspaceActions"

const renderComponent = async (props: Partial<WorkspaceActionsProps> = {}) => {
Expand Down
Loading