Skip to content

[devtools] panel ui issues tab sidebar #80728

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

Draft
wants to merge 6 commits into
base: jiwon/06-23-_devtools_add_correct_scrollbar_to_dialog_body
Choose a base branch
from
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ export const DEVTOOLS_PANEL_FOOTER_STYLES = css`
display: flex;
justify-content: space-between;
align-items: center;
margin-top: auto;
border-top: 1px solid var(--color-gray-400);
border-radius: 0 0 var(--rounded-xl) var(--rounded-xl);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { css } from '../../../../utils/css'

export function IssuesTabSidebarFrameSkeleton({
isActive,
...props
}: {
isActive: boolean
} & React.ButtonHTMLAttributes<HTMLButtonElement>) {
return (
<button
data-nextjs-devtools-panel-tab-issues-sidebar-frame
data-nextjs-devtools-panel-tab-issues-sidebar-frame-skeleton
data-nextjs-devtools-panel-tab-issues-sidebar-frame-active={isActive}
{...props}
>
<div
data-nextjs-devtools-panel-tab-issues-sidebar-frame-skeleton-bar
data-nextjs-devtools-panel-tab-issues-sidebar-frame-skeleton-bar-1
/>
<div
data-nextjs-devtools-panel-tab-issues-sidebar-frame-skeleton-bar
data-nextjs-devtools-panel-tab-issues-sidebar-frame-skeleton-bar-2
/>
</button>
)
}

export const DEVTOOLS_PANEL_TAB_ISSUES_SIDEBAR_FRAME_SKELETON_STYLES = css`
[data-nextjs-devtools-panel-tab-issues-sidebar-frame-skeleton-bar] {
height: var(--size-12);
border-radius: 100px;
background: linear-gradient(
90deg,
var(--color-gray-200) 25%,
var(--color-gray-100) 50%,
var(--color-gray-200) 75%
);
background-size: 200% 100%;
animation: skeleton-shimmer 1.5s ease-in-out infinite;
}

[data-nextjs-devtools-panel-tab-issues-sidebar-frame-skeleton-bar-1] {
width: 75%;
margin-bottom: 8px;
}

[data-nextjs-devtools-panel-tab-issues-sidebar-frame-skeleton-bar-2] {
width: 36.5%;
}

@keyframes skeleton-shimmer {
0% {
background-position: -200% 0;
}
100% {
background-position: 200% 0;
}
}

/* Respect user's motion preferences */
@media (prefers-reduced-motion: reduce) {
[data-nextjs-devtools-panel-tab-issues-sidebar-frame-skeleton-bar-1],
[data-nextjs-devtools-panel-tab-issues-sidebar-frame-skeleton-bar-2] {
animation: none;
background: var(--color-gray-200);
}
}
`
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import type { ReadyRuntimeError } from '../../../../utils/get-error-by-type'

import { Suspense, useMemo } from 'react'

import { css } from '../../../../utils/css'
import { getFrameSource } from '../../../../../shared/stack-frame'
import { useFrames } from '../../../../utils/get-error-by-type'
import { getErrorTypeLabel } from '../../../../container/errors'
import { IssuesTabSidebarFrameSkeleton } from './issues-tab-sidebar-frame-skeleton'

export function IssuesTabSidebar({
runtimeErrors,
activeIdx,
setActiveIndex,
}: {
runtimeErrors: ReadyRuntimeError[]
errorType: string | null
activeIdx: number
setActiveIndex: (idx: number) => void
}) {
return (
<aside data-nextjs-devtools-panel-tab-issues-sidebar>
{runtimeErrors.map((runtimeError, idx) => {
const isActive = idx === activeIdx
return (
<Suspense
key={idx}
fallback={<IssuesTabSidebarFrameSkeleton isActive={isActive} />}
>
<IssuesTabSidebarFrame
runtimeError={runtimeError}
idx={idx}
isActive={isActive}
setActiveIndex={setActiveIndex}
/>
</Suspense>
)
})}
</aside>
)
}

function IssuesTabSidebarFrame({
runtimeError,
idx,
isActive,
setActiveIndex,
}: {
runtimeError: ReadyRuntimeError
idx: number
isActive: boolean
setActiveIndex: (idx: number) => void
}) {
const frames = useFrames(runtimeError)

const firstFrame = useMemo(() => {
const firstFirstPartyFrameIndex = frames.findIndex(
(entry) =>
!entry.ignored &&
Boolean(entry.originalCodeFrame) &&
Boolean(entry.originalStackFrame)
)

return frames[firstFirstPartyFrameIndex] ?? null
}, [frames])

if (!firstFrame?.originalStackFrame) {
// TODO: Better handling
return <div>No frame</div>
}

const frameSource = getFrameSource(firstFrame.originalStackFrame)
const errorType = getErrorTypeLabel(runtimeError.error, runtimeError.type)

return (
<button
data-nextjs-devtools-panel-tab-issues-sidebar-frame
data-nextjs-devtools-panel-tab-issues-sidebar-frame-active={isActive}
onClick={() => setActiveIndex(idx)}
>
<span data-nextjs-devtools-panel-tab-issues-sidebar-frame-error-type>
{errorType}
</span>
<span data-nextjs-devtools-panel-tab-issues-sidebar-frame-source>
{frameSource}
</span>
</button>
)
}

export const DEVTOOLS_PANEL_TAB_ISSUES_SIDEBAR_STYLES = css`
[data-nextjs-devtools-panel-tab-issues-sidebar] {
display: flex;
flex-direction: column;
gap: 4px;
padding: 8px;
border-right: 1px solid var(--color-gray-400);
overflow-y: auto;
min-height: 0;

min-width: 128px;

@media (min-width: 576px) {
max-width: 138px;
width: 100%;
}

@media (min-width: 768px) {
max-width: 172.5px;
width: 100%;
}

@media (min-width: 992px) {
max-width: 230px;
width: 100%;
}
}

[data-nextjs-devtools-panel-tab-issues-sidebar-frame] {
display: flex;
flex-direction: column;
padding: 10px 8px;
border-radius: var(--rounded-lg);
transition: background-color 0.2s ease-in-out;

&:hover {
background-color: var(--color-gray-200);
}

&:active {
background-color: var(--color-gray-300);
}
}

[data-nextjs-devtools-panel-tab-issues-sidebar-frame-active='true'] {
background-color: var(--color-gray-100);
}

[data-nextjs-devtools-panel-tab-issues-sidebar-frame-error-type] {
display: inline-block;
align-self: flex-start;
color: var(--color-gray-1000);
font-size: var(--size-14);
font-weight: 500;
line-height: var(--size-20);
}

[data-nextjs-devtools-panel-tab-issues-sidebar-frame-source] {
display: inline-block;
align-self: flex-start;
color: var(--color-gray-900);
font-size: var(--size-13);
line-height: var(--size-18);
}
`
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ 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,
Expand All @@ -22,8 +23,15 @@ export function IssuesTab({
runtimeErrors: ReadyRuntimeError[]
getSquashedHydrationErrorDetails: (error: Error) => HydrationErrorState | null
}) {
const { isLoading, errorCode, errorType, hydrationWarning, activeError } =
useActiveRuntimeError({ runtimeErrors, getSquashedHydrationErrorDetails })
const {
isLoading,
errorCode,
errorType,
hydrationWarning,
activeError,
activeIdx,
setActiveIndex,
} = useActiveRuntimeError({ runtimeErrors, getSquashedHydrationErrorDetails })

if (isLoading) {
// TODO: better loading state
Expand All @@ -42,8 +50,12 @@ export function IssuesTab({

return (
<div data-nextjs-devtools-panel-tab-issues>
{/* TODO: Sidebar */}
<aside>Sidebar</aside>
<IssuesTabSidebar
runtimeErrors={runtimeErrors}
errorType={errorType}
activeIdx={activeIdx}
setActiveIndex={setActiveIndex}
/>
<div data-nextjs-devtools-panel-tab-issues-content>
<div className="nextjs-container-errors-header">
<div
Expand Down Expand Up @@ -77,5 +89,15 @@ export function IssuesTab({
export const DEVTOOLS_PANEL_TAB_ISSUES_STYLES = css`
[data-nextjs-devtools-panel-tab-issues] {
display: flex;
flex: 1;
min-height: 0;
}

[data-nextjs-devtools-panel-tab-issues-content] {
flex: 1;
display: flex;
flex-direction: column;
overflow-y: auto;
min-height: 0;
}
`
39 changes: 38 additions & 1 deletion packages/next/src/next-devtools/dev-overlay/storybook/errors.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { SupportedErrorEvent } from '../container/runtime-error/render-error'
import type { ReadyRuntimeError } from '../utils/get-error-by-type'
import { lorem } from '../utils/lorem'

const originalCodeFrame = (message: string) => {
return `\u001b[0m \u001b[90m 1 \u001b[39m \u001b[36mexport\u001b[39m \u001b[36mdefault\u001b[39m \u001b[36mfunction\u001b[39m \u001b[33mHome\u001b[39m() {\u001b[0m
Expand Down Expand Up @@ -103,7 +104,7 @@ export const runtimeErrors: ReadyRuntimeError[] = [
{
id: 1,
runtime: true,
error: new Error('First error message'),
error: new Error(lorem),
frames: () =>
Promise.resolve([
frame,
Expand Down Expand Up @@ -181,4 +182,40 @@ export const runtimeErrors: ReadyRuntimeError[] = [
]),
type: 'runtime',
},
{
id: 5,
runtime: true,
error: new Error('Fifth error message'),
frames: () =>
Promise.resolve([
{
error: true,
reason: 'Fifth error message',
external: false,
ignored: false,
sourceStackFrame,
originalStackFrame,
originalCodeFrame: originalCodeFrame('Fifth error message'),
},
]),
type: 'console',
},
{
id: 6,
runtime: true,
error: new Error('Sixth error message'),
frames: () =>
Promise.resolve([
{
error: true,
reason: 'Sixth error message',
external: false,
ignored: false,
sourceStackFrame,
originalStackFrame,
originalCodeFrame: originalCodeFrame('Sixth error message'),
},
]),
type: 'recoverable',
},
]
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import { DEVTOOLS_PANEL_VERSION_INFO_STYLES } from '../components/devtools-panel
import { DEVTOOLS_PANEL_TAB_SETTINGS_STYLES } from '../components/devtools-panel/devtools-panel-tab/settings-tab'
import { CALL_STACK_STYLES } from '../components/call-stack/call-stack'
import { DEVTOOLS_PANEL_TAB_ISSUES_STYLES } from '../components/devtools-panel/devtools-panel-tab/issues-tab/issues-tab'
import { DEVTOOLS_PANEL_TAB_ISSUES_SIDEBAR_STYLES } from '../components/devtools-panel/devtools-panel-tab/issues-tab/issues-tab-sidebar'
import { DEVTOOLS_PANEL_TAB_ISSUES_SIDEBAR_FRAME_SKELETON_STYLES } from '../components/devtools-panel/devtools-panel-tab/issues-tab/issues-tab-sidebar-frame-skeleton'

export function ComponentStyles() {
return (
Expand Down Expand Up @@ -66,6 +68,8 @@ export function ComponentStyles() {
${DEVTOOLS_PANEL_VERSION_INFO_STYLES}
${DEVTOOLS_PANEL_TAB_SETTINGS_STYLES}
${DEVTOOLS_PANEL_TAB_ISSUES_STYLES}
${DEVTOOLS_PANEL_TAB_ISSUES_SIDEBAR_STYLES}
${DEVTOOLS_PANEL_TAB_ISSUES_SIDEBAR_FRAME_SKELETON_STYLES}
`}
</style>
)
Expand Down
Loading