πŸ‘€ Our most exciting product launch yet πŸš€ Join us May 8th for Sanity Connect

Trouble with custom input for ecommerce app, resolved with conditional fields.

4 replies
Last updated: Dec 13, 2021
Hi, All. I'm working on setting up an ecommerce app for an artist, and I'm having trouble with a custom input. Each artwork will have two sets of options: size and media. I want the artist to be able to add the available size and media options, which will vary for each artwork, and then dynamically generate another set of inputs where the artist can add the price for each combination of options. For example, if the media options are "paper" and "canvas", and the size options are "large", "medium", and "small", I want to dynamically generate price inputs for "large paper", "large canvas", "medium paper", "medium canvas", etc (like what Shopify does with variants). It's easy enough to create a custom component that loops through the options to create all the combinations. I followed the Twitter text input example in the docs to create a
TextInput
for each combination, but this basically creates a single input that is repeated for each combination: adding a price to one input updates all the inputs. When I think through it this makes perfect sense, but I can't figure out how to create independent inputs. I looked at the instructions for working with objects using the
FormBuilderInput
and
FieldSet
, but couldn't figure it out - and I'm not sure that's what I want to be doing. Any help will be greatly appreciated. Thanks!
Dec 12, 2021, 5:10 AM
Hey User! Just to make sure I'm understanding correctly: do you want the custom input to generate schema for the artist to input values?
Dec 13, 2021, 6:12 PM
Hi, User. Yes, that's right. Here's the snippet of what I'm using to generate a custom input from the options:

return (
    <div>
        {props.parent.mediaOptions.map((media) => {
            return (
                props.parent.sizeOptions.map((size) => {
                    
                    return (
                        <FormField
                            description={type.description}
                            title={type.title}
                            compareValue={compareValue}
                            __unstable_marks={markers}
                            __unstable_presence={presence}
                            inputId={inputId}
                        >
                            <Flex align={'center'}>
                                <Text>{`${media} - ${size} - ${inputId}`}</Text>
                                <Box flex={1}>
                                    <TextInput
                                        id={inputId}
                                        value={value}
                                        readOnly={readOnly}
                                        placeholder={placeholder}
                                        onFocus={onFocus}
                                        onBlur={onBlur}
                                        onChange={handleChange}
                                    />
                                </Box>
                            </Flex>
                        </FormField>
                    )
                })
            )
        })}
    </div>
)
Dec 13, 2021, 6:33 PM
Got it. In this case you probably want to set this up using conditional fields . Take this example of a product:
export default {
  name: 'product',
  title: 'Product',
  type: 'document',
  fields: [
    {
      name: 'title',
      titlte: 'Title',
      type: 'string'
    },
    {
      name: 'mediaOptions',
      title: 'Media Options',
      type: 'array',
      of: [
        { type: 'string'}
      ],
      options: {
        list: [
          { title: 'Paper', value: 'paper'},
          { title: 'Canvas', value: 'canvas'},
        ]
      }
    },
    {
      name: 'paperInfo',
      title: 'Paper Info',
      type: 'array',
      hidden: ({document}) => !document.mediaOptions?.includes('paper'),
      of: [
        {
          name: 'option',
          title: 'Option',
          type: 'object',
          fields: [
            {
              name: 'size',
              title: 'Size',
              type: 'string',
              options: {
                list: [
                  { title: 'Small', value: 'small' },
                  { title: 'Medium', value: 'medium' },
                  { title: 'Large', value: 'large' },
                ]
              }
            },
            {
              name: 'price',
              title: 'Price',
              type: 'number'
            }
          ]
        }
      ]
    },
    {
      name: 'canvasInfo',
      title: 'Canvas Info',
      type: 'array',
      hidden: ({document}) => !document.mediaOptions?.includes('canvas'),
      of: [
        {
          name: 'option',
          title: 'Option',
          type: 'object',
          fields: [
            {
              name: 'size',
              title: 'Size',
              type: 'string',
              options: {
                list: [
                  { title: 'Small', value: 'small' },
                  { title: 'Medium', value: 'medium' },
                  { title: 'Large', value: 'large' },
                ]
              }
            },
            {
              name: 'price',
              title: 'Price',
              type: 'number'
            }
          ]
        }
      ]
    }
  ]
}
I just spun this up quickly, so it's maybe not the best content model, but you can see that you can change the input fields available based off of an editor's selection in the document. You'd probably want to pull the
option
object out into it's own file, but I just copy/pasted for the sake of the example here.
Dec 13, 2021, 7:31 PM
Thanks, User. I'll give this a spin.
Dec 13, 2021, 9:05 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?