sanity-io / sanity

Sanity Studio – Rapidly configure content workspaces powered by structured content
https://www.sanity.io
MIT License
5.27k stars 427 forks source link

Make “All fields” in field groups customizeable #3142

Open bjornwang opened 2 years ago

bjornwang commented 2 years ago

Is your feature request related to a problem? Please describe. Field groups should be great for handling document types with lots of fields. In practice though, the "All fields" tab sabotages this.

As long as all fields show up in the "All fields" tab, there's still the need to organise them in field sets to retain some structure, and maybe even collapse the fields sets to not overwhelm the users of this view.

When appearing in a tab (group), however, we'd typically want different field group settings. For example, it doesn't make sense to have a collapsed field set as the only field set on a tab, or maybe no field set is needed if it duplicates the tab group.

Describe the solution you'd like The needs of organising fields on the "All fields" tab is in conflict with the needs for the individual tabs.

I'd like to either:

  1. Get rid of the "All fields" tab all together (problem solved),
  2. Make the "All fields" tab optional, or
  3. Have individual control of the field sets per group (se below).

If the "All fields" tab is indeed necessary, individual configuration for each tab (group) would allow me to drop field set for tabs where all fields sit in the same field set in the "All fields" tab. I could also keep my field sets for the "All fields" tab which would be total chaos without them. Also, the collapsed setting for field sets should be group specific, so that a field set that it's natural to collapse in the "All fields" tab could be expanded if it get's its own tab.

Describe alternatives you've considered

bjornwang commented 2 years ago

@vicbergquist, am I misunderstanding this feature? Is there maybe some way to hide "All fields" or some other workaround?

thunder87 commented 2 years ago

I totally agree with this, because as of now we have to resort to overwriting css and hiding data-testid='group-tab-all-fields'.. By doing this we also have to make sure that all the fields are "tagged" with a group and that the first group has default: true..

chief-austinc commented 2 years ago

+1

feledori commented 1 year ago

I was just looking for this. +1 for this feature request. 👍🏻

MartCube commented 1 year ago

++

maxdyy commented 1 year ago

+1️⃣

williamiommi commented 1 year ago

Hi, my 2 cents on this. Maybe someone already thing about it but instead of 'All fields', what about a non configurable group (maybe just icon and name are configurable) called 'Other fields' that lives always at the end of all custom groups and contains automatically only the fields that are not mapped in a custom group. As soon as you map every fields within a custom group this tab is not visible anymore and it will reappear only if you add some new fields without a group or remove an old field from a custom group. Doing this you are sure that all fields are visible at least once. Of course if you don't have any custom groups there will be the standard view with no group tabs at all.

Silon commented 1 year ago

UP 👍

TreeMan360 commented 1 year ago

+1

DennisRosen commented 1 year ago

+1, just upgraded our studio to v3 and the "All fields" tab is rather annoying. Ideally all fields not tagged with a group should end up there - not every single field - and the group name should be configurable.

missweizhang commented 1 year ago

+1

filipesmedeiros commented 1 year ago

I don't approve of all these "+1" because they spam, but I do think this would be cool to have. I would imagine something like

groups?: {
  allFieldsGroup?: FieldGroupDefinition // default -> the same as it is right now on v3.9.0
  customGroups?: FieldGroupDefinition[]
  reviewChangesGroup?: string // default -> `allFieldsGroup.hidden === true ? customGroups[0].name : 'all-fields'`
}

I believe there are a few changes to make, but shouldn't be too hard, right? Don't really know

r3nandotdev commented 1 year ago

Or maybe just: groups: [ { name: 'all-fields', hidden: true, }, ]

Mtillmann commented 1 year ago

A workaround is to import custom CSS as described here, then add this to the custom.css:

div[data-testid="field-group-tabs"] > div:has(#all-fields-tab){
    display: none;
}

This will softly remove all "All fields" tabs. Don't forget to set default:true on one of your custom groups

williamiommi commented 1 year ago

I was trying to come out of it by creating a plugin to manage groups in a custom way. However, overriding it doesn't appear to be that simple (at least with my current knowledge). You also need to consider the scenario when you open the side menu with the current modifications (the groups get disabled, and only 'all fields' remains active).

Anyway, while I was experimenting, I believe I've discovered a couple of extra workarounds. Both are done inside the form->components->input in the sanity.config.js (naturally, you can extract the logic into a utility method).

🚨 Keep in mind that these are workarounds of course, I haven't tested every possible scenario. Let me know if you find some weird behavior 😄

Remove the 'All fields' tab

as mentioned in the previous comment, for this scenario, it's better if you have at least on group set as default:true

// sanity.config.js
defineConfig({
  // your configuration
  form: {
    components: {
      input: (props) => {
        if (Array.isArray(props.groups) && props.groups.length > 0) {
          if (props.groups[0].name === 'all-fields') {
            props.groups.shift()
          }
        }
        return props.renderDefault(props)
      },
    },
  },
})

Move 'All Fields' at the end of the list

// sanity.config.js
defineConfig({
  // your configuration
  form: {
    components: {
      input: (props) => {
        if (Array.isArray(props.groups) && props.groups.length > 0) {
          if (props.groups[0].name === 'all-fields') {
            props.groups.push(props.groups.shift())
          }
        }
        return props.renderDefault(props)
      },
    },
  },
})


Move 'All Fields' at the end of the list + minor customization

// sanity.config.js
import { CogIcon } from '@sanity/icons'

defineConfig({
  // your configuration
  form: {
    components: {
      input: (props) => {
        if (Array.isArray(props.groups) && props.groups.length > 0) {
          if (props.groups[0].name === 'all-fields') {
            const el = props.groups.shift()
            el.title = 'Admin'
            el.icon = CogIcon
            props.groups.push(el)
          }
        }
        return props.renderDefault(props)
      },
    },
  },
})

Mtillmann commented 1 year ago

Great work @williamiommi . I understand that the issue has low priority but it makes a huge difference in user acceptance. Your approach with the modified Label, position and Icon is most helpful

kerns commented 1 year ago

This is so needed, ...please someone on the inside flag it as "low hanging fruit" :) Thanks @williamiommi for the stop gap.

williamiommi commented 1 year ago

Hello again 😄, I think I've found another approach for a specific case. I didn't think about it before, but it's pretty easy and, more importantly, it should be "totally legal" 😂 (Okay, we still need to 'hack' the all-fields group, but then we are good to go 😄 ). This solution should address @DennisRosen comment above.

Create a dynamic custom group for unmapped fields

The idea here is to create a dynamic group that contains all the fields that don't have a defined group. Of course, we don't want to do this for every document/object we have. However, since the Sanity Studio is 'just' JavaScript, we can enhance our definitions all at once somehow. Let's start with our magic function:

// some-utility-method-file-blah-blah.ts
import {DocumentDefinition, FieldGroupDefinition, ObjectDefinition} from 'sanity'
import {LinkRemovedIcon} from '@sanity/icons'

// this is the definition of our custom group for unmapped fields
const unmappedFieldsGroup: FieldGroupDefinition = {
  name: 'unmapped-fields',
  icon: LinkRemovedIcon,
  title: 'Unmapped Fields',
}

type DocumentObjectDefinitionType = DocumentDefinition | ObjectDefinition

export const enhancedGroupsDefinition = (definitions: DocumentObjectDefinitionType[]): DocumentObjectDefinitionType[]  => {

  const doMagic = (def: DocumentObjectDefinitionType) => {
    // Check if your document/object has any groups defined.
    if (def.groups && Array.isArray(def.groups) && def.groups.length > 0) {

      // Check if every field has a group defined.
      const everyFieldsHaveGroup = !!def.fields.every(
        (field) =>
          typeof field.group === 'string' || (Array.isArray(field.group) && field.group.length > 0)
      )

      // If some fields don't have a group, we can add our custom group.
      if (!everyFieldsHaveGroup) {
        // Add our custom group at the end of the list.
        def.groups.push(unmappedFieldsGroup)
        // Loop through all fields and add the unmapped group if a group is not defined.
        def.fields = def.fields.map((field) => {
          if (!field.group || (Array.isArray(field.group) && field.group.length === 0))
            field.group = unmappedFieldsGroup.name
          return field
        })
      }
    }
    return def
  }

  return definitions.map(doMagic)
}

Alright, now that we have our function defined, we can use it within our schema definition:

// sanity.config.js
import {enhancedGroupsDefinition} from '../../some-utility-method-file-blah-blah'

defineConfig({
  // your configuration
 schema: {
    types: [
      ...enhancedGroupsDefinition([Document1, Document2]),
    ],
  },
  form: {
    components: {
      input: (props) => {
        if (Array.isArray(props.groups) && props.groups.length > 0) {
          if (props.groups[0].name === 'all-fields') {
            props.groups.shift()
          }
        }
        return props.renderDefault(props)
      },
    },
  },
})

TIP 1: Of course, you can also decide to 'enhance' only specific documents/objects and not every one. TIP 2: You can also play around with the function and decide to set a default group if you notice that none has been defined.

Designer023 commented 1 year ago

@williamiommi This is fantastic. Works like a charm Just a note that there is a type between unmappedFieldGroup and unmappedFieldsGroup... Note the extra s on Fields.

williamiommi commented 1 year ago

@williamiommi This is fantastic. Works like a charm Just a note that there is a type between unmappedFieldGroup and unmappedFieldsGroup... Note the extra s on Fields.

👀 Spotted 👀 Fixed here and also on the Sanity Exchange. Thanks 😃

igalil commented 10 months ago

Hello, Does anyone knows if this will be included in the near future version ? It's been 2 years almost :/ Thanks :)

oscarcarlstrom commented 4 months ago

🆙

NisugaJ commented 3 months ago

+1

ErikHen commented 1 month ago

This was the first thing I wanted to do when creating my first Sanity schema type...

tarjep commented 2 weeks ago

+1