React - How to Display a List of Schemas/Content Types but Only Allow the User to Select One
13 replies
Last updated: Jun 29, 2022
W
Hey đ Just a quick question, is it possible to display a list of schemas/content types but only allow the user to select one?
May 6, 2022, 3:32 PM
K
Like radio inputs somewhat?
May 6, 2022, 3:47 PM
W
Just made a quick example and attached a screenshot (ignore the names they make little sense đ). Hoping to allow a user to select one, and only one, of the two options (in this example, Navigation Item or Link). I am just using an array type at the moment so can limit it to 1 using validation but this catches the errors after theyâve added too much content (they assume they did it by accident) which doesnât seem great. Any help is super appreciated đ
May 6, 2022, 3:56 PM
K
Ah! I see.
May 6, 2022, 3:57 PM
K
We have exactly the same thing at work and we solved it with a
.max(1)validation rule.
May 6, 2022, 3:57 PM
K
It may seem annoying that you can effectively have several items in a draft, but itâs actually pretty handy for editors, rather than having to delete their item first before creating a new one.
May 6, 2022, 3:58 PM
K
And you ensure that a production document canât have more than 1 with the validation rule. Win-win. đ
May 6, 2022, 3:58 PM
W
Yeh good points! Thanks for the quick reply đ. It would be cool to see how others have dealt with it
May 6, 2022, 4:02 PM
I
May 6, 2022, 5:11 PM
S
user A
has previously helped me with a custom array part which should do what youâre after! Once the array has the maximum number of items (set using validation: (Rule) => Rule.max(x)), the âAdd itemâŠâ button is removed:
May 6, 2022, 5:47 PM
S
Hereâs the
You need to add this file to your
And then you can use this by setting
LimitArray.jspart:
import React from "react"; import { isReferenceSchemaType } from "@sanity/types"; import { AddIcon } from "@sanity/icons"; import { Button, Grid, Menu, MenuButton, MenuItem } from "@sanity/ui"; import { useId } from "@reach/auto-id"; const LimitArray = React.forwardRef((props, ref) => { const { type, readOnly, children, onCreateValue, onAppendItem, value } = props; const menuButtonId = useId(); const insertItem = React.useCallback( (itemType) => { const item = onCreateValue(itemType); onAppendItem(item); }, [onCreateValue, onAppendItem] ); const handleAddBtnClick = React.useCallback(() => { insertItem(type.of[0]); }, [type, insertItem]); if (readOnly) { return null; } const maxLength = type.validation[0]._rules.find((rule) => rule.flag === "max"); if (maxLength && value && value.length >= maxLength.constraint) { return null; } return ( <Grid gap={1} style={{ gridTemplateColumns: "repeat(auto-fit, minmax(100px, 1fr))" }} ref={ref} > {type.of.length === 1 ? ( <Button icon={AddIcon} mode="ghost" onClick={handleAddBtnClick} text="Add item" /> ) : ( <MenuButton button={<Button icon={AddIcon} mode="ghost" text="Add itemâŠ" />} id={menuButtonId || ""} menu={ <Menu> {type.of.map((memberDef, i) => { const referenceIcon = isReferenceSchemaType(memberDef) && (<http://memberDef.to|memberDef.to> || []).length === 1 && <http://memberDef.to[0].icon;|memberDef.to[0].icon;> const icon = memberDef.icon || memberDef.type?.icon || referenceIcon; return ( <MenuItem key={i} text={memberDef.title || memberDef.type?.name} onClick={() => insertItem(memberDef)} icon={icon} /> ); })} </Menu> } /> )} {children} </Grid> ); }); export default LimitArray;
partsin your
sanity.jsonas follows:
{ "implements": "part:@sanity/form-builder/input/array/functions", "path": "./schemas/parts/LimitArray.js" }
validationon an array field:
{ title: "Content", name: "content", type: "array", of: [{ type: "img" }, { type: "vid" }, { type: "word" }], validation: (Rule) => Rule.max(2), },
May 6, 2022, 5:48 PM
W
Wow, thanks for sharing đ
May 6, 2022, 6:45 PM
W
Just tried it and it works perfectly đ Thanks again!
May 6, 2022, 7:37 PM
S
2 months later, still coming in clutch. Thanks
user A
and user P
Jun 29, 2022, 8:02 PM
Sanityâ build remarkable experiences at scale
Sanity is a modern headless CMS that treats content as data to power your digital business. Free to get started, and pay-as-you-go on all plans.