@sanity/sdk-react 0.0.0-alpha.20 → 0.0.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/README.md +73 -34
  2. package/dist/_chunks-es/context.js +8 -0
  3. package/dist/_chunks-es/context.js.map +1 -0
  4. package/dist/_chunks-es/useLogOut.js +44 -0
  5. package/dist/_chunks-es/useLogOut.js.map +1 -0
  6. package/dist/components.d.ts +83 -0
  7. package/dist/components.js +132 -0
  8. package/dist/components.js.map +1 -0
  9. package/dist/context.d.ts +39 -0
  10. package/dist/context.js +5 -0
  11. package/dist/context.js.map +1 -0
  12. package/dist/hooks.d.ts +277 -0
  13. package/dist/hooks.js +153 -0
  14. package/dist/hooks.js.map +1 -0
  15. package/dist/index.d.ts +2 -4742
  16. package/dist/index.js +2 -1054
  17. package/dist/index.js.map +1 -1
  18. package/package.json +43 -22
  19. package/src/_exports/components.ts +2 -0
  20. package/src/_exports/context.ts +2 -0
  21. package/src/_exports/hooks.ts +21 -0
  22. package/src/_exports/index.ts +10 -66
  23. package/src/components/Login/LoginLinks.test.tsx +3 -2
  24. package/src/components/SanityApp.test.tsx +2 -104
  25. package/src/components/SanityApp.tsx +16 -80
  26. package/src/components/auth/AuthBoundary.test.tsx +10 -4
  27. package/src/components/auth/AuthBoundary.tsx +2 -18
  28. package/src/components/auth/Login.test.tsx +9 -2
  29. package/src/components/auth/Login.tsx +0 -1
  30. package/src/components/auth/LoginCallback.test.tsx +9 -2
  31. package/src/components/auth/LoginError.test.tsx +10 -2
  32. package/src/components/auth/LoginFooter.test.tsx +9 -2
  33. package/src/components/auth/LoginLayout.test.tsx +9 -2
  34. package/src/context/SanityProvider.test.tsx +1 -1
  35. package/src/context/SanityProvider.tsx +13 -21
  36. package/src/hooks/auth/useAuthState.tsx +5 -4
  37. package/src/hooks/auth/useAuthToken.tsx +1 -1
  38. package/src/hooks/auth/useCurrentUser.tsx +4 -27
  39. package/src/hooks/auth/useHandleCallback.tsx +0 -1
  40. package/src/hooks/auth/useLogOut.tsx +1 -1
  41. package/src/hooks/auth/useLoginUrls.tsx +0 -1
  42. package/src/hooks/client/useClient.test.tsx +130 -0
  43. package/src/hooks/client/useClient.ts +30 -8
  44. package/src/hooks/comlink/useFrameConnection.test.tsx +10 -55
  45. package/src/hooks/comlink/useFrameConnection.ts +47 -43
  46. package/src/hooks/comlink/useWindowConnection.test.ts +12 -53
  47. package/src/hooks/comlink/useWindowConnection.ts +33 -73
  48. package/src/hooks/context/useSanityInstance.test.tsx +1 -1
  49. package/src/hooks/context/useSanityInstance.ts +7 -23
  50. package/src/hooks/documentCollection/useDocuments.test.ts +130 -0
  51. package/src/hooks/documentCollection/useDocuments.ts +87 -0
  52. package/src/hooks/helpers/createCallbackHook.tsx +2 -3
  53. package/src/hooks/helpers/createStateSourceHook.test.tsx +0 -66
  54. package/src/hooks/helpers/createStateSourceHook.tsx +10 -29
  55. package/src/hooks/preview/usePreview.test.tsx +10 -19
  56. package/src/hooks/preview/usePreview.tsx +12 -66
  57. package/src/components/SDKProvider.test.tsx +0 -79
  58. package/src/components/SDKProvider.tsx +0 -42
  59. package/src/components/auth/authTestHelpers.tsx +0 -11
  60. package/src/components/utils.ts +0 -22
  61. package/src/context/SanityInstanceContext.ts +0 -4
  62. package/src/hooks/_synchronous-groq-js.mjs +0 -4
  63. package/src/hooks/auth/useDashboardOrganizationId.test.tsx +0 -42
  64. package/src/hooks/auth/useDashboardOrganizationId.tsx +0 -29
  65. package/src/hooks/comlink/useManageFavorite.test.ts +0 -106
  66. package/src/hooks/comlink/useManageFavorite.ts +0 -101
  67. package/src/hooks/comlink/useRecordDocumentHistoryEvent.test.ts +0 -77
  68. package/src/hooks/comlink/useRecordDocumentHistoryEvent.ts +0 -79
  69. package/src/hooks/dashboard/useNavigateToStudioDocument.ts +0 -97
  70. package/src/hooks/dashboard/useStudioWorkspacesByResourceId.test.tsx +0 -274
  71. package/src/hooks/dashboard/useStudioWorkspacesByResourceId.ts +0 -91
  72. package/src/hooks/datasets/useDatasets.ts +0 -37
  73. package/src/hooks/document/useApplyActions.test.ts +0 -25
  74. package/src/hooks/document/useApplyActions.ts +0 -74
  75. package/src/hooks/document/useDocument.test.ts +0 -81
  76. package/src/hooks/document/useDocument.ts +0 -107
  77. package/src/hooks/document/useDocumentEvent.test.ts +0 -63
  78. package/src/hooks/document/useDocumentEvent.ts +0 -54
  79. package/src/hooks/document/useDocumentSyncStatus.test.ts +0 -16
  80. package/src/hooks/document/useDocumentSyncStatus.ts +0 -30
  81. package/src/hooks/document/useEditDocument.test.ts +0 -179
  82. package/src/hooks/document/useEditDocument.ts +0 -195
  83. package/src/hooks/document/usePermissions.ts +0 -82
  84. package/src/hooks/infiniteList/useInfiniteList.test.tsx +0 -152
  85. package/src/hooks/infiniteList/useInfiniteList.ts +0 -174
  86. package/src/hooks/paginatedList/usePaginatedList.test.tsx +0 -259
  87. package/src/hooks/paginatedList/usePaginatedList.ts +0 -290
  88. package/src/hooks/projection/useProjection.test.tsx +0 -218
  89. package/src/hooks/projection/useProjection.ts +0 -147
  90. package/src/hooks/projects/useProject.ts +0 -45
  91. package/src/hooks/projects/useProjects.ts +0 -41
  92. package/src/hooks/query/useQuery.test.tsx +0 -188
  93. package/src/hooks/query/useQuery.ts +0 -103
  94. package/src/hooks/users/useUsers.test.ts +0 -163
  95. package/src/hooks/users/useUsers.ts +0 -107
  96. package/src/utils/getEnv.ts +0 -21
  97. package/src/version.ts +0 -8
@@ -1,97 +0,0 @@
1
- import {type Status} from '@sanity/comlink'
2
- import {type DocumentHandle} from '@sanity/sdk'
3
- import {useCallback, useState} from 'react'
4
-
5
- import {useWindowConnection} from '../comlink/useWindowConnection'
6
- import {useStudioWorkspacesByResourceId} from './useStudioWorkspacesByResourceId'
7
-
8
- interface NavigateToResourceMessage {
9
- type: 'core/v1/bridge/navigate-to-resource'
10
- data: {
11
- /**
12
- * Resource ID
13
- */
14
- resourceId: string
15
- /**
16
- * Resource type
17
- * @example 'application' | 'studio'
18
- */
19
- resourceType: string
20
- /**
21
- * Path within the resource to navigate to.
22
- */
23
- path?: string
24
- }
25
- }
26
-
27
- interface NavigateToStudioResult {
28
- navigateToStudioDocument: () => void
29
- isConnected: boolean
30
- }
31
-
32
- /**
33
- * @public
34
- * Hook that provides a function to navigate to a studio document.
35
- * @param documentHandle - The document handle containing document ID, type, and resource ID
36
- * @returns An object containing:
37
- * - navigateToStudioDocument - Function that when called will navigate to the studio document
38
- * - isConnected - Boolean indicating if connection to Core UI is established
39
- */
40
- export function useNavigateToStudioDocument(
41
- documentHandle: DocumentHandle,
42
- ): NavigateToStudioResult {
43
- const {workspacesByResourceId, isConnected: workspacesConnected} =
44
- useStudioWorkspacesByResourceId()
45
- const [status, setStatus] = useState<Status>('idle')
46
- const {sendMessage} = useWindowConnection<NavigateToResourceMessage, never>({
47
- name: 'core/nodes/sdk',
48
- connectTo: 'core/channels/sdk',
49
- onStatus: setStatus,
50
- })
51
-
52
- const navigateToStudioDocument = useCallback(() => {
53
- if (!workspacesConnected || status !== 'connected' || !documentHandle.resourceId) {
54
- return
55
- }
56
-
57
- // Extract projectId and dataset from the resourceId (current format: document:projectId.dataset:documentId)
58
- const [, projectAndDataset] = documentHandle.resourceId.split(':')
59
- const [projectId, dataset] = projectAndDataset.split('.')
60
- if (!projectId || !dataset) {
61
- return
62
- }
63
-
64
- // Find the workspace for this document
65
- const workspaces = workspacesByResourceId[`${projectId}:${dataset}`]
66
- if (!workspaces?.length) {
67
- // eslint-disable-next-line no-console
68
- console.warn('No workspace found for document', documentHandle.resourceId)
69
- return
70
- }
71
-
72
- if (workspaces.length > 1) {
73
- // eslint-disable-next-line no-console
74
- console.warn('Multiple workspaces found for document', documentHandle.resourceId)
75
- // eslint-disable-next-line no-console
76
- console.warn('Using the first one', workspaces[0])
77
- }
78
-
79
- const workspace = workspaces[0]
80
-
81
- const message: NavigateToResourceMessage = {
82
- type: 'core/v1/bridge/navigate-to-resource',
83
- data: {
84
- resourceId: workspace._ref,
85
- resourceType: 'studio',
86
- path: `/intent/edit/id=${documentHandle._id};type=${documentHandle._type}`,
87
- },
88
- }
89
-
90
- sendMessage(message.type, message.data)
91
- }, [documentHandle, workspacesConnected, status, sendMessage, workspacesByResourceId])
92
-
93
- return {
94
- navigateToStudioDocument,
95
- isConnected: workspacesConnected && status === 'connected',
96
- }
97
- }
@@ -1,274 +0,0 @@
1
- import {type Message, type Status} from '@sanity/comlink'
2
- import {renderHook, waitFor} from '@testing-library/react'
3
- import {describe, expect, it, vi} from 'vitest'
4
-
5
- import {useWindowConnection, type WindowConnection} from '../comlink/useWindowConnection'
6
- import {useStudioWorkspacesByResourceId} from './useStudioWorkspacesByResourceId'
7
-
8
- vi.mock('../comlink/useWindowConnection', () => ({
9
- useWindowConnection: vi.fn(),
10
- }))
11
-
12
- const mockWorkspaceData = {
13
- context: {
14
- availableResources: [
15
- {
16
- projectId: 'project1',
17
- workspaces: [
18
- {
19
- name: 'workspace1',
20
- title: 'Workspace 1',
21
- basePath: '/workspace1',
22
- dataset: 'dataset1',
23
- userApplicationId: 'user1',
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
- ],
37
- },
38
- {
39
- projectId: 'project2',
40
- workspaces: [
41
- {
42
- name: 'workspace3',
43
- title: 'Workspace 3',
44
- basePath: '/workspace3',
45
- dataset: 'dataset2',
46
- userApplicationId: 'user2',
47
- url: 'https://test.sanity.studio',
48
- _ref: 'user2-workspace3',
49
- },
50
- ],
51
- },
52
- {
53
- // Project without workspaces
54
- projectId: 'project3',
55
- workspaces: [],
56
- },
57
- ],
58
- },
59
- }
60
-
61
- describe('useStudioWorkspacesByResourceId', () => {
62
- it('should return empty workspaces and connected=false when not connected', async () => {
63
- // Create a mock that captures the onStatus callback
64
- let capturedOnStatus: ((status: Status) => void) | undefined
65
-
66
- vi.mocked(useWindowConnection).mockImplementation(({onStatus}) => {
67
- capturedOnStatus = onStatus
68
-
69
- return {
70
- fetch: undefined,
71
- sendMessage: vi.fn(),
72
- } as unknown as WindowConnection<Message>
73
- })
74
-
75
- const {result} = renderHook(() => useStudioWorkspacesByResourceId())
76
-
77
- // Call onStatus with 'idle' to simulate not connected
78
- if (capturedOnStatus) capturedOnStatus('idle')
79
-
80
- expect(result.current).toEqual({
81
- workspacesByResourceId: {},
82
- error: null,
83
- isConnected: false,
84
- })
85
- })
86
-
87
- it('should process workspaces into lookup by projectId:dataset', async () => {
88
- const mockFetch = vi.fn().mockResolvedValue(mockWorkspaceData)
89
- let capturedOnStatus: ((status: Status) => void) | undefined
90
-
91
- vi.mocked(useWindowConnection).mockImplementation(({onStatus}) => {
92
- capturedOnStatus = onStatus
93
-
94
- return {
95
- fetch: mockFetch,
96
- sendMessage: vi.fn(),
97
- } as unknown as WindowConnection<Message>
98
- })
99
-
100
- const {result} = renderHook(() => useStudioWorkspacesByResourceId())
101
-
102
- // Call onStatus with 'connected' to simulate connected state
103
- if (capturedOnStatus) capturedOnStatus('connected')
104
-
105
- await waitFor(() => {
106
- expect(result.current.workspacesByResourceId).toEqual({
107
- 'project1:dataset1': [
108
- {
109
- name: 'workspace1',
110
- title: 'Workspace 1',
111
- basePath: '/workspace1',
112
- dataset: 'dataset1',
113
- userApplicationId: 'user1',
114
- url: 'https://test.sanity.studio',
115
- _ref: 'user1-workspace1',
116
- },
117
- {
118
- name: 'workspace2',
119
- title: 'Workspace 2',
120
- basePath: '/workspace2',
121
- dataset: 'dataset1',
122
- userApplicationId: 'user1',
123
- url: 'https://test.sanity.studio',
124
- _ref: 'user1-workspace2',
125
- },
126
- ],
127
- 'project2:dataset2': [
128
- {
129
- name: 'workspace3',
130
- title: 'Workspace 3',
131
- basePath: '/workspace3',
132
- dataset: 'dataset2',
133
- userApplicationId: 'user2',
134
- url: 'https://test.sanity.studio',
135
- _ref: 'user2-workspace3',
136
- },
137
- ],
138
- })
139
- expect(result.current.error).toBeNull()
140
- expect(result.current.isConnected).toBe(true)
141
- })
142
-
143
- expect(mockFetch).toHaveBeenCalledWith('core/v1/bridge/context', undefined, expect.any(Object))
144
- })
145
-
146
- it('should handle fetch errors', async () => {
147
- const mockFetch = vi.fn().mockRejectedValue(new Error('Failed to fetch'))
148
- let capturedOnStatus: ((status: Status) => void) | undefined
149
-
150
- vi.mocked(useWindowConnection).mockImplementation(({onStatus}) => {
151
- capturedOnStatus = onStatus
152
-
153
- return {
154
- fetch: mockFetch,
155
- sendMessage: vi.fn(),
156
- } as unknown as WindowConnection<Message>
157
- })
158
-
159
- const {result} = renderHook(() => useStudioWorkspacesByResourceId())
160
-
161
- // Call onStatus with 'connected' to simulate connected state
162
- if (capturedOnStatus) capturedOnStatus('connected')
163
-
164
- await waitFor(() => {
165
- expect(result.current.workspacesByResourceId).toEqual({})
166
- expect(result.current.error).toBe('Failed to fetch workspaces')
167
- expect(result.current.isConnected).toBe(true)
168
- })
169
- })
170
-
171
- it('should handle AbortError silently', async () => {
172
- const abortError = new Error('Aborted')
173
- abortError.name = 'AbortError'
174
- const mockFetch = vi.fn().mockRejectedValue(abortError)
175
- let capturedOnStatus: ((status: Status) => void) | undefined
176
-
177
- vi.mocked(useWindowConnection).mockImplementation(({onStatus}) => {
178
- capturedOnStatus = onStatus
179
-
180
- return {
181
- fetch: mockFetch,
182
- sendMessage: vi.fn(),
183
- } as unknown as WindowConnection<Message>
184
- })
185
-
186
- const {result} = renderHook(() => useStudioWorkspacesByResourceId())
187
-
188
- // Call onStatus with 'connected' to simulate connected state
189
- if (capturedOnStatus) capturedOnStatus('connected')
190
-
191
- await waitFor(() => {
192
- expect(result.current.workspacesByResourceId).toEqual({})
193
- expect(result.current.error).toBeNull()
194
- expect(result.current.isConnected).toBe(true)
195
- })
196
- })
197
-
198
- it('should handle projects without workspaces', async () => {
199
- const mockFetch = vi.fn().mockResolvedValue({
200
- context: {
201
- availableResources: [
202
- {
203
- projectId: 'project1',
204
- workspaces: [],
205
- },
206
- ],
207
- },
208
- })
209
- let capturedOnStatus: ((status: Status) => void) | undefined
210
-
211
- vi.mocked(useWindowConnection).mockImplementation(({onStatus}) => {
212
- capturedOnStatus = onStatus
213
-
214
- return {
215
- fetch: mockFetch,
216
- sendMessage: vi.fn(),
217
- } as unknown as WindowConnection<Message>
218
- })
219
-
220
- const {result} = renderHook(() => useStudioWorkspacesByResourceId())
221
-
222
- // Call onStatus with 'connected' to simulate connected state
223
- if (capturedOnStatus) capturedOnStatus('connected')
224
-
225
- await waitFor(() => {
226
- expect(result.current.workspacesByResourceId).toEqual({})
227
- expect(result.current.error).toBeNull()
228
- expect(result.current.isConnected).toBe(true)
229
- })
230
- })
231
-
232
- it('should handle projects without projectId', async () => {
233
- const mockFetch = vi.fn().mockResolvedValue({
234
- context: {
235
- availableResources: [
236
- {
237
- workspaces: [
238
- {
239
- name: 'workspace1',
240
- title: 'Workspace 1',
241
- basePath: '/workspace1',
242
- dataset: 'dataset1',
243
- userApplicationId: 'user1',
244
- url: 'https://test.sanity.studio',
245
- _ref: 'user1-workspace1',
246
- },
247
- ],
248
- },
249
- ],
250
- },
251
- })
252
- let capturedOnStatus: ((status: Status) => void) | undefined
253
-
254
- vi.mocked(useWindowConnection).mockImplementation(({onStatus}) => {
255
- capturedOnStatus = onStatus
256
-
257
- return {
258
- fetch: mockFetch,
259
- sendMessage: vi.fn(),
260
- } as unknown as WindowConnection<Message>
261
- })
262
-
263
- const {result} = renderHook(() => useStudioWorkspacesByResourceId())
264
-
265
- // Call onStatus with 'connected' to simulate connected state
266
- if (capturedOnStatus) capturedOnStatus('connected')
267
-
268
- await waitFor(() => {
269
- expect(result.current.workspacesByResourceId).toEqual({})
270
- expect(result.current.error).toBeNull()
271
- expect(result.current.isConnected).toBe(true)
272
- })
273
- })
274
- })
@@ -1,91 +0,0 @@
1
- import {type Status} from '@sanity/comlink'
2
- import {useEffect, useState} from 'react'
3
-
4
- import {useWindowConnection} from '../comlink/useWindowConnection'
5
-
6
- interface Workspace {
7
- name: string
8
- title: string
9
- basePath: string
10
- dataset: string
11
- userApplicationId: string
12
- url: string
13
- _ref: string
14
- }
15
-
16
- interface WorkspacesByResourceId {
17
- [key: string]: Workspace[] // key format: `${projectId}:${dataset}`
18
- }
19
-
20
- interface StudioWorkspacesResult {
21
- workspacesByResourceId: WorkspacesByResourceId
22
- error: string | null
23
- isConnected: boolean
24
- }
25
-
26
- /**
27
- * Hook that fetches studio workspaces and organizes them by projectId:dataset
28
- * @internal
29
- */
30
- export function useStudioWorkspacesByResourceId(): StudioWorkspacesResult {
31
- const [workspacesByResourceId, setWorkspacesByResourceId] = useState<WorkspacesByResourceId>({})
32
- const [status, setStatus] = useState<Status>('idle')
33
- const [error, setError] = useState<string | null>(null)
34
-
35
- const {fetch} = useWindowConnection({
36
- name: 'core/nodes/sdk',
37
- connectTo: 'core/channels/sdk',
38
- onStatus: setStatus,
39
- })
40
-
41
- // Once computed, this should probably be in a store and poll for changes
42
- // However, our stores are currently being refactored
43
- useEffect(() => {
44
- if (!fetch || status !== 'connected') return
45
-
46
- async function fetchWorkspaces(signal: AbortSignal) {
47
- try {
48
- const data = await fetch<{
49
- context: {availableResources: Array<{projectId: string; workspaces: Workspace[]}>}
50
- }>('core/v1/bridge/context', undefined, {signal})
51
-
52
- const workspaceMap: WorkspacesByResourceId = {}
53
-
54
- data.context.availableResources.forEach((resource) => {
55
- if (!resource.projectId || !resource.workspaces?.length) return
56
-
57
- resource.workspaces.forEach((workspace) => {
58
- const key = `${resource.projectId}:${workspace.dataset}`
59
- if (!workspaceMap[key]) {
60
- workspaceMap[key] = []
61
- }
62
- workspaceMap[key].push(workspace)
63
- })
64
- })
65
-
66
- setWorkspacesByResourceId(workspaceMap)
67
- setError(null)
68
- } catch (err: unknown) {
69
- if (err instanceof Error) {
70
- if (err.name === 'AbortError') {
71
- return
72
- }
73
- setError('Failed to fetch workspaces')
74
- }
75
- }
76
- }
77
-
78
- const controller = new AbortController()
79
- fetchWorkspaces(controller.signal)
80
-
81
- return () => {
82
- controller.abort()
83
- }
84
- }, [fetch, status])
85
-
86
- return {
87
- workspacesByResourceId,
88
- error,
89
- isConnected: status === 'connected',
90
- }
91
- }
@@ -1,37 +0,0 @@
1
- import {type DatasetsResponse} from '@sanity/client'
2
- import {getDatasetsState, resolveDatasets, type SanityInstance, type StateSource} from '@sanity/sdk'
3
-
4
- import {createStateSourceHook} from '../helpers/createStateSourceHook'
5
-
6
- type UseDatasets = {
7
- /**
8
- *
9
- * Returns metadata for each dataset in your organization.
10
- *
11
- * @category Datasets
12
- * @returns The metadata for your organization's datasets
13
- *
14
- * @example
15
- * ```tsx
16
- * const datasets = useDatasets()
17
- *
18
- * return (
19
- * <select>
20
- * {datasets.map((dataset) => (
21
- * <option key={dataset.name}>{dataset.name}</option>
22
- * ))}
23
- * </select>
24
- * )
25
- * ```
26
- *
27
- */
28
- (): DatasetsResponse
29
- }
30
-
31
- /** @public */
32
- export const useDatasets: UseDatasets = createStateSourceHook({
33
- // remove `undefined` since we're suspending when that is the case
34
- getState: getDatasetsState as (instance: SanityInstance) => StateSource<DatasetsResponse>,
35
- shouldSuspend: (instance) => getDatasetsState(instance).getCurrent() === undefined,
36
- suspender: resolveDatasets,
37
- })
@@ -1,25 +0,0 @@
1
- import {applyActions, createDocument, type ResourceId} from '@sanity/sdk'
2
- import {describe, it} from 'vitest'
3
-
4
- import {createCallbackHook} from '../helpers/createCallbackHook'
5
-
6
- vi.mock('../helpers/createCallbackHook', () => ({
7
- createCallbackHook: vi.fn((cb) => () => cb),
8
- }))
9
- vi.mock('@sanity/sdk', async (importOriginal) => {
10
- const original = await importOriginal<typeof import('@sanity/sdk')>()
11
- return {...original, applyActions: vi.fn()}
12
- })
13
-
14
- describe('useApplyActions', () => {
15
- it('calls `createCallbackHook` with `applyActions`', async () => {
16
- const {useApplyActions} = await import('./useApplyActions')
17
- const resourceId: ResourceId = 'project1.dataset1'
18
- expect(createCallbackHook).not.toHaveBeenCalled()
19
-
20
- expect(applyActions).not.toHaveBeenCalled()
21
- const apply = useApplyActions(resourceId)
22
- apply(createDocument({_type: 'author'}))
23
- expect(applyActions).toHaveBeenCalledWith(createDocument({_type: 'author'}))
24
- })
25
- })
@@ -1,74 +0,0 @@
1
- import {
2
- type ActionsResult,
3
- applyActions,
4
- type ApplyActionsOptions,
5
- type DocumentAction,
6
- type ResourceId,
7
- } from '@sanity/sdk'
8
- import {type SanityDocument} from '@sanity/types'
9
-
10
- import {createCallbackHook} from '../helpers/createCallbackHook'
11
-
12
- /**
13
- *
14
- * @beta
15
- *
16
- * Provides a callback for applying one or more actions to a document.
17
- *
18
- * @category Documents
19
- * @param resourceId - The resource ID of the document to apply actions to. If not provided, the document will use the default resource.
20
- * @returns A function that takes one more more {@link DocumentAction}s and returns a promise that resolves to an {@link ActionsResult}.
21
- * @example Publish or unpublish a document
22
- * ```
23
- * import { publishDocument, unpublishDocument } from '@sanity/sdk'
24
- * import { useApplyActions } from '@sanity/sdk-react'
25
- *
26
- * const apply = useApplyActions()
27
- * const myDocument = { _id: 'my-document-id', _type: 'my-document-type' }
28
- *
29
- * return (
30
- * <button onClick={() => apply(publishDocument(myDocument))}>Publish</button>
31
- * <button onClick={() => apply(unpublishDocument(myDocument))}>Unpublish</button>
32
- * )
33
- * ```
34
- *
35
- * @example Create and publish a new document
36
- * ```
37
- * import { createDocument, publishDocument } from '@sanity/sdk'
38
- * import { useApplyActions } from '@sanity/sdk-react'
39
- *
40
- * const apply = useApplyActions()
41
- *
42
- * const handleCreateAndPublish = () => {
43
- * const handle = { _id: window.crypto.randomUUID(), _type: 'my-document-type' }
44
- * apply([
45
- * createDocument(handle),
46
- * publishDocument(handle),
47
- * ])
48
- * }
49
- *
50
- * return (
51
- * <button onClick={handleCreateAndPublish}>
52
- * I’m feeling lucky
53
- * </button>
54
- * )
55
- * ```
56
- */
57
- export function useApplyActions(
58
- resourceId?: ResourceId,
59
- ): <TDocument extends SanityDocument>(
60
- action: DocumentAction<TDocument> | DocumentAction<TDocument>[],
61
- options?: ApplyActionsOptions,
62
- ) => Promise<ActionsResult<TDocument>>
63
-
64
- /** @beta */
65
- export function useApplyActions(
66
- resourceId?: ResourceId,
67
- ): (
68
- action: DocumentAction | DocumentAction[],
69
- options?: ApplyActionsOptions,
70
- ) => Promise<ActionsResult> {
71
- return _useApplyActions(resourceId)()
72
- }
73
-
74
- const _useApplyActions = (resourceId?: ResourceId) => createCallbackHook(applyActions, resourceId)
@@ -1,81 +0,0 @@
1
- // tests/useDocument.test.ts
2
- import {
3
- createSanityInstance,
4
- getDocumentState,
5
- resolveDocument,
6
- type StateSource,
7
- } from '@sanity/sdk'
8
- import {type SanityDocument} from '@sanity/types'
9
- import {renderHook} from '@testing-library/react'
10
- import {beforeEach, describe, expect, it, vi} from 'vitest'
11
-
12
- import {useSanityInstance} from '../context/useSanityInstance'
13
- import {useDocument} from './useDocument'
14
-
15
- vi.mock('@sanity/sdk', async (importOriginal) => {
16
- const original = await importOriginal<typeof import('@sanity/sdk')>()
17
- return {...original, getDocumentState: vi.fn(), resolveDocument: vi.fn()}
18
- })
19
-
20
- vi.mock('../context/useSanityInstance', () => ({
21
- useSanityInstance: vi.fn(),
22
- }))
23
-
24
- // Create a fake instance to be returned by useSanityInstance.
25
- const instance = createSanityInstance({projectId: 'p', dataset: 'd'})
26
- const doc: SanityDocument = {
27
- _id: 'doc1',
28
- foo: 'bar',
29
- _type: 'book',
30
- _rev: 'tx0',
31
- _createdAt: '2025-02-06T00:11:00.000Z',
32
- _updatedAt: '2025-02-06T00:11:00.000Z',
33
- }
34
-
35
- describe('useDocument hook', () => {
36
- beforeEach(() => {
37
- vi.resetAllMocks()
38
- vi.mocked(useSanityInstance).mockReturnValue(instance)
39
- })
40
-
41
- it('returns the current document when ready (without a path)', () => {
42
- const getCurrent = vi.fn().mockReturnValue(doc)
43
- const subscribe = vi.fn().mockReturnValue(vi.fn())
44
- vi.mocked(getDocumentState).mockReturnValue({
45
- getCurrent,
46
- subscribe,
47
- } as unknown as StateSource<unknown>)
48
-
49
- const {result} = renderHook(() => useDocument({_id: 'doc1', _type: 'book'}))
50
-
51
- expect(result.current).toEqual(doc)
52
- expect(getCurrent).toHaveBeenCalled()
53
- expect(subscribe).toHaveBeenCalled()
54
- })
55
-
56
- it('throws a promise (suspends) when the document is not ready', () => {
57
- const getCurrent = vi.fn().mockReturnValue(undefined)
58
- const subscribe = vi.fn().mockReturnValue(vi.fn())
59
- vi.mocked(getDocumentState).mockReturnValue({
60
- getCurrent,
61
- subscribe,
62
- } as unknown as StateSource<unknown>)
63
-
64
- const resolveDocPromise = Promise.resolve(doc)
65
-
66
- // Also, simulate resolveDocument to return a known promise.
67
- vi.mocked(resolveDocument).mockReturnValue(resolveDocPromise)
68
-
69
- // Render the hook and capture the thrown promise.
70
- const {result} = renderHook(() => {
71
- try {
72
- return useDocument({_id: 'doc1', _type: 'book'})
73
- } catch (e) {
74
- return e
75
- }
76
- })
77
-
78
- // When the document is not ready, the hook throws the promise from resolveDocument.
79
- expect(result.current).toBe(resolveDocPromise)
80
- })
81
- })