Using a custom input component to solve a schema problem in Sanity

3 replies
Last updated: Sep 20, 2022
Hi I am working on a schema and got stuck on this. Is there any simple way to use one field value as an index in the next field in sanity?
    {
      name: "tagType",
      title: "Tag Type",
      type: "string",
      options: {
        list: [
          { title: "Activity", value: "activity" },
          { title: "Equipment", value: "equipment" },
          { title: "Category", value: "category" },
          { title: "Duration", value: "duration" },
        ],
      },
    },
    {
      name: "tagText",
      title: "Tag Text",
      type: "string",
      options: {
        list: tagTextOptions[document?.tagType],   <-------- here
      },
    },

    /* this is const is in the same document
    const tagTextOptions = {
      category: [
        { title: "Strength", value: "strength" },
        { title: "Cardio", value: "Cardio" },
      ],
      equipment: [{ title: "Dumbbell", value: "dumbbell" }],
      activity: [
        { title: "Walking", value: "walking" },
        { title: "Running", value: "running" },
      ],
    }
    */
Sep 15, 2022, 12:08 PM
You'd have to use a custom input component here, since your schema won't be aware of the current
document.tagType
when the Studio compiles. Something like this should work:

import ConditionalSelect from '../components/ConditionalSelect';

//other document schema
{
      name: 'tagType',
      title: 'Tag Type',
      type: 'string',
      options: {
        list: [
          { title: 'Activity', value: 'activity' },
          { title: 'Equipment', value: 'equipment' },
          { title: 'Category', value: 'category' },
          { title: 'Duration', value: 'duration' },
        ],
      },
    },
    {
      name: 'tagText',
      title: 'Tag Text',
      type: 'string',
      options: {
        list: [],
        tagTextOptions: {
          category: [
            { title: 'Strength', value: 'strength' },
            { title: 'Cardio', value: 'Cardio' },
          ],
          equipment: [{ title: 'Dumbbell', value: 'dumbbell' }],
          activity: [
            { title: 'Walking', value: 'walking' },
            { title: 'Running', value: 'running' },
          ],
        },
      },
      inputComponent: ConditionalSelect,
    },

./components/ConditionalSelect

import React from 'react';
import { Card, Stack, Select } from '@sanity/ui';
import { FormField } from '@sanity/base/components';
import PatchEvent, { set, unset } from '@sanity/form-builder/PatchEvent';
import { withDocument } from 'part:@sanity/form-builder';
import { useId } from '@reach/auto-id';

const ConditionalSelect = React.forwardRef((props, ref) => {
  const {
    type, // Schema information
    value, // Current field value
    readOnly, // Boolean if field is not editable
    markers, // Markers including validation rules
    presence, // Presence information for collaborative avatars
    compareValue, // Value to check for "edited" functionality
    onFocus, // Method to handle focus state
    onBlur, // Method to handle blur state
    onChange, // Method to handle patch events,
    document, //Document values
  } = props;

  const inputId = useId();

  const tagTextOptions = type.options.tagTextOptions;

  // Creates a change handler for patching data
  const handleChange = React.useCallback(
    // useCallback will help with performance
    event => {
      const inputValue = event.currentTarget.value; // get current value
      // if the value exists, set the data, if not, unset the data
      onChange(PatchEvent.from(inputValue ? set(inputValue) : unset()));
    },
    [onChange]
  );

  return (
    <FormField
      description={type.description} // Creates description from schema
      title={type.title} // Creates label from schema title
      __unstable_markers={markers} // Handles all markers including validation
      __unstable_presence={presence} // Handles presence avatars
      compareValue={compareValue} // Handles "edited" status
      inputId={inputId}
    >
      <Card padding={0}>
        <Stack>
          <Select
            id={inputId}
            fontSize={2}
            padding={[2, 2, 3]}
            space={[3, 3, 4]}
            value={value} // Current field value
            readOnly={readOnly} // If "readOnly" is defined make this field read only
            onFocus={onFocus} // Handles focus events
            onBlur={onBlur} // Handles blur events
            ref={ref}
            onChange={handleChange} // A function to call when the input value changes
          >
            <option value={'---'}>---</option>
            {tagTextOptions[document.tagType] &&
              tagTextOptions[document.tagType].map(({ value, title }) => (
                <option key={value} value={value}>
                  {title}
                </option>
              ))}
          </Select>
        </Stack>
      </Card>
    </FormField>
  );
});

export default withDocument(ConditionalSelect);
Sep 19, 2022, 10:44 PM
Thank you so much for this example. Really appreciated.
Sep 20, 2022, 9:21 AM
You're welcome!
Sep 20, 2022, 3:07 PM

Sanity– build remarkable experiences at scale

The Sanity Composable Content Cloud is the headless CMS that treats content as data to power your digital business. Free to get started, and pay-as-you-go on all plans.

Was this answer helpful?