Created
July 13, 2021 20:09
-
-
Save georgebutter/2cc45dde23c5ba194c9c6038a3fca82e to your computer and use it in GitHub Desktop.
Example of a custom, typescript, sanity input that allows the user to select from a list of values retrieved from an external API
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 * as React from "react"; | |
import FormField from 'part:@sanity/components/formfields/default' | |
import SearchableSelect from 'part:@sanity/components/selects/searchable' | |
import {PatchEvent, set, unset} from 'part:@sanity/form-builder/patch-event' | |
const externalAPI = async (query: string) => { | |
return ["foo", "bar"] | |
} | |
export const MeditationInput: React.FC<Props> = (props) => { | |
const { | |
level, | |
markers, | |
onBlur, | |
onChange, | |
onFocus, | |
readOnly, | |
type, | |
value, | |
} = props; | |
const [lastQuery, setLastQuery] = React.useState<string>(); | |
const [inputValue, setInputValue] = React.useState<string>(); | |
const [fetching, setFetching] = React.useState<boolean>(false); | |
const [results, setResults] = React.useState<Array<Result>>(); | |
const inputRef = React.createRef<HTMLInputElement>(); | |
const handleOpen = React.useCallback(() => { | |
search(''); | |
}, []) | |
const handleFocus = React.useCallback(() => { | |
if (lastQuery) { | |
search(lastQuery); | |
setInputValue(lastQuery); | |
} | |
onFocus?.(); | |
}, [lastQuery]) | |
const handleBlur = React.useCallback(() => { | |
setInputValue(''); | |
onBlur?.(); | |
}, []) | |
const handleSearch = React.useCallback((query: string) => { | |
search(query) | |
}, []) | |
const handleChange = React.useCallback((hit) => { | |
onChange?.(PatchEvent.from(set(hit))); | |
}, [onChange]) | |
const handleClear = React.useCallback(() => { | |
onChange?.(PatchEvent.from(unset())) | |
}, [onChange]) | |
const search = React.useCallback(async (query: string) => { | |
setLastQuery(query); | |
setInputValue(query); | |
setFetching(true); | |
const res = await externalAPI(query); | |
setFetching(false); | |
setResults(res); | |
}, [lastQuery]) | |
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={handleOpen} | |
onFocus={handleFocus} | |
onBlur={handleBlur} | |
onSearch={handleSearch} | |
onChange={handleChange} | |
onClear={handleClear} | |
openItemElement={(o) => <p>{o}</p>} | |
value={value} | |
inputValue={inputValue === undefined ? value : inputValue} | |
renderItem={(v) => <p>{v}</p>} | |
isLoading={fetching} | |
items={results} | |
ref={inputRef} | |
readOnly={readOnly} | |
/> | |
</FormField> | |
) | |
} | |
type Props = { | |
level: number; | |
markers: Array<Marker>; | |
readOnly?: boolean | |
type: { | |
description: string | |
title: string | |
} | |
value?: undefined | |
onBlur?: () => void | |
onChange?: (event: any) => void | |
onFocus?: () => void | |
} | |
type Marker = { | |
type: string | |
level: string | |
item: { | |
message: string | |
} | |
} | |
type Result = { | |
} | |
export default MeditationInput; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment