✨Discover storytelling in the AI age with Pixar's Matthew Luhn at Sanity Connect, May 8th—register now

How to create a custom input component in Sanity for updating fields based on dropdown selection

5 replies
Last updated: Oct 20, 2022
hello, I had a quick search but couldn't find what I was looking for,I have two objects (displayOptions & management)
In the management object I have a dropdown list with statuses and if a status equals a value then it would either check or uncheck a boolean field in my displayOptions field, for example if the status equals 'extended' then the extended checkbox would be true,
I've included a simplified version of my objects, is there a way to do this?

displayOptions.js:

export default {
  name: 'displayOptions',
  title: 'Display Options',
  type: 'object',
  fields: [
    { 
      name: 'shortlist',
      title: 'Shortlist',
      type: 'boolean',
      initialValue: false,
      options: { layout: 'checkbox' },
    },
    { 
      name: 'extended',
      title: 'Extended',
      type: 'boolean',
      description: 'TODO:',
      initialValue: false,
      options: { layout: 'checkbox' },
    },
    { 
      name: 'omitted',
      title: 'Omitted',
      type: 'boolean',
      initialValue: false,
      options: { layout: 'checkbox' },
    },
  ],
}
management.js:

export default {
  name: 'management',
  title: 'Management',
  type: 'object',
  fields: [
    { 
      name: 'accessId', 
      title: 'Access ID', 
      type: 'string' 
    },
    { 
      name: 'hotelStatusType', 
      title: 'Hotel Status Type',
      type: 'tag',
      options: {
        predefinedTags: [
          {label: 'Extended, value: 'extended'},
          {label: 'Another Status', value: 'anotherStatus'},],
      }
    },
  ],
}
Oct 13, 2022, 9:44 AM
Hey
user Q
! Assuming these are sibling fields in the same document and not objects within an array (but feel free to correct me if I have that wrong!), you'll need to create a custom input component that you wrap in a withDocument HOC . I believe you could make either the
hotelStatusType
or
extendedFields
a custom component that checks the value of the sibling field on the document then patches itself.
Oct 13, 2022, 5:48 PM
Hi
user M
, they are objects within the same document, I'm fairly new to sanity and custom input components but those links look promising, so I'll look into them, thank you! 🙂
Oct 14, 2022, 7:17 PM
You're welcome! Just as a warning, custom input components seem super complex when you first approach them (and they kind of are 🙂 ) but once you start working with them you find that a great deal of it can be copy/pasted from the boilerplate props in the docs.
Oct 14, 2022, 8:44 PM
user M
Thank you for pointing me in the right direction! I got something working so I'm posting a simplified version here in case it helps anyone else - using document props I get my hotelStatusDropdown and with useEffect, set my checkbox if the title matches 🙂

import React, { useEffect } from 'react';
import { withDocument } from 'part:@sanity/form-builder';
import { Checkbox } from '@sanity/ui';
import PatchEvent, { set, unset } from '@sanity/form-builder/PatchEvent';
import { useState } from 'react';
import styles from '../../brandStyling/styles/styles.css';

const StatusNotification = React.forwardRef((props, ref) => {
  const {
    value, // Current field value
    onChange, // Method to handle patch events
    type,
  } = props;

  // The status dropdown that determines display options
  const hotelStatusType = props.document.management.hotelStatusType;

  // Set the conditions where each display option checkbox will be true or not
  let hotelInGuide, hotelShortlist;
  if (
    hotelStatusType === 'old' ||
    hotelStatusType === 'New'
  ) {
    hotelInGuide = true;
  }
  let hotelOmitted = hotelStatusType === 'Omitted' ? true : false;

  // Hooks
  const [inputReadOnly, setInputReadOnly] = useState(false);

  // Functions
  const truePatchEvent = () => {
    onChange(PatchEvent.from('true' ? set(true) : set(false)));
    setInputReadOnly(true);
  };
  const falsePatchEvent = () => {
    onChange(PatchEvent.from('false' ? set(false) : set(true)));
    setInputReadOnly(true);
  };

  /** On page load & anytime the hotel status dropdown is changed we need to
   * update display option checkboxes
   * set to readOnly if applicable
   */
  useEffect(() => {
    console.log('title:', type.title, 'statusType:', hotelStatusType);
    // Check to see what checkbox is current
    // In Guide Checkbox
    if (type.title === 'In Guide') {
      if (hotelInGuide) {
        console.log('in guide');
        truePatchEvent();
      } else if (hotelOmitted) {
        console.log('in guide gets unchecked - omitted!');
        // uncheck in guide as the hotel is omitted
        falsePatchEvent();
      } else {
        console.log('not in guide');
        setInputReadOnly(false);
      }
    }

    // Omitted Checkbox - should always be readonly?
    if (type.title === 'Omitted') {
      if (hotelOmitted) {
        console.log('omitted');
        truePatchEvent();
      } else {
        console.log('not omitted');
        falsePatchEvent();
      }
    }
  }, [hotelStatusType]);

  // Creates a change handler for patching data - this visualises whether the checkbox is 'checked' or not
  const handleChange = React.useCallback(
    // useCallback will help with performance
    (event) => {
      console.log('event', event.target.checked, event.target);
      const inputValue = event.target.checked; // get current value
      onChange(PatchEvent.from(inputValue ? set(true) : set(false)));
    },
    [onChange]
  );

  return (
    <div>
      <label className={inputReadOnly ? styles.labelReadOnly : styles.label}>
        <Checkbox
          checked={value}
          onChange={handleChange}
          readOnly={inputReadOnly}
        />
        <span className={styles.checkboxText}>{type.title}</span>
      </label>
    </div>
  );
});

export default withDocument(StatusNotification);
Oct 20, 2022, 9:26 AM
Thanks so much for sharing your solution
user Q
!
Oct 20, 2022, 3:11 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.

Was this answer helpful?