Skip to content

Commit 4386525

Browse files
authored
[form-builder] Remove createFormBuilderFactory (#151)
1 parent 602ef35 commit 4386525

File tree

14 files changed

+234
-187
lines changed

14 files changed

+234
-187
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# The FormBuilder Component
22

3-
The `FormBuilder` component returned by `createFormBuilder(...)` is responsible for rendering the generated form and takes the following props:
3+
The `FormBuilder` component is responsible for rendering the generated form and takes the following props:
44

55
## Props
66
- **`value`** - required. The current form builder value. It should always be an instance of FormBuilderValue
77
- **`onChange`** - required. This is the function that will be called whenever a change happens. The change must be applied for it to update the form field. If the change is not applied the form will effectively be read-only.
8-
- **`validation`** - optional. An instance of `ValidationResult`
8+
- **`validation`** - optional. An instance of `ValidationResult`

packages/@sanity/form-builder/docs/custom-components.md

+15-7
Original file line numberDiff line numberDiff line change
@@ -69,15 +69,23 @@ export default class MyColorPicker extends React.Component {
6969
Now that you have this component, you can then create a FormBuilder component providing a `resolveInputComponent` function that returns it for the `favoriteColor` field.
7070

7171
```js
72-
import {createFormBuilder} from '@sanity/form-builder'
72+
import FormBuilder from '@sanity/form-builder'
7373
import MyColorPicker from './MyColorPicker.js'
7474

75-
const FormBuilder = createFormBuilder({
76-
resolveInputComponent(field) {
77-
if (field.name === 'favoriteColor') {
78-
return MyColorPicker
79-
}
75+
function resolveInputComponent(field) {
76+
if (field.name === 'favoriteColor') {
77+
return MyColorPicker
8078
}
81-
})
79+
}
80+
81+
function MyComponent() {
82+
return (
83+
<FormBuilder
84+
value={/*...*/}
85+
onChange={/*...*/}
86+
resolveInputComponent={resolveInputComponent} />
87+
)
88+
}
8289
```
90+
8391
Now the FormBuilder would render a color picker for the `favoriteColor` field on coworker

packages/@sanity/form-builder/docs/quickstart.md

+4-5
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
```js
55
import React from 'react'
66
import ReactDOM from 'react-dom'
7-
import {createFormBuilder, Schema} from '@sanity/form-builder'
7+
import {FormBuilder} from '@sanity/form-builder'
8+
import Schema from '@sanity/schema'
89

910
const schema = Schema.compile({
1011
name: 'simple',
@@ -33,9 +34,7 @@ const schema = Schema.compile({
3334
]
3435
})
3536

36-
const FormBuilder = createFormBuilder({schema: schema})
37-
38-
let currentValue = FormBuilder.createEmpty('book')
37+
let currentValue = {_type: 'book'}
3938

4039
function handleChange(event) {
4140
console.log('Received a patch:', event.patch)
@@ -66,7 +65,7 @@ class MyComponent extends React.Component {
6665
super(...args)
6766
this.handleChange = this.handleChange.bind(this)
6867
this.state = {
69-
editorValue: FormBuilder.createEmpty('book')
68+
editorValue: {_type: 'book'}
7069
}
7170
}
7271

packages/@sanity/form-builder/examples/quickstart/Main.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'react'
2-
import {createFormBuilder} from '../../src'
2+
import FormBuilder from '../../src'
33
import applyPatch from '../../src/simplePatch'
44
import Schema from '@sanity/schema'
55

@@ -30,8 +30,6 @@ const schema = Schema.compile({
3030
]
3131
})
3232

33-
const FormBuilder = createFormBuilder({schema: schema})
34-
3533
export default class QuickstartExample extends React.Component {
3634

3735
state = {
@@ -50,7 +48,7 @@ export default class QuickstartExample extends React.Component {
5048
render() {
5149
return (
5250
<div>
53-
<FormBuilder value={this.state.editorValue} onChange={this.handleChange} />
51+
<FormBuilder schema={schema} value={this.state.editorValue} onChange={this.handleChange} />
5452
<button type="button" onClick={this.handleLogClick}>Output current value to console</button>
5553
</div>
5654
)

packages/@sanity/form-builder/examples/schema-testbed/components/SchemaForm.js

+42-33
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {save, restore} from '../lib/persist'
77

88
import sourceSchemas from '../schemas'
99
import Schema from '@sanity/schema'
10-
import {createFormBuilder} from '../../../src'
10+
import {FormBuilder} from '../../../src'
1111
import {parseParams, preventDefault} from '../lib/utils'
1212

1313
import MyCustomLatLonInput from './custom/MyCustomLatLonInput'
@@ -20,6 +20,7 @@ import resolveReferenceInput from './custom/resolveReferenceInput'
2020
import {arrayToJSONMatchPath} from '@sanity/mutator'
2121
import InputWithCustomState from './custom/InputWithCustomState'
2222
import {set, unset} from '../../../src/utils/patches'
23+
import {resolvePreviewComponent} from '../../../src/defaultConfig'
2324

2425
const SCHEMA_NAMES = Object.keys(sourceSchemas)
2526
const params = parseParams(document.location.pathname)
@@ -42,36 +43,34 @@ function logPatch(patch) {
4243
)
4344
}
4445

45-
const FormBuilder = schema && createFormBuilder({
46-
schema: schema,
47-
resolveInputComponent(type) {
48-
if (type.component) {
49-
return type.component
50-
}
51-
if (type.name === 'latlon') {
52-
return MyCustomLatLonInput
53-
}
54-
if (type.name === 'reference') {
55-
return resolveReferenceInput(type)
56-
}
57-
if (type.name === 'image') {
58-
return MyCustomImageInput
59-
}
60-
if (type.name === 'customPatchHandlingExampleType') {
61-
return InputWithCustomState
62-
}
63-
if (type.name === 'file') {
64-
return MyCustomFileInput
65-
}
66-
if (type.name === 'slug') {
67-
return MyCustomSlugInput
68-
}
69-
return type.inputComponent || undefined // signal to use default
70-
},
71-
resolveValidationComponent() {
72-
return MyCustomValidationList
46+
function resolveInputComponent(type) {
47+
if (type.component) {
48+
return type.component
49+
}
50+
if (type.name === 'latlon') {
51+
return MyCustomLatLonInput
52+
}
53+
if (type.name === 'reference') {
54+
return resolveReferenceInput(type)
55+
}
56+
if (type.name === 'image') {
57+
return MyCustomImageInput
58+
}
59+
if (type.name === 'customPatchHandlingExampleType') {
60+
return InputWithCustomState
7361
}
74-
})
62+
if (type.name === 'file') {
63+
return MyCustomFileInput
64+
}
65+
if (type.name === 'slug') {
66+
return MyCustomSlugInput
67+
}
68+
return type.inputComponent || undefined // signal to use default
69+
}
70+
71+
function resolveValidationComponent() {
72+
return MyCustomValidationList
73+
}
7574

7675
export default class Main extends React.Component {
7776
state = {
@@ -80,6 +79,8 @@ export default class Main extends React.Component {
8079
saved: false
8180
}
8281

82+
patchChannel = FormBuilder.createPatchChannel()
83+
8384
componentDidUpdate(prevProps, prevState) {
8485
if (prevState.inspect !== this.state.inspect) {
8586
document.body.style.overflow = this.state.inspect === 'fullscreen' ? 'hidden' : ''
@@ -92,7 +93,7 @@ export default class Main extends React.Component {
9293

9394
receivePatches(patches) {
9495
const nextValue = patches.reduce((prev, patch) => applyPatch(prev, patch), this.state.value)
95-
FormBuilder.receivePatches({patches, snapshot: nextValue})
96+
this.patchChannel.receivePatches({patches, snapshot: nextValue})
9697

9798
this.setState({
9899
value: nextValue,
@@ -105,15 +106,19 @@ export default class Main extends React.Component {
105106
save(PERSISTKEY, this.state.value)
106107
this.setState({saved: true})
107108
}
109+
108110
cmdLog(event) {
109111
console.log(this.state.value) // eslint-disable-line no-console
110112
}
113+
111114
cmdClear(event) {
112115
this.receivePatches([unset()])
113116
}
117+
114118
cmdRevert(event) {
115119
this.receivePatches([set(restore(PERSISTKEY))])
116120
}
121+
117122
cmdInspectLive(event) {
118123
this.setState({inspect: event.currentTarget.checked ? 'docked' : false})
119124
}
@@ -163,10 +168,12 @@ export default class Main extends React.Component {
163168
<div className={styles[inspect === 'docked' ? 'inspectPane' : 'inspectPaneFullScreen']}>
164169
<button className={styles.closeInspectPaneButton} onClick={() => this.setState({inspect: false})}>x</button>
165170
{inspect === 'docked' && (
166-
<button className={styles.fullscreenInspectPaneButton} onClick={() => this.setState({inspect: 'fullscreen'})}></button>
171+
<button className={styles.fullscreenInspectPaneButton} onClick={() => this.setState({inspect: 'fullscreen'})}>
172+
</button>
167173
)}
168174
{inspect === 'fullscreen' && (
169-
<button className={styles.dockedInspectPaneButton} onClick={() => this.setState({inspect: 'docked'})}></button>
175+
<button className={styles.dockedInspectPaneButton} onClick={() => this.setState({inspect: 'docked'})}>
176+
</button>
170177
)}
171178
<div className={styles[inspect === 'docked' ? 'inspectPaneInner' : 'inspectPaneInnerFullScreen']}>
172179
<Inspector inspect={value} />
@@ -190,6 +197,8 @@ export default class Main extends React.Component {
190197
<form onSubmit={preventDefault}>
191198
<FormBuilder
192199
patchChannel={this.patchChannel}
200+
resolveInputComponent={resolveInputComponent}
201+
resolvePreviewComponent={resolvePreviewComponent}
193202
value={value}
194203
type={schemaType}
195204
validation={validation}

packages/@sanity/form-builder/examples/undo/Main.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React from 'react'
22
import Undoable from './lib/Undoable'
33

44
import Schema from '@sanity/schema'
5-
import {createFormBuilder} from '../../src'
5+
import {FormBuilder} from '../../src'
66
import applyPatch from '../../src/simplePatch'
77

88
const schema = Schema.compile({
@@ -32,8 +32,6 @@ const schema = Schema.compile({
3232
]
3333
})
3434

35-
const FormBuilder = createFormBuilder({schema})
36-
3735
export default class SimpleExample extends React.Component {
3836
state = {
3937
value: new Undoable(undefined),
@@ -83,7 +81,7 @@ export default class SimpleExample extends React.Component {
8381
<button onClick={this.handleCommand} data-command="undo" disabled={!value.canUndo}>Undo</button>
8482
<button onClick={this.handleCommand} data-command="redo" disabled={!value.canRedo}>Redo</button>
8583
</div>
86-
<FormBuilder value={value.get()} onChange={this.handleChange} />
84+
<FormBuilder schema={schema} value={value.get()} onChange={this.handleChange} />
8785
<div>
8886
<label>
8987
<input type="checkbox" checked={shouldInspect} onChange={this.handleCommand} data-command="inspect" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import PropTypes from 'prop-types'
2+
import React from 'react'
3+
import {FormBuilderInput} from './FormBuilderInput'
4+
import * as defaultConfig from './defaultConfig'
5+
import Schema from '@sanity/schema'
6+
import pubsub from 'nano-pubsub'
7+
8+
const NOOP = () => {}
9+
10+
function resolve(type, providedResolve = NOOP, defaultResolve = NOOP) {
11+
let itType = type
12+
while (itType) {
13+
const resolved = providedResolve(itType) || defaultResolve(itType)
14+
if (resolved) {
15+
return resolved
16+
}
17+
itType = itType.type
18+
}
19+
return undefined
20+
}
21+
22+
export default class FormBuilder extends React.Component {
23+
static createPatchChannel = () => {
24+
const channel = pubsub()
25+
return {receivePatches: channel.publish}
26+
}
27+
static propTypes = {
28+
value: PropTypes.any,
29+
schema: PropTypes.instanceOf(Schema).isRequired,
30+
type: PropTypes.object,
31+
children: PropTypes.any,
32+
onChange: PropTypes.func,
33+
patchChannel: PropTypes.shape({
34+
onPatch: PropTypes.func
35+
}),
36+
resolveInputComponent: PropTypes.func,
37+
resolvePreviewComponent: PropTypes.func
38+
}
39+
40+
static childContextTypes = {
41+
getValuePath: PropTypes.func,
42+
onPatch: PropTypes.func,
43+
formBuilder: PropTypes.shape({
44+
schema: PropTypes.instanceOf(Schema),
45+
resolveInputComponent: PropTypes.func,
46+
document: PropTypes.any
47+
})
48+
}
49+
50+
getDocument = () => {
51+
return this.props.value
52+
}
53+
54+
resolveInputComponent = type => {
55+
const {resolveInputComponent} = this.props
56+
return resolve(type, resolveInputComponent, defaultConfig.resolveInputComponent)
57+
|| defaultConfig.jsonTypeFallbacks[type.jsonType]
58+
}
59+
60+
resolvePreviewComponent = type => {
61+
const {resolvePreviewComponent} = this.props
62+
63+
return resolve(type, resolvePreviewComponent, defaultConfig.resolvePreviewComponent)
64+
}
65+
66+
getChildContext() {
67+
const {schema, patchChannel} = this.props
68+
return {
69+
getValuePath: () => ([]),
70+
formBuilder: {
71+
onPatch: patchChannel ? patchChannel.subscribe : () => {
72+
// eslint-disable-next-line no-console
73+
console.log('No patch channel provided to form-builder. If you need input based patch updates, please provide one')
74+
},
75+
schema: schema,
76+
resolveInputComponent: this.resolveInputComponent,
77+
resolvePreviewComponent: this.resolvePreviewComponent,
78+
getDocument: this.getDocument
79+
}
80+
}
81+
}
82+
83+
render() {
84+
const {schema, value, type, onChange} = this.props
85+
86+
if (!schema) {
87+
throw new TypeError('You must provide a schema to <FormBuilder (...)')
88+
}
89+
90+
return (
91+
<FormBuilderInput
92+
value={value}
93+
type={type}
94+
onChange={onChange}
95+
level={0}
96+
isRoot
97+
autoFocus
98+
/>
99+
)
100+
}
101+
}

0 commit comments

Comments
 (0)