@sanity/sdk-react 0.0.0-alpha.21 → 0.0.0-alpha.23
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/index.d.ts +502 -3460
- package/dist/index.js +400 -465
- package/dist/index.js.map +1 -1
- package/package.json +17 -15
- package/src/_exports/index.ts +4 -5
- package/src/components/SDKProvider.test.tsx +78 -54
- package/src/components/SDKProvider.tsx +31 -26
- package/src/components/SanityApp.test.tsx +121 -15
- package/src/components/SanityApp.tsx +26 -15
- package/src/components/auth/AuthBoundary.test.tsx +32 -14
- package/src/components/auth/AuthBoundary.tsx +53 -23
- package/src/components/auth/LoginCallback.test.tsx +19 -6
- package/src/components/auth/LoginCallback.tsx +2 -11
- package/src/components/auth/LoginError.test.tsx +12 -4
- package/src/components/auth/LoginError.tsx +13 -21
- package/src/components/auth/LoginFooter.test.tsx +7 -3
- package/src/context/ResourceProvider.test.tsx +157 -0
- package/src/context/ResourceProvider.tsx +111 -0
- package/src/context/SanityInstanceContext.ts +1 -1
- package/src/hooks/auth/useLoginUrl.tsx +14 -0
- package/src/hooks/client/useClient.ts +2 -1
- package/src/hooks/comlink/useManageFavorite.test.ts +16 -8
- package/src/hooks/comlink/useManageFavorite.ts +37 -13
- package/src/hooks/comlink/useRecordDocumentHistoryEvent.test.ts +8 -4
- package/src/hooks/comlink/useRecordDocumentHistoryEvent.ts +10 -8
- package/src/hooks/context/useSanityInstance.test.tsx +157 -15
- package/src/hooks/context/useSanityInstance.ts +66 -26
- package/src/hooks/dashboard/useNavigateToStudioDocument.test.ts +13 -31
- package/src/hooks/dashboard/useNavigateToStudioDocument.ts +12 -15
- package/src/hooks/dashboard/{useStudioWorkspacesByResourceId.test.tsx → useStudioWorkspacesByProjectIdDataset.test.tsx} +13 -13
- package/src/hooks/dashboard/{useStudioWorkspacesByResourceId.ts → useStudioWorkspacesByProjectIdDataset.ts} +10 -9
- package/src/hooks/datasets/useDatasets.ts +15 -4
- package/src/hooks/document/useApplyDocumentActions.test.ts +4 -9
- package/src/hooks/document/useApplyDocumentActions.ts +6 -31
- package/src/hooks/document/useDocument.test.ts +2 -2
- package/src/hooks/document/useDocument.ts +40 -19
- package/src/hooks/document/useDocumentEvent.test.ts +2 -3
- package/src/hooks/document/useDocumentEvent.ts +7 -11
- package/src/hooks/document/useDocumentPermissions.test.ts +204 -0
- package/src/hooks/document/useDocumentPermissions.ts +31 -23
- package/src/hooks/document/useDocumentSyncStatus.ts +5 -4
- package/src/hooks/document/useEditDocument.test.ts +2 -3
- package/src/hooks/document/useEditDocument.ts +43 -29
- package/src/hooks/documents/useDocuments.test.tsx +30 -3
- package/src/hooks/documents/useDocuments.ts +20 -7
- package/src/hooks/helpers/createCallbackHook.test.tsx +2 -2
- package/src/hooks/helpers/createCallbackHook.tsx +2 -3
- package/src/hooks/helpers/createStateSourceHook.test.tsx +1 -1
- package/src/hooks/helpers/createStateSourceHook.tsx +5 -8
- package/src/hooks/paginatedDocuments/usePaginatedDocuments.test.tsx +43 -18
- package/src/hooks/paginatedDocuments/usePaginatedDocuments.ts +36 -50
- package/src/hooks/preview/usePreview.test.tsx +66 -7
- package/src/hooks/preview/usePreview.tsx +17 -12
- package/src/hooks/projection/useProjection.test.tsx +68 -3
- package/src/hooks/projection/useProjection.ts +21 -24
- package/src/hooks/projects/useProject.ts +7 -4
- package/src/hooks/query/useQuery.ts +32 -14
- package/src/hooks/users/useUsers.test.tsx +330 -0
- package/src/hooks/users/useUsers.ts +65 -52
- package/src/components/Login/LoginLinks.test.tsx +0 -90
- package/src/components/Login/LoginLinks.tsx +0 -58
- package/src/components/auth/Login.test.tsx +0 -27
- package/src/components/auth/Login.tsx +0 -39
- package/src/components/auth/LoginLayout.test.tsx +0 -19
- package/src/components/auth/LoginLayout.tsx +0 -69
- package/src/components/auth/authTestHelpers.tsx +0 -11
- package/src/context/SanityProvider.test.tsx +0 -25
- package/src/context/SanityProvider.tsx +0 -50
- package/src/hooks/auth/useLoginUrls.test.tsx +0 -68
- package/src/hooks/auth/useLoginUrls.tsx +0 -52
- package/src/hooks/users/useUsers.test.ts +0 -163
|
@@ -8,7 +8,7 @@ import {useNavigateToStudioDocument} from './useNavigateToStudioDocument'
|
|
|
8
8
|
// Mock dependencies
|
|
9
9
|
const mockSendMessage = vi.fn()
|
|
10
10
|
const mockFetch = vi.fn()
|
|
11
|
-
let
|
|
11
|
+
let mockWorkspacesByProjectIdAndDataset = {}
|
|
12
12
|
let mockWorkspacesIsConnected = true
|
|
13
13
|
let mockStatusCallback: ((status: Status) => void) | null = null
|
|
14
14
|
|
|
@@ -24,10 +24,10 @@ vi.mock('../comlink/useWindowConnection', () => {
|
|
|
24
24
|
}
|
|
25
25
|
})
|
|
26
26
|
|
|
27
|
-
vi.mock('./
|
|
27
|
+
vi.mock('./useStudioWorkspacesByProjectIdDataset', () => {
|
|
28
28
|
return {
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
useStudioWorkspacesByProjectIdDataset: () => ({
|
|
30
|
+
workspacesByProjectIdAndDataset: mockWorkspacesByProjectIdAndDataset,
|
|
31
31
|
error: null,
|
|
32
32
|
isConnected: mockWorkspacesIsConnected,
|
|
33
33
|
}),
|
|
@@ -36,9 +36,10 @@ vi.mock('./useStudioWorkspacesByResourceId', () => {
|
|
|
36
36
|
|
|
37
37
|
describe('useNavigateToStudioDocument', () => {
|
|
38
38
|
const mockDocumentHandle: DocumentHandle = {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
documentId: 'doc123',
|
|
40
|
+
documentType: 'article',
|
|
41
|
+
projectId: 'project1',
|
|
42
|
+
dataset: 'dataset1',
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
const mockWorkspace = {
|
|
@@ -53,7 +54,7 @@ describe('useNavigateToStudioDocument', () => {
|
|
|
53
54
|
|
|
54
55
|
beforeEach(() => {
|
|
55
56
|
vi.resetAllMocks()
|
|
56
|
-
|
|
57
|
+
mockWorkspacesByProjectIdAndDataset = {
|
|
57
58
|
'project1:dataset1': [mockWorkspace],
|
|
58
59
|
}
|
|
59
60
|
mockWorkspacesIsConnected = true
|
|
@@ -95,7 +96,7 @@ describe('useNavigateToStudioDocument', () => {
|
|
|
95
96
|
})
|
|
96
97
|
|
|
97
98
|
it('does not send message when not connected', () => {
|
|
98
|
-
|
|
99
|
+
mockWorkspacesByProjectIdAndDataset = {}
|
|
99
100
|
mockWorkspacesIsConnected = false
|
|
100
101
|
|
|
101
102
|
const {result} = renderHook(() => useNavigateToStudioDocument(mockDocumentHandle))
|
|
@@ -111,7 +112,7 @@ describe('useNavigateToStudioDocument', () => {
|
|
|
111
112
|
})
|
|
112
113
|
|
|
113
114
|
it('does not send message when no workspace is found', () => {
|
|
114
|
-
|
|
115
|
+
mockWorkspacesByProjectIdAndDataset = {}
|
|
115
116
|
mockWorkspacesIsConnected = true
|
|
116
117
|
|
|
117
118
|
const {result} = renderHook(() => useNavigateToStudioDocument(mockDocumentHandle))
|
|
@@ -126,30 +127,11 @@ describe('useNavigateToStudioDocument', () => {
|
|
|
126
127
|
expect(mockSendMessage).not.toHaveBeenCalled()
|
|
127
128
|
})
|
|
128
129
|
|
|
129
|
-
it('handles invalid resourceId format', () => {
|
|
130
|
-
const invalidDocHandle: DocumentHandle = {
|
|
131
|
-
_id: 'doc123',
|
|
132
|
-
_type: 'article',
|
|
133
|
-
resourceId: 'document:project1.invalid:doc123' as `document:${string}.${string}:${string}`,
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const {result} = renderHook(() => useNavigateToStudioDocument(invalidDocHandle))
|
|
137
|
-
|
|
138
|
-
// Simulate connection
|
|
139
|
-
act(() => {
|
|
140
|
-
mockStatusCallback?.('connected')
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
result.current.navigateToStudioDocument()
|
|
144
|
-
|
|
145
|
-
expect(mockSendMessage).not.toHaveBeenCalled()
|
|
146
|
-
})
|
|
147
|
-
|
|
148
130
|
it('warns when multiple workspaces are found', () => {
|
|
149
131
|
const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
|
150
132
|
const mockWorkspace2 = {...mockWorkspace, _ref: 'workspace2'}
|
|
151
133
|
|
|
152
|
-
|
|
134
|
+
mockWorkspacesByProjectIdAndDataset = {
|
|
153
135
|
'project1:dataset1': [mockWorkspace, mockWorkspace2],
|
|
154
136
|
}
|
|
155
137
|
|
|
@@ -164,7 +146,7 @@ describe('useNavigateToStudioDocument', () => {
|
|
|
164
146
|
|
|
165
147
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
166
148
|
'Multiple workspaces found for document',
|
|
167
|
-
mockDocumentHandle
|
|
149
|
+
mockDocumentHandle,
|
|
168
150
|
)
|
|
169
151
|
expect(mockSendMessage).toHaveBeenCalledWith(
|
|
170
152
|
'dashboard/v1/bridge/navigate-to-resource',
|
|
@@ -4,7 +4,7 @@ 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 {useStudioWorkspacesByProjectIdDataset} from './useStudioWorkspacesByProjectIdDataset'
|
|
8
8
|
|
|
9
9
|
interface NavigateToResourceMessage {
|
|
10
10
|
type: 'dashboard/v1/bridge/navigate-to-resource'
|
|
@@ -66,8 +66,8 @@ interface NavigateToStudioResult {
|
|
|
66
66
|
export function useNavigateToStudioDocument(
|
|
67
67
|
documentHandle: DocumentHandle,
|
|
68
68
|
): NavigateToStudioResult {
|
|
69
|
-
const {
|
|
70
|
-
|
|
69
|
+
const {workspacesByProjectIdAndDataset, isConnected: workspacesConnected} =
|
|
70
|
+
useStudioWorkspacesByProjectIdDataset()
|
|
71
71
|
const [status, setStatus] = useState<Status>('idle')
|
|
72
72
|
const {sendMessage} = useWindowConnection<NavigateToResourceMessage, never>({
|
|
73
73
|
name: SDK_NODE_NAME,
|
|
@@ -76,28 +76,25 @@ export function useNavigateToStudioDocument(
|
|
|
76
76
|
})
|
|
77
77
|
|
|
78
78
|
const navigateToStudioDocument = useCallback(() => {
|
|
79
|
-
|
|
80
|
-
return
|
|
81
|
-
}
|
|
79
|
+
const {projectId, dataset} = documentHandle
|
|
82
80
|
|
|
83
|
-
|
|
84
|
-
const [, projectAndDataset] = documentHandle.resourceId.split(':')
|
|
85
|
-
const [projectId, dataset] = projectAndDataset.split('.')
|
|
86
|
-
if (!projectId || !dataset) {
|
|
81
|
+
if (!workspacesConnected || status !== 'connected' || !projectId || !dataset) {
|
|
87
82
|
return
|
|
88
83
|
}
|
|
89
84
|
|
|
90
85
|
// Find the workspace for this document
|
|
91
|
-
const workspaces =
|
|
86
|
+
const workspaces = workspacesByProjectIdAndDataset[`${projectId}:${dataset}`]
|
|
92
87
|
if (!workspaces?.length) {
|
|
93
88
|
// eslint-disable-next-line no-console
|
|
94
|
-
console.warn(
|
|
89
|
+
console.warn(
|
|
90
|
+
`No workspace found for document with projectId: ${projectId} and dataset: ${dataset}`,
|
|
91
|
+
)
|
|
95
92
|
return
|
|
96
93
|
}
|
|
97
94
|
|
|
98
95
|
if (workspaces.length > 1) {
|
|
99
96
|
// eslint-disable-next-line no-console
|
|
100
|
-
console.warn('Multiple workspaces found for document', documentHandle
|
|
97
|
+
console.warn('Multiple workspaces found for document', documentHandle)
|
|
101
98
|
// eslint-disable-next-line no-console
|
|
102
99
|
console.warn('Using the first one', workspaces[0])
|
|
103
100
|
}
|
|
@@ -109,12 +106,12 @@ export function useNavigateToStudioDocument(
|
|
|
109
106
|
data: {
|
|
110
107
|
resourceId: workspace._ref,
|
|
111
108
|
resourceType: 'studio',
|
|
112
|
-
path: `/intent/edit/id=${documentHandle.
|
|
109
|
+
path: `/intent/edit/id=${documentHandle.documentId};type=${documentHandle.documentType}`,
|
|
113
110
|
},
|
|
114
111
|
}
|
|
115
112
|
|
|
116
113
|
sendMessage(message.type, message.data)
|
|
117
|
-
}, [documentHandle, workspacesConnected, status,
|
|
114
|
+
}, [documentHandle, workspacesConnected, status, workspacesByProjectIdAndDataset, sendMessage])
|
|
118
115
|
|
|
119
116
|
return {
|
|
120
117
|
navigateToStudioDocument,
|
|
@@ -3,7 +3,7 @@ import {renderHook, waitFor} from '@testing-library/react'
|
|
|
3
3
|
import {describe, expect, it, vi} from 'vitest'
|
|
4
4
|
|
|
5
5
|
import {useWindowConnection, type WindowConnection} from '../comlink/useWindowConnection'
|
|
6
|
-
import {
|
|
6
|
+
import {useStudioWorkspacesByProjectIdDataset} from './useStudioWorkspacesByProjectIdDataset'
|
|
7
7
|
|
|
8
8
|
vi.mock('../comlink/useWindowConnection', () => ({
|
|
9
9
|
useWindowConnection: vi.fn(),
|
|
@@ -72,13 +72,13 @@ describe('useStudioWorkspacesByResourceId', () => {
|
|
|
72
72
|
} as unknown as WindowConnection<Message>
|
|
73
73
|
})
|
|
74
74
|
|
|
75
|
-
const {result} = renderHook(() =>
|
|
75
|
+
const {result} = renderHook(() => useStudioWorkspacesByProjectIdDataset())
|
|
76
76
|
|
|
77
77
|
// Call onStatus with 'idle' to simulate not connected
|
|
78
78
|
if (capturedOnStatus) capturedOnStatus('idle')
|
|
79
79
|
|
|
80
80
|
expect(result.current).toEqual({
|
|
81
|
-
|
|
81
|
+
workspacesByProjectIdAndDataset: {},
|
|
82
82
|
error: null,
|
|
83
83
|
isConnected: false,
|
|
84
84
|
})
|
|
@@ -97,13 +97,13 @@ describe('useStudioWorkspacesByResourceId', () => {
|
|
|
97
97
|
} as unknown as WindowConnection<Message>
|
|
98
98
|
})
|
|
99
99
|
|
|
100
|
-
const {result} = renderHook(() =>
|
|
100
|
+
const {result} = renderHook(() => useStudioWorkspacesByProjectIdDataset())
|
|
101
101
|
|
|
102
102
|
// Call onStatus with 'connected' to simulate connected state
|
|
103
103
|
if (capturedOnStatus) capturedOnStatus('connected')
|
|
104
104
|
|
|
105
105
|
await waitFor(() => {
|
|
106
|
-
expect(result.current.
|
|
106
|
+
expect(result.current.workspacesByProjectIdAndDataset).toEqual({
|
|
107
107
|
'project1:dataset1': [
|
|
108
108
|
{
|
|
109
109
|
name: 'workspace1',
|
|
@@ -160,13 +160,13 @@ describe('useStudioWorkspacesByResourceId', () => {
|
|
|
160
160
|
} as unknown as WindowConnection<Message>
|
|
161
161
|
})
|
|
162
162
|
|
|
163
|
-
const {result} = renderHook(() =>
|
|
163
|
+
const {result} = renderHook(() => useStudioWorkspacesByProjectIdDataset())
|
|
164
164
|
|
|
165
165
|
// Call onStatus with 'connected' to simulate connected state
|
|
166
166
|
if (capturedOnStatus) capturedOnStatus('connected')
|
|
167
167
|
|
|
168
168
|
await waitFor(() => {
|
|
169
|
-
expect(result.current.
|
|
169
|
+
expect(result.current.workspacesByProjectIdAndDataset).toEqual({})
|
|
170
170
|
expect(result.current.error).toBe('Failed to fetch workspaces')
|
|
171
171
|
expect(result.current.isConnected).toBe(true)
|
|
172
172
|
})
|
|
@@ -187,13 +187,13 @@ describe('useStudioWorkspacesByResourceId', () => {
|
|
|
187
187
|
} as unknown as WindowConnection<Message>
|
|
188
188
|
})
|
|
189
189
|
|
|
190
|
-
const {result} = renderHook(() =>
|
|
190
|
+
const {result} = renderHook(() => useStudioWorkspacesByProjectIdDataset())
|
|
191
191
|
|
|
192
192
|
// Call onStatus with 'connected' to simulate connected state
|
|
193
193
|
if (capturedOnStatus) capturedOnStatus('connected')
|
|
194
194
|
|
|
195
195
|
await waitFor(() => {
|
|
196
|
-
expect(result.current.
|
|
196
|
+
expect(result.current.workspacesByProjectIdAndDataset).toEqual({})
|
|
197
197
|
expect(result.current.error).toBeNull()
|
|
198
198
|
expect(result.current.isConnected).toBe(true)
|
|
199
199
|
})
|
|
@@ -221,13 +221,13 @@ describe('useStudioWorkspacesByResourceId', () => {
|
|
|
221
221
|
} as unknown as WindowConnection<Message>
|
|
222
222
|
})
|
|
223
223
|
|
|
224
|
-
const {result} = renderHook(() =>
|
|
224
|
+
const {result} = renderHook(() => useStudioWorkspacesByProjectIdDataset())
|
|
225
225
|
|
|
226
226
|
// Call onStatus with 'connected' to simulate connected state
|
|
227
227
|
if (capturedOnStatus) capturedOnStatus('connected')
|
|
228
228
|
|
|
229
229
|
await waitFor(() => {
|
|
230
|
-
expect(result.current.
|
|
230
|
+
expect(result.current.workspacesByProjectIdAndDataset).toEqual({})
|
|
231
231
|
expect(result.current.error).toBeNull()
|
|
232
232
|
expect(result.current.isConnected).toBe(true)
|
|
233
233
|
})
|
|
@@ -264,13 +264,13 @@ describe('useStudioWorkspacesByResourceId', () => {
|
|
|
264
264
|
} as unknown as WindowConnection<Message>
|
|
265
265
|
})
|
|
266
266
|
|
|
267
|
-
const {result} = renderHook(() =>
|
|
267
|
+
const {result} = renderHook(() => useStudioWorkspacesByProjectIdDataset())
|
|
268
268
|
|
|
269
269
|
// Call onStatus with 'connected' to simulate connected state
|
|
270
270
|
if (capturedOnStatus) capturedOnStatus('connected')
|
|
271
271
|
|
|
272
272
|
await waitFor(() => {
|
|
273
|
-
expect(result.current.
|
|
273
|
+
expect(result.current.workspacesByProjectIdAndDataset).toEqual({})
|
|
274
274
|
expect(result.current.error).toBeNull()
|
|
275
275
|
expect(result.current.isConnected).toBe(true)
|
|
276
276
|
})
|
|
@@ -14,12 +14,12 @@ interface Workspace {
|
|
|
14
14
|
_ref: string
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
interface
|
|
18
|
-
[key: string]: Workspace[] // key format: `${projectId}:${dataset}`
|
|
17
|
+
interface WorkspacesByProjectIdDataset {
|
|
18
|
+
[key: `${string}:${string}`]: Workspace[] // key format: `${projectId}:${dataset}`
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
interface StudioWorkspacesResult {
|
|
22
|
-
|
|
22
|
+
workspacesByProjectIdAndDataset: WorkspacesByProjectIdDataset
|
|
23
23
|
error: string | null
|
|
24
24
|
isConnected: boolean
|
|
25
25
|
}
|
|
@@ -28,8 +28,9 @@ interface StudioWorkspacesResult {
|
|
|
28
28
|
* Hook that fetches studio workspaces and organizes them by projectId:dataset
|
|
29
29
|
* @internal
|
|
30
30
|
*/
|
|
31
|
-
export function
|
|
32
|
-
const [
|
|
31
|
+
export function useStudioWorkspacesByProjectIdDataset(): StudioWorkspacesResult {
|
|
32
|
+
const [workspacesByProjectIdAndDataset, setWorkspacesByProjectIdAndDataset] =
|
|
33
|
+
useState<WorkspacesByProjectIdDataset>({})
|
|
33
34
|
const [status, setStatus] = useState<Status>('idle')
|
|
34
35
|
const [error, setError] = useState<string | null>(null)
|
|
35
36
|
|
|
@@ -50,13 +51,13 @@ export function useStudioWorkspacesByResourceId(): StudioWorkspacesResult {
|
|
|
50
51
|
context: {availableResources: Array<{projectId: string; workspaces: Workspace[]}>}
|
|
51
52
|
}>('dashboard/v1/bridge/context', undefined, {signal})
|
|
52
53
|
|
|
53
|
-
const workspaceMap:
|
|
54
|
+
const workspaceMap: WorkspacesByProjectIdDataset = {}
|
|
54
55
|
|
|
55
56
|
data.context.availableResources.forEach((resource) => {
|
|
56
57
|
if (!resource.projectId || !resource.workspaces?.length) return
|
|
57
58
|
|
|
58
59
|
resource.workspaces.forEach((workspace) => {
|
|
59
|
-
const key = `${resource.projectId}:${workspace.dataset}`
|
|
60
|
+
const key = `${resource.projectId}:${workspace.dataset}` as const
|
|
60
61
|
if (!workspaceMap[key]) {
|
|
61
62
|
workspaceMap[key] = []
|
|
62
63
|
}
|
|
@@ -64,7 +65,7 @@ export function useStudioWorkspacesByResourceId(): StudioWorkspacesResult {
|
|
|
64
65
|
})
|
|
65
66
|
})
|
|
66
67
|
|
|
67
|
-
|
|
68
|
+
setWorkspacesByProjectIdAndDataset(workspaceMap)
|
|
68
69
|
setError(null)
|
|
69
70
|
} catch (err: unknown) {
|
|
70
71
|
if (err instanceof Error) {
|
|
@@ -85,7 +86,7 @@ export function useStudioWorkspacesByResourceId(): StudioWorkspacesResult {
|
|
|
85
86
|
}, [fetch, status])
|
|
86
87
|
|
|
87
88
|
return {
|
|
88
|
-
|
|
89
|
+
workspacesByProjectIdAndDataset,
|
|
89
90
|
error,
|
|
90
91
|
isConnected: status === 'connected',
|
|
91
92
|
}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import {type DatasetsResponse} from '@sanity/client'
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
getDatasetsState,
|
|
4
|
+
type ProjectHandle,
|
|
5
|
+
resolveDatasets,
|
|
6
|
+
type SanityInstance,
|
|
7
|
+
type StateSource,
|
|
8
|
+
} from '@sanity/sdk'
|
|
3
9
|
|
|
4
10
|
import {createStateSourceHook} from '../helpers/createStateSourceHook'
|
|
5
11
|
|
|
@@ -33,8 +39,13 @@ type UseDatasets = {
|
|
|
33
39
|
* @function
|
|
34
40
|
*/
|
|
35
41
|
export const useDatasets: UseDatasets = createStateSourceHook({
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
42
|
+
getState: getDatasetsState as (
|
|
43
|
+
instance: SanityInstance,
|
|
44
|
+
projectHandle?: ProjectHandle,
|
|
45
|
+
) => StateSource<DatasetsResponse>,
|
|
46
|
+
shouldSuspend: (instance, projectHandle?: ProjectHandle) =>
|
|
47
|
+
// remove `undefined` since we're suspending when that is the case
|
|
48
|
+
getDatasetsState(instance, projectHandle).getCurrent() === undefined,
|
|
39
49
|
suspender: resolveDatasets,
|
|
50
|
+
getConfig: (projectHandle?: ProjectHandle) => projectHandle,
|
|
40
51
|
})
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {applyDocumentActions
|
|
1
|
+
import {applyDocumentActions} from '@sanity/sdk'
|
|
2
2
|
import {describe, it} from 'vitest'
|
|
3
3
|
|
|
4
4
|
import {createCallbackHook} from '../helpers/createCallbackHook'
|
|
@@ -12,14 +12,9 @@ vi.mock('@sanity/sdk', async (importOriginal) => {
|
|
|
12
12
|
})
|
|
13
13
|
|
|
14
14
|
describe('useApplyDocumentActions', () => {
|
|
15
|
-
it('calls `createCallbackHook` with `
|
|
16
|
-
const {useApplyDocumentActions} = await import('./useApplyDocumentActions')
|
|
17
|
-
const resourceId: ResourceId = 'project1.dataset1'
|
|
15
|
+
it('calls `createCallbackHook` with `applyActions`', async () => {
|
|
18
16
|
expect(createCallbackHook).not.toHaveBeenCalled()
|
|
19
|
-
|
|
20
|
-
expect(
|
|
21
|
-
const apply = useApplyDocumentActions(resourceId)
|
|
22
|
-
apply(createDocument({_type: 'author'}))
|
|
23
|
-
expect(applyDocumentActions).toHaveBeenCalledWith(createDocument({_type: 'author'}))
|
|
17
|
+
await import('./useApplyDocumentActions')
|
|
18
|
+
expect(createCallbackHook).toHaveBeenCalledWith(applyDocumentActions)
|
|
24
19
|
})
|
|
25
20
|
})
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type ActionsResult,
|
|
3
|
-
applyDocumentActions,
|
|
4
|
-
type ApplyDocumentActionsOptions,
|
|
5
|
-
type DocumentAction,
|
|
6
|
-
type ResourceId,
|
|
7
|
-
} from '@sanity/sdk'
|
|
8
|
-
import {type SanityDocument} from '@sanity/types'
|
|
1
|
+
import {applyDocumentActions} from '@sanity/sdk'
|
|
9
2
|
|
|
10
3
|
import {createCallbackHook} from '../helpers/createCallbackHook'
|
|
11
4
|
|
|
@@ -16,7 +9,7 @@ import {createCallbackHook} from '../helpers/createCallbackHook'
|
|
|
16
9
|
* Provides a callback for applying one or more actions to a document.
|
|
17
10
|
*
|
|
18
11
|
* @category Documents
|
|
19
|
-
* @param
|
|
12
|
+
* @param dataset - An optional dataset handle with projectId and dataset. If not provided, the nearest SanityInstance from context will be used.
|
|
20
13
|
* @returns A function that takes one more more {@link DocumentAction}s and returns a promise that resolves to an {@link ActionsResult}.
|
|
21
14
|
* @example Publish or unpublish a document
|
|
22
15
|
* ```
|
|
@@ -24,7 +17,7 @@ import {createCallbackHook} from '../helpers/createCallbackHook'
|
|
|
24
17
|
* import { useApplyDocumentActions } from '@sanity/sdk-react'
|
|
25
18
|
*
|
|
26
19
|
* const apply = useApplyDocumentActions()
|
|
27
|
-
* const myDocument = {
|
|
20
|
+
* const myDocument = { documentId: 'my-document-id', documentType: 'my-document-type' }
|
|
28
21
|
*
|
|
29
22
|
* return (
|
|
30
23
|
* <button onClick={() => apply(publishDocument(myDocument))}>Publish</button>
|
|
@@ -40,7 +33,7 @@ import {createCallbackHook} from '../helpers/createCallbackHook'
|
|
|
40
33
|
* const apply = useApplyDocumentActions()
|
|
41
34
|
*
|
|
42
35
|
* const handleCreateAndPublish = () => {
|
|
43
|
-
* const handle = {
|
|
36
|
+
* const handle = { documentId: window.crypto.randomUUID(), documentType: 'my-document-type' }
|
|
44
37
|
* apply([
|
|
45
38
|
* createDocument(handle),
|
|
46
39
|
* publishDocument(handle),
|
|
@@ -49,27 +42,9 @@ import {createCallbackHook} from '../helpers/createCallbackHook'
|
|
|
49
42
|
*
|
|
50
43
|
* return (
|
|
51
44
|
* <button onClick={handleCreateAndPublish}>
|
|
52
|
-
* I
|
|
45
|
+
* I'm feeling lucky
|
|
53
46
|
* </button>
|
|
54
47
|
* )
|
|
55
48
|
* ```
|
|
56
49
|
*/
|
|
57
|
-
export
|
|
58
|
-
resourceId?: ResourceId,
|
|
59
|
-
): <TDocument extends SanityDocument>(
|
|
60
|
-
action: DocumentAction<TDocument> | DocumentAction<TDocument>[],
|
|
61
|
-
options?: ApplyDocumentActionsOptions,
|
|
62
|
-
) => Promise<ActionsResult<TDocument>>
|
|
63
|
-
|
|
64
|
-
/** @beta */
|
|
65
|
-
export function useApplyDocumentActions(
|
|
66
|
-
resourceId?: ResourceId,
|
|
67
|
-
): (
|
|
68
|
-
action: DocumentAction | DocumentAction[],
|
|
69
|
-
options?: ApplyDocumentActionsOptions,
|
|
70
|
-
) => Promise<ActionsResult> {
|
|
71
|
-
return _useApplyDocumentActions(resourceId)()
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const _useApplyDocumentActions = (resourceId?: ResourceId) =>
|
|
75
|
-
createCallbackHook(applyDocumentActions, resourceId)
|
|
50
|
+
export const useApplyDocumentActions = createCallbackHook(applyDocumentActions)
|
|
@@ -46,7 +46,7 @@ describe('useDocument hook', () => {
|
|
|
46
46
|
subscribe,
|
|
47
47
|
} as unknown as StateSource<unknown>)
|
|
48
48
|
|
|
49
|
-
const {result} = renderHook(() => useDocument({
|
|
49
|
+
const {result} = renderHook(() => useDocument({documentId: 'doc1', documentType: 'book'}))
|
|
50
50
|
|
|
51
51
|
expect(result.current).toEqual(doc)
|
|
52
52
|
expect(getCurrent).toHaveBeenCalled()
|
|
@@ -69,7 +69,7 @@ describe('useDocument hook', () => {
|
|
|
69
69
|
// Render the hook and capture the thrown promise.
|
|
70
70
|
const {result} = renderHook(() => {
|
|
71
71
|
try {
|
|
72
|
-
return useDocument({
|
|
72
|
+
return useDocument({documentId: 'doc1', documentType: 'book'})
|
|
73
73
|
} catch (e) {
|
|
74
74
|
return e
|
|
75
75
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type DocumentHandle,
|
|
3
3
|
getDocumentState,
|
|
4
|
-
getResourceId,
|
|
5
4
|
type JsonMatch,
|
|
6
5
|
type JsonMatchPath,
|
|
7
6
|
resolveDocument,
|
|
8
7
|
} from '@sanity/sdk'
|
|
9
8
|
import {type SanityDocument} from '@sanity/types'
|
|
9
|
+
import {identity} from 'rxjs'
|
|
10
10
|
|
|
11
11
|
import {createStateSourceHook} from '../helpers/createStateSourceHook'
|
|
12
12
|
|
|
@@ -16,20 +16,26 @@ import {createStateSourceHook} from '../helpers/createStateSourceHook'
|
|
|
16
16
|
* ## useDocument(doc, path)
|
|
17
17
|
* Read and subscribe to nested values in a document
|
|
18
18
|
* @category Documents
|
|
19
|
-
* @param doc - The document to read state from
|
|
20
|
-
* the document will be read from the specified Sanity project and dataset that is included in the handle. If no `resourceId` is provided, the default project and dataset from your `SanityApp` configuration will be used.
|
|
19
|
+
* @param doc - The document to read state from, specified as a DocumentHandle
|
|
21
20
|
* @param path - The path to the nested value to read from
|
|
22
21
|
* @returns The value at the specified path
|
|
23
22
|
* @example
|
|
24
23
|
* ```tsx
|
|
25
|
-
* import {
|
|
24
|
+
* import {useDocument} from '@sanity/sdk-react'
|
|
26
25
|
*
|
|
27
|
-
*
|
|
26
|
+
* const documentHandle = {
|
|
27
|
+
* documentId: 'order-123',
|
|
28
|
+
* documentType: 'order',
|
|
29
|
+
* projectId: 'abc123',
|
|
30
|
+
* dataset: 'production'
|
|
31
|
+
* }
|
|
32
|
+
*
|
|
33
|
+
* function OrderLink() {
|
|
28
34
|
* const title = useDocument(documentHandle, 'title')
|
|
29
35
|
* const id = useDocument(documentHandle, '_id')
|
|
30
36
|
*
|
|
31
37
|
* return (
|
|
32
|
-
* <a href
|
|
38
|
+
* <a href={`/order/${id}`}>Order {title} today!</a>
|
|
33
39
|
* )
|
|
34
40
|
* }
|
|
35
41
|
* ```
|
|
@@ -44,11 +50,11 @@ export function useDocument<
|
|
|
44
50
|
* @beta
|
|
45
51
|
* ## useDocument(doc)
|
|
46
52
|
* Read and subscribe to an entire document
|
|
47
|
-
* @param doc - The document to read state from
|
|
53
|
+
* @param doc - The document to read state from, specified as a DocumentHandle
|
|
48
54
|
* @returns The document state as an object
|
|
49
55
|
* @example
|
|
50
56
|
* ```tsx
|
|
51
|
-
* import {type SanityDocument,
|
|
57
|
+
* import {type SanityDocument, useDocument} from '@sanity/sdk-react'
|
|
52
58
|
*
|
|
53
59
|
* interface Book extends SanityDocument {
|
|
54
60
|
* title: string
|
|
@@ -56,19 +62,30 @@ export function useDocument<
|
|
|
56
62
|
* summary: string
|
|
57
63
|
* }
|
|
58
64
|
*
|
|
59
|
-
*
|
|
65
|
+
* const documentHandle = {
|
|
66
|
+
* documentId: 'book-123',
|
|
67
|
+
* documentType: 'book',
|
|
68
|
+
* projectId: 'abc123',
|
|
69
|
+
* dataset: 'production'
|
|
70
|
+
* }
|
|
71
|
+
*
|
|
72
|
+
* function DocumentView() {
|
|
60
73
|
* const book = useDocument<Book>(documentHandle)
|
|
61
74
|
*
|
|
75
|
+
* if (!book) {
|
|
76
|
+
* return <div>Loading...</div>
|
|
77
|
+
* }
|
|
78
|
+
*
|
|
62
79
|
* return (
|
|
63
80
|
* <article>
|
|
64
|
-
* <h1>{book
|
|
65
|
-
* <address>By {book
|
|
81
|
+
* <h1>{book.title}</h1>
|
|
82
|
+
* <address>By {book.author}</address>
|
|
66
83
|
*
|
|
67
84
|
* <h2>Summary</h2>
|
|
68
|
-
* {book
|
|
85
|
+
* {book.summary}
|
|
69
86
|
*
|
|
70
87
|
* <h2>Order</h2>
|
|
71
|
-
* <a href
|
|
88
|
+
* <a href={`/order/${book._id}`}>Order {book.title} today!</a>
|
|
72
89
|
* </article>
|
|
73
90
|
* )
|
|
74
91
|
* }
|
|
@@ -81,17 +98,21 @@ export function useDocument<TDocument extends SanityDocument>(
|
|
|
81
98
|
|
|
82
99
|
/**
|
|
83
100
|
* @beta
|
|
84
|
-
* Reads and subscribes to a document
|
|
85
|
-
* When called with a `path` argument, the hook will return the nested value
|
|
86
|
-
* When called without a `path` argument, the entire document
|
|
101
|
+
* Reads and subscribes to a document's realtime state, incorporating both local and remote changes.
|
|
102
|
+
* When called with a `path` argument, the hook will return the nested value's state.
|
|
103
|
+
* When called without a `path` argument, the entire document's state will be returned.
|
|
87
104
|
*
|
|
88
105
|
* @remarks
|
|
89
106
|
* `useDocument` is designed to be used within a realtime context in which local updates to documents
|
|
90
107
|
* need to be displayed before they are persisted to the remote copy. This can be useful within a collaborative
|
|
91
108
|
* or realtime editing interface where local changes need to be reflected immediately.
|
|
92
109
|
*
|
|
110
|
+
* The hook automatically uses the correct Sanity instance based on the project and dataset
|
|
111
|
+
* specified in the DocumentHandle. This makes it easy to work with documents from different
|
|
112
|
+
* projects or datasets in the same component.
|
|
113
|
+
*
|
|
93
114
|
* However, this hook can be too resource intensive for applications where static document values simply
|
|
94
|
-
* need to be displayed (or when changes to documents don
|
|
115
|
+
* need to be displayed (or when changes to documents don't need to be reflected immediately);
|
|
95
116
|
* consider using `usePreview` or `useQuery` for these use cases instead. These hooks leverage the Sanity
|
|
96
117
|
* Live Content API to provide a more efficient way to read and subscribe to document state.
|
|
97
118
|
*/
|
|
@@ -101,7 +122,7 @@ export function useDocument(doc: DocumentHandle, path?: string): unknown {
|
|
|
101
122
|
|
|
102
123
|
const _useDocument = createStateSourceHook<[doc: DocumentHandle, path?: string], unknown>({
|
|
103
124
|
getState: getDocumentState,
|
|
104
|
-
shouldSuspend: (instance, doc) => getDocumentState(instance, doc
|
|
125
|
+
shouldSuspend: (instance, doc) => getDocumentState(instance, doc).getCurrent() === undefined,
|
|
105
126
|
suspender: resolveDocument,
|
|
106
|
-
|
|
127
|
+
getConfig: identity,
|
|
107
128
|
})
|
|
@@ -22,9 +22,8 @@ vi.mock('../context/useSanityInstance', () => ({
|
|
|
22
22
|
|
|
23
23
|
const instance = createSanityInstance({projectId: 'p', dataset: 'd'})
|
|
24
24
|
const docHandle: DocumentHandle = {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
resourceId: 'document:p.d:doc1',
|
|
25
|
+
documentId: 'doc1',
|
|
26
|
+
documentType: 'book',
|
|
28
27
|
}
|
|
29
28
|
|
|
30
29
|
describe('useDocumentEvent hook', () => {
|