diff --git a/site/src/components/CodeExample/CodeExample.tsx b/site/src/components/CodeExample/CodeExample.tsx index abfdf3b206d0a..aaf5aca6432a0 100644 --- a/site/src/components/CodeExample/CodeExample.tsx +++ b/site/src/components/CodeExample/CodeExample.tsx @@ -1,9 +1,7 @@ -import { makeStyles } from "@mui/styles"; -import { FC } from "react"; +import { type FC } from "react"; +import { useTheme } from "@emotion/react"; import { MONOSPACE_FONT_FAMILY } from "theme/constants"; -import { combineClasses } from "utils/combineClasses"; import { CopyButton } from "../CopyButton/CopyButton"; -import { Theme } from "@mui/material/styles"; export interface CodeExampleProps { code: string; @@ -14,46 +12,40 @@ export interface CodeExampleProps { /** * Component to show single-line code examples, with a copy button */ -export const CodeExample: FC = ({ - code, - password, - className, -}) => { - const styles = useStyles({ password }); +export const CodeExample: FC = (props) => { + const { code, password, className } = props; + const theme = useTheme(); return ( -
- {code} +
+ + {code} +
); }; - -interface styleProps { - inline?: boolean; - password?: boolean; -} - -const useStyles = makeStyles((theme) => ({ - root: (props) => ({ - display: props.inline ? "inline-flex" : "flex", - flexDirection: "row", - alignItems: "center", - background: "rgb(0 0 0 / 30%)", - color: theme.palette.primary.contrastText, - fontFamily: MONOSPACE_FONT_FAMILY, - fontSize: 14, - borderRadius: theme.shape.borderRadius, - padding: theme.spacing(1), - lineHeight: "150%", - border: `1px solid ${theme.palette.divider}`, - }), - code: { - padding: theme.spacing(0, 1), - width: "100%", - display: "flex", - alignItems: "center", - wordBreak: "break-all", - "-webkit-text-security": (props) => (props.password ? "disc" : undefined), - }, -})); diff --git a/site/src/components/Dashboard/Navbar/UserDropdown/UserDropdown.tsx b/site/src/components/Dashboard/Navbar/UserDropdown/UserDropdown.tsx index c285cbc61fa00..50143eeecf217 100644 --- a/site/src/components/Dashboard/Navbar/UserDropdown/UserDropdown.tsx +++ b/site/src/components/Dashboard/Navbar/UserDropdown/UserDropdown.tsx @@ -5,10 +5,7 @@ import { colors } from "theme/colors"; import * as TypesGen from "api/typesGenerated"; import { navHeight } from "theme/constants"; import { BorderedMenu } from "./BorderedMenu"; -import { - CloseDropdown, - OpenDropdown, -} from "components/DropdownArrows/DropdownArrows"; +import { DropdownArrow } from "components/DropdownArrow/DropdownArrow"; import { UserAvatar } from "components/UserAvatar/UserAvatar"; import { UserDropdownContent } from "./UserDropdownContent"; import { BUTTON_SM_HEIGHT } from "theme/theme"; @@ -69,11 +66,7 @@ export const UserDropdown: FC> = ({ avatarURL={user.avatar_url} /> - {anchorEl ? ( - - ) : ( - - )} +
diff --git a/site/src/components/DropdownArrow/DropdownArrow.tsx b/site/src/components/DropdownArrow/DropdownArrow.tsx new file mode 100644 index 0000000000000..1e769692ccc24 --- /dev/null +++ b/site/src/components/DropdownArrow/DropdownArrow.tsx @@ -0,0 +1,28 @@ +import KeyboardArrowDown from "@mui/icons-material/KeyboardArrowDown"; +import KeyboardArrowUp from "@mui/icons-material/KeyboardArrowUp"; +import { type FC } from "react"; +import { type Theme } from "@emotion/react"; + +interface ArrowProps { + margin?: boolean; + color?: string; + close?: boolean; +} + +export const DropdownArrow: FC = (props) => { + const { margin = true, color, close } = props; + + const Arrow = close ? KeyboardArrowUp : KeyboardArrowDown; + + return ( + ({ + color: color ?? theme.palette.primary.contrastText, + marginLeft: margin ? theme.spacing(1) : 0, + width: 16, + height: 16, + })} + /> + ); +}; diff --git a/site/src/components/DropdownArrows/DropdownArrows.tsx b/site/src/components/DropdownArrows/DropdownArrows.tsx deleted file mode 100644 index af0f73ea5052e..0000000000000 --- a/site/src/components/DropdownArrows/DropdownArrows.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { makeStyles } from "@mui/styles"; -import KeyboardArrowDown from "@mui/icons-material/KeyboardArrowDown"; -import KeyboardArrowUp from "@mui/icons-material/KeyboardArrowUp"; -import { FC } from "react"; -import { Theme } from "@mui/material/styles"; - -const useStyles = makeStyles((theme: Theme) => ({ - arrowIcon: { - color: ({ color }) => color ?? theme.palette.primary.contrastText, - marginLeft: ({ margin }) => (margin ? theme.spacing(1) : 0), - width: 16, - height: 16, - }, - arrowIconUp: { - color: ({ color }) => color ?? theme.palette.primary.contrastText, - }, -})); - -interface ArrowProps { - margin?: boolean; - color?: string; -} - -export const OpenDropdown: FC = ({ margin = true, color }) => { - const styles = useStyles({ margin, color }); - return ( - - ); -}; - -export const CloseDropdown: FC = ({ margin = true, color }) => { - const styles = useStyles({ margin, color }); - return ( - - ); -}; diff --git a/site/src/components/Expander/Expander.tsx b/site/src/components/Expander/Expander.tsx index 5fb61376786c7..25fac1d7ad2ec 100644 --- a/site/src/components/Expander/Expander.tsx +++ b/site/src/components/Expander/Expander.tsx @@ -1,11 +1,8 @@ +import Collapse from "@mui/material/Collapse"; import Link from "@mui/material/Link"; import makeStyles from "@mui/styles/makeStyles"; -import { - CloseDropdown, - OpenDropdown, -} from "components/DropdownArrows/DropdownArrows"; -import { PropsWithChildren, FC } from "react"; -import Collapse from "@mui/material/Collapse"; +import { DropdownArrow } from "components/DropdownArrow/DropdownArrow"; +import { type FC, type PropsWithChildren } from "react"; import { combineClasses } from "utils/combineClasses"; export interface ExpanderProps { @@ -28,7 +25,7 @@ export const Expander: FC> = ({ Click here to learn more - + )} @@ -42,7 +39,7 @@ export const Expander: FC> = ({ > Click here to hide - + )} diff --git a/site/src/components/HelpTooltip/HelpTooltip.tsx b/site/src/components/HelpTooltip/HelpTooltip.tsx index 184ca9bc93b55..8050c433798e4 100644 --- a/site/src/components/HelpTooltip/HelpTooltip.tsx +++ b/site/src/components/HelpTooltip/HelpTooltip.tsx @@ -1,6 +1,5 @@ import Link from "@mui/material/Link"; import Popover, { PopoverProps } from "@mui/material/Popover"; -import { makeStyles } from "@mui/styles"; import HelpIcon from "@mui/icons-material/HelpOutline"; import OpenInNewIcon from "@mui/icons-material/OpenInNew"; import { @@ -11,9 +10,10 @@ import { FC, PropsWithChildren, } from "react"; -import { combineClasses } from "utils/combineClasses"; import { Stack } from "components/Stack/Stack"; import Box, { BoxProps } from "@mui/material/Box"; +import { type CSSObject, css as className } from "@emotion/css"; +import { css, type Interpolation, type Theme, useTheme } from "@emotion/react"; type Icon = typeof HelpIcon; @@ -46,12 +46,24 @@ const useHelpTooltip = () => { export const HelpPopover: FC< PopoverProps & { onOpen: () => void; onClose: () => void } > = ({ onOpen, onClose, children, ...props }) => { - const styles = useStyles({ size: "small" }); + const theme = useTheme(); return ( > = ({ iconClassName, buttonClassName, }) => { - const styles = useStyles({ size }); + const theme = useTheme(); const anchorRef = useRef(null); const [isOpen, setIsOpen] = useState(Boolean(open)); const id = isOpen ? "help-popover" : undefined; @@ -94,7 +106,24 @@ export const HelpTooltip: FC> = ({ > = ({ export const HelpTooltipTitle: FC> = ({ children, }) => { - const styles = useStyles({}); - - return

{children}

; + return

{children}

; }; export const HelpTooltipText = (props: BoxProps) => { - const styles = useStyles({}); - - return ( - - ); + return ; }; export const HelpTooltipLink: FC> = ({ children, href, }) => { - const styles = useStyles({}); - return ( - - + + {children} ); @@ -165,20 +188,19 @@ export const HelpTooltipAction: FC< ariaLabel?: string; }> > = ({ children, icon: Icon, onClick, ariaLabel }) => { - const styles = useStyles({}); const tooltip = useHelpTooltip(); return ( ); @@ -187,10 +209,8 @@ export const HelpTooltipAction: FC< export const HelpTooltipLinksGroup: FC> = ({ children, }) => { - const styles = useStyles({}); - return ( - + {children} ); @@ -216,80 +236,40 @@ const getIconSpacingFromSize = (size?: Size): number => { } }; -const useStyles = makeStyles((theme) => ({ - button: { - display: "flex", - alignItems: "center", - justifyContent: "center", - width: ({ size }: { size?: Size }) => - theme.spacing(getButtonSpacingFromSize(size)), - height: ({ size }: { size?: Size }) => - theme.spacing(getButtonSpacingFromSize(size)), - padding: 0, - border: 0, - background: "transparent", - color: theme.palette.text.primary, - opacity: 0.5, - cursor: "pointer", - - "&:hover": { - opacity: 0.75, - }, - }, - - icon: { - width: ({ size }: { size?: Size }) => - theme.spacing(getIconSpacingFromSize(size)), - height: ({ size }: { size?: Size }) => - theme.spacing(getIconSpacingFromSize(size)), - }, - - popover: { - pointerEvents: "none", - }, - - popoverPaper: { - marginTop: theme.spacing(0.5), - width: theme.spacing(38), - padding: theme.spacing(2.5), - color: theme.palette.text.secondary, - pointerEvents: "auto", - ...theme.typography.body2, - }, - - title: { +const styles = { + title: (theme) => ({ marginTop: 0, marginBottom: theme.spacing(1), color: theme.palette.text.primary, fontSize: 14, lineHeight: "120%", fontWeight: 600, - }, + }), - text: { + text: (theme) => ({ marginTop: theme.spacing(0.5), marginBottom: theme.spacing(0.5), - ...theme.typography.body2, - }, + ...(theme.typography.body2 as CSSObject), + }), - link: { + link: (theme) => ({ display: "flex", alignItems: "center", - ...theme.typography.body2, - }, + ...(theme.typography.body2 as CSSObject), + }), - linkIcon: { + linkIcon: (theme) => ({ color: "inherit", width: 14, height: 14, marginRight: theme.spacing(1), - }, + }), - linksGroup: { + linksGroup: (theme) => ({ marginTop: theme.spacing(2), - }, + }), - action: { + action: (theme) => ({ display: "flex", alignItems: "center", background: "none", @@ -298,12 +278,12 @@ const useStyles = makeStyles((theme) => ({ padding: 0, cursor: "pointer", fontSize: 14, - }, + }), - actionIcon: { + actionIcon: (theme) => ({ color: "inherit", width: 14, height: 14, marginRight: theme.spacing(1), - }, -})); + }), +} satisfies Record>; diff --git a/site/src/components/IconField/IconField.tsx b/site/src/components/IconField/IconField.tsx index 0d11665257f07..2e5e0ba606a1a 100644 --- a/site/src/components/IconField/IconField.tsx +++ b/site/src/components/IconField/IconField.tsx @@ -2,7 +2,7 @@ import Button from "@mui/material/Button"; import InputAdornment from "@mui/material/InputAdornment"; import Popover from "@mui/material/Popover"; import TextField, { TextFieldProps } from "@mui/material/TextField"; -import { OpenDropdown } from "components/DropdownArrows/DropdownArrows"; +import { DropdownArrow } from "components/DropdownArrow/DropdownArrow"; import { useRef, FC, useState } from "react"; import Picker from "@emoji-mart/react"; import { makeStyles } from "@mui/styles"; @@ -52,7 +52,7 @@ const IconField: FC = ({ onPickEmoji, ...textFieldProps }) => { ) : ( @@ -456,7 +453,7 @@ export const AgentRow: FC = ({ setShowLogs((v) => !v); }} > - + Show logs )} diff --git a/site/src/components/Resources/ResourceCard.tsx b/site/src/components/Resources/ResourceCard.tsx index e19137ad0e551..f5c0779d68a29 100644 --- a/site/src/components/Resources/ResourceCard.tsx +++ b/site/src/components/Resources/ResourceCard.tsx @@ -1,17 +1,71 @@ -import { makeStyles } from "@mui/styles"; -import { FC, useState } from "react"; +import { type FC, useState } from "react"; +import IconButton from "@mui/material/IconButton"; +import Tooltip from "@mui/material/Tooltip"; +import { type CSSObject, type Interpolation, type Theme } from "@emotion/react"; import { WorkspaceAgent, WorkspaceResource } from "api/typesGenerated"; +import { DropdownArrow } from "components/DropdownArrow/DropdownArrow"; +import { CopyableValue } from "components/CopyableValue/CopyableValue"; import { Stack } from "../Stack/Stack"; import { ResourceAvatar } from "./ResourceAvatar"; import { SensitiveValue } from "./SensitiveValue"; -import { - OpenDropdown, - CloseDropdown, -} from "components/DropdownArrows/DropdownArrows"; -import IconButton from "@mui/material/IconButton"; -import Tooltip from "@mui/material/Tooltip"; -import { CopyableValue } from "components/CopyableValue/CopyableValue"; -import { type Theme } from "@mui/material/styles"; + +const styles = { + resourceCard: (theme) => ({ + background: theme.palette.background.paper, + borderRadius: theme.shape.borderRadius, + border: `1px solid ${theme.palette.divider}`, + + "&:not(:first-of-type)": { + borderTop: 0, + borderTopLeftRadius: 0, + borderTopRightRadius: 0, + }, + + "&:not(:last-child)": { + borderBottomLeftRadius: 0, + borderBottomRightRadius: 0, + }, + }), + + resourceCardProfile: { + flexShrink: 0, + width: "fit-content", + }, + + resourceCardHeader: (theme) => ({ + padding: theme.spacing(3, 4), + borderBottom: `1px solid ${theme.palette.divider}`, + + "&:last-child": { + borderBottom: 0, + }, + + [theme.breakpoints.down("md")]: { + width: "100%", + overflow: "scroll", + }, + }), + + metadata: (theme) => ({ + ...(theme.typography.body2 as CSSObject), + lineHeight: "120%", + }), + + metadataLabel: (theme) => ({ + fontSize: 12, + color: theme.palette.text.secondary, + textOverflow: "ellipsis", + overflow: "hidden", + whiteSpace: "nowrap", + }), + + metadataValue: (theme) => ({ + textOverflow: "ellipsis", + overflow: "hidden", + whiteSpace: "nowrap", + ...(theme.typography.body1 as CSSObject), + }), +} satisfies Record>; export interface ResourceCardProps { resource: WorkspaceResource; @@ -33,44 +87,52 @@ export const ResourceCard: FC = ({ resource, agentRow }) => { metadataLength += 1; visibleMetadata.pop(); } - const styles = useStyles({ metadataLength }); + const gridWidth = metadataLength === 1 ? 1 : 4; return ( -
+
-
-
{resource.type}
-
{resource.name}
+
+
{resource.type}
+
{resource.name}
-
+
({ + flexGrow: 2, + display: "grid", + gridTemplateColumns: `repeat(${gridWidth}, minmax(0, 1fr))`, + gap: theme.spacing(5), + rowGap: theme.spacing(3), + })} + > {resource.daily_cost > 0 && ( -
-
+
+
cost
-
{resource.daily_cost}
+
{resource.daily_cost}
)} {visibleMetadata.map((meta) => { return ( -
-
{meta.key}
-
+
+
{meta.key}
+
{meta.sensitive ? ( ) : ( @@ -95,11 +157,7 @@ export const ResourceCard: FC = ({ resource, agentRow }) => { }} size="large" > - {shouldDisplayAllMetadata ? ( - - ) : ( - - )} + )} @@ -111,66 +169,3 @@ export const ResourceCard: FC = ({ resource, agentRow }) => {
); }; - -const useStyles = makeStyles((theme) => ({ - resourceCard: { - background: theme.palette.background.paper, - borderRadius: theme.shape.borderRadius, - border: `1px solid ${theme.palette.divider}`, - - "&:not(:first-of-type)": { - borderTop: 0, - borderTopLeftRadius: 0, - borderTopRightRadius: 0, - }, - - "&:not(:last-child)": { - borderBottomLeftRadius: 0, - borderBottomRightRadius: 0, - }, - }, - - resourceCardProfile: { - flexShrink: 0, - width: "fit-content", - }, - - resourceCardHeader: { - padding: theme.spacing(3, 4), - borderBottom: `1px solid ${theme.palette.divider}`, - - "&:last-child": { - borderBottom: 0, - }, - }, - - metadataHeader: (props) => ({ - flexGrow: 2, - display: "grid", - gridTemplateColumns: `repeat(${ - props.metadataLength === 1 ? 1 : 4 - }, minmax(0, 1fr))`, - gap: theme.spacing(5), - rowGap: theme.spacing(3), - }), - - metadata: { - ...theme.typography.body2, - lineHeight: "120%", - }, - - metadataLabel: { - fontSize: 12, - color: theme.palette.text.secondary, - textOverflow: "ellipsis", - overflow: "hidden", - whiteSpace: "nowrap", - }, - - metadataValue: { - textOverflow: "ellipsis", - overflow: "hidden", - whiteSpace: "nowrap", - ...theme.typography.body1, - }, -})); diff --git a/site/src/components/Resources/Resources.tsx b/site/src/components/Resources/Resources.tsx index cfb24b565aaf2..28152756ab598 100644 --- a/site/src/components/Resources/Resources.tsx +++ b/site/src/components/Resources/Resources.tsx @@ -1,9 +1,6 @@ import Button from "@mui/material/Button"; import { makeStyles } from "@mui/styles"; -import { - CloseDropdown, - OpenDropdown, -} from "components/DropdownArrows/DropdownArrows"; +import { DropdownArrow } from "components/DropdownArrow/DropdownArrow"; import { FC, useState } from "react"; import { WorkspaceAgent, WorkspaceResource } from "api/typesGenerated"; import { Stack } from "../Stack/Stack"; @@ -49,15 +46,8 @@ export const Resources: FC> = ({ size="small" onClick={() => setShouldDisplayHideResources((v) => !v)} > - {shouldDisplayHideResources ? ( - <> - Hide resources - - ) : ( - <> - Show hidden resources - - )} + {shouldDisplayHideResources ? "Hide" : "Show hidden"} resources +
)} diff --git a/site/src/components/RichParameterInput/RichParameterInput.tsx b/site/src/components/RichParameterInput/RichParameterInput.tsx index 79a460d0d0408..d2b2436c966dd 100644 --- a/site/src/components/RichParameterInput/RichParameterInput.tsx +++ b/site/src/components/RichParameterInput/RichParameterInput.tsx @@ -1,27 +1,111 @@ import FormControlLabel from "@mui/material/FormControlLabel"; import Radio from "@mui/material/Radio"; import RadioGroup from "@mui/material/RadioGroup"; -import { makeStyles } from "@mui/styles"; import TextField, { TextFieldProps } from "@mui/material/TextField"; -import { Stack } from "components/Stack/Stack"; +import Box from "@mui/material/Box"; import { FC } from "react"; import { TemplateVersionParameter } from "api/typesGenerated"; -import { colors } from "theme/colors"; import { MemoizedMarkdown } from "components/Markdown/Markdown"; +import { Stack } from "components/Stack/Stack"; +import { colors } from "theme/colors"; import { MultiTextField } from "./MultiTextField"; -import Box from "@mui/material/Box"; -import { Theme } from "@mui/material/styles"; +import { Interpolation, Theme } from "@emotion/react"; const isBoolean = (parameter: TemplateVersionParameter) => { return parameter.type === "bool"; }; +const styles = { + label: (theme) => ({ + marginBottom: theme.spacing(0.5), + }), + labelCaption: (theme) => ({ + fontSize: 14, + color: theme.palette.text.secondary, + + ".small &": { + fontSize: 13, + lineHeight: "140%", + }, + }), + labelPrimary: (theme) => ({ + fontSize: 16, + color: theme.palette.text.primary, + fontWeight: 600, + + "& p": { + margin: 0, + lineHeight: "24px", // Keep the same as ParameterInput + }, + + ".small &": { + fontSize: 14, + }, + }), + labelImmutable: (theme) => ({ + marginTop: theme.spacing(0.5), + marginBottom: theme.spacing(0.5), + color: colors.yellow[7], + }), + textField: { + ".small & .MuiInputBase-root": { + height: 36, + fontSize: 14, + borderRadius: 6, + }, + }, + radioGroup: (theme) => ({ + ".small & .MuiFormControlLabel-label": { + fontSize: 14, + }, + ".small & .MuiRadio-root": { + padding: theme.spacing(0.75, "9px"), // 8px + 1px border + }, + ".small & .MuiRadio-root svg": { + width: 16, + height: 16, + }, + }), + checkbox: (theme) => ({ + display: "flex", + alignItems: "center", + gap: theme.spacing(1), + }), + labelIconWrapper: (theme) => ({ + width: theme.spacing(2.5), + height: theme.spacing(2.5), + display: "block", + + ".small &": { + display: "none", + }, + }), + labelIcon: { + width: "100%", + height: "100%", + objectFit: "contain", + }, + radioOption: (theme) => ({ + display: "flex", + alignItems: "center", + gap: theme.spacing(1.5), + }), + optionIcon: { + maxHeight: 20, + width: 20, + + ".small &": { + maxHeight: 16, + width: 16, + }, + }, +} satisfies Record>; + export interface ParameterLabelProps { parameter: TemplateVersionParameter; } const ParameterLabel: FC = ({ parameter }) => { - const styles = useStyles(); const hasDescription = parameter.description && parameter.description !== ""; const displayName = parameter.display_name ? parameter.display_name @@ -31,9 +115,9 @@ const ParameterLabel: FC = ({ parameter }) => { @@ -94,14 +178,12 @@ const RichParameterField: React.FC = ({ size, ...props }) => { - const styles = useStyles(); - if (isBoolean(parameter)) { return ( onChange(value)} > @@ -126,7 +208,7 @@ const RichParameterField: React.FC = ({ onChange(value)} > @@ -137,10 +219,10 @@ const RichParameterField: React.FC = ({ value={option.value} control={} label={ - + {option.icon && ( Parameter icon = ({ {...props} id={parameter.name} data-testid="parameter-field-text" - className={styles.textField} + css={styles.textField} type={parameter.type} disabled={disabled} required={parameter.required} @@ -210,89 +292,3 @@ const RichParameterField: React.FC = ({ /> ); }; - -const useStyles = makeStyles((theme) => ({ - label: { - marginBottom: theme.spacing(0.5), - }, - labelCaption: { - fontSize: 14, - color: theme.palette.text.secondary, - - ".small &": { - fontSize: 13, - lineHeight: "140%", - }, - }, - labelPrimary: { - fontSize: 16, - color: theme.palette.text.primary, - fontWeight: 600, - - "& p": { - margin: 0, - lineHeight: "24px", // Keep the same as ParameterInput - }, - - ".small &": { - fontSize: 14, - }, - }, - labelImmutable: { - marginTop: theme.spacing(0.5), - marginBottom: theme.spacing(0.5), - color: colors.yellow[7], - }, - textField: { - ".small & .MuiInputBase-root": { - height: 36, - fontSize: 14, - borderRadius: 6, - }, - }, - radioGroup: { - ".small & .MuiFormControlLabel-label": { - fontSize: 14, - }, - ".small & .MuiRadio-root": { - padding: theme.spacing(0.75, "9px"), // 8px + 1px border - }, - ".small & .MuiRadio-root svg": { - width: 16, - height: 16, - }, - }, - checkbox: { - display: "flex", - alignItems: "center", - gap: theme.spacing(1), - }, - labelIconWrapper: { - width: theme.spacing(2.5), - height: theme.spacing(2.5), - display: "block", - - ".small &": { - display: "none", - }, - }, - labelIcon: { - width: "100%", - height: "100%", - objectFit: "contain", - }, - radioOption: { - display: "flex", - alignItems: "center", - gap: theme.spacing(1.5), - }, - optionIcon: { - maxHeight: 20, - width: 20, - - ".small &": { - maxHeight: 16, - width: 16, - }, - }, -})); diff --git a/site/src/components/Stack/Stack.tsx b/site/src/components/Stack/Stack.tsx index 13c3cfa46c46c..bc19dfc63c03d 100644 --- a/site/src/components/Stack/Stack.tsx +++ b/site/src/components/Stack/Stack.tsx @@ -1,61 +1,39 @@ -import { makeStyles } from "@mui/styles"; -import { CSSProperties } from "@mui/styles/withStyles"; -import { FC } from "react"; -import { ReactNode } from "react-markdown/lib/react-markdown"; -import { combineClasses } from "utils/combineClasses"; - -type Direction = "column" | "row"; +import type { FC } from "react"; +import type { CSSObject } from "@emotion/react"; export type StackProps = { className?: string; - direction?: Direction; + direction?: "column" | "row"; spacing?: number; - alignItems?: CSSProperties["alignItems"]; - justifyContent?: CSSProperties["justifyContent"]; - maxWidth?: CSSProperties["maxWidth"]; - wrap?: CSSProperties["flexWrap"]; + alignItems?: CSSObject["alignItems"]; + justifyContent?: CSSObject["justifyContent"]; + wrap?: CSSObject["flexWrap"]; } & React.HTMLProps; -type StyleProps = Omit; - -const useStyles = makeStyles((theme) => ({ - stack: { - display: "flex", - flexDirection: ({ direction }: StyleProps) => direction, - gap: ({ spacing }: StyleProps) => spacing && theme.spacing(spacing), - alignItems: ({ alignItems }: StyleProps) => alignItems, - justifyContent: ({ justifyContent }: StyleProps) => justifyContent, - flexWrap: ({ wrap }: StyleProps) => wrap, - maxWidth: ({ maxWidth }: StyleProps) => maxWidth, - - [theme.breakpoints.down("md")]: { - width: "100%", - }, - }, -})); - -export const Stack: FC = ({ - children, - className, - direction = "column", - spacing = 2, - alignItems, - justifyContent, - maxWidth, - wrap, - ...divProps -}) => { - const styles = useStyles({ - spacing, - direction, +export const Stack: FC = (props) => { + const { + children, + direction = "column", + spacing = 2, alignItems, justifyContent, wrap, - maxWidth, - }); + ...divProps + } = props; return ( -
+
({ + display: "flex", + flexDirection: direction, + gap: spacing && theme.spacing(spacing), + alignItems: alignItems, + justifyContent: justifyContent, + flexWrap: wrap, + maxWidth: "100%", + })} + > {children}
); diff --git a/site/src/pages/AuditPage/AuditLogRow/AuditLogRow.tsx b/site/src/pages/AuditPage/AuditLogRow/AuditLogRow.tsx index 3537b37d29fee..dbeefc6d3d56b 100644 --- a/site/src/pages/AuditPage/AuditLogRow/AuditLogRow.tsx +++ b/site/src/pages/AuditPage/AuditLogRow/AuditLogRow.tsx @@ -2,10 +2,7 @@ import Collapse from "@mui/material/Collapse"; import { makeStyles } from "@mui/styles"; import TableCell from "@mui/material/TableCell"; import { AuditLog } from "api/typesGenerated"; -import { - CloseDropdown, - OpenDropdown, -} from "components/DropdownArrows/DropdownArrows"; +import { DropdownArrow } from "components/DropdownArrow/DropdownArrow"; import { Pill, type PillType } from "components/Pill/Pill"; import { Stack } from "components/Stack/Stack"; import { TimelineEntry } from "components/Timeline/TimelineEntry"; @@ -154,7 +151,7 @@ export const AuditLogRow: React.FC = ({ {shouldDisplayDiff ? ( -
{isDiffOpen ? : }
+
{}
) : (
)} diff --git a/site/src/pages/CreateUserPage/CreateUserForm.tsx b/site/src/pages/CreateUserPage/CreateUserForm.tsx index 43767ad921aac..630316aea8c57 100644 --- a/site/src/pages/CreateUserPage/CreateUserForm.tsx +++ b/site/src/pages/CreateUserPage/CreateUserForm.tsx @@ -1,7 +1,10 @@ import TextField from "@mui/material/TextField"; +import MenuItem from "@mui/material/MenuItem"; +import Link from "@mui/material/Link"; import { FormikContextType, useFormik } from "formik"; import { FC } from "react"; import * as Yup from "yup"; +import { hasApiFieldErrors, isApiError } from "api/errors"; import * as TypesGen from "api/typesGenerated"; import { getFormHelpers, @@ -12,11 +15,6 @@ import { FormFooter } from "components/FormFooter/FormFooter"; import { FullPageForm } from "components/FullPageForm/FullPageForm"; import { Stack } from "components/Stack/Stack"; import { ErrorAlert } from "components/Alert/ErrorAlert"; -import { hasApiFieldErrors, isApiError } from "api/errors"; -import MenuItem from "@mui/material/MenuItem"; -import { makeStyles } from "@mui/styles"; -import { Theme } from "@mui/material/styles"; -import Link from "@mui/material/Link"; export const Language = { emailLabel: "Email", @@ -104,8 +102,6 @@ export const CreateUserForm: FC< error, ); - const styles = useStyles(); - const methods = [ authMethods?.password.enabled && "password", authMethods?.oidc.enabled && "oidc", @@ -161,9 +157,21 @@ export const CreateUserForm: FC< const language = authMethodLanguage[value]; return ( - + {language.displayName} - + ({ + fontSize: 14, + color: theme.palette.text.secondary, + wordWrap: "normal", + whiteSpace: "break-spaces", + })} + > {language.description} @@ -192,12 +200,3 @@ export const CreateUserForm: FC< ); }; - -const useStyles = makeStyles((theme) => ({ - labelDescription: { - fontSize: 14, - color: theme.palette.text.secondary, - wordWrap: "normal", - whiteSpace: "break-spaces", - }, -})); diff --git a/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPageView.stories.tsx b/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPageView.stories.tsx index 1cca2f6a7ac9d..1dac26ffe043d 100644 --- a/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPageView.stories.tsx +++ b/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPageView.stories.tsx @@ -32,31 +32,6 @@ export const NoIcon: Story = { }, }; -export const SmallViewport: Story = { - args: { - template: MockTemplate, - activeVersion: { - ...MockTemplateVersion, - readme: `--- - name:Template test - --- - ## Instructions - You can add instructions here - - [Some link info](https://coder.com) - \`\`\` - # This is a really long sentence to test that the code block wraps into a new line properly. - \`\`\` - `, - }, - resources: [MockWorkspaceResource, MockWorkspaceVolumeResource], - }, -}; - -SmallViewport.parameters = { - chromatic: { viewports: [600] }, -}; - export const WithDeprecatedParameters: Story = { args: { template: MockTemplate, diff --git a/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPageView.tsx b/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPageView.tsx index 1ebed63451e92..a54f96f91d57f 100644 --- a/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPageView.tsx +++ b/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPageView.tsx @@ -1,4 +1,6 @@ -import { +import { type FC, useEffect } from "react"; +import { useLocation, useNavigate } from "react-router-dom"; +import type { Template, TemplateVersion, WorkspaceResource, @@ -7,8 +9,6 @@ import { Loader } from "components/Loader/Loader"; import { Stack } from "components/Stack/Stack"; import { TemplateResourcesTable } from "components/TemplateResourcesTable/TemplateResourcesTable"; import { TemplateStats } from "./TemplateStats"; -import { FC, useEffect } from "react"; -import { useLocation, useNavigate } from "react-router-dom"; import { TemplateVersionWarnings } from "components/TemplateVersionWarnings/TemplateVersionWarnings"; export interface TemplateSummaryPageViewProps { diff --git a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.stories.tsx b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.stories.tsx index bc40bf5cd38ef..844bb33beff77 100644 --- a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.stories.tsx +++ b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.stories.tsx @@ -1,4 +1,6 @@ import { + MockFailedProvisionerJob, + MockRunningProvisionerJob, MockTemplate, MockTemplateVersion, MockTemplateVersionFileTree, @@ -15,7 +17,7 @@ import { TemplateVersionEditor } from "./TemplateVersionEditor"; import type { Meta, StoryObj } from "@storybook/react"; const meta: Meta = { - title: "components/TemplateVersionEditor", + title: "pages/TemplateVersionEditor", component: TemplateVersionEditor, args: { template: MockTemplate, @@ -34,12 +36,18 @@ export const Example: Story = {}; export const Logs = { args: { + isBuildingNewVersion: true, buildLogs: MockWorkspaceBuildLogs, + templateVersion: { + ...MockTemplateVersion, + job: MockRunningProvisionerJob, + }, }, }; export const Resources: Story = { args: { + isBuildingNewVersion: true, buildLogs: MockWorkspaceBuildLogs, resources: [ MockWorkspaceResource, @@ -54,10 +62,11 @@ export const Resources: Story = { export const ManyLogs = { args: { + isBuildingNewVersion: true, templateVersion: { ...MockTemplateVersion, job: { - ...MockTemplateVersion.job, + ...MockFailedProvisionerJob, error: "template import provision for start: terraform plan: exit status 1", }, diff --git a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx index 8bbe8947e135a..82988be7b0cca 100644 --- a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx +++ b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx @@ -1,7 +1,6 @@ import Button from "@mui/material/Button"; import IconButton from "@mui/material/IconButton"; import Link from "@mui/material/Link"; -import { makeStyles } from "@mui/styles"; import Tooltip from "@mui/material/Tooltip"; import CreateIcon from "@mui/icons-material/AddOutlined"; import BuildIcon from "@mui/icons-material/BuildOutlined"; @@ -21,7 +20,7 @@ import { AvatarData } from "components/AvatarData/AvatarData"; import { TemplateResourcesTable } from "components/TemplateResourcesTable/TemplateResourcesTable"; import { WorkspaceBuildLogs } from "components/WorkspaceBuildLogs/WorkspaceBuildLogs"; import { PublishVersionData } from "pages/TemplateVersionEditorPage/types"; -import { FC, useCallback, useEffect, useRef, useState } from "react"; +import { type FC, useCallback, useEffect, useRef, useState } from "react"; import { createFile, existsFile, @@ -46,17 +45,17 @@ import { getStatus, TemplateVersionStatusBadge, } from "./TemplateVersionStatusBadge"; -import { Theme } from "@mui/material/styles"; import AlertTitle from "@mui/material/AlertTitle"; import { DashboardFullPage } from "components/Dashboard/DashboardLayout"; +import { type Interpolation, type Theme, useTheme } from "@emotion/react"; export interface TemplateVersionEditorProps { template: Template; templateVersion: TemplateVersion; + isBuildingNewVersion: boolean; defaultFileTree: FileTree; buildLogs?: ProvisionerJobLog[]; resources?: WorkspaceResource[]; - deploymentBannerVisible?: boolean; disablePreview: boolean; disableUpdate: boolean; onPreview: (files: FileTree) => void; @@ -92,8 +91,8 @@ export const TemplateVersionEditor: FC = ({ disablePreview, disableUpdate, template, - deploymentBannerVisible, templateVersion, + isBuildingNewVersion, defaultFileTree, onPreview, onPublish, @@ -111,6 +110,7 @@ export const TemplateVersionEditor: FC = ({ onSubmitMissingVariableValues, onCancelSubmitMissingVariableValues, }) => { + const theme = useTheme(); // If resources are provided, show them by default! // This is for Storybook! const [selectedTab, setSelectedTab] = useState(() => (resources ? 1 : 0)); @@ -170,25 +170,22 @@ export const TemplateVersionEditor: FC = ({ }, [templateVersion]); const hasIcon = template.icon && template.icon !== ""; - const templateVersionSucceeded = templateVersion.job.status === "succeeded"; const showBuildLogs = Boolean(buildLogs); const editorValue = getFileContent(activePath ?? "", fileTree) as string; - const firstTemplateVersionOnEditor = useRef(templateVersion); useEffect(() => { window.dispatchEvent(new Event("resize")); }, [showBuildLogs]); - const styles = useStyles({ - templateVersionSucceeded, - showBuildLogs, - deploymentBannerVisible, - }); return ( <> - -
-
+ +
+
= ({ )} -
- {/* Only start to show the build when a new template version is building */} - {templateVersion.id !== firstTemplateVersionOnEditor.current.id && ( -
- -
+
+ {isBuildingNewVersion && ( + )}
-
-
-
+
+
+
Template files -
+
= ({ />
-
-
+
+
{activePath ? ( = ({ )}
-
-
+
+
{templateVersion.job.error && (
@@ -419,9 +423,13 @@ export const TemplateVersionEditor: FC = ({
{resources && ( = ({ )}
- - {templateVersionSucceeded && ( - <> -
- - )}
@@ -462,18 +464,8 @@ export const TemplateVersionEditor: FC = ({ ); }; -const useStyles = makeStyles< - Theme, - { - templateVersionSucceeded: boolean; - showBuildLogs: boolean; - deploymentBannerVisible?: boolean; - } ->((theme) => ({ - root: { - background: theme.palette.background.default, - }, - topbar: { +const styles = { + topbar: (theme) => ({ padding: theme.spacing(2), borderBottom: `1px solid ${theme.palette.divider}`, display: "flex", @@ -481,29 +473,24 @@ const useStyles = makeStyles< justifyContent: "space-between", height: topbarHeight, background: theme.palette.background.paper, - }, - topbarSides: { + }), + topbarSides: (theme) => ({ display: "flex", alignItems: "center", gap: theme.spacing(2), - }, - buildStatus: { - display: "flex", - alignItems: "center", - gap: 8, - }, + }), sidebarAndEditor: { display: "flex", flex: 1, flexBasis: 0, overflow: "hidden", }, - sidebar: { + sidebar: (theme) => ({ minWidth: 256, backgroundColor: theme.palette.background.paper, borderRight: `1px solid ${theme.palette.divider}`, - }, - sidebarTitle: { + }), + sidebarTitle: (theme) => ({ fontSize: 10, textTransform: "uppercase", padding: theme.spacing(1, 2), @@ -512,45 +499,33 @@ const useStyles = makeStyles< letterSpacing: "0.5px", display: "flex", alignItems: "center", - }, - sidebarActions: { + }), + sidebarActions: (theme) => ({ marginLeft: "auto", "& svg": { fill: theme.palette.text.primary, }, - }, - editorPane: { - display: "grid", - width: "100%", - gridTemplateColumns: (props) => - props.showBuildLogs ? "1fr 1fr" : "1fr 0fr", - minHeight: "100%", - overflow: "hidden", - }, + }), editor: { flex: 1, }, - panelWrapper: { + panelWrapper: (theme) => ({ flex: 1, borderLeft: `1px solid ${theme.palette.divider}`, overflow: "hidden", display: "flex", flexDirection: "column", - }, + }), panel: { overflowY: "auto", height: "100%", - "&.hidden": { - display: "none", - }, - // Hack to access customize resource-card from here "& .resource-card": { border: 0, }, }, - tabs: { + tabs: (theme) => ({ borderBottom: `1px solid ${theme.palette.divider}`, display: "flex", boxShadow: "#000000 0 6px 6px -6px inset", @@ -561,8 +536,8 @@ const useStyles = makeStyles< textTransform: "none", letterSpacing: "unset", }, - }, - tab: { + }), + tab: (theme) => ({ cursor: "pointer", padding: theme.spacing(1.5), fontSize: 10, @@ -601,8 +576,8 @@ const useStyles = makeStyles< "&:hover": { color: theme.palette.text.primary, }, - }, - tabBar: { + }), + tabBar: (theme) => ({ padding: "8px 16px", position: "sticky", top: 0, @@ -615,12 +590,5 @@ const useStyles = makeStyles< "&.top": { borderTop: `1px solid ${theme.palette.divider}`, }, - }, - buildLogs: { - display: "flex", - flexDirection: "column", - }, - resources: { - paddingBottom: theme.spacing(2), - }, -})); + }), +} satisfies Record>; diff --git a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx index 6065f61bdb833..3e571900c90f5 100644 --- a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx +++ b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx @@ -1,7 +1,6 @@ import { useMachine } from "@xstate/react"; import { TemplateVersionEditor } from "./TemplateVersionEditor"; import { useOrganizationId } from "hooks/useOrganizationId"; -import { usePermissions } from "hooks/usePermissions"; import { FC } from "react"; import { Helmet } from "react-helmet-async"; import { useNavigate, useParams } from "react-router-dom"; @@ -22,7 +21,6 @@ export const TemplateVersionEditorPage: FC = () => { const [editorState, sendEvent] = useMachine(templateVersionEditorMachine, { context: { orgId }, }); - const permissions = usePermissions(); const { isSuccess, data } = useTemplateVersionData( { orgId, @@ -45,8 +43,8 @@ export const TemplateVersionEditorPage: FC = () => { {isSuccess && ( { sendEvent({