feat: Add selected template link at the template select field (#1918)

This commit is contained in:
Bruno Quaresma
2022-05-31 13:28:22 -05:00
committed by GitHub
parent 75bcb739f9
commit 65c17a04df
10 changed files with 164 additions and 122 deletions

View File

@ -7,6 +7,7 @@ import * as TypesGen from "../../api/typesGenerated"
import { getFormHelpers, nameValidator, onChangeTrimmed } from "../../util/formUtils"
import { FormFooter } from "../FormFooter/FormFooter"
import { FullPageForm } from "../FullPageForm/FullPageForm"
import { Stack } from "../Stack/Stack"
export const Language = {
emailLabel: "Email",
@ -57,32 +58,34 @@ export const CreateUserForm: FC<CreateUserFormProps> = ({
return (
<FullPageForm title="Create user" onCancel={onCancel}>
<form onSubmit={form.handleSubmit}>
<TextField
{...getFieldHelpers("username")}
onChange={onChangeTrimmed(form)}
autoComplete="username"
autoFocus
fullWidth
label={Language.usernameLabel}
variant="outlined"
/>
<TextField
{...getFieldHelpers("email")}
onChange={onChangeTrimmed(form)}
autoComplete="email"
fullWidth
label={Language.emailLabel}
variant="outlined"
/>
<TextField
{...getFieldHelpers("password")}
autoComplete="current-password"
fullWidth
id="password"
label={Language.passwordLabel}
type="password"
variant="outlined"
/>
<Stack spacing={1}>
<TextField
{...getFieldHelpers("username")}
onChange={onChangeTrimmed(form)}
autoComplete="username"
autoFocus
fullWidth
label={Language.usernameLabel}
variant="outlined"
/>
<TextField
{...getFieldHelpers("email")}
onChange={onChangeTrimmed(form)}
autoComplete="email"
fullWidth
label={Language.emailLabel}
variant="outlined"
/>
<TextField
{...getFieldHelpers("password")}
autoComplete="current-password"
fullWidth
id="password"
label={Language.passwordLabel}
type="password"
variant="outlined"
/>
</Stack>
{error && <FormHelperText error>{error}</FormHelperText>}
<FormFooter onCancel={onCancel} isLoading={isLoading} />
</form>

View File

@ -14,16 +14,17 @@ export interface FormFooterProps {
submitLabel?: string
}
const useStyles = makeStyles(() => ({
const useStyles = makeStyles((theme) => ({
footer: {
display: "flex",
flex: "0",
flexDirection: "row",
justifyContent: "center",
gap: theme.spacing(1.5),
alignItems: "center",
marginTop: theme.spacing(3),
},
button: {
margin: "1em",
width: "100%",
},
}))

View File

@ -9,9 +9,8 @@ export interface FormTitleProps {
const useStyles = makeStyles((theme) => ({
title: {
textAlign: "center",
marginTop: theme.spacing(5),
marginBottom: theme.spacing(5),
marginTop: theme.spacing(6),
marginBottom: theme.spacing(4),
"& h3": {
marginBottom: theme.spacing(1),

View File

@ -23,7 +23,7 @@ export const FullPageForm: FC<FullPageFormProps> = ({ title, detail, onCancel, c
const styles = useStyles()
return (
<main className={styles.root}>
<Margins>
<Margins size="small">
<FormTitle title={title} detail={detail} />
<FormCloseButton onClose={onCancel} />

View File

@ -1,18 +1,30 @@
import { makeStyles } from "@material-ui/core/styles"
import { FC } from "react"
import { maxWidth, sidePadding } from "../../theme/constants"
import { containerWidth, sidePadding } from "../../theme/constants"
type Size = "regular" | "medium" | "small"
const widthBySize: Record<Size, number> = {
regular: containerWidth,
medium: containerWidth / 2,
small: containerWidth / 3,
}
const useStyles = makeStyles(() => ({
margins: {
margin: "0 auto",
maxWidth,
padding: `0 ${sidePadding}`,
maxWidth: ({ maxWidth }: { maxWidth: number }) => maxWidth,
padding: `0 ${sidePadding}px`,
flex: 1,
width: "100%",
},
}))
export const Margins: FC = ({ children }) => {
const styles = useStyles()
interface MarginsProps {
size?: Size
}
export const Margins: FC<MarginsProps> = ({ children, size = "regular" }) => {
const styles = useStyles({ maxWidth: widthBySize[size] })
return <div className={styles.margins}>{children}</div>
}

View File

@ -1,8 +1,7 @@
import FormControlLabel from "@material-ui/core/FormControlLabel"
import Paper from "@material-ui/core/Paper"
import Radio from "@material-ui/core/Radio"
import RadioGroup from "@material-ui/core/RadioGroup"
import { lighten, makeStyles } from "@material-ui/core/styles"
import { makeStyles } from "@material-ui/core/styles"
import TextField from "@material-ui/core/TextField"
import { FC } from "react"
import { ParameterSchema } from "../../api/typesGenerated"
@ -17,7 +16,7 @@ export interface ParameterInputProps {
export const ParameterInput: FC<ParameterInputProps> = ({ disabled, onChange, schema }) => {
const styles = useStyles()
return (
<Paper className={styles.paper}>
<div className={styles.root}>
<div className={styles.title}>
<h2>var.{schema.name}</h2>
{schema.description && <span>{schema.description}</span>}
@ -25,7 +24,7 @@ export const ParameterInput: FC<ParameterInputProps> = ({ disabled, onChange, sc
<div className={styles.input}>
<ParameterField disabled={disabled} onChange={onChange} schema={schema} />
</div>
</Paper>
</div>
)
}
@ -67,28 +66,26 @@ const ParameterField: React.FC<ParameterInputProps> = ({ disabled, onChange, sch
}
const useStyles = makeStyles((theme) => ({
paper: {
root: {
display: "flex",
flexDirection: "column",
fontFamily: MONOSPACE_FONT_FAMILY,
paddingTop: theme.spacing(2),
paddingBottom: theme.spacing(2),
},
title: {
background: lighten(theme.palette.background.default, 0.1),
borderBottom: `1px solid ${theme.palette.divider}`,
padding: theme.spacing(3),
display: "flex",
flexDirection: "column",
"& h2": {
margin: 0,
},
"& span": {
paddingTop: theme.spacing(2),
paddingTop: theme.spacing(1),
},
},
input: {
padding: theme.spacing(3),
marginTop: theme.spacing(2),
display: "flex",
flexDirection: "column",
maxWidth: 480,
},
}))

View File

@ -167,8 +167,8 @@ export const WorkspaceScheduleForm: FC<WorkspaceScheduleFormProps> = ({
return (
<FullPageForm onCancel={onCancel} title="Workspace Schedule">
<form className={styles.form} onSubmit={form.handleSubmit}>
<Stack className={styles.stack}>
<form onSubmit={form.handleSubmit} className={styles.form}>
<Stack>
<TextField
{...formHelpers("startTime", Language.startTimeHelperText)}
disabled={form.isSubmitting || isLoading}
@ -177,7 +177,6 @@ export const WorkspaceScheduleForm: FC<WorkspaceScheduleFormProps> = ({
}}
label={Language.startTimeLabel}
type="time"
variant="standard"
/>
<TextField
@ -195,7 +194,6 @@ export const WorkspaceScheduleForm: FC<WorkspaceScheduleFormProps> = ({
shrink: true,
}}
label={Language.timezoneLabel}
variant="standard"
/>
<FormControl component="fieldset" error={Boolean(form.errors.monday)}>
@ -212,6 +210,9 @@ export const WorkspaceScheduleForm: FC<WorkspaceScheduleFormProps> = ({
disabled={!form.values.startTime || form.isSubmitting || isLoading}
onChange={form.handleChange}
name={checkbox.name}
color="primary"
size="small"
disableRipple
/>
}
key={checkbox.name}
@ -229,7 +230,6 @@ export const WorkspaceScheduleForm: FC<WorkspaceScheduleFormProps> = ({
inputProps={{ min: 0, step: 1 }}
label={Language.ttlLabel}
type="number"
variant="standard"
/>
<FormFooter onCancel={onCancel} isLoading={form.isSubmitting || isLoading} />
@ -241,21 +241,10 @@ export const WorkspaceScheduleForm: FC<WorkspaceScheduleFormProps> = ({
const useStyles = makeStyles({
form: {
display: "flex",
justifyContent: "center",
"& input": {
colorScheme: "dark",
},
},
stack: {
// REMARK: 360 is 'arbitrary' in that it gives the helper text enough room
// to render on one line. If we change the text, we might want to
// adjust these. Without constraining the width, the date picker
// and number inputs aren't visually appealing or maximally usable.
maxWidth: 360,
minWidth: 360,
},
daysOfWeekLabel: {
fontSize: 12,
},

View File

@ -1,13 +1,16 @@
import Link from "@material-ui/core/Link"
import MenuItem from "@material-ui/core/MenuItem"
import { makeStyles } from "@material-ui/core/styles"
import TextField, { TextFieldProps } from "@material-ui/core/TextField"
import OpenInNewIcon from "@material-ui/icons/OpenInNew"
import { FormikContextType, useFormik } from "formik"
import { FC, useState } from "react"
import { Link as RouterLink } from "react-router-dom"
import * as Yup from "yup"
import * as TypesGen from "../../api/typesGenerated"
import { FormFooter } from "../../components/FormFooter/FormFooter"
import { FullPageForm } from "../../components/FullPageForm/FullPageForm"
import { Loader } from "../../components/Loader/Loader"
import { Margins } from "../../components/Margins/Margins"
import { ParameterInput } from "../../components/ParameterInput/ParameterInput"
import { Stack } from "../../components/Stack/Stack"
import { getFormHelpers, nameValidator, onChangeTrimmed } from "../../util/formUtils"
@ -35,6 +38,7 @@ export const validationSchema = Yup.object({
export const CreateWorkspacePageView: FC<CreateWorkspacePageViewProps> = (props) => {
const [parameterValues, setParameterValues] = useState<Record<string, string>>({})
const styles = useStyles()
const form: FormikContextType<TypesGen.CreateWorkspaceRequest> = useFormik<TypesGen.CreateWorkspaceRequest>({
initialValues: {
name: "",
@ -67,6 +71,10 @@ export const CreateWorkspacePageView: FC<CreateWorkspacePageViewProps> = (props)
},
})
const getFieldHelpers = getFormHelpers<TypesGen.CreateWorkspaceRequest>(form)
const selectedTemplate =
props.templates &&
form.values.template_id &&
props.templates.find((template) => template.id === form.values.template_id)
const handleTemplateChange: TextFieldProps["onChange"] = (event) => {
if (!props.templates) {
@ -85,67 +93,90 @@ export const CreateWorkspacePageView: FC<CreateWorkspacePageViewProps> = (props)
}
return (
<Margins>
<FullPageForm title="Create workspace" onCancel={props.onCancel}>
<form onSubmit={form.handleSubmit}>
{props.loadingTemplates && <Loader />}
<FullPageForm title="Create workspace" onCancel={props.onCancel}>
<form onSubmit={form.handleSubmit}>
{props.loadingTemplates && <Loader />}
<Stack>
{props.templates && (
<Stack>
{props.templates && (
<TextField
{...getFieldHelpers("template_id")}
disabled={form.isSubmitting}
onChange={handleTemplateChange}
autoFocus
fullWidth
label={Language.templateLabel}
variant="outlined"
select
helperText={
selectedTemplate && (
<Link
className={styles.readMoreLink}
component={RouterLink}
to={`/templates/${selectedTemplate.name}`}
target="_blank"
>
Read more about this template <OpenInNewIcon />
</Link>
)
}
>
{props.templates.map((template) => (
<MenuItem key={template.id} value={template.id}>
{template.name}
</MenuItem>
))}
</TextField>
)}
{props.selectedTemplate && props.templateSchema && (
<>
<TextField
{...getFieldHelpers("template_id")}
{...getFieldHelpers("name")}
disabled={form.isSubmitting}
onChange={handleTemplateChange}
onChange={onChangeTrimmed(form)}
autoFocus
fullWidth
label={Language.templateLabel}
label={Language.nameLabel}
variant="outlined"
select
>
{props.templates.map((template) => (
<MenuItem key={template.id} value={template.id}>
{template.name}
</MenuItem>
))}
</TextField>
)}
/>
{props.selectedTemplate && props.templateSchema && (
<>
<TextField
{...getFieldHelpers("name")}
disabled={form.isSubmitting}
onChange={onChangeTrimmed(form)}
autoFocus
fullWidth
label={Language.nameLabel}
variant="outlined"
/>
{props.templateSchema.length > 0 && (
<Stack>
{props.templateSchema.map((schema) => (
<ParameterInput
disabled={form.isSubmitting}
key={schema.id}
onChange={(value) => {
setParameterValues({
...parameterValues,
[schema.name]: value,
})
}}
schema={schema}
/>
))}
</Stack>
)}
{props.templateSchema.length > 0 && (
<Stack>
{props.templateSchema.map((schema) => (
<ParameterInput
disabled={form.isSubmitting}
key={schema.id}
onChange={(value) => {
setParameterValues({
...parameterValues,
[schema.name]: value,
})
}}
schema={schema}
/>
))}
</Stack>
)}
<FormFooter onCancel={props.onCancel} isLoading={props.creatingWorkspace} />
</>
)}
</Stack>
</form>
</FullPageForm>
</Margins>
<FormFooter onCancel={props.onCancel} isLoading={props.creatingWorkspace} />
</>
)}
</Stack>
</form>
</FullPageForm>
)
}
const useStyles = makeStyles((theme) => ({
readMoreLink: {
display: "flex",
alignItems: "center",
"& svg": {
width: 12,
height: 12,
marginLeft: theme.spacing(0.5),
},
},
}))

View File

@ -7,8 +7,8 @@ export const BODY_FONT_FAMILY = `"Inter", sans-serif`
export const lightButtonShadow = "0 2px 2px rgba(0, 23, 121, 0.08)"
export const emptyBoxShadow = "none"
export const navHeight = 62
export const maxWidth = 1380
export const sidePadding = "50px"
export const containerWidth = 1380
export const sidePadding = 24
export const TitleIconSize = 48
export const CardRadius = 2
export const CardPadding = 20

View File

@ -95,5 +95,15 @@ export const getOverrides = (palette: PaletteOptions) => {
border: `1px solid ${palette.divider}`,
},
},
MuiFormHelperText: {
contained: {
marginLeft: 0,
marginRight: 0,
},
marginDense: {
marginTop: 8,
},
},
}
}