Skip to content

Commit 1a27af3

Browse files
authored
[playground] Update the playground UI (#34468)
<!-- Thanks for submitting a pull request! We appreciate you spending the time to work on these changes. Please provide enough information so that others can review your pull request. The three fields below are mandatory. Before submitting a pull request, please make sure the following is done: 1. Fork [the repository](https://github.com/facebook/react) and create your branch from `main`. 2. Run `yarn` in the repository root. 3. If you've fixed a bug or added code that should be tested, add tests! 4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch TestName` is helpful in development. 5. Run `yarn test --prod` to test in the production environment. It supports the same options as `yarn test`. 6. If you need a debugger, run `yarn test --debug --watch TestName`, open `chrome://inspect`, and press "Inspect". 7. Format your code with [prettier](https://github.com/prettier/prettier) (`yarn prettier`). 8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only check changed files. 9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`). 10. If you haven't already, complete the CLA. Learn more about contributing: https://reactjs.org/docs/how-to-contribute.html --> ## Summary Updated the UI of the React compiler playground. The config, Input, and Output panels will now span the viewport width when "Show Internals" is not toggled on. When "Show Internals" is toggled on, the old vertical accordion tabs are still used. Going to add support for the "Applied Configs" tabs underneath the "Config Overrides" tab next. <!-- Explain the **motivation** for making this change. What existing problem does the pull request solve? --> ## How did you test this change? https://github.com/user-attachments/assets/b8eab028-f58c-4cb9-a8b2-0f098f2cc262 <!-- Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes the user interface. How exactly did you verify that your PR solves the issue you wanted to solve? If you leave this empty, your PR will very likely be closed. -->
1 parent 0e10ee9 commit 1a27af3

File tree

10 files changed

+495
-292
lines changed

10 files changed

+495
-292
lines changed

compiler/apps/playground/__tests__/e2e/__snapshots__/page.spec.ts/compilationMode-all-output.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import { c as _c } from "react/compiler-runtime"; // 
2-
@compilationMode:"all"
1+
import { c as _c } from "react/compiler-runtime"; // @compilationMode:"all"
32
function nonReactFn() {
43
  const $ = _c(1);
54
  let t0;
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
import {Resizable} from 're-resizable';
9+
import React, {useCallback} from 'react';
10+
11+
type TabsRecord = Map<string, React.ReactNode>;
12+
13+
export default function AccordionWindow(props: {
14+
defaultTab: string | null;
15+
tabs: TabsRecord;
16+
tabsOpen: Set<string>;
17+
setTabsOpen: (newTab: Set<string>) => void;
18+
changedPasses: Set<string>;
19+
}): React.ReactElement {
20+
if (props.tabs.size === 0) {
21+
return (
22+
<div
23+
className="flex items-center justify-center"
24+
style={{width: 'calc(100vw - 650px)'}}>
25+
No compiler output detected, see errors below
26+
</div>
27+
);
28+
}
29+
return (
30+
<div className="flex flex-row h-full">
31+
{Array.from(props.tabs.keys()).map(name => {
32+
return (
33+
<AccordionWindowItem
34+
name={name}
35+
key={name}
36+
tabs={props.tabs}
37+
tabsOpen={props.tabsOpen}
38+
setTabsOpen={props.setTabsOpen}
39+
hasChanged={props.changedPasses.has(name)}
40+
/>
41+
);
42+
})}
43+
</div>
44+
);
45+
}
46+
47+
function AccordionWindowItem({
48+
name,
49+
tabs,
50+
tabsOpen,
51+
setTabsOpen,
52+
hasChanged,
53+
}: {
54+
name: string;
55+
tabs: TabsRecord;
56+
tabsOpen: Set<string>;
57+
setTabsOpen: (newTab: Set<string>) => void;
58+
hasChanged: boolean;
59+
}): React.ReactElement {
60+
const isShow = tabsOpen.has(name);
61+
62+
const toggleTabs = useCallback(() => {
63+
const nextState = new Set(tabsOpen);
64+
if (nextState.has(name)) {
65+
nextState.delete(name);
66+
} else {
67+
nextState.add(name);
68+
}
69+
setTabsOpen(nextState);
70+
}, [tabsOpen, name, setTabsOpen]);
71+
72+
// Replace spaces with non-breaking spaces
73+
const displayName = name.replace(/ /g, '\u00A0');
74+
75+
return (
76+
<div key={name} className="flex flex-row">
77+
{isShow ? (
78+
<Resizable className="border-r" minWidth={550} enable={{right: true}}>
79+
<h2
80+
title="Minimize tab"
81+
aria-label="Minimize tab"
82+
onClick={toggleTabs}
83+
className={`p-4 duration-150 ease-in border-b cursor-pointer border-grey-200 ${
84+
hasChanged ? 'font-bold' : 'font-light'
85+
} text-secondary hover:text-link`}>
86+
- {displayName}
87+
</h2>
88+
{tabs.get(name) ?? <div>No output for {name}</div>}
89+
</Resizable>
90+
) : (
91+
<div className="relative items-center h-full px-1 py-6 align-middle border-r border-grey-200">
92+
<button
93+
title={`Expand compiler tab: ${name}`}
94+
aria-label={`Expand compiler tab: ${name}`}
95+
style={{transform: 'rotate(90deg) translate(-50%)'}}
96+
onClick={toggleTabs}
97+
className={`flex-grow-0 w-5 transition-colors duration-150 ease-in ${
98+
hasChanged ? 'font-bold' : 'font-light'
99+
} text-secondary hover:text-link`}>
100+
{displayName}
101+
</button>
102+
</div>
103+
)}
104+
</div>
105+
);
106+
}

compiler/apps/playground/components/Editor/ConfigEditor.tsx

Lines changed: 92 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@
88
import MonacoEditor, {loader, type Monaco} from '@monaco-editor/react';
99
import type {editor} from 'monaco-editor';
1010
import * as monaco from 'monaco-editor';
11-
import React, {useState, useCallback} from 'react';
11+
import React, {useState} from 'react';
1212
import {Resizable} from 're-resizable';
1313
import {useStore, useStoreDispatch} from '../StoreContext';
1414
import {monacoOptions} from './monacoOptions';
15+
import {IconChevron} from '../Icons/IconChevron';
1516

1617
// @ts-expect-error - webpack asset/source loader handles .d.ts files as strings
1718
import compilerTypeDefs from 'babel-plugin-react-compiler/dist/index.d.ts';
@@ -20,13 +21,26 @@ loader.config({monaco});
2021

2122
export default function ConfigEditor(): React.ReactElement {
2223
const [isExpanded, setIsExpanded] = useState(false);
24+
25+
return (
26+
<div className="flex flex-row relative">
27+
{isExpanded ? (
28+
<ExpandedEditor onToggle={setIsExpanded} />
29+
) : (
30+
<CollapsedEditor onToggle={setIsExpanded} />
31+
)}
32+
</div>
33+
);
34+
}
35+
36+
function ExpandedEditor({
37+
onToggle,
38+
}: {
39+
onToggle: (expanded: boolean) => void;
40+
}): React.ReactElement {
2341
const store = useStore();
2442
const dispatchStore = useStoreDispatch();
2543

26-
const toggleExpanded = useCallback(() => {
27-
setIsExpanded(prev => !prev);
28-
}, []);
29-
3044
const handleChange: (value: string | undefined) => void = value => {
3145
if (value === undefined) return;
3246

@@ -68,57 +82,82 @@ export default function ConfigEditor(): React.ReactElement {
6882
};
6983

7084
return (
71-
<div className="flex flex-row relative">
72-
{isExpanded ? (
73-
<Resizable
74-
className="border-r"
75-
minWidth={300}
76-
maxWidth={600}
77-
defaultSize={{width: 350, height: 'auto'}}
78-
enable={{right: true}}>
79-
<h2
80-
title="Minimize config editor"
81-
aria-label="Minimize config editor"
82-
onClick={toggleExpanded}
83-
className="p-4 duration-150 ease-in border-b cursor-pointer border-grey-200 font-light text-secondary hover:text-link">
84-
- Config Overrides
85+
<Resizable
86+
className="border-r"
87+
minWidth={300}
88+
maxWidth={600}
89+
defaultSize={{width: 350, height: 'auto'}}
90+
enable={{right: true, bottom: false}}
91+
style={{position: 'relative', height: 'calc(100vh - 3.5rem)'}}>
92+
<div className="bg-gray-700 p-2">
93+
<div className="pb-2">
94+
<h2 className="inline-block text-secondary-dark text-center outline-none py-1.5 px-1.5 xs:px-3 sm:px-4 rounded-full capitalize whitespace-nowrap text-sm">
95+
Config Overrides
8596
</h2>
86-
<div className="h-[calc(100vh_-_3.5rem_-_4rem)]">
87-
<MonacoEditor
88-
path={'config.ts'}
89-
language={'typescript'}
90-
value={store.config}
91-
onMount={handleMount}
92-
onChange={handleChange}
93-
options={{
94-
...monacoOptions,
95-
lineNumbers: 'off',
96-
folding: false,
97-
renderLineHighlight: 'none',
98-
scrollBeyondLastLine: false,
99-
hideCursorInOverviewRuler: true,
100-
overviewRulerBorder: false,
101-
overviewRulerLanes: 0,
102-
fontSize: 12,
103-
}}
104-
/>
105-
</div>
106-
</Resizable>
107-
) : (
108-
<div className="relative items-center h-full px-1 py-6 align-middle border-r border-grey-200">
109-
<button
110-
title="Expand config editor"
111-
aria-label="Expand config editor"
112-
style={{
113-
transform: 'rotate(90deg) translate(-50%)',
114-
whiteSpace: 'nowrap',
97+
</div>
98+
<div
99+
className="absolute w-10 h-16 bg-gray-700 hover:translate-x-2 transition-transform rounded-r-full flex items-center justify-center z-[5] cursor-pointer"
100+
title="Minimize config editor"
101+
onClick={() => onToggle(false)}
102+
style={{
103+
top: '50%',
104+
marginTop: '-32px',
105+
right: '-32px',
106+
borderTopLeftRadius: 0,
107+
borderBottomLeftRadius: 0,
108+
}}>
109+
<IconChevron
110+
displayDirection="left"
111+
className="text-secondary-dark"
112+
/>
113+
</div>
114+
<div className="h-[calc(100vh_-_3.5rem_-_3.5rem)] rounded-lg overflow-hidden">
115+
<MonacoEditor
116+
path={'config.ts'}
117+
language={'typescript'}
118+
value={store.config}
119+
onMount={handleMount}
120+
onChange={handleChange}
121+
options={{
122+
...monacoOptions,
123+
lineNumbers: 'off',
124+
folding: false,
125+
renderLineHighlight: 'none',
126+
hideCursorInOverviewRuler: true,
127+
overviewRulerBorder: false,
128+
overviewRulerLanes: 0,
129+
fontSize: 12,
130+
scrollBeyondLastLine: false,
115131
}}
116-
onClick={toggleExpanded}
117-
className="flex-grow-0 w-5 transition-colors duration-150 ease-in font-light text-secondary hover:text-link">
118-
Config Overrides
119-
</button>
132+
/>
120133
</div>
121-
)}
134+
</div>
135+
</Resizable>
136+
);
137+
}
138+
139+
function CollapsedEditor({
140+
onToggle,
141+
}: {
142+
onToggle: (expanded: boolean) => void;
143+
}): React.ReactElement {
144+
return (
145+
<div
146+
className="w-4"
147+
style={{height: 'calc(100vh - 3.5rem)', position: 'relative'}}>
148+
<div
149+
className="absolute w-10 h-16 bg-gray-700 hover:translate-x-2 transition-transform rounded-r-full flex items-center justify-center z-[5] cursor-pointer"
150+
title="Expand config editor"
151+
onClick={() => onToggle(true)}
152+
style={{
153+
top: '50%',
154+
marginTop: '-32px',
155+
left: '-8px',
156+
borderTopLeftRadius: 0,
157+
borderBottomLeftRadius: 0,
158+
}}>
159+
<IconChevron displayDirection="right" className="text-secondary-dark" />
160+
</div>
122161
</div>
123162
);
124163
}

0 commit comments

Comments
 (0)