-
Notifications
You must be signed in to change notification settings - Fork 28.7k
[devtools] panel ui issues tab content #80729
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
devjiwonchoi
merged 11 commits into
canary
from
jiwon/06-20-_devtools_panel_ui_issues_tab_content
Jun 23, 2025
+599
−55
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
804082f
[devtools] panel ui issues tab content
devjiwonchoi f8b7007
add feedback button
devjiwonchoi 35522b3
add loading skeleton
devjiwonchoi 82433a1
codeframe should not hidden
devjiwonchoi a5a52d1
add empty state
devjiwonchoi 1c79464
remove console
devjiwonchoi 2ad55dc
display codeframe when expected value all exist
devjiwonchoi 7a830c7
Update packages/next/src/next-devtools/dev-overlay/styles/component-s…
devjiwonchoi c95e1e6
remove unused style
devjiwonchoi 4d5265c
move error overlay related to issues tab content
devjiwonchoi 91fb36f
Update packages/next/src/next-devtools/dev-overlay/container/runtime-…
devjiwonchoi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
174 changes: 174 additions & 0 deletions
174
...ev-overlay/components/devtools-panel/devtools-panel-tab/issues-tab/issues-tab-content.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
import type { OverlayState } from '../../../../shared' | ||
import type { DebugInfo } from '../../../../../shared/types' | ||
import type { ReadyRuntimeError } from '../../../../utils/get-error-by-type' | ||
import type { ErrorType } from '../../../errors/error-type-label/error-type-label' | ||
|
||
import { Suspense, useMemo, useState } from 'react' | ||
|
||
import { | ||
GenericErrorDescription, | ||
HydrationErrorDescription, | ||
} from '../../../../container/errors' | ||
import { EnvironmentNameLabel } from '../../../errors/environment-name-label/environment-name-label' | ||
import { ErrorMessage } from '../../../errors/error-message/error-message' | ||
import { ErrorOverlayToolbar } from '../../../errors/error-overlay-toolbar/error-overlay-toolbar' | ||
import { ErrorTypeLabel } from '../../../errors/error-type-label/error-type-label' | ||
import { IssueFeedbackButton } from '../../../errors/error-overlay-toolbar/issue-feedback-button' | ||
import { Terminal } from '../../../terminal' | ||
import { HotlinkedText } from '../../../hot-linked-text' | ||
import { PseudoHtmlDiff } from '../../../../container/runtime-error/component-stack-pseudo-html' | ||
import { useFrames } from '../../../../utils/get-error-by-type' | ||
import { CodeFrame } from '../../../code-frame/code-frame' | ||
import { CallStack } from '../../../call-stack/call-stack' | ||
import { NEXTJS_HYDRATION_ERROR_LINK } from '../../../../../shared/react-19-hydration-error' | ||
import { ErrorContentSkeleton } from '../../../../container/runtime-error/error-content-skeleton' | ||
import { css } from '../../../../utils/css' | ||
|
||
export function IssuesTabContent({ | ||
notes, | ||
buildError, | ||
hydrationWarning, | ||
errorDetails, | ||
activeError, | ||
errorCode, | ||
errorType, | ||
debugInfo, | ||
}: { | ||
notes: string | null | ||
buildError: OverlayState['buildError'] | ||
hydrationWarning: string | null | ||
errorDetails: { | ||
hydrationWarning: string | null | ||
notes: string | null | ||
reactOutputComponentDiff: string | null | ||
} | ||
activeError: ReadyRuntimeError | ||
errorCode: string | undefined | ||
errorType: ErrorType | ||
debugInfo: DebugInfo | ||
}) { | ||
if (buildError) { | ||
return <Terminal content={buildError} /> | ||
} | ||
|
||
const errorMessage = hydrationWarning ? ( | ||
<HydrationErrorDescription message={hydrationWarning} /> | ||
) : ( | ||
<GenericErrorDescription error={activeError.error} /> | ||
) | ||
|
||
return ( | ||
<div data-nextjs-devtools-panel-tab-issues-content-container> | ||
<div className="nextjs-container-errors-header"> | ||
<div | ||
className="nextjs__container_errors__error_title" | ||
// allow assertion in tests before error rating is implemented | ||
data-nextjs-error-code={errorCode} | ||
> | ||
<span data-nextjs-error-label-group> | ||
<ErrorTypeLabel errorType={errorType} /> | ||
{activeError.error.environmentName && ( | ||
<EnvironmentNameLabel | ||
environmentName={activeError.error.environmentName} | ||
/> | ||
)} | ||
</span> | ||
<ErrorOverlayToolbar | ||
error={activeError.error} | ||
debugInfo={debugInfo} | ||
// TODO: Move the button inside and remove the feedback on the footer of the error overlay. | ||
feedbackButton={ | ||
errorCode && <IssueFeedbackButton errorCode={errorCode} /> | ||
} | ||
/> | ||
</div> | ||
<ErrorMessage errorMessage={errorMessage} /> | ||
</div> | ||
<div className="error-overlay-notes-container"> | ||
{notes ? ( | ||
<> | ||
<p | ||
id="nextjs__container_errors__notes" | ||
className="nextjs__container_errors__notes" | ||
> | ||
{notes} | ||
</p> | ||
</> | ||
) : null} | ||
{hydrationWarning ? ( | ||
<p | ||
id="nextjs__container_errors__link" | ||
className="nextjs__container_errors__link" | ||
> | ||
<HotlinkedText | ||
text={`See more info here: ${NEXTJS_HYDRATION_ERROR_LINK}`} | ||
/> | ||
</p> | ||
) : null} | ||
</div> | ||
{errorDetails.reactOutputComponentDiff ? ( | ||
<PseudoHtmlDiff | ||
reactOutputComponentDiff={errorDetails.reactOutputComponentDiff || ''} | ||
/> | ||
) : null} | ||
<Suspense fallback={<ErrorContentSkeleton />}> | ||
<RuntimeError key={activeError.id.toString()} error={activeError} /> | ||
</Suspense> | ||
</div> | ||
) | ||
} | ||
|
||
/* Ported the content from container/runtime-error/index.tsx */ | ||
function RuntimeError({ error }: { error: ReadyRuntimeError }) { | ||
const [isIgnoreListOpen, setIsIgnoreListOpen] = useState(false) | ||
const frames = useFrames(error) | ||
|
||
const ignoredFramesTally = useMemo(() => { | ||
return frames.reduce((tally, frame) => tally + (frame.ignored ? 1 : 0), 0) | ||
}, [frames]) | ||
|
||
const firstFrame = useMemo(() => { | ||
const firstFirstPartyFrameIndex = frames.findIndex( | ||
(entry) => | ||
!entry.ignored && | ||
Boolean(entry.originalCodeFrame) && | ||
Boolean(entry.originalStackFrame) | ||
) | ||
|
||
return frames[firstFirstPartyFrameIndex] ?? null | ||
}, [frames]) | ||
|
||
return ( | ||
<> | ||
{firstFrame && | ||
firstFrame.originalStackFrame && | ||
firstFrame.originalCodeFrame && ( | ||
<CodeFrame | ||
stackFrame={firstFrame.originalStackFrame} | ||
codeFrame={firstFrame.originalCodeFrame} | ||
/> | ||
)} | ||
|
||
{frames.length > 0 && ( | ||
<CallStack | ||
frames={frames} | ||
isIgnoreListOpen={isIgnoreListOpen} | ||
onToggleIgnoreList={() => setIsIgnoreListOpen(!isIgnoreListOpen)} | ||
ignoredFramesTally={ignoredFramesTally} | ||
/> | ||
)} | ||
</> | ||
) | ||
} | ||
|
||
// The components in this file shares the style with the Error Overlay. | ||
export const DEVTOOLS_PANEL_TAB_ISSUES_CONTENT_STYLES = css` | ||
[data-nextjs-devtools-panel-tab-issues-content-container] { | ||
flex: 1; | ||
display: flex; | ||
flex-direction: column; | ||
overflow-y: auto; | ||
min-height: 0; | ||
padding: 14px; | ||
} | ||
` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,53 +1,54 @@ | ||
import type { OverlayState } from '../../../../shared' | ||
import type { DebugInfo } from '../../../../../shared/types' | ||
import type { ReadyRuntimeError } from '../../../../utils/get-error-by-type' | ||
import type { HydrationErrorState } from '../../../../../shared/hydration-error' | ||
|
||
import { IssuesTabSidebar } from './issues-tab-sidebar' | ||
import { | ||
GenericErrorDescription, | ||
HydrationErrorDescription, | ||
} from '../../../../container/errors' | ||
import { EnvironmentNameLabel } from '../../../errors/environment-name-label/environment-name-label' | ||
import { ErrorMessage } from '../../../errors/error-message/error-message' | ||
import { ErrorOverlayToolbar } from '../../../errors/error-overlay-toolbar/error-overlay-toolbar' | ||
import { ErrorTypeLabel } from '../../../errors/error-type-label/error-type-label' | ||
import { IssuesTabContent } from './issues-tab-content' | ||
import { css } from '../../../../utils/css' | ||
import { useActiveRuntimeError } from '../../../../hooks/use-active-runtime-error' | ||
import { Warning } from '../../../../icons/warning' | ||
|
||
export function IssuesTab({ | ||
debugInfo, | ||
runtimeErrors, | ||
getSquashedHydrationErrorDetails, | ||
buildError, | ||
}: { | ||
debugInfo: DebugInfo | ||
runtimeErrors: ReadyRuntimeError[] | ||
getSquashedHydrationErrorDetails: (error: Error) => HydrationErrorState | null | ||
buildError: OverlayState['buildError'] | ||
}) { | ||
const { | ||
isLoading, | ||
errorCode, | ||
errorType, | ||
hydrationWarning, | ||
activeError, | ||
activeIdx, | ||
setActiveIndex, | ||
notes, | ||
errorDetails, | ||
} = useActiveRuntimeError({ runtimeErrors, getSquashedHydrationErrorDetails }) | ||
|
||
if (isLoading) { | ||
// TODO: better loading state | ||
return null | ||
} | ||
Comment on lines
-36
to
-39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure if this |
||
|
||
if (!activeError) { | ||
return null | ||
return ( | ||
<div data-nextjs-devtools-panel-tab-issues-empty> | ||
<div data-nextjs-devtools-panel-tab-issues-empty-content> | ||
<div data-nextjs-devtools-panel-tab-issues-empty-icon> | ||
<Warning width={16} height={16} /> | ||
</div> | ||
<h3 data-nextjs-devtools-panel-tab-issues-empty-title> | ||
No Issues Found | ||
</h3> | ||
<p data-nextjs-devtools-panel-tab-issues-empty-subtitle> | ||
Issues will appear here when they occur. | ||
</p> | ||
</div> | ||
</div> | ||
) | ||
} | ||
|
||
const errorMessage = hydrationWarning ? ( | ||
<HydrationErrorDescription message={hydrationWarning} /> | ||
) : ( | ||
<GenericErrorDescription error={activeError.error} /> | ||
) | ||
|
||
return ( | ||
<div data-nextjs-devtools-panel-tab-issues> | ||
<IssuesTabSidebar | ||
|
@@ -56,32 +57,18 @@ export function IssuesTab({ | |
activeIdx={activeIdx} | ||
setActiveIndex={setActiveIndex} | ||
/> | ||
<div data-nextjs-devtools-panel-tab-issues-content> | ||
<div className="nextjs-container-errors-header"> | ||
<div | ||
className="nextjs__container_errors__error_title" | ||
// allow assertion in tests before error rating is implemented | ||
data-nextjs-error-code={errorCode} | ||
> | ||
<span data-nextjs-error-label-group> | ||
<ErrorTypeLabel errorType={errorType} /> | ||
{activeError.error.environmentName && ( | ||
<EnvironmentNameLabel | ||
environmentName={activeError.error.environmentName} | ||
/> | ||
)} | ||
</span> | ||
<ErrorOverlayToolbar | ||
error={activeError.error} | ||
debugInfo={debugInfo} | ||
/> | ||
</div> | ||
<ErrorMessage errorMessage={errorMessage} /> | ||
</div> | ||
|
||
{/* TODO: Content */} | ||
<div>Content</div> | ||
</div> | ||
{/* This is the copy of the Error Overlay content. */} | ||
<IssuesTabContent | ||
buildError={buildError} | ||
notes={notes} | ||
hydrationWarning={hydrationWarning} | ||
errorDetails={errorDetails} | ||
activeError={activeError} | ||
debugInfo={debugInfo} | ||
errorCode={errorCode} | ||
errorType={errorType} | ||
/> | ||
</div> | ||
) | ||
} | ||
|
@@ -93,11 +80,45 @@ export const DEVTOOLS_PANEL_TAB_ISSUES_STYLES = css` | |
min-height: 0; | ||
} | ||
|
||
[data-nextjs-devtools-panel-tab-issues-content] { | ||
[data-nextjs-devtools-panel-tab-issues-empty] { | ||
display: flex; | ||
flex: 1; | ||
padding: 12px; | ||
min-height: 0; | ||
} | ||
|
||
[data-nextjs-devtools-panel-tab-issues-empty-content] { | ||
display: flex; | ||
flex-direction: column; | ||
overflow-y: auto; | ||
min-height: 0; | ||
align-items: center; | ||
justify-content: center; | ||
flex: 1; | ||
border: 1px dashed var(--color-gray-alpha-500); | ||
border-radius: 4px; | ||
} | ||
|
||
[data-nextjs-devtools-panel-tab-issues-empty-icon] { | ||
margin-bottom: 16px; | ||
padding: 8px; | ||
border: 1px solid var(--color-gray-alpha-400); | ||
border-radius: 6px; | ||
|
||
background-color: var(--color-background-100); | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
} | ||
|
||
[data-nextjs-devtools-panel-tab-issues-empty-title] { | ||
color: var(--color-gray-1000); | ||
font-size: 16px; | ||
font-weight: 500; | ||
line-height: var(--line-height-20); | ||
} | ||
|
||
[data-nextjs-devtools-panel-tab-issues-empty-subtitle] { | ||
color: var(--color-gray-900); | ||
font-size: 14px; | ||
line-height: var(--line-height-21); | ||
} | ||
` |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we able to extract the content of Error's content from errors.tsx to a separate component and reuse here?
It looks like a error view that can be reused here, the difference between the previous dev-overlay and this tab is the outside layout are different.
This tab can be composed by a issue tab layout (with
nextjs-devtools-panel-tab-issues-content
attribute) and the Error overview. Prefer to refactor rather than duplicate everythingThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Duplicating once is fine. Especially for UI. Abstracting it based on two usages is likely to be incorrect. Abstracting would be counter to forking the UI between feature flagged and not feature flagged.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The abstraction here is confusing tho. the component is IssueTabContent, then it could be either build or runtime error.
nextjs-devtools-panel-tab-issues-conten
is not used here.I'm fine with even just refactoring a bit here with keeping the duplication. At least we can make it more like
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, I've moved all the Error Overlay contents inside the
IssueTabContent
and added comments to reduce confusion.