Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
6 changes: 5 additions & 1 deletion site/e2e/tests/workspaces/autoCreateWorkspace.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,9 @@ test("show error if `match` parameter is invalid", async ({ page }) => {
},
);
await page.getByRole("button", { name: /confirm and create/i }).click();
await expect(page.getByText("Invalid match value")).toBeVisible();
await expect(
page.getByRole("alert").getByRole("heading", {
name: "Invalid match value",
}),
).toBeVisible();
});
10 changes: 4 additions & 6 deletions site/src/components/Alert/Alert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,9 @@ export const Alert: FC<AlertProps> = ({
{...props}
>
<div className="flex items-center justify-between gap-4 text-sm">
<div className="flex flex-row items-start gap-3">
<div className="flex min-w-0 flex-1 flex-row items-start gap-3">
<Icon className={cn("size-icon-sm mt-[3px]", iconClassName)} />
<div className="flex-1">{children}</div>
<div className="min-w-0 flex-1">{children}</div>
</div>
<div className="flex items-center gap-2">
{actions}
Expand All @@ -125,7 +125,7 @@ export const Alert: FC<AlertProps> = ({
);
};

export const AlertDetail: React.FC<React.PropsWithChildren> = ({
export const AlertDescription: React.FC<React.PropsWithChildren> = ({
children,
}) => {
return (
Expand All @@ -139,7 +139,5 @@ export const AlertTitle: React.FC<React.ComponentPropsWithRef<"h1">> = ({
className,
...props
}) => {
return (
<h1 className={cn("m-0 mb-1 text-sm font-medium", className)} {...props} />
);
return <h1 className={cn("m-0 text-sm font-medium", className)} {...props} />;
};
70 changes: 45 additions & 25 deletions site/src/components/Alert/ErrorAlert.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { getErrorDetail, getErrorMessage, getErrorStatus } from "api/errors";
import { isAxiosError } from "axios";
import type { FC } from "react";
import { Link } from "../Link/Link";
import { Alert, AlertDetail, type AlertProps, AlertTitle } from "./Alert";
import { Alert, AlertDescription, type AlertProps, AlertTitle } from "./Alert";

type ErrorAlertProps = Readonly<
Omit<AlertProps, "severity" | "children"> & { error: unknown }
Expand All @@ -13,33 +14,52 @@ export const ErrorAlert: FC<ErrorAlertProps> = ({ error, ...alertProps }) => {
const status = getErrorStatus(error);

// For some reason, the message and detail can be the same on the BE, but does
// not make sense in the FE to showing them duplicated
const shouldDisplayDetail = message !== detail;
// not make sense in the FE to showing them duplicated. However, we should always
// display the detail if its a 403 Forbidden response.
const shouldDisplayDetail = status === 403 || message !== detail;
const shouldDisplayResponseData = isAxiosError(error) && error.response?.data;
const shouldDisplayStackTrace = error instanceof Error;

return (
<Alert severity="error" prominent {...alertProps}>
{
// When the error is a Forbidden response we include a link for the user to
// go back to a known viewable page.
status === 403 ? (
<>
<AlertTitle>{message}</AlertTitle>
<AlertDetail>
{detail}{" "}
<Link href="/workspaces" className="w-fit">
Go to workspaces
</Link>
</AlertDetail>
</>
) : detail ? (
<>
<AlertTitle>{message}</AlertTitle>
{shouldDisplayDetail && <AlertDetail>{detail}</AlertDetail>}
</>
) : (
message
)
}
<AlertTitle>{message}</AlertTitle>
<AlertDescription>
{shouldDisplayDetail && detail}
{status === 403 && (
// When the error is a Forbidden response we include a link for the user to
// go back to a known viewable page.
<Link href="/workspaces" className="w-fit">
Go to workspaces
</Link>
)}
</AlertDescription>
{(shouldDisplayResponseData || shouldDisplayStackTrace) && (
<div className="mt-2 min-w-0">
{shouldDisplayResponseData && (
<details className="max-w-full">
<summary>Response data</summary>
<div className="mt-2 max-w-full overflow-x-auto">
<pre className="m-0 w-max min-w-full">
{JSON.stringify(error.response?.data, null, 2)}
</pre>
</div>
</details>
)}
{/*
* Error.isError() is not reliably available in all browsers
* so we fallback to `instanceof Error`. In future we should use
* it is more reliable.
*/}
{shouldDisplayStackTrace && (
<details className="max-w-full">
<summary>Stack Trace</summary>
<div className="mt-2 max-w-full overflow-x-auto">
<pre className="m-0 w-max min-w-full">{error.stack}</pre>
</div>
</details>
)}
</div>
)}
</Alert>
);
};
4 changes: 2 additions & 2 deletions site/src/components/GitDeviceAuth/GitDeviceAuth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Link from "@mui/material/Link";
import type { ApiErrorResponse } from "api/errors";
import type { ExternalAuthDevice } from "api/typesGenerated";
import { isAxiosError } from "axios";
import { Alert, AlertDetail, AlertTitle } from "components/Alert/Alert";
import { Alert, AlertDescription, AlertTitle } from "components/Alert/Alert";
import { CopyButton } from "components/CopyButton/CopyButton";
import { ExternalLinkIcon } from "lucide-react";
import type { FC } from "react";
Expand Down Expand Up @@ -111,7 +111,7 @@ export const GitDeviceAuth: FC<GitDeviceAuthProps> = ({
<Alert severity="error">
<AlertTitle>{deviceExchangeError.message}</AlertTitle>
{deviceExchangeError.detail && (
<AlertDetail>{deviceExchangeError.detail}</AlertDetail>
<AlertDescription>{deviceExchangeError.detail}</AlertDescription>
)}
</Alert>
);
Expand Down
6 changes: 3 additions & 3 deletions site/src/modules/provisioners/ProvisionerAlert.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {
Alert,
type AlertColor,
AlertDetail,
AlertDescription,
AlertTitle,
} from "components/Alert/Alert";
import { ProvisionerTag } from "modules/provisioners/ProvisionerTag";
Expand Down Expand Up @@ -53,7 +53,7 @@ export const ProvisionerAlert: FC<ProvisionerAlertProps> = ({
return (
<Alert severity={severity} className={getAlertClassName(variant, severity)}>
<AlertTitle>{title}</AlertTitle>
<AlertDetail>
<AlertDescription>
<div>{detail}</div>
<div className="flex items-center gap-2 flex-wrap mt-2">
{Object.entries(tags ?? {})
Expand All @@ -62,7 +62,7 @@ export const ProvisionerAlert: FC<ProvisionerAlertProps> = ({
<ProvisionerTag key={key} tagName={key} tagValue={value} />
))}
</div>
</AlertDetail>
</AlertDescription>
</Alert>
);
};
6 changes: 3 additions & 3 deletions site/src/modules/resources/WildcardHostnameWarning.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { WorkspaceResource } from "api/typesGenerated";
import { Alert, AlertDetail, AlertTitle } from "components/Alert/Alert";
import { Alert, AlertDescription, AlertTitle } from "components/Alert/Alert";
import { Link } from "components/Link/Link";
import { useProxy } from "contexts/ProxyContext";
import { useAuthenticated } from "hooks/useAuthenticated";
Expand Down Expand Up @@ -47,7 +47,7 @@ export const WildcardHostnameWarning: FC<WildcardHostnameWarningProps> = ({
}
>
<AlertTitle>Some workspace applications will not work</AlertTitle>
<AlertDetail>
<AlertDescription>
<div>
{hasResources
? "This template contains coder_app resources with"
Expand Down Expand Up @@ -78,7 +78,7 @@ export const WildcardHostnameWarning: FC<WildcardHostnameWarningProps> = ({
</span>
</Link>
</div>
</AlertDetail>
</AlertDescription>
</Alert>
);
};
5 changes: 4 additions & 1 deletion site/src/modules/tasks/TaskPrompt/TaskPrompt.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,10 @@ export const ExternalAuthError: Story = {
});

await step("Renders error", async () => {
await canvas.findByText(/failed to load external auth/i);
const alert = await canvas.findByRole("alert");
await within(alert).findByRole("heading", {
name: /failed to load external auth/i,
});
});
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
updateChatProviderConfig as updateChatProviderConfigMutation,
} from "api/queries/chats";
import type * as TypesGen from "api/typesGenerated";
import { Alert, AlertDetail, AlertTitle } from "components/Alert/Alert";
import { Alert, AlertDescription, AlertTitle } from "components/Alert/Alert";
import { ErrorAlert } from "components/Alert/ErrorAlert";
import { Loader2Icon } from "lucide-react";
import { type FC, useMemo, useState } from "react";
Expand Down Expand Up @@ -322,7 +322,9 @@ export const ChatModelAdminPanel: FC<ChatModelAdminPanelProps> = ({
<AlertTitle>
Chat provider admin API is unavailable on this deployment.
</AlertTitle>
<AlertDetail>/api/v2/chats/providers is missing.</AlertDetail>
<AlertDescription>
/api/v2/chats/providers is missing.
</AlertDescription>
</Alert>
)}

Expand All @@ -331,7 +333,9 @@ export const ChatModelAdminPanel: FC<ChatModelAdminPanelProps> = ({
<AlertTitle>
Chat model admin API is unavailable on this deployment.
</AlertTitle>
<AlertDetail>/api/v2/chats/model-configs is missing.</AlertDetail>
<AlertDescription>
/api/v2/chats/model-configs is missing.
</AlertDescription>
</Alert>
)}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getErrorMessage } from "api/errors";
import type * as TypesGen from "api/typesGenerated";
import { Alert, AlertDetail, AlertTitle } from "components/Alert/Alert";
import { Alert, AlertDescription, AlertTitle } from "components/Alert/Alert";
import { Button } from "components/Button/Button";
import { CollapsibleContent } from "components/Collapsible/Collapsible";
import { Input } from "components/Input/Input";
Expand Down Expand Up @@ -131,10 +131,10 @@ export const ProviderForm: FC<ProviderFormProps> = ({
{isAPIKeyEnvManaged && (
<Alert severity="info">
<AlertTitle>API key managed by environment variable.</AlertTitle>
<AlertDetail>
<AlertDescription>
This provider key is configured from deployment environment
settings and cannot be edited in this UI.
</AlertDetail>
</AlertDescription>
</Alert>
)}

Expand Down
10 changes: 8 additions & 2 deletions site/src/pages/CreateWorkspacePage/CreateWorkspacePage.jest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,10 @@ describe("CreateWorkspacePage", () => {
await waitFor(() => {
expect(mockPublisher).toBeDefined();
mockPublisher.publishError(new Event("Connection failed"));
expect(screen.getByText(/connection failed/i)).toBeInTheDocument();
const alert = screen.getByRole("alert");
expect(
within(alert).getByRole("heading", { name: /connection failed/i }),
).toBeInTheDocument();
});
});

Expand All @@ -210,8 +213,11 @@ describe("CreateWorkspacePage", () => {
await waitFor(() => {
expect(mockPublisher).toBeDefined();
mockPublisher.publishClose(new Event("close") as CloseEvent);
const alert = screen.getByRole("alert");
expect(
screen.getByText(/websocket connection.*unexpectedly closed/i),
within(alert).getByRole("heading", {
name: /websocket connection.*unexpectedly closed/i,
}),
).toBeInTheDocument();
});
});
Expand Down
6 changes: 3 additions & 3 deletions site/src/pages/SetupPage/SetupPageView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import TextField from "@mui/material/TextField";
import { countries } from "api/countriesGenerated";
import type * as TypesGen from "api/typesGenerated";
import { isAxiosError } from "axios";
import { Alert, AlertDetail, AlertTitle } from "components/Alert/Alert";
import { Alert, AlertDescription, AlertTitle } from "components/Alert/Alert";
import { Button } from "components/Button/Button";
import { ExternalImage } from "components/ExternalImage/ExternalImage";
import { FormFields, VerticalForm } from "components/Form/Form";
Expand Down Expand Up @@ -354,13 +354,13 @@ export const SetupPageView: FC<SetupPageViewProps> = ({
<Alert severity="error" prominent>
<AlertTitle>{error.response.data.message}</AlertTitle>
{error.response.data.detail && (
<AlertDetail>
<AlertDescription>
{error.response.data.detail}
<br />
<Link target="_blank" href="https://coder.com/contact/sales">
Contact Sales
</Link>
</AlertDetail>
</AlertDescription>
)}
</Alert>
)}
Expand Down
8 changes: 7 additions & 1 deletion site/src/pages/TaskPage/ModifyPromptDialog.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ export const Submitting: Story = {
const submitButton = body.getByRole("button", {
name: /update and restart build/i,
});
await waitFor(() => {
expect(submitButton).not.toBeDisabled();
});
await userEvent.click(submitButton);
});

Expand Down Expand Up @@ -248,7 +251,10 @@ export const Failure: Story = {
});

await step("Shows error message", async () => {
await body.findByText(/Failed to update task prompt/i);
const alert = await body.findByRole("alert");
await within(alert).findByRole("heading", {
name: /Failed to update task prompt/i,
});
});
},
};
Expand Down
10 changes: 6 additions & 4 deletions site/src/pages/WorkspacePage/Workspace.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type * as TypesGen from "api/typesGenerated";
import type { WorkspaceAgentStatus } from "api/typesGenerated";
import { Alert, AlertDetail, AlertTitle } from "components/Alert/Alert";
import { Alert, AlertDescription, AlertTitle } from "components/Alert/Alert";
import { SidebarIconButton } from "components/FullPageLayout/Sidebar";
import { Link } from "components/Link/Link";
import { useSearchParamsKey } from "hooks/useSearchParamsKey";
Expand Down Expand Up @@ -188,7 +188,9 @@ export const Workspace: FC<WorkspaceProps> = ({
{workspace.latest_build.job.error && (
<Alert severity="error" prominent>
<AlertTitle>Workspace build failed</AlertTitle>
<AlertDetail>{workspace.latest_build.job.error}</AlertDetail>
<AlertDescription>
{workspace.latest_build.job.error}
</AlertDescription>
</Alert>
)}

Expand Down Expand Up @@ -294,7 +296,7 @@ const UnhealthyWorkspaceAlert: FC<UnhealthyWorkspaceAlertProps> = ({
return (
<Alert severity="warning" prominent>
<AlertTitle>{title}</AlertTitle>
<AlertDetail>
<AlertDescription>
<p>
Your workspace is running but{" "}
{failingAgentCount > 1
Expand All @@ -310,7 +312,7 @@ const UnhealthyWorkspaceAlert: FC<UnhealthyWorkspaceAlertProps> = ({
</Link>
)}
</p>
</AlertDetail>
</AlertDescription>
</Alert>
);
};
Expand Down
Loading