@sanity/sdk-react 2.8.0 → 3.0.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +125 -63
  2. package/dist/index.d.ts +381 -571
  3. package/dist/index.js +435 -366
  4. package/dist/index.js.map +1 -1
  5. package/package.json +7 -9
  6. package/src/_exports/index.ts +4 -0
  7. package/src/_exports/sdk-react.ts +16 -0
  8. package/src/components/SDKProvider.test.tsx +23 -58
  9. package/src/components/SDKProvider.tsx +38 -30
  10. package/src/components/SanityApp.test.tsx +12 -68
  11. package/src/components/SanityApp.tsx +88 -65
  12. package/src/components/auth/AuthBoundary.test.tsx +8 -26
  13. package/src/components/auth/LoginError.tsx +5 -5
  14. package/src/config/handles.ts +53 -0
  15. package/src/context/ComlinkTokenRefresh.test.tsx +27 -10
  16. package/src/context/DefaultResourceContext.ts +10 -0
  17. package/src/context/PerspectiveContext.ts +12 -0
  18. package/src/context/ResourceProvider.test.tsx +99 -19
  19. package/src/context/ResourceProvider.tsx +103 -37
  20. package/src/context/ResourcesContext.tsx +7 -0
  21. package/src/context/SDKStudioContext.test.tsx +33 -28
  22. package/src/context/SDKStudioContext.ts +6 -0
  23. package/src/context/renderSanityApp.test.tsx +49 -151
  24. package/src/context/renderSanityApp.tsx +8 -12
  25. package/src/hooks/agent/agentActions.test.tsx +1 -1
  26. package/src/hooks/agent/agentActions.ts +56 -19
  27. package/src/hooks/auth/useDashboardOrganizationId.test.tsx +8 -2
  28. package/src/hooks/auth/useVerifyOrgProjects.test.tsx +32 -8
  29. package/src/hooks/client/useClient.test.tsx +4 -1
  30. package/src/hooks/client/useClient.ts +0 -1
  31. package/src/hooks/context/useDefaultResource.test.tsx +25 -0
  32. package/src/hooks/context/useDefaultResource.ts +30 -0
  33. package/src/hooks/context/useSanityInstance.test.tsx +2 -140
  34. package/src/hooks/context/useSanityInstance.ts +9 -53
  35. package/src/hooks/dashboard/useDispatchIntent.test.ts +24 -15
  36. package/src/hooks/dashboard/useDispatchIntent.ts +7 -7
  37. package/src/hooks/dashboard/useManageFavorite.test.tsx +34 -94
  38. package/src/hooks/dashboard/useManageFavorite.ts +16 -10
  39. package/src/hooks/dashboard/useNavigateToStudioDocument.test.ts +7 -5
  40. package/src/hooks/dashboard/useNavigateToStudioDocument.ts +6 -2
  41. package/src/hooks/dashboard/useRecordDocumentHistoryEvent.test.ts +2 -0
  42. package/src/hooks/dashboard/useRecordDocumentHistoryEvent.ts +2 -1
  43. package/src/hooks/dashboard/utils/useResourceIdFromDocumentHandle.test.ts +17 -38
  44. package/src/hooks/dashboard/utils/useResourceIdFromDocumentHandle.ts +12 -19
  45. package/src/hooks/datasets/useDatasets.test.ts +8 -22
  46. package/src/hooks/datasets/useDatasets.ts +8 -16
  47. package/src/hooks/document/useApplyDocumentActions.test.ts +98 -52
  48. package/src/hooks/document/useApplyDocumentActions.ts +35 -37
  49. package/src/hooks/document/useDocument.test.tsx +8 -37
  50. package/src/hooks/document/useDocument.ts +78 -129
  51. package/src/hooks/document/useDocumentEvent.test.tsx +7 -19
  52. package/src/hooks/document/useDocumentEvent.ts +21 -19
  53. package/src/hooks/document/useDocumentPermissions.test.tsx +75 -84
  54. package/src/hooks/document/useDocumentPermissions.ts +41 -28
  55. package/src/hooks/document/useDocumentSyncStatus.test.ts +13 -3
  56. package/src/hooks/document/useDocumentSyncStatus.ts +19 -14
  57. package/src/hooks/document/useEditDocument.test.tsx +28 -70
  58. package/src/hooks/document/useEditDocument.ts +29 -149
  59. package/src/hooks/documents/useDocuments.test.tsx +44 -64
  60. package/src/hooks/documents/useDocuments.ts +19 -25
  61. package/src/hooks/helpers/createCallbackHook.test.tsx +19 -13
  62. package/src/hooks/helpers/createStateSourceHook.test.tsx +10 -10
  63. package/src/hooks/helpers/createStateSourceHook.tsx +2 -4
  64. package/src/hooks/helpers/useNormalizedResourceOptions.test.ts +65 -0
  65. package/src/hooks/helpers/useNormalizedResourceOptions.ts +127 -0
  66. package/src/hooks/paginatedDocuments/usePaginatedDocuments.test.tsx +27 -34
  67. package/src/hooks/paginatedDocuments/usePaginatedDocuments.ts +19 -20
  68. package/src/hooks/presence/usePresence.test.tsx +71 -9
  69. package/src/hooks/presence/usePresence.ts +28 -3
  70. package/src/hooks/preview/useDocumentPreview.test.tsx +85 -193
  71. package/src/hooks/preview/useDocumentPreview.tsx +42 -62
  72. package/src/hooks/projection/useDocumentProjection.test.tsx +9 -37
  73. package/src/hooks/projection/useDocumentProjection.ts +9 -82
  74. package/src/hooks/projects/useProject.test.ts +1 -2
  75. package/src/hooks/projects/useProject.ts +7 -8
  76. package/src/hooks/query/useQuery.test.tsx +5 -6
  77. package/src/hooks/query/useQuery.ts +12 -91
  78. package/src/hooks/releases/useActiveReleases.test.tsx +2 -2
  79. package/src/hooks/releases/useActiveReleases.ts +25 -13
  80. package/src/hooks/releases/usePerspective.test.tsx +9 -17
  81. package/src/hooks/releases/usePerspective.ts +29 -18
  82. package/src/hooks/users/useUser.test.tsx +9 -3
  83. package/src/hooks/users/useUser.ts +1 -1
  84. package/src/hooks/users/useUsers.test.tsx +5 -2
  85. package/src/hooks/users/useUsers.ts +1 -1
  86. package/src/context/SourcesContext.tsx +0 -7
  87. package/src/hooks/helpers/useNormalizedSourceOptions.ts +0 -85
@@ -1,10 +1,16 @@
1
1
  import {type Message} from '@sanity/comlink'
2
- import {type FavoriteStatusResponse, getFavoritesState, resolveFavoritesState} from '@sanity/sdk'
3
- import {act, renderHook} from '@testing-library/react'
2
+ import {
3
+ type FavoriteStatusResponse,
4
+ getFavoritesState,
5
+ resolveFavoritesState,
6
+ type SanityInstance,
7
+ } from '@sanity/sdk'
4
8
  import {BehaviorSubject} from 'rxjs'
5
9
  import {beforeEach, describe, expect, it, vi} from 'vitest'
6
10
 
11
+ import {act, renderHook} from '../../../test/test-utils'
7
12
  import {ResourceProvider} from '../../context/ResourceProvider'
13
+ import {useSanityInstance} from '../../hooks/context/useSanityInstance'
8
14
  import {useWindowConnection, type WindowConnection} from '../comlink/useWindowConnection'
9
15
  import {useManageFavorite} from './useManageFavorite'
10
16
 
@@ -21,6 +27,10 @@ vi.mock('../comlink/useWindowConnection', () => ({
21
27
  useWindowConnection: vi.fn(),
22
28
  }))
23
29
 
30
+ vi.mock('../../hooks/context/useSanityInstance', () => ({
31
+ useSanityInstance: vi.fn(),
32
+ }))
33
+
24
34
  describe('useManageFavorite', () => {
25
35
  let favoriteStatusSubject: BehaviorSubject<FavoriteStatusResponse>
26
36
  let mockFetch: ReturnType<typeof vi.fn>
@@ -30,6 +40,7 @@ describe('useManageFavorite', () => {
30
40
  documentId: 'mock-id',
31
41
  documentType: 'mock-type',
32
42
  resourceType: 'studio' as const,
43
+ resource: {projectId: 'test', dataset: 'test'},
33
44
  }
34
45
 
35
46
  beforeEach(() => {
@@ -67,31 +78,21 @@ describe('useManageFavorite', () => {
67
78
  })
68
79
  })
69
80
 
81
+ vi.mocked(useSanityInstance).mockImplementation(() => ({}) as unknown as SanityInstance)
82
+
70
83
  afterEach(() => {
71
84
  favoriteStatusSubject.complete()
72
85
  vi.clearAllMocks()
73
86
  })
74
87
 
75
88
  it('should initialize with default states', () => {
76
- const {result} = renderHook(() => useManageFavorite(mockDocumentHandle), {
77
- wrapper: ({children}) => (
78
- <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
79
- {children}
80
- </ResourceProvider>
81
- ),
82
- })
89
+ const {result} = renderHook(() => useManageFavorite(mockDocumentHandle))
83
90
 
84
91
  expect(result.current.isFavorited).toBe(false)
85
92
  })
86
93
 
87
94
  it('should handle favorite action and update state', async () => {
88
- const {result} = renderHook(() => useManageFavorite(mockDocumentHandle), {
89
- wrapper: ({children}) => (
90
- <ResourceProvider projectId="test" dataset="test" fallback={null}>
91
- {children}
92
- </ResourceProvider>
93
- ),
94
- })
95
+ const {result} = renderHook(() => useManageFavorite(mockDocumentHandle))
95
96
 
96
97
  expect(result.current.isFavorited).toBe(false)
97
98
 
@@ -120,13 +121,7 @@ describe('useManageFavorite', () => {
120
121
  })
121
122
 
122
123
  it('should handle unfavorite action and update state', async () => {
123
- const {result} = renderHook(() => useManageFavorite(mockDocumentHandle), {
124
- wrapper: ({children}) => (
125
- <ResourceProvider projectId="test" dataset="test" fallback={null}>
126
- {children}
127
- </ResourceProvider>
128
- ),
129
- })
124
+ const {result} = renderHook(() => useManageFavorite(mockDocumentHandle))
130
125
 
131
126
  // Set initial state to favorited
132
127
  await act(async () => {
@@ -161,13 +156,7 @@ describe('useManageFavorite', () => {
161
156
  it('should not update state if favorite action fails', async () => {
162
157
  mockFetch.mockResolvedValueOnce({success: false})
163
158
 
164
- const {result} = renderHook(() => useManageFavorite(mockDocumentHandle), {
165
- wrapper: ({children}) => (
166
- <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
167
- {children}
168
- </ResourceProvider>
169
- ),
170
- })
159
+ const {result} = renderHook(() => useManageFavorite(mockDocumentHandle))
171
160
 
172
161
  expect(result.current.isFavorited).toBe(false)
173
162
 
@@ -187,13 +176,7 @@ describe('useManageFavorite', () => {
187
176
  throw new Error(errorMessage)
188
177
  })
189
178
 
190
- const {result} = renderHook(() => useManageFavorite(mockDocumentHandle), {
191
- wrapper: ({children}) => (
192
- <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
193
- {children}
194
- </ResourceProvider>
195
- ),
196
- })
179
+ const {result} = renderHook(() => useManageFavorite(mockDocumentHandle))
197
180
 
198
181
  await act(async () => {
199
182
  await expect(result.current.favorite()).rejects.toThrow(errorMessage)
@@ -210,7 +193,7 @@ describe('useManageFavorite', () => {
210
193
  consoleErrorSpy.mockRestore()
211
194
  })
212
195
 
213
- it('should throw error when studio resource is missing projectId or dataset', () => {
196
+ it('should throw error when studio resource is missing resource', () => {
214
197
  const mockDocumentHandleWithoutProjectId = {
215
198
  documentId: 'mock-id',
216
199
  documentType: 'mock-type',
@@ -219,10 +202,8 @@ describe('useManageFavorite', () => {
219
202
 
220
203
  expect(() =>
221
204
  renderHook(() => useManageFavorite(mockDocumentHandleWithoutProjectId), {
222
- wrapper: ({children}) => (
223
- <ResourceProvider projectId={undefined} dataset={undefined} fallback={null}>
224
- {children}
225
- </ResourceProvider>
205
+ wrapper: ({children}: {children: React.ReactNode}) => (
206
+ <ResourceProvider fallback={null}>{children}</ResourceProvider>
226
207
  ),
227
208
  }),
228
209
  ).toThrow('projectId and dataset are required for studio resources')
@@ -234,17 +215,12 @@ describe('useManageFavorite', () => {
234
215
  documentType: 'mock-type',
235
216
  resourceType: 'media-library' as const,
236
217
  resourceId: undefined,
218
+ resource: {mediaLibraryId: 'media-library-id'},
237
219
  }
238
220
 
239
- expect(() =>
240
- renderHook(() => useManageFavorite(mockMediaDocumentHandle), {
241
- wrapper: ({children}) => (
242
- <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
243
- {children}
244
- </ResourceProvider>
245
- ),
246
- }),
247
- ).toThrow('resourceId is required for media-library and canvas resources')
221
+ expect(() => renderHook(() => useManageFavorite(mockMediaDocumentHandle))).toThrow(
222
+ 'resourceId is required for media-library and canvas resources',
223
+ )
248
224
  })
249
225
 
250
226
  it('should include schemaName in payload when provided', async () => {
@@ -252,13 +228,7 @@ describe('useManageFavorite', () => {
252
228
  ...mockDocumentHandle,
253
229
  schemaName: 'testSchema',
254
230
  }
255
- const {result} = renderHook(() => useManageFavorite(mockDocumentHandleWithSchema), {
256
- wrapper: ({children}) => (
257
- <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
258
- {children}
259
- </ResourceProvider>
260
- ),
261
- })
231
+ const {result} = renderHook(() => useManageFavorite(mockDocumentHandleWithSchema))
262
232
 
263
233
  await act(async () => {
264
234
  await result.current.favorite()
@@ -271,7 +241,7 @@ describe('useManageFavorite', () => {
271
241
  id: 'mock-id',
272
242
  type: 'mock-type',
273
243
  resource: {
274
- id: 'test-project.test-dataset',
244
+ id: 'test.test',
275
245
  type: 'studio',
276
246
  schemaName: 'testSchema',
277
247
  },
@@ -293,13 +263,7 @@ describe('useManageFavorite', () => {
293
263
  getCurrent: () => undefined,
294
264
  observable: favoriteStatusSubject.asObservable(),
295
265
  }))
296
- const {result} = renderHook(() => useManageFavorite(mockDocumentHandle), {
297
- wrapper: ({children}) => (
298
- <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
299
- {children}
300
- </ResourceProvider>
301
- ),
302
- })
266
+ const {result} = renderHook(() => useManageFavorite(mockDocumentHandle))
303
267
  expect(result.current.isFavorited).toBe(false)
304
268
  })
305
269
 
@@ -308,13 +272,7 @@ describe('useManageFavorite', () => {
308
272
  fetch: undefined,
309
273
  sendMessage: mockSendMessage,
310
274
  } as unknown as WindowConnection<Message>)
311
- const {result} = renderHook(() => useManageFavorite(mockDocumentHandle), {
312
- wrapper: ({children}) => (
313
- <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
314
- {children}
315
- </ResourceProvider>
316
- ),
317
- })
275
+ const {result} = renderHook(() => useManageFavorite(mockDocumentHandle))
318
276
  await act(async () => {
319
277
  await result.current.favorite()
320
278
  await result.current.unfavorite()
@@ -325,13 +283,7 @@ describe('useManageFavorite', () => {
325
283
  it('should do nothing if documentId is missing', async () => {
326
284
  const handle = {...mockDocumentHandle, documentId: undefined}
327
285
  // @ts-expect-error -- no access to ManageFavorite props type
328
- const {result} = renderHook(() => useManageFavorite(handle), {
329
- wrapper: ({children}) => (
330
- <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
331
- {children}
332
- </ResourceProvider>
333
- ),
334
- })
286
+ const {result} = renderHook(() => useManageFavorite(handle))
335
287
  await act(async () => {
336
288
  await result.current.favorite()
337
289
  await result.current.unfavorite()
@@ -342,13 +294,7 @@ describe('useManageFavorite', () => {
342
294
  it('should do nothing if documentType is missing', async () => {
343
295
  const handle = {...mockDocumentHandle, documentType: undefined}
344
296
  // @ts-expect-error -- no access to ManageFavorite props type
345
- const {result} = renderHook(() => useManageFavorite(handle), {
346
- wrapper: ({children}) => (
347
- <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
348
- {children}
349
- </ResourceProvider>
350
- ),
351
- })
297
+ const {result} = renderHook(() => useManageFavorite(handle))
352
298
  await act(async () => {
353
299
  await result.current.favorite()
354
300
  await result.current.unfavorite()
@@ -359,13 +305,7 @@ describe('useManageFavorite', () => {
359
305
  it('should do nothing if resourceType is missing', async () => {
360
306
  const handle = {...mockDocumentHandle, resourceType: undefined, resourceId: 'studio'}
361
307
  // @ts-expect-error -- no access to ManageFavorite props type
362
- const {result} = renderHook(() => useManageFavorite(handle), {
363
- wrapper: ({children}) => (
364
- <ResourceProvider projectId="test-project" dataset="test-dataset" fallback={null}>
365
- {children}
366
- </ResourceProvider>
367
- ),
368
- })
308
+ const {result} = renderHook(() => useManageFavorite(handle))
369
309
  await act(async () => {
370
310
  await result.current.favorite()
371
311
  await result.current.unfavorite()
@@ -7,14 +7,16 @@ import {
7
7
  type StudioResource,
8
8
  } from '@sanity/message-protocol'
9
9
  import {
10
- type DocumentHandle,
11
10
  type FavoriteStatusResponse,
12
11
  type FrameMessage,
13
12
  getFavoritesState,
13
+ isDatasetResource,
14
14
  resolveFavoritesState,
15
15
  } from '@sanity/sdk'
16
- import {useCallback, useMemo, useSyncExternalStore} from 'react'
16
+ import {useCallback, useContext, useMemo, useSyncExternalStore} from 'react'
17
17
 
18
+ import {type DocumentHandle} from '../../config/handles'
19
+ import {ResourceContext} from '../../context/DefaultResourceContext'
18
20
  import {useWindowConnection} from '../comlink/useWindowConnection'
19
21
  import {useSanityInstance} from '../context/useSanityInstance'
20
22
 
@@ -82,8 +84,7 @@ interface UseManageFavoriteProps extends DocumentHandle {
82
84
  export function useManageFavorite({
83
85
  documentId,
84
86
  documentType,
85
- projectId: paramProjectId,
86
- dataset: paramDataset,
87
+ resource: paramResource,
87
88
  resourceId: paramResourceId,
88
89
  resourceType,
89
90
  schemaName,
@@ -93,11 +94,11 @@ export function useManageFavorite({
93
94
  connectTo: SDK_CHANNEL_NAME,
94
95
  })
95
96
  const instance = useSanityInstance()
96
- const {config} = instance
97
- const instanceProjectId = config?.projectId
98
- const instanceDataset = config?.dataset
99
- const projectId = paramProjectId ?? instanceProjectId
100
- const dataset = paramDataset ?? instanceDataset
97
+ const contextResource = useContext(ResourceContext)
98
+ const resource = paramResource ?? contextResource
99
+ const datasetResource = resource && isDatasetResource(resource) ? resource : undefined
100
+ const projectId = datasetResource?.projectId
101
+ const dataset = datasetResource?.dataset
101
102
 
102
103
  if (resourceType === 'studio' && (!projectId || !dataset)) {
103
104
  throw new Error('projectId and dataset are required for studio resources')
@@ -110,16 +111,21 @@ export function useManageFavorite({
110
111
  throw new Error('resourceId is required for media-library and canvas resources')
111
112
  }
112
113
 
114
+ if (!resource) {
115
+ throw new Error('resource is required')
116
+ }
117
+
113
118
  // used for favoriteStore functions like getFavoritesState and resolveFavoritesState
114
119
  const context = useMemo(
115
120
  () => ({
116
121
  documentId,
117
122
  documentType,
123
+ resource,
118
124
  resourceId,
119
125
  resourceType,
120
126
  schemaName,
121
127
  }),
122
- [documentId, documentType, resourceId, resourceType, schemaName],
128
+ [documentId, documentType, resource, resourceId, resourceType, schemaName],
123
129
  )
124
130
 
125
131
  // Get favorite status using StateSource
@@ -33,8 +33,7 @@ describe('useNavigateToStudioDocument', () => {
33
33
  const mockDocumentHandle: DocumentHandle = {
34
34
  documentId: 'doc123',
35
35
  documentType: 'article',
36
- projectId: 'project1',
37
- dataset: 'dataset1',
36
+ resource: {projectId: 'project1', dataset: 'dataset1'},
38
37
  }
39
38
 
40
39
  const mockWorkspace = {
@@ -109,13 +108,13 @@ describe('useNavigateToStudioDocument', () => {
109
108
  consoleSpy.mockRestore()
110
109
  })
111
110
 
112
- it('warns and does not navigate when projectId or dataset is missing', () => {
111
+ it('warns and does not navigate when resource is not a dataset resource', () => {
113
112
  const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
114
113
 
115
114
  const incompleteDocumentHandle: DocumentHandle = {
116
115
  documentId: 'doc123',
117
116
  documentType: 'article',
118
- // missing projectId and dataset
117
+ resource: {mediaLibraryId: 'ml123'},
119
118
  }
120
119
 
121
120
  const {result} = renderHook(() => useNavigateToStudioDocument(incompleteDocumentHandle))
@@ -197,8 +196,11 @@ describe('useNavigateToStudioDocument', () => {
197
196
 
198
197
  result.current.navigateToStudioDocument()
199
198
 
199
+ const resource = mockDocumentHandle.resource
200
+ const projectId = 'projectId' in resource ? resource.projectId : undefined
201
+ const dataset = 'dataset' in resource ? resource.dataset : undefined
200
202
  expect(consoleSpy).toHaveBeenCalledWith(
201
- `No workspace found for document with projectId: ${mockDocumentHandle.projectId} and dataset: ${mockDocumentHandle.dataset} or with preferred studio url: ${preferredUrl}`,
203
+ `No workspace found for document with projectId: ${projectId} and dataset: ${dataset} or with preferred studio url: ${preferredUrl}`,
202
204
  )
203
205
  expect(mockSendMessage).not.toHaveBeenCalled()
204
206
 
@@ -1,7 +1,8 @@
1
1
  import {type Bridge, SDK_CHANNEL_NAME, SDK_NODE_NAME} from '@sanity/message-protocol'
2
- import {type DocumentHandle} from '@sanity/sdk'
2
+ import {isDatasetResource} from '@sanity/sdk'
3
3
  import {useCallback} from 'react'
4
4
 
5
+ import {type DocumentHandle} from '../../config/handles'
5
6
  import {useWindowConnection} from '../comlink/useWindowConnection'
6
7
  import {
7
8
  type DashboardResource,
@@ -71,7 +72,10 @@ export function useNavigateToStudioDocument(
71
72
  })
72
73
 
73
74
  const navigateToStudioDocument = useCallback(() => {
74
- const {projectId, dataset} = documentHandle
75
+ const {resource} = documentHandle
76
+ const datasetResource = resource && isDatasetResource(resource) ? resource : undefined
77
+ const projectId = datasetResource?.projectId
78
+ const dataset = datasetResource?.dataset
75
79
 
76
80
  if (!projectId || !dataset) {
77
81
  // eslint-disable-next-line no-console
@@ -15,6 +15,7 @@ describe('useRecordDocumentHistoryEvent', () => {
15
15
  documentType: 'mock-type',
16
16
  resourceType: 'studio' as const,
17
17
  resourceId: 'mock-resource-id',
18
+ resource: {projectId: 'mock-project-id', dataset: 'mock-dataset'},
18
19
  }
19
20
 
20
21
  beforeEach(() => {
@@ -62,6 +63,7 @@ describe('useRecordDocumentHistoryEvent', () => {
62
63
  documentType: 'mock-type',
63
64
  resourceType: 'media-library' as const,
64
65
  resourceId: undefined,
66
+ resource: {mediaLibraryId: 'mock-media-library-id'},
65
67
  }
66
68
 
67
69
  expect(() => renderHook(() => useRecordDocumentHistoryEvent(mockMediaDocumentHandle))).toThrow(
@@ -6,9 +6,10 @@ import {
6
6
  SDK_NODE_NAME,
7
7
  type StudioResource,
8
8
  } from '@sanity/message-protocol'
9
- import {type DocumentHandle, type FrameMessage} from '@sanity/sdk'
9
+ import {type FrameMessage} from '@sanity/sdk'
10
10
  import {useCallback} from 'react'
11
11
 
12
+ import {type DocumentHandle} from '../../config/handles'
12
13
  import {useWindowConnection} from '../comlink/useWindowConnection'
13
14
 
14
15
  interface DocumentInteractionHistory {
@@ -4,13 +4,12 @@ import {renderHook} from '../../../../test/test-utils'
4
4
  import {useResourceIdFromDocumentHandle} from './useResourceIdFromDocumentHandle'
5
5
 
6
6
  describe('getResourceIdFromDocumentHandle', () => {
7
- describe('with traditional DocumentHandle (projectId/dataset)', () => {
8
- it('should return resource ID from projectId and dataset', () => {
7
+ describe('with DocumentHandle using resource object', () => {
8
+ it('should return resource ID from resource object with projectId and dataset', () => {
9
9
  const documentHandle = {
10
10
  documentId: 'test-document-id',
11
11
  documentType: 'test-document-type',
12
- projectId: 'test-project-id',
13
- dataset: 'test-dataset',
12
+ resource: {projectId: 'test-project-id', dataset: 'test-dataset'},
14
13
  }
15
14
 
16
15
  const {result} = renderHook(() => useResourceIdFromDocumentHandle(documentHandle))
@@ -22,12 +21,12 @@ describe('getResourceIdFromDocumentHandle', () => {
22
21
  })
23
22
  })
24
23
 
25
- describe('with DocumentHandleWithSource - media library', () => {
26
- it('should return media library ID and resourceType when media library source is provided', () => {
24
+ describe('with DocumentHandle - media library resource', () => {
25
+ it('should return media library ID and resourceType when media library resource is provided', () => {
27
26
  const documentHandle = {
28
27
  documentId: 'test-asset-id',
29
28
  documentType: 'sanity.asset',
30
- sourceName: 'media-library',
29
+ resourceName: 'media-library',
31
30
  } as const
32
31
 
33
32
  const {result} = renderHook(() => useResourceIdFromDocumentHandle(documentHandle))
@@ -38,13 +37,11 @@ describe('getResourceIdFromDocumentHandle', () => {
38
37
  })
39
38
  })
40
39
 
41
- it('should prioritize source over projectId/dataset when both are provided', () => {
40
+ it('should use resourceName when provided', () => {
42
41
  const documentHandle = {
43
42
  documentId: 'test-asset-id',
44
43
  documentType: 'sanity.asset',
45
- projectId: 'test-project-id',
46
- dataset: 'test-dataset',
47
- sourceName: 'media-library',
44
+ resourceName: 'media-library',
48
45
  }
49
46
 
50
47
  const {result} = renderHook(() => useResourceIdFromDocumentHandle(documentHandle))
@@ -56,12 +53,12 @@ describe('getResourceIdFromDocumentHandle', () => {
56
53
  })
57
54
  })
58
55
 
59
- describe('with DocumentHandleWithSource - canvas', () => {
60
- it('should return canvas ID and resourceType when canvas source is provided', () => {
56
+ describe('with DocumentHandle - canvas resource', () => {
57
+ it('should return canvas ID and resourceType when canvas resource is provided', () => {
61
58
  const documentHandle = {
62
59
  documentId: 'test-canvas-document-id',
63
60
  documentType: 'sanity.canvas.document',
64
- sourceName: 'canvas',
61
+ resourceName: 'canvas',
65
62
  }
66
63
 
67
64
  const {result} = renderHook(() => useResourceIdFromDocumentHandle(documentHandle))
@@ -73,29 +70,12 @@ describe('getResourceIdFromDocumentHandle', () => {
73
70
  })
74
71
  })
75
72
 
76
- describe('with DocumentHandleWithSource - dataset source', () => {
77
- it('should return dataset resource ID when dataset source is provided', () => {
73
+ describe('with DocumentHandle - dataset resource', () => {
74
+ it('should return dataset resource ID when dataset resource is provided', () => {
78
75
  const documentHandle = {
79
76
  documentId: 'test-document-id',
80
77
  documentType: 'test-document-type',
81
- sourceName: 'dataset',
82
- }
83
-
84
- const {result} = renderHook(() => useResourceIdFromDocumentHandle(documentHandle))
85
-
86
- expect(result.current).toEqual({
87
- id: 'source-project-id.source-dataset',
88
- type: undefined,
89
- })
90
- })
91
-
92
- it('should use dataset source over projectId/dataset when both are provided', () => {
93
- const documentHandle = {
94
- documentId: 'test-document-id',
95
- documentType: 'test-document-type',
96
- projectId: 'test-project-id',
97
- dataset: 'test-dataset',
98
- sourceName: 'dataset',
78
+ resourceName: 'dataset',
99
79
  }
100
80
 
101
81
  const {result} = renderHook(() => useResourceIdFromDocumentHandle(documentHandle))
@@ -108,13 +88,12 @@ describe('getResourceIdFromDocumentHandle', () => {
108
88
  })
109
89
 
110
90
  describe('edge cases', () => {
111
- it('should handle DocumentHandleWithSource with undefined source', () => {
91
+ it('should handle DocumentHandle with explicit resource and no resourceName', () => {
112
92
  const documentHandle = {
113
93
  documentId: 'test-document-id',
114
94
  documentType: 'test-document-type',
115
- projectId: 'test-project-id',
116
- dataset: 'test-dataset',
117
- sourceName: undefined,
95
+ resource: {projectId: 'test-project-id', dataset: 'test-dataset'},
96
+ resourceName: undefined,
118
97
  }
119
98
 
120
99
  const {result} = renderHook(() => useResourceIdFromDocumentHandle(documentHandle))
@@ -1,11 +1,7 @@
1
- import {
2
- type DocumentHandle,
3
- isCanvasSource,
4
- isDatasetSource,
5
- isMediaLibrarySource,
6
- } from '@sanity/sdk'
1
+ import {isCanvasResource, isDatasetResource, isMediaLibraryResource} from '@sanity/sdk'
7
2
 
8
- import {useNormalizedSourceOptions} from '../../helpers/useNormalizedSourceOptions'
3
+ import {type DocumentHandle} from '../../../config/handles'
4
+ import {useNormalizedResourceOptions} from '../../helpers/useNormalizedResourceOptions'
9
5
 
10
6
  interface DashboardMessageResource {
11
7
  id: string
@@ -18,23 +14,20 @@ interface DashboardMessageResource {
18
14
  export function useResourceIdFromDocumentHandle(
19
15
  documentHandle: DocumentHandle,
20
16
  ): DashboardMessageResource {
21
- const options = useNormalizedSourceOptions(documentHandle)
22
- const {projectId, dataset, source} = options
17
+ const options = useNormalizedResourceOptions(documentHandle)
18
+ const {resource} = options
23
19
  let resourceId: string = ''
24
20
  let resourceType: 'media-library' | 'canvas' | undefined
25
- if (projectId && dataset) {
26
- resourceId = `${projectId}.${dataset}`
27
- }
28
21
 
29
- if (source) {
30
- if (isDatasetSource(source)) {
31
- resourceId = `${source.projectId}.${source.dataset}`
22
+ if (resource) {
23
+ if (isDatasetResource(resource)) {
24
+ resourceId = `${resource.projectId}.${resource.dataset}`
32
25
  resourceType = undefined
33
- } else if (isMediaLibrarySource(source)) {
34
- resourceId = source.mediaLibraryId
26
+ } else if (isMediaLibraryResource(resource)) {
27
+ resourceId = resource.mediaLibraryId
35
28
  resourceType = 'media-library'
36
- } else if (isCanvasSource(source)) {
37
- resourceId = source.canvasId
29
+ } else if (isCanvasResource(resource)) {
30
+ resourceId = resource.canvasId
38
31
  resourceType = 'canvas'
39
32
  }
40
33
  }
@@ -1,4 +1,4 @@
1
- import {getDatasetsState, type ProjectHandle, type SanityInstance} from '@sanity/sdk'
1
+ import {getDatasetsState, type SanityInstance} from '@sanity/sdk'
2
2
  import {beforeEach, describe, expect, it, vi} from 'vitest'
3
3
 
4
4
  import {createStateSourceHook} from '../helpers/createStateSourceHook'
@@ -6,7 +6,7 @@ import {createStateSourceHook} from '../helpers/createStateSourceHook'
6
6
  // Mock dependencies
7
7
  vi.mock('@sanity/sdk', () => ({
8
8
  getDatasetsState: vi.fn(() => ({
9
- getCurrent: vi.fn(() => undefined), // Mocking getCurrent to satisfy the call within shouldSuspend
9
+ getCurrent: vi.fn(() => undefined),
10
10
  })),
11
11
  resolveDatasets: vi.fn(),
12
12
  }))
@@ -15,10 +15,8 @@ vi.mock('../helpers/createStateSourceHook', () => ({
15
15
  }))
16
16
 
17
17
  describe('useDatasets', () => {
18
- // Use beforeEach to reset modules and ensure mocks are fresh for each test
19
18
  beforeEach(() => {
20
19
  vi.resetModules()
21
- // Re-mock dependencies for each test after resetModules
22
20
  vi.mock('@sanity/sdk', () => ({
23
21
  getDatasetsState: vi.fn(() => ({
24
22
  getCurrent: vi.fn(() => undefined),
@@ -31,50 +29,38 @@ describe('useDatasets', () => {
31
29
  })
32
30
 
33
31
  it('should call createStateSourceHook with correct arguments on import', async () => {
34
- // Dynamically import the hook *after* mocks are set up and modules reset
35
32
  await import('./useDatasets')
36
33
 
37
- // Check if createStateSourceHook was called during the module evaluation (import)
38
34
  expect(createStateSourceHook).toHaveBeenCalled()
39
35
  expect(createStateSourceHook).toHaveBeenCalledWith(
40
36
  expect.objectContaining({
41
37
  getState: expect.any(Function),
42
38
  shouldSuspend: expect.any(Function),
43
- suspender: expect.any(Function), // Actual function reference doesn't matter here as it's mocked
44
- getConfig: expect.any(Function), // Actual function reference doesn't matter here
39
+ suspender: expect.any(Function),
45
40
  }),
46
41
  )
47
42
  })
48
43
 
49
44
  it('shouldSuspend should call getDatasetsState and getCurrent', async () => {
50
- // Dynamically import the hook *after* mocks are set up and modules reset
51
45
  await import('./useDatasets')
52
46
 
53
- // Get the arguments passed to createStateSourceHook
54
- // Need to ensure createStateSourceHook mock is correctly typed for access
55
47
  const mockCreateStateSourceHook = createStateSourceHook as ReturnType<typeof vi.fn>
56
48
  expect(mockCreateStateSourceHook.mock.calls.length).toBeGreaterThan(0)
57
49
  const createStateSourceHookArgs = mockCreateStateSourceHook.mock.calls[0][0]
58
50
  const shouldSuspend = createStateSourceHookArgs.shouldSuspend
59
51
 
60
- // Mock instance and projectHandle for the test call
61
- const mockInstance = {} as SanityInstance // Use specific type
62
- const mockProjectHandle = {} as ProjectHandle // Use specific type
52
+ const mockInstance = {} as SanityInstance
53
+ const mockOptions = {projectId: 'test-project'}
63
54
 
64
- // Call the shouldSuspend function
65
- const result = shouldSuspend(mockInstance, mockProjectHandle)
55
+ const result = shouldSuspend(mockInstance, mockOptions)
66
56
 
67
- // Assert that getDatasetsState was called with the correct arguments
68
- // Need to ensure getDatasetsState mock is correctly typed for access
69
57
  const mockGetDatasetsState = getDatasetsState as ReturnType<typeof vi.fn>
70
- expect(mockGetDatasetsState).toHaveBeenCalledWith(mockInstance, mockProjectHandle)
58
+ expect(mockGetDatasetsState).toHaveBeenCalledWith(mockInstance, mockOptions)
71
59
 
72
- // Assert that getCurrent was called on the result of getDatasetsState
73
60
  expect(mockGetDatasetsState.mock.results.length).toBeGreaterThan(0)
74
61
  const getDatasetsStateMockResult = mockGetDatasetsState.mock.results[0].value
75
62
  expect(getDatasetsStateMockResult.getCurrent).toHaveBeenCalled()
76
63
 
77
- // Assert the result of shouldSuspend based on the mocked getCurrent value
78
- expect(result).toBe(true) // Since getCurrent is mocked to return undefined
64
+ expect(result).toBe(true)
79
65
  })
80
66
  })