Skip to content

Instantly share code, notes, and snippets.

@bjoerge
Created December 21, 2018 12:46
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save bjoerge/c3a1aaa49dbd608bd341666ff70a128e to your computer and use it in GitHub Desktop.
Save bjoerge/c3a1aaa49dbd608bd341666ff70a128e to your computer and use it in GitHub Desktop.
Example of a custom sanity input that allows the user to select from a list of values retrieved from an external API
import React from 'react'
import SearchableSelect from 'part:@sanity/components/selects/searchable'
import FormField from 'part:@sanity/components/formfields/default'
import {PatchEvent, set, unset} from 'part:@sanity/form-builder/patch-event'
const STREET_NAMES = [
'Thorvald Meyers gate',
'Champs-Élysées',
'Lombard Street',
'Abbey Road',
'Fifth Avenue',
'Santa Monica Boulevard',
'Ginza',
'Beale Street',
'Bourbon Street'
]
const delay = ms => new Promise(resolve => setTimeout(resolve, ms))
const searchExternalAPI = keyword =>
delay(100).then(() =>
STREET_NAMES.filter(name => name.toLowerCase().startsWith(keyword.toLowerCase()))
)
export default class ExternalDataInput extends React.Component {
state = {
hits: [],
isFetching: false,
inputValue: null
}
_lastQuery = null
inputRef = React.createRef()
handleFocus = () => {
if (this._lastQuery) {
this.search(this._lastQuery)
this.setState({inputValue: this._lastQuery})
}
if (this.props.onFocus) {
this.props.onFocus()
}
}
handleBlur = () => {
this.setState({inputValue: null})
if (this.props.onBlur) {
this.props.onBlur()
}
}
handleChange = hit => {
const {onChange} = this.props
onChange(PatchEvent.from(set(hit)))
this._lastQuery = null
this.setState({inputValue: null})
}
handleClear = () => {
this.props.onChange(PatchEvent.from(unset()))
}
handleSearch = query => {
this.search(query)
}
handleOpen = () => {
this.search('')
}
search = query => {
this._lastQuery = query
this.setState({inputValue: query, isFetching: true})
searchExternalAPI(query).then(hits => {
if (this._lastQuery === query) {
this.setState({hits, isFetching: false})
}
})
}
focus() {
if (this.inputRef) {
this.inputRef.current.focus()
}
}
render() {
const {type, value, level, markers, readOnly, onBlur, onSearch} = this.props
const {hits, inputValue, isFetching} = this.state
const validation = markers.filter(marker => marker.type === 'validation')
const errors = validation.filter(marker => marker.level === 'error')
return (
<FormField markers={markers} label={type.title} level={level} description={type.description}>
<SearchableSelect
placeholder="Type to search…"
title={inputValue}
customValidity={errors.length > 0 ? errors[0].item.message : ''}
onOpen={this.handleOpen}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
onSearch={this.handleSearch}
onChange={this.handleChange}
onClear={this.handleClear}
openItemElement={this.renderOpenItemElement}
value={value}
inputValue={inputValue === null ? value : inputValue}
renderItem={this.renderHit}
isLoading={isFetching}
items={hits}
ref={this.inputRef}
readOnly={readOnly}
/>
</FormField>
)
}
}
@georgebutter
Copy link

Thank you so much for making this gist public. I have iterated on it and create a version using typescript and hooks.
https://gist.github.com/ButsAndCats/2cc45dde23c5ba194c9c6038a3fca82e

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment