[devtools-shell] layout options for testing (#33516)
## Summary This PR adds a 'Layout' selector to the devtools shell main example, as well as a resizable split pane, allowing more realistic testing of how the devtools behaves when used in a vertical or horizontal layout and at different sizes (e.g. when resizing the Chrome Dev Tools pane). ## How did you test this change? https://github.com/user-attachments/assets/81179413-7b46-47a9-bc52-4f7ec414e8be
James Friend committed
Jun 13, 2025 at 10:25 UTC
ed023cfc7304e4e02a79195c68601eff7606136a
1 file changed
+202
-63
packages/react-devtools-shell/index.html
+202
-63
@@ -1,74 +1,213 @@
1
<!doctype html>
2
<html>
3
- <head>
4
- <meta charset="utf8">
5
- <title>React DevTools</title>
6
- <meta name="viewport" content="width=device-width, initial-scale=1">
7
- <style>
8
- #target {
9
- flex: 1;
10
- border: none;
11
- }
12
- #devtools {
13
- height: 400px;
14
- max-height: 50%;
15
- overflow: hidden;
16
- z-index: 10000001;
17
- }
18
- body {
19
- display: flex;
20
- flex-direction: column;
21
- position: absolute;
22
- top: 0;
23
- left: 0;
24
- right: 0;
25
- bottom: 0;
26
- margin: 0;
27
- padding: 0;
28
- font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,
29
- sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;
30
- font-size: 12px;
31
- line-height: 1.5;
32
- }
33
- .optionsRow {
34
- width: 100%;
35
- display: flex;
36
- padding: 0.25rem;
37
- background: aliceblue;
38
- border-bottom: 1px solid lightblue;
39
- box-sizing: border-box;
40
- }
41
- .optionsRowSpacer {
42
- flex: 1;
43
- }
44
- </style>
45
- </head>
46
- <body>
47
- <div class="optionsRow">
48
- <button id="mountButton">Unmount test app</button>
49
- <div class="optionsRowSpacer"> </div>
50
- <span>
51
- <a href="/multi.html">multi DevTools</a>
52
- |
53
- <a href="/e2e.html">e2e tests</a>
54
- |
55
- <a href="/e2e-regression.html">e2e regression tests</a>
56
- |
57
- <a href="/perf-regression.html">perf regression tests</a>
58
- </span>
59
- </div>
3
4
+<head>
5
+ <meta charset="utf8">
6
+ <title>React DevTools</title>
7
+ <meta name="viewport" content="width=device-width, initial-scale=1">
8
+ <style>
9
+ #panes {
10
+ display: grid;
11
+ height: 100%;
12
+ width: 100%;
13
+ position: relative;
14
+ }
15
+
16
+ #divider {
17
+ position: absolute;
18
+ z-index: 10000002;
19
+ background-color: #ccc;
20
+ transition: background-color 0.2s;
21
+ }
22
+
23
+ #divider:hover,
24
+ #divider.dragging {
25
+ background-color: #aaa;
26
+ }
27
+
28
+ #divider.horizontal-divider {
29
+ width: 100%;
30
+ height: 5px;
31
+ cursor: row-resize;
32
+ }
33
+
34
+ #divider.vertical-divider {
35
+ width: 5px;
36
+ height: 100%;
37
+ cursor: col-resize;
38
+ }
39
+
40
+ #target {
41
+ height: 100%;
42
+ width: 100%;
43
+ border: none;
44
+ }
45
+
46
+ #devtools {
47
+ height: 100%;
48
+ width: 100%;
49
+ overflow: hidden;
50
+ z-index: 10000001;
51
+ }
52
+
53
+ body {
54
+ display: flex;
55
+ height: 100vh;
56
+ width: 100vw;
57
+ contain: strict;
58
+ flex-direction: column;
59
+ margin: 0;
60
+ padding: 0;
61
+ font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,
62
+ sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;
63
+ font-size: 12px;
64
+ line-height: 1.5;
65
+ }
66
+
67
+ .optionsRow {
68
+ width: 100%;
69
+ display: flex;
70
+ padding: 0.25rem;
71
+ background: aliceblue;
72
+ border-bottom: 1px solid lightblue;
73
+ box-sizing: border-box;
74
+ }
75
+
76
+ .optionsRowSpacer {
77
+ flex: 1;
78
+ }
79
+ </style>
80
+</head>
81
+
82
+<body>
83
+ <div class="optionsRow">
84
+ <button id="mountButton">Unmount test app</button>
85
+ <div class="optionsRowSpacer"> </div>
86
+ <span>
87
+ <a href="/multi.html">multi DevTools</a>
88
+ |
89
+ <a href="/e2e.html">e2e tests</a>
90
+ |
91
+ <a href="/e2e-regression.html">e2e regression tests</a>
92
+ |
93
+ <a href="/perf-regression.html">perf regression tests</a>
94
+ </span>
95
+
96
+ <label style="margin-left: 4px">
97
+ Layout:
98
+ <select id="layout">
99
+ <option value="leftright">Left/Right Split</option>
100
+ <option value="topbottom">Top/Bottom Split</option>
101
+ </select></label>
102
+ </div>
103
+
104
+ <div id="panes">
105
<!-- React test app (shells/dev/app) is injected here -->
106
<!-- DevTools backend (shells/dev/src) is injected here -->
107
<!-- global "hook" is defined on the iframe's contentWindow -->
108
<iframe id="target"></iframe>
109
110
+ <!-- Draggable divider between panes -->
111
+ <div id="divider"></div>
112
+
113
<!-- DevTools frontend UI (shells/dev/src) renders here -->
114
<div id="devtools"></div>
115
+ </div>
116
+
117
+ <!-- This script installs the hook, injects the backend, and renders the DevTools UI -->
118
+ <!-- In DEV mode, this file is served by the Webpack dev server -->
119
+ <!-- For production builds, it's built by Webpack and uploaded from the local file system -->
120
+ <script src="dist/app-devtools.js"></script>
121
+ <script type="module">
122
+ let layoutType = 'leftright';
123
+ let splitRatio = 0.5;
124
+ let isDragging = false;
125
+
126
+ // handle layout changes
127
+ const layout = document.getElementById('layout');
128
+ function setLayout(layoutType, splitRatio) {
129
+ const panes = document.getElementById('panes');
130
+ if (layoutType === 'topbottom') {
131
+ panes.style.gridTemplateColumns = '100%'; // Full width for each row
132
+ panes.style.gridTemplateRows = `${splitRatio * 100}% ${(1 - splitRatio) * 100}%`;
133
+ } else if (layoutType === 'leftright') {
134
+ panes.style.gridTemplateRows = '100%'; // Full height for each column
135
+ panes.style.gridTemplateColumns = `${splitRatio * 100}% ${(1 - splitRatio) * 100}%`;
136
+ }
137
+ }
138
+ layout.addEventListener('change', () => {
139
+ layoutType = layout.value;
140
+ setLayout(layoutType, splitRatio);
141
+ updateDividerPosition(); // Ensure divider updates when layout changes
142
+ });
143
+
144
+ // handle changing the split ratio
145
+ const divider = document.getElementById('divider');
146
+ function updateDividerPosition() {
147
+ if (layoutType === 'topbottom') {
148
+ // For top/bottom layout, divider should be horizontal (spanning across)
149
+ divider.className = 'horizontal-divider';
150
+ divider.style.top = `calc(${splitRatio * 100}% - 2.5px)`;
151
+ divider.style.left = '0';
152
+ } else {
153
+ // For left/right layout, divider should be vertical (spanning down)
154
+ divider.className = 'vertical-divider';
155
+ divider.style.left = `calc(${splitRatio * 100}% - 2.5px)`;
156
+ divider.style.top = '0';
157
+ }
158
+ }
159
+
160
+ // Add event listeners for dragging
161
+ divider.addEventListener('mousedown', (e) => {
162
+ isDragging = true;
163
+ divider.classList.add('dragging');
164
+
165
+ // Disable pointer events on the iframe to prevent it from capturing mouse events
166
+ const iframe = document.getElementById('target');
167
+ iframe.style.pointerEvents = 'none';
168
+
169
+ e.preventDefault(); // Prevent text selection during drag
170
+ });
171
+
172
+ document.addEventListener('mousemove', (e) => {
173
+ if (!isDragging) return;
174
+
175
+ const panes = document.getElementById('panes');
176
+ const rect = panes.getBoundingClientRect();
177
+
178
+ if (layoutType === 'topbottom') {
179
+ // Calculate new split ratio based on vertical position
180
+ const newRatio = Math.max(0.1, Math.min(0.9, (e.clientY - rect.top) / rect.height));
181
+ splitRatio = newRatio;
182
+ } else {
183
+ // Calculate new split ratio based on horizontal position
184
+ const newRatio = Math.max(0.1, Math.min(0.9, (e.clientX - rect.left) / rect.width));
185
+ splitRatio = newRatio;
186
+ }
187
+
188
+ // Update layout and divider position
189
+ setLayout(layoutType, splitRatio);
190
+ updateDividerPosition();
191
+ });
192
+
193
+ document.addEventListener('mouseup', () => {
194
+ if (isDragging) {
195
+ isDragging = false;
196
+ divider.classList.remove('dragging');
197
+
198
+ // Re-enable pointer events on the iframe
199
+ const iframe = document.getElementById('target');
200
+ iframe.style.pointerEvents = 'auto';
201
+ }
202
+ });
203
+
204
+ // Initialize
205
+ setLayout(
206
+ layoutType,
207
+ splitRatio,
208
+ );
209
+ updateDividerPosition();
210
+ </script>
211
+</body>
212
69
- <!-- This script installs the hook, injects the backend, and renders the DevTools UI -->
70
- <!-- In DEV mode, this file is served by the Webpack dev server -->
71
- <!-- For production builds, it's built by Webpack and uploaded from the local file system -->
72
- <script src="dist/app-devtools.js"></script>
73
- </body>
74
-</html>
\ No newline at end of file
213
+</html>