Skip to content

Commit 19caa18

Browse files
authored
[DevOverlay] Add Toolbar (vercel#74555)
This PR added style for Toolbar buttons, and moved files to `Errors/error-overlay-toolbar`. Will follow up with TBD: - Replace the Node.js button with the "Debugging" Icon - Add Docs Button? ### Light ![CleanShot 2025-01-07 at 01 12 52](https://github.com/user-attachments/assets/a26ad870-d938-4b24-a8e7-2409e65026d1) ### Dark ![CleanShot 2025-01-07 at 01 12 25](https://github.com/user-attachments/assets/0abe5d67-677b-4002-bf43-6d7a26dee602) Closes NDX-601 Closes NDX-602
1 parent b0a0c92 commit 19caa18

File tree

12 files changed

+219
-69
lines changed

12 files changed

+219
-69
lines changed

packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/Errors/error-overlay-layout/error-overlay-layout.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ import {
1313
} from '../../Dialog'
1414
import { Overlay } from '../../Overlay'
1515
import { ErrorPagination } from '../ErrorPagination/ErrorPagination'
16-
import { ToolButtonsGroup } from '../../ToolButtonsGroup/ToolButtonsGroup'
16+
import {
17+
ErrorOverlayToolbar,
18+
styles as toolbarStyles,
19+
} from '../error-overlay-toolbar/error-overlay-toolbar'
1720
import { VersionStalenessInfo } from '../../VersionStalenessInfo'
1821
import { ErrorOverlayBottomStacks } from '../error-overlay-bottom-stacks/error-overlay-bottom-stacks'
1922
import { ErrorOverlayFooter } from '../error-overlay-footer/error-overlay-footer'
@@ -81,7 +84,7 @@ export function ErrorOverlayLayout({
8184
data-nextjs-error-code={errorCode}
8285
>
8386
<ErrorTypeLabel errorType={errorType} />
84-
<ToolButtonsGroup error={error} debugInfo={debugInfo} />
87+
<ErrorOverlayToolbar error={error} debugInfo={debugInfo} />
8588
</div>
8689
<VersionStalenessInfo versionInfo={versionInfo} />
8790
<ErrorMessage errorMessage={errorMessage} />
@@ -109,4 +112,5 @@ export function ErrorOverlayLayout({
109112
export const styles = css`
110113
${errorTypeLabelStyles}
111114
${errorMessageStyles}
115+
${toolbarStyles}
112116
`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import type { Meta, StoryObj } from '@storybook/react'
2+
import { CopyStackTraceButton } from './copy-stack-trace-button'
3+
import { withShadowPortal } from '../../../storybook/with-shadow-portal'
4+
5+
const meta: Meta<typeof CopyStackTraceButton> = {
6+
title: 'ErrorOverlayToolbar/CopyStackTraceButton',
7+
component: CopyStackTraceButton,
8+
parameters: {
9+
layout: 'centered',
10+
},
11+
decorators: [withShadowPortal],
12+
}
13+
14+
export default meta
15+
type Story = StoryObj<typeof CopyStackTraceButton>
16+
17+
const errorWithStack = new Error('Test error')
18+
errorWithStack.stack = `Error: Test error
19+
at Context.<anonymous> (test.ts:1:1)
20+
at processImmediate (node:internal/timers:466:21)`
21+
22+
export const WithStackTrace: Story = {
23+
args: {
24+
error: errorWithStack,
25+
},
26+
}
27+
28+
export const WithoutStackTrace: Story = {
29+
args: {
30+
error: new Error('Error without stack'),
31+
},
32+
}
33+
34+
export const Disabled: Story = {
35+
args: {
36+
error: undefined,
37+
},
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { CopyButton } from '../../copy-button'
2+
3+
export function CopyStackTraceButton({ error }: { error: Error | undefined }) {
4+
return (
5+
<CopyButton
6+
data-nextjs-data-runtime-error-copy-stack
7+
className="copy-stack-trace-button"
8+
actionLabel="Copy Stack Trace"
9+
successLabel="Stack Trace Copied"
10+
content={error?.stack || ''}
11+
disabled={!error?.stack}
12+
/>
13+
)
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import type { Meta, StoryObj } from '@storybook/react'
2+
import { ErrorOverlayToolbar } from './error-overlay-toolbar'
3+
import { withShadowPortal } from '../../../storybook/with-shadow-portal'
4+
5+
const meta: Meta<typeof ErrorOverlayToolbar> = {
6+
title: 'ErrorOverlayToolbar',
7+
component: ErrorOverlayToolbar,
8+
parameters: {
9+
layout: 'centered',
10+
},
11+
decorators: [withShadowPortal],
12+
}
13+
14+
export default meta
15+
type Story = StoryObj<typeof ErrorOverlayToolbar>
16+
17+
export const WithErrorAndDebugInfo: Story = {
18+
args: {
19+
error: new Error('Test error with stack trace'),
20+
debugInfo: {
21+
devtoolsFrontendUrl: 'chrome-devtools://devtools/bundled/inspector.html',
22+
},
23+
},
24+
}
25+
26+
export const WithErrorOnly: Story = {
27+
args: {
28+
error: new Error('Test error without debug info'),
29+
debugInfo: undefined,
30+
},
31+
}
32+
33+
export const WithoutError: Story = {
34+
args: {
35+
error: undefined,
36+
debugInfo: undefined,
37+
},
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import type { DebugInfo } from '../../../../../types'
2+
import { NodejsInspectorButton } from './nodejs-inspector-button'
3+
import { noop as css } from '../../../helpers/noop-template'
4+
import { CopyStackTraceButton } from './copy-stack-trace-button'
5+
6+
type ErrorOverlayToolbarProps = {
7+
error: Error | undefined
8+
debugInfo: DebugInfo | undefined
9+
}
10+
11+
export function ErrorOverlayToolbar({
12+
error,
13+
debugInfo,
14+
}: ErrorOverlayToolbarProps) {
15+
return (
16+
<span className="error-overlay-toolbar">
17+
<CopyStackTraceButton error={error} />
18+
<NodejsInspectorButton
19+
devtoolsFrontendUrl={debugInfo?.devtoolsFrontendUrl}
20+
/>
21+
</span>
22+
)
23+
}
24+
25+
export const styles = css`
26+
.error-overlay-toolbar {
27+
display: flex;
28+
gap: var(--size-1_5);
29+
}
30+
31+
.nodejs-inspector-button,
32+
.copy-stack-trace-button {
33+
display: flex;
34+
justify-content: center;
35+
align-items: center;
36+
37+
margin: 0;
38+
width: var(--size-8);
39+
padding: var(--size-1_5);
40+
background: var(--color-background-100);
41+
box-shadow: var(--shadow-sm);
42+
43+
border-radius: var(--rounded-full);
44+
border: 1px solid var(--color-gray-400);
45+
46+
&:focus {
47+
outline: none;
48+
}
49+
50+
&:not(:disabled):hover {
51+
background: var(--color-gray-alpha-100);
52+
}
53+
54+
&:active {
55+
background: var(--color-gray-alpha-200);
56+
}
57+
58+
&:disabled {
59+
opacity: 0.7;
60+
cursor: not-allowed;
61+
}
62+
}
63+
64+
.error-overlay-toolbar-button-icon {
65+
color: var(--color-gray-900);
66+
}
67+
`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import type { Meta, StoryObj } from '@storybook/react'
2+
import { NodejsInspectorButton } from './nodejs-inspector-button'
3+
import { withShadowPortal } from '../../../storybook/with-shadow-portal'
4+
5+
const meta: Meta<typeof NodejsInspectorButton> = {
6+
title: 'ErrorOverlayToolbar/NodejsInspectorButton',
7+
component: NodejsInspectorButton,
8+
parameters: {
9+
layout: 'centered',
10+
},
11+
decorators: [withShadowPortal],
12+
}
13+
14+
export default meta
15+
type Story = StoryObj<typeof NodejsInspectorButton>
16+
17+
export const WithDevtoolsUrl: Story = {
18+
args: {
19+
devtoolsFrontendUrl: 'chrome-devtools://devtools/bundled/inspector.html',
20+
},
21+
}
22+
23+
export const WithoutDevtoolsUrl: Story = {
24+
args: {
25+
devtoolsFrontendUrl: undefined,
26+
},
27+
}

packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/nodejs-inspector.tsx renamed to packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/Errors/error-overlay-toolbar/nodejs-inspector-button.tsx

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { CopyButton } from './copy-button'
1+
import { CopyButton } from '../../copy-button'
22

33
// Inline this helper to avoid widely used across the codebase,
44
// as for this feature the Chrome detector doesn't need to be super accurate.
@@ -75,7 +75,7 @@ function NodeJsDisabledIcon(props: any) {
7575
const label =
7676
'Learn more about enabling Node.js inspector for server code with Chrome DevTools'
7777

78-
export function NodejsInspectorCopyButton({
78+
export function NodejsInspectorButton({
7979
devtoolsFrontendUrl,
8080
}: {
8181
devtoolsFrontendUrl: string | undefined
@@ -87,22 +87,33 @@ export function NodejsInspectorCopyButton({
8787
<a
8888
title={label}
8989
aria-label={label}
90-
className="nextjs-data-runtime-error-inspect-link"
90+
className="nodejs-inspector-button"
9191
href={`https://nextjs.org/docs/app/building-your-application/configuring/debugging#server-side-code`}
9292
target="_blank"
9393
rel="noopener noreferrer"
9494
>
95-
<NodeJsDisabledIcon width={16} height={16} />
95+
<NodeJsDisabledIcon
96+
className="error-overlay-toolbar-button-icon"
97+
width={16}
98+
height={16}
99+
/>
96100
</a>
97101
)
98102
}
99103
return (
100104
<CopyButton
101105
data-nextjs-data-runtime-error-copy-devtools-url
106+
className="nodejs-inspector-button"
102107
actionLabel={'Copy Chrome DevTools URL'}
103108
successLabel="Copied"
104109
content={content}
105-
icon={<NodeJsIcon width={16} height={16} />}
110+
icon={
111+
<NodeJsIcon
112+
className="error-overlay-toolbar-button-icon"
113+
width={16}
114+
height={16}
115+
/>
116+
}
106117
/>
107118
)
108119
}

packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/ToolButtonsGroup/ToolButtonsGroup.tsx

Lines changed: 0 additions & 25 deletions
This file was deleted.

packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/copy-button/index.tsx

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,9 @@ export function CopyButton({
181181
title={label}
182182
aria-label={label}
183183
aria-disabled={isDisabled}
184+
disabled={isDisabled}
184185
data-nextjs-data-runtime-error-copy-button
185-
className={`nextjs-data-runtime-error-copy-button nextjs-data-runtime-error-copy-button--${copyState.state}`}
186+
className={`${props.className || ''} nextjs-data-runtime-error-copy-button nextjs-data-runtime-error-copy-button--${copyState.state}`}
186187
onClick={() => {
187188
if (!isDisabled) {
188189
copy()
@@ -200,15 +201,17 @@ function CopyIcon() {
200201
<svg
201202
width="16"
202203
height="16"
203-
viewBox="0 0 24 24"
204-
fill="transparent"
205-
stroke="currentColor"
206-
strokeWidth="1.5"
207-
strokeLinecap="round"
208-
strokeLinejoin="round"
204+
viewBox="0 0 16 16"
205+
fill="none"
206+
xmlns="http://www.w3.org/2000/svg"
207+
className="error-overlay-toolbar-button-icon"
209208
>
210-
<rect width="14" height="14" x="8" y="8" rx="2" ry="2" />
211-
<path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" />
209+
<path
210+
fillRule="evenodd"
211+
clipRule="evenodd"
212+
d="M2.75 0.5C1.7835 0.5 1 1.2835 1 2.25V9.75C1 10.7165 1.7835 11.5 2.75 11.5H3.75H4.5V10H3.75H2.75C2.61193 10 2.5 9.88807 2.5 9.75V2.25C2.5 2.11193 2.61193 2 2.75 2H8.25C8.38807 2 8.5 2.11193 8.5 2.25V3H10V2.25C10 1.2835 9.2165 0.5 8.25 0.5H2.75ZM7.75 4.5C6.7835 4.5 6 5.2835 6 6.25V13.75C6 14.7165 6.7835 15.5 7.75 15.5H13.25C14.2165 15.5 15 14.7165 15 13.75V6.25C15 5.2835 14.2165 4.5 13.25 4.5H7.75ZM7.5 6.25C7.5 6.11193 7.61193 6 7.75 6H13.25C13.3881 6 13.5 6.11193 13.5 6.25V13.75C13.5 13.8881 13.3881 14 13.25 14H7.75C7.61193 14 7.5 13.8881 7.5 13.75V6.25Z"
213+
fill="currentColor"
214+
/>
212215
</svg>
213216
)
214217
}

packages/next/src/client/components/react-dev-overlay/_experimental/internal/container/Errors.tsx

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -410,12 +410,6 @@ export const styles = css`
410410
align-items: center;
411411
justify-content: space-between;
412412
}
413-
.nextjs-data-runtime-error-inspect-link,
414-
.nextjs-data-runtime-error-inspect-link:hover {
415-
margin: 0 8px;
416-
color: inherit;
417-
}
418-
419413
.error-overlay-notes-container {
420414
padding: var(--size-4);
421415
padding-top: 0;

packages/next/src/client/components/react-dev-overlay/_experimental/internal/container/RuntimeError/index.tsx

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -101,28 +101,6 @@ export const styles = css`
101101
outline: none;
102102
}
103103
104-
[data-nextjs-data-runtime-error-copy-button],
105-
[data-nextjs-data-runtime-error-copy-button]:focus:not(:focus-visible) {
106-
position: relative;
107-
margin-left: var(--size-gap);
108-
padding: 0;
109-
border: none;
110-
background: none;
111-
outline: none;
112-
}
113-
[data-nextjs-data-runtime-error-copy-button] > svg {
114-
vertical-align: middle;
115-
}
116-
.nextjs-data-runtime-error-copy-button {
117-
color: inherit;
118-
}
119-
.nextjs-data-runtime-error-copy-button--initial:hover {
120-
cursor: pointer;
121-
}
122-
.nextjs-data-runtime-error-copy-button[aria-disabled='true'] {
123-
opacity: 0.3;
124-
cursor: not-allowed;
125-
}
126104
.nextjs-data-runtime-error-copy-button--error,
127105
.nextjs-data-runtime-error-copy-button--error:hover {
128106
color: var(--color-ansi-red);

packages/next/src/client/components/react-dev-overlay/_experimental/internal/styles/Base.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ export function Base() {
8686
--rounded-xl: 0.75rem; /* 12px */
8787
--rounded-2xl: 1rem; /* 16px */
8888
--rounded-3xl: 1.5rem; /* 24px */
89+
--rounded-full: 9999px;
8990
9091
--size-0: 0px;
9192
--size-px: 1px;

0 commit comments

Comments
 (0)