From c90c22648498cd1750234c2cde76d7aa1ddc95d2 Mon Sep 17 00:00:00 2001 From: Bruno Date: Mon, 23 May 2022 18:10:48 +0000 Subject: [PATCH 01/11] Refactor create workspace machine --- .vscode/settings.json | 3 + .../CreateWorkspacePage.tsx | 67 ++++++--- .../CreateWorkspacePageView.tsx | 108 ++++++++++---- .../createWorkspaceXService.ts | 140 ++++++++++++++++++ 4 files changed, 262 insertions(+), 56 deletions(-) create mode 100644 site/src/xServices/createWorkspace/createWorkspaceXService.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index e962a55458f04..2ea7220cd50ab 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -15,6 +15,7 @@ "drpcserver", "Dsts", "fatih", + "Formik", "goarch", "gographviz", "goleak", @@ -22,6 +23,7 @@ "gsyslog", "hashicorp", "hclsyntax", + "httpapi", "httpmw", "idtoken", "Iflag", @@ -63,6 +65,7 @@ "tfjson", "tfstate", "trimprefix", + "typegen", "unconvert", "Untar", "VMID", diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx index 177d3f1492cb6..297313e9f5e82 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx @@ -1,36 +1,55 @@ -import { useMachine } from "@xstate/react" -import React from "react" -import { useNavigate } from "react-router" -import { useParams } from "react-router-dom" -import { createWorkspace } from "../../api/api" -import { templateMachine } from "../../xServices/template/templateXService" +import { useActor, useMachine } from "@xstate/react" +import React, { useContext } from "react" +import { useNavigate } from "react-router-dom" +import { Template } from "../../api/typesGenerated" +import { createWorkspaceMachine } from "../../xServices/createWorkspace/createWorkspaceXService" +import { XServiceContext } from "../../xServices/StateContext" import { CreateWorkspacePageView } from "./CreateWorkspacePageView" +const useOrganizationId = () => { + const xServices = useContext(XServiceContext) + const [authState] = useActor(xServices.authXService) + const organizationId = authState.context.me?.organization_ids[0] + + if (!organizationId) { + throw new Error("No organization ID found") + } + + return organizationId +} + const CreateWorkspacePage: React.FC = () => { - const { template } = useParams() - const [templateState] = useMachine(templateMachine, { - context: { - name: template, + const organizationId = useOrganizationId() + const navigate = useNavigate() + const [createWorkspaceState, send] = useMachine(createWorkspaceMachine, { + context: { organizationId }, + actions: { + onCreateWorkspace: (_, event) => { + navigate("/workspaces/" + event.data.id) + }, }, }) - const navigate = useNavigate() - const loading = templateState.hasTag("loading") - if (!templateState.context.template || !templateState.context.templateSchema) { - return null - } return ( navigate("/templates")} - onSubmit={async (req) => { - if (!templateState.context.template) { - throw new Error("template isn't valid") - } - const workspace = await createWorkspace(templateState.context.template.organization_id, req) - navigate("/workspaces/" + workspace.id) + onSubmit={(request) => { + send({ + type: "CREATE_WORKSPACE", + request, + }) + }} + onSelectTemplate={(template: Template) => { + send({ + type: "SELECT_TEMPLATE", + template, + }) }} /> ) diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx index cdc24710ccf6a..95133d9287e1b 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx @@ -1,5 +1,6 @@ +import MenuItem from "@material-ui/core/MenuItem" import { makeStyles } from "@material-ui/core/styles" -import TextField from "@material-ui/core/TextField" +import TextField, { TextFieldProps } from "@material-ui/core/TextField" import { FormikContextType, useFormik } from "formik" import React from "react" import * as Yup from "yup" @@ -11,6 +12,7 @@ import { ParameterInput } from "../../components/ParameterInput/ParameterInput" import { getFormHelpers, onChangeTrimmed } from "../../util/formUtils" export const Language = { + templateLabel: "Template", nameLabel: "Name", nameRequired: "Please enter a name.", nameMatches: "Name must start with a-Z or 0-9 and can contain a-Z, 0-9 or -", @@ -24,12 +26,15 @@ const maxLenName = 32 const usernameRE = /^[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*$/ export interface CreateWorkspacePageViewProps { - loading?: boolean - template: TypesGen.Template - templateSchema: TypesGen.ParameterSchema[] - + loadingTemplates: boolean + loadingTemplateSchema: boolean + creatingWorkspace: boolean + templates?: TypesGen.Template[] + selectedTemplate?: TypesGen.Template + templateSchema?: TypesGen.ParameterSchema[] onCancel: () => void - onSubmit: (req: TypesGen.CreateWorkspaceRequest) => Promise + onSubmit: (req: TypesGen.CreateWorkspaceRequest) => void + onSelectTemplate: (template: TypesGen.Template) => void } export const validationSchema = Yup.object({ @@ -45,10 +50,14 @@ export const CreateWorkspacePageView: React.FC = ( const form: FormikContextType = useFormik({ initialValues: { name: "", - template_id: props.template.id, + template_id: "", }, validationSchema, onSubmit: (request) => { + if (!props.templateSchema) { + throw new Error("No template schema loaded") + } + const createRequests: TypesGen.CreateParameterRequest[] = [] props.templateSchema.forEach((schema) => { let value = schema.default_source_value @@ -70,38 +79,73 @@ export const CreateWorkspacePageView: React.FC = ( }) const getFieldHelpers = getFormHelpers(form) + const handleTemplateChange: TextFieldProps["onChange"] = (event) => { + if (!props.templates) { + throw new Error("Templates are not loaded") + } + + // The TextField + MenuItem returns the index of the selected option + const templateIndex = Number(event.currentTarget.value) + const selectedTemplate = props.templates[templateIndex] + form.setFieldValue("template_id", selectedTemplate.id) + props.onSelectTemplate(selectedTemplate) + } + return (
- - {props.templateSchema.length > 0 && ( -
- {props.templateSchema.map((schema) => ( - { - setParameterValues({ - ...parameterValues, - [schema.name]: value, - }) - }} - schema={schema} - /> + {props.templates && ( + + {props.templates.map((template) => ( + + {template.name} + ))} -
+
+ )} + + {props.selectedTemplate && props.templateSchema && ( + <> + + {props.templateSchema.length > 0 && ( +
+ {props.templateSchema.map((schema) => ( + { + setParameterValues({ + ...parameterValues, + [schema.name]: value, + }) + }} + schema={schema} + /> + ))} +
+ )} + )} - +
diff --git a/site/src/xServices/createWorkspace/createWorkspaceXService.ts b/site/src/xServices/createWorkspace/createWorkspaceXService.ts new file mode 100644 index 0000000000000..9ec7b15c38c97 --- /dev/null +++ b/site/src/xServices/createWorkspace/createWorkspaceXService.ts @@ -0,0 +1,140 @@ +import { assign, createMachine } from "xstate" +import { createWorkspace, getTemplates, getTemplateVersionSchema } from "../../api/api" +import { CreateWorkspaceRequest, ParameterSchema, Template, Workspace } from "../../api/typesGenerated" + +type CreateWorkspaceContext = { + organizationId: string + templates?: Template[] + selectedTemplate?: Template + templateSchema?: ParameterSchema[] + createWorkspaceRequest?: CreateWorkspaceRequest + createdWorkspace?: Workspace +} + +type CreateWorkspaceEvent = + | { + type: "SELECT_TEMPLATE" + template: Template + } + | { + type: "CREATE_WORKSPACE" + request: CreateWorkspaceRequest + } + +export const createWorkspaceMachine = createMachine( + { + id: "createWorkspaceState", + initial: "gettingTemplates", + schema: { + context: {} as CreateWorkspaceContext, + events: {} as CreateWorkspaceEvent, + services: {} as { + getTemplates: { + data: Template[] + } + getTemplateSchema: { + data: ParameterSchema[] + } + createWorkspace: { + data: Workspace + } + }, + }, + tsTypes: {} as import("./createWorkspaceXService.typegen").Typegen0, + states: { + gettingTemplates: { + invoke: { + src: "getTemplates", + onDone: { + actions: ["assignTemplates"], + target: "selectingTemplate", + }, + onError: { + target: "error", + }, + }, + }, + selectingTemplate: { + on: { + SELECT_TEMPLATE: { + actions: ["assignSelectedTemplate"], + target: "gettingTemplateSchema", + }, + }, + }, + gettingTemplateSchema: { + invoke: { + src: "getTemplateSchema", + onDone: { + actions: ["assignTemplateSchema"], + target: "fillingForm", + }, + onError: { + target: "error", + }, + }, + }, + fillingForm: { + on: { + CREATE_WORKSPACE: { + actions: ["assignCreateWorkspaceRequest"], + target: "creatingWorkspace", + }, + }, + }, + creatingWorkspace: { + invoke: { + src: "createWorkspace", + onDone: { + actions: ["onCreateWorkspace"], + target: "created", + }, + onError: { + target: "error", + }, + }, + }, + created: { + type: "final", + }, + error: {}, + }, + }, + { + services: { + getTemplates: (context) => getTemplates(context.organizationId), + getTemplateSchema: (context) => { + const { selectedTemplate } = context + + if (!selectedTemplate) { + throw new Error("No selected template") + } + + return getTemplateVersionSchema(selectedTemplate.active_version_id) + }, + createWorkspace: (context) => { + const { createWorkspaceRequest, organizationId } = context + + if (!createWorkspaceRequest) { + throw new Error("No create workspace request") + } + + return createWorkspace(organizationId, createWorkspaceRequest) + }, + }, + actions: { + assignTemplates: assign({ + templates: (_, event) => event.data, + }), + assignSelectedTemplate: assign({ + selectedTemplate: (_, event) => event.template, + }), + assignTemplateSchema: assign({ + templateSchema: (_, event) => event.data, + }), + assignCreateWorkspaceRequest: assign({ + createWorkspaceRequest: (_, event) => event.request, + }), + }, + }, +) From 6f6b9109debf2c112014432f02d308276f7bfd0c Mon Sep 17 00:00:00 2001 From: Bruno Date: Mon, 23 May 2022 18:15:52 +0000 Subject: [PATCH 02/11] Update router --- site/src/AppRouter.tsx | 21 +++++++++---------- .../WorkspacesPage/WorkspacesPageView.tsx | 2 +- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/site/src/AppRouter.tsx b/site/src/AppRouter.tsx index b11a33d3e39cc..43150232fb8ce 100644 --- a/site/src/AppRouter.tsx +++ b/site/src/AppRouter.tsx @@ -56,6 +56,16 @@ export const AppRouter: React.FC = () => ( } /> + + + + + } + /> + ( } /> - - - - - - } - /> - diff --git a/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx b/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx index e1f173675ccd5..48696fb46ae25 100644 --- a/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx +++ b/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx @@ -39,7 +39,7 @@ export const WorkspacesPageView: React.FC = (props) =>
- +
From 679cb4acd29089bb6e21ba153e52b04cacd19b30 Mon Sep 17 00:00:00 2001 From: Bruno Date: Mon, 23 May 2022 18:18:03 +0000 Subject: [PATCH 03/11] Fix route --- site/src/AppRouter.tsx | 4 ++-- site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/site/src/AppRouter.tsx b/site/src/AppRouter.tsx index 43150232fb8ce..4ec0f463cae26 100644 --- a/site/src/AppRouter.tsx +++ b/site/src/AppRouter.tsx @@ -60,9 +60,9 @@ export const AppRouter: React.FC = () => ( + - + } /> diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx index 297313e9f5e82..3aee46c93d24d 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx @@ -38,7 +38,7 @@ const CreateWorkspacePage: React.FC = () => { templates={createWorkspaceState.context.templates} selectedTemplate={createWorkspaceState.context.selectedTemplate} templateSchema={createWorkspaceState.context.templateSchema} - onCancel={() => navigate("/templates")} + onCancel={() => navigate("/workspaces")} onSubmit={(request) => { send({ type: "CREATE_WORKSPACE", From ef436841e787f782ba2699331ce8d727bf8c6b70 Mon Sep 17 00:00:00 2001 From: Bruno Date: Mon, 23 May 2022 18:19:01 +0000 Subject: [PATCH 04/11] Move buttons to condition --- .../src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx index 95133d9287e1b..3f45a53bca740 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx @@ -142,10 +142,10 @@ export const CreateWorkspacePageView: React.FC = ( ))} )} + + )} - -
From 4af315ba8ac4b4848a5f38fc9bdc0ade4a42e172 Mon Sep 17 00:00:00 2001 From: Bruno Date: Mon, 23 May 2022 18:25:30 +0000 Subject: [PATCH 05/11] Add minor adjustments on spacing --- .../CreateWorkspacePageView.tsx | 106 +++++++++--------- 1 file changed, 51 insertions(+), 55 deletions(-) diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx index 3f45a53bca740..6fbce139e5a9f 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx @@ -1,5 +1,4 @@ import MenuItem from "@material-ui/core/MenuItem" -import { makeStyles } from "@material-ui/core/styles" import TextField, { TextFieldProps } from "@material-ui/core/TextField" import { FormikContextType, useFormik } from "formik" import React from "react" @@ -7,8 +6,10 @@ 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, onChangeTrimmed } from "../../util/formUtils" export const Language = { @@ -45,7 +46,6 @@ export const validationSchema = Yup.object({ }) export const CreateWorkspacePageView: React.FC = (props) => { - const styles = useStyles() const [parameterValues, setParameterValues] = React.useState>({}) const form: FormikContextType = useFormik({ initialValues: { @@ -95,68 +95,64 @@ export const CreateWorkspacePageView: React.FC = (
- {props.templates && ( - - {props.templates.map((template) => ( - - {template.name} - - ))} - - )} + {props.loadingTemplates && } - {props.selectedTemplate && props.templateSchema && ( - <> + + {props.templates && ( - {props.templateSchema.length > 0 && ( -
- {props.templateSchema.map((schema) => ( - { - setParameterValues({ - ...parameterValues, - [schema.name]: value, - }) - }} - schema={schema} - /> - ))} -
- )} + select + > + {props.templates.map((template) => ( + + {template.name} + + ))} +
+ )} - - - )} + {props.selectedTemplate && props.templateSchema && ( + <> + + + {props.templateSchema.length > 0 && ( + + {props.templateSchema.map((schema) => ( + { + setParameterValues({ + ...parameterValues, + [schema.name]: value, + }) + }} + schema={schema} + /> + ))} + + )} + + + + )} +
) } - -const useStyles = makeStyles((theme) => ({ - parameters: { - paddingTop: theme.spacing(4), - "& > *": { - marginBottom: theme.spacing(4), - }, - }, -})) From 20f8742108b64e0d42b230b3beb9f2b9196cf361 Mon Sep 17 00:00:00 2001 From: Bruno Date: Mon, 23 May 2022 19:20:35 +0000 Subject: [PATCH 06/11] Accept pre defined template --- .../CreateWorkspacePage.tsx | 6 ++- .../CreateWorkspacePageView.tsx | 13 ++++-- .../pages/TemplatesPage/TemplatesPageView.tsx | 6 ++- .../createWorkspaceXService.ts | 43 ++++++++++++++++--- 4 files changed, 55 insertions(+), 13 deletions(-) diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx index 3aee46c93d24d..639f03a10350e 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx @@ -1,6 +1,6 @@ import { useActor, useMachine } from "@xstate/react" import React, { useContext } from "react" -import { useNavigate } from "react-router-dom" +import { useNavigate, useSearchParams } from "react-router-dom" import { Template } from "../../api/typesGenerated" import { createWorkspaceMachine } from "../../xServices/createWorkspace/createWorkspaceXService" import { XServiceContext } from "../../xServices/StateContext" @@ -20,9 +20,11 @@ const useOrganizationId = () => { const CreateWorkspacePage: React.FC = () => { const organizationId = useOrganizationId() + const [searchParams] = useSearchParams() + const preSelectedTemplateName = searchParams.get("template") const navigate = useNavigate() const [createWorkspaceState, send] = useMachine(createWorkspaceMachine, { - context: { organizationId }, + context: { organizationId, preSelectedTemplateName }, actions: { onCreateWorkspace: (_, event) => { navigate("/workspaces/" + event.data.id) diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx index 6fbce139e5a9f..3370c3bef2340 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx @@ -50,8 +50,9 @@ export const CreateWorkspacePageView: React.FC = ( const form: FormikContextType = useFormik({ initialValues: { name: "", - template_id: "", + template_id: props.selectedTemplate ? props.selectedTemplate.id : "", }, + enableReinitialize: true, validationSchema, onSubmit: (request) => { if (!props.templateSchema) { @@ -84,9 +85,13 @@ export const CreateWorkspacePageView: React.FC = ( throw new Error("Templates are not loaded") } - // The TextField + MenuItem returns the index of the selected option - const templateIndex = Number(event.currentTarget.value) - const selectedTemplate = props.templates[templateIndex] + const templateId = event.target.value + const selectedTemplate = props.templates.find((template) => template.id === templateId) + + if (!selectedTemplate) { + throw new Error(`Template ${templateId} not found`) + } + form.setFieldValue("template_id", selectedTemplate.id) props.onSelectTemplate(selectedTemplate) } diff --git a/site/src/pages/TemplatesPage/TemplatesPageView.tsx b/site/src/pages/TemplatesPage/TemplatesPageView.tsx index 122863beb3aa4..a48032e39bc29 100644 --- a/site/src/pages/TemplatesPage/TemplatesPageView.tsx +++ b/site/src/pages/TemplatesPage/TemplatesPageView.tsx @@ -83,7 +83,11 @@ export const TemplatesPageView: React.FC = (props) => { {firstLetter(template.name)} - + {template.name} {template.description} diff --git a/site/src/xServices/createWorkspace/createWorkspaceXService.ts b/site/src/xServices/createWorkspace/createWorkspaceXService.ts index 9ec7b15c38c97..2988972fed76d 100644 --- a/site/src/xServices/createWorkspace/createWorkspaceXService.ts +++ b/site/src/xServices/createWorkspace/createWorkspaceXService.ts @@ -9,6 +9,10 @@ type CreateWorkspaceContext = { templateSchema?: ParameterSchema[] createWorkspaceRequest?: CreateWorkspaceRequest createdWorkspace?: Workspace + // This is useful when the user wants to create a workspace from the template + // page having it pre selected. It is string or null because of the + // useSearchQuery + preSelectedTemplateName: string | null } type CreateWorkspaceEvent = @@ -45,10 +49,17 @@ export const createWorkspaceMachine = createMachine( gettingTemplates: { invoke: { src: "getTemplates", - onDone: { - actions: ["assignTemplates"], - target: "selectingTemplate", - }, + onDone: [ + { + actions: ["assignTemplates", "assignPreSelectedTemplate"], + target: "gettingTemplateSchema", + cond: "hasValidPreSelectedTemplate", + }, + { + actions: ["assignTemplates"], + target: "selectingTemplate", + }, + ], onError: { target: "error", }, @@ -67,14 +78,14 @@ export const createWorkspaceMachine = createMachine( src: "getTemplateSchema", onDone: { actions: ["assignTemplateSchema"], - target: "fillingForm", + target: "fillingParams", }, onError: { target: "error", }, }, }, - fillingForm: { + fillingParams: { on: { CREATE_WORKSPACE: { actions: ["assignCreateWorkspaceRequest"], @@ -122,6 +133,15 @@ export const createWorkspaceMachine = createMachine( return createWorkspace(organizationId, createWorkspaceRequest) }, }, + guards: { + hasValidPreSelectedTemplate: (ctx, event) => { + if (!ctx.preSelectedTemplateName) { + return false + } + const template = event.data.find((template) => template.name === ctx.preSelectedTemplateName) + return !!template + }, + }, actions: { assignTemplates: assign({ templates: (_, event) => event.data, @@ -135,6 +155,17 @@ export const createWorkspaceMachine = createMachine( assignCreateWorkspaceRequest: assign({ createWorkspaceRequest: (_, event) => event.request, }), + assignPreSelectedTemplate: assign({ + selectedTemplate: (ctx, event) => { + const selectedTemplate = event.data.find((template) => template.name === ctx.preSelectedTemplateName) + // The proper validation happens on hasValidPreSelectedTemplate + if (!selectedTemplate) { + throw new Error("Invalid template selected") + } + + return selectedTemplate + }, + }), }, }, ) From 3b7d3d079b2110177ae135072c9ae2adf2ce46a0 Mon Sep 17 00:00:00 2001 From: Bruno Date: Mon, 23 May 2022 19:47:12 +0000 Subject: [PATCH 07/11] Fix storybooks --- site/src/components/FormFooter/FormFooter.tsx | 2 +- .../CreateWorkspacePageView.stories.tsx | 31 +++++++++++++++++-- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/site/src/components/FormFooter/FormFooter.tsx b/site/src/components/FormFooter/FormFooter.tsx index 03233f42b720a..ec41d99a38ac9 100644 --- a/site/src/components/FormFooter/FormFooter.tsx +++ b/site/src/components/FormFooter/FormFooter.tsx @@ -35,7 +35,7 @@ export const FormFooter: React.FC = ({ const styles = useStyles() return (
- diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx index 48f0131f1b9dc..0f27c89a201c7 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx @@ -1,9 +1,32 @@ import { ComponentMeta, Story } from "@storybook/react" import React from "react" -import { createParameterSchema } from "../../components/ParameterInput/ParameterInput.stories" +import { ParameterSchema } from "../../api/typesGenerated" import { MockTemplate } from "../../testHelpers/entities" import { CreateWorkspacePageView, CreateWorkspacePageViewProps } from "./CreateWorkspacePageView" +const createParameterSchema = (partial: Partial): ParameterSchema => { + return { + id: "000000", + job_id: "000000", + allow_override_destination: false, + allow_override_source: true, + created_at: "", + default_destination_scheme: "none", + default_refresh: "", + default_source_scheme: "data", + default_source_value: "default-value", + name: "parameter name", + description: "Some description!", + redisplay_value: false, + validation_condition: "", + validation_contains: [], + validation_error: "", + validation_type_system: "", + validation_value_type: "", + ...partial, + } +} + export default { title: "pages/CreateWorkspacePageView", component: CreateWorkspacePageView, @@ -13,13 +36,15 @@ const Template: Story = (args) => Date: Mon, 23 May 2022 19:49:56 +0000 Subject: [PATCH 08/11] Fix cancel redirect --- site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx index 639f03a10350e..ae71dd5cf4319 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx @@ -40,7 +40,9 @@ const CreateWorkspacePage: React.FC = () => { templates={createWorkspaceState.context.templates} selectedTemplate={createWorkspaceState.context.selectedTemplate} templateSchema={createWorkspaceState.context.templateSchema} - onCancel={() => navigate("/workspaces")} + onCancel={() => { + navigate(preSelectedTemplateName ? "/templates" : "/workspaces") + }} onSubmit={(request) => { send({ type: "CREATE_WORKSPACE", From eeeda45462197cc22a6abb63f8a8ba5a0b76c501 Mon Sep 17 00:00:00 2001 From: Bruno Date: Mon, 23 May 2022 19:53:18 +0000 Subject: [PATCH 09/11] Fix tests --- .../CreateWorkspacePage.test.tsx | 2 +- .../xServices/template/templateXService.ts | 167 ------------------ 2 files changed, 1 insertion(+), 168 deletions(-) delete mode 100644 site/src/xServices/template/templateXService.ts diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx index 57b0cebdd72f7..cb82ba198b0e0 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx @@ -20,7 +20,7 @@ const nameSchema = reach(validationSchema, "name") as StringSchema describe("CreateWorkspacePage", () => { beforeEach(() => { - history.replace("/templates/" + MockTemplate.name + "/new") + history.replace("/workspaces/new?template=" + MockTemplate.name) }) it("renders", async () => { diff --git a/site/src/xServices/template/templateXService.ts b/site/src/xServices/template/templateXService.ts deleted file mode 100644 index f2e610e4b5a24..0000000000000 --- a/site/src/xServices/template/templateXService.ts +++ /dev/null @@ -1,167 +0,0 @@ -import { assign, createMachine } from "xstate" -import * as API from "../../api/api" -import * as TypesGen from "../../api/typesGenerated" - -interface TemplateContext { - name: string - - organizations?: TypesGen.Organization[] - organizationsError?: Error | unknown - template?: TypesGen.Template - templateError?: Error | unknown - templateVersion?: TypesGen.TemplateVersion - templateVersionError?: Error | unknown - templateSchema?: TypesGen.ParameterSchema[] - templateSchemaError?: Error | unknown -} - -export const templateMachine = createMachine( - { - tsTypes: {} as import("./templateXService.typegen").Typegen0, - schema: { - context: {} as TemplateContext, - services: {} as { - getOrganizations: { - data: TypesGen.Organization[] - } - getTemplate: { - data: TypesGen.Template - } - getTemplateVersion: { - data: TypesGen.TemplateVersion - } - getTemplateSchema: { - data: TypesGen.ParameterSchema[] - } - }, - }, - id: "templateState", - initial: "gettingOrganizations", - states: { - gettingOrganizations: { - entry: "clearOrganizationsError", - invoke: { - src: "getOrganizations", - id: "getOrganizations", - onDone: [ - { - actions: ["assignOrganizations", "clearOrganizationsError"], - target: "gettingTemplate", - }, - ], - onError: [ - { - actions: "assignOrganizationsError", - target: "error", - }, - ], - }, - tags: "loading", - }, - gettingTemplate: { - entry: "clearTemplateError", - invoke: { - src: "getTemplate", - id: "getTemplate", - onDone: { - target: "gettingTemplateVersion", - actions: ["assignTemplate", "clearTemplateError"], - }, - onError: { - target: "error", - actions: "assignTemplateError", - }, - }, - tags: "loading", - }, - gettingTemplateVersion: { - entry: "clearTemplateVersionError", - invoke: { - src: "getTemplateVersion", - id: "getTemplateVersion", - onDone: { - target: "gettingTemplateSchema", - actions: ["assignTemplateVersion", "clearTemplateVersionError"], - }, - onError: { - target: "error", - actions: "assignTemplateVersionError", - }, - }, - }, - gettingTemplateSchema: { - entry: "clearTemplateSchemaError", - invoke: { - src: "getTemplateSchema", - id: "getTemplateSchema", - onDone: { - target: "done", - actions: ["assignTemplateSchema", "clearTemplateSchemaError"], - }, - onError: { - target: "error", - actions: "assignTemplateSchemaError", - }, - }, - }, - done: {}, - error: {}, - }, - }, - { - actions: { - assignOrganizations: assign({ - organizations: (_, event) => event.data, - }), - assignOrganizationsError: assign({ - organizationsError: (_, event) => event.data, - }), - clearOrganizationsError: assign((context) => ({ - ...context, - organizationsError: undefined, - })), - assignTemplate: assign({ - template: (_, event) => event.data, - }), - assignTemplateError: assign({ - templateError: (_, event) => event.data, - }), - clearTemplateError: (context) => assign({ ...context, templateError: undefined }), - assignTemplateVersion: assign({ - templateVersion: (_, event) => event.data, - }), - assignTemplateVersionError: assign({ - templateVersionError: (_, event) => event.data, - }), - clearTemplateVersionError: (context) => assign({ ...context, templateVersionError: undefined }), - assignTemplateSchema: assign({ - templateSchema: (_, event) => event.data, - }), - assignTemplateSchemaError: assign({ - templateSchemaError: (_, event) => event.data, - }), - clearTemplateSchemaError: (context) => assign({ ...context, templateSchemaError: undefined }), - }, - services: { - getOrganizations: API.getOrganizations, - getTemplate: async (context) => { - if (!context.organizations || context.organizations.length === 0) { - throw new Error("no organizations") - } - return API.getTemplateByName(context.organizations[0].id, context.name) - }, - getTemplateVersion: async (context) => { - if (!context.template) { - throw new Error("no template") - } - return API.getTemplateVersion(context.template.active_version_id) - }, - getTemplateSchema: async (context) => { - if (!context.templateVersion) { - throw new Error("no template version") - } - return API.getTemplateVersionSchema(context.templateVersion.id) - }, - }, - }, -) From 58f92314c690ccc63ac3a9efbd20be355462dbd9 Mon Sep 17 00:00:00 2001 From: Bruno Date: Tue, 24 May 2022 12:26:07 +0000 Subject: [PATCH 10/11] Fix test --- .../pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx index cb82ba198b0e0..38f6c243dc270 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx @@ -5,7 +5,7 @@ import { reach, StringSchema } from "yup" import * as API from "../../api/api" import { Language as FooterLanguage } from "../../components/FormFooter/FormFooter" import { MockTemplate, MockWorkspace } from "../../testHelpers/entities" -import { history, render } from "../../testHelpers/renderHelpers" +import { history, render, renderWithAuth } from "../../testHelpers/renderHelpers" import CreateWorkspacePage from "./CreateWorkspacePage" import { Language, validationSchema } from "./CreateWorkspacePageView" @@ -24,7 +24,7 @@ describe("CreateWorkspacePage", () => { }) it("renders", async () => { - render() + renderWithAuth() const element = await screen.findByText("Create workspace") expect(element).toBeDefined() }) From 48088e30d19c87cbdb06633c66e5f85ade976c8d Mon Sep 17 00:00:00 2001 From: Bruno Date: Tue, 24 May 2022 12:44:05 +0000 Subject: [PATCH 11/11] Fix tests --- .../CreateWorkspacePage.test.tsx | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx index 38f6c243dc270..a0f11bd6e5f1a 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx @@ -5,10 +5,17 @@ import { reach, StringSchema } from "yup" import * as API from "../../api/api" import { Language as FooterLanguage } from "../../components/FormFooter/FormFooter" import { MockTemplate, MockWorkspace } from "../../testHelpers/entities" -import { history, render, renderWithAuth } from "../../testHelpers/renderHelpers" +import { renderWithAuth } from "../../testHelpers/renderHelpers" import CreateWorkspacePage from "./CreateWorkspacePage" import { Language, validationSchema } from "./CreateWorkspacePageView" +const renderCreateWorkspacePage = () => { + return renderWithAuth(, { + route: "/workspaces/new?template=" + MockTemplate.name, + path: "/workspaces/new", + }) +} + const fillForm = async ({ name = "example" }: { name?: string }) => { const nameField = await screen.findByLabelText(Language.nameLabel) await userEvent.type(nameField, name) @@ -19,25 +26,21 @@ const fillForm = async ({ name = "example" }: { name?: string }) => { const nameSchema = reach(validationSchema, "name") as StringSchema describe("CreateWorkspacePage", () => { - beforeEach(() => { - history.replace("/workspaces/new?template=" + MockTemplate.name) - }) - it("renders", async () => { - renderWithAuth() + renderCreateWorkspacePage() const element = await screen.findByText("Create workspace") expect(element).toBeDefined() }) it("shows validation error message", async () => { - render() + renderCreateWorkspacePage() await fillForm({ name: "$$$" }) const errorMessage = await screen.findByText(Language.nameMatches) expect(errorMessage).toBeDefined() }) it("succeeds", async () => { - render() + renderCreateWorkspacePage() // You have to spy the method before it is used. jest.spyOn(API, "createWorkspace").mockResolvedValueOnce(MockWorkspace) await fillForm({ name: "test" }) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy