Skip to content

Commit 319a786

Browse files
authored
[playground] ViewTransition on config expand (facebook#34595)
<!-- 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 <!-- Explain the **motivation** for making this change. What existing problem does the pull request solve? --> Introduced `<ViewTransition>` to the React Compiler Playground. Added an initial animation on the config panel opening/closing to allow for a smoother visual experience. Previously, the panel would flash in and out of the screen upon open/close. ## How did you test this change? <!-- 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. --> https://github.com/user-attachments/assets/9dc77a6b-d4a5-4a7a-9d81-007ebb55e8d2
1 parent d15d7fd commit 319a786

File tree

10 files changed

+238
-137
lines changed

10 files changed

+238
-137
lines changed

compiler/apps/playground/__tests__/e2e/page.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ function formatPrint(data: Array<string>): Promise<string> {
2323

2424
async function expandConfigs(page: Page): Promise<void> {
2525
const expandButton = page.locator('[title="Expand config editor"]');
26-
expandButton.click();
26+
await expandButton.click();
27+
await page.waitForSelector('.monaco-editor-config', {state: 'visible'});
2728
}
2829

2930
const TEST_SOURCE = `export default function TestComponent({ x }) {

compiler/apps/playground/components/AccordionWindow.tsx

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,21 @@ export default function AccordionWindow(props: {
1818
changedPasses: Set<string>;
1919
}): React.ReactElement {
2020
return (
21-
<div className="flex flex-row h-full">
22-
{Array.from(props.tabs.keys()).map(name => {
23-
return (
24-
<AccordionWindowItem
25-
name={name}
26-
key={name}
27-
tabs={props.tabs}
28-
tabsOpen={props.tabsOpen}
29-
setTabsOpen={props.setTabsOpen}
30-
hasChanged={props.changedPasses.has(name)}
31-
/>
32-
);
33-
})}
21+
<div className="flex-1 min-w-[550px] sm:min-w-0">
22+
<div className="flex flex-row h-full">
23+
{Array.from(props.tabs.keys()).map(name => {
24+
return (
25+
<AccordionWindowItem
26+
name={name}
27+
key={name}
28+
tabs={props.tabs}
29+
tabsOpen={props.tabsOpen}
30+
setTabsOpen={props.setTabsOpen}
31+
hasChanged={props.changedPasses.has(name)}
32+
/>
33+
);
34+
})}
35+
</div>
3436
</div>
3537
);
3638
}

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

Lines changed: 101 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,19 @@ import MonacoEditor, {loader, type Monaco} from '@monaco-editor/react';
99
import {PluginOptions} from 'babel-plugin-react-compiler';
1010
import type {editor} from 'monaco-editor';
1111
import * as monaco from 'monaco-editor';
12-
import React, {useState, useRef} from 'react';
12+
import React, {
13+
useState,
14+
useRef,
15+
unstable_ViewTransition as ViewTransition,
16+
unstable_addTransitionType as addTransitionType,
17+
startTransition,
18+
} from 'react';
1319
import {Resizable} from 're-resizable';
1420
import {useStore, useStoreDispatch} from '../StoreContext';
1521
import {monacoOptions} from './monacoOptions';
1622
import {IconChevron} from '../Icons/IconChevron';
1723
import prettyFormat from 'pretty-format';
24+
import {CONFIG_PANEL_TRANSITION} from '../../lib/transitionTypes';
1825

1926
// @ts-expect-error - webpack asset/source loader handles .d.ts files as strings
2027
import compilerTypeDefs from 'babel-plugin-react-compiler/dist/index.d.ts';
@@ -36,15 +43,27 @@ export default function ConfigEditor({
3643
display: isExpanded ? 'block' : 'none',
3744
}}>
3845
<ExpandedEditor
39-
onToggle={setIsExpanded}
46+
onToggle={() => {
47+
startTransition(() => {
48+
addTransitionType(CONFIG_PANEL_TRANSITION);
49+
setIsExpanded(false);
50+
});
51+
}}
4052
appliedOptions={appliedOptions}
4153
/>
4254
</div>
4355
<div
4456
style={{
4557
display: !isExpanded ? 'block' : 'none',
4658
}}>
47-
<CollapsedEditor onToggle={setIsExpanded} />
59+
<CollapsedEditor
60+
onToggle={() => {
61+
startTransition(() => {
62+
addTransitionType(CONFIG_PANEL_TRANSITION);
63+
setIsExpanded(true);
64+
});
65+
}}
66+
/>
4867
</div>
4968
</>
5069
);
@@ -54,7 +73,7 @@ function ExpandedEditor({
5473
onToggle,
5574
appliedOptions,
5675
}: {
57-
onToggle: (expanded: boolean) => void;
76+
onToggle: () => void;
5877
appliedOptions: PluginOptions | null;
5978
}): React.ReactElement {
6079
const store = useStore();
@@ -111,90 +130,93 @@ function ExpandedEditor({
111130
: 'Invalid configs';
112131

113132
return (
114-
<Resizable
115-
minWidth={300}
116-
maxWidth={600}
117-
defaultSize={{width: 350}}
118-
enable={{right: true, bottom: false}}>
119-
<div className="bg-blue-10 relative h-full flex flex-col !h-[calc(100vh_-_3.5rem)] border border-gray-300">
120-
<div
121-
className="absolute w-8 h-16 bg-blue-10 rounded-r-full flex items-center justify-center z-[2] cursor-pointer border border-l-0 border-gray-300"
122-
title="Minimize config editor"
123-
onClick={() => onToggle(false)}
124-
style={{
125-
top: '50%',
126-
marginTop: '-32px',
127-
right: '-32px',
128-
borderTopLeftRadius: 0,
129-
borderBottomLeftRadius: 0,
130-
}}>
131-
<IconChevron displayDirection="left" className="text-blue-50" />
132-
</div>
133-
134-
<div className="flex-1 flex flex-col m-2 mb-2">
135-
<div className="pb-2">
136-
<h2 className="inline-block text-blue-50 py-1.5 px-1.5 xs:px-3 sm:px-4 text-sm">
137-
Config Overrides
138-
</h2>
139-
</div>
140-
<div className="flex-1 rounded-lg overflow-hidden border border-gray-300">
141-
<MonacoEditor
142-
path={'config.ts'}
143-
language={'typescript'}
144-
value={store.config}
145-
onMount={handleMount}
146-
onChange={handleChange}
147-
loading={''}
148-
className="monaco-editor-config"
149-
options={{
150-
...monacoOptions,
151-
lineNumbers: 'off',
152-
renderLineHighlight: 'none',
153-
overviewRulerBorder: false,
154-
overviewRulerLanes: 0,
155-
fontSize: 12,
156-
scrollBeyondLastLine: false,
157-
glyphMargin: false,
158-
}}
159-
/>
133+
<ViewTransition
134+
update={{[CONFIG_PANEL_TRANSITION]: 'slide-in', default: 'none'}}>
135+
<Resizable
136+
minWidth={300}
137+
maxWidth={600}
138+
defaultSize={{width: 350}}
139+
enable={{right: true, bottom: false}}>
140+
<div className="bg-blue-10 relative h-full flex flex-col !h-[calc(100vh_-_3.5rem)] border border-gray-300">
141+
<div
142+
className="absolute w-8 h-16 bg-blue-10 rounded-r-full flex items-center justify-center z-[2] cursor-pointer border border-l-0 border-gray-300"
143+
title="Minimize config editor"
144+
onClick={onToggle}
145+
style={{
146+
top: '50%',
147+
marginTop: '-32px',
148+
right: '-32px',
149+
borderTopLeftRadius: 0,
150+
borderBottomLeftRadius: 0,
151+
}}>
152+
<IconChevron displayDirection="left" className="text-blue-50" />
160153
</div>
161-
</div>
162-
<div className="flex-1 flex flex-col m-2">
163-
<div className="pb-2">
164-
<h2 className="inline-block text-blue-50 py-1.5 px-1.5 xs:px-3 sm:px-4 text-sm">
165-
Applied Configs
166-
</h2>
154+
155+
<div className="flex-1 flex flex-col m-2 mb-2">
156+
<div className="pb-2">
157+
<h2 className="inline-block text-blue-50 py-1.5 px-1.5 xs:px-3 sm:px-4 text-sm">
158+
Config Overrides
159+
</h2>
160+
</div>
161+
<div className="flex-1 rounded-lg overflow-hidden border border-gray-300">
162+
<MonacoEditor
163+
path={'config.ts'}
164+
language={'typescript'}
165+
value={store.config}
166+
onMount={handleMount}
167+
onChange={handleChange}
168+
loading={''}
169+
className="monaco-editor-config"
170+
options={{
171+
...monacoOptions,
172+
lineNumbers: 'off',
173+
renderLineHighlight: 'none',
174+
overviewRulerBorder: false,
175+
overviewRulerLanes: 0,
176+
fontSize: 12,
177+
scrollBeyondLastLine: false,
178+
glyphMargin: false,
179+
}}
180+
/>
181+
</div>
167182
</div>
168-
<div className="flex-1 rounded-lg overflow-hidden border border-gray-300">
169-
<MonacoEditor
170-
path={'applied-config.js'}
171-
language={'javascript'}
172-
value={formattedAppliedOptions}
173-
loading={''}
174-
className="monaco-editor-applied-config"
175-
options={{
176-
...monacoOptions,
177-
lineNumbers: 'off',
178-
renderLineHighlight: 'none',
179-
overviewRulerBorder: false,
180-
overviewRulerLanes: 0,
181-
fontSize: 12,
182-
scrollBeyondLastLine: false,
183-
readOnly: true,
184-
glyphMargin: false,
185-
}}
186-
/>
183+
<div className="flex-1 flex flex-col m-2">
184+
<div className="pb-2">
185+
<h2 className="inline-block text-blue-50 py-1.5 px-1.5 xs:px-3 sm:px-4 text-sm">
186+
Applied Configs
187+
</h2>
188+
</div>
189+
<div className="flex-1 rounded-lg overflow-hidden border border-gray-300">
190+
<MonacoEditor
191+
path={'applied-config.js'}
192+
language={'javascript'}
193+
value={formattedAppliedOptions}
194+
loading={''}
195+
className="monaco-editor-applied-config"
196+
options={{
197+
...monacoOptions,
198+
lineNumbers: 'off',
199+
renderLineHighlight: 'none',
200+
overviewRulerBorder: false,
201+
overviewRulerLanes: 0,
202+
fontSize: 12,
203+
scrollBeyondLastLine: false,
204+
readOnly: true,
205+
glyphMargin: false,
206+
}}
207+
/>
208+
</div>
187209
</div>
188210
</div>
189-
</div>
190-
</Resizable>
211+
</Resizable>
212+
</ViewTransition>
191213
);
192214
}
193215

194216
function CollapsedEditor({
195217
onToggle,
196218
}: {
197-
onToggle: (expanded: boolean) => void;
219+
onToggle: () => void;
198220
}): React.ReactElement {
199221
return (
200222
<div
@@ -203,7 +225,7 @@ function CollapsedEditor({
203225
<div
204226
className="absolute w-10 h-16 bg-blue-10 hover:translate-x-2 transition-transform rounded-r-full flex items-center justify-center z-[2] cursor-pointer border border-gray-300"
205227
title="Expand config editor"
206-
onClick={() => onToggle(true)}
228+
onClick={onToggle}
207229
style={{
208230
top: '50%',
209231
marginTop: '-32px',

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

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -343,12 +343,8 @@ export default function Editor(): JSX.Element {
343343
<ConfigEditor appliedOptions={appliedOptions} />
344344
</div>
345345
<div className="flex flex-1 min-w-0">
346-
<div className="flex-1 min-w-[550px] sm:min-w-0">
347-
<Input language={language} errors={errors} />
348-
</div>
349-
<div className="flex-1 min-w-[550px] sm:min-w-0">
350-
<Output store={deferredStore} compilerOutput={mergedOutput} />
351-
</div>
346+
<Input language={language} errors={errors} />
347+
<Output store={deferredStore} compilerOutput={mergedOutput} />
352348
</div>
353349
</div>
354350
</>

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

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,17 @@ import {
1313
import invariant from 'invariant';
1414
import type {editor} from 'monaco-editor';
1515
import * as monaco from 'monaco-editor';
16-
import {useEffect, useState} from 'react';
16+
import {
17+
useEffect,
18+
useState,
19+
unstable_ViewTransition as ViewTransition,
20+
} from 'react';
1721
import {renderReactCompilerMarkers} from '../../lib/reactCompilerMonacoDiagnostics';
1822
import {useStore, useStoreDispatch} from '../StoreContext';
1923
import TabbedWindow from '../TabbedWindow';
2024
import {monacoOptions} from './monacoOptions';
25+
import {CONFIG_PANEL_TRANSITION} from '../../lib/transitionTypes';
26+
2127
// @ts-expect-error TODO: Make TS recognize .d.ts files, in addition to loading them with webpack.
2228
import React$Types from '../../node_modules/@types/react/index.d.ts';
2329

@@ -155,16 +161,20 @@ export default function Input({errors, language}: Props): JSX.Element {
155161
const [activeTab, setActiveTab] = useState('Input');
156162

157163
return (
158-
<div className="relative flex flex-col flex-none border-r border-gray-200">
159-
<div className="!h-[calc(100vh_-_3.5rem)]">
160-
<div className="flex flex-col h-full">
164+
<ViewTransition
165+
update={{
166+
[CONFIG_PANEL_TRANSITION]: 'container',
167+
default: 'none',
168+
}}>
169+
<div className="flex-1 min-w-[550px] sm:min-w-0">
170+
<div className="flex flex-col h-full !h-[calc(100vh_-_3.5rem)] border-r border-gray-200">
161171
<TabbedWindow
162172
tabs={tabs}
163173
activeTab={activeTab}
164174
onTabChange={setActiveTab}
165175
/>
166176
</div>
167177
</div>
168-
</div>
178+
</ViewTransition>
169179
);
170180
}

0 commit comments

Comments
 (0)