@sanity/sdk-react 2.7.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 +450 -366
- package/dist/index.js.map +1 -1
- package/package.json +6 -8
- 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 +11 -26
- package/src/components/auth/LoginError.test.tsx +5 -0
- package/src/components/auth/LoginError.tsx +23 -2
- 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,4 +1,4 @@
|
|
|
1
|
-
import {type
|
|
1
|
+
import {type DocumentResource} from '@sanity/sdk'
|
|
2
2
|
import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest'
|
|
3
3
|
|
|
4
4
|
import {SanityApp} from '../components/SanityApp'
|
|
@@ -26,6 +26,7 @@ vi.mock('react-dom/client', () => ({
|
|
|
26
26
|
|
|
27
27
|
describe('renderSanityApp', () => {
|
|
28
28
|
let rootElement: HTMLElement | null
|
|
29
|
+
let namedResources: Record<string, DocumentResource>
|
|
29
30
|
|
|
30
31
|
beforeEach(() => {
|
|
31
32
|
vi.clearAllMocks()
|
|
@@ -34,6 +35,9 @@ describe('renderSanityApp', () => {
|
|
|
34
35
|
mockCreateRoot.mockClear()
|
|
35
36
|
rootElement = document.createElement('div')
|
|
36
37
|
document.body.appendChild(rootElement)
|
|
38
|
+
namedResources = {
|
|
39
|
+
default: {projectId: 'test-project', dataset: 'production'},
|
|
40
|
+
}
|
|
37
41
|
})
|
|
38
42
|
|
|
39
43
|
afterEach(() => {
|
|
@@ -44,53 +48,44 @@ describe('renderSanityApp', () => {
|
|
|
44
48
|
})
|
|
45
49
|
|
|
46
50
|
it('throws error when rootElement is null', () => {
|
|
47
|
-
|
|
48
|
-
main: {projectId: 'test-project', dataset: 'production'},
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
expect(() => renderSanityApp(null, namedSources, {}, <div>Test</div>)).toThrowError(
|
|
51
|
+
expect(() => renderSanityApp(null, namedResources, {}, <div>Test</div>)).toThrowError(
|
|
52
52
|
'Missing root element to mount application into',
|
|
53
53
|
)
|
|
54
54
|
})
|
|
55
55
|
|
|
56
56
|
it('creates root with the provided element', () => {
|
|
57
|
-
|
|
58
|
-
main: {projectId: 'test-project', dataset: 'production'},
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
renderSanityApp(rootElement, namedSources, {}, <div>Test</div>)
|
|
57
|
+
renderSanityApp(rootElement, namedResources, {}, <div>Test</div>)
|
|
62
58
|
|
|
63
59
|
expect(mockCreateRoot).toHaveBeenCalledWith(rootElement)
|
|
64
60
|
expect(mockCreateRoot).toHaveBeenCalledTimes(1)
|
|
65
61
|
})
|
|
66
62
|
|
|
67
|
-
it('
|
|
68
|
-
|
|
69
|
-
|
|
63
|
+
it('merges namedResources into a single config and resources map', () => {
|
|
64
|
+
namedResources = {
|
|
65
|
+
default: {projectId: 'project-1', dataset: 'production'},
|
|
70
66
|
secondary: {projectId: 'project-2', dataset: 'staging'},
|
|
71
67
|
}
|
|
72
68
|
|
|
73
|
-
renderSanityApp(rootElement,
|
|
69
|
+
renderSanityApp(rootElement, namedResources, {}, <div>Test</div>)
|
|
74
70
|
|
|
75
71
|
expect(mockRender).toHaveBeenCalledTimes(1)
|
|
76
72
|
const renderCall = mockRender.mock.calls[0][0]
|
|
77
73
|
expect(renderCall).toBeDefined()
|
|
78
74
|
|
|
79
|
-
// The renderCall is the SanityApp component directly when not using StrictMode
|
|
80
75
|
expect(renderCall.type).toBe(SanityApp)
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
76
|
+
// config has no resource fields
|
|
77
|
+
expect(renderCall.props.config).toEqual(undefined)
|
|
78
|
+
// resources map is populated from named entries
|
|
79
|
+
expect(renderCall.props.resources).toEqual({
|
|
80
|
+
default: {projectId: 'project-1', dataset: 'production'},
|
|
81
|
+
secondary: {projectId: 'project-2', dataset: 'staging'},
|
|
82
|
+
})
|
|
85
83
|
})
|
|
86
84
|
|
|
87
85
|
it('renders without StrictMode when reactStrictMode is false', () => {
|
|
88
|
-
const namedSources = {
|
|
89
|
-
main: {projectId: 'test-project', dataset: 'production'},
|
|
90
|
-
}
|
|
91
86
|
const children = <div>Test Children</div>
|
|
92
87
|
|
|
93
|
-
renderSanityApp(rootElement,
|
|
88
|
+
renderSanityApp(rootElement, namedResources, {reactStrictMode: false}, children)
|
|
94
89
|
|
|
95
90
|
expect(mockRender).toHaveBeenCalledTimes(1)
|
|
96
91
|
const renderCall = mockRender.mock.calls[0][0]
|
|
@@ -101,12 +96,9 @@ describe('renderSanityApp', () => {
|
|
|
101
96
|
})
|
|
102
97
|
|
|
103
98
|
it('renders without StrictMode by default', () => {
|
|
104
|
-
const namedSources = {
|
|
105
|
-
main: {projectId: 'test-project', dataset: 'production'},
|
|
106
|
-
}
|
|
107
99
|
const children = <div>Test Children</div>
|
|
108
100
|
|
|
109
|
-
renderSanityApp(rootElement,
|
|
101
|
+
renderSanityApp(rootElement, namedResources, {}, children)
|
|
110
102
|
|
|
111
103
|
expect(mockRender).toHaveBeenCalledTimes(1)
|
|
112
104
|
const renderCall = mockRender.mock.calls[0][0]
|
|
@@ -117,17 +109,14 @@ describe('renderSanityApp', () => {
|
|
|
117
109
|
})
|
|
118
110
|
|
|
119
111
|
it('renders with StrictMode when reactStrictMode is true', () => {
|
|
120
|
-
const namedSources = {
|
|
121
|
-
main: {projectId: 'test-project', dataset: 'production'},
|
|
122
|
-
}
|
|
123
112
|
const children = <div>Test Children</div>
|
|
124
113
|
|
|
125
|
-
renderSanityApp(rootElement,
|
|
114
|
+
renderSanityApp(rootElement, namedResources, {reactStrictMode: true}, children)
|
|
126
115
|
|
|
127
116
|
expect(mockRender).toHaveBeenCalledTimes(1)
|
|
128
117
|
const renderCall = mockRender.mock.calls[0][0]
|
|
129
118
|
|
|
130
|
-
// Should have StrictMode wrapper (StrictMode is a Symbol
|
|
119
|
+
// Should have StrictMode wrapper (StrictMode is a Symbol)
|
|
131
120
|
expect(renderCall.type).toBeDefined()
|
|
132
121
|
expect(renderCall.type.toString()).toContain('Symbol')
|
|
133
122
|
const strictModeChild = renderCall.props.children
|
|
@@ -136,11 +125,7 @@ describe('renderSanityApp', () => {
|
|
|
136
125
|
})
|
|
137
126
|
|
|
138
127
|
it('passes loading fallback to SanityApp', () => {
|
|
139
|
-
|
|
140
|
-
main: {projectId: 'test-project', dataset: 'production'},
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
renderSanityApp(rootElement, namedSources, {}, <div>Test</div>)
|
|
128
|
+
renderSanityApp(rootElement, namedResources, {}, <div>Test</div>)
|
|
144
129
|
|
|
145
130
|
expect(mockRender).toHaveBeenCalledTimes(1)
|
|
146
131
|
const renderCall = mockRender.mock.calls[0][0]
|
|
@@ -151,103 +136,51 @@ describe('renderSanityApp', () => {
|
|
|
151
136
|
})
|
|
152
137
|
|
|
153
138
|
it('returns an unmount function', () => {
|
|
154
|
-
const
|
|
155
|
-
main: {projectId: 'test-project', dataset: 'production'},
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const unmount = renderSanityApp(rootElement, namedSources, {}, <div>Test</div>)
|
|
139
|
+
const unmount = renderSanityApp(rootElement, namedResources, {}, <div>Test</div>)
|
|
159
140
|
|
|
160
141
|
expect(typeof unmount).toBe('function')
|
|
161
142
|
})
|
|
162
143
|
|
|
163
144
|
it('calls root.unmount when unmount function is invoked', () => {
|
|
164
|
-
const
|
|
165
|
-
main: {projectId: 'test-project', dataset: 'production'},
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const unmount = renderSanityApp(rootElement, namedSources, {}, <div>Test</div>)
|
|
145
|
+
const unmount = renderSanityApp(rootElement, namedResources, {}, <div>Test</div>)
|
|
169
146
|
|
|
170
147
|
expect(mockUnmount).not.toHaveBeenCalled()
|
|
171
148
|
unmount()
|
|
172
149
|
expect(mockUnmount).toHaveBeenCalledTimes(1)
|
|
173
150
|
})
|
|
174
151
|
|
|
175
|
-
it('handles empty
|
|
176
|
-
|
|
152
|
+
it('handles empty namedResources object', () => {
|
|
153
|
+
namedResources = {}
|
|
177
154
|
|
|
178
|
-
renderSanityApp(rootElement,
|
|
155
|
+
renderSanityApp(rootElement, namedResources, {}, <div>Test</div>)
|
|
179
156
|
|
|
180
157
|
expect(mockRender).toHaveBeenCalledTimes(1)
|
|
181
158
|
const renderCall = mockRender.mock.calls[0][0]
|
|
182
159
|
const sanityAppElement = renderCall
|
|
183
160
|
|
|
184
161
|
expect(sanityAppElement.type).toBe(SanityApp)
|
|
185
|
-
expect(sanityAppElement.props.config).toEqual(
|
|
162
|
+
expect(sanityAppElement.props.config).toEqual(undefined)
|
|
163
|
+
expect(sanityAppElement.props.resources).toEqual({})
|
|
186
164
|
})
|
|
187
165
|
|
|
188
166
|
it('handles single namedSource', () => {
|
|
189
|
-
const
|
|
190
|
-
|
|
167
|
+
const namedResource = {
|
|
168
|
+
default: {projectId: 'test-project', dataset: 'production'},
|
|
191
169
|
}
|
|
192
|
-
|
|
193
|
-
renderSanityApp(rootElement, namedSources, {}, <div>Test</div>)
|
|
170
|
+
renderSanityApp(rootElement, namedResource, {}, <div>Test</div>)
|
|
194
171
|
|
|
195
172
|
expect(mockRender).toHaveBeenCalledTimes(1)
|
|
196
173
|
const renderCall = mockRender.mock.calls[0][0]
|
|
197
174
|
const sanityAppElement = renderCall
|
|
198
175
|
|
|
199
176
|
expect(sanityAppElement.type).toBe(SanityApp)
|
|
200
|
-
expect(sanityAppElement.props.config).toEqual(
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
it('handles multiple namedSources', () => {
|
|
206
|
-
const namedSources = {
|
|
207
|
-
main: {projectId: 'project-1', dataset: 'production'},
|
|
208
|
-
blog: {projectId: 'project-2', dataset: 'staging'},
|
|
209
|
-
ecommerce: {projectId: 'project-3', dataset: 'development'},
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
renderSanityApp(rootElement, namedSources, {}, <div>Test</div>)
|
|
213
|
-
|
|
214
|
-
expect(mockRender).toHaveBeenCalledTimes(1)
|
|
215
|
-
const renderCall = mockRender.mock.calls[0][0]
|
|
216
|
-
const sanityAppElement = renderCall
|
|
217
|
-
|
|
218
|
-
expect(sanityAppElement.type).toBe(SanityApp)
|
|
219
|
-
expect(sanityAppElement.props.config).toHaveLength(3)
|
|
220
|
-
expect(sanityAppElement.props.config).toEqual([
|
|
221
|
-
{projectId: 'project-1', dataset: 'production'},
|
|
222
|
-
{projectId: 'project-2', dataset: 'staging'},
|
|
223
|
-
{projectId: 'project-3', dataset: 'development'},
|
|
224
|
-
])
|
|
225
|
-
})
|
|
226
|
-
|
|
227
|
-
it('preserves order of namedSources in config array', () => {
|
|
228
|
-
const namedSources = {
|
|
229
|
-
z: {projectId: 'project-z', dataset: 'z-dataset'},
|
|
230
|
-
a: {projectId: 'project-a', dataset: 'a-dataset'},
|
|
231
|
-
m: {projectId: 'project-m', dataset: 'm-dataset'},
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
renderSanityApp(rootElement, namedSources, {}, <div>Test</div>)
|
|
235
|
-
|
|
236
|
-
const renderCall = mockRender.mock.calls[0][0]
|
|
237
|
-
const sanityAppElement = renderCall
|
|
238
|
-
|
|
239
|
-
// Object.values preserves insertion order in modern JS
|
|
240
|
-
expect(sanityAppElement.props.config).toEqual([
|
|
241
|
-
{projectId: 'project-z', dataset: 'z-dataset'},
|
|
242
|
-
{projectId: 'project-a', dataset: 'a-dataset'},
|
|
243
|
-
{projectId: 'project-m', dataset: 'm-dataset'},
|
|
244
|
-
])
|
|
177
|
+
expect(sanityAppElement.props.config).toEqual(undefined)
|
|
178
|
+
expect(sanityAppElement.props.resources).toEqual({
|
|
179
|
+
default: {projectId: 'test-project', dataset: 'production'},
|
|
180
|
+
})
|
|
245
181
|
})
|
|
246
182
|
|
|
247
183
|
it('passes children to SanityApp', () => {
|
|
248
|
-
const namedSources = {
|
|
249
|
-
main: {projectId: 'test-project', dataset: 'production'},
|
|
250
|
-
}
|
|
251
184
|
const children = (
|
|
252
185
|
<div>
|
|
253
186
|
<h1>Test App</h1>
|
|
@@ -255,7 +188,7 @@ describe('renderSanityApp', () => {
|
|
|
255
188
|
</div>
|
|
256
189
|
)
|
|
257
190
|
|
|
258
|
-
renderSanityApp(rootElement,
|
|
191
|
+
renderSanityApp(rootElement, namedResources, {}, children)
|
|
259
192
|
|
|
260
193
|
const renderCall = mockRender.mock.calls[0][0]
|
|
261
194
|
const sanityAppElement = renderCall
|
|
@@ -264,12 +197,8 @@ describe('renderSanityApp', () => {
|
|
|
264
197
|
})
|
|
265
198
|
|
|
266
199
|
it('works with different types of children', () => {
|
|
267
|
-
const namedSources = {
|
|
268
|
-
main: {projectId: 'test-project', dataset: 'production'},
|
|
269
|
-
}
|
|
270
|
-
|
|
271
200
|
// Test with string children
|
|
272
|
-
renderSanityApp(rootElement,
|
|
201
|
+
renderSanityApp(rootElement, namedResources, {}, 'String child')
|
|
273
202
|
|
|
274
203
|
let renderCall = mockRender.mock.calls[0][0]
|
|
275
204
|
let sanityAppElement = renderCall
|
|
@@ -278,7 +207,7 @@ describe('renderSanityApp', () => {
|
|
|
278
207
|
|
|
279
208
|
// Test with null children
|
|
280
209
|
mockRender.mockClear()
|
|
281
|
-
renderSanityApp(rootElement,
|
|
210
|
+
renderSanityApp(rootElement, namedResources, {}, null)
|
|
282
211
|
|
|
283
212
|
renderCall = mockRender.mock.calls[0][0]
|
|
284
213
|
sanityAppElement = renderCall
|
|
@@ -288,7 +217,7 @@ describe('renderSanityApp', () => {
|
|
|
288
217
|
// Test with array of children
|
|
289
218
|
mockRender.mockClear()
|
|
290
219
|
const arrayChildren = [<div key="1">Child 1</div>, <div key="2">Child 2</div>]
|
|
291
|
-
renderSanityApp(rootElement,
|
|
220
|
+
renderSanityApp(rootElement, namedResources, {}, arrayChildren)
|
|
292
221
|
|
|
293
222
|
renderCall = mockRender.mock.calls[0][0]
|
|
294
223
|
sanityAppElement = renderCall
|
|
@@ -296,50 +225,19 @@ describe('renderSanityApp', () => {
|
|
|
296
225
|
expect(sanityAppElement.props.children).toEqual(arrayChildren)
|
|
297
226
|
})
|
|
298
227
|
|
|
299
|
-
it('integrates with StrictMode and passes all props correctly', () => {
|
|
300
|
-
const namedSources = {
|
|
301
|
-
main: {
|
|
302
|
-
projectId: 'test-project',
|
|
303
|
-
dataset: 'production',
|
|
304
|
-
apiVersion: '2023-01-01',
|
|
305
|
-
} as SanityConfig,
|
|
306
|
-
secondary: {
|
|
307
|
-
projectId: 'test-project-2',
|
|
308
|
-
dataset: 'staging',
|
|
309
|
-
} as SanityConfig,
|
|
310
|
-
}
|
|
311
|
-
const children = <div>App Content</div>
|
|
312
|
-
|
|
313
|
-
renderSanityApp(rootElement, namedSources, {reactStrictMode: true}, children)
|
|
314
|
-
|
|
315
|
-
const renderCall = mockRender.mock.calls[0][0]
|
|
316
|
-
|
|
317
|
-
// Verify StrictMode wrapper (StrictMode is a Symbol in React 18)
|
|
318
|
-
expect(renderCall.type).toBeDefined()
|
|
319
|
-
expect(renderCall.type.toString()).toContain('Symbol')
|
|
320
|
-
|
|
321
|
-
// Verify SanityApp is inside StrictMode
|
|
322
|
-
const strictModeChild = renderCall.props.children
|
|
323
|
-
expect(strictModeChild.type).toBe(SanityApp)
|
|
324
|
-
|
|
325
|
-
// Verify all props are passed correctly
|
|
326
|
-
expect(strictModeChild.props.config).toEqual([
|
|
327
|
-
{projectId: 'test-project', dataset: 'production', apiVersion: '2023-01-01'},
|
|
328
|
-
{projectId: 'test-project-2', dataset: 'staging'},
|
|
329
|
-
])
|
|
330
|
-
expect(strictModeChild.props.fallback).toEqual(<div>Loading...</div>)
|
|
331
|
-
expect(strictModeChild.props.children).toEqual(children)
|
|
332
|
-
})
|
|
333
|
-
|
|
334
228
|
it('can be called multiple times with different roots', () => {
|
|
335
229
|
const rootElement2 = document.createElement('div')
|
|
336
230
|
document.body.appendChild(rootElement2)
|
|
337
231
|
|
|
338
|
-
const
|
|
339
|
-
|
|
232
|
+
const namedResources1 = {
|
|
233
|
+
default: {projectId: 'project-1', dataset: 'production'},
|
|
234
|
+
}
|
|
235
|
+
const namedResources2 = {
|
|
236
|
+
default: {projectId: 'project-2', dataset: 'staging'},
|
|
237
|
+
}
|
|
340
238
|
|
|
341
|
-
const unmount1 = renderSanityApp(rootElement,
|
|
342
|
-
const unmount2 = renderSanityApp(rootElement2,
|
|
239
|
+
const unmount1 = renderSanityApp(rootElement, namedResources1, {}, <div>App 1</div>)
|
|
240
|
+
const unmount2 = renderSanityApp(rootElement2, namedResources2, {}, <div>App 2</div>)
|
|
343
241
|
|
|
344
242
|
expect(mockCreateRoot).toHaveBeenCalledTimes(2)
|
|
345
243
|
expect(mockCreateRoot).toHaveBeenNthCalledWith(1, rootElement)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {type
|
|
1
|
+
import {type DocumentResource} from '@sanity/sdk'
|
|
2
2
|
import {StrictMode} from 'react'
|
|
3
3
|
import {createRoot} from 'react-dom/client'
|
|
4
4
|
|
|
@@ -8,16 +8,14 @@ interface RenderSanitySDKAppOptions {
|
|
|
8
8
|
reactStrictMode?: boolean
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
*/
|
|
14
|
-
interface NamedSources {
|
|
15
|
-
[key: string]: SanityConfig
|
|
11
|
+
interface NamedResources {
|
|
12
|
+
[key: string]: DocumentResource
|
|
16
13
|
}
|
|
14
|
+
|
|
17
15
|
/** @internal */
|
|
18
16
|
export function renderSanityApp(
|
|
19
17
|
rootElement: HTMLElement | null,
|
|
20
|
-
|
|
18
|
+
namedResources: NamedResources,
|
|
21
19
|
options: RenderSanitySDKAppOptions,
|
|
22
20
|
children: React.ReactNode,
|
|
23
21
|
): () => void {
|
|
@@ -27,18 +25,16 @@ export function renderSanityApp(
|
|
|
27
25
|
const {reactStrictMode = false} = options
|
|
28
26
|
|
|
29
27
|
const root = createRoot(rootElement)
|
|
30
|
-
const config = Object.values(namedSources)
|
|
31
|
-
|
|
32
28
|
root.render(
|
|
33
29
|
reactStrictMode ? (
|
|
34
30
|
<StrictMode>
|
|
35
|
-
{/* TODO:
|
|
36
|
-
<SanityApp
|
|
31
|
+
{/* TODO: we should find some way to pass top-level config, like auth, from a flatfile */}
|
|
32
|
+
<SanityApp resources={namedResources} fallback={<div>Loading...</div>}>
|
|
37
33
|
{children}
|
|
38
34
|
</SanityApp>
|
|
39
35
|
</StrictMode>
|
|
40
36
|
) : (
|
|
41
|
-
<SanityApp
|
|
37
|
+
<SanityApp resources={namedResources} fallback={<div>Loading...</div>}>
|
|
42
38
|
{children}
|
|
43
39
|
</SanityApp>
|
|
44
40
|
),
|
|
@@ -26,7 +26,7 @@ vi.mock('@sanity/sdk', async (orig) => {
|
|
|
26
26
|
|
|
27
27
|
describe('agent action hooks', () => {
|
|
28
28
|
const wrapper = ({children}: {children: React.ReactNode}) => (
|
|
29
|
-
<ResourceProvider projectId
|
|
29
|
+
<ResourceProvider resource={{projectId: 'p', dataset: 'd'}} fallback={null}>
|
|
30
30
|
{children}
|
|
31
31
|
</ResourceProvider>
|
|
32
32
|
)
|
|
@@ -7,15 +7,23 @@ import {
|
|
|
7
7
|
agentPrompt,
|
|
8
8
|
type AgentPromptOptions,
|
|
9
9
|
type AgentPromptResult,
|
|
10
|
+
type AgentResourceOptions,
|
|
10
11
|
agentTransform,
|
|
11
12
|
type AgentTransformOptions,
|
|
12
13
|
agentTranslate,
|
|
13
14
|
type AgentTranslateOptions,
|
|
14
15
|
type SanityInstance,
|
|
15
16
|
} from '@sanity/sdk'
|
|
17
|
+
import {useCallback, useContext} from 'react'
|
|
16
18
|
import {firstValueFrom} from 'rxjs'
|
|
17
19
|
|
|
18
|
-
import {
|
|
20
|
+
import {ResourceContext} from '../../context/DefaultResourceContext'
|
|
21
|
+
import {ResourcesContext} from '../../context/ResourcesContext'
|
|
22
|
+
import {useSanityInstance} from '../context/useSanityInstance'
|
|
23
|
+
import {
|
|
24
|
+
normalizeResourceOptions,
|
|
25
|
+
type WithResourceNameSupport,
|
|
26
|
+
} from '../helpers/useNormalizedResourceOptions'
|
|
19
27
|
|
|
20
28
|
interface Subscription {
|
|
21
29
|
unsubscribe(): void
|
|
@@ -36,6 +44,26 @@ interface Subscribable<T> {
|
|
|
36
44
|
): Subscription
|
|
37
45
|
}
|
|
38
46
|
|
|
47
|
+
function useAgentCallback<TOptions extends object, TReturn>(
|
|
48
|
+
action: (instance: SanityInstance, options: TOptions & AgentResourceOptions) => TReturn,
|
|
49
|
+
): (options: TOptions & WithResourceNameSupport<AgentResourceOptions>) => TReturn {
|
|
50
|
+
const instance = useSanityInstance()
|
|
51
|
+
const resources = useContext(ResourcesContext)
|
|
52
|
+
const contextResource = useContext(ResourceContext)
|
|
53
|
+
return useCallback(
|
|
54
|
+
(options: TOptions & WithResourceNameSupport<AgentResourceOptions>) => {
|
|
55
|
+
const {resource} = normalizeResourceOptions(
|
|
56
|
+
{resource: options.resource, resourceName: options.resourceName},
|
|
57
|
+
resources,
|
|
58
|
+
contextResource,
|
|
59
|
+
)
|
|
60
|
+
const {resourceName: _, ...rest} = options
|
|
61
|
+
return action(instance, {...rest, resource} as TOptions & AgentResourceOptions)
|
|
62
|
+
},
|
|
63
|
+
[instance, resources, contextResource, action],
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
39
67
|
/**
|
|
40
68
|
* @alpha
|
|
41
69
|
* Generates content for a document (or specific fields) via Sanity Agent Actions.
|
|
@@ -103,10 +131,11 @@ interface Subscribable<T> {
|
|
|
103
131
|
*
|
|
104
132
|
* @category Agent Actions
|
|
105
133
|
*/
|
|
106
|
-
export
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
134
|
+
export function useAgentGenerate(): (
|
|
135
|
+
options: AgentGenerateOptions & WithResourceNameSupport<AgentResourceOptions>,
|
|
136
|
+
) => Subscribable<unknown> {
|
|
137
|
+
return useAgentCallback<AgentGenerateOptions, Subscribable<unknown>>(agentGenerate)
|
|
138
|
+
}
|
|
110
139
|
|
|
111
140
|
/**
|
|
112
141
|
* @alpha
|
|
@@ -179,10 +208,11 @@ export const useAgentGenerate: () => (options: AgentGenerateOptions) => Subscrib
|
|
|
179
208
|
*
|
|
180
209
|
* @category Agent Actions
|
|
181
210
|
*/
|
|
182
|
-
export
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
211
|
+
export function useAgentTransform(): (
|
|
212
|
+
options: AgentTransformOptions & WithResourceNameSupport<AgentResourceOptions>,
|
|
213
|
+
) => Subscribable<unknown> {
|
|
214
|
+
return useAgentCallback<AgentTransformOptions, Subscribable<unknown>>(agentTransform)
|
|
215
|
+
}
|
|
186
216
|
|
|
187
217
|
/**
|
|
188
218
|
* @alpha
|
|
@@ -274,10 +304,11 @@ export const useAgentTransform: () => (options: AgentTransformOptions) => Subscr
|
|
|
274
304
|
*
|
|
275
305
|
* @category Agent Actions
|
|
276
306
|
*/
|
|
277
|
-
export
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
307
|
+
export function useAgentTranslate(): (
|
|
308
|
+
options: AgentTranslateOptions & WithResourceNameSupport<AgentResourceOptions>,
|
|
309
|
+
) => Subscribable<unknown> {
|
|
310
|
+
return useAgentCallback<AgentTranslateOptions, Subscribable<unknown>>(agentTranslate)
|
|
311
|
+
}
|
|
281
312
|
|
|
282
313
|
/**
|
|
283
314
|
* @internal
|
|
@@ -285,7 +316,7 @@ export const useAgentTranslate: () => (options: AgentTranslateOptions) => Subscr
|
|
|
285
316
|
*/
|
|
286
317
|
function promptAdapter(
|
|
287
318
|
instance: SanityInstance,
|
|
288
|
-
options: AgentPromptOptions,
|
|
319
|
+
options: AgentPromptOptions & AgentResourceOptions,
|
|
289
320
|
): Promise<AgentPromptResult> {
|
|
290
321
|
return firstValueFrom(agentPrompt(instance, options))
|
|
291
322
|
}
|
|
@@ -384,8 +415,11 @@ function promptAdapter(
|
|
|
384
415
|
*
|
|
385
416
|
* @category Agent Actions
|
|
386
417
|
*/
|
|
387
|
-
export
|
|
388
|
-
|
|
418
|
+
export function useAgentPrompt(): (
|
|
419
|
+
options: AgentPromptOptions & WithResourceNameSupport<AgentResourceOptions>,
|
|
420
|
+
) => Promise<AgentPromptResult> {
|
|
421
|
+
return useAgentCallback<AgentPromptOptions, Promise<AgentPromptResult>>(promptAdapter)
|
|
422
|
+
}
|
|
389
423
|
|
|
390
424
|
/**
|
|
391
425
|
* @internal
|
|
@@ -393,7 +427,7 @@ export const useAgentPrompt: () => (options: AgentPromptOptions) => Promise<Agen
|
|
|
393
427
|
*/
|
|
394
428
|
function patchAdapter(
|
|
395
429
|
instance: SanityInstance,
|
|
396
|
-
options: AgentPatchOptions,
|
|
430
|
+
options: AgentPatchOptions & AgentResourceOptions,
|
|
397
431
|
): Promise<AgentPatchResult> {
|
|
398
432
|
return firstValueFrom(agentPatch(instance, options))
|
|
399
433
|
}
|
|
@@ -547,5 +581,8 @@ function patchAdapter(
|
|
|
547
581
|
*
|
|
548
582
|
* @category Agent Actions
|
|
549
583
|
*/
|
|
550
|
-
export
|
|
551
|
-
|
|
584
|
+
export function useAgentPatch(): (
|
|
585
|
+
options: AgentPatchOptions & WithResourceNameSupport<AgentResourceOptions>,
|
|
586
|
+
) => Promise<AgentPatchResult> {
|
|
587
|
+
return useAgentCallback<AgentPatchOptions, Promise<AgentPatchResult>>(patchAdapter)
|
|
588
|
+
}
|
|
@@ -22,7 +22,10 @@ describe('useDashboardOrganizationId', () => {
|
|
|
22
22
|
|
|
23
23
|
const {result} = renderHook(() => useDashboardOrganizationId(), {
|
|
24
24
|
wrapper: ({children}) => (
|
|
25
|
-
<ResourceProvider
|
|
25
|
+
<ResourceProvider
|
|
26
|
+
resource={{projectId: 'test-project', dataset: 'test-dataset'}}
|
|
27
|
+
fallback={null}
|
|
28
|
+
>
|
|
26
29
|
{children}
|
|
27
30
|
</ResourceProvider>
|
|
28
31
|
),
|
|
@@ -41,7 +44,10 @@ describe('useDashboardOrganizationId', () => {
|
|
|
41
44
|
|
|
42
45
|
const {result} = renderHook(() => useDashboardOrganizationId(), {
|
|
43
46
|
wrapper: ({children}) => (
|
|
44
|
-
<ResourceProvider
|
|
47
|
+
<ResourceProvider
|
|
48
|
+
resource={{projectId: 'test-project', dataset: 'test-dataset'}}
|
|
49
|
+
fallback={null}
|
|
50
|
+
>
|
|
45
51
|
{children}
|
|
46
52
|
</ResourceProvider>
|
|
47
53
|
),
|
|
@@ -26,7 +26,10 @@ describe('useVerifyOrgProjects', () => {
|
|
|
26
26
|
it('should return null and not observe state if disabled', () => {
|
|
27
27
|
const {result} = renderHook(() => useVerifyOrgProjects(true, testProjectIds), {
|
|
28
28
|
wrapper: ({children}) => (
|
|
29
|
-
<ResourceProvider
|
|
29
|
+
<ResourceProvider
|
|
30
|
+
resource={{projectId: 'test-project', dataset: 'test-dataset'}}
|
|
31
|
+
fallback={null}
|
|
32
|
+
>
|
|
30
33
|
{children}
|
|
31
34
|
</ResourceProvider>
|
|
32
35
|
),
|
|
@@ -39,7 +42,10 @@ describe('useVerifyOrgProjects', () => {
|
|
|
39
42
|
it('should return null and not observe state if projectIds is missing or empty', () => {
|
|
40
43
|
const {result: resultUndefined} = renderHook(() => useVerifyOrgProjects(false, undefined), {
|
|
41
44
|
wrapper: ({children}) => (
|
|
42
|
-
<ResourceProvider
|
|
45
|
+
<ResourceProvider
|
|
46
|
+
resource={{projectId: 'test-project', dataset: 'test-dataset'}}
|
|
47
|
+
fallback={null}
|
|
48
|
+
>
|
|
43
49
|
{children}
|
|
44
50
|
</ResourceProvider>
|
|
45
51
|
),
|
|
@@ -49,7 +55,10 @@ describe('useVerifyOrgProjects', () => {
|
|
|
49
55
|
|
|
50
56
|
const {result: resultEmpty} = renderHook(() => useVerifyOrgProjects(false, []), {
|
|
51
57
|
wrapper: ({children}) => (
|
|
52
|
-
<ResourceProvider
|
|
58
|
+
<ResourceProvider
|
|
59
|
+
resource={{projectId: 'test-project', dataset: 'test-dataset'}}
|
|
60
|
+
fallback={null}
|
|
61
|
+
>
|
|
53
62
|
{children}
|
|
54
63
|
</ResourceProvider>
|
|
55
64
|
),
|
|
@@ -64,7 +73,10 @@ describe('useVerifyOrgProjects', () => {
|
|
|
64
73
|
|
|
65
74
|
const {result} = renderHook(() => useVerifyOrgProjects(false, testProjectIds), {
|
|
66
75
|
wrapper: ({children}) => (
|
|
67
|
-
<ResourceProvider
|
|
76
|
+
<ResourceProvider
|
|
77
|
+
resource={{projectId: 'test-project', dataset: 'test-dataset'}}
|
|
78
|
+
fallback={null}
|
|
79
|
+
>
|
|
68
80
|
{children}
|
|
69
81
|
</ResourceProvider>
|
|
70
82
|
),
|
|
@@ -80,7 +92,10 @@ describe('useVerifyOrgProjects', () => {
|
|
|
80
92
|
|
|
81
93
|
const {result} = renderHook(() => useVerifyOrgProjects(false, testProjectIds), {
|
|
82
94
|
wrapper: ({children}) => (
|
|
83
|
-
<ResourceProvider
|
|
95
|
+
<ResourceProvider
|
|
96
|
+
resource={{projectId: 'test-project', dataset: 'test-dataset'}}
|
|
97
|
+
fallback={null}
|
|
98
|
+
>
|
|
84
99
|
{children}
|
|
85
100
|
</ResourceProvider>
|
|
86
101
|
),
|
|
@@ -102,7 +117,10 @@ describe('useVerifyOrgProjects', () => {
|
|
|
102
117
|
|
|
103
118
|
const {result} = renderHook(() => useVerifyOrgProjects(false, testProjectIds), {
|
|
104
119
|
wrapper: ({children}) => (
|
|
105
|
-
<ResourceProvider
|
|
120
|
+
<ResourceProvider
|
|
121
|
+
resource={{projectId: 'test-project', dataset: 'test-dataset'}}
|
|
122
|
+
fallback={null}
|
|
123
|
+
>
|
|
106
124
|
{children}
|
|
107
125
|
</ResourceProvider>
|
|
108
126
|
),
|
|
@@ -124,7 +142,10 @@ describe('useVerifyOrgProjects', () => {
|
|
|
124
142
|
|
|
125
143
|
const {unmount} = renderHook(() => useVerifyOrgProjects(false, testProjectIds), {
|
|
126
144
|
wrapper: ({children}) => (
|
|
127
|
-
<ResourceProvider
|
|
145
|
+
<ResourceProvider
|
|
146
|
+
resource={{projectId: 'test-project', dataset: 'test-dataset'}}
|
|
147
|
+
fallback={null}
|
|
148
|
+
>
|
|
128
149
|
{children}
|
|
129
150
|
</ResourceProvider>
|
|
130
151
|
),
|
|
@@ -152,7 +173,10 @@ describe('useVerifyOrgProjects', () => {
|
|
|
152
173
|
{
|
|
153
174
|
initialProps: {disabled: false, pIds: testProjectIds},
|
|
154
175
|
wrapper: ({children}) => (
|
|
155
|
-
<ResourceProvider
|
|
176
|
+
<ResourceProvider
|
|
177
|
+
resource={{projectId: 'test-project', dataset: 'test-dataset'}}
|
|
178
|
+
fallback={null}
|
|
179
|
+
>
|
|
156
180
|
{children}
|
|
157
181
|
</ResourceProvider>
|
|
158
182
|
),
|