Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save georgebutter/2cc45dde23c5ba194c9c6038a3fca82e to your computer and use it in GitHub Desktop.
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
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