Skip to content

Commit 717c0cb

Browse files
Feature: Modernized the playground ui
Modernized the playground UI to allow for more real-estate for the actual form - Updated the `SampleSelector` to render the samples inside of a `Drawer` on the left side of the screen, that when closed just shows the selected sample name - Refactored the theme and subtheme selector from `Headers` into the `Editors` component, moving that and the existing content into an `Accordion` - Renamed the `Headers` component to `OptionsDrawer` moving all of the remaining content into a permanent right-side drawer - Also consolidated the two live settings schemas and uiSchemas into a single schema and uiSchema - Also moved the buttons above the live settings schema form - Updated `Playground` and `Sample.ts` to fix the imports due to the file rename - Updated the `ThemeSelector` and `SubthemeSelectors` to add click handlers to stop the click propagation - Updated `CopyLink` to make it wider and change the title to `Share Playground` - Updated `Playground` to change the fragment into a `Box` flexed, with 100%, adding the `SampleSelector` directly - Also added a second `Box` width 100% around the `Editors`, `Divider`, and `DemoFrame` - Moved the renamed `OptionsDrawer` below the second `Box` - Updated `Footer` to absolutely position the Netlify badge to the left of the Options Drawer
1 parent c81ccfb commit 717c0cb

File tree

9 files changed

+315
-187
lines changed

9 files changed

+315
-187
lines changed

packages/playground/src/components/CopyLink.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,17 @@ export default function CopyLink({ shareURL, onShare }: CopyLinkProps) {
1313
navigator.clipboard.writeText(input.current?.value ?? '');
1414
}
1515

16+
const style = { maxWidth: '21.525rem', margin: '5px 0' };
1617
if (!shareURL) {
1718
return (
18-
<button className='btn btn-default' type='button' onClick={onShare}>
19-
Share
19+
<button className='btn btn-default' type='button' onClick={onShare} style={style}>
20+
Share Playground
2021
</button>
2122
);
2223
}
2324

2425
return (
25-
<div className='input-group'>
26+
<div className='input-group' style={style}>
2627
<input type='text' ref={input} className='form-control' defaultValue={shareURL} />
2728
<span className='input-group-btn'>
2829
<button className='btn btn-default' type='button' onClick={onCopyClick}>

packages/playground/src/components/Editors.tsx

Lines changed: 73 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,31 @@
1-
import { useCallback, useState } from 'react';
1+
import { Dispatch, SetStateAction, useCallback, useState } from 'react';
2+
import { styled } from '@mui/material/styles';
3+
import Accordion from '@mui/material/Accordion';
4+
import MuiAccordionSummary from '@mui/material/AccordionSummary';
5+
import AccordionDetails from '@mui/material/AccordionDetails';
6+
import Grid from '@mui/material/Grid';
7+
import Typography from '@mui/material/Typography';
8+
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
29
import MonacoEditor from '@monaco-editor/react';
310
import { ErrorSchema, RJSFSchema, UiSchema } from '@rjsf/utils';
411
import isEqualWith from 'lodash/isEqualWith';
512

13+
import ThemeSelector, { ThemesType } from './ThemeSelector';
14+
import SubthemeSelector, { SubthemeType } from './SubthemeSelector';
15+
616
const monacoEditorOptions = {
717
minimap: {
818
enabled: false,
919
},
1020
automaticLayout: true,
1121
};
1222

23+
const AccordionSummary = styled(MuiAccordionSummary)({
24+
'.MuiAccordionSummary-content': {
25+
margin: 0,
26+
},
27+
});
28+
1329
type EditorProps = {
1430
title: string;
1531
code: string;
@@ -70,6 +86,12 @@ type EditorsProps = {
7086
setExtraErrors: React.Dispatch<React.SetStateAction<ErrorSchema | undefined>>;
7187
setShareURL: React.Dispatch<React.SetStateAction<string | null>>;
7288
hasUiSchemaGenerator: boolean;
89+
themes: { [themeName: string]: ThemesType };
90+
theme: string;
91+
subtheme: string | null;
92+
onThemeSelected: (theme: string, themeObj: ThemesType) => void;
93+
setSubtheme: Dispatch<SetStateAction<string | null>>;
94+
setStylesheet: Dispatch<SetStateAction<string | null>>;
7395
};
7496

7597
export default function Editors({
@@ -83,7 +105,20 @@ export default function Editors({
83105
setShareURL,
84106
setUiSchema,
85107
hasUiSchemaGenerator,
108+
themes,
109+
theme,
110+
subtheme,
111+
onThemeSelected,
112+
setSubtheme,
113+
setStylesheet,
86114
}: EditorsProps) {
115+
const onSubthemeSelected = useCallback(
116+
(subtheme: any, { stylesheet }: SubthemeType) => {
117+
setSubtheme(subtheme);
118+
setStylesheet(stylesheet || null);
119+
},
120+
[setSubtheme, setStylesheet],
121+
);
87122
const onSchemaEdited = useCallback(
88123
(newSchema: any) => {
89124
setSchema(newSchema);
@@ -127,23 +162,42 @@ export default function Editors({
127162
const uiSchemaTitle = hasUiSchemaGenerator ? 'UISchema (regenerated on theme change)' : 'UiSchema';
128163

129164
return (
130-
<div className='col-sm-7'>
131-
<Editor title='JSONSchema' code={toJson(schema)} onChange={onSchemaEdited} />
132-
<div className='row'>
133-
<div className='col-sm-6'>
134-
<Editor title={uiSchemaTitle} code={toJson(uiSchema)} onChange={onUISchemaEdited} />
135-
</div>
136-
<div className='col-sm-6'>
137-
<Editor title='formData' code={toJson(formData)} onChange={onFormDataEdited} />
138-
</div>
139-
</div>
140-
{extraErrors && (
141-
<div className='row'>
142-
<div className='col'>
143-
<Editor title='extraErrors' code={toJson(extraErrors || {})} onChange={onExtraErrorsEdited} />
144-
</div>
145-
</div>
146-
)}
147-
</div>
165+
<Accordion defaultExpanded disableGutters>
166+
<AccordionSummary expandIcon={<ExpandMoreIcon fontSize='large' />} title='Toggle Editors'>
167+
<Grid container spacing={1} sx={{ width: '100%' }}>
168+
<Grid size={6}>
169+
<Typography component='div' variant='h2' sx={{ pr: 1 }}>
170+
react-jsonschema-form
171+
</Typography>
172+
</Grid>
173+
<Grid size={3}>
174+
<ThemeSelector themes={themes} theme={theme} select={onThemeSelected} />
175+
</Grid>
176+
<Grid size={3}>
177+
{themes[theme] && themes[theme].subthemes && (
178+
<SubthemeSelector subthemes={themes[theme].subthemes!} subtheme={subtheme} select={onSubthemeSelected} />
179+
)}
180+
</Grid>
181+
</Grid>
182+
</AccordionSummary>
183+
<AccordionDetails sx={{ p: 0 }}>
184+
<Grid container spacing={0.5}>
185+
<Grid size={extraErrors ? 3 : 4}>
186+
<Editor title='JSONSchema' code={toJson(schema)} onChange={onSchemaEdited} />
187+
</Grid>
188+
<Grid size={extraErrors ? 3 : 4}>
189+
<Editor title={uiSchemaTitle} code={toJson(uiSchema)} onChange={onUISchemaEdited} />
190+
</Grid>
191+
<Grid size={extraErrors ? 3 : 4}>
192+
<Editor title='formData' code={toJson(formData)} onChange={onFormDataEdited} />
193+
</Grid>
194+
{extraErrors && (
195+
<Grid size={3}>
196+
<Editor title='extraErrors' code={toJson(extraErrors || {})} onChange={onExtraErrorsEdited} />
197+
</Grid>
198+
)}
199+
</Grid>
200+
</AccordionDetails>
201+
</Accordion>
148202
);
149203
}

packages/playground/src/components/Header.tsx renamed to packages/playground/src/components/OptionsDrawer.tsx

Lines changed: 34 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,39 @@ import {
66
PropsWithChildren,
77
SetStateAction,
88
} from 'react';
9+
import Drawer from '@mui/material/Drawer';
910
import Form, { IChangeEvent } from '@rjsf/core';
1011
import { RJSFSchema, UiSchema, ValidatorType } from '@rjsf/utils';
1112
import localValidator from '@rjsf/validator-ajv8';
12-
import base64 from '../utils/base64';
1313

14+
import base64 from '../utils/base64';
1415
import CopyLink from './CopyLink';
15-
import ThemeSelector, { ThemesType } from './ThemeSelector';
16-
import SampleSelector, { SampleSelectorProps } from './SampleSelector';
1716
import ValidatorSelector from './ValidatorSelector';
18-
import SubthemeSelector, { SubthemeType } from './SubthemeSelector';
1917
import RawValidatorTest from './RawValidatorTest';
2018

19+
export const DRAWER_WIDTH = '28rem';
20+
2121
type HeaderButtonProps = {
2222
title: string;
2323
onClick: () => void;
2424
} & ButtonHTMLAttributes<HTMLButtonElement>;
2525

2626
function HeaderButton({ title, onClick, children, ...buttonProps }: PropsWithChildren<HeaderButtonProps>) {
2727
return (
28-
<button type='button' className='btn btn-default' title={title} onClick={onClick} {...buttonProps}>
28+
<button
29+
type='button'
30+
className='btn btn-default'
31+
title={title}
32+
onClick={onClick}
33+
{...buttonProps}
34+
style={{ marginRight: '5px' }}
35+
>
2936
{children}
3037
</button>
3138
);
3239
}
3340

34-
function HeaderButtons({ playGroundFormRef }: { playGroundFormRef: MutableRefObject<any> }) {
41+
function OptionsButtons({ playGroundFormRef }: { playGroundFormRef: MutableRefObject<any> }) {
3542
const submitClick = useCallback(() => {
3643
playGroundFormRef.current.submit();
3744
}, [playGroundFormRef]);
@@ -61,6 +68,7 @@ function HeaderButtons({ playGroundFormRef }: { playGroundFormRef: MutableRefObj
6168

6269
const liveSettingsBooleanSchema: RJSFSchema = {
6370
type: 'object',
71+
title: 'Form Options',
6472
properties: {
6573
liveValidate: { type: 'boolean', title: 'Live validation' },
6674
disabled: { type: 'boolean', title: 'Disable whole form' },
@@ -82,12 +90,6 @@ const liveSettingsBooleanSchema: RJSFSchema = {
8290
title: 'Show Error List',
8391
enum: [false, 'top', 'bottom'],
8492
},
85-
},
86-
};
87-
88-
const liveSettingsSelectSchema: RJSFSchema = {
89-
type: 'object',
90-
properties: {
9193
experimental_defaultFormStateBehavior: {
9294
title: 'Default Form State Behavior (Experimental)',
9395
type: 'object',
@@ -221,9 +223,6 @@ const liveSettingsBooleanUiSchema: UiSchema = {
221223
inline: true,
222224
},
223225
},
224-
};
225-
226-
const liveSettingsSelectUiSchema: UiSchema = {
227226
experimental_defaultFormStateBehavior: {
228227
'ui:options': {
229228
label: false,
@@ -241,59 +240,39 @@ export interface LiveSettings {
241240
[key: string]: any;
242241
}
243242

244-
type HeaderProps = {
243+
type OptionsDrawerProps = {
245244
schema: RJSFSchema;
246245
uiSchema: UiSchema;
247246
formData: any;
248247
shareURL: string | null;
249-
themes: { [themeName: string]: ThemesType };
250248
theme: string;
251-
subtheme: string | null;
252249
sampleName: string;
253250
validators: {
254251
[validatorName: string]: ValidatorType<any, RJSFSchema, any>;
255252
};
256253
validator: string;
257254
liveSettings: LiveSettings;
258255
playGroundFormRef: MutableRefObject<any>;
259-
onSampleSelected: SampleSelectorProps['onSelected'];
260-
onThemeSelected: (theme: string, themeObj: ThemesType) => void;
261-
setSubtheme: Dispatch<SetStateAction<string | null>>;
262-
setStylesheet: Dispatch<SetStateAction<string | null>>;
263256
setValidator: Dispatch<SetStateAction<string>>;
264257
setLiveSettings: Dispatch<SetStateAction<LiveSettings>>;
265258
setShareURL: Dispatch<SetStateAction<string | null>>;
266259
};
267260

268-
export default function Header({
261+
export default function OptionsDrawer({
269262
schema,
270263
uiSchema,
271264
formData,
272265
shareURL,
273-
themes,
274266
theme,
275-
subtheme,
276267
validators,
277268
validator,
278269
liveSettings,
279270
playGroundFormRef,
280-
onThemeSelected,
281-
setSubtheme,
282-
setStylesheet,
283271
setValidator,
284272
setLiveSettings,
285273
setShareURL,
286274
sampleName,
287-
onSampleSelected,
288-
}: HeaderProps) {
289-
const onSubthemeSelected = useCallback(
290-
(subtheme: any, { stylesheet }: SubthemeType) => {
291-
setSubtheme(subtheme);
292-
setStylesheet(stylesheet || null);
293-
},
294-
[setSubtheme, setStylesheet],
295-
);
296-
275+
}: OptionsDrawerProps) {
297276
const onValidatorSelected = useCallback(
298277
(validator: string) => {
299278
setValidator(validator);
@@ -332,52 +311,23 @@ export default function Header({
332311
console.error(error);
333312
}
334313
}, [formData, liveSettings, schema, theme, uiSchema, validator, setShareURL, sampleName]);
335-
314+
const drawerSx = { width: DRAWER_WIDTH, p: 1 };
336315
return (
337-
<div className='page-header'>
338-
<h1>react-jsonschema-form</h1>
339-
<div className='row'>
340-
<div className='col-sm-4'>
341-
<SampleSelector onSelected={onSampleSelected} selectedSample={sampleName} />
342-
</div>
343-
<div className='col-sm-2'>
344-
<Form
345-
idPrefix='rjsf_options'
346-
schema={liveSettingsBooleanSchema}
347-
formData={liveSettings}
348-
validator={localValidator}
349-
onChange={handleSetLiveSettings}
350-
uiSchema={liveSettingsBooleanUiSchema}
351-
>
352-
<div />
353-
</Form>
354-
</div>
355-
<div className='col-sm-2'>
356-
<Form
357-
idPrefix='rjsf_options'
358-
schema={liveSettingsSelectSchema}
359-
formData={liveSettings}
360-
validator={localValidator}
361-
onChange={handleSetLiveSettings}
362-
uiSchema={liveSettingsSelectUiSchema}
363-
>
364-
<div />
365-
</Form>
366-
</div>
367-
<div className='col-sm-2'>
368-
<ThemeSelector themes={themes} theme={theme} select={onThemeSelected} />
369-
{themes[theme] && themes[theme].subthemes && (
370-
<SubthemeSelector subthemes={themes[theme].subthemes!} subtheme={subtheme} select={onSubthemeSelected} />
371-
)}
372-
<ValidatorSelector validators={validators} validator={validator} select={onValidatorSelected} />
373-
<HeaderButtons playGroundFormRef={playGroundFormRef} />
374-
<div style={{ marginTop: '5px' }} />
375-
<CopyLink shareURL={shareURL} onShare={onShare} />
376-
</div>
377-
<div className='col-sm-2'>
378-
<RawValidatorTest validator={validators[validator]} schema={schema} formData={formData} />
379-
</div>
380-
</div>
381-
</div>
316+
<Drawer open variant='permanent' anchor='right' sx={drawerSx} slotProps={{ paper: { sx: drawerSx } }}>
317+
<OptionsButtons playGroundFormRef={playGroundFormRef} />
318+
<CopyLink shareURL={shareURL} onShare={onShare} />
319+
<Form
320+
idPrefix='rjsf_options'
321+
schema={liveSettingsBooleanSchema}
322+
formData={liveSettings}
323+
validator={localValidator}
324+
onChange={handleSetLiveSettings}
325+
uiSchema={liveSettingsBooleanUiSchema}
326+
>
327+
<span />
328+
</Form>
329+
<ValidatorSelector validators={validators} validator={validator} select={onValidatorSelected} />
330+
<RawValidatorTest validator={validators[validator]} schema={schema} formData={formData} />
331+
</Drawer>
382332
);
383333
}

0 commit comments

Comments
 (0)