Created
February 7, 2020 06:13
-
-
Save kmelve/e34feccafe8e9ee3c7e09d60ee3fd4d2 to your computer and use it in GitHub Desktop.
Tim’s conditional fields for Sanity.io
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import PropTypes from 'prop-types' | |
import React from 'react' | |
import Fieldset from 'part:@sanity/components/fieldsets/default' | |
import {setIfMissing} from 'part:@sanity/form-builder/patch-event' | |
// FormBuilderInput automatically generates fields from a schema | |
import {FormBuilderInput} from 'part:@sanity/form-builder' | |
// a Higher Order Component that passes document values as props | |
import {withDocument} from 'part:@sanity/form-builder' | |
class confitionalFields extends React.PureComponent { | |
static propTypes = { | |
type: PropTypes.shape({ | |
title: PropTypes.string, | |
name: PropTypes.string | |
}).isRequired, | |
level: PropTypes.number, | |
value: PropTypes.shape({ | |
_type: PropTypes.string | |
}), | |
focusPath: PropTypes.array.isRequired, | |
onFocus: PropTypes.func.isRequired, | |
onChange: PropTypes.func.isRequired, | |
onBlur: PropTypes.func.isRequired | |
} | |
firstFieldInput = React.createRef() | |
handleFieldChange = (field, fieldPatchEvent) => { | |
const {onChange, type} = this.props | |
// Whenever the field input emits a patch event, we need to make sure to each of the included patches | |
// are prefixed with its field name, e.g. going from: | |
// {path: [], set: <nextvalue>} to {path: [<fieldName>], set: <nextValue>} | |
// and ensure this input's value exists | |
onChange(fieldPatchEvent.prefixAll(field.name).prepend(setIfMissing({_type: type.name}))) | |
} | |
focus() { | |
this.firstFieldInput.current.focus() | |
} | |
render() { | |
console.log(this.props) | |
const {document, type, value, level, focusPath, onFocus, onBlur} = this.props | |
let condition = this.props && this.props.value && this.props.value.condition; | |
return ( | |
<Fieldset level={level} legend={type.title} description={type.description}> | |
<div> | |
{type.fields[0].type.fields | |
.map((field, i) => ( | |
// Delegate to the generic FormBuilderInput. It will resolve and insert the actual input component | |
// for the given field type | |
<FormBuilderInput | |
level={level + 1} | |
ref={i === 0 ? this.firstFieldInput : null} | |
key={field.name} | |
type={field.type} | |
value={value && value[field.name]} | |
onChange={patchEvent => this.handleFieldChange(field, patchEvent)} | |
path={[field.name]} | |
focusPath={focusPath} | |
onFocus={onFocus} | |
onBlur={onBlur} | |
/> | |
))} | |
<br/> | |
{type.fields[1].type.fields | |
.filter(field => ( | |
field.name === condition ? true : false | |
)) | |
.map((field, i) => ( | |
// Delegate to the generic FormBuilderInput. It will resolve and insert the actual input component | |
// for the given field type | |
<FormBuilderInput | |
level={level + 1} | |
ref={i === 0 ? this.firstFieldInput : null} | |
key={field.name} | |
type={field.type} | |
value={value && value[field.name]} | |
onChange={patchEvent => this.handleFieldChange(field, patchEvent)} | |
path={[field.name]} | |
focusPath={focusPath} | |
onFocus={onFocus} | |
onBlur={onBlur} | |
/> | |
))} | |
</div> | |
</Fieldset> | |
) | |
} | |
} | |
export default withDocument(confitionalFields) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react'; | |
import conditionalFields from "../objects/confitionalFields"; | |
export default { | |
title: "Link", | |
name: "link", | |
type: 'object', | |
fields: [ | |
{ | |
title: 'Link Label', | |
name: 'label', | |
type: 'localeString', | |
}, | |
{ | |
title: "Select the link type", | |
name: 'aFieldWithCondition', | |
type: 'object', | |
inputComponent: conditionalFields, | |
fields: [ | |
{ | |
type: 'object', | |
name: 'input', | |
fields: [ | |
{ | |
name: 'condition', | |
title: 'Link Type', | |
type: 'string', | |
options: { | |
list: [ | |
{title: 'Internal, inside this website', value: 'linkInternal'}, | |
{title: 'External, outside this website', value: 'linkExternal'}, | |
{title: 'String', value: 'name'}, | |
], | |
// layout: 'radio', // <-- leave out to make it a dropdown menu | |
}, | |
}, | |
] | |
}, | |
{ | |
type: 'object', | |
name: 'options', | |
fields: [ | |
{ | |
title: 'Page Reference', | |
name: 'linkInternal', | |
type: 'reference', | |
to: [{type: 'landing'}] | |
}, | |
{ | |
name: "linkExternal", | |
title: "URL", | |
type: "url", | |
validation: false | |
}, | |
{ | |
title: 'This one is just to show you can add more options', | |
name: 'name', | |
type: 'string' | |
}, | |
] | |
} | |
] | |
}, | |
] | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hey! Perfect. Currently, I try to wrap my head around building a custom input component, based on a document.
I still wonder if I need to do it in a class component. However, let me describe my problem.
What I try to achieve: Set a document were I define all the fields that need to be rendered (reviewTemplate).
-> create another document (review) where I select the template and the field will be rendered accordingly.
The Input fields shall show up like a "native" sanity ui component.
It works with an HTML input field. but on every change event, it loses the focus and I have to click the input field again to type any further (just one character at a time).
So I played around with the formbuilderinput. But unfortunately, this error message pops up:
func.apply is not a function
.Is this the right place to comment with code? Or where can I ask for help? Do you have documentations for the api to
build such a custom form?
THX. :)