@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.
- package/README.md +125 -63
- package/dist/index.d.ts +381 -571
- package/dist/index.js +435 -366
- package/dist/index.js.map +1 -1
- package/package.json +7 -9
- package/src/_exports/index.ts +4 -0
- package/src/_exports/sdk-react.ts +16 -0
- package/src/components/SDKProvider.test.tsx +23 -58
- package/src/components/SDKProvider.tsx +38 -30
- package/src/components/SanityApp.test.tsx +12 -68
- package/src/components/SanityApp.tsx +88 -65
- package/src/components/auth/AuthBoundary.test.tsx +8 -26
- package/src/components/auth/LoginError.tsx +5 -5
- package/src/config/handles.ts +53 -0
- package/src/context/ComlinkTokenRefresh.test.tsx +27 -10
- package/src/context/DefaultResourceContext.ts +10 -0
- package/src/context/PerspectiveContext.ts +12 -0
- package/src/context/ResourceProvider.test.tsx +99 -19
- package/src/context/ResourceProvider.tsx +103 -37
- package/src/context/ResourcesContext.tsx +7 -0
- package/src/context/SDKStudioContext.test.tsx +33 -28
- package/src/context/SDKStudioContext.ts +6 -0
- package/src/context/renderSanityApp.test.tsx +49 -151
- package/src/context/renderSanityApp.tsx +8 -12
- package/src/hooks/agent/agentActions.test.tsx +1 -1
- package/src/hooks/agent/agentActions.ts +56 -19
- package/src/hooks/auth/useDashboardOrganizationId.test.tsx +8 -2
- package/src/hooks/auth/useVerifyOrgProjects.test.tsx +32 -8
- package/src/hooks/client/useClient.test.tsx +4 -1
- package/src/hooks/client/useClient.ts +0 -1
- package/src/hooks/context/useDefaultResource.test.tsx +25 -0
- package/src/hooks/context/useDefaultResource.ts +30 -0
- package/src/hooks/context/useSanityInstance.test.tsx +2 -140
- package/src/hooks/context/useSanityInstance.ts +9 -53
- package/src/hooks/dashboard/useDispatchIntent.test.ts +24 -15
- package/src/hooks/dashboard/useDispatchIntent.ts +7 -7
- package/src/hooks/dashboard/useManageFavorite.test.tsx +34 -94
- package/src/hooks/dashboard/useManageFavorite.ts +16 -10
- package/src/hooks/dashboard/useNavigateToStudioDocument.test.ts +7 -5
- package/src/hooks/dashboard/useNavigateToStudioDocument.ts +6 -2
- package/src/hooks/dashboard/useRecordDocumentHistoryEvent.test.ts +2 -0
- package/src/hooks/dashboard/useRecordDocumentHistoryEvent.ts +2 -1
- package/src/hooks/dashboard/utils/useResourceIdFromDocumentHandle.test.ts +17 -38
- package/src/hooks/dashboard/utils/useResourceIdFromDocumentHandle.ts +12 -19
- package/src/hooks/datasets/useDatasets.test.ts +8 -22
- package/src/hooks/datasets/useDatasets.ts +8 -16
- package/src/hooks/document/useApplyDocumentActions.test.ts +98 -52
- package/src/hooks/document/useApplyDocumentActions.ts +35 -37
- package/src/hooks/document/useDocument.test.tsx +8 -37
- package/src/hooks/document/useDocument.ts +78 -129
- package/src/hooks/document/useDocumentEvent.test.tsx +7 -19
- package/src/hooks/document/useDocumentEvent.ts +21 -19
- package/src/hooks/document/useDocumentPermissions.test.tsx +75 -84
- package/src/hooks/document/useDocumentPermissions.ts +41 -28
- package/src/hooks/document/useDocumentSyncStatus.test.ts +13 -3
- package/src/hooks/document/useDocumentSyncStatus.ts +19 -14
- package/src/hooks/document/useEditDocument.test.tsx +28 -70
- package/src/hooks/document/useEditDocument.ts +29 -149
- package/src/hooks/documents/useDocuments.test.tsx +44 -64
- package/src/hooks/documents/useDocuments.ts +19 -25
- package/src/hooks/helpers/createCallbackHook.test.tsx +19 -13
- package/src/hooks/helpers/createStateSourceHook.test.tsx +10 -10
- package/src/hooks/helpers/createStateSourceHook.tsx +2 -4
- package/src/hooks/helpers/useNormalizedResourceOptions.test.ts +65 -0
- package/src/hooks/helpers/useNormalizedResourceOptions.ts +127 -0
- package/src/hooks/paginatedDocuments/usePaginatedDocuments.test.tsx +27 -34
- package/src/hooks/paginatedDocuments/usePaginatedDocuments.ts +19 -20
- package/src/hooks/presence/usePresence.test.tsx +71 -9
- package/src/hooks/presence/usePresence.ts +28 -3
- package/src/hooks/preview/useDocumentPreview.test.tsx +85 -193
- package/src/hooks/preview/useDocumentPreview.tsx +42 -62
- package/src/hooks/projection/useDocumentProjection.test.tsx +9 -37
- package/src/hooks/projection/useDocumentProjection.ts +9 -82
- package/src/hooks/projects/useProject.test.ts +1 -2
- package/src/hooks/projects/useProject.ts +7 -8
- package/src/hooks/query/useQuery.test.tsx +5 -6
- package/src/hooks/query/useQuery.ts +12 -91
- package/src/hooks/releases/useActiveReleases.test.tsx +2 -2
- package/src/hooks/releases/useActiveReleases.ts +25 -13
- package/src/hooks/releases/usePerspective.test.tsx +9 -17
- package/src/hooks/releases/usePerspective.ts +29 -18
- package/src/hooks/users/useUser.test.tsx +9 -3
- package/src/hooks/users/useUser.ts +1 -1
- package/src/hooks/users/useUsers.test.tsx +5 -2
- package/src/hooks/users/useUsers.ts +1 -1
- package/src/context/SourcesContext.tsx +0 -7
- package/src/hooks/helpers/useNormalizedSourceOptions.ts +0 -85
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import {type Message} from '@sanity/comlink'
|
|
2
|
-
import {
|
|
3
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
241
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
97
|
-
const
|
|
98
|
-
const
|
|
99
|
-
const projectId =
|
|
100
|
-
const dataset =
|
|
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
|
|
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
|
-
|
|
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: ${
|
|
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 {
|
|
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 {
|
|
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
|
|
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
|
|
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
|
|
26
|
-
it('should return media library ID and resourceType when media library
|
|
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
|
-
|
|
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
|
|
40
|
+
it('should use resourceName when provided', () => {
|
|
42
41
|
const documentHandle = {
|
|
43
42
|
documentId: 'test-asset-id',
|
|
44
43
|
documentType: 'sanity.asset',
|
|
45
|
-
|
|
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
|
|
60
|
-
it('should return canvas ID and resourceType when canvas
|
|
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
|
-
|
|
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
|
|
77
|
-
it('should return dataset resource ID when dataset
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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 {
|
|
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 =
|
|
22
|
-
const {
|
|
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 (
|
|
30
|
-
if (
|
|
31
|
-
resourceId = `${
|
|
22
|
+
if (resource) {
|
|
23
|
+
if (isDatasetResource(resource)) {
|
|
24
|
+
resourceId = `${resource.projectId}.${resource.dataset}`
|
|
32
25
|
resourceType = undefined
|
|
33
|
-
} else if (
|
|
34
|
-
resourceId =
|
|
26
|
+
} else if (isMediaLibraryResource(resource)) {
|
|
27
|
+
resourceId = resource.mediaLibraryId
|
|
35
28
|
resourceType = 'media-library'
|
|
36
|
-
} else if (
|
|
37
|
-
resourceId =
|
|
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
|
|
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),
|
|
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),
|
|
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
|
-
|
|
61
|
-
const
|
|
62
|
-
const mockProjectHandle = {} as ProjectHandle // Use specific type
|
|
52
|
+
const mockInstance = {} as SanityInstance
|
|
53
|
+
const mockOptions = {projectId: 'test-project'}
|
|
63
54
|
|
|
64
|
-
|
|
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,
|
|
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
|
-
|
|
78
|
-
expect(result).toBe(true) // Since getCurrent is mocked to return undefined
|
|
64
|
+
expect(result).toBe(true)
|
|
79
65
|
})
|
|
80
66
|
})
|