Skip to content

Commit a7d8ddd

Browse files
authored
[DevTools] Add Settings button on Suspense Tab (facebook#34624)
The settings dialog appears on all tabs and should be reachable from Suspense tab too. It's a bit weird because it's not contextual to the tab and it shows you whatever your last settings tab was opened. Maybe it should default to opening to the current tab's settings? There aren't any Suspense specific settings yet but there definitely will be. We could move the "Show all" into settings but it might be frequently that you want to check why something isn't suspending a Suspense boundary or test SSR streaming. However, the general settings still apply to the Suspense tab. E.g. switching dark/light mode. <img width="857" height="233" alt="Screenshot 2025-09-27 at 12 35 05 PM" src="https://github.com/user-attachments/assets/4a38e94f-2074-4dce-906b-9a1c40bccb9b" />
1 parent 8309724 commit a7d8ddd

File tree

2 files changed

+85
-66
lines changed

2 files changed

+85
-66
lines changed

packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseTab.css

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@
1414
-webkit-font-smoothing: var(--font-smoothing);
1515
}
1616

17+
.VRule {
18+
height: 20px;
19+
width: 1px;
20+
flex: 0 0 1px;
21+
margin: 0 0.5rem;
22+
background-color: var(--color-border);
23+
}
24+
1725
.TreeWrapper {
1826
border-top: 1px solid var(--color-border);
1927
flex: 1 1 65%;
@@ -107,13 +115,13 @@
107115
.SuspenseTreeViewHeader {
108116
flex: 0 0 42px;
109117
padding: 0.5rem;
110-
display: grid;
111-
grid-template-columns: auto 1fr auto auto auto;
118+
display: flex;
112119
align-items: flex-start;
113120
border-bottom: 1px solid var(--color-border);
114121
}
115122

116123
.SuspenseBreadcrumbs {
124+
flex: 1;
117125
/**
118126
* TODO: Switch to single item view on overflow like OwnerStack does.
119127
* OwnerStack has more constraints that make it easier so it won't be a 1:1 port.

packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseTab.js

Lines changed: 75 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,17 @@ import {
3333
SuspenseTreeDispatcherContext,
3434
SuspenseTreeStateContext,
3535
} from './SuspenseTreeContext';
36-
import {StoreContext} from '../context';
36+
import {StoreContext, OptionsContext} from '../context';
3737
import {TreeDispatcherContext} from '../Components/TreeContext';
3838
import Button from '../Button';
3939
import Tooltip from '../Components/reach-ui/tooltip';
4040
import typeof {
4141
SyntheticEvent,
4242
SyntheticPointerEvent,
4343
} from 'react-dom-bindings/src/events/SyntheticEvent';
44+
import SettingsModal from 'react-devtools-shared/src/devtools/views/Settings/SettingsModal';
45+
import SettingsModalContextToggle from 'react-devtools-shared/src/devtools/views/Settings/SettingsModalContextToggle';
46+
import {SettingsModalContextController} from 'react-devtools-shared/src/devtools/views/Settings/SettingsModalContext';
4447

4548
type Orientation = 'horizontal' | 'vertical';
4649

@@ -212,6 +215,7 @@ function ToggleInspectedElement({
212215
}
213216

214217
function SuspenseTab(_: {}) {
218+
const {hideSettings} = useContext(OptionsContext);
215219
const [state, dispatch] = useReducer<LayoutState, null, LayoutAction>(
216220
layoutReducer,
217221
null,
@@ -394,75 +398,82 @@ function SuspenseTab(_: {}) {
394398
};
395399

396400
return (
397-
<div className={styles.SuspenseTab} ref={wrapperTreeRef}>
398-
<div className={styles.TreeWrapper} ref={resizeTreeRef}>
399-
{treeListDisabled ? null : (
400-
<div
401-
className={styles.TreeList}
402-
hidden={treeListHidden}
403-
ref={resizeTreeListRef}>
404-
<SuspenseTreeList />
405-
</div>
406-
)}
407-
{treeListDisabled ? null : (
408-
<div className={styles.ResizeBarWrapper} hidden={treeListHidden}>
401+
<SettingsModalContextController>
402+
<div className={styles.SuspenseTab} ref={wrapperTreeRef}>
403+
<div className={styles.TreeWrapper} ref={resizeTreeRef}>
404+
{treeListDisabled ? null : (
409405
<div
410-
onPointerDown={onResizeStart}
411-
onPointerMove={onResizeTreeList}
412-
onPointerUp={onResizeEnd}
413-
className={styles.ResizeBar}
414-
/>
415-
</div>
416-
)}
417-
<div className={styles.TreeView}>
418-
<header className={styles.SuspenseTreeViewHeader}>
419-
{treeListDisabled ? (
420-
<div />
421-
) : (
422-
<ToggleTreeList dispatch={dispatch} state={state} />
423-
)}
424-
<div className={styles.SuspenseBreadcrumbs}>
425-
<SuspenseBreadcrumbs />
406+
className={styles.TreeList}
407+
hidden={treeListHidden}
408+
ref={resizeTreeListRef}>
409+
<SuspenseTreeList />
426410
</div>
427-
<ToggleUniqueSuspenders />
428-
<SelectRoot />
429-
<ToggleInspectedElement
430-
dispatch={dispatch}
431-
state={state}
432-
orientation="horizontal"
433-
/>
434-
</header>
435-
<div className={styles.Rects}>
436-
<SuspenseRects />
437-
</div>
438-
<footer className={styles.SuspenseTreeViewFooter}>
439-
<div className={styles.SuspenseTimeline}>
440-
<SuspenseTimeline />
411+
)}
412+
{treeListDisabled ? null : (
413+
<div className={styles.ResizeBarWrapper} hidden={treeListHidden}>
414+
<div
415+
onPointerDown={onResizeStart}
416+
onPointerMove={onResizeTreeList}
417+
onPointerUp={onResizeEnd}
418+
className={styles.ResizeBar}
419+
/>
420+
</div>
421+
)}
422+
<div className={styles.TreeView}>
423+
<header className={styles.SuspenseTreeViewHeader}>
424+
{treeListDisabled ? (
425+
<div />
426+
) : (
427+
<ToggleTreeList dispatch={dispatch} state={state} />
428+
)}
429+
<div className={styles.SuspenseBreadcrumbs}>
430+
<SuspenseBreadcrumbs />
431+
</div>
432+
<ToggleUniqueSuspenders />
433+
<SelectRoot />
434+
{!hideSettings && <div className={styles.VRule} />}
435+
{!hideSettings && <SettingsModalContextToggle />}
436+
<ToggleInspectedElement
437+
dispatch={dispatch}
438+
state={state}
439+
orientation="horizontal"
440+
/>
441+
</header>
442+
<div className={styles.Rects}>
443+
<SuspenseRects />
441444
</div>
442-
<ToggleInspectedElement
443-
dispatch={dispatch}
444-
state={state}
445-
orientation="vertical"
446-
/>
447-
</footer>
445+
<footer className={styles.SuspenseTreeViewFooter}>
446+
<div className={styles.SuspenseTimeline}>
447+
<SuspenseTimeline />
448+
</div>
449+
<ToggleInspectedElement
450+
dispatch={dispatch}
451+
state={state}
452+
orientation="vertical"
453+
/>
454+
</footer>
455+
</div>
448456
</div>
449-
</div>
450-
<div className={styles.ResizeBarWrapper} hidden={inspectedElementHidden}>
451457
<div
452-
onPointerDown={onResizeStart}
453-
onPointerMove={onResizeTree}
454-
onPointerUp={onResizeEnd}
455-
className={styles.ResizeBar}
456-
/>
457-
</div>
458-
<div
459-
className={styles.InspectedElementWrapper}
460-
hidden={inspectedElementHidden}>
461-
<InspectedElementErrorBoundary>
462-
<InspectedElement />
463-
</InspectedElementErrorBoundary>
458+
className={styles.ResizeBarWrapper}
459+
hidden={inspectedElementHidden}>
460+
<div
461+
onPointerDown={onResizeStart}
462+
onPointerMove={onResizeTree}
463+
onPointerUp={onResizeEnd}
464+
className={styles.ResizeBar}
465+
/>
466+
</div>
467+
<div
468+
className={styles.InspectedElementWrapper}
469+
hidden={inspectedElementHidden}>
470+
<InspectedElementErrorBoundary>
471+
<InspectedElement />
472+
</InspectedElementErrorBoundary>
473+
</div>
474+
<SettingsModal />
464475
</div>
465-
</div>
476+
</SettingsModalContextController>
466477
);
467478
}
468479

0 commit comments

Comments
 (0)