Repository: vercel/next.js
Pull request: [devtools] add dialog behavior to panel ui

### Testing Plan

Open Storybook and goto `dev-overlay > With Panel` OR `devtools-panel > Default`

https://github.com/user-attachments/assets/d1b08482-9382-4a1c-906d-a8e9d7c68f66

Closes NEXT-4516

Changed files:
filename: packages/next/src/next-devtools/dev-overlay/components/devtools-panel/devtools-panel.stories.tsx
patch: @@ -0,0 +1,32 @@
+import type { Meta, StoryObj } from '@storybook/react'
+import type { OverlayState } from '../../shared'
+
+import { DevToolsPanel } from './devtools-panel'
+import { INITIAL_OVERLAY_STATE } from '../../shared'
+import { withShadowPortal } from '../../storybook/with-shadow-portal'
+
+const meta: Meta<typeof DevToolsPanel> = {
+  component: DevToolsPanel,
+  parameters: {
+    layout: 'centered',
+  },
+  argTypes: {},
+  decorators: [withShadowPortal],
+}
+
+export default meta
+type Story = StoryObj<typeof DevToolsPanel>
+
+const state: OverlayState = {
+  ...INITIAL_OVERLAY_STATE,
+  routerType: 'app',
+  isErrorOverlayOpen: false,
+  isDevToolsPanelOpen: true,
+}
+
+export const Default: Story = {
+  args: {
+    state,
+    dispatch: () => {},
+  },
+}

filename: packages/next/src/next-devtools/dev-overlay/components/devtools-panel/devtools-panel.tsx
patch: @@ -1,12 +1,62 @@
+import type { OverlayDispatch, OverlayState } from '../../shared'
+
+import { Dialog, DialogContent, DialogHeader, DialogBody } from '../dialog'
+import { Overlay } from '../overlay/overlay'
+import { ACTION_DEVTOOLS_PANEL_TOGGLE } from '../../shared'
 import { css } from '../../utils/css'
 
-export function DevToolsPanel() {
-  // TODO: Remove style; It is to indicate faster in the browser.
-  return <div data-nextjs-devtools-panel>DevToolsPanel</div>
+export function DevToolsPanel({
+  // eslint-disable-next-line @typescript-eslint/no-unused-vars
+  state,
+  dispatch,
+}: {
+  state: OverlayState
+  dispatch: OverlayDispatch
+}) {
+  const onClose = () => {
+    dispatch({ type: ACTION_DEVTOOLS_PANEL_TOGGLE })
+  }
+
+  return (
+    <Overlay data-nextjs-devtools-panel-overlay>
+      <Dialog
+        data-nextjs-devtools-panel-dialog
+        aria-labelledby="nextjs__container_dev_tools_panel_label"
+        aria-describedby="nextjs__container_dev_tools_panel_desc"
+        onClose={onClose}
+      >
+        <DialogContent>
+          <DialogHeader></DialogHeader>
+          <DialogBody></DialogBody>
+        </DialogContent>
+      </Dialog>
+    </Overlay>
+  )
 }
 
 export const DEVTOOLS_PANEL_STYLES = css`
-  [data-nextjs-devtools-panel] {
-    color: black;
+  [data-nextjs-devtools-panel-overlay] {
+    padding: initial;
+    top: 10vh;
+  }
+
+  [data-nextjs-devtools-panel-dialog] {
+    -webkit-font-smoothing: antialiased;
+    display: flex;
+    flex-direction: column;
+    background: var(--color-background-100);
+    background-clip: padding-box;
+    border: 1px solid var(--color-gray-400);
+    border-radius: var(--rounded-xl);
+    box-shadow: var(--shadow-lg);
+    position: relative;
+    overflow-y: auto;
+
+    /* TODO: Remove once the content is filled. */
+    min-width: 800px;
+    min-height: 500px;
+
+    /* This is handled from dialog/styles.ts */
+    max-width: var(--next-dialog-max-width);
   }
 `

filename: packages/next/src/next-devtools/dev-overlay/components/dialog/styles.ts
patch: @@ -1,4 +1,6 @@
-const styles = `
+import { css } from '../../utils/css'
+
+export const styles = css`
   [data-nextjs-dialog-root] {
     --next-dialog-radius: var(--rounded-xl);
     --next-dialog-max-width: 960px;
@@ -25,9 +27,11 @@ const styles = `
       scale: 1;
     }
 
-    [data-nextjs-scroll-fader][data-side="top"] {
+    [data-nextjs-scroll-fader][data-side='top'] {
       left: 1px;
-      top: calc(var(--next-dialog-notch-height) + var(--next-dialog-border-width));
+      top: calc(
+        var(--next-dialog-notch-height) + var(--next-dialog-border-width)
+      );
       width: calc(100% - var(--next-dialog-padding));
       opacity: 0;
     }
@@ -37,7 +41,8 @@ const styles = `
     outline: 0;
   }
 
-  [data-nextjs-dialog], [data-nextjs-dialog] * {
+  [data-nextjs-dialog],
+  [data-nextjs-dialog] * {
     &::-webkit-scrollbar {
       width: 6px;
       height: 6px;
@@ -53,7 +58,7 @@ const styles = `
       border-radius: 0 0 1rem 1rem;
       background-color: var(--color-background-100);
     }
-      
+
     &::-webkit-scrollbar-thumb {
       border-radius: 1rem;
       background-color: var(--color-gray-500);
@@ -123,5 +128,3 @@ const styles = `
     }
   }
 `
-
-export { styles }

filename: packages/next/src/next-devtools/dev-overlay/dev-overlay.stories.tsx
patch: @@ -7,6 +7,7 @@ import imgApp from './app.png'
 import { useReducer } from 'react'
 import { DevOverlay } from './dev-overlay'
 import {
+  ACTION_DEVTOOLS_PANEL_TOGGLE,
   ACTION_ERROR_OVERLAY_CLOSE,
   ACTION_ERROR_OVERLAY_OPEN,
   ACTION_ERROR_OVERLAY_TOGGLE,
@@ -93,8 +94,7 @@ const initialState: OverlayState = {
     installed: '15.2.0',
     staleness: 'fresh',
   },
-  isErrorOverlayOpen: true,
-  // TODO: This will be handled on the next stack——with proper story.
+  isErrorOverlayOpen: false,
   isDevToolsPanelOpen: false,
 }
 
@@ -111,11 +111,13 @@ function useOverlayReducer() {
         case ACTION_ERROR_OVERLAY_TOGGLE: {
           return { ...state, isErrorOverlayOpen: !state.isErrorOverlayOpen }
         }
+        case ACTION_DEVTOOLS_PANEL_TOGGLE: {
+          return { ...state, isDevToolsPanelOpen: !state.isDevToolsPanelOpen }
+        }
         default: {
           return state
         }
       }
-      return state
     },
     initialState
   )
@@ -150,3 +152,37 @@ export const Default: Story = {
     )
   },
 }
+
+export const WithPanel: Story = {
+  beforeEach: () => {
+    process.env.__NEXT_DEVTOOL_NEW_PANEL_UI = 'true'
+
+    // clean up callback function
+    return () => {
+      delete process.env.__NEXT_DEVTOOL_NEW_PANEL_UI
+    }
+  },
+  render: function DevOverlayStory() {
+    const [state, dispatch] = useOverlayReducer()
+    return (
+      <>
+        <img
+          src={imgApp}
+          style={{
+            width: '100%',
+            height: '100vh',
+            objectFit: 'contain',
+          }}
+        />
+        <DevOverlay
+          state={state}
+          dispatch={dispatch}
+          getSquashedHydrationErrorDetails={
+            // Testing like App Router where we no longer quash hydration errors
+            getNoSquashedHydrationErrorDetails
+          }
+        />
+      </>
+    )
+  },
+}

filename: packages/next/src/next-devtools/dev-overlay/dev-overlay.tsx
patch: @@ -48,7 +48,9 @@ export function DevOverlay({
                       isBuildError={isBuildError}
                     />
 
-                    {state.isDevToolsPanelOpen && <DevToolsPanel />}
+                    {state.isDevToolsPanelOpen && (
+                      <DevToolsPanel state={state} dispatch={dispatch} />
+                    )}
                   </>
                 ) : (
                   <DevToolsIndicator