Skip to content

Commit 9842ec2

Browse files
committed
Add ability to customize form section tooltips and Form Name and Form Description labels. Add ability to hide form head.
1 parent 5bd2937 commit 9842ec2

File tree

9 files changed

+215
-61
lines changed

9 files changed

+215
-61
lines changed

docs/Plugins.md renamed to docs/Mods.md

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
1-
# Plugins
1+
# Mods
22

3-
Plugins allow the definition of custom input types. The documentation on the [Background](Background.md) of the Form Builder lists the set of input types supported by default, but for different use cases, the `FormBuilder` may be more useful to Form Builders if certain custom types can be inserted. This allows, for example, the definition of an input type that is associated with specific code in ui schema or data schema.
4-
5-
One example is the `Time` input type - it is a string type element in JSON schema, but is associated with the `format: date-time` property in the data schema at all times. This means that whenever a `Time` Input type is defined by a form builder, it is rendered accordingly by whatever form rendering software is used (`react-jsonschema-form`, for example, renders this as an input line that only allows time value to be entered).
6-
7-
Custom input types are encoded in exactly the same way the Default input types are encoded, and the Default input types are all available in the `default` directory [here](https://github.com/ginkgobioworks/react-json-schema-form-builder/tree/main/src/formBuilder/defaults).
3+
Mods provide for the customization of the Form Builder component, such as the definition of custom input types.
84

95
## Type Definition
106

117
Flow type defintions are available via [flow-typed](https://github.com/flow-typed/flow-typed).
128

13-
Recall that the plugin input types are passed into `mods`, as a property called `customFormInputs`,into the `FormBuilder` (see [Usage](Usage.md)). The type definition is as follows:
9+
The type definition for Mods is as follows:
1410

1511
```react
1612
declare type Mods = {|
@@ -24,10 +20,20 @@ declare type Mods = {|
2420
cardDisplayName?: string,
2521
cardDescription?: string,
2622
cardInputType?: string,
23+
cardSectionObjectName?: string,
24+
cardSectionDisplayName?: string,
25+
cardSectionDescription?: string,
26+
|},
27+
labels?: {|
28+
formNameLabel?: string,
29+
formDescriptionLabel?: string,
2730
|},
31+
showFormHead?: boolean,
2832
|};
2933
```
3034

35+
`tooltipDescriptions` and `labels` describe how some of the labels and tooltips in the Form Builder are to be customized. `showFormHead` is a boolean which controls whether the top section of the Form Builder, which contains inputs for the Form Name and Form Description, are show. It is set to `true` by default.
36+
3137
A single `FormInput` has a type definition as follows:
3238

3339
```react
@@ -81,7 +87,17 @@ declare type DataType =
8187

8288
`cardModal` refers to the React component that appears inside the modal that appears when the form builder clicks on the pencil icon. This React component also receives the same `Parameters` object, which is explained in further detail in the *Parameters* section.
8389

84-
## Matching Algorithm
90+
The `FormBuilder` component takes a prop `mods`, which is a `Mods` type object.
91+
92+
## Custom Form Inputs
93+
94+
The documentation on the [Background](Background.md) of the Form Builder lists the set of input types supported by default, but for different use cases, the `FormBuilder` may be more useful to Form Builders if certain custom types can be inserted. This allows, for example, the definition of an input type that is associated with specific code in ui schema or data schema.
95+
96+
One example is the `Time` input type - it is a string type element in JSON schema, but is associated with the `format: date-time` property in the data schema at all times. This means that whenever a `Time` Input type is defined by a form builder, it is rendered accordingly by whatever form rendering software is used (`react-jsonschema-form`, for example, renders this as an input line that only allows time value to be entered).
97+
98+
Custom input types are encoded in exactly the same way the Default input types are encoded, and the Default input types are all available in the `default` directory [here](https://github.com/ginkgobioworks/react-json-schema-form-builder/tree/main/src/formBuilder/defaults).
99+
100+
### Matching Algorithm
85101

86102
The `matchIf` array in a `FormInput` contains a series of `MatchType` objects, which represent different possible 'scenarios' that the `FormBuilder` may encounter when parsing a set of Data and UI Schema. The `MatchType` is defined as follows:
87103

@@ -108,7 +124,7 @@ declare type MatchType = {|
108124

109125
`enum` is a boolean that evaluates to true if the component has a property `enum` in the Data Schema.
110126

111-
## Component Types
127+
### Component Types
112128

113129
`cardBody` and `modalBody` are components whose props have a type of `CardBodyProps`:
114130

@@ -135,3 +151,20 @@ declare type Parameters = {|
135151

136152
It can hold any number of keys pointing to specific values. One common example is `parameters.default`, which stores the default value specified by the builder for this `FormInput`.
137153

154+
## Tooltips and Labels
155+
156+
By passing in alternative text to the `tooltipDescriptions` object of the `mods` prop, the text for various tooltips in the Form Builder can be customized:
157+
158+
- `add` - The tooltip when hovering over the button to add a new element/section.
159+
- `cardObjectName` - The tooltip for the "Object Name" field for a form element.
160+
- `cardDisplayName` - The tooltip for the "Display Name" field for a form element.
161+
- `cardDescription` - The tooltip for the "Description" field for a form element.
162+
- `cardInputType` - The tooltip for the "Input Type" field for a form element.
163+
- `cardSectionObjectName` - The tooltip for the "Object Name" field for a form section.
164+
- `cardSectionDisplayName` - The tooltip for the "Display Name" field for a form section.
165+
- `cardSectionDescription` - The tooltip for the "Description" field for a form section.
166+
167+
The text for the labels of a few of the inputs in the Form Builder can similarly be customized by specifying a `labels` object of `mods`.
168+
169+
- `formNameLabel` - The label for the input for the name/title of the entire form.
170+
- `formDescriptionLabel` - The lable for the input for the description of the entire form.

docs/Usage.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -172,11 +172,11 @@ export default function Example() {
172172

173173
## Advanced
174174

175-
### Plugins
175+
### Custom Form Inputs
176176

177177
In addition to the default types of form inputs (Time, Checkbox, Radio, Dropdown, Short Answer, Long Answer, Password, Integer, Number, Array), custom form inputs can also be specified. These form inputs are defined in a JS object that is passed into the `FormBuilder` component (and the `PredefinedGallery` component if it's being used) as part of a `mods` property, which has a comprehensive type definition in [src/formBuilder/types.js](https://github.com/ginkgobioworks/react-json-schema-form-builder/blob/main/src/formBuilder/types.js) as `Mods`.
178178

179-
#### Example Plugin
179+
#### Example Custom Form Input
180180

181181
```react
182182
const customFormInputs = {
@@ -266,10 +266,10 @@ export type Mods = {
266266
}
267267
```
268268

269-
The `customFormInputs` define the logic that translates abstract "Input Types" into raw data schema and ui schema. For more information about these "Plugin" input types, see the page [here](Plugins.md).
269+
The `customFormInputs` define the logic that translates abstract "Input Types" into raw data schema and UI schema. For more information about these Custon Form Inputs, see the page [here](Mods.md).
270270

271271
The `tooltipDescriptions` allows an implementation of the `FormBuilder` that changes the tooltip descriptions that appear on hover over certain areas of the tool. The `add` popup appears when hovering over the plus buttons, the `cardObjectName` is the name of the back end name that appears in every card object input, the `cardDisplayName` allows rewriting the description of the display name tooltip, the `cardDescription` option allows overwriting the tooltip for the description, and the `cardInputType` allows setting a custom tooltip for the Input Type dropdown.
272272

273273
### Styling
274274

275-
To avoid collisions with existing CSS styles, this app uses [react-jss](https://cssinjs.org/react-jss/?v=v10.5.0) in order to generate class names avoiding overlap with others in the global scope. Using CSS to style FormBuilder and PredefinedGallery components will not work and is not supported. The ability to "skin" the FormBuilder and PredefinedGallery components may be a feature in the future.
275+
To avoid collisions with existing CSS styles, this app uses [react-jss](https://cssinjs.org/react-jss/?v=v10.5.0) in order to generate class names avoiding overlap with others in the global scope. Using CSS to style FormBuilder and PredefinedGallery components will not work and is not supported. The ability to "skin" the FormBuilder and PredefinedGallery components may be a feature in the future.

flow-libdef/@ginkgo-bioworks/react-json-schema-form-builder_v1.x.x/flow_v0.92.x-/react-json-schema-form-builder_v1.x.x.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,15 @@ declare module '@ginkgo-bioworks/react-json-schema-form-builder' {
6666
cardDisplayName?: string,
6767
cardDescription?: string,
6868
cardInputType?: string,
69+
cardSectionObjectName?: string,
70+
cardSectionDisplayName?: string,
71+
cardSectionDescription?: string,
6972
|},
73+
labels?: {|
74+
formNameLabel?: string,
75+
formDescriptionLabel?: string,
76+
|},
77+
showFormHead?: boolean,
7078
|};
7179

7280
declare type FormBuilderProps = {|

mkdocs.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ nav:
99
- Installation: Installation.md
1010
- Background: Background.md
1111
- Usage: Usage.md
12-
- Plugins: Plugins.md
12+
- Mods: Mods.md
1313
- Contributing: https://github.com/ginkgobioworks/react-json-schema-form-builder/blob/main/CONTRIBUTING.md
1414
- Bugs: https://github.com/ginkgobioworks/react-json-schema-form-builder/issues
1515
- Repository: https://github.com/ginkgobioworks/react-json-schema-form-builder
1616

1717
markdown_extensions:
1818
- toc:
19-
permalink: true
19+
permalink: true

src/formBuilder/FormBuilder.js

Lines changed: 51 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -318,44 +318,58 @@ export default function FormBuilder({
318318
<li key={index}>{message}</li>
319319
))}
320320
</Alert>
321-
<div className={classes.formHead}>
322-
<div>
323-
<h5>Form Name</h5>
324-
<Input
325-
value={schemaData.title || ''}
326-
placeholder='Title'
327-
type='text'
328-
onChange={(ev: SyntheticInputEvent<HTMLInputElement>) => {
329-
onChange(
330-
stringify({
331-
...schemaData,
332-
title: ev.target.value,
333-
}),
334-
uischema,
335-
);
336-
}}
337-
className='form-title'
338-
/>
321+
{(!mods || mods.showFormHead !== false) && (
322+
<div className={classes.formHead} data-test='form-head'>
323+
<div>
324+
<h5 data-test='form-name-label'>
325+
{mods &&
326+
mods.labels &&
327+
typeof mods.labels.formNameLabel === 'string'
328+
? mods.labels.formNameLabel
329+
: 'Form Name'}
330+
</h5>
331+
<Input
332+
value={schemaData.title || ''}
333+
placeholder='Title'
334+
type='text'
335+
onChange={(ev: SyntheticInputEvent<HTMLInputElement>) => {
336+
onChange(
337+
stringify({
338+
...schemaData,
339+
title: ev.target.value,
340+
}),
341+
uischema,
342+
);
343+
}}
344+
className='form-title'
345+
/>
346+
</div>
347+
<div>
348+
<h5 data-test='form-description-label'>
349+
{mods &&
350+
mods.labels &&
351+
typeof mods.labels.formDescriptionLabel === 'string'
352+
? mods.labels.formDescriptionLabel
353+
: 'Form Description'}
354+
</h5>
355+
<Input
356+
value={schemaData.description || ''}
357+
placeholder='Description'
358+
type='text'
359+
onChange={(ev: SyntheticInputEvent<HTMLInputElement>) =>
360+
onChange(
361+
stringify({
362+
...schemaData,
363+
description: ev.target.value,
364+
}),
365+
uischema,
366+
)
367+
}
368+
className='form-description'
369+
/>
370+
</div>
339371
</div>
340-
<div>
341-
<h5>Form Description</h5>
342-
<Input
343-
value={schemaData.description || ''}
344-
placeholder='Description'
345-
type='text'
346-
onChange={(ev: SyntheticInputEvent<HTMLInputElement>) =>
347-
onChange(
348-
stringify({
349-
...schemaData,
350-
description: ev.target.value,
351-
}),
352-
uischema,
353-
)
354-
}
355-
className='form-description'
356-
/>
357-
</div>
358-
</div>
372+
)}
359373
<div className={`form-body ${classes.formBody}`}>
360374
<DragDropContext
361375
onDragEnd={(result) =>

src/formBuilder/FormBuilder.test.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ describe('FormBuilder', () => {
1818
document.body.appendChild(div);
1919
const wrapper = mount(<FormBuilder {...props} />, { attachTo: div });
2020
expect(wrapper.exists('.form-body')).toBeTruthy();
21+
expect(wrapper.exists('[data-test="form-head"]')).toBeTruthy();
2122
});
2223

2324
it('renders the appropriate number of cards', () => {
@@ -140,4 +141,38 @@ describe('FormBuilder', () => {
140141
expect(mockEvent).toHaveBeenCalledTimes(1);
141142
mockEvent.mockClear();
142143
});
144+
145+
it('renders custom labels in the form head', () => {
146+
const div = document.createElement('div');
147+
document.body.appendChild(div);
148+
const wrapper = mount(
149+
<FormBuilder
150+
{...props}
151+
mods={{
152+
labels: {
153+
formNameLabel: 'test name label',
154+
formDescriptionLabel: 'test description label',
155+
},
156+
}}
157+
/>,
158+
{ attachTo: div },
159+
);
160+
expect(wrapper.find('[data-test="form-name-label"]').text()).toEqual(
161+
'test name label',
162+
);
163+
expect(wrapper.find('[data-test="form-description-label"]').text()).toEqual(
164+
'test description label',
165+
);
166+
});
167+
168+
it('does not render the form head if showFormHead is false', () => {
169+
const div = document.createElement('div');
170+
document.body.appendChild(div);
171+
const wrapper = mount(
172+
<FormBuilder {...props} mods={{ showFormHead: false }} />,
173+
{ attachTo: div },
174+
);
175+
expect(wrapper.exists('.form-body')).toBeTruthy();
176+
expect(wrapper.exists('[data-test="form-head"]')).toBeFalsy();
177+
});
143178
});

src/formBuilder/Section.js

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -232,11 +232,19 @@ export default function Section({
232232
) : (
233233
''
234234
)}
235-
<div className='section-entry'>
235+
<div className='section-entry' data-test='section-object-name'>
236236
<h5>
237237
Section Object Name{' '}
238238
<Tooltip
239-
text='The key to the object that will represent this form section.'
239+
text={
240+
mods &&
241+
mods.tooltipDescriptions &&
242+
mods.tooltipDescriptions &&
243+
typeof mods.tooltipDescriptions.cardSectionObjectName ===
244+
'string'
245+
? mods.tooltipDescriptions.cardSectionObjectName
246+
: 'The key to the object that will represent this form section.'
247+
}
240248
id={`${keyName}_nameinfo`}
241249
type='help'
242250
/>
@@ -255,11 +263,19 @@ export default function Section({
255263
readOnly={hideKey}
256264
/>
257265
</div>
258-
<div className='section-entry'>
266+
<div className='section-entry' data-test='section-display-name'>
259267
<h5>
260268
Section Display Name{' '}
261269
<Tooltip
262-
text='The name of the form section that will be shown to users of the form.'
270+
text={
271+
mods &&
272+
mods.tooltipDescriptions &&
273+
mods.tooltipDescriptions &&
274+
typeof mods.tooltipDescriptions.cardSectionDisplayName ===
275+
'string'
276+
? mods.tooltipDescriptions.cardSectionDisplayName
277+
: 'The name of the form section that will be shown to users of the form.'
278+
}
263279
id={`${keyName}_titleinfo`}
264280
type='help'
265281
/>
@@ -280,11 +296,19 @@ export default function Section({
280296
className='card-text'
281297
/>
282298
</div>
283-
<div className='section-entry'>
299+
<div className='section-entry' data-test='section-description'>
284300
<h5>
285301
Section Description{' '}
286302
<Tooltip
287-
text='This will appear as gray text in the service request form'
303+
text={
304+
mods &&
305+
mods.tooltipDescriptions &&
306+
mods.tooltipDescriptions &&
307+
typeof mods.tooltipDescriptions.cardSectionDescription ===
308+
'string'
309+
? mods.tooltipDescriptions.cardSectionDescription
310+
: 'A description of the section which will be visible on the form.'
311+
}
288312
id={`${keyName}_descriptioninfo`}
289313
type='help'
290314
/>

src/formBuilder/Section.test.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,36 @@ describe('Section', () => {
3333
expect(wrapper.exists('.section-container')).toBeTruthy();
3434
});
3535

36+
it('uses mods.tooltipDescriptions', () => {
37+
const div = document.createElement('div');
38+
document.body.appendChild(div);
39+
const wrapper = mount(
40+
<Section
41+
{...props}
42+
mods={{
43+
tooltipDescriptions: {
44+
cardSectionObjectName: 'test object name',
45+
cardSectionDisplayName: 'test display name',
46+
cardSectionDescription: 'test description',
47+
},
48+
}}
49+
/>,
50+
{ attachTo: div },
51+
);
52+
expect(
53+
wrapper.find('[data-test="section-object-name"] Tooltip').props()
54+
.children,
55+
).toEqual('test object name');
56+
expect(
57+
wrapper.find('[data-test="section-display-name"] Tooltip').props()
58+
.children,
59+
).toEqual('test display name');
60+
expect(
61+
wrapper.find('[data-test="section-description"] Tooltip').props()
62+
.children,
63+
).toEqual('test description');
64+
});
65+
3666
it('calls the delete function on delete', () => {
3767
const div = document.createElement('div');
3868
document.body.appendChild(div);

0 commit comments

Comments
 (0)