Skip to content

Commit

Permalink
Separate tabs for Errors and Warnings in error overlay (vercel/turbor…
Browse files Browse the repository at this point in the history
  • Loading branch information
ForsakenHarmony authored Mar 2, 2023
1 parent f8055a0 commit 617e2d2
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 25 deletions.
2 changes: 1 addition & 1 deletion crates/next-core/js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"@vercel/turbopack-runtime": "latest",
"anser": "^2.1.1",
"css.escape": "^1.5.1",
"next": "13.1.7-canary.28",
"next": "13.1.7-canary.30",
"platform": "1.3.6",
"react-dom": "^18.2.0",
"react": "^18.2.0",
Expand Down
91 changes: 71 additions & 20 deletions crates/next-core/js/src/overlay/internal/container/Errors.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -166,41 +166,58 @@ function useDisplayState(
}

const enum TabId {
TurbopackIssues = "turbopack-issues",
TurbopackErrors = "turbopack-issues",
TurbopackWarnings = "turbopack-warnings",
RuntimeErrors = "runtime-errors",
}

const TAB_PRIORITY = [
TabId.TurbopackErrors,
TabId.RuntimeErrors,
TabId.TurbopackWarnings,
];

export function Errors({ issues, errors }: ErrorsProps) {
const [readyErrors, isLoading] = useResolvedErrors(errors);

const hasIssues = issues.length !== 0;
const hasIssueWithError = issues.some((issue) =>
const turbopackWarnings = issues.filter(
(issue) => !["bug", "fatal", "error"].includes(issue.severity)
);
const turbopackErrors = issues.filter((issue) =>
["bug", "fatal", "error"].includes(issue.severity)
);

const hasTurbopackWarnings = turbopackWarnings.length > 0;
const hasTurbopackErrors = turbopackErrors.length > 0;

const hasErrors = errors.length !== 0;
const hasServerError = readyErrors.some((err) =>
["server", "edge-server"].includes(getErrorSource(err.error) || "")
);

// TODO for now it's already closable, but in future we might want to block users from using a broken app
// const isClosable = !isLoading && !hasIssueWithError && !hasServerError;
// const isClosable = !isLoading && !hasTurbopackErrors && !hasServerError;
const isClosable = true;

const defaultTab =
hasIssueWithError || !hasErrors
? TabId.TurbopackIssues
: TabId.RuntimeErrors;
TAB_PRIORITY.find(
(tabId) =>
({
[TabId.TurbopackErrors]: turbopackErrors.length > 0,
[TabId.TurbopackWarnings]: turbopackWarnings.length > 0,
[TabId.RuntimeErrors]: hasErrors,
}[tabId])
) ?? TabId.RuntimeErrors;

const [selectedTab, setSelectedTab] = React.useState<string>(defaultTab);

React.useEffect(() => {
if (defaultTab === TabId.TurbopackIssues) {
setSelectedTab(TabId.TurbopackIssues);
if (defaultTab === TabId.TurbopackErrors) {
setSelectedTab(TabId.TurbopackErrors);
}
}, [defaultTab]);

const onlyHasWarnings = !hasErrors && !hasIssueWithError;
const onlyHasWarnings = !hasErrors && !hasTurbopackErrors;

const [stateDisplayState, { fullscreen, minimize, hide }] = useDisplayState(
onlyHasWarnings ? DisplayState.Minimized : DisplayState.Fullscreen
Expand All @@ -213,7 +230,7 @@ export function Errors({ issues, errors }: ErrorsProps) {

// This component shouldn't be rendered with no errors, but if it is, let's
// handle it gracefully by rendering nothing.
if (!hasErrors && !hasIssues) {
if (!hasErrors && !hasTurbopackWarnings && !hasTurbopackErrors) {
return null;
}

Expand All @@ -224,7 +241,8 @@ export function Errors({ issues, errors }: ErrorsProps) {
if (displayState === DisplayState.Minimized) {
return (
<ErrorsToast
errorCount={readyErrors.length + issues.length}
errorCount={readyErrors.length + turbopackErrors.length}
warningCount={turbopackWarnings.length}
severity={onlyHasWarnings ? "warning" : "error"}
onClick={fullscreen}
onClose={hide}
Expand All @@ -248,20 +266,45 @@ export function Errors({ issues, errors }: ErrorsProps) {
close={isClosable ? minimize : undefined}
>
<DialogHeaderTabList>
{hasIssues && (
{hasTurbopackErrors && (
<Tab
id={TabId.TurbopackErrors}
next={
hasTurbopackWarnings
? TabId.TurbopackWarnings
: hasErrors
? TabId.RuntimeErrors
: undefined
}
data-severity="error"
>
<PackageX />
{turbopackErrors.length} Turbopack Error
{turbopackErrors.length > 1 ? "s" : ""}
</Tab>
)}
{hasTurbopackWarnings && (
<Tab
id={TabId.TurbopackIssues}
id={TabId.TurbopackWarnings}
prev={hasTurbopackErrors ? TabId.TurbopackErrors : undefined}
next={hasErrors ? TabId.RuntimeErrors : undefined}
data-severity={hasIssueWithError ? "error" : "warning"}
data-severity="warning"
>
<PackageX />
{issues.length} Turbopack Issue{issues.length > 1 ? "s" : ""}
{turbopackWarnings.length} Turbopack Warning
{turbopackWarnings.length > 1 ? "s" : ""}
</Tab>
)}
{hasErrors && (
<Tab
id={TabId.RuntimeErrors}
prev={hasIssues ? TabId.TurbopackIssues : undefined}
prev={
hasTurbopackWarnings
? TabId.TurbopackWarnings
: hasTurbopackErrors
? TabId.TurbopackErrors
: undefined
}
data-severity="error"
>
<AlertOctagon />
Expand All @@ -274,11 +317,19 @@ export function Errors({ issues, errors }: ErrorsProps) {
)}
</DialogHeaderTabList>
</DialogHeader>
{hasIssues && (
{hasTurbopackErrors && (
<TabPanel
as={TurbopackIssuesDialogBody}
id={TabId.TurbopackErrors}
issues={turbopackErrors}
className="errors-body"
/>
)}
{hasTurbopackWarnings && (
<TabPanel
as={TurbopackIssuesDialogBody}
id={TabId.TurbopackIssues}
issues={issues}
id={TabId.TurbopackWarnings}
issues={turbopackWarnings}
className="errors-body"
/>
)}
Expand Down
20 changes: 16 additions & 4 deletions crates/next-core/js/src/overlay/internal/container/ErrorsToast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,38 @@ import { noop as css } from "../helpers/noop-template";

export type ErrorsToastProps = {
errorCount: number;
warningCount: number;
severity: "error" | "warning";
onClick: () => void;
onClose: () => void;
};

export function ErrorsToast({
errorCount,
warningCount,
severity,
onClick,
onClose,
}: ErrorsToastProps) {
let message = "";

if (errorCount > 0) {
message += errorCount + " " + (errorCount > 1 ? "Errors" : "Error");
}
if (warningCount > 0) {
if (errorCount > 0) {
message += " and ";
}

message += warningCount + " " + (warningCount > 1 ? "Warnings" : "Warning");
}

return (
<Toast className="toast-errors" onClick={onClick} data-severity={severity}>
<div className="toast-errors-body">
{severity == "error" && <AlertOctagon />}
{severity == "warning" && <AlertTriangle />}
<span>
{errorCount} {severity}
{errorCount > 1 ? "s" : ""}
</span>
<span>{message}</span>
<button
data-toast-errors-hide-button
className="toast-errors-hide-button"
Expand Down

0 comments on commit 617e2d2

Please sign in to comment.