sanity-typed-schema-builder icon indicating copy to clipboard operation
sanity-typed-schema-builder copied to clipboard

Make initialValue tests

Open m-bp opened this issue 1 year ago • 10 comments

We need tests for initialValue across the board, mainly considering array and objectNamed({ ... }).ref(). Docs around both are also essential.

Original Ticket: When you use an object as a field type you can pass initial values which will populate some fields.

Docs for reference.

    {
      title: 'Custom type',
      type: 'myCustomObjType',
      name: 'myCustomObjType',
      initialValue: {
        customName: "This is a custom name",
        customString: "This is a custom string"
      }
    },

Personally I use this on my CTA object to fill a hidden field which is then used to hide/show some extra customization options.

    {
      name: "style",
      title: "Style",
      type: s.string({
        initialValue: "primary",
        hidden: ({ parent }) => parent?.type === "link",
        options: {
          list: ["primary", "primary-light", "secondary", "tertiary", "link"],
        },
      }),
    },

At the moment my workaround is to have duplicated objects with different initialValues already defined but it's not ideal.

m-bp avatar Apr 25 '23 12:04 m-bp

I guess it's not documented, but it's essentially the same as the example you have for string:

    {
      name: "obj",
      title: "Obj",
      type: s.object({
        initialValue: { foo: 'bar' },
        fields: [{ name: 'foo', type: s.string() }],
      }),
    },

Is that not working for you?

saiichihashimoto avatar Apr 25 '23 18:04 saiichihashimoto

The idea is to define the object somewhere else so it is reusable and then being able to give it initial values each time it is referenced.

Like so:

    const myObj = s.objectNamed({
        name: 'myObj',
        fields: [{ name: 'foo', type: s.string() }],
      })

    ...
    // Somewhere else

    fields: [
        {
          name: "obj",
          title: "Obj",
          type: myObj,
          initialValues: { ... }
        },
    ]

For example I'd be able to have a CTA object that has properties such as "url", "label", "style", "color"... And, depending on a value I'd pass through initialValues I would be able to hide the color option if the CTA for that component has a hardcoded color that shouldn't be overriden.

    const cta = s.objectNamed({
        name: 'cta',
        fields: [
            {
              name: "hideColor",
              type: s.boolean({
                hidden: true
              }),
            },
            {
              name: "color",
              title: "Color",
              type: s.string({
                initialValue: "red",
                hidden: ({ parent }) => parent?.hideColor === true,
                options: {
                  list: ["red", "blue"],
                },
              }),
            },
        ],
      })

    ...
    // Somewhere else

    fields: [
        {
          name: "cta",
          title: "CTA",
          type: cta,
          initialValues: { hideColor: true, color: 'blue' }
        },
    ]

This is a bit complicated but I'm sure there are more common use cases that would benefit from this feature.

m-bp avatar Apr 26 '23 15:04 m-bp

Maybe I’m misunderstanding but I will say this: the goal of this package is to be as close to just defining the same schema while being typed and producing document types. It’s not really meant to add too many new features on top of that. The zod parsing and faker mocking complicate that a bit, but ultimately I want to just facilitate having typed schemas. I still don’t fully understand your ask, but I’m not sure if this is outside of that.

saiichihashimoto avatar Apr 26 '23 23:04 saiichihashimoto

the goal of this package is to be as close to just defining the same schema while being typed and producing document types

That's the issue, though. I'm trying to migrate my code and use your package but there's some functionality that's lacking and stops me from doing so. I have no way of passing initial values to an object I have previously created.

As I said in the OP, the docs explain this feature pretty clearly.

This is part of the example code they show:


    // This is possible using sanity-typed-schema-builder, you define the object "inline" and give it initial values
    {
      name: 'myObject',
      title: 'My custom input',
      type: 'object',
      initialValue: {
        name: "some name",
        someField: "Some other thing"
      },
      fields: [
        {
            name: 'name',
            title: 'Title',
            type: 'string'
        },
        {
            name: 'someField',
            title: 'Something',
            type: 'string'
        },
      ],
    },

    // This, however, is not possible. The object is defined somewhere else, reused here and given initial values exclusive to this field
    {
      title: 'Custom type',
      type: 'myCustomObjType',
      name: 'myCustomObjType',
      initialValue: {
        customName: "This is a custom name",
        customString: "This is a custom string"
      }
    },

This is a feature from Sanity, not a new one.

I've also ran into a couple other missing features that I've been able to ignore by adding ugly workarounds but this one is a big pain.

m-bp avatar Apr 27 '23 12:04 m-bp

ohhhhh I hear you now.

https://saiichihashimoto.github.io/sanity-typed-schema-builder/#object-named

Look at the .ref() example. .ref() definitely seems misleading (since there's an actual reference type), but I think that's what you want.

    const cta = s.objectNamed({
        name: 'cta',
        fields: [
            {
              name: "hideColor",
              type: s.boolean({
                hidden: true
              }),
            },
            {
              name: "color",
              title: "Color",
              type: s.string({
                initialValue: "red",
                hidden: ({ parent }) => parent?.hideColor === true,
                options: {
                  list: ["red", "blue"],
                },
              }),
            },
        ],
      })

    ...
    // Somewhere else

    fields: [
        {
          name: "cta",
          title: "CTA",
          type: cta.ref(),
          // ^^^^^ does this work?
          initialValues: { hideColor: true, color: 'blue' }
        },
    ]

Can you try that, see if it does it for you?

saiichihashimoto avatar Apr 27 '23 16:04 saiichihashimoto

Okay! That solves part of the issue. For some reason, intellisense refuses to suggest initialValue inside the field definition but once I add it it does work! I thought the property wasn't available because of this.

There is one other use case where this approach does not work, however: array fields.

      // Usually you'd do this:
              {
                name: "field",
                title: "Field",
                type: 'array'
                of: [
                 {
                    type: 'whatever'
                  },
                  {
                    type: 'cta',
                    initialValue: { hideColor: true },
                  },
                ],
              },


        // But I can't do this
              {
                name: "field",
                title: "Field",
                type: s.array({
                  of: [
                    {
                      type: ctaType.ref(),
                      initialValue: { hideColor: true },
                    },
                    whateverType.ref()
                  ],
                }),
              },

I CAN do this, but it is not quite the same.

              {
                name: "field",
                title: "Field",
                type: s.array({
                  of: [whateverType.ref(), ctaType.ref()],
                }),
                initialValue: [{ hideColor: true }],
              },

The first example (the one that does not work) would add an initial value to each new CTA added to the array. The second (the one that works) simply pre-fills the array with a single CTA that has the hideColor prop set to true.

m-bp avatar Apr 27 '23 19:04 m-bp

I should write some tests per type with initialValue, I think. I'm not remembering the syntax for them and it's not obvious, especially since this library splits fields and types in a way that isn't the same as a raw sanity schema.

The second syntax is correct. The reason is that fields contain "field" information (the name of the field and the title), but the type is what should contain everything about the value. An array has no fields, so the type goes directly in there.

I think the initial thinking was that you could reuse the same type in multiple places and have different initial values, since that's likely to be field specific rather than type specific. However, that doesn't really mean anything with arrays. Will have to rethink this a bit.

Also, now that you've run through this, ref() definitely seems like the wrong name, considering there's an actual type called reference. Any thoughts on what you'd name this? It's essentially just using the named object as a type somewhere and allowing you to bring all of the typescript types with it.

saiichihashimoto avatar Apr 27 '23 19:04 saiichihashimoto

Personally I don't think .ref() is that bad. It's still a reference of some sort, even if the name overlaps with Sanity as long as the documentation is good there should be no issue. I'd rather improve the docs than create a breaking change.

However, that doesn't really mean anything with arrays. Will have to rethink this a bit.

Looking forward to it. I love this library. It's made working with Sanity a lot... saner. Heh. I don't even think I'd be using Sanity if this lib didn't exist.

m-bp avatar Apr 27 '23 20:04 m-bp

Love to hear it! When I first started sanity and typescript, I was very sad that something like this didn't exist. I did try to derive types natively from schemas (and I think sanity's getting closer every day to this) but there's just some major roadblocks to it occurring, so I had to make this library. My hope is that deriving types from sanity schemas becomes possible and this library becomes irrelevant, but we'll see.

For this ticket, I think I'll change it to the work I'll need to do.

saiichihashimoto avatar Apr 27 '23 22:04 saiichihashimoto

Keep in mind there's still the issue about the one missing feature

                type: s.array({
                  of: [
                    {
                      type: ctaType.ref(),
                      initialValue: { hideColor: true },
                    },
                    whateverType.ref()
                  ],
                }),

Thank you, man 👍

m-bp avatar Apr 27 '23 23:04 m-bp