[playground] Allow accordion tabs to open on error (#34844)
There was a bug where the other output passes (aside from the "Output" tab) were unable to open on compiler error. This PR still allows for the "Output" tab to automatically open on error, but also allows other tabs to be opened. https://github.com/user-attachments/assets/157bf5d6-c289-46fd-bafb-073c2e0ff52b
Eugene Choi committed
Oct 14, 2025 at 15:07 UTC
e7984651e4f123d8112f5abab39782ee70d8f4aa
3 files changed
+21
-14
compiler/apps/playground/components/AccordionWindow.tsx
+1
-4
@@ -22,7 +22,6 @@ export default function AccordionWindow(props: {
22
tabsOpen: Set<string>;
23
setTabsOpen: (newTab: Set<string>) => void;
24
changedPasses: Set<string>;
25
- isFailure: boolean;
25
}): React.ReactElement {
26
return (
27
<div className="flex-1 min-w-[550px] sm:min-w-0">
@@ -36,7 +35,6 @@ export default function AccordionWindow(props: {
35
tabsOpen={props.tabsOpen}
36
setTabsOpen={props.setTabsOpen}
37
hasChanged={props.changedPasses.has(name)}
39
- isFailure={props.isFailure}
38
/>
39
);
40
})}
@@ -51,7 +49,6 @@ function AccordionWindowItem({
49
tabsOpen,
50
setTabsOpen,
51
hasChanged,
54
- isFailure,
52
}: {
53
name: string;
54
tabs: TabsRecord;
@@ -61,7 +58,7 @@ function AccordionWindowItem({
58
isFailure: boolean;
59
}): React.ReactElement {
60
const id = useId();
64
- const isShow = isFailure ? name === 'Output' : tabsOpen.has(name);
61
+ const isShow = tabsOpen.has(name);
62
63
const transitionName = `accordion-window-item-${id}`;
64
compiler/apps/playground/components/Editor/Output.tsx
+18
-4
@@ -27,6 +27,8 @@ import {
27
useState,
28
Suspense,
29
unstable_ViewTransition as ViewTransition,
30
+ unstable_addTransitionType as addTransitionType,
31
+ startTransition,
32
} from 'react';
33
import AccordionWindow from '../AccordionWindow';
34
import TabbedWindow from '../TabbedWindow';
@@ -35,6 +37,7 @@ import {BabelFileResult} from '@babel/core';
37
import {
38
CONFIG_PANEL_TRANSITION,
39
TOGGLE_INTERNALS_TRANSITION,
40
+ EXPAND_ACCORDION_TRANSITION,
41
} from '../../lib/transitionTypes';
42
import {LRUCache} from 'lru-cache';
43
@@ -265,8 +268,22 @@ function OutputContent({store, compilerOutput}: Props): JSX.Element {
268
* Update the active tab back to the output or errors tab when the compilation state
269
* changes between success/failure.
270
*/
268
-
271
+ const [previousOutputKind, setPreviousOutputKind] = useState(
272
+ compilerOutput.kind,
273
+ );
274
const isFailure = compilerOutput.kind !== 'ok';
275
+
276
+ if (compilerOutput.kind !== previousOutputKind) {
277
+ setPreviousOutputKind(compilerOutput.kind);
278
+ if (isFailure) {
279
+ startTransition(() => {
280
+ addTransitionType(EXPAND_ACCORDION_TRANSITION);
281
+ setTabsOpen(prev => new Set(prev).add('Output'));
282
+ setActiveTab('Output');
283
+ });
284
+ }
285
+ }
286
+
287
const changedPasses: Set<string> = new Set(['Output', 'HIR']); // Initial and final passes should always be bold
288
let lastResult: string = '';
289
for (const [passName, results] of compilerOutput.results) {
@@ -295,8 +312,6 @@ function OutputContent({store, compilerOutput}: Props): JSX.Element {
312
tabs={tabs}
313
activeTab={activeTab}
314
onTabChange={setActiveTab}
298
- // Display the Output tab on compilation failure
299
- activeTabOverride={isFailure ? 'Output' : undefined}
315
/>
316
</ViewTransition>
317
);
@@ -315,7 +330,6 @@ function OutputContent({store, compilerOutput}: Props): JSX.Element {
330
tabsOpen={tabsOpen}
331
tabs={tabs}
332
changedPasses={changedPasses}
318
- isFailure={isFailure}
333
/>
334
</ViewTransition>
335
);
compiler/apps/playground/components/TabbedWindow.tsx
+2
-6
@@ -17,15 +17,11 @@ export default function TabbedWindow({
17
tabs,
18
activeTab,
19
onTabChange,
20
- activeTabOverride,
20
}: {
21
tabs: Map<string, React.ReactNode>;
22
activeTab: string;
23
onTabChange: (tab: string) => void;
25
- activeTabOverride?: string;
24
}): React.ReactElement {
27
- const currentActiveTab = activeTabOverride ? activeTabOverride : activeTab;
28
-
25
const id = useId();
26
const transitionName = `tab-highlight-${id}`;
27
@@ -41,7 +37,7 @@ export default function TabbedWindow({
37
<div className="flex flex-col h-full max-w-full">
38
<div className="flex p-2 flex-shrink-0">
39
{Array.from(tabs.keys()).map(tab => {
44
- const isActive = currentActiveTab === tab;
40
+ const isActive = activeTab === tab;
41
return (
42
<button
43
key={tab}
@@ -77,7 +73,7 @@ export default function TabbedWindow({
73
})}
74
</div>
75
<div className="flex-1 overflow-hidden w-full h-full">
80
- {tabs.get(currentActiveTab)}
76
+ {tabs.get(activeTab)}
77
</div>
78
</div>
79
</div>