@sanity/sdk-react 0.0.0-alpha.26 → 0.0.0-alpha.28
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/dist/_chunks-es/index.js +43 -25
- package/dist/_chunks-es/index.js.map +1 -1
- package/dist/index.d.ts +16 -11
- package/package.json +6 -5
- package/src/context/ResourceProvider.tsx +2 -2
- package/src/hooks/context/useSanityInstance.ts +2 -2
- package/src/hooks/dashboard/useNavigateToStudioDocument.test.ts +120 -4
- package/src/hooks/dashboard/useNavigateToStudioDocument.ts +51 -16
- package/src/hooks/dashboard/useStudioWorkspacesByProjectIdDataset.test.tsx +103 -90
- package/src/hooks/dashboard/useStudioWorkspacesByProjectIdDataset.ts +21 -13
package/dist/index.d.ts
CHANGED
|
@@ -96,6 +96,18 @@ declare interface AuthBoundaryProps {
|
|
|
96
96
|
*/
|
|
97
97
|
export declare type ComlinkStatus = 'idle' | 'handshaking' | 'connected' | 'disconnected'
|
|
98
98
|
|
|
99
|
+
declare interface DashboardResource {
|
|
100
|
+
id: string
|
|
101
|
+
name: string
|
|
102
|
+
title: string
|
|
103
|
+
basePath: string
|
|
104
|
+
projectId: string
|
|
105
|
+
dataset: string
|
|
106
|
+
type: string
|
|
107
|
+
userApplicationId: string
|
|
108
|
+
url: string
|
|
109
|
+
}
|
|
110
|
+
|
|
99
111
|
/** @public */
|
|
100
112
|
declare type DatasetAclMode = 'public' | 'private' | 'custom'
|
|
101
113
|
|
|
@@ -1271,6 +1283,8 @@ declare interface UseManageFavoriteProps extends DocumentHandle {
|
|
|
1271
1283
|
*
|
|
1272
1284
|
* @category Documents
|
|
1273
1285
|
* @param documentHandle - The document handle for the document to navigate to
|
|
1286
|
+
* @param preferredStudioUrl - The preferred studio url to navigate to if you have multiple
|
|
1287
|
+
* studios with the same projectId and dataset
|
|
1274
1288
|
* @returns An object containing:
|
|
1275
1289
|
* - `navigateToStudioDocument` - Function that when called will navigate to the studio document
|
|
1276
1290
|
* - `isConnected` - Boolean indicating if connection to Dashboard is established
|
|
@@ -1292,6 +1306,7 @@ declare interface UseManageFavoriteProps extends DocumentHandle {
|
|
|
1292
1306
|
*/
|
|
1293
1307
|
export declare function useNavigateToStudioDocument(
|
|
1294
1308
|
documentHandle: DocumentHandle,
|
|
1309
|
+
preferredStudioUrl?: string,
|
|
1295
1310
|
): NavigateToStudioResult
|
|
1296
1311
|
|
|
1297
1312
|
/**
|
|
@@ -1854,18 +1869,8 @@ export declare type WindowMessageHandler<TFrameMessage extends FrameMessage> = (
|
|
|
1854
1869
|
event: TFrameMessage['data'],
|
|
1855
1870
|
) => TFrameMessage['response']
|
|
1856
1871
|
|
|
1857
|
-
declare interface Workspace {
|
|
1858
|
-
name: string
|
|
1859
|
-
title: string
|
|
1860
|
-
basePath: string
|
|
1861
|
-
dataset: string
|
|
1862
|
-
userApplicationId: string
|
|
1863
|
-
url: string
|
|
1864
|
-
_ref: string
|
|
1865
|
-
}
|
|
1866
|
-
|
|
1867
1872
|
declare interface WorkspacesByProjectIdDataset {
|
|
1868
|
-
[key: `${string}:${string}`]:
|
|
1873
|
+
[key: `${string}:${string}`]: DashboardResource[]
|
|
1869
1874
|
}
|
|
1870
1875
|
|
|
1871
1876
|
export * from '@sanity/sdk'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/sdk-react",
|
|
3
|
-
"version": "0.0.0-alpha.
|
|
3
|
+
"version": "0.0.0-alpha.28",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Sanity SDK React toolkit for Content OS",
|
|
6
6
|
"keywords": [
|
|
@@ -47,9 +47,10 @@
|
|
|
47
47
|
"@sanity/types": "^3.83.0",
|
|
48
48
|
"@types/lodash-es": "^4.17.12",
|
|
49
49
|
"lodash-es": "^4.17.21",
|
|
50
|
+
"react-compiler-runtime": "19.0.0-beta-ebf51a3-20250411",
|
|
50
51
|
"react-error-boundary": "^5.0.0",
|
|
51
52
|
"rxjs": "^7.8.2",
|
|
52
|
-
"@sanity/sdk": "0.0.0-alpha.
|
|
53
|
+
"@sanity/sdk": "0.0.0-alpha.28"
|
|
53
54
|
},
|
|
54
55
|
"devDependencies": {
|
|
55
56
|
"@sanity/browserslist-config": "^1.0.5",
|
|
@@ -63,15 +64,15 @@
|
|
|
63
64
|
"@types/react-dom": "^19.1.1",
|
|
64
65
|
"@vitejs/plugin-react": "^4.3.4",
|
|
65
66
|
"@vitest/coverage-v8": "3.1.1",
|
|
66
|
-
"babel-plugin-react-compiler": "19.0.0-beta-
|
|
67
|
+
"babel-plugin-react-compiler": "19.0.0-beta-ebf51a3-20250411",
|
|
67
68
|
"eslint": "^9.22.0",
|
|
68
69
|
"jsdom": "^25.0.1",
|
|
69
70
|
"prettier": "^3.5.3",
|
|
70
71
|
"react": "^19.1.0",
|
|
71
72
|
"react-dom": "^19.1.0",
|
|
72
73
|
"typescript": "^5.7.3",
|
|
73
|
-
"vite": "^6.2
|
|
74
|
-
"vitest": "^3.1.
|
|
74
|
+
"vite": "^6.3.2",
|
|
75
|
+
"vitest": "^3.1.2",
|
|
75
76
|
"@repo/config-eslint": "0.0.0",
|
|
76
77
|
"@repo/config-test": "0.0.1",
|
|
77
78
|
"@repo/package.bundle": "3.82.0",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {createSanityInstance, type SanityConfig, type SanityInstance} from '@sanity/sdk'
|
|
2
|
-
import {Suspense,
|
|
2
|
+
import {Suspense, useContext, useEffect, useMemo, useRef} from 'react'
|
|
3
3
|
|
|
4
4
|
import {SanityInstanceContext} from './SanityInstanceContext'
|
|
5
5
|
|
|
@@ -72,7 +72,7 @@ export function ResourceProvider({
|
|
|
72
72
|
fallback,
|
|
73
73
|
...config
|
|
74
74
|
}: ResourceProviderProps): React.ReactNode {
|
|
75
|
-
const parent =
|
|
75
|
+
const parent = useContext(SanityInstanceContext)
|
|
76
76
|
const instance = useMemo(
|
|
77
77
|
() => (parent ? parent.createChild(config) : createSanityInstance(config)),
|
|
78
78
|
[config, parent],
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {type SanityConfig, type SanityInstance} from '@sanity/sdk'
|
|
2
|
-
import {
|
|
2
|
+
import {useContext} from 'react'
|
|
3
3
|
|
|
4
4
|
import {SanityInstanceContext} from '../../context/SanityInstanceContext'
|
|
5
5
|
|
|
@@ -58,7 +58,7 @@ import {SanityInstanceContext} from '../../context/SanityInstanceContext'
|
|
|
58
58
|
* @throws Error if no matching instance is found for the provided config
|
|
59
59
|
*/
|
|
60
60
|
export const useSanityInstance = (config?: SanityConfig): SanityInstance => {
|
|
61
|
-
const instance =
|
|
61
|
+
const instance = useContext(SanityInstanceContext)
|
|
62
62
|
|
|
63
63
|
if (!instance) {
|
|
64
64
|
throw new Error(
|
|
@@ -43,13 +43,13 @@ describe('useNavigateToStudioDocument', () => {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
const mockWorkspace = {
|
|
46
|
+
id: 'workspace123',
|
|
46
47
|
name: 'workspace1',
|
|
47
48
|
title: 'Workspace 1',
|
|
48
49
|
basePath: '/workspace1',
|
|
49
50
|
dataset: 'dataset1',
|
|
50
51
|
userApplicationId: 'user1',
|
|
51
52
|
url: 'https://test.sanity.studio',
|
|
52
|
-
_ref: 'workspace123',
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
beforeEach(() => {
|
|
@@ -129,7 +129,7 @@ describe('useNavigateToStudioDocument', () => {
|
|
|
129
129
|
|
|
130
130
|
it('warns when multiple workspaces are found', () => {
|
|
131
131
|
const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
|
132
|
-
const mockWorkspace2 = {...mockWorkspace,
|
|
132
|
+
const mockWorkspace2 = {...mockWorkspace, id: 'workspace2'}
|
|
133
133
|
|
|
134
134
|
mockWorkspacesByProjectIdAndDataset = {
|
|
135
135
|
'project1:dataset1': [mockWorkspace, mockWorkspace2],
|
|
@@ -145,16 +145,132 @@ describe('useNavigateToStudioDocument', () => {
|
|
|
145
145
|
result.current.navigateToStudioDocument()
|
|
146
146
|
|
|
147
147
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
148
|
-
'Multiple workspaces found for document',
|
|
148
|
+
'Multiple workspaces found for document and no preferred studio url',
|
|
149
149
|
mockDocumentHandle,
|
|
150
150
|
)
|
|
151
151
|
expect(mockSendMessage).toHaveBeenCalledWith(
|
|
152
152
|
'dashboard/v1/bridge/navigate-to-resource',
|
|
153
153
|
expect.objectContaining({
|
|
154
|
-
resourceId: mockWorkspace.
|
|
154
|
+
resourceId: mockWorkspace.id,
|
|
155
155
|
}),
|
|
156
156
|
)
|
|
157
157
|
|
|
158
158
|
consoleSpy.mockRestore()
|
|
159
159
|
})
|
|
160
|
+
|
|
161
|
+
it('warns and does not navigate when projectId or dataset is missing', () => {
|
|
162
|
+
const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
|
163
|
+
|
|
164
|
+
const incompleteDocumentHandle: DocumentHandle = {
|
|
165
|
+
documentId: 'doc123',
|
|
166
|
+
documentType: 'article',
|
|
167
|
+
// missing projectId and dataset
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const {result} = renderHook(() => useNavigateToStudioDocument(incompleteDocumentHandle))
|
|
171
|
+
|
|
172
|
+
// Simulate connection
|
|
173
|
+
act(() => {
|
|
174
|
+
mockStatusCallback?.('connected')
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
result.current.navigateToStudioDocument()
|
|
178
|
+
|
|
179
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
180
|
+
'Project ID and dataset are required to navigate to a studio document',
|
|
181
|
+
)
|
|
182
|
+
expect(mockSendMessage).not.toHaveBeenCalled()
|
|
183
|
+
|
|
184
|
+
consoleSpy.mockRestore()
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
it('uses preferred studio URL when multiple workspaces are available', () => {
|
|
188
|
+
const preferredUrl = 'https://preferred.sanity.studio'
|
|
189
|
+
const mockWorkspace2 = {...mockWorkspace, id: 'workspace2', url: preferredUrl}
|
|
190
|
+
|
|
191
|
+
mockWorkspacesByProjectIdAndDataset = {
|
|
192
|
+
'project1:dataset1': [mockWorkspace, mockWorkspace2],
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const {result} = renderHook(() => useNavigateToStudioDocument(mockDocumentHandle, preferredUrl))
|
|
196
|
+
|
|
197
|
+
// Simulate connection
|
|
198
|
+
act(() => {
|
|
199
|
+
mockStatusCallback?.('connected')
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
result.current.navigateToStudioDocument()
|
|
203
|
+
|
|
204
|
+
// Should choose workspace2 because it matches the preferred URL
|
|
205
|
+
expect(mockSendMessage).toHaveBeenCalledWith(
|
|
206
|
+
'dashboard/v1/bridge/navigate-to-resource',
|
|
207
|
+
expect.objectContaining({
|
|
208
|
+
resourceId: 'workspace2',
|
|
209
|
+
}),
|
|
210
|
+
)
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
it('considers NO_PROJECT_ID:NO_DATASET workspaces when matching preferred URL', () => {
|
|
214
|
+
const preferredUrl = 'https://preferred.sanity.studio'
|
|
215
|
+
// Only have a workspace without projectId/dataset that matches the preferred URL
|
|
216
|
+
const mockWorkspaceNoProject = {
|
|
217
|
+
...mockWorkspace,
|
|
218
|
+
id: 'workspace3',
|
|
219
|
+
url: preferredUrl,
|
|
220
|
+
projectId: undefined,
|
|
221
|
+
dataset: undefined,
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
mockWorkspacesByProjectIdAndDataset = {
|
|
225
|
+
'NO_PROJECT_ID:NO_DATASET': [mockWorkspaceNoProject],
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const {result} = renderHook(() => useNavigateToStudioDocument(mockDocumentHandle, preferredUrl))
|
|
229
|
+
|
|
230
|
+
// Simulate connection
|
|
231
|
+
act(() => {
|
|
232
|
+
mockStatusCallback?.('connected')
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
result.current.navigateToStudioDocument()
|
|
236
|
+
|
|
237
|
+
// Should choose the NO_PROJECT_ID:NO_DATASET workspace because it matches the preferred URL
|
|
238
|
+
expect(mockSendMessage).toHaveBeenCalledWith(
|
|
239
|
+
'dashboard/v1/bridge/navigate-to-resource',
|
|
240
|
+
expect.objectContaining({
|
|
241
|
+
resourceId: 'workspace3',
|
|
242
|
+
}),
|
|
243
|
+
)
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
it('warns with preferred URL info when no matching workspace is found', () => {
|
|
247
|
+
const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
|
248
|
+
const preferredUrl = 'https://nonexistent.sanity.studio'
|
|
249
|
+
|
|
250
|
+
// Set up workspaces that don't match the preferred URL
|
|
251
|
+
mockWorkspacesByProjectIdAndDataset = {
|
|
252
|
+
'project1:dataset1': [
|
|
253
|
+
{
|
|
254
|
+
...mockWorkspace,
|
|
255
|
+
url: 'https://different.sanity.studio',
|
|
256
|
+
},
|
|
257
|
+
],
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const {result} = renderHook(() => useNavigateToStudioDocument(mockDocumentHandle, preferredUrl))
|
|
261
|
+
|
|
262
|
+
// Simulate connection
|
|
263
|
+
act(() => {
|
|
264
|
+
mockStatusCallback?.('connected')
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
result.current.navigateToStudioDocument()
|
|
268
|
+
|
|
269
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
270
|
+
`No workspace found for document with projectId: ${mockDocumentHandle.projectId} and dataset: ${mockDocumentHandle.dataset} or with preferred studio url: ${preferredUrl}`,
|
|
271
|
+
)
|
|
272
|
+
expect(mockSendMessage).not.toHaveBeenCalled()
|
|
273
|
+
|
|
274
|
+
consoleSpy.mockRestore()
|
|
275
|
+
})
|
|
160
276
|
})
|
|
@@ -4,7 +4,10 @@ import {type DocumentHandle} from '@sanity/sdk'
|
|
|
4
4
|
import {useCallback, useState} from 'react'
|
|
5
5
|
|
|
6
6
|
import {useWindowConnection} from '../comlink/useWindowConnection'
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
type DashboardResource,
|
|
9
|
+
useStudioWorkspacesByProjectIdDataset,
|
|
10
|
+
} from './useStudioWorkspacesByProjectIdDataset'
|
|
8
11
|
|
|
9
12
|
/**
|
|
10
13
|
* @public
|
|
@@ -28,6 +31,8 @@ export interface NavigateToStudioResult {
|
|
|
28
31
|
*
|
|
29
32
|
* @category Documents
|
|
30
33
|
* @param documentHandle - The document handle for the document to navigate to
|
|
34
|
+
* @param preferredStudioUrl - The preferred studio url to navigate to if you have multiple
|
|
35
|
+
* studios with the same projectId and dataset
|
|
31
36
|
* @returns An object containing:
|
|
32
37
|
* - `navigateToStudioDocument` - Function that when called will navigate to the studio document
|
|
33
38
|
* - `isConnected` - Boolean indicating if connection to Dashboard is established
|
|
@@ -49,6 +54,7 @@ export interface NavigateToStudioResult {
|
|
|
49
54
|
*/
|
|
50
55
|
export function useNavigateToStudioDocument(
|
|
51
56
|
documentHandle: DocumentHandle,
|
|
57
|
+
preferredStudioUrl?: string,
|
|
52
58
|
): NavigateToStudioResult {
|
|
53
59
|
const {workspacesByProjectIdAndDataset, isConnected: workspacesConnected} =
|
|
54
60
|
useStudioWorkspacesByProjectIdDataset()
|
|
@@ -62,40 +68,69 @@ export function useNavigateToStudioDocument(
|
|
|
62
68
|
const navigateToStudioDocument = useCallback(() => {
|
|
63
69
|
const {projectId, dataset} = documentHandle
|
|
64
70
|
|
|
65
|
-
if (!workspacesConnected || status !== 'connected'
|
|
71
|
+
if (!workspacesConnected || status !== 'connected') {
|
|
72
|
+
// eslint-disable-next-line no-console
|
|
73
|
+
console.warn('Not connected to Dashboard')
|
|
66
74
|
return
|
|
67
75
|
}
|
|
68
76
|
|
|
69
|
-
|
|
70
|
-
const workspaces = workspacesByProjectIdAndDataset[`${projectId}:${dataset}`]
|
|
71
|
-
if (!workspaces?.length) {
|
|
77
|
+
if (!projectId || !dataset) {
|
|
72
78
|
// eslint-disable-next-line no-console
|
|
73
|
-
console.warn(
|
|
74
|
-
`No workspace found for document with projectId: ${projectId} and dataset: ${dataset}`,
|
|
75
|
-
)
|
|
79
|
+
console.warn('Project ID and dataset are required to navigate to a studio document')
|
|
76
80
|
return
|
|
77
81
|
}
|
|
78
82
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
//
|
|
83
|
-
|
|
83
|
+
let workspace: DashboardResource | undefined
|
|
84
|
+
|
|
85
|
+
if (preferredStudioUrl) {
|
|
86
|
+
// Get workspaces matching the projectId:dataset and any workspaces without projectId/dataset,
|
|
87
|
+
// in case there hasn't been a manifest loaded yet
|
|
88
|
+
const allWorkspaces = [
|
|
89
|
+
...(workspacesByProjectIdAndDataset[`${projectId}:${dataset}`] || []),
|
|
90
|
+
...(workspacesByProjectIdAndDataset['NO_PROJECT_ID:NO_DATASET'] || []),
|
|
91
|
+
]
|
|
92
|
+
workspace = allWorkspaces.find((w) => w.url === preferredStudioUrl)
|
|
93
|
+
} else {
|
|
94
|
+
const workspaces = workspacesByProjectIdAndDataset[`${projectId}:${dataset}`]
|
|
95
|
+
if (workspaces?.length > 1) {
|
|
96
|
+
// eslint-disable-next-line no-console
|
|
97
|
+
console.warn(
|
|
98
|
+
'Multiple workspaces found for document and no preferred studio url',
|
|
99
|
+
documentHandle,
|
|
100
|
+
)
|
|
101
|
+
// eslint-disable-next-line no-console
|
|
102
|
+
console.warn('Using the first one', workspaces[0])
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
workspace = workspaces?.[0]
|
|
84
106
|
}
|
|
85
107
|
|
|
86
|
-
|
|
108
|
+
if (!workspace) {
|
|
109
|
+
// eslint-disable-next-line no-console
|
|
110
|
+
console.warn(
|
|
111
|
+
`No workspace found for document with projectId: ${projectId} and dataset: ${dataset}${preferredStudioUrl ? ` or with preferred studio url: ${preferredStudioUrl}` : ''}`,
|
|
112
|
+
)
|
|
113
|
+
return
|
|
114
|
+
}
|
|
87
115
|
|
|
88
116
|
const message: Bridge.Navigation.NavigateToResourceMessage = {
|
|
89
117
|
type: 'dashboard/v1/bridge/navigate-to-resource',
|
|
90
118
|
data: {
|
|
91
|
-
resourceId: workspace.
|
|
119
|
+
resourceId: workspace.id,
|
|
92
120
|
resourceType: 'studio',
|
|
93
121
|
path: `/intent/edit/id=${documentHandle.documentId};type=${documentHandle.documentType}`,
|
|
94
122
|
},
|
|
95
123
|
}
|
|
96
124
|
|
|
97
125
|
sendMessage(message.type, message.data)
|
|
98
|
-
}, [
|
|
126
|
+
}, [
|
|
127
|
+
documentHandle,
|
|
128
|
+
workspacesConnected,
|
|
129
|
+
status,
|
|
130
|
+
workspacesByProjectIdAndDataset,
|
|
131
|
+
sendMessage,
|
|
132
|
+
preferredStudioUrl,
|
|
133
|
+
])
|
|
99
134
|
|
|
100
135
|
return {
|
|
101
136
|
navigateToStudioDocument,
|
|
@@ -13,52 +13,47 @@ const mockWorkspaceData = {
|
|
|
13
13
|
context: {
|
|
14
14
|
availableResources: [
|
|
15
15
|
{
|
|
16
|
+
id: 'user1-workspace1',
|
|
16
17
|
projectId: 'project1',
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
url: 'https://test.sanity.studio',
|
|
25
|
-
_ref: 'user1-workspace1',
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
name: 'workspace2',
|
|
29
|
-
title: 'Workspace 2',
|
|
30
|
-
basePath: '/workspace2',
|
|
31
|
-
dataset: 'dataset1',
|
|
32
|
-
userApplicationId: 'user1',
|
|
33
|
-
url: 'https://test.sanity.studio',
|
|
34
|
-
_ref: 'user1-workspace2',
|
|
35
|
-
},
|
|
36
|
-
],
|
|
18
|
+
dataset: 'dataset1',
|
|
19
|
+
name: 'workspace1',
|
|
20
|
+
title: 'Workspace 1',
|
|
21
|
+
basePath: '/workspace1',
|
|
22
|
+
userApplicationId: 'user1',
|
|
23
|
+
url: 'https://test1.sanity.studio',
|
|
24
|
+
type: 'studio',
|
|
37
25
|
},
|
|
38
26
|
{
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
_ref: 'user2-workspace3',
|
|
49
|
-
},
|
|
50
|
-
],
|
|
27
|
+
id: 'user1-workspace2',
|
|
28
|
+
projectId: 'project1',
|
|
29
|
+
dataset: 'dataset1',
|
|
30
|
+
name: 'workspace2',
|
|
31
|
+
title: 'Workspace 2',
|
|
32
|
+
basePath: '/workspace2',
|
|
33
|
+
userApplicationId: 'user1',
|
|
34
|
+
url: 'https://test2.sanity.studio',
|
|
35
|
+
type: 'studio',
|
|
51
36
|
},
|
|
52
37
|
{
|
|
53
|
-
|
|
54
|
-
projectId: '
|
|
55
|
-
|
|
38
|
+
id: 'user2-workspace3',
|
|
39
|
+
projectId: 'project2',
|
|
40
|
+
dataset: 'dataset2',
|
|
41
|
+
name: 'workspace3',
|
|
42
|
+
title: 'Workspace 3',
|
|
43
|
+
basePath: '/workspace3',
|
|
44
|
+
userApplicationId: 'user2',
|
|
45
|
+
url: 'https://test3.sanity.studio',
|
|
46
|
+
type: 'studio',
|
|
56
47
|
},
|
|
57
48
|
],
|
|
58
49
|
},
|
|
59
50
|
}
|
|
60
51
|
|
|
61
52
|
describe('useStudioWorkspacesByResourceId', () => {
|
|
53
|
+
afterEach(() => {
|
|
54
|
+
vi.clearAllMocks()
|
|
55
|
+
})
|
|
56
|
+
|
|
62
57
|
it('should return empty workspaces and connected=false when not connected', async () => {
|
|
63
58
|
// Create a mock that captures the onStatus callback
|
|
64
59
|
let capturedOnStatus: ((status: Status) => void) | undefined
|
|
@@ -106,33 +101,39 @@ describe('useStudioWorkspacesByResourceId', () => {
|
|
|
106
101
|
expect(result.current.workspacesByProjectIdAndDataset).toEqual({
|
|
107
102
|
'project1:dataset1': [
|
|
108
103
|
{
|
|
104
|
+
id: 'user1-workspace1',
|
|
105
|
+
projectId: 'project1',
|
|
106
|
+
dataset: 'dataset1',
|
|
109
107
|
name: 'workspace1',
|
|
110
108
|
title: 'Workspace 1',
|
|
111
109
|
basePath: '/workspace1',
|
|
112
|
-
dataset: 'dataset1',
|
|
113
110
|
userApplicationId: 'user1',
|
|
114
|
-
url: 'https://
|
|
115
|
-
|
|
111
|
+
url: 'https://test1.sanity.studio',
|
|
112
|
+
type: 'studio',
|
|
116
113
|
},
|
|
117
114
|
{
|
|
115
|
+
id: 'user1-workspace2',
|
|
116
|
+
projectId: 'project1',
|
|
117
|
+
dataset: 'dataset1',
|
|
118
118
|
name: 'workspace2',
|
|
119
119
|
title: 'Workspace 2',
|
|
120
120
|
basePath: '/workspace2',
|
|
121
|
-
dataset: 'dataset1',
|
|
122
121
|
userApplicationId: 'user1',
|
|
123
|
-
url: 'https://
|
|
124
|
-
|
|
122
|
+
url: 'https://test2.sanity.studio',
|
|
123
|
+
type: 'studio',
|
|
125
124
|
},
|
|
126
125
|
],
|
|
127
126
|
'project2:dataset2': [
|
|
128
127
|
{
|
|
128
|
+
id: 'user2-workspace3',
|
|
129
|
+
projectId: 'project2',
|
|
130
|
+
dataset: 'dataset2',
|
|
129
131
|
name: 'workspace3',
|
|
130
132
|
title: 'Workspace 3',
|
|
131
133
|
basePath: '/workspace3',
|
|
132
|
-
dataset: 'dataset2',
|
|
133
134
|
userApplicationId: 'user2',
|
|
134
|
-
url: 'https://
|
|
135
|
-
|
|
135
|
+
url: 'https://test3.sanity.studio',
|
|
136
|
+
type: 'studio',
|
|
136
137
|
},
|
|
137
138
|
],
|
|
138
139
|
})
|
|
@@ -199,60 +200,56 @@ describe('useStudioWorkspacesByResourceId', () => {
|
|
|
199
200
|
})
|
|
200
201
|
})
|
|
201
202
|
|
|
202
|
-
it('should handle
|
|
203
|
-
const
|
|
203
|
+
it('should filter non-studio resources and handle resources without projectId/dataset', async () => {
|
|
204
|
+
const mockDataWithMixedResources = {
|
|
204
205
|
context: {
|
|
205
206
|
availableResources: [
|
|
206
207
|
{
|
|
208
|
+
id: 'studio1',
|
|
207
209
|
projectId: 'project1',
|
|
208
|
-
|
|
210
|
+
dataset: 'dataset1',
|
|
211
|
+
name: 'workspace1',
|
|
212
|
+
title: 'Workspace 1',
|
|
213
|
+
basePath: '/workspace1',
|
|
214
|
+
userApplicationId: 'user1',
|
|
215
|
+
url: 'https://test1.sanity.studio',
|
|
216
|
+
type: 'studio',
|
|
209
217
|
},
|
|
210
|
-
],
|
|
211
|
-
},
|
|
212
|
-
})
|
|
213
|
-
let capturedOnStatus: ((status: Status) => void) | undefined
|
|
214
|
-
|
|
215
|
-
vi.mocked(useWindowConnection).mockImplementation(({onStatus}) => {
|
|
216
|
-
capturedOnStatus = onStatus
|
|
217
|
-
|
|
218
|
-
return {
|
|
219
|
-
fetch: mockFetch,
|
|
220
|
-
sendMessage: vi.fn(),
|
|
221
|
-
} as unknown as WindowConnection<Message>
|
|
222
|
-
})
|
|
223
|
-
|
|
224
|
-
const {result} = renderHook(() => useStudioWorkspacesByProjectIdDataset())
|
|
225
|
-
|
|
226
|
-
// Call onStatus with 'connected' to simulate connected state
|
|
227
|
-
if (capturedOnStatus) capturedOnStatus('connected')
|
|
228
|
-
|
|
229
|
-
await waitFor(() => {
|
|
230
|
-
expect(result.current.workspacesByProjectIdAndDataset).toEqual({})
|
|
231
|
-
expect(result.current.error).toBeNull()
|
|
232
|
-
expect(result.current.isConnected).toBe(true)
|
|
233
|
-
})
|
|
234
|
-
})
|
|
235
|
-
|
|
236
|
-
it('should handle projects without projectId', async () => {
|
|
237
|
-
const mockFetch = vi.fn().mockResolvedValue({
|
|
238
|
-
context: {
|
|
239
|
-
availableResources: [
|
|
240
218
|
{
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
219
|
+
id: 'non-studio',
|
|
220
|
+
projectId: 'project2',
|
|
221
|
+
dataset: 'dataset2',
|
|
222
|
+
name: 'non-studio',
|
|
223
|
+
title: 'Non Studio Resource',
|
|
224
|
+
basePath: '/non-studio',
|
|
225
|
+
userApplicationId: 'user2',
|
|
226
|
+
url: 'https://test2.sanity.studio',
|
|
227
|
+
type: 'other',
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
id: 'studio-no-project',
|
|
231
|
+
name: 'incomplete-workspace',
|
|
232
|
+
title: 'Incomplete Workspace',
|
|
233
|
+
basePath: '/incomplete',
|
|
234
|
+
userApplicationId: 'user3',
|
|
235
|
+
url: 'https://test3.sanity.studio',
|
|
236
|
+
type: 'studio',
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
id: 'studio-no-dataset',
|
|
240
|
+
projectId: 'project3',
|
|
241
|
+
name: 'no-dataset-workspace',
|
|
242
|
+
title: 'No Dataset Workspace',
|
|
243
|
+
basePath: '/no-dataset',
|
|
244
|
+
userApplicationId: 'user4',
|
|
245
|
+
url: 'https://test4.sanity.studio',
|
|
246
|
+
type: 'studio',
|
|
252
247
|
},
|
|
253
248
|
],
|
|
254
249
|
},
|
|
255
|
-
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const mockFetch = vi.fn().mockResolvedValue(mockDataWithMixedResources)
|
|
256
253
|
let capturedOnStatus: ((status: Status) => void) | undefined
|
|
257
254
|
|
|
258
255
|
vi.mocked(useWindowConnection).mockImplementation(({onStatus}) => {
|
|
@@ -270,7 +267,23 @@ describe('useStudioWorkspacesByResourceId', () => {
|
|
|
270
267
|
if (capturedOnStatus) capturedOnStatus('connected')
|
|
271
268
|
|
|
272
269
|
await waitFor(() => {
|
|
273
|
-
|
|
270
|
+
// Should only include the studio resource with valid projectId and dataset
|
|
271
|
+
expect(result.current.workspacesByProjectIdAndDataset['project1:dataset1']).toHaveLength(1)
|
|
272
|
+
expect(result.current.workspacesByProjectIdAndDataset['project1:dataset1'][0].id).toBe(
|
|
273
|
+
'studio1',
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
// Should not include the non-studio resource
|
|
277
|
+
expect(result.current.workspacesByProjectIdAndDataset['project2:dataset2']).toBeUndefined()
|
|
278
|
+
|
|
279
|
+
// Should group resources without projectId or dataset under NO_PROJECT_ID:NO_DATASET
|
|
280
|
+
expect(
|
|
281
|
+
result.current.workspacesByProjectIdAndDataset['NO_PROJECT_ID:NO_DATASET'],
|
|
282
|
+
).toHaveLength(2)
|
|
283
|
+
expect(
|
|
284
|
+
result.current.workspacesByProjectIdAndDataset['NO_PROJECT_ID:NO_DATASET'].map((r) => r.id),
|
|
285
|
+
).toEqual(['studio-no-project', 'studio-no-dataset'])
|
|
286
|
+
|
|
274
287
|
expect(result.current.error).toBeNull()
|
|
275
288
|
expect(result.current.isConnected).toBe(true)
|
|
276
289
|
})
|