Skip to content

Commit 6eda534

Browse files
authored
[playground] bug fixes & UX improvements (#34499)
<!-- 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 Made many small changes to the compiler playground to improve user experience. Removed any "Loading" indicators that would flash in before a component would finish loading in. Additionally, before users would see the "Show Internals" button toggling from false to true if they had set it at true previously. I was able to refactor the URL/local storage loading so that the `Store` would be fully initialized before the components would load in. Attempted to integrate `<Activity>` into showing/hiding these different editors, but the current state of [monaco editors](https://github.com/suren-atoyan/monaco-react) does not allow for this. I created an issue for them to address: suren-atoyan/monaco-react#753 Added a debounce to the config editor so every key type wouldn't cause the output panel to respond instantly. Users can type for 500 ms before an error is thrown at them. <!-- Explain the **motivation** for making this change. What existing problem does the pull request solve? --> ## How did you test this change? Here is what loading the page would look like before (not sure why its so blurry): https://github.com/user-attachments/assets/58f4281a-cc02-4141-b9b5-f70d6ace12a2 Here is how it looks now: https://github.com/user-attachments/assets/40535165-fc7c-44fb-9282-9c7fa76e7d53 Here is the debouncing: https://github.com/user-attachments/assets/e4ab29e4-1afd-4249-beca-671fb6542f5e <!-- 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 c03a51d commit 6eda534

File tree

15 files changed

+149
-230
lines changed

15 files changed

+149
-230
lines changed

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ test('editor should compile from hash successfully', async ({page}) => {
136136
path: 'test-results/01-compiles-from-hash.png',
137137
});
138138
const text =
139-
(await page.locator('.monaco-editor').nth(1).allInnerTexts()) ?? [];
139+
(await page.locator('.monaco-editor').nth(3).allInnerTexts()) ?? [];
140140
const output = await formatPrint(text);
141141

142142
expect(output).not.toEqual('');
@@ -162,7 +162,7 @@ test('reset button works', async ({page}) => {
162162
path: 'test-results/02-reset-button-works.png',
163163
});
164164
const text =
165-
(await page.locator('.monaco-editor').nth(1).allInnerTexts()) ?? [];
165+
(await page.locator('.monaco-editor').nth(3).allInnerTexts()) ?? [];
166166
const output = await formatPrint(text);
167167

168168
expect(output).not.toEqual('');
@@ -183,7 +183,7 @@ TEST_CASE_INPUTS.forEach((t, idx) =>
183183
});
184184

185185
const text =
186-
(await page.locator('.monaco-editor').nth(1).allInnerTexts()) ?? [];
186+
(await page.locator('.monaco-editor').nth(3).allInnerTexts()) ?? [];
187187
let output: string;
188188
if (t.noFormat) {
189189
output = text.join('');

compiler/apps/playground/app/index.tsx

Lines changed: 0 additions & 56 deletions
This file was deleted.

compiler/apps/playground/components/AccordionWindow.tsx

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,6 @@ export default function AccordionWindow(props: {
1717
setTabsOpen: (newTab: Set<string>) => void;
1818
changedPasses: Set<string>;
1919
}): 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-
}
2920
return (
3021
<div className="flex flex-row h-full">
3122
{Array.from(props.tabs.keys()).map(name => {

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

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ 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} from 'react';
12+
import React, {useState, useRef, useEffect} from 'react';
1313
import {Resizable} from 're-resizable';
1414
import {useStore, useStoreDispatch} from '../StoreContext';
1515
import {monacoOptions} from './monacoOptions';
@@ -28,10 +28,25 @@ export default function ConfigEditor({
2828
}): React.ReactElement {
2929
const [isExpanded, setIsExpanded] = useState(false);
3030

31-
return isExpanded ? (
32-
<ExpandedEditor onToggle={setIsExpanded} appliedOptions={appliedOptions} />
33-
) : (
34-
<CollapsedEditor onToggle={setIsExpanded} />
31+
return (
32+
// TODO: Use <Activity> when it is compatible with Monaco: https://github.com/suren-atoyan/monaco-react/issues/753
33+
<>
34+
<div
35+
style={{
36+
display: isExpanded ? 'block' : 'none',
37+
}}>
38+
<ExpandedEditor
39+
onToggle={setIsExpanded}
40+
appliedOptions={appliedOptions}
41+
/>
42+
</div>
43+
<div
44+
style={{
45+
display: !isExpanded ? 'block' : 'none',
46+
}}>
47+
<CollapsedEditor onToggle={setIsExpanded} />
48+
</div>
49+
</>
3550
);
3651
}
3752

@@ -44,16 +59,25 @@ function ExpandedEditor({
4459
}): React.ReactElement {
4560
const store = useStore();
4661
const dispatchStore = useStoreDispatch();
62+
const debounceTimerRef = useRef<NodeJS.Timeout | null>(null);
4763

48-
const handleChange: (value: string | undefined) => void = value => {
64+
const handleChange: (value: string | undefined) => void = (
65+
value: string | undefined,
66+
) => {
4967
if (value === undefined) return;
5068

51-
dispatchStore({
52-
type: 'updateConfig',
53-
payload: {
54-
config: value,
55-
},
56-
});
69+
if (debounceTimerRef.current) {
70+
clearTimeout(debounceTimerRef.current);
71+
}
72+
73+
debounceTimerRef.current = setTimeout(() => {
74+
dispatchStore({
75+
type: 'updateConfig',
76+
payload: {
77+
config: value,
78+
},
79+
});
80+
}, 500); // 500ms debounce delay
5781
};
5882

5983
const handleMount: (
@@ -77,12 +101,6 @@ function ExpandedEditor({
77101
allowSyntheticDefaultImports: true,
78102
jsx: monaco.languages.typescript.JsxEmit.React,
79103
});
80-
81-
const uri = monaco.Uri.parse(`file:///config.ts`);
82-
const model = monaco.editor.getModel(uri);
83-
if (model) {
84-
model.updateOptions({tabSize: 2});
85-
}
86104
};
87105

88106
const formattedAppliedOptions = appliedOptions
@@ -126,6 +144,7 @@ function ExpandedEditor({
126144
value={store.config}
127145
onMount={handleMount}
128146
onChange={handleChange}
147+
loading={''}
129148
options={{
130149
...monacoOptions,
131150
lineNumbers: 'off',
@@ -139,7 +158,6 @@ function ExpandedEditor({
139158
/>
140159
</div>
141160
</div>
142-
143161
<div className="flex-1 flex flex-col m-2">
144162
<div className="pb-2">
145163
<h2 className="inline-block text-blue-50 py-1.5 px-1.5 xs:px-3 sm:px-4 text-sm">
@@ -151,6 +169,7 @@ function ExpandedEditor({
151169
path={'applied-config.js'}
152170
language={'javascript'}
153171
value={formattedAppliedOptions}
172+
loading={''}
154173
options={{
155174
...monacoOptions,
156175
lineNumbers: 'off',

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

Lines changed: 1 addition & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,8 @@ import BabelPluginReactCompiler, {
2424
printFunctionWithOutlined,
2525
type LoggerEvent,
2626
} from 'babel-plugin-react-compiler';
27-
import invariant from 'invariant';
28-
import {useSnackbar} from 'notistack';
2927
import {useDeferredValue, useMemo} from 'react';
30-
import {useMountEffect} from '../../hooks';
31-
import {defaultStore} from '../../lib/defaultStore';
32-
import {
33-
createMessage,
34-
initStoreFromUrlOrLocalStorage,
35-
MessageLevel,
36-
MessageSource,
37-
type Store,
38-
} from '../../lib/stores';
39-
import {useStore, useStoreDispatch} from '../StoreContext';
28+
import {useStore} from '../StoreContext';
4029
import ConfigEditor from './ConfigEditor';
4130
import Input from './Input';
4231
import {
@@ -174,7 +163,6 @@ function parseOptions(
174163
// Parse config overrides from config editor
175164
let configOverrideOptions: any = {};
176165
const configMatch = configOverrides.match(/^\s*import.*?\n\n\((.*)\)/s);
177-
// TODO: initialize store with URL params, not empty store
178166
if (configOverrides.trim()) {
179167
if (configMatch && configMatch[1]) {
180168
const configString = configMatch[1].replace(/satisfies.*$/, '').trim();
@@ -327,8 +315,6 @@ function compile(
327315
export default function Editor(): JSX.Element {
328316
const store = useStore();
329317
const deferredStore = useDeferredValue(store);
330-
const dispatchStore = useStoreDispatch();
331-
const {enqueueSnackbar} = useSnackbar();
332318
const [compilerOutput, language, appliedOptions] = useMemo(
333319
() => compile(deferredStore.source, 'compiler', deferredStore.config),
334320
[deferredStore.source, deferredStore.config],
@@ -338,32 +324,6 @@ export default function Editor(): JSX.Element {
338324
[deferredStore.source, deferredStore.config],
339325
);
340326

341-
useMountEffect(() => {
342-
// Initialize store
343-
let mountStore: Store;
344-
try {
345-
mountStore = initStoreFromUrlOrLocalStorage();
346-
} catch (e) {
347-
invariant(e instanceof Error, 'Only Error may be caught.');
348-
enqueueSnackbar(e.message, {
349-
variant: 'warning',
350-
...createMessage(
351-
'Bad URL - fell back to the default Playground.',
352-
MessageLevel.Info,
353-
MessageSource.Playground,
354-
),
355-
});
356-
mountStore = defaultStore;
357-
}
358-
359-
dispatchStore({
360-
type: 'setStore',
361-
payload: {
362-
store: mountStore,
363-
},
364-
});
365-
});
366-
367327
let mergedOutput: CompilerOutput;
368328
let errors: Array<CompilerErrorDetail | CompilerDiagnostic>;
369329
if (compilerOutput.kind === 'ok') {

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

Lines changed: 10 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import {
1313
import invariant from 'invariant';
1414
import type {editor} from 'monaco-editor';
1515
import * as monaco from 'monaco-editor';
16-
import {Resizable} from 're-resizable';
1716
import {useEffect, useState} from 'react';
1817
import {renderReactCompilerMarkers} from '../../lib/reactCompilerMonacoDiagnostics';
1918
import {useStore, useStoreDispatch} from '../StoreContext';
@@ -46,11 +45,6 @@ export default function Input({errors, language}: Props): JSX.Element {
4645
details: errors,
4746
source: store.source,
4847
});
49-
/**
50-
* N.B. that `tabSize` is a model property, not an editor property.
51-
* So, the tab size has to be set per model.
52-
*/
53-
model.updateOptions({tabSize: 2});
5448
}, [monaco, errors, store.source]);
5549

5650
useEffect(() => {
@@ -152,38 +146,24 @@ export default function Input({errors, language}: Props): JSX.Element {
152146
onMount={handleMount}
153147
onChange={handleChange}
154148
options={monacoOptions}
149+
loading={''}
155150
/>
156151
);
157152

158153
const tabs = new Map([['Input', editorContent]]);
159154
const [activeTab, setActiveTab] = useState('Input');
160155

161-
const tabbedContent = (
162-
<div className="flex flex-col h-full">
163-
<TabbedWindow
164-
tabs={tabs}
165-
activeTab={activeTab}
166-
onTabChange={setActiveTab}
167-
/>
168-
</div>
169-
);
170-
171156
return (
172157
<div className="relative flex flex-col flex-none border-r border-gray-200">
173-
{store.showInternals ? (
174-
<Resizable
175-
minWidth={550}
176-
enable={{right: true}}
177-
/**
178-
* Restrict MonacoEditor's height, since the config autoLayout:true
179-
* will grow the editor to fit within parent element
180-
*/
181-
className="!h-[calc(100vh_-_3.5rem)]">
182-
{tabbedContent}
183-
</Resizable>
184-
) : (
185-
<div className="!h-[calc(100vh_-_3.5rem)]">{tabbedContent}</div>
186-
)}
158+
<div className="!h-[calc(100vh_-_3.5rem)]">
159+
<div className="flex flex-col h-full">
160+
<TabbedWindow
161+
tabs={tabs}
162+
activeTab={activeTab}
163+
onTabChange={setActiveTab}
164+
/>
165+
</div>
166+
</div>
187167
</div>
188168
);
189169
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@ function TextTabContent({
324324
<DiffEditor
325325
original={diff}
326326
modified={output}
327+
loading={''}
327328
options={{
328329
...monacoOptions,
329330
readOnly: true,
@@ -338,6 +339,7 @@ function TextTabContent({
338339
<MonacoEditor
339340
language={language ?? 'javascript'}
340341
value={output}
342+
loading={''}
341343
options={{
342344
...monacoOptions,
343345
readOnly: true,

compiler/apps/playground/components/Editor/monacoOptions.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,6 @@ export const monacoOptions: Partial<EditorProps['options']> = {
2929
automaticLayout: true,
3030
wordWrap: 'on',
3131
wrappingIndent: 'same',
32+
33+
tabSize: 2,
3234
};

0 commit comments

Comments
 (0)