Skip to content

chore: use emotion for styling (pt. 4) #10149

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 25 commits into from
Oct 10, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
emotion: Form & FormFooter
  • Loading branch information
aslilac committed Oct 9, 2023
commit b0cdf3fcce40817f601fa5033604cb72dd37f77c
173 changes: 75 additions & 98 deletions site/src/components/Form/Form.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { makeStyles } from "@mui/styles";
import { AlphaBadge } from "components/DeploySettingsLayout/Badges";
import {
FormFooterProps as BaseFormFooterProps,
FormFooter as BaseFormFooter,
} from "components/FormFooter/FormFooter";
import { Stack } from "components/Stack/Stack";
import { type Interpolation, type Theme, useTheme } from "@emotion/react";
import {
createContext,
FC,
HTMLProps,
PropsWithChildren,
type FC,
type HTMLProps,
type PropsWithChildren,
useContext,
} from "react";
import { combineClasses } from "utils/combineClasses";
import { AlphaBadge } from "components/DeploySettingsLayout/Badges";
import { Stack } from "components/Stack/Stack";
import {
FormFooter as BaseFormFooter,
FormFooterProps,
type FormFooterStyles,
} from "../FormFooter/FormFooter";

type FormContextValue = { direction?: "horizontal" | "vertical" };

Expand All @@ -24,14 +24,22 @@ type FormProps = HTMLProps<HTMLFormElement> & {
direction?: FormContextValue["direction"];
};

export const Form: FC<FormProps> = ({ direction, className, ...formProps }) => {
const styles = useStyles({ direction });
export const Form: FC<FormProps> = ({ direction, ...formProps }) => {
const theme = useTheme();

return (
<FormContext.Provider value={{ direction }}>
<form
{...formProps}
className={combineClasses([styles.form, className])}
css={{
display: "flex",
flexDirection: "column",
gap: theme.spacing(direction === "horizontal" ? 10 : 5),

[theme.breakpoints.down("md")]: {
gap: theme.spacing(8),
},
}}
/>
</FormContext.Provider>
);
Expand Down Expand Up @@ -71,28 +79,50 @@ export const FormSection: FC<
alpha?: boolean;
}
> = ({ children, title, description, classes = {}, alpha = false }) => {
const formContext = useContext(FormContext);
const styles = useStyles(formContext);
const { direction } = useContext(FormContext);
const theme = useTheme();

return (
<div className={combineClasses([styles.formSection, classes.root])}>
<div
css={{
display: "flex",
alignItems: "flex-start",
flexDirection: direction === "horizontal" ? "row" : "column",
gap: theme.spacing(direction === "horizontal" ? 15 : 3),

[theme.breakpoints.down("md")]: {
flexDirection: "column",
gap: theme.spacing(2),
},
}}
className={classes.root}
>
<div
className={combineClasses([
classes.sectionInfo,
styles.formSectionInfo,
])}
css={{
width: "100%",
maxWidth: direction === "horizontal" ? 312 : undefined,
flexShrink: 0,
position: direction === "horizontal" ? "sticky" : undefined,
top: theme.spacing(3),

[theme.breakpoints.down("md")]: {
width: "100%",
position: "initial" as const,
},
}}
className={classes.sectionInfo}
>
<h2
className={combineClasses([
css={[
styles.formSectionInfoTitle,
alpha && styles.formSectionInfoTitleAlpha,
classes.infoTitle,
])}
]}
className={classes.infoTitle}
>
{title}
{alpha && <AlphaBadge />}
</h2>
<div className={styles.formSectionInfoDescription}>{description}</div>
<div css={styles.formSectionInfoDescription}>{description}</div>
</div>

{children}
Expand All @@ -101,108 +131,55 @@ export const FormSection: FC<
};

export const FormFields: FC<PropsWithChildren> = ({ children }) => {
const styles = useStyles();
return (
<Stack
direction="column"
spacing={2.5}
className={styles.formSectionFields}
>
<Stack direction="column" spacing={2.5} css={styles.formSectionFields}>
{children}
</Stack>
);
};

export const FormFooter: FC<BaseFormFooterProps> = (props) => {
const formFooterStyles = useFormFooterStyles();
return (
<BaseFormFooter
{...props}
styles={{ ...formFooterStyles, ...props.styles }}
/>
);
};
const getFlexDirection = ({ direction }: FormContextValue = {}):
| "row"
| "column" =>
direction === "horizontal" ? ("row" as const) : ("column" as const);

const useStyles = makeStyles((theme) => ({
form: {
display: "flex",
flexDirection: "column",
gap: ({ direction }: FormContextValue = {}) =>
direction === "horizontal" ? theme.spacing(10) : theme.spacing(5),

[theme.breakpoints.down("md")]: {
gap: theme.spacing(8),
},
},

formSection: {
display: "flex",
alignItems: "flex-start",
gap: ({ direction }: FormContextValue = {}) =>
direction === "horizontal" ? theme.spacing(15) : theme.spacing(3),
flexDirection: getFlexDirection,

[theme.breakpoints.down("md")]: {
flexDirection: "column",
gap: theme.spacing(2),
},
},

formSectionInfo: {
width: "100%",
maxWidth: ({ direction }: FormContextValue = {}) =>
direction === "horizontal" ? 312 : undefined,
flexShrink: 0,
position: ({ direction }: FormContextValue = {}) =>
direction === "horizontal" ? "sticky" : undefined,
top: theme.spacing(3),

[theme.breakpoints.down("md")]: {
width: "100%",
position: "initial" as const,
},
},

formSectionInfoTitle: {
const styles = {
formSectionInfoTitle: (theme) => ({
fontSize: 20,
color: theme.palette.text.primary,
fontWeight: 400,
margin: 0,
marginBottom: theme.spacing(1),
},
}),

formSectionInfoTitleAlpha: {
formSectionInfoTitleAlpha: (theme) => ({
display: "flex",
flexDirection: "row",
alignItems: "center",
gap: theme.spacing(1.5),
},
}),

formSectionInfoDescription: {
formSectionInfoDescription: (theme) => ({
fontSize: 14,
color: theme.palette.text.secondary,
lineHeight: "160%",
margin: 0,
},
}),

formSectionFields: {
width: "100%",
},
}));
} satisfies Record<string, Interpolation<Theme>>;

export const FormFooter = (props: Exclude<FormFooterProps, "styles">) => (
<BaseFormFooter {...props} styles={footerStyles} />
);

const useFormFooterStyles = makeStyles((theme) => ({
button: {
const footerStyles = {
button: (theme) => ({
minWidth: theme.spacing(23),

[theme.breakpoints.down("md")]: {
width: "100%",
},
},
footer: {
}),

footer: (theme) => ({
display: "flex",
alignItems: "center",
justifyContent: "flex-start",
Expand All @@ -213,5 +190,5 @@ const useFormFooterStyles = makeStyles((theme) => ({
flexDirection: "column",
gap: theme.spacing(1),
},
},
}));
}),
} satisfies FormFooterStyles;
27 changes: 15 additions & 12 deletions site/src/components/FormFooter/FormFooter.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import Button from "@mui/material/Button";
import { makeStyles } from "@mui/styles";
import { ClassNameMap } from "@mui/styles/withStyles";
import { FC } from "react";
import { type FC } from "react";
import { LoadingButton } from "../LoadingButton/LoadingButton";
import { Interpolation, Theme } from "@emotion/react";

export const Language = {
cancelLabel: "Cancel",
defaultSubmitLabel: "Submit",
};

type FormFooterStyles = ClassNameMap<"footer" | "button">;
export interface FormFooterStyles {
footer: Interpolation<Theme>;
button: Interpolation<Theme>;
}

export interface FormFooterProps {
onCancel: () => void;
isLoading: boolean;
Expand All @@ -23,15 +26,15 @@ export const FormFooter: FC<FormFooterProps> = ({
isLoading,
submitDisabled,
submitLabel = Language.defaultSubmitLabel,
styles = defaultStyles(),
styles = defaultStyles,
}) => {
return (
<div className={styles.footer}>
<div css={styles.footer}>
<LoadingButton
size="large"
tabIndex={0}
loading={isLoading}
className={styles.button}
css={styles.button}
variant="contained"
color="primary"
type="submit"
Expand All @@ -43,7 +46,7 @@ export const FormFooter: FC<FormFooterProps> = ({
<Button
size="large"
type="button"
className={styles.button}
css={styles.button}
onClick={onCancel}
tabIndex={0}
>
Expand All @@ -53,8 +56,8 @@ export const FormFooter: FC<FormFooterProps> = ({
);
};

const defaultStyles = makeStyles((theme) => ({
footer: {
const defaultStyles = {
footer: (theme) => ({
display: "flex",
flex: "0",
// The first button is the submit so it is the first element to be focused
Expand All @@ -63,8 +66,8 @@ const defaultStyles = makeStyles((theme) => ({
gap: theme.spacing(1.5),
alignItems: "center",
marginTop: theme.spacing(3),
},
}),
button: {
width: "100%",
},
}));
} satisfies FormFooterStyles;