diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx index 76f86b4bff53d..bc48e8e74d81f 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx @@ -233,19 +233,6 @@ describe("CreateWorkspacePage", () => { ); }); - it("external auth errors if unauthenticated", async () => { - jest - .spyOn(API, "getTemplateVersionExternalAuth") - .mockResolvedValueOnce([MockTemplateVersionExternalAuthGithub]); - - renderCreateWorkspacePage(); - await waitForLoaderToBeRemoved(); - - await screen.findByText( - "To create a workspace using the selected template, please ensure you are authenticated with all the external providers listed below.", - ); - }); - it("auto create a workspace if uses mode=auto", async () => { const param = "first_parameter"; const paramValue = "It works!"; @@ -312,7 +299,7 @@ describe("CreateWorkspacePage", () => { route: `/templates/${MockWorkspace.name}/workspace?${params.toString()}`, }); - const warningMessage = await screen.findByRole("alert"); + const warningMessage = await screen.findByTestId("duplication-warning"); const nameInput = await screen.findByRole("textbox", { name: "Workspace Name", }); diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx index 5e24527f46541..9c9d64be0c8db 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx @@ -126,6 +126,80 @@ export const ExternalAuth: Story = { authenticate_url: "", display_icon: "/icon/gitlab.svg", display_name: "GitLab", + optional: true, + }, + ], + }, +}; + +export const ExternalAuthError: Story = { + args: { + error: true, + externalAuth: [ + { + id: "github", + type: "github", + authenticated: false, + authenticate_url: "", + display_icon: "/icon/github.svg", + display_name: "GitHub", + }, + { + id: "gitlab", + type: "gitlab", + authenticated: false, + authenticate_url: "", + display_icon: "/icon/gitlab.svg", + display_name: "GitLab", + optional: true, + }, + ], + }, +}; + +export const ExternalAuthAllRequiredConnected: Story = { + args: { + externalAuth: [ + { + id: "github", + type: "github", + authenticated: true, + authenticate_url: "", + display_icon: "/icon/github.svg", + display_name: "GitHub", + }, + { + id: "gitlab", + type: "gitlab", + authenticated: false, + authenticate_url: "", + display_icon: "/icon/gitlab.svg", + display_name: "GitLab", + optional: true, + }, + ], + }, +}; + +export const ExternalAuthAllConnected: Story = { + args: { + externalAuth: [ + { + id: "github", + type: "github", + authenticated: true, + authenticate_url: "", + display_icon: "/icon/github.svg", + display_name: "GitHub", + }, + { + id: "gitlab", + type: "gitlab", + authenticated: true, + authenticate_url: "", + display_icon: "/icon/gitlab.svg", + display_name: "GitLab", + optional: true, }, ], }, diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx index 9f52bd254191c..a3ef3163775c8 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx @@ -147,6 +147,10 @@ export const CreateWorkspacePageView: FC = ({ ); }, [autofillParameters]); + const hasAllRequiredExternalAuth = externalAuth.every( + (auth) => auth.optional || auth.authenticated, + ); + return ( Cancel}> @@ -179,7 +183,7 @@ export const CreateWorkspacePageView: FC = ({ {Boolean(error) && } {mode === "duplicate" && ( - + {Language.duplicationWarning} )} @@ -248,21 +252,19 @@ export const CreateWorkspacePageView: FC = ({ {externalAuth && externalAuth.length > 0 && ( - {requiresExternalAuth && ( - // This should really be a `notice` but `severity` is a MUI prop, and we'd need - // to basically make our own `Alert` component. - - To create a workspace using the selected template, please - ensure you are authenticated with all the external providers - listed below. + {Boolean(error) && !hasAllRequiredExternalAuth && ( + + To create a workspace using this template, please connect to + all required external authentication providers listed below. )} {externalAuth.map((auth) => ( = ({ diff --git a/site/src/pages/CreateWorkspacePage/ExternalAuthButton.stories.tsx b/site/src/pages/CreateWorkspacePage/ExternalAuthButton.stories.tsx index 97c9d743552ad..6fe8457d06b89 100644 --- a/site/src/pages/CreateWorkspacePage/ExternalAuthButton.stories.tsx +++ b/site/src/pages/CreateWorkspacePage/ExternalAuthButton.stories.tsx @@ -12,7 +12,7 @@ const MockExternalAuth: TemplateVersionExternalAuth = { }; const meta: Meta = { - title: "pages/CreateWorkspacePage/ExternalAuth", + title: "pages/CreateWorkspacePage/ExternalAuthButton", component: ExternalAuthButton, }; @@ -25,6 +25,15 @@ export const Github: Story = { }, }; +export const GithubOptional: Story = { + args: { + auth: { + ...MockExternalAuth, + optional: true, + }, + }, +}; + export const GithubWithRetry: Story = { args: { auth: MockExternalAuth, @@ -48,6 +57,7 @@ export const Gitlab: Story = { display_icon: "/icon/gitlab.svg", display_name: "GitLab", authenticated: false, + optional: true, }, }, }; @@ -70,6 +80,7 @@ export const AzureDevOps: Story = { display_icon: "/icon/azure-devops.svg", display_name: "Azure DevOps", authenticated: false, + optional: true, }, }, }; @@ -92,6 +103,7 @@ export const Bitbucket: Story = { display_icon: "/icon/bitbucket.svg", display_name: "Bitbucket", authenticated: false, + optional: true, }, }, }; diff --git a/site/src/pages/CreateWorkspacePage/ExternalAuthButton.tsx b/site/src/pages/CreateWorkspacePage/ExternalAuthButton.tsx index 3412a9aac0b3d..331ea0f22f9e4 100644 --- a/site/src/pages/CreateWorkspacePage/ExternalAuthButton.tsx +++ b/site/src/pages/CreateWorkspacePage/ExternalAuthButton.tsx @@ -1,17 +1,19 @@ import ReplayIcon from "@mui/icons-material/Replay"; import Button from "@mui/material/Button"; import Tooltip from "@mui/material/Tooltip"; -import { type FC } from "react"; import LoadingButton from "@mui/lab/LoadingButton"; import { visuallyHidden } from "@mui/utils"; +import { type FC } from "react"; +import type { TemplateVersionExternalAuth } from "api/typesGenerated"; import { ExternalImage } from "components/ExternalImage/ExternalImage"; -import { TemplateVersionExternalAuth } from "api/typesGenerated"; +import { Pill } from "components/Pill/Pill"; export interface ExternalAuthButtonProps { auth: TemplateVersionExternalAuth; displayRetry: boolean; isLoading: boolean; onStartPolling: () => void; + error?: unknown; } export const ExternalAuthButton: FC = ({ @@ -19,6 +21,7 @@ export const ExternalAuthButton: FC = ({ displayRetry, isLoading, onStartPolling, + error, }) => { return ( <> @@ -48,9 +51,18 @@ export const ExternalAuthButton: FC = ({ onStartPolling(); }} > - {auth.authenticated - ? `Authenticated with ${auth.display_name}` - : `Login with ${auth.display_name}`} + {auth.authenticated ? ( + `Authenticated with ${auth.display_name}` + ) : ( + <> + Login with {auth.display_name} + {!auth.optional && ( + + Required + + )} + + )} {displayRetry && (