fix(site): Fix loading buttons (#7549)

This commit is contained in:
Bruno Quaresma
2023-05-17 13:56:15 -03:00
committed by GitHub
parent 119098a1eb
commit a7f14f89e3
5 changed files with 94 additions and 158 deletions
@@ -19,12 +19,10 @@ const Template: Story<LoadingButtonProps> = (args) => (
export const Loading = Template.bind({})
Loading.args = {
variant: "contained",
loading: true,
}
export const NotLoading = Template.bind({})
NotLoading.args = {
variant: "contained",
loading: false,
}
@@ -1,71 +1,21 @@
import Button, { ButtonProps } from "@mui/material/Button"
import CircularProgress from "@mui/material/CircularProgress"
import { makeStyles } from "@mui/styles"
import { Theme } from "@mui/material/styles"
import { FC } from "react"
import MuiLoadingButton, {
LoadingButtonProps as MuiLoadingButtonProps,
} from "@mui/lab/LoadingButton"
export interface LoadingButtonProps extends ButtonProps {
/** Whether or not to disable the button and show a spinner */
loading?: boolean
/** An optional label to display with the loading spinner */
loadingLabel?: string
}
export type LoadingButtonProps = MuiLoadingButtonProps
/**
* LoadingButton is a small wrapper around Material-UI's button to show a loading spinner
*
* In Material-UI 5+ - this is built-in, but since we're on an earlier version,
* we have to roll our own.
*/
export const LoadingButton: FC<React.PropsWithChildren<LoadingButtonProps>> = ({
loading = false,
loadingLabel,
export const LoadingButton: FC<LoadingButtonProps> = ({
children,
...rest
loadingIndicator,
...buttonProps
}) => {
const styles = useStyles({ hasLoadingLabel: Boolean(loadingLabel) })
const hidden = loading ? { opacity: 0 } : undefined
return (
<Button {...rest} disabled={rest.disabled || loading}>
<span style={hidden}>{children}</span>
{loading && (
<div className={styles.loader}>
<CircularProgress size={16} className={styles.spinner} />
</div>
)}
{Boolean(loadingLabel) && loadingLabel}
</Button>
<MuiLoadingButton variant="outlined" color="neutral" {...buttonProps}>
{/* known issue: https://github.com/mui/material-ui/issues/27853 */}
<span>
{buttonProps.loading && loadingIndicator ? loadingIndicator : children}
</span>
</MuiLoadingButton>
)
}
interface StyleProps {
hasLoadingLabel?: boolean
}
const useStyles = makeStyles<Theme, StyleProps>((theme) => ({
loader: {
position: (props) => {
if (!props.hasLoadingLabel) {
return "absolute"
}
},
transform: (props) => {
if (!props.hasLoadingLabel) {
return "translate(-50%, -50%)"
}
},
marginRight: (props) => {
if (props.hasLoadingLabel) {
return "10px"
}
},
top: "50%",
left: "50%",
height: 22, // centering loading icon
width: 16,
},
spinner: {
color: theme.palette.text.disabled,
},
}))
@@ -5,76 +5,85 @@ import CropSquareIcon from "@mui/icons-material/CropSquare"
import PlayCircleOutlineIcon from "@mui/icons-material/PlayCircleOutline"
import ReplayIcon from "@mui/icons-material/Replay"
import { LoadingButton } from "components/LoadingButton/LoadingButton"
import { FC, PropsWithChildren } from "react"
import { useTranslation } from "react-i18next"
import { FC } from "react"
import BlockOutlined from "@mui/icons-material/BlockOutlined"
interface WorkspaceAction {
loading?: boolean
handleAction: () => void
}
export const UpdateButton: FC<PropsWithChildren<WorkspaceAction>> = ({
export const UpdateButton: FC<WorkspaceAction> = ({
handleAction,
loading,
}) => {
const { t } = useTranslation("workspacePage")
return (
<Button
<LoadingButton
loading={loading}
loadingIndicator="Updating..."
loadingPosition="start"
size="small"
data-testid="workspace-update-button"
startIcon={<CloudQueueIcon />}
onClick={handleAction}
>
{t("actionButton.update")}
</Button>
Update
</LoadingButton>
)
}
export const StartButton: FC<PropsWithChildren<WorkspaceAction>> = ({
handleAction,
}) => {
const { t } = useTranslation("workspacePage")
export const StartButton: FC<WorkspaceAction> = ({ handleAction, loading }) => {
return (
<Button startIcon={<PlayCircleOutlineIcon />} onClick={handleAction}>
{t("actionButton.start")}
</Button>
<LoadingButton
size="small"
loading={loading}
loadingIndicator="Starting..."
loadingPosition="start"
startIcon={<PlayCircleOutlineIcon />}
onClick={handleAction}
>
Start
</LoadingButton>
)
}
export const StopButton: FC<PropsWithChildren<WorkspaceAction>> = ({
handleAction,
}) => {
const { t } = useTranslation("workspacePage")
export const StopButton: FC<WorkspaceAction> = ({ handleAction, loading }) => {
return (
<Button size="small" startIcon={<CropSquareIcon />} onClick={handleAction}>
{t("actionButton.stop")}
</Button>
<LoadingButton
size="small"
loading={loading}
loadingIndicator="Stopping..."
loadingPosition="start"
startIcon={<CropSquareIcon />}
onClick={handleAction}
>
Stop
</LoadingButton>
)
}
export const RestartButton: FC<PropsWithChildren<WorkspaceAction>> = ({
export const RestartButton: FC<WorkspaceAction> = ({
handleAction,
loading,
}) => {
const { t } = useTranslation("workspacePage")
return (
<Button
<LoadingButton
loading={loading}
loadingIndicator="Restarting..."
loadingPosition="start"
size="small"
startIcon={<ReplayIcon />}
onClick={handleAction}
data-testid="workspace-restart-button"
>
{t("actionButton.restart")}
</Button>
Restart
</LoadingButton>
)
}
export const CancelButton: FC<PropsWithChildren<WorkspaceAction>> = ({
handleAction,
}) => {
export const CancelButton: FC<WorkspaceAction> = ({ handleAction }) => {
return (
<Button startIcon={<BlockIcon />} onClick={handleAction}>
<Button size="small" startIcon={<BlockIcon />} onClick={handleAction}>
Cancel
</Button>
)
@@ -84,11 +93,9 @@ interface DisabledProps {
label: string
}
export const DisabledButton: FC<PropsWithChildren<DisabledProps>> = ({
label,
}) => {
export const DisabledButton: FC<DisabledProps> = ({ label }) => {
return (
<Button size="small" disabled>
<Button size="small" startIcon={<BlockOutlined />} disabled>
{label}
</Button>
)
@@ -98,8 +105,15 @@ interface LoadingProps {
label: string
}
export const ActionLoadingButton: FC<PropsWithChildren<LoadingProps>> = ({
label,
}) => {
return <LoadingButton loading size="small" loadingLabel={label} />
export const ActionLoadingButton: FC<LoadingProps> = ({ label }) => {
return (
<LoadingButton
loading
size="small"
loadingPosition="start"
loadingIndicator={label}
// This icon can be anything
startIcon={<ReplayIcon />}
/>
)
}
@@ -3,7 +3,6 @@ import Menu from "@mui/material/Menu"
import { makeStyles } from "@mui/styles"
import MoreVertOutlined from "@mui/icons-material/MoreVertOutlined"
import { FC, ReactNode, useRef, useState } from "react"
import { useTranslation } from "react-i18next"
import { WorkspaceStatus } from "api/typesGenerated"
import {
ActionLoadingButton,
@@ -57,7 +56,6 @@ export const WorkspaceActions: FC<WorkspaceActionsProps> = ({
canChangeVersions,
}) => {
const styles = useStyles()
const { t } = useTranslation("workspacePage")
const {
canCancel,
canAcceptJobs,
@@ -69,64 +67,26 @@ export const WorkspaceActions: FC<WorkspaceActionsProps> = ({
// A mapping of button type to the corresponding React component
const buttonMapping: ButtonMapping = {
[ButtonTypesEnum.update]: (
<UpdateButton handleAction={handleUpdate} key={ButtonTypesEnum.update} />
),
[ButtonTypesEnum.update]: <UpdateButton handleAction={handleUpdate} />,
[ButtonTypesEnum.updating]: (
<ActionLoadingButton
label={t("actionButton.updating")}
key={ButtonTypesEnum.updating}
/>
),
[ButtonTypesEnum.start]: (
<StartButton handleAction={handleStart} key={ButtonTypesEnum.start} />
<UpdateButton loading handleAction={handleUpdate} />
),
[ButtonTypesEnum.start]: <StartButton handleAction={handleStart} />,
[ButtonTypesEnum.starting]: (
<ActionLoadingButton
label={t("actionButton.starting")}
key={ButtonTypesEnum.starting}
/>
),
[ButtonTypesEnum.stop]: (
<StopButton handleAction={handleStop} key={ButtonTypesEnum.stop} />
<StartButton loading handleAction={handleStart} />
),
[ButtonTypesEnum.stop]: <StopButton handleAction={handleStop} />,
[ButtonTypesEnum.stopping]: (
<ActionLoadingButton
label={t("actionButton.stopping")}
key={ButtonTypesEnum.stopping}
/>
<StopButton loading handleAction={handleStop} />
),
[ButtonTypesEnum.restart]: <RestartButton handleAction={handleRestart} />,
[ButtonTypesEnum.restarting]: (
<ActionLoadingButton
label="Restarting"
key={ButtonTypesEnum.restarting}
/>
),
[ButtonTypesEnum.deleting]: (
<ActionLoadingButton
label={t("actionButton.deleting")}
key={ButtonTypesEnum.deleting}
/>
),
[ButtonTypesEnum.canceling]: (
<DisabledButton
label={t("disabledButton.canceling")}
key={ButtonTypesEnum.canceling}
/>
),
[ButtonTypesEnum.deleted]: (
<DisabledButton
label={t("disabledButton.deleted")}
key={ButtonTypesEnum.deleted}
/>
),
[ButtonTypesEnum.pending]: (
<ActionLoadingButton
label={t("disabledButton.pending")}
key={ButtonTypesEnum.pending}
/>
<RestartButton loading handleAction={handleRestart} />
),
[ButtonTypesEnum.deleting]: <ActionLoadingButton label="Deleting" />,
[ButtonTypesEnum.canceling]: <DisabledButton label="Canceling..." />,
[ButtonTypesEnum.deleted]: <DisabledButton label="Deleted" />,
[ButtonTypesEnum.pending]: <ActionLoadingButton label="Pending..." />,
}
// Returns a function that will execute the action and close the menu
+14
View File
@@ -133,12 +133,26 @@ dark = createTheme(dark, {
sizeSmall: {
borderRadius: 6,
height: BUTTON_SM_HEIGHT,
"& .MuiCircularProgress-root": {
width: "14px !important",
height: "14px !important",
},
},
sizeLarge: {
height: BUTTON_LG_HEIGHT,
},
outlinedNeutral: {
borderColor: colors.gray[12],
"&.Mui-disabled": {
borderColor: colors.gray[13],
color: colors.gray[11],
"& > .MuiLoadingButton-loadingIndicator": {
color: colors.gray[11],
},
},
},
containedNeutral: {
borderColor: colors.gray[12],