Skip to content

Commit

Permalink
Add interface to debug properties for code components (#803)
Browse files Browse the repository at this point in the history
* Add code component editor properties panel

* fault tolerant

* Remove redundant ErrorBoundary
  • Loading branch information
Janpot authored Aug 17, 2022
1 parent ff14689 commit 31336a3
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,16 @@ import createCache from '@emotion/cache';
import { CacheProvider } from '@emotion/react';
import * as ReactDOM from 'react-dom';
import { ErrorBoundary } from 'react-error-boundary';
import { NodeId, createComponent, ToolpadComponent, TOOLPAD_COMPONENT } from '@mui/toolpad-core';
import {
NodeId,
createComponent,
ToolpadComponent,
TOOLPAD_COMPONENT,
ArgTypeDefinitions,
ArgTypeDefinition,
} from '@mui/toolpad-core';
import { useQuery } from '@tanstack/react-query';
import invariant from 'invariant';
import * as appDom from '../../../appDom';
import { useDom, useDomApi } from '../../DomLoader';
import { tryFormat } from '../../../utils/prettier';
Expand All @@ -17,17 +25,57 @@ import usePageTitle from '../../../utils/usePageTitle';
import useLatest from '../../../utils/useLatest';
import AppThemeProvider from '../../../runtime/AppThemeProvider';
import useCodeComponent from './useCodeComponent';
import { mapValues } from '../../../utils/collections';
import { filterValues, mapValues } from '../../../utils/collections';
import ErrorAlert from '../PageEditor/ErrorAlert';
import lazyComponent from '../../../utils/lazyComponent';
import CenteredSpinner from '../../../components/CenteredSpinner';
import SplitPane from '../../../components/SplitPane';
import { getDefaultControl } from '../../propertyControls';
import { WithControlledProp } from '../../../utils/types';

const TypescriptEditor = lazyComponent(() => import('../../../components/TypescriptEditor'), {
noSsr: true,
fallback: <CenteredSpinner />,
});

interface PropertyEditorProps extends WithControlledProp<any> {
name: string;
argType: ArgTypeDefinition;
}

function PropertyEditor({ argType, name, value, onChange }: PropertyEditorProps) {
const Control = getDefaultControl(argType);
if (!Control) {
return null;
}
return <Control label={name} propType={argType.typeDef} value={value} onChange={onChange} />;
}

interface PropertiesEditorProps extends WithControlledProp<Record<string, any>> {
argTypes: ArgTypeDefinitions;
}

function PropertiesEditor({ argTypes, value, onChange }: PropertiesEditorProps) {
return (
<Stack sx={{ p: 2, gap: 2, overflow: 'auto', height: '100%' }}>
<Typography>Properties:</Typography>
{Object.entries(argTypes).map(([name, argType]) => {
invariant(argType, 'argType not defined');
return (
<ErrorBoundary fallback={<div>{name}</div>} resetKeys={[argType]}>
<PropertyEditor
name={name}
argType={argType}
value={value[name]}
onChange={(newPropValue) => onChange({ ...value, [name]: newPropValue })}
/>
</ErrorBoundary>
);
})}
</Stack>
);
}

const Noop = createComponent(() => null);

const CanvasFrame = styled('iframe')({
Expand Down Expand Up @@ -148,6 +196,8 @@ function CodeComponentEditorContent({ codeComponentNode }: CodeComponentEditorCo
[argTypes],
);

const [props, setProps] = React.useState({});

return (
<React.Fragment>
<Stack sx={{ height: '100%' }}>
Expand All @@ -162,7 +212,10 @@ function CodeComponentEditorContent({ codeComponentNode }: CodeComponentEditorCo
extraLibs={extraLibs}
/>

<CanvasFrame ref={frameRef} title="Code component sandbox" onLoad={onLoad} />
<SplitPane split="horizontal" allowResize size="20%" primary="second">
<CanvasFrame ref={frameRef} title="Code component sandbox" onLoad={onLoad} />
<PropertiesEditor argTypes={argTypes} value={props} onChange={setProps} />
</SplitPane>
</SplitPane>
</Box>
<Toolbar
Expand All @@ -184,7 +237,10 @@ function CodeComponentEditorContent({ codeComponentNode }: CodeComponentEditorCo
fallbackRender={({ error: runtimeError }) => <ErrorAlert error={runtimeError} />}
>
<AppThemeProvider dom={dom}>
<CodeComponent {...defaultProps} />
<CodeComponent
{...defaultProps}
{...filterValues(props, (propValue) => typeof propValue !== 'undefined')}
/>
</AppThemeProvider>
</ErrorBoundary>
{compileError ? <ErrorAlert error={compileError} /> : null}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ function SelectPropEditor({ label, propType, value, onChange, disabled }: Editor
const items = propType.type === 'string' ? propType.enum ?? [] : [];
const handleChange = React.useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
onChange(event.target.value);
onChange(event.target.value || undefined);
},
[onChange],
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { EditorProps } from '../../types';
function StringPropEditor({ label, value, onChange, disabled }: EditorProps<string>) {
const handleChange = React.useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
onChange(event.target.value);
onChange(event.target.value || undefined);
},
[onChange],
);
Expand Down
2 changes: 1 addition & 1 deletion packages/toolpad-app/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export interface EditorProps<T> {
propType: PropValueType;
disabled?: boolean;
value: T | undefined;
onChange: (newValue: T) => void;
onChange: (newValue: T | undefined) => void;
}

export type FlowDirection = 'row' | 'column' | 'row-reverse' | 'column-reverse';
Expand Down

0 comments on commit 31336a3

Please sign in to comment.