diff --git a/dev/test-studio/preview/FieldGroups.tsx b/dev/test-studio/preview/FieldGroups.tsx
new file mode 100644
index 00000000000..bca2af357cb
--- /dev/null
+++ b/dev/test-studio/preview/FieldGroups.tsx
@@ -0,0 +1,62 @@
+import {Box, Card, Stack, Text} from '@sanity/ui'
+
+import {useQuery} from './loader'
+
+export function FieldGroups(): JSX.Element {
+ const {data, loading, error} = useQuery<
+ {
+ _id: string
+ field1: string | null
+ field2: string | null
+ nested: {
+ field3: string | null
+ field4: string | null
+ field5: string | null
+ nested: {
+ field6: string | null
+ field7: string | null
+ field8: string | null
+ } | null
+ } | null
+ }[]
+ >(
+ /* groq */ `*[_type == "fieldGroupsWithFieldsetsHidden"]{_id,field1,field2,nested{field3,field4,field5,nested{field6,field7,field8}}}`,
+ )
+
+ if (error) {
+ throw error
+ }
+
+ if (loading) {
+ return
Loading...
+ }
+
+ return (
+
+ {data?.map((item) => {
+ return (
+
+
+ {item.field1 || 'N/A'}
+ {item.field2 || 'N/A'}
+ {item.nested && (
+
+ {item.nested.field3 || 'N/A'}
+ {item.nested.field4 || 'N/A'}
+ {item.nested.field5 || 'N/A'}
+ {item.nested.nested && (
+
+ {item.nested.nested.field6 || 'N/A'}
+ {item.nested.nested.field7 || 'N/A'}
+ {item.nested.nested.field8 || 'N/A'}
+
+ )}
+
+ )}
+
+
+ )
+ })}
+
+ )
+}
diff --git a/dev/test-studio/preview/main.tsx b/dev/test-studio/preview/main.tsx
index 2d8b6eac32e..7fb32fa58a7 100644
--- a/dev/test-studio/preview/main.tsx
+++ b/dev/test-studio/preview/main.tsx
@@ -1,14 +1,51 @@
+import {Box, Flex, studioTheme, Tab, TabList, TabPanel, ThemeProvider} from '@sanity/ui'
import {enableVisualEditing} from '@sanity/visual-editing'
-import {Suspense, useEffect} from 'react'
+import {Suspense, useEffect, useState} from 'react'
import {createRoot} from 'react-dom/client'
+import {FieldGroups} from './FieldGroups'
import {useLiveMode} from './loader'
import {SimpleBlockPortableText} from './SimpleBlockPortableText'
function Main() {
+ const [id, setId] = useState('simple')
return (
<>
-
+
+
+
+
+ setId('simple')}
+ selected={id === 'simple'}
+ />
+ setId('nested')}
+ selected={id === 'nested'}
+ />
+
+
+
+ {id === 'simple' && (
+
+
+
+ )}
+
+ {id === 'nested' && (
+
+
+
+ )}
+
+
+
diff --git a/dev/test-studio/schema/debug/fieldGroupsWithFieldsetsHidden.js b/dev/test-studio/schema/debug/fieldGroupsWithFieldsetsHidden.js
new file mode 100644
index 00000000000..66008cb0dac
--- /dev/null
+++ b/dev/test-studio/schema/debug/fieldGroupsWithFieldsetsHidden.js
@@ -0,0 +1,86 @@
+const group = {
+ name: 'group',
+ title: 'Group',
+ default: true,
+}
+const group2 = {
+ name: 'group2',
+ title: 'Group 2',
+}
+const fieldset = {
+ name: 'fieldset',
+ title: 'Fieldset',
+ options: {collapsed: true},
+}
+
+export default {
+ name: 'fieldGroupsWithFieldsetsHidden',
+ title: 'With default groups and collapsed fieldsets',
+ type: 'document',
+ groups: [group, group2],
+ fieldsets: [fieldset],
+ fields: [
+ {
+ name: 'field1',
+ type: 'string',
+ },
+ {
+ name: 'field2',
+ type: 'string',
+ group: group.name,
+ fieldset: fieldset.name,
+ },
+ {
+ name: 'nested',
+ type: 'object',
+ group: group.name,
+ fieldset: fieldset.name,
+ groups: [group, group2],
+ fieldsets: [fieldset],
+ fields: [
+ {
+ name: 'field3',
+ type: 'string',
+ fieldset: fieldset.name,
+ },
+ {
+ name: 'field4',
+ type: 'string',
+ group: group.name,
+ fieldset: fieldset.name,
+ },
+ {
+ name: 'field5',
+ type: 'string',
+ group: group2.name,
+ },
+ {
+ name: 'nested',
+ type: 'object',
+ group: group.name,
+ fieldset: fieldset.name,
+ groups: [group, group2],
+ fieldsets: [fieldset],
+ fields: [
+ {
+ name: 'field6',
+ type: 'string',
+ fieldset: fieldset.name,
+ },
+ {
+ name: 'field7',
+ type: 'string',
+ group: group.name,
+ fieldset: fieldset.name,
+ },
+ {
+ name: 'field8',
+ type: 'string',
+ group: group2.name,
+ },
+ ],
+ },
+ ],
+ },
+ ],
+}
diff --git a/dev/test-studio/schema/index.ts b/dev/test-studio/schema/index.ts
index 51b229532b6..b579539513f 100644
--- a/dev/test-studio/schema/index.ts
+++ b/dev/test-studio/schema/index.ts
@@ -32,6 +32,7 @@ import fieldGroupsDefault from './debug/fieldGroupsDefault'
import fieldGroupsMany from './debug/fieldGroupsMany'
import fieldGroupsWithFieldsets from './debug/fieldGroupsWithFieldsets'
import fieldGroupsWithFieldsetsAndValidation from './debug/fieldGroupsWithFieldsetsAndValidation'
+import fieldGroupsWithFieldsetsHidden from './debug/fieldGroupsWithFieldsetsHidden'
import fieldGroupsWithI18n from './debug/fieldGroupsWithI18n'
import fieldGroupsWithValidation from './debug/fieldGroupsWithValidation'
import fieldsets from './debug/fieldsets'
@@ -252,6 +253,7 @@ export const schemaTypes = [
fieldGroupsWithI18n,
fieldGroupsWithValidation,
fieldGroupsWithFieldsetsAndValidation,
+ fieldGroupsWithFieldsetsHidden,
virtualizationInObject,
virtualizationDebug,
diff --git a/dev/test-studio/structure/constants.ts b/dev/test-studio/structure/constants.ts
index fbbc480ed0d..ffcdea6447a 100644
--- a/dev/test-studio/structure/constants.ts
+++ b/dev/test-studio/structure/constants.ts
@@ -98,6 +98,7 @@ export const DEBUG_FIELD_GROUP_TYPES = [
'fieldGroupsWithValidation',
'fieldGroupsWithFieldsets',
'fieldGroupsWithFieldsetsAndValidation',
+ 'fieldGroupsWithFieldsetsHidden',
]
export const EXTERNAL_PLUGIN_INPUT_TYPES = ['markdownTest', 'muxVideoPost']
diff --git a/packages/sanity/src/core/form/store/utils/__tests__/immutableReconcile.test.ts b/packages/sanity/src/core/form/store/utils/__tests__/immutableReconcile.test.ts
index 041280744ab..90796f09109 100644
--- a/packages/sanity/src/core/form/store/utils/__tests__/immutableReconcile.test.ts
+++ b/packages/sanity/src/core/form/store/utils/__tests__/immutableReconcile.test.ts
@@ -171,3 +171,16 @@ test('returns new array when previous and next has different length', () => {
expect(immutableReconcile(lessItems, moreItems)).not.toBe(lessItems)
})
+
+test('returns latest non-enumerable value', () => {
+ const prev = {enumerable: true}
+ const next = {enumerable: true}
+ Object.defineProperty(next, 'nonEnumerable', {
+ value: {foo: 'bar'},
+ enumerable: false,
+ })
+ // @ts-expect-error Object.defineProperty
+ expect(immutableReconcile(next, prev).nonEnumerable).toBeUndefined()
+ // @ts-expect-error Object.defineProperty
+ expect(immutableReconcile(prev, next).nonEnumerable).toBe(next.nonEnumerable)
+})
diff --git a/packages/sanity/src/core/form/store/utils/getExpandOperations.ts b/packages/sanity/src/core/form/store/utils/getExpandOperations.ts
index be285de9c1c..79acae7bc30 100644
--- a/packages/sanity/src/core/form/store/utils/getExpandOperations.ts
+++ b/packages/sanity/src/core/form/store/utils/getExpandOperations.ts
@@ -107,19 +107,21 @@ function getObjectFieldsetAndFieldGroupOperations(
// Group handling
const schemaField = node.schemaType.fields.find((field) => field.name === fieldName)
const selectedGroupName = node.groups.find((group) => group.selected)?.name
- const defaultGroupName = (node.schemaType.groups || []).find((group) => group.default)?.name
+ const schemaFieldGroup = (schemaField && castArray(schemaField.group)) || []
const inSelectedGroup =
selectedGroupName &&
- (selectedGroupName === ALL_FIELDS_GROUP.name ||
- (schemaField && castArray(schemaField.group).includes(selectedGroupName)))
+ (selectedGroupName === ALL_FIELDS_GROUP.name || schemaFieldGroup.includes(selectedGroupName))
const ops: (ExpandFieldSetOperation | SetActiveGroupOperation)[] = []
if (!inSelectedGroup) {
+ const groupName =
+ node.groups.find((group) => schemaFieldGroup.includes(group.name))?.name ||
+ ALL_FIELDS_GROUP.name
ops.push({
type: 'setSelectedGroup',
path: node.path,
- groupName: defaultGroupName || ALL_FIELDS_GROUP.name,
+ groupName,
})
}
diff --git a/packages/sanity/src/core/form/store/utils/immutableReconcile.ts b/packages/sanity/src/core/form/store/utils/immutableReconcile.ts
index 095c1a84dbc..8e8da4d12f1 100644
--- a/packages/sanity/src/core/form/store/utils/immutableReconcile.ts
+++ b/packages/sanity/src/core/form/store/utils/immutableReconcile.ts
@@ -54,12 +54,14 @@ function _immutableReconcile(
assertType>(previous)
assertType>(next)
- const nextKeys = Object.keys(next)
- let allEqual = Object.keys(previous).length === nextKeys.length
+ const nextKeys = Object.getOwnPropertyNames(next)
+ let allEqual = Object.getOwnPropertyNames(previous).length === nextKeys.length
const result: Record = {}
parents.set(next, result)
for (const key of nextKeys) {
- const nextValue = _immutableReconcile(previous[key], next[key]!, parents)
+ const nextValue = next.propertyIsEnumerable(key)
+ ? _immutableReconcile(previous[key], next[key]!, parents)
+ : next[key]
if (nextValue !== previous[key]) {
allEqual = false
}