Skip to content

Commit

Permalink
Create ErrorOverlay component (#74073)
Browse files Browse the repository at this point in the history
### Why?

This PR refactored overlay to have a common layout as they share much in common and are also better for testing the layout. Will follow up with `Errors` component as it needs some UI break downs.

Note: patched `@storybook/react` for the type error:
- https://github.com/vercel/next.js/actions/runs/12394130672/job/34596896487#step:8:565
  • Loading branch information
devjiwonchoi authored Dec 18, 2024
1 parent 21565ff commit f880a7a
Show file tree
Hide file tree
Showing 9 changed files with 185 additions and 95 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,8 @@
"scheduler": "0.25.0-rc-372ec00c-20241209"
},
"patchedDependencies": {
"[email protected]": "patches/[email protected]"
"[email protected]": "patches/[email protected]",
"@storybook/[email protected]": "patches/@[email protected]"
}
}
}
20 changes: 17 additions & 3 deletions packages/next/.storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ function getAbsolutePath(value: string): any {
}
const config: StorybookConfig = {
stories: [
'../stories/**/*.mdx',
'../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)',
// Could to '../src/**/*.stories.@(ts|tsx)', but not sure how much it'll affect perf.
// Scoped to experimental dev overlay for now.
'../src/client/components/react-dev-overlay/_experimental/**/*.stories.@(ts|tsx)',
],
addons: [
getAbsolutePath('@storybook/addon-webpack5-compiler-swc'),
Expand All @@ -23,7 +24,20 @@ const config: StorybookConfig = {
],
framework: {
name: getAbsolutePath('@storybook/react-webpack5'),
options: {},
options: {
builder: {
useSWC: true,
},
},
},
swc: () => ({
jsc: {
transform: {
react: {
runtime: 'automatic',
},
},
},
}),
}
export default config
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { Meta, StoryObj } from '@storybook/react'
import { ErrorOverlayLayout } from './ErrorOverlayLayout'
import { withShadowPortal } from '../../storybook/with-shadow-portal'

const meta: Meta<typeof ErrorOverlayLayout> = {
title: 'ErrorOverlayLayout',
component: ErrorOverlayLayout,
parameters: {
layout: 'fullscreen',
},
decorators: [withShadowPortal],
}

export default meta
type Story = StoryObj<typeof ErrorOverlayLayout>

export const Default: Story = {
args: {
errorType: 'Build Error',
errorMessage: 'Failed to compile',
versionInfo: {
installed: '15.0.0',
staleness: 'fresh',
},
children: "Module not found: Cannot find module './missing-module'",
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import type { VersionInfo } from '../../../../../../../server/dev/parse-version-info'
import { Dialog, DialogHeader, DialogBody, DialogContent } from '../Dialog'
import { Overlay } from '../Overlay'
import { VersionStalenessInfo } from '../VersionStalenessInfo'

type ErrorOverlayLayoutProps = {
errorType:
| 'Build Error'
| 'Runtime Error'
| 'Console Error'
| 'Unhandled Runtime Error'
| 'Missing Required HTML Tag'
errorMessage: string | React.ReactNode
onClose: () => void
isBuildError?: boolean
versionInfo?: VersionInfo
children?: React.ReactNode
}

export function ErrorOverlayLayout({
errorType,
errorMessage,
onClose,
children,
versionInfo,
isBuildError,
}: ErrorOverlayLayoutProps) {
return (
<Overlay fixed={isBuildError}>
<Dialog
type="error"
aria-labelledby="nextjs__container_errors_label"
aria-describedby="nextjs__container_errors_desc"
onClose={onClose}
>
<DialogContent>
<DialogHeader className="nextjs-container-errors-header">
<h1
id="nextjs__container_errors_label"
className="nextjs__container_errors_label"
>
{errorType}
</h1>
<VersionStalenessInfo versionInfo={versionInfo} />
<p
id="nextjs__container_errors_desc"
className="nextjs__container_errors_desc"
>
{errorMessage}
</p>
</DialogHeader>
<DialogBody className="nextjs-container-errors-body">
{children}
</DialogBody>
</DialogContent>
</Dialog>
</Overlay>
)
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
import * as React from 'react'
import type { VersionInfo } from '../../../../../../server/dev/parse-version-info'
import {
Dialog,
DialogBody,
DialogContent,
DialogHeader,
} from '../components/Dialog'
import { Overlay } from '../components/Overlay'
import { Terminal } from '../components/Terminal'
import { VersionStalenessInfo } from '../components/VersionStalenessInfo'
import { noop as css } from '../helpers/noop-template'
import { ErrorOverlayLayout } from '../components/ErrorOverlayLayout/ErrorOverlayLayout'

export type BuildErrorProps = { message: string; versionInfo?: VersionInfo }

Expand All @@ -19,43 +12,22 @@ export const BuildError: React.FC<BuildErrorProps> = function BuildError({
}) {
const noop = React.useCallback(() => {}, [])
return (
<Overlay fixed>
<Dialog
type="error"
aria-labelledby="nextjs__container_errors_label"
aria-describedby="nextjs__container_errors_desc"
onClose={noop}
>
<DialogContent>
<DialogHeader className="nextjs-container-errors-header">
<h1
id="nextjs__container_errors_label"
className="nextjs__container_errors_label"
>
{'Build Error'}
</h1>
<VersionStalenessInfo versionInfo={versionInfo} />
<p
id="nextjs__container_errors_desc"
className="nextjs__container_errors_desc"
>
Failed to compile
</p>
</DialogHeader>
<DialogBody className="nextjs-container-errors-body">
<Terminal content={message} />
<footer>
<p id="nextjs__container_build_error_desc">
<small>
This error occurred during the build process and can only be
dismissed by fixing the error.
</small>
</p>
</footer>
</DialogBody>
</DialogContent>
</Dialog>
</Overlay>
<ErrorOverlayLayout
errorType="Build Error"
errorMessage="Failed to compile"
onClose={noop}
versionInfo={versionInfo}
>
<Terminal content={message} />
<footer>
<p id="nextjs__container_build_error_desc">
<small>
This error occurred during the build process and can only be
dismissed by fixing the error.
</small>
</p>
</footer>
</ErrorOverlayLayout>
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,50 +1,32 @@
import * as React from 'react'
import type { VersionInfo } from '../../../../../../server/dev/parse-version-info'
import { Dialog, DialogContent, DialogHeader } from '../components/Dialog'
import { Overlay } from '../components/Overlay'
import { VersionStalenessInfo } from '../components/VersionStalenessInfo'
import { useCallback } from 'react'
import { HotlinkedText } from '../components/hot-linked-text'
import { ErrorOverlayLayout } from '../components/ErrorOverlayLayout/ErrorOverlayLayout'

type RootLayoutMissingTagsErrorProps = {
missingTags: string[]
versionInfo?: VersionInfo
}

export const RootLayoutMissingTagsError: React.FC<RootLayoutMissingTagsErrorProps> =
function RootLayoutMissingTagsError({ missingTags, versionInfo }) {
const noop = React.useCallback(() => {}, [])
return (
<Overlay>
<Dialog
type="error"
aria-labelledby="nextjs__container_errors_label"
aria-describedby="nextjs__container_errors_desc"
onClose={noop}
>
<DialogContent>
<DialogHeader className="nextjs-container-errors-header">
<VersionStalenessInfo versionInfo={versionInfo} />
<h1
id="nextjs__container_errors_label"
className="nextjs__container_errors_label"
>
Missing required html tags
</h1>
<p
id="nextjs__container_errors_desc"
className="nextjs__container_errors_desc"
>
<HotlinkedText
text={`The following tags are missing in the Root Layout: ${missingTags
.map((tagName) => `<${tagName}>`)
.join(
', '
)}.\nRead more at https://nextjs.org/docs/messages/missing-root-layout-tags`}
/>
</p>
</DialogHeader>
</DialogContent>
</Dialog>
</Overlay>
)
}
export function RootLayoutMissingTagsError({
missingTags,
versionInfo,
}: RootLayoutMissingTagsErrorProps) {
const noop = useCallback(() => {}, [])
return (
<ErrorOverlayLayout
errorType="Missing Required HTML Tag"
errorMessage={
<HotlinkedText
text={`The following tags are missing in the Root Layout: ${missingTags
.map((tagName) => `<${tagName}>`)
.join(
', '
)}.\nRead more at https://nextjs.org/docs/messages/missing-root-layout-tags`}
/>
}
onClose={noop}
versionInfo={versionInfo}
/>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Base } from '../styles/Base'
import { CssReset } from '../styles/CssReset'
import { ComponentStyles } from '../styles/ComponentStyles'
import { ShadowPortal } from '../components/ShadowPortal'

export const withShadowPortal = (Story: any) => (
<ShadowPortal>
<CssReset />
<Base />
<ComponentStyles />
<Story />
</ShadowPortal>
)
19 changes: 19 additions & 0 deletions patches/@[email protected]
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
diff --git a/dist/types-a5624094.d.ts b/dist/types-a5624094.d.ts
index 8e1eb5e94642cb7e943fdb09da96606021701921..9958875a5d2026867455238ec44a71f905d758a4 100644
--- a/dist/types-a5624094.d.ts
+++ b/dist/types-a5624094.d.ts
@@ -1,4 +1,4 @@
-import { ComponentType } from 'react';
+import { ComponentType, JSX } from 'react';
import { WebRenderer, Canvas } from 'storybook/internal/types';

interface ReactRenderer extends WebRenderer {
diff --git a/template/components/index.js b/template/components/index.js
deleted file mode 100644
index c473ab7bed22857c051643fc4f0fce5637793adc..0000000000000000000000000000000000000000
diff --git a/template/stories/docgen-components/imported.js b/template/stories/docgen-components/imported.js
deleted file mode 100644
index bd94145261b03fff811e93a3d8f350a03b7a5105..0000000000000000000000000000000000000000
diff --git a/template/stories/docgen-components/js-proptypes/ext.js b/template/stories/docgen-components/js-proptypes/ext.js
deleted file mode 100644
index 11d0ad4810bb77980bc7be0b7697f3a85b15ffd2..0000000000000000000000000000000000000000
11 changes: 7 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit f880a7a

Please sign in to comment.