Repository: facebook/react
File: compiler/apps/playground/components/Editor/ConfigEditor.tsx (TypeScript)

Code before:
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import MonacoEditor, {loader, type Monaco} from '@monaco-editor/react';
import type {editor} from 'monaco-editor';
import * as monaco from 'monaco-editor';
import {useState} from 'react';
import {Resizable} from 're-resizable';
import {useStore, useStoreDispatch} from '../StoreContext';
import {monacoOptions} from './monacoOptions';
import {
  generateOverridePragmaFromConfig,
  updateSourceWithOverridePragma,
} from '../../lib/configUtils';

loader.config({monaco});

export default function ConfigEditor(): JSX.Element {
  const [, setMonaco] = useState<Monaco | null>(null);
  const store = useStore();
  const dispatchStore = useStoreDispatch();

  const handleChange: (value: string | undefined) => void = async value => {
    if (value === undefined) return;

    try {
      const newPragma = await generateOverridePragmaFromConfig(value);
      const updatedSource = updateSourceWithOverridePragma(
        store.source,
        newPragma,
      );

      // Update the store with both the new config and updated source
      dispatchStore({
        type: 'updateFile',
        payload: {
          source: updatedSource,
          config: value,
        },
      });
    } catch (_) {
      dispatchStore({
        type: 'updateFile',
        payload: {
          source: store.source,
          config: value,
        },
      });
    }

Review comment (refactor):
Chatted offline about abstracting a CollapsiblePane type component that can be used across all the panes as a follow up so we don't have to recreate this layout logic each time.

Code after:
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import MonacoEditor, {loader, type Monaco} from '@monaco-editor/react';
import type {editor} from 'monaco-editor';
import * as monaco from 'monaco-editor';
import React, {useState, useCallback} from 'react';
import {Resizable} from 're-resizable';
import {useSnackbar} from 'notistack';
import {useStore, useStoreDispatch} from '../StoreContext';
import {monacoOptions} from './monacoOptions';
import {
  ConfigError,
  generateOverridePragmaFromConfig,
  updateSourceWithOverridePragma,
} from '../../lib/configUtils';

// @ts-expect-error - webpack asset/source loader handles .d.ts files as strings
import compilerTypeDefs from 'babel-plugin-react-compiler/dist/index.d.ts';

loader.config({monaco});

export default function ConfigEditor(): React.ReactElement {
  const [isExpanded, setIsExpanded] = useState(false);
  const store = useStore();
  const dispatchStore = useStoreDispatch();
  const {enqueueSnackbar} = useSnackbar();

  const toggleExpanded = useCallback(() => {
    setIsExpanded(prev => !prev);
  }, []);

  const handleApplyConfig: () => Promise<void> = async () => {
    try {
      const config = store.config || '';

      if (!config.trim()) {
        enqueueSnackbar(
          'Config is empty. Please add configuration options first.',
          {
            variant: 'warning',
          },
        );
        return;
      }
      const newPragma = await generateOverridePragmaFromConfig(config);
      const updatedSource = updateSourceWithOverridePragma(
        store.source,
        newPragma,