React - How to Display a List of Schemas/Content Types but Only Allow the User to Select One
16 replies
Last updated: May 9, 2024
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
N
Sorry for bumping this, but have any of you tried implementing this with v3? Finding it quite nice to have a media type array where the user can add either image or video, but would be extra nice to have the button to be removed if the max length is reached. Now that i think of it, would be even nicer to have the text of the button change to āremove mediaā or āchange mediaā.
May 4, 2024, 10:22 PM
S
Iāve actually been trying to make a plugin that does the same thing in V3! I havenāt had a chance to publish it yet, but here it is:
Then add this component to your sanity.config file via the form option:
// Array form component export const LimitArray = (props) => { const { schemaType: { validation = [] }, } = props; const validationRules = validation .flatMap((rule) => rule._rules) .find((rule) => rule.flag === "max"); const arrayLimit = validationRules ? validationRules.constraint : undefined; const arrayHasMaxElements = members.length >= arrayLimit ? true : false; return arrayHasMaxElements ? props.renderDefault({ ...props, arrayFunctions: () => null }): props.renderDefault(props); };
export default defineConfig({ ... form: { components: { input: (props) => { if (props.schemaType.name === "array") { return LimitArray(props) } return props.renderDefault(props); }, }, }, });
May 9, 2024, 3:26 PM
N
Niceee Simon! Gonna take it for a spin, thanks!
May 9, 2024, 3:43 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.