Skip to content

Instantly share code, notes, and snippets.

@wildseansy
Created February 4, 2021 21:34
Show Gist options
  • Save wildseansy/afd201a60265fc7bf65e3549077c3e06 to your computer and use it in GitHub Desktop.
Save wildseansy/afd201a60265fc7bf65e3549077c3e06 to your computer and use it in GitHub Desktop.
Sanity: Custom Input for serialized array string
import stringEnumerator from "../../components/stringEnumerator";
export const mySchema = {
//...other fields
fruits: {
name: "fruits",
title: "Favorite Fruits",
type: "string",
inputComponent: stringEnumerator,
options: {
values: [
{ value: "blueberries", title: "Blueberries", initialValue: true },
{ value: "apples", title: "Apples", initialValue: true },
{ value: "bananas", title: "Bananas", initialValue: true },
{ value: "grapes", title: "Grapes", initialValue: false },
{ value: "peaches", title: "Peaches", initialValue: false },
{ value: "pears", title: "Pears", initialValue: false },
],
},
},
}
import * as React from "react";
import { Checkbox, Grid, Flex, Text, Box } from "@sanity/ui";
import PatchEvent, { set, unset } from "part:@sanity/form-builder/patch-event";
import { withDocument } from "part:@sanity/form-builder";
import { SanityProps } from "./types";
import FormField from "part:@sanity/components/formfields/default";
const createPatchFrom = (value) =>
PatchEvent.from(value === "" ? unset() : set(value));
type EnumItem = {
title: string;
value: string;
selected: boolean;
initialValue?: boolean;
};
const deserializeValue = (
value: string = "",
delimiter: string = ","
): string[] => {
return value.split(delimiter);
};
const serializeItems = (items: EnumItem[], delimiter: string = ",") => {
const serialized = items
.filter(({ selected }) => selected)
.map(({ value }) => value)
.join(delimiter);
return serialized;
};
const stringEnumerator = React.forwardRef((props: SanityProps, ref) => {
const { type, level, value } = props;
const options = props.type.options || {};
const enums: EnumItem[] = options.values || [];
const delimiter = options.delimiter ?? ",";
const checkboxes = React.useMemo(() => {
const selected = new Set(
value ? deserializeValue((value as string) ?? "", delimiter) : []
);
return enums.map((item) => {
return {
...item,
selected: selected.has(item.value),
};
});
}, [value]);
React.useEffect(() => {
//First creation:
if (value === undefined) {
props.onChange(
createPatchFrom(
serializeItems(
enums.map((item) => {
return {
...item,
selected: item.initialValue,
};
}),
delimiter
)
)
);
}
}, []);
const toggleChecked = (item: EnumItem, checked: boolean) => {
const serialized = serializeItems(
checkboxes.map((checkbox) => {
if (item.value === checkbox.value) {
return {
...item,
selected: checked,
};
}
return checkbox;
}),
delimiter
);
props.onChange(createPatchFrom(!serialized ? "" : serialized));
};
return (
<FormField label={type.title} description={type.description}>
<Grid columns={[2]} gap={[1, 1, 2, 3]}>
{checkboxes.map((item) => {
return (
<Flex align="center" as="label">
<Checkbox
key={item.value}
checked={item.selected}
onChange={(event) => {
toggleChecked(item, event.target.checked);
}}
/>
<Box marginLeft={3}>
<Text>{item.title}</Text>
</Box>
</Flex>
);
})}
</Grid>
</FormField>
);
});
export default withDocument(stringEnumerator);
@wildseansy
Copy link
Author

The input component above produces:

Screen Shot 2021-02-04 at 1 29 18 PM

Now I can run graphql queries like:

query {
  allPeople(where: { fruits: { matches: "pears|apples" } }) {
    first_name
  }
}

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