Skip to content

Commit 7ee3691

Browse files
authored
Merge branch 'canary' into feat/gtm-consent-management
2 parents 84bf774 + 9823f47 commit 7ee3691

File tree

19 files changed

+621
-41
lines changed

19 files changed

+621
-41
lines changed

packages/next/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@
173173
"@storybook/addon-a11y": "8.6.0",
174174
"@storybook/addon-essentials": "8.6.0",
175175
"@storybook/addon-interactions": "8.6.0",
176-
"@storybook/addon-webpack5-compiler-swc": "1.0.5",
176+
"@storybook/addon-webpack5-compiler-swc": "3.0.0",
177177
"@storybook/blocks": "8.6.0",
178178
"@storybook/react": "8.6.0",
179179
"@storybook/react-webpack5": "8.6.0",

packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/devtools-indicator.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import type { CSSProperties } from 'react'
22
import type { OverlayState, OverlayDispatch } from '../../shared'
3-
import type { DevToolsScale } from '../errors/dev-tools-indicator/dev-tools-info/preferences'
43

54
import { useState } from 'react'
65
import { NextLogo } from './next-logo'
@@ -26,13 +25,11 @@ export function DevToolsIndicator({
2625
dispatch,
2726
errorCount,
2827
isBuildError,
29-
scale,
3028
}: {
3129
state: OverlayState
3230
dispatch: OverlayDispatch
3331
errorCount: number
3432
isBuildError: boolean
35-
scale: DevToolsScale
3633
}) {
3734
const [open, setOpen] = useState(false)
3835

@@ -89,7 +86,7 @@ export function DevToolsIndicator({
8986
isDevBuilding={state.buildingIndicator}
9087
isDevRendering={state.renderingIndicator}
9188
isBuildError={isBuildError}
92-
scale={scale}
89+
scale={state.scale}
9390
/>
9491
</Draggable>
9592
</Toast>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import type { DevToolsPanelTabType } from '../devtools-panel'
2+
import type { Corners } from '../../../shared'
3+
4+
import { SettingsTab } from './settings-tab'
5+
6+
export function DevToolsPanelTab({
7+
activeTab,
8+
devToolsPosition,
9+
scale,
10+
handlePositionChange,
11+
handleScaleChange,
12+
}: {
13+
activeTab: DevToolsPanelTabType
14+
devToolsPosition: Corners
15+
scale: number
16+
handlePositionChange: (e: React.ChangeEvent<HTMLSelectElement>) => void
17+
handleScaleChange: (e: React.ChangeEvent<HTMLSelectElement>) => void
18+
}) {
19+
switch (activeTab) {
20+
case 'settings':
21+
return (
22+
<SettingsTab
23+
devToolsPosition={devToolsPosition}
24+
scale={scale}
25+
handlePositionChange={handlePositionChange}
26+
handleScaleChange={handleScaleChange}
27+
/>
28+
)
29+
case 'route':
30+
return <div>Route</div>
31+
case 'issues':
32+
return <div>Issues</div>
33+
default:
34+
return null
35+
}
36+
}
Lines changed: 312 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,312 @@
1+
import type { Corners } from '../../../shared'
2+
3+
import { useTheme } from '../hooks/use-theme'
4+
import { Select } from '../../select/select'
5+
import { STORAGE_KEY_THEME, NEXT_DEV_TOOLS_SCALE } from '../../../shared'
6+
import { css } from '../../../utils/css'
7+
import LightIcon from '../../../icons/light-icon'
8+
import DarkIcon from '../../../icons/dark-icon'
9+
import SystemIcon from '../../../icons/system-icon'
10+
import EyeIcon from '../../../icons/eye-icon'
11+
12+
function ThemeIcon({ theme }: { theme: 'dark' | 'light' | 'system' }) {
13+
switch (theme) {
14+
case 'system':
15+
return <SystemIcon />
16+
case 'dark':
17+
return <DarkIcon />
18+
case 'light':
19+
return <LightIcon />
20+
default:
21+
return null
22+
}
23+
}
24+
25+
export function SettingsTab({
26+
devToolsPosition,
27+
scale,
28+
handlePositionChange,
29+
handleScaleChange,
30+
}: {
31+
devToolsPosition: Corners
32+
scale: number
33+
handlePositionChange: (e: React.ChangeEvent<HTMLSelectElement>) => void
34+
handleScaleChange: (e: React.ChangeEvent<HTMLSelectElement>) => void
35+
}) {
36+
const [theme, setTheme] = useTheme()
37+
38+
const handleThemeChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
39+
const portal = document.querySelector('nextjs-portal')
40+
if (!portal) return
41+
42+
setTheme(e.target.value)
43+
44+
if (e.target.value === 'system') {
45+
portal.classList.remove('dark')
46+
portal.classList.remove('light')
47+
localStorage.removeItem(STORAGE_KEY_THEME)
48+
return
49+
}
50+
51+
if (e.target.value === 'dark') {
52+
portal.classList.add('dark')
53+
portal.classList.remove('light')
54+
localStorage.setItem(STORAGE_KEY_THEME, 'dark')
55+
} else {
56+
portal.classList.remove('dark')
57+
portal.classList.add('light')
58+
localStorage.setItem(STORAGE_KEY_THEME, 'light')
59+
}
60+
}
61+
62+
function handleRestartDevServer(invalidatePersistentCache: boolean) {
63+
let endpoint = '/__nextjs_restart_dev'
64+
65+
if (invalidatePersistentCache) {
66+
endpoint = '/__nextjs_restart_dev?invalidatePersistentCache'
67+
}
68+
69+
fetch(endpoint, {
70+
method: 'POST',
71+
}).then(() => {
72+
// TODO: poll server status and reload when the server is back up.
73+
// https://github.com/vercel/next.js/pull/80005
74+
})
75+
}
76+
77+
function hide() {
78+
fetch('/__nextjs_disable_dev_indicator', {
79+
method: 'POST',
80+
})
81+
}
82+
83+
return (
84+
<div data-nextjs-devtools-panel-settings>
85+
<div data-nextjs-devtools-panel-settings-section>
86+
<div data-nextjs-devtools-panel-settings-section-header>
87+
<label htmlFor="theme">Theme</label>
88+
<p data-nextjs-devtools-panel-settings-section-description>
89+
Select your theme preference.
90+
</p>
91+
</div>
92+
<Select
93+
id="theme"
94+
name="theme"
95+
prefix={<ThemeIcon theme={theme as 'dark' | 'light' | 'system'} />}
96+
value={theme}
97+
onChange={handleThemeChange}
98+
>
99+
<option value="system">System</option>
100+
<option value="light">Light</option>
101+
<option value="dark">Dark</option>
102+
</Select>
103+
</div>
104+
105+
<div data-nextjs-devtools-panel-settings-section>
106+
<div data-nextjs-devtools-panel-settings-section-header>
107+
<label htmlFor="position">Position</label>
108+
<p data-nextjs-devtools-panel-settings-section-description>
109+
Adjust the placement of your dev tools.
110+
</p>
111+
</div>
112+
<Select
113+
id="position"
114+
name="position"
115+
value={devToolsPosition}
116+
onChange={handlePositionChange}
117+
>
118+
<option value="bottom-left">Bottom Left</option>
119+
<option value="bottom-right">Bottom Right</option>
120+
<option value="top-left">Top Left</option>
121+
<option value="top-right">Top Right</option>
122+
</Select>
123+
</div>
124+
125+
<div data-nextjs-devtools-panel-settings-section>
126+
<div data-nextjs-devtools-panel-settings-section-header>
127+
<label htmlFor="size">Size</label>
128+
<p data-nextjs-devtools-panel-settings-section-description>
129+
Adjust the size of your dev tools.
130+
</p>
131+
</div>
132+
<Select
133+
id="size"
134+
name="size"
135+
value={scale}
136+
onChange={handleScaleChange}
137+
>
138+
{Object.entries(NEXT_DEV_TOOLS_SCALE).map(([key, value]) => {
139+
return (
140+
<option value={value} key={key}>
141+
{key}
142+
</option>
143+
)
144+
})}
145+
</Select>
146+
</div>
147+
148+
<div data-nextjs-devtools-panel-settings-section>
149+
<div data-nextjs-devtools-panel-settings-section-header>
150+
<label id="hide-dev-tools">Hide Dev Tools for this session</label>
151+
<p data-nextjs-devtools-panel-settings-section-description>
152+
Hide Dev Tools until you restart your dev server, or 1 day.
153+
</p>
154+
</div>
155+
<div>
156+
<button
157+
aria-describedby="hide-dev-tools"
158+
name="hide-dev-tools"
159+
data-hide-dev-tools
160+
data-nextjs-devtools-panel-settings-section-action-button
161+
onClick={hide}
162+
>
163+
<EyeIcon />
164+
<span>Hide</span>
165+
</button>
166+
</div>
167+
</div>
168+
169+
<div data-nextjs-devtools-panel-settings-section>
170+
<div data-nextjs-devtools-panel-settings-section-header>
171+
<label>Disable Dev Tools for this project</label>
172+
<p data-nextjs-devtools-panel-settings-section-description>
173+
To disable this UI completely, set{' '}
174+
<code className="dev-tools-info-code">devIndicators: false</code> in
175+
your <code className="dev-tools-info-code">next.config</code> file.
176+
</p>
177+
</div>
178+
</div>
179+
180+
<div data-nextjs-devtools-panel-settings-section>
181+
<div data-nextjs-devtools-panel-settings-section-header>
182+
<label id="restart-dev-server">Restart Dev Server</label>
183+
<p data-nextjs-devtools-panel-settings-section-description>
184+
Restarts the development server without needing to leave the
185+
browser.
186+
</p>
187+
</div>
188+
<div>
189+
<button
190+
aria-describedby="restart-dev-server"
191+
title="Restarts the development server without needing to leave the browser."
192+
name="restart-dev-server"
193+
data-restart-dev-server
194+
data-nextjs-devtools-panel-settings-section-action-button
195+
onClick={() =>
196+
handleRestartDevServer(/*invalidatePersistentCache*/ false)
197+
}
198+
>
199+
<span>Restart</span>
200+
</button>
201+
</div>
202+
</div>
203+
204+
{process.env.__NEXT_TURBOPACK_PERSISTENT_CACHE ? (
205+
<div data-nextjs-devtools-panel-settings-section>
206+
<div data-nextjs-devtools-panel-settings-section-header>
207+
<label id="reset-bundler-cache">Reset Bundler Cache</label>
208+
<p data-nextjs-devtools-panel-settings-section-description>
209+
Clears the bundler cache and restarts the dev server. Helpful if
210+
you are seeing stale errors or changes are not appearing.
211+
</p>
212+
</div>
213+
<div>
214+
<button
215+
aria-describedby="reset-bundler-cache"
216+
title="Clears the bundler cache and restarts the dev server. Helpful if you are seeing stale errors or changes are not appearing."
217+
name="reset-bundler-cache"
218+
data-reset-bundler-cache
219+
data-nextjs-devtools-panel-settings-section-action-button
220+
onClick={() =>
221+
handleRestartDevServer(/*invalidatePersistentCache*/ true)
222+
}
223+
>
224+
<span>Reset Cache</span>
225+
</button>
226+
</div>
227+
</div>
228+
) : null}
229+
</div>
230+
)
231+
}
232+
233+
export const DEVTOOLS_PANEL_TAB_SETTINGS_STYLES = css`
234+
[data-nextjs-devtools-panel-settings] {
235+
padding: 16px;
236+
}
237+
238+
[data-nextjs-devtools-panel-settings-section]:first-child {
239+
padding-top: 0;
240+
}
241+
242+
[data-nextjs-devtools-panel-settings-section] {
243+
padding: 12px 0;
244+
border-bottom: 1px solid var(--color-gray-400);
245+
display: flex;
246+
justify-content: space-between;
247+
align-items: center;
248+
gap: 24px;
249+
}
250+
251+
[data-nextjs-devtools-panel-settings-section]:last-child {
252+
padding-bottom: 0;
253+
border-bottom: none;
254+
}
255+
256+
[data-nextjs-devtools-panel-settings-section-header] {
257+
margin-bottom: 0;
258+
flex: 1;
259+
}
260+
261+
[data-nextjs-devtools-panel-settings-section-header] label {
262+
font-size: var(--size-14);
263+
font-weight: 500;
264+
color: var(--color-gray-1000);
265+
margin: 0;
266+
}
267+
268+
[data-nextjs-devtools-panel-settings-section-description] {
269+
color: var(--color-gray-900);
270+
font-size: var(--size-14);
271+
margin: 0;
272+
}
273+
274+
[data-nextjs-select],
275+
[data-nextjs-devtools-panel-settings-section-action-button] {
276+
display: flex;
277+
align-items: center;
278+
gap: 8px;
279+
background: var(--color-background-100);
280+
border: 1px solid var(--color-gray-400);
281+
border-radius: var(--rounded-lg);
282+
font-weight: 400;
283+
font-size: var(--size-14);
284+
color: var(--color-gray-1000);
285+
padding: 6px 8px;
286+
287+
&:hover {
288+
background: var(--color-gray-100);
289+
}
290+
}
291+
292+
[data-nextjs-select] {
293+
&:focus-within {
294+
outline: var(--focus-ring);
295+
}
296+
297+
select {
298+
all: unset;
299+
}
300+
301+
option {
302+
color: var(--color-gray-1000);
303+
background: var(--color-background-100);
304+
}
305+
}
306+
307+
:global(.icon) {
308+
width: 18px;
309+
height: 18px;
310+
color: #666;
311+
}
312+
`

0 commit comments

Comments
 (0)