Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
a65f6f6
feat(structure): add incoming references inspector
pedrobonamin Aug 13, 2025
919f85f
feat(structure): deep link to reference from incoming references view
pedrobonamin Aug 13, 2025
484022b
feat(structure): create new incoming references, wip
pedrobonamin Aug 13, 2025
b14518f
feat(core): add referencedBy document schema option
pedrobonamin Aug 19, 2025
4ec6830
chore(core): refactor initial value creation to use params.referencedBy
pedrobonamin Aug 19, 2025
2315c8e
chore(core): remove menu options for incoming reference document preview
pedrobonamin Aug 19, 2025
10c77f3
fix(structure): typo
pedrobonamin Aug 19, 2025
3763c21
fix(structure): remove unnecessary export
pedrobonamin Aug 19, 2025
ba44fb4
chore(structure): cleanup
pedrobonamin Aug 19, 2025
2313fb9
fix(structure): references should have stregthenOnPublish
pedrobonamin Aug 19, 2025
d9dea82
feat: add incoming references array input, wip
pedrobonamin Aug 26, 2025
ba7383f
feat: document incoming references types and support filter query
pedrobonamin Sep 25, 2025
9b6117e
fix: disable support for more than one type in incoming references
pedrobonamin Sep 25, 2025
3f06bb5
feat: support creation allowed, update types docs
pedrobonamin Sep 26, 2025
ec83237
fix: update exports tags
pedrobonamin Sep 26, 2025
8027cd2
chore: improvementes to IncomingReferencesPanel
pedrobonamin Sep 26, 2025
c4ba67d
chore: move IncomingReferencesInput to structure
pedrobonamin Sep 26, 2025
fe47cd8
chore: export and use ReferenceAutoComplete in incomingReferenceInput
pedrobonamin Sep 26, 2025
aff6070
feat(structure): use Action components for referenced document actions
pedrobonamin Sep 29, 2025
f991287
feat(structure): support dialogs in reference documents actions
pedrobonamin Sep 29, 2025
f9d5d69
chore(core): fix sapp-3146 spacing for cross dataset references
pedrobonamin Oct 1, 2025
11eabe3
feat(structure): support cross dataset references in incoming referen…
pedrobonamin Oct 1, 2025
78ebfed
fix(core): observeFields use raw perspective, update crossDatasetInco…
pedrobonamin Oct 1, 2025
0c9ce0f
feat(structure): support filter resolver function for incoming refere…
pedrobonamin Oct 2, 2025
36de19a
fix: update pnpm-lock file
pedrobonamin Oct 2, 2025
4f8a90b
chore: small tweaks to cleanup code changes
pedrobonamin Oct 2, 2025
af91cdc
chore(structure): add defineIncomingReferenceField function
pedrobonamin Oct 2, 2025
c787a5d
chore(structure) reuse getIncomingReferences in incomingReferences pane
pedrobonamin Oct 2, 2025
91cc9fb
feat(structure): add crossDataset incoming refs to references inspector
pedrobonamin Oct 2, 2025
560a331
fix(structure): updates to cross dataset resolvers functions
pedrobonamin Oct 7, 2025
5391f15
fix(structure): make filter resolver stable while document changes
pedrobonamin Oct 7, 2025
6f9c641
chore(structure): replace linkedDocument for document in actions desc…
pedrobonamin Oct 13, 2025
c3c5121
fix: linting
pedrobonamin Oct 13, 2025
b7028ef
chore: remove old readme
pedrobonamin Oct 13, 2025
154df6d
chore(structure): add isIncomingReferenceCreation helper
pedrobonamin Oct 13, 2025
b46331d
fix: cleanup
pedrobonamin Oct 13, 2025
36adefc
fix: remove duplicated getReferencePaths
pedrobonamin Nov 13, 2025
b16e791
fix: add missing translations
pedrobonamin Nov 13, 2025
0a18776
fix: miscelaneous changes, addressing PR comments
pedrobonamin Nov 13, 2025
13245b8
feat(types): add initial defineDecoration type helper
pedrobonamin Nov 17, 2025
b79fe9e
feat(core): move incomingReferences input from structure to core
pedrobonamin Nov 17, 2025
11e2c07
feat(schema): add internal form decorator schema type
pedrobonamin Sep 30, 2025
f22cc18
chore(test-studio): use internal form decorator in author
pedrobonamin Sep 30, 2025
745dd21
fix(schema): support null in schema type
pedrobonamin Nov 18, 2025
e1fca8b
chore(types): rename sanity.internalFormDecorator to internalFormDeco…
pedrobonamin Nov 18, 2025
33caa52
feat(core): add decorationField
pedrobonamin Nov 18, 2025
0c7b4b4
fix(core): update decoration field to work with react compiler
pedrobonamin Nov 18, 2025
3fe3d36
chore: cleanup
pedrobonamin Nov 18, 2025
f37ca05
chore(core): rename to internalFormDecorator to formDecoration
pedrobonamin Nov 18, 2025
c0226f3
chore(test-studio): add decoration debug schema
pedrobonamin Nov 18, 2025
bb6df20
Merge branch 'internal-form-decorator-type' into incoming-references/…
pedrobonamin Nov 18, 2025
dd6e8f8
feat(schema): add incomingReferences type
pedrobonamin Nov 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions dev/test-studio/components/IncomingReferencesActions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {TrashIcon} from '@sanity/icons'
import {type IncomingReferenceAction} from '@sanity/types'
import {getDraftId} from 'sanity'

export const RemoveReferenceAction: IncomingReferenceAction = ({document, getClient}) => {
const client = getClient({apiVersion: '2025-10-01'})
const handleRemoveReference = async () => {
if (document._type === 'author') {
await client.createOrReplace({
...document,
_id: getDraftId(document._id),
bestFriend: undefined,
})
}
if (document._type === 'book') {
await client.createOrReplace({
...document,
_id: getDraftId(document._id),
author: undefined,
})
}
}

return {
label: 'Remove reference',
icon: TrashIcon,
tone: 'critical',
onHandle: handleRemoveReference,
}
}
90 changes: 78 additions & 12 deletions dev/test-studio/schema/author.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import {UserIcon as icon} from '@sanity/icons'
import {type StringRule} from '@sanity/types'
import {defineField, defineType} from 'sanity'
import {defineDecoration, defineField, defineType, isIncomingReferenceCreation} from 'sanity'

import {AudienceSelectInput} from '../components/AudienceSelectInput'
import {RemoveReferenceAction} from '../components/IncomingReferencesActions'

// Generic decide field implementation that works for all types
const defineLocalDecideField = (config: any) => {
Expand Down Expand Up @@ -103,6 +104,55 @@ export default defineType({
},
validation: (rule: StringRule) => rule.required(),
}),
defineDecoration({
type: 'incomingReferences',
name: 'incomingReferencesDesigner',
title: 'Incoming references with the same role',
options: {
onLinkDocument: (document, reference) => {
return {
...document,
bestFriend: reference,
}
},
filter: (context) => {
return {
filter: `role == $role`,
filterParams: {role: (context.document.role as string) || ''},
}
},
actions: [RemoveReferenceAction],
types: [{type: 'author'}],
},
}),
defineDecoration({
type: 'incomingReferences',
name: 'booksCreated',
title: 'Books created by this author',
options: {
onLinkDocument: (document, reference) => {
return {
...document,
author: reference,
}
},
actions: [RemoveReferenceAction],
types: [
{type: 'book'},
{
type: 'book',
dataset: 'test-us',
title: 'Book in test-us dataset',
studioUrl: ({id, type}) => {
return type ? `/us/intent/edit/id=${id};type=${type}` : null
},
preview: {
select: {title: 'title', media: 'coverImage', subtitle: 'publicationYear'},
},
},
],
},
}),
{
name: 'bestFriend',
title: 'Best friend',
Expand All @@ -126,7 +176,6 @@ export default defineType({
type: 'reference',
to: [{type: 'author'}],
}),

{
name: 'role',
title: 'Role',
Expand Down Expand Up @@ -193,15 +242,32 @@ export default defineType({
},
],

initialValue: () => ({
name: 'Foo',
bestFriend: {_type: 'reference', _ref: 'foo-bar'},
image: {
_type: 'image',
asset: {
_ref: 'image-8dcc1391e06e4b4acbdc6bbf2e8c8588d537cbb8-4896x3264-jpg',
_type: 'reference',
initialValue: (params) => {
if (isIncomingReferenceCreation(params)) {
return {
name: 'Foo',
bestFriend: params.reference,
image: {
_type: 'image',
asset: {
_ref: 'image-8dcc1391e06e4b4acbdc6bbf2e8c8588d537cbb8-4896x3264-jpg',
_type: 'reference',
},
},
role: params.from.fieldName === 'incomingReferencesDesigner' ? 'designer' : undefined,
}
}

return {
name: 'Foo',
bestFriend: {_type: 'reference', _ref: 'foo-bar'},
image: {
_type: 'image',
asset: {
_ref: 'image-8dcc1391e06e4b4acbdc6bbf2e8c8588d537cbb8-4896x3264-jpg',
_type: 'reference',
},
},
},
}),
}
},
})
14 changes: 9 additions & 5 deletions dev/test-studio/schema/book.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {BookIcon} from '@sanity/icons'
import {type Rule} from '@sanity/types'
import {defineType, type Rule} from '@sanity/types'
import {isIncomingReferenceCreation} from 'sanity'

function formatSubtitle(book: any) {
return [
Expand All @@ -12,7 +13,7 @@ function formatSubtitle(book: any) {
.join(' ')
}

export default {
export default defineType({
name: 'book',
type: 'document',
title: 'Book',
Expand Down Expand Up @@ -154,7 +155,10 @@ export default {
})
},
},
initialValue: {
title: 'Foo',
initialValue: (params) => {
return {
title: 'Foo',
author: isIncomingReferenceCreation(params) ? params.reference : undefined,
}
},
}
})
49 changes: 49 additions & 0 deletions dev/test-studio/schema/debug/decorationFields.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import {Box, Card, Text} from '@sanity/ui'
import {defineDecoration, defineField, defineType} from 'sanity'

export default defineType({
name: 'decorationFields',
type: 'document',
fields: [
defineDecoration({
name: 'noDefaultComponents',
type: 'formDecoration',
title: 'No default components',
description:
'This field has no default components, so it will use the default input component which shows a warning.',
}),
defineField({
name: 'withInput',
type: 'formDecoration',
title: 'With defined input',
description:
'This field has a defined input component, so it will use that one instead of the warning',
components: {
input: () => (
<Box paddingY={2}>
<Text size={1}>Using form decorator input component</Text>
</Box>
),
},
}),
defineField({
name: 'withFieldAndInput',
type: 'formDecoration',
title: 'With defined field and input',
components: {
field: (props) => (
<Card padding={2} border tone="primary">
{props.renderDefault(props)}
</Card>
),
input: () => (
<Card padding={2} border>
<Box padding={2}>
<Text size={1}>Using form decorator input component</Text>
</Box>
</Card>
),
},
}),
],
})
2 changes: 2 additions & 0 deletions dev/test-studio/schema/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import customInputsWithPatches from './debug/customInputsWithPatches'
import customNumber from './debug/customNumber'
import dateTimeValidation from './debug/dateTimeValidation'
import dateValidation from './debug/dateValidation'
import decorationFields from './debug/decorationFields'
import {deprecatedDocument} from './debug/deprecatedDocument'
import {
deprecatedFields,
Expand Down Expand Up @@ -208,6 +209,7 @@ export function createSchemaTypes(projectId: string) {
customNumber,
dateTimeValidation,
dateValidation,
decorationFields,
deprecatedDocument,
deprecatedFields,
documentActions,
Expand Down
1 change: 1 addition & 0 deletions dev/test-studio/structure/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export const DEBUG_INPUT_TYPES = [
'customInputsTest',
'customInputsWithPatches',
'dateValidation',
'decorationFields',
'dateTimeValidation',
'deprecatedFields',
'deprecatedDocument',
Expand Down
2 changes: 1 addition & 1 deletion packages/@sanity/schema/src/_exports/_internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export {
resolveSearchConfig,
resolveSearchConfigForBaseFieldPaths,
} from '../legacy/searchConfig/resolve'
export {ALL_FIELDS_GROUP_NAME} from '../legacy/types/constants'
export {ALL_FIELDS_GROUP_NAME, FORM_DECORATION} from '../legacy/types/constants'
export {builtinTypes} from '../sanity/builtinTypes'
export {createSchemaFromManifestTypes} from '../sanity/createSchemaFromManifestTypes'
export {extractSchema} from '../sanity/extractSchema'
Expand Down
2 changes: 1 addition & 1 deletion packages/@sanity/schema/src/descriptors/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export type ObjectMarker = {__type: 'object'; value: EncodableObject}
*/
export interface CoreTypeDef extends CommonTypeDef {
extends: null
jsonType: 'boolean' | 'number' | 'string' | 'object' | 'array'
jsonType: 'boolean' | 'number' | 'string' | 'object' | 'array' | 'null'
}

/**
Expand Down
8 changes: 8 additions & 0 deletions packages/@sanity/schema/src/legacy/types/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,11 @@ export const DEFAULT_OVERRIDEABLE_FIELDS = [
export const OWN_PROPS_NAME = '_internal_ownProps'

export const ALL_FIELDS_GROUP_NAME = 'all-fields'

/**
* Name for configuring a decoration field type
* this fields are rendered in the form but are not extracted by the schema extractor.
* They serve only for UI purposes.
* @beta
*/
export const FORM_DECORATION = 'formDecoration'
50 changes: 50 additions & 0 deletions packages/@sanity/schema/src/legacy/types/formDecoration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import {pick} from 'lodash'

import primitivePreview from '../preview/primitivePreview'
import {DEFAULT_OVERRIDEABLE_FIELDS, FORM_DECORATION, OWN_PROPS_NAME} from './constants'
import {hiddenGetter} from './utils'

const OVERRIDABLE_FIELDS = [...DEFAULT_OVERRIDEABLE_FIELDS]

const FORM_DECORATION_CORE = {
name: FORM_DECORATION,
title: 'Form Decoration',
type: null,
jsonType: 'null',
}

export const formDecorationType = {
get() {
return FORM_DECORATION_CORE
},
extend(subTypeDef: any) {
const ownProps = {
...subTypeDef,
preview: primitivePreview,
}

const parsed = Object.assign(pick(FORM_DECORATION_CORE, OVERRIDABLE_FIELDS), ownProps, {
type: FORM_DECORATION_CORE,
})

hiddenGetter(parsed, OWN_PROPS_NAME, ownProps)

return subtype(parsed)

function subtype(parent: any) {
return {
get() {
return parent
},
extend: (extensionDef: any) => {
const subOwnProps = pick(extensionDef, OVERRIDABLE_FIELDS)
const current = Object.assign({}, parent, subOwnProps, {
type: parent,
})
hiddenGetter(current, OWN_PROPS_NAME, subOwnProps)
return subtype(current)
},
}
}
},
}
1 change: 1 addition & 0 deletions packages/@sanity/schema/src/legacy/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export {DateTimeType as datetime} from './datetime'
export {DocumentType as document} from './document'
export {EmailType as email} from './email'
export {FileType as file} from './file'
export {formDecorationType as formDecoration} from './formDecoration'
export {GlobalDocumentReferenceType as globalDocumentReference} from './globalDocumentReference'
export {ImageType as image} from './image'
export {NumberType as number} from './number'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default {
name: 'incomingReferences',
title: 'Incoming references',
type: 'formDecoration',
}
2 changes: 2 additions & 0 deletions packages/@sanity/schema/src/sanity/builtinTypes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import imageHotspot from './imageHotspot'
import imageMetadata from './imageMetadata'
import imagePalette from './imagePalette'
import imagePaletteSwatch from './imagePaletteSwatch'
import incomingReferences from './incomingReferences'
import slug from './slug'

export const builtinTypes = [
Expand All @@ -23,4 +24,5 @@ export const builtinTypes = [
imageDimensions,
imagePalette,
imagePaletteSwatch,
incomingReferences,
]
4 changes: 4 additions & 0 deletions packages/@sanity/schema/src/sanity/coreTypes.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import {FORM_DECORATION} from '../legacy/types/constants'

const coreTypes = [
{name: 'array', jsonType: 'array', type: 'type'},
{name: 'block', jsonType: 'object', type: 'type'},
Expand All @@ -20,6 +22,8 @@ const coreTypes = [
{name: 'telephone', jsonType: 'string', type: 'type'},
{name: 'text', jsonType: 'string', type: 'type'},
{name: 'url', jsonType: 'string', type: 'type'},
{name: FORM_DECORATION, jsonType: 'null', type: 'type'},
{name: 'incomingReferences', jsonType: 'null', type: 'type'},
] as const

export const coreTypeNames = coreTypes.map((t) => t.name)
Expand Down
Loading