Skip to content

Commit

Permalink
[form-builder] Remove createFormBuilderFactory (#151)
Browse files Browse the repository at this point in the history
  • Loading branch information
bjoerge committed Aug 31, 2017
1 parent 602ef35 commit 4386525
Show file tree
Hide file tree
Showing 14 changed files with 234 additions and 187 deletions.
4 changes: 2 additions & 2 deletions packages/@sanity/form-builder/docs/components/form-builder.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# The FormBuilder Component

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

## Props
- **`value`** - required. The current form builder value. It should always be an instance of FormBuilderValue
- **`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.
- **`validation`** - optional. An instance of `ValidationResult`
- **`validation`** - optional. An instance of `ValidationResult`
22 changes: 15 additions & 7 deletions packages/@sanity/form-builder/docs/custom-components.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,23 @@ export default class MyColorPicker extends React.Component {
Now that you have this component, you can then create a FormBuilder component providing a `resolveInputComponent` function that returns it for the `favoriteColor` field.

```js
import {createFormBuilder} from '@sanity/form-builder'
import FormBuilder from '@sanity/form-builder'
import MyColorPicker from './MyColorPicker.js'

const FormBuilder = createFormBuilder({
resolveInputComponent(field) {
if (field.name === 'favoriteColor') {
return MyColorPicker
}
function resolveInputComponent(field) {
if (field.name === 'favoriteColor') {
return MyColorPicker
}
})
}

function MyComponent() {
return (
<FormBuilder
value={/*...*/}
onChange={/*...*/}
resolveInputComponent={resolveInputComponent} />
)
}
```

Now the FormBuilder would render a color picker for the `favoriteColor` field on coworker
9 changes: 4 additions & 5 deletions packages/@sanity/form-builder/docs/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
```js
import React from 'react'
import ReactDOM from 'react-dom'
import {createFormBuilder, Schema} from '@sanity/form-builder'
import {FormBuilder} from '@sanity/form-builder'
import Schema from '@sanity/schema'

const schema = Schema.compile({
name: 'simple',
Expand Down Expand Up @@ -33,9 +34,7 @@ const schema = Schema.compile({
]
})

const FormBuilder = createFormBuilder({schema: schema})

let currentValue = FormBuilder.createEmpty('book')
let currentValue = {_type: 'book'}

function handleChange(event) {
console.log('Received a patch:', event.patch)
Expand Down Expand Up @@ -66,7 +65,7 @@ class MyComponent extends React.Component {
super(...args)
this.handleChange = this.handleChange.bind(this)
this.state = {
editorValue: FormBuilder.createEmpty('book')
editorValue: {_type: 'book'}
}
}

Expand Down
6 changes: 2 additions & 4 deletions packages/@sanity/form-builder/examples/quickstart/Main.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react'
import {createFormBuilder} from '../../src'
import FormBuilder from '../../src'
import applyPatch from '../../src/simplePatch'
import Schema from '@sanity/schema'

Expand Down Expand Up @@ -30,8 +30,6 @@ const schema = Schema.compile({
]
})

const FormBuilder = createFormBuilder({schema: schema})

export default class QuickstartExample extends React.Component {

state = {
Expand All @@ -50,7 +48,7 @@ export default class QuickstartExample extends React.Component {
render() {
return (
<div>
<FormBuilder value={this.state.editorValue} onChange={this.handleChange} />
<FormBuilder schema={schema} value={this.state.editorValue} onChange={this.handleChange} />
<button type="button" onClick={this.handleLogClick}>Output current value to console</button>
</div>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {save, restore} from '../lib/persist'

import sourceSchemas from '../schemas'
import Schema from '@sanity/schema'
import {createFormBuilder} from '../../../src'
import {FormBuilder} from '../../../src'
import {parseParams, preventDefault} from '../lib/utils'

import MyCustomLatLonInput from './custom/MyCustomLatLonInput'
Expand All @@ -20,6 +20,7 @@ import resolveReferenceInput from './custom/resolveReferenceInput'
import {arrayToJSONMatchPath} from '@sanity/mutator'
import InputWithCustomState from './custom/InputWithCustomState'
import {set, unset} from '../../../src/utils/patches'
import {resolvePreviewComponent} from '../../../src/defaultConfig'

const SCHEMA_NAMES = Object.keys(sourceSchemas)
const params = parseParams(document.location.pathname)
Expand All @@ -42,36 +43,34 @@ function logPatch(patch) {
)
}

const FormBuilder = schema && createFormBuilder({
schema: schema,
resolveInputComponent(type) {
if (type.component) {
return type.component
}
if (type.name === 'latlon') {
return MyCustomLatLonInput
}
if (type.name === 'reference') {
return resolveReferenceInput(type)
}
if (type.name === 'image') {
return MyCustomImageInput
}
if (type.name === 'customPatchHandlingExampleType') {
return InputWithCustomState
}
if (type.name === 'file') {
return MyCustomFileInput
}
if (type.name === 'slug') {
return MyCustomSlugInput
}
return type.inputComponent || undefined // signal to use default
},
resolveValidationComponent() {
return MyCustomValidationList
function resolveInputComponent(type) {
if (type.component) {
return type.component
}
if (type.name === 'latlon') {
return MyCustomLatLonInput
}
if (type.name === 'reference') {
return resolveReferenceInput(type)
}
if (type.name === 'image') {
return MyCustomImageInput
}
if (type.name === 'customPatchHandlingExampleType') {
return InputWithCustomState
}
})
if (type.name === 'file') {
return MyCustomFileInput
}
if (type.name === 'slug') {
return MyCustomSlugInput
}
return type.inputComponent || undefined // signal to use default
}

function resolveValidationComponent() {
return MyCustomValidationList
}

export default class Main extends React.Component {
state = {
Expand All @@ -80,6 +79,8 @@ export default class Main extends React.Component {
saved: false
}

patchChannel = FormBuilder.createPatchChannel()

componentDidUpdate(prevProps, prevState) {
if (prevState.inspect !== this.state.inspect) {
document.body.style.overflow = this.state.inspect === 'fullscreen' ? 'hidden' : ''
Expand All @@ -92,7 +93,7 @@ export default class Main extends React.Component {

receivePatches(patches) {
const nextValue = patches.reduce((prev, patch) => applyPatch(prev, patch), this.state.value)
FormBuilder.receivePatches({patches, snapshot: nextValue})
this.patchChannel.receivePatches({patches, snapshot: nextValue})

this.setState({
value: nextValue,
Expand All @@ -105,15 +106,19 @@ export default class Main extends React.Component {
save(PERSISTKEY, this.state.value)
this.setState({saved: true})
}

cmdLog(event) {
console.log(this.state.value) // eslint-disable-line no-console
}

cmdClear(event) {
this.receivePatches([unset()])
}

cmdRevert(event) {
this.receivePatches([set(restore(PERSISTKEY))])
}

cmdInspectLive(event) {
this.setState({inspect: event.currentTarget.checked ? 'docked' : false})
}
Expand Down Expand Up @@ -163,10 +168,12 @@ export default class Main extends React.Component {
<div className={styles[inspect === 'docked' ? 'inspectPane' : 'inspectPaneFullScreen']}>
<button className={styles.closeInspectPaneButton} onClick={() => this.setState({inspect: false})}>x</button>
{inspect === 'docked' && (
<button className={styles.fullscreenInspectPaneButton} onClick={() => this.setState({inspect: 'fullscreen'})}></button>
<button className={styles.fullscreenInspectPaneButton} onClick={() => this.setState({inspect: 'fullscreen'})}>
</button>
)}
{inspect === 'fullscreen' && (
<button className={styles.dockedInspectPaneButton} onClick={() => this.setState({inspect: 'docked'})}></button>
<button className={styles.dockedInspectPaneButton} onClick={() => this.setState({inspect: 'docked'})}>
</button>
)}
<div className={styles[inspect === 'docked' ? 'inspectPaneInner' : 'inspectPaneInnerFullScreen']}>
<Inspector inspect={value} />
Expand All @@ -190,6 +197,8 @@ export default class Main extends React.Component {
<form onSubmit={preventDefault}>
<FormBuilder
patchChannel={this.patchChannel}
resolveInputComponent={resolveInputComponent}
resolvePreviewComponent={resolvePreviewComponent}
value={value}
type={schemaType}
validation={validation}
Expand Down
6 changes: 2 additions & 4 deletions packages/@sanity/form-builder/examples/undo/Main.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react'
import Undoable from './lib/Undoable'

import Schema from '@sanity/schema'
import {createFormBuilder} from '../../src'
import {FormBuilder} from '../../src'
import applyPatch from '../../src/simplePatch'

const schema = Schema.compile({
Expand Down Expand Up @@ -32,8 +32,6 @@ const schema = Schema.compile({
]
})

const FormBuilder = createFormBuilder({schema})

export default class SimpleExample extends React.Component {
state = {
value: new Undoable(undefined),
Expand Down Expand Up @@ -83,7 +81,7 @@ export default class SimpleExample extends React.Component {
<button onClick={this.handleCommand} data-command="undo" disabled={!value.canUndo}>Undo</button>
<button onClick={this.handleCommand} data-command="redo" disabled={!value.canRedo}>Redo</button>
</div>
<FormBuilder value={value.get()} onChange={this.handleChange} />
<FormBuilder schema={schema} value={value.get()} onChange={this.handleChange} />
<div>
<label>
<input type="checkbox" checked={shouldInspect} onChange={this.handleCommand} data-command="inspect" />
Expand Down
101 changes: 101 additions & 0 deletions packages/@sanity/form-builder/src/FormBuilder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import PropTypes from 'prop-types'
import React from 'react'
import {FormBuilderInput} from './FormBuilderInput'
import * as defaultConfig from './defaultConfig'
import Schema from '@sanity/schema'
import pubsub from 'nano-pubsub'

const NOOP = () => {}

function resolve(type, providedResolve = NOOP, defaultResolve = NOOP) {
let itType = type
while (itType) {
const resolved = providedResolve(itType) || defaultResolve(itType)
if (resolved) {
return resolved
}
itType = itType.type
}
return undefined
}

export default class FormBuilder extends React.Component {
static createPatchChannel = () => {
const channel = pubsub()
return {receivePatches: channel.publish}
}
static propTypes = {
value: PropTypes.any,
schema: PropTypes.instanceOf(Schema).isRequired,
type: PropTypes.object,
children: PropTypes.any,
onChange: PropTypes.func,
patchChannel: PropTypes.shape({
onPatch: PropTypes.func
}),
resolveInputComponent: PropTypes.func,
resolvePreviewComponent: PropTypes.func
}

static childContextTypes = {
getValuePath: PropTypes.func,
onPatch: PropTypes.func,
formBuilder: PropTypes.shape({
schema: PropTypes.instanceOf(Schema),
resolveInputComponent: PropTypes.func,
document: PropTypes.any
})
}

getDocument = () => {
return this.props.value
}

resolveInputComponent = type => {
const {resolveInputComponent} = this.props
return resolve(type, resolveInputComponent, defaultConfig.resolveInputComponent)
|| defaultConfig.jsonTypeFallbacks[type.jsonType]
}

resolvePreviewComponent = type => {
const {resolvePreviewComponent} = this.props

return resolve(type, resolvePreviewComponent, defaultConfig.resolvePreviewComponent)
}

getChildContext() {
const {schema, patchChannel} = this.props
return {
getValuePath: () => ([]),
formBuilder: {
onPatch: patchChannel ? patchChannel.subscribe : () => {
// eslint-disable-next-line no-console
console.log('No patch channel provided to form-builder. If you need input based patch updates, please provide one')
},
schema: schema,
resolveInputComponent: this.resolveInputComponent,
resolvePreviewComponent: this.resolvePreviewComponent,
getDocument: this.getDocument
}
}
}

render() {
const {schema, value, type, onChange} = this.props

if (!schema) {
throw new TypeError('You must provide a schema to <FormBuilder (...)')
}

return (
<FormBuilderInput
value={value}
type={type}
onChange={onChange}
level={0}
isRoot
autoFocus
/>
)
}
}

0 comments on commit 4386525

Please sign in to comment.