@samitouri / QOS-React / commits / ed023cfc73

[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">&nbsp;</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">&nbsp;</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>