0
# Form System
1
2
Advanced form rendering capabilities using React JSON Schema Form (RJSF) with JupyterLab-specific customizations. The form system provides dynamic form generation, validation, and custom field rendering with full TypeScript support.
3
4
## Capabilities
5
6
### FormComponent
7
8
Generic RJSF form component with JupyterLab customizations and styling.
9
10
```typescript { .api }
11
/**
12
* Generic RJSF form component with JupyterLab styling and customizations
13
*/
14
interface IFormComponentProps<T = ReadonlyJSONObject> extends FormProps<T>, FormComponent.ILabCustomizerProps {
15
/** Current form data */
16
formData: T;
17
/** Callback for form data changes */
18
onChange: (e: IChangeEvent<T>) => any;
19
/** Additional form context */
20
formContext?: unknown;
21
}
22
23
/**
24
* Main form component for rendering JSON Schema forms
25
* @param props - Form configuration and data
26
* @returns JSX form element
27
*/
28
function FormComponent(props: IFormComponentProps): JSX.Element;
29
30
namespace FormComponent {
31
interface IButtonProps {
32
/** Button style variant for array controls */
33
buttonStyle?: 'icons' | 'text';
34
/** Translator for internationalization */
35
translator?: ITranslator;
36
}
37
38
interface ILabCustomizerProps extends IButtonProps {
39
/** Use compact form layout */
40
compact?: boolean;
41
/** Show indicators for values that differ from defaults */
42
showModifiedFromDefault?: boolean;
43
}
44
}
45
46
/**
47
* Default UI options for forms
48
*/
49
const DEFAULT_UI_OPTIONS = {
50
submitButtonOptions: { norender: true }
51
};
52
```
53
54
**Usage Examples:**
55
56
```typescript
57
import React, { useState } from 'react';
58
import { FormComponent, DEFAULT_UI_OPTIONS } from '@jupyterlab/ui-components';
59
import { JSONSchema7 } from 'json-schema';
60
61
// Basic form usage
62
interface UserSettings {
63
theme: string;
64
fontSize: number;
65
enableAutoSave: boolean;
66
}
67
68
const userSchema: JSONSchema7 = {
69
type: 'object',
70
properties: {
71
theme: {
72
type: 'string',
73
enum: ['light', 'dark', 'auto'],
74
default: 'light'
75
},
76
fontSize: {
77
type: 'number',
78
minimum: 10,
79
maximum: 24,
80
default: 14
81
},
82
enableAutoSave: {
83
type: 'boolean',
84
default: true
85
}
86
}
87
};
88
89
function SettingsForm() {
90
const [formData, setFormData] = useState<UserSettings>({
91
theme: 'light',
92
fontSize: 14,
93
enableAutoSave: true
94
});
95
96
return (
97
<FormComponent
98
schema={userSchema}
99
formData={formData}
100
onChange={(e) => setFormData(e.formData)}
101
uiSchema={{
102
...DEFAULT_UI_OPTIONS,
103
theme: { 'ui:widget': 'select' },
104
fontSize: { 'ui:widget': 'range' }
105
}}
106
compact={true}
107
showModifiedFromDefault={true}
108
/>
109
);
110
}
111
112
// Advanced form with custom widgets
113
const advancedSchema: JSONSchema7 = {
114
type: 'object',
115
properties: {
116
plugins: {
117
type: 'array',
118
items: {
119
type: 'object',
120
properties: {
121
name: { type: 'string' },
122
enabled: { type: 'boolean' },
123
config: { type: 'object' }
124
}
125
}
126
}
127
}
128
};
129
130
function AdvancedForm() {
131
const [data, setData] = useState({ plugins: [] });
132
133
return (
134
<FormComponent
135
schema={advancedSchema}
136
formData={data}
137
onChange={(e) => setData(e.formData)}
138
buttonStyle="icons"
139
uiSchema={{
140
plugins: {
141
'ui:options': {
142
addable: true,
143
removable: true
144
}
145
}
146
}}
147
/>
148
);
149
}
150
```
151
152
### Array Control Buttons
153
154
Specialized buttons for managing array items in forms.
155
156
```typescript { .api }
157
/**
158
* Button for moving array items up/down
159
* @param props - Button styling properties
160
* @returns JSX button element
161
*/
162
function MoveButton(props: FormComponent.IButtonProps): JSX.Element;
163
164
/**
165
* Button for removing array items
166
* @param props - Button styling properties
167
* @returns JSX button element
168
*/
169
function DropButton(props: FormComponent.IButtonProps): JSX.Element;
170
171
/**
172
* Button for adding new array items
173
* @param props - Button styling properties
174
* @returns JSX button element
175
*/
176
function AddButton(props: FormComponent.IButtonProps): JSX.Element;
177
```
178
179
**Usage Examples:**
180
181
```typescript
182
import { MoveButton, DropButton, AddButton } from '@jupyterlab/ui-components';
183
184
// Custom array template with styled buttons
185
function CustomArrayTemplate(props: any) {
186
return (
187
<div className="array-container">
188
<div className="array-header">
189
<h3>{props.title}</h3>
190
<AddButton buttonStyle="icons" />
191
</div>
192
193
{props.items.map((element: any, index: number) => (
194
<div key={index} className="array-item">
195
<div className="item-content">
196
{element.children}
197
</div>
198
<div className="item-controls">
199
<MoveButton buttonStyle="icons" />
200
<DropButton buttonStyle="text" />
201
</div>
202
</div>
203
))}
204
</div>
205
);
206
}
207
208
// Use in form configuration
209
const arrayFormConfig = {
210
schema: myArraySchema,
211
uiSchema: {
212
myArray: {
213
'ui:ArrayFieldTemplate': CustomArrayTemplate
214
}
215
}
216
};
217
```
218
219
### Form Renderer Registry
220
221
Registry system for custom form field and widget renderers.
222
223
```typescript { .api }
224
/**
225
* Interface for form renderers
226
*/
227
interface IFormRenderer {
228
/** Custom field renderer */
229
fieldRenderer?: Field;
230
/** Custom widget renderer */
231
widgetRenderer?: Widget;
232
}
233
234
/**
235
* Registry interface for managing form renderers
236
*/
237
interface IFormRendererRegistry {
238
/** Add a new renderer to the registry */
239
addRenderer: (id: string, renderer: IFormRenderer) => void;
240
/** Get renderer by ID */
241
getRenderer: (id: string) => IFormRenderer;
242
/** All registered renderers */
243
renderers: { [id: string]: IFormRenderer };
244
}
245
246
/**
247
* Dependency injection token for form renderer registry
248
*/
249
const IFormRendererRegistry: Token<IFormRendererRegistry>;
250
251
/**
252
* Concrete implementation of form renderer registry
253
*/
254
class FormRendererRegistry implements IFormRendererRegistry {
255
addRenderer(id: string, renderer: IFormRenderer): void;
256
getRenderer(id: string): IFormRenderer;
257
get renderers(): { [id: string]: IFormRenderer };
258
}
259
260
/**
261
* Placeholder interface for future icon manager functionality
262
*/
263
interface ILabIconManager {}
264
265
/**
266
* Dependency injection token for icon manager service
267
*/
268
const ILabIconManager: Token<ILabIconManager>;
269
```
270
271
**Usage Examples:**
272
273
```typescript
274
import { FormRendererRegistry, IFormRenderer } from '@jupyterlab/ui-components';
275
276
// Create custom renderer
277
const colorPickerRenderer: IFormRenderer = {
278
widgetRenderer: ({ value, onChange }) => (
279
<input
280
type="color"
281
value={value || '#000000'}
282
onChange={(e) => onChange(e.target.value)}
283
className="color-picker-widget"
284
/>
285
)
286
};
287
288
// Register custom renderer
289
const registry = new FormRendererRegistry();
290
registry.addRenderer('colorPicker', colorPickerRenderer);
291
292
// Use in form schema
293
const schemaWithCustomWidget: JSONSchema7 = {
294
type: 'object',
295
properties: {
296
backgroundColor: {
297
type: 'string',
298
format: 'color',
299
default: '#ffffff'
300
}
301
}
302
};
303
304
const uiSchemaWithCustomWidget = {
305
backgroundColor: {
306
'ui:widget': 'colorPicker'
307
}
308
};
309
310
// Register with dependency injection
311
import { ServiceManager } from '@jupyterlab/services';
312
313
const services = new ServiceManager();
314
services.contents.add(IFormRendererRegistry, new FormRendererRegistry());
315
316
// Get registered renderer
317
const renderer = registry.getRenderer('colorPicker');
318
```
319
320
### Form Validation and Error Handling
321
322
Built-in validation and error display capabilities.
323
324
```typescript { .api }
325
// Form validation is handled through JSON Schema validation
326
// Custom validation can be added through schema definitions
327
328
interface ValidationError {
329
name: string;
330
property: string;
331
message: string;
332
argument: any;
333
stack: string;
334
}
335
336
// Error handling in form components
337
interface FormErrorProps {
338
errors: ValidationError[];
339
schema: JSONSchema7;
340
uiSchema: any;
341
}
342
```
343
344
**Usage Examples:**
345
346
```typescript
347
import { FormComponent } from '@jupyterlab/ui-components';
348
import { JSONSchema7 } from 'json-schema';
349
350
// Schema with validation rules
351
const validationSchema: JSONSchema7 = {
352
type: 'object',
353
properties: {
354
email: {
355
type: 'string',
356
format: 'email',
357
title: 'Email Address'
358
},
359
age: {
360
type: 'number',
361
minimum: 0,
362
maximum: 150,
363
title: 'Age'
364
},
365
password: {
366
type: 'string',
367
minLength: 8,
368
pattern: '^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)',
369
title: 'Password'
370
}
371
},
372
required: ['email', 'password']
373
};
374
375
// Custom validation function
376
function customValidate(formData: any, errors: any) {
377
if (formData.password && formData.confirmPassword) {
378
if (formData.password !== formData.confirmPassword) {
379
errors.confirmPassword.addError('Passwords must match');
380
}
381
}
382
return errors;
383
}
384
385
// Form with validation
386
function ValidatedForm() {
387
const [formData, setFormData] = useState({});
388
const [errors, setErrors] = useState<ValidationError[]>([]);
389
390
return (
391
<div>
392
<FormComponent
393
schema={validationSchema}
394
formData={formData}
395
onChange={(e) => {
396
setFormData(e.formData);
397
setErrors(e.errors || []);
398
}}
399
validate={customValidate}
400
showErrorList={true}
401
liveValidate={true}
402
/>
403
404
{errors.length > 0 && (
405
<div className="form-errors">
406
<h4>Please fix the following errors:</h4>
407
<ul>
408
{errors.map((error, i) => (
409
<li key={i}>{error.message}</li>
410
))}
411
</ul>
412
</div>
413
)}
414
</div>
415
);
416
}
417
```
418
419
### Custom Field Templates
420
421
Create custom templates for specific form fields and layouts.
422
423
```typescript { .api }
424
// Custom field template interfaces from RJSF
425
interface FieldTemplateProps {
426
id: string;
427
label: string;
428
children: React.ReactNode;
429
errors: React.ReactNode;
430
help: React.ReactNode;
431
description: React.ReactNode;
432
hidden: boolean;
433
required: boolean;
434
readonly: boolean;
435
disabled: boolean;
436
displayLabel: boolean;
437
schema: JSONSchema7;
438
uiSchema: any;
439
formContext: any;
440
}
441
```
442
443
**Usage Examples:**
444
445
```typescript
446
import { FormComponent } from '@jupyterlab/ui-components';
447
448
// Custom field template
449
function CustomFieldTemplate(props: FieldTemplateProps) {
450
const { id, label, children, errors, help, required, schema } = props;
451
452
return (
453
<div className={`field-${schema.type}`}>
454
<div className="field-header">
455
<label htmlFor={id} className="field-label">
456
{label}
457
{required && <span className="required">*</span>}
458
</label>
459
{help && <div className="field-help">{help}</div>}
460
</div>
461
462
<div className="field-input">
463
{children}
464
</div>
465
466
{errors && <div className="field-errors">{errors}</div>}
467
</div>
468
);
469
}
470
471
// Use custom template in form
472
<FormComponent
473
schema={mySchema}
474
formData={formData}
475
onChange={handleChange}
476
templates={{
477
FieldTemplate: CustomFieldTemplate
478
}}
479
/>
480
481
// Custom object field template
482
function CustomObjectTemplate(props: any) {
483
return (
484
<fieldset className="object-fieldset">
485
<legend>{props.title}</legend>
486
<div className="object-properties">
487
{props.properties.map((prop: any) => prop.content)}
488
</div>
489
</fieldset>
490
);
491
}
492
493
// Apply to specific fields
494
const customUiSchema = {
495
myObject: {
496
'ui:ObjectFieldTemplate': CustomObjectTemplate
497
}
498
};
499
```