@sanity/sdk-react 2.15.0 → 2.16.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 +22 -1
- package/dist/index.d.ts +55 -2
- package/dist/index.js +524 -332
- package/dist/index.js.map +1 -1
- package/package.json +16 -16
- package/src/components/SDKProvider.test.tsx +135 -22
- package/src/components/SDKProvider.tsx +54 -17
- package/src/components/SanityApp.tsx +29 -0
- package/src/context/OrganizationResourcesProvider.test.tsx +189 -0
- package/src/context/OrganizationResourcesProvider.tsx +111 -0
- package/src/context/ProjectContext.ts +15 -0
- package/src/context/ResourceProvider.test.tsx +57 -1
- package/src/context/ResourceProvider.tsx +30 -9
- package/src/hooks/datasets/useDatasets.test.tsx +116 -0
- package/src/hooks/datasets/useDatasets.ts +20 -8
- package/src/hooks/document/useApplyDocumentActions.ts +1 -1
- package/src/hooks/document/useDocument.ts +2 -2
- package/src/hooks/helpers/useResolvedProjectId.test.tsx +59 -0
- package/src/hooks/helpers/useResolvedProjectId.ts +35 -0
- package/src/hooks/projects/useProject.test.tsx +114 -0
- package/src/hooks/projects/useProject.ts +17 -7
- package/src/hooks/users/useUsers.test.tsx +101 -2
- package/src/hooks/users/useUsers.ts +35 -3
- package/src/utils/resolveOrgResources.test.ts +111 -0
- package/src/utils/resolveOrgResources.ts +69 -0
- package/src/hooks/datasets/useDatasets.test.ts +0 -80
- package/src/hooks/projects/useProject.test.ts +0 -80
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import {getProjectState, type Project, type ProjectOptions, resolveProject} from '@sanity/sdk'
|
|
2
|
-
import {identity} from 'rxjs'
|
|
3
2
|
|
|
4
3
|
import {createStateSourceHook} from '../helpers/createStateSourceHook'
|
|
4
|
+
import {useResolvedProjectId} from '../helpers/useResolvedProjectId'
|
|
5
|
+
|
|
6
|
+
const useProjectBase = createStateSourceHook({
|
|
7
|
+
getState: getProjectState,
|
|
8
|
+
shouldSuspend: (instance, ...params) =>
|
|
9
|
+
getProjectState(instance, ...params).getCurrent() === undefined,
|
|
10
|
+
suspender: resolveProject,
|
|
11
|
+
})
|
|
5
12
|
|
|
6
13
|
/**
|
|
7
14
|
* Returns metadata for a given project.
|
|
@@ -29,15 +36,18 @@ import {createStateSourceHook} from '../helpers/createStateSourceHook'
|
|
|
29
36
|
* const projectWithoutMembers = useProject({projectId, includeMembers: false})
|
|
30
37
|
* const projectWithoutFeatures = useProject({projectId, includeFeatures: false})
|
|
31
38
|
* ```
|
|
39
|
+
* @remarks
|
|
40
|
+
* The `projectId` is resolved in order from:
|
|
41
|
+
* 1. an explicit `projectId` option
|
|
42
|
+
* 2. A legacy ProjectContext (e.g. a `<ResourceProvider projectId="…">` with no dataset), then
|
|
43
|
+
* 3. The active resource (`ResourceProvider`/`SDKProvider`)
|
|
44
|
+
* 4. `instance.config`.
|
|
32
45
|
* @public
|
|
33
46
|
* @function
|
|
34
47
|
*/
|
|
35
|
-
export const useProject =
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
getProjectState(instance, ...params).getCurrent() === undefined,
|
|
39
|
-
suspender: resolveProject,
|
|
40
|
-
getConfig: identity,
|
|
48
|
+
export const useProject = ((options?: ProjectOptions<boolean, boolean>) => {
|
|
49
|
+
const projectId = useResolvedProjectId(options)
|
|
50
|
+
return useProjectBase(projectId ? {...options, projectId} : options)
|
|
41
51
|
}) as <IncludeMembers extends boolean = true, IncludeFeatures extends boolean = true>(
|
|
42
52
|
options?: ProjectOptions<IncludeMembers, IncludeFeatures>,
|
|
43
53
|
) => Project<IncludeMembers, IncludeFeatures>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
createSanityInstance,
|
|
2
3
|
getUsersState,
|
|
3
4
|
loadMoreUsers,
|
|
4
5
|
resolveUsers,
|
|
@@ -6,12 +7,14 @@ import {
|
|
|
6
7
|
type StateSource,
|
|
7
8
|
type UserProfile,
|
|
8
9
|
} from '@sanity/sdk'
|
|
9
|
-
import {act, fireEvent, render, screen} from '@testing-library/react'
|
|
10
|
-
import {useState} from 'react'
|
|
10
|
+
import {act, fireEvent, render, renderHook, screen} from '@testing-library/react'
|
|
11
|
+
import {type ReactNode, useState} from 'react'
|
|
11
12
|
import {type Observable, Subject} from 'rxjs'
|
|
12
13
|
import {describe, expect, it, vi} from 'vitest'
|
|
13
14
|
|
|
14
15
|
import {ResourceProvider} from '../../context/ResourceProvider'
|
|
16
|
+
import {SanityInstanceContext} from '../../context/SanityInstanceContext'
|
|
17
|
+
import {useSanityInstance} from '../context/useSanityInstance'
|
|
15
18
|
import {useUsers} from './useUsers'
|
|
16
19
|
|
|
17
20
|
// Mock the functions from '@sanity/sdk'
|
|
@@ -335,4 +338,100 @@ describe('useUsers', () => {
|
|
|
335
338
|
},
|
|
336
339
|
)
|
|
337
340
|
})
|
|
341
|
+
|
|
342
|
+
it('resolves the projectId from the default resource for a project-scoped query', () => {
|
|
343
|
+
vi.mocked(getUsersState).mockReturnValue({
|
|
344
|
+
getCurrent: vi.fn().mockReturnValue({data: mockUsers, hasMore: false, totalCount: 2}),
|
|
345
|
+
subscribe: vi.fn(),
|
|
346
|
+
get observable(): Observable<unknown> {
|
|
347
|
+
throw new Error('Not implemented')
|
|
348
|
+
},
|
|
349
|
+
} as unknown as StateSource<
|
|
350
|
+
{data: SanityUser[]; totalCount: number; hasMore: boolean} | undefined
|
|
351
|
+
>)
|
|
352
|
+
|
|
353
|
+
const {
|
|
354
|
+
result: {current: instance},
|
|
355
|
+
} = renderHook(
|
|
356
|
+
() => {
|
|
357
|
+
useUsers({resourceType: 'project'})
|
|
358
|
+
return useSanityInstance()
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
wrapper: ({children}: {children: ReactNode}) => (
|
|
362
|
+
<ResourceProvider
|
|
363
|
+
resource={{projectId: 'resource-project', dataset: 'production'}}
|
|
364
|
+
fallback={null}
|
|
365
|
+
>
|
|
366
|
+
{children}
|
|
367
|
+
</ResourceProvider>
|
|
368
|
+
),
|
|
369
|
+
},
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
expect(getUsersState).toHaveBeenLastCalledWith(
|
|
373
|
+
instance,
|
|
374
|
+
expect.objectContaining({projectId: 'resource-project', resourceType: 'project'}),
|
|
375
|
+
)
|
|
376
|
+
})
|
|
377
|
+
|
|
378
|
+
it('does not inject a projectId for an organization-scoped query', () => {
|
|
379
|
+
vi.mocked(getUsersState).mockReturnValue({
|
|
380
|
+
getCurrent: vi.fn().mockReturnValue({data: mockUsers, hasMore: false, totalCount: 2}),
|
|
381
|
+
subscribe: vi.fn(),
|
|
382
|
+
get observable(): Observable<unknown> {
|
|
383
|
+
throw new Error('Not implemented')
|
|
384
|
+
},
|
|
385
|
+
} as unknown as StateSource<
|
|
386
|
+
{data: SanityUser[]; totalCount: number; hasMore: boolean} | undefined
|
|
387
|
+
>)
|
|
388
|
+
|
|
389
|
+
function TestComponent() {
|
|
390
|
+
useUsers({resourceType: 'organization', organizationId: 'test-org'})
|
|
391
|
+
return null
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
render(
|
|
395
|
+
<ResourceProvider
|
|
396
|
+
resource={{projectId: 'resource-project', dataset: 'production'}}
|
|
397
|
+
fallback={null}
|
|
398
|
+
>
|
|
399
|
+
<TestComponent />
|
|
400
|
+
</ResourceProvider>,
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
// Organization queries key off organizationId; the ambient project resource
|
|
404
|
+
// must not leak in as a projectId.
|
|
405
|
+
expect(vi.mocked(getUsersState).mock.lastCall?.[1]).not.toHaveProperty('projectId')
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
it('resolves a bare projectId from a ResourceProvider when the parent instance has no config', () => {
|
|
409
|
+
vi.mocked(getUsersState).mockReturnValue({
|
|
410
|
+
getCurrent: vi.fn().mockReturnValue({data: mockUsers, hasMore: false, totalCount: 2}),
|
|
411
|
+
subscribe: vi.fn(),
|
|
412
|
+
get observable(): Observable<unknown> {
|
|
413
|
+
throw new Error('Not implemented')
|
|
414
|
+
},
|
|
415
|
+
} as unknown as StateSource<
|
|
416
|
+
{data: SanityUser[]; totalCount: number; hasMore: boolean} | undefined
|
|
417
|
+
>)
|
|
418
|
+
|
|
419
|
+
// An instance with no project/dataset config, then a
|
|
420
|
+
// projectId-only ResourceProvider.
|
|
421
|
+
const emptyInstance = createSanityInstance({})
|
|
422
|
+
renderHook(() => useUsers(), {
|
|
423
|
+
wrapper: ({children}: {children: ReactNode}) => (
|
|
424
|
+
<SanityInstanceContext.Provider value={emptyInstance}>
|
|
425
|
+
<ResourceProvider projectId="bare-project" fallback={null}>
|
|
426
|
+
{children}
|
|
427
|
+
</ResourceProvider>
|
|
428
|
+
</SanityInstanceContext.Provider>
|
|
429
|
+
),
|
|
430
|
+
})
|
|
431
|
+
|
|
432
|
+
expect(getUsersState).toHaveBeenLastCalledWith(
|
|
433
|
+
emptyInstance,
|
|
434
|
+
expect.objectContaining({projectId: 'bare-project'}),
|
|
435
|
+
)
|
|
436
|
+
})
|
|
338
437
|
})
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
import {useCallback, useEffect, useMemo, useState, useSyncExternalStore, useTransition} from 'react'
|
|
11
11
|
|
|
12
12
|
import {useSanityInstance} from '../context/useSanityInstance'
|
|
13
|
+
import {useResolvedProjectId} from '../helpers/useResolvedProjectId'
|
|
13
14
|
import {trackHookUsage} from '../helpers/useTrackHookUsage'
|
|
14
15
|
|
|
15
16
|
/**
|
|
@@ -36,6 +37,23 @@ export interface UsersResult {
|
|
|
36
37
|
loadMore: () => void
|
|
37
38
|
}
|
|
38
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Injects a resolved `projectId` into project-scoped users options. A missing
|
|
42
|
+
* `projectId` is a no-op, and organization-scoped queries (explicit
|
|
43
|
+
* `resourceType`/`organizationId`) or options that already carry a `projectId`
|
|
44
|
+
* are returned unchanged.
|
|
45
|
+
*/
|
|
46
|
+
function withResolvedProjectId(
|
|
47
|
+
options: GetUsersOptions | undefined,
|
|
48
|
+
projectId: string | undefined,
|
|
49
|
+
): GetUsersOptions | undefined {
|
|
50
|
+
if (!projectId) return options
|
|
51
|
+
if (!options) return {projectId}
|
|
52
|
+
const isOrgScoped = options.resourceType === 'organization' || !!options.organizationId
|
|
53
|
+
if (isOrgScoped || options.projectId) return options
|
|
54
|
+
return {...options, projectId}
|
|
55
|
+
}
|
|
56
|
+
|
|
39
57
|
/**
|
|
40
58
|
*
|
|
41
59
|
* @public
|
|
@@ -67,6 +85,12 @@ export interface UsersResult {
|
|
|
67
85
|
* </div>
|
|
68
86
|
* )
|
|
69
87
|
* ```
|
|
88
|
+
* @remarks
|
|
89
|
+
* For project-scoped queries the `projectId` is resolved in order from:
|
|
90
|
+
* 1. an explicit `projectId` option
|
|
91
|
+
* 2. A legacy ProjectContext (e.g. a `<ResourceProvider projectId="…">` with no dataset), then
|
|
92
|
+
* 3. The active resource (`ResourceProvider`/`SDKProvider`)
|
|
93
|
+
* 4. `instance.config`.
|
|
70
94
|
*/
|
|
71
95
|
export function useUsers(options?: GetUsersOptions): UsersResult {
|
|
72
96
|
const instance = useSanityInstance()
|
|
@@ -74,8 +98,16 @@ export function useUsers(options?: GetUsersOptions): UsersResult {
|
|
|
74
98
|
// Use React's useTransition to avoid UI jank when user options change
|
|
75
99
|
const [isPending, startTransition] = useTransition()
|
|
76
100
|
|
|
101
|
+
// Resolve the projectId from the ambient project/resource context so a
|
|
102
|
+
// project-scoped users request can pick it up rather than the top-level config.
|
|
103
|
+
const resolvedProjectId = useResolvedProjectId(options)
|
|
104
|
+
const effectiveOptions = useMemo(
|
|
105
|
+
() => withResolvedProjectId(options, resolvedProjectId),
|
|
106
|
+
[options, resolvedProjectId],
|
|
107
|
+
)
|
|
108
|
+
|
|
77
109
|
// Get the unique key for this users request and its options
|
|
78
|
-
const key = getUsersKey(instance,
|
|
110
|
+
const key = getUsersKey(instance, effectiveOptions)
|
|
79
111
|
// Use a deferred state to avoid immediate re-renders when the users request changes
|
|
80
112
|
const [deferredKey, setDeferredKey] = useState(key)
|
|
81
113
|
// Parse the deferred users key back into users options
|
|
@@ -115,8 +147,8 @@ export function useUsers(options?: GetUsersOptions): UsersResult {
|
|
|
115
147
|
const {data, hasMore} = useSyncExternalStore(subscribe, getCurrent)!
|
|
116
148
|
|
|
117
149
|
const loadMore = useCallback(() => {
|
|
118
|
-
loadMoreUsers(instance,
|
|
119
|
-
}, [instance,
|
|
150
|
+
loadMoreUsers(instance, effectiveOptions)
|
|
151
|
+
}, [instance, effectiveOptions])
|
|
120
152
|
|
|
121
153
|
return {data, hasMore, isPending, loadMore}
|
|
122
154
|
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import {type SanityClient} from '@sanity/client'
|
|
2
|
+
import {getClientState, type SanityInstance, type StateSource} from '@sanity/sdk'
|
|
3
|
+
import {firstValueFrom} from 'rxjs'
|
|
4
|
+
import {beforeEach, describe, expect, it, vi} from 'vitest'
|
|
5
|
+
|
|
6
|
+
import {resolveOrgResources} from './resolveOrgResources'
|
|
7
|
+
|
|
8
|
+
vi.mock('@sanity/sdk', () => ({
|
|
9
|
+
getClientState: vi.fn(),
|
|
10
|
+
}))
|
|
11
|
+
|
|
12
|
+
vi.mock('rxjs', () => ({
|
|
13
|
+
firstValueFrom: vi.fn(),
|
|
14
|
+
}))
|
|
15
|
+
|
|
16
|
+
const mockGetClientState = vi.mocked(getClientState)
|
|
17
|
+
const mockFirstValueFrom = vi.mocked(firstValueFrom)
|
|
18
|
+
|
|
19
|
+
const mockRequest = vi.fn()
|
|
20
|
+
const mockClient = {request: mockRequest}
|
|
21
|
+
const mockInstance = {} as SanityInstance
|
|
22
|
+
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
vi.clearAllMocks()
|
|
25
|
+
mockGetClientState.mockReturnValue({
|
|
26
|
+
observable: 'mock-observable',
|
|
27
|
+
} as unknown as StateSource<SanityClient>)
|
|
28
|
+
mockFirstValueFrom.mockResolvedValue(mockClient as unknown as SanityClient)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const ORG_ID = 'org-1'
|
|
32
|
+
|
|
33
|
+
describe('resolveOrgResources', () => {
|
|
34
|
+
it('returns both mediaLibrary and canvas when both requests succeed', async () => {
|
|
35
|
+
mockRequest
|
|
36
|
+
.mockResolvedValueOnce({data: [{id: 'ml-123'}]})
|
|
37
|
+
.mockResolvedValueOnce({data: [{id: 'canvas-456'}]})
|
|
38
|
+
|
|
39
|
+
const result = await resolveOrgResources(mockInstance, ORG_ID)
|
|
40
|
+
|
|
41
|
+
expect(result).toEqual({
|
|
42
|
+
mediaLibrary: {mediaLibraryId: 'ml-123'},
|
|
43
|
+
canvas: {canvasId: 'canvas-456'},
|
|
44
|
+
})
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('scopes both requests to the organization', async () => {
|
|
48
|
+
mockRequest
|
|
49
|
+
.mockResolvedValueOnce({data: [{id: 'ml-123'}]})
|
|
50
|
+
.mockResolvedValueOnce({data: [{id: 'canvas-456'}]})
|
|
51
|
+
|
|
52
|
+
await resolveOrgResources(mockInstance, ORG_ID)
|
|
53
|
+
|
|
54
|
+
expect(mockRequest).toHaveBeenCalledWith(
|
|
55
|
+
expect.objectContaining({uri: '/media-libraries', query: {organizationId: ORG_ID}}),
|
|
56
|
+
)
|
|
57
|
+
expect(mockRequest).toHaveBeenCalledWith(
|
|
58
|
+
expect.objectContaining({uri: '/canvases', query: {organizationId: ORG_ID}}),
|
|
59
|
+
)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('returns only mediaLibrary when canvas request fails', async () => {
|
|
63
|
+
mockRequest
|
|
64
|
+
.mockResolvedValueOnce({data: [{id: 'ml-123'}]})
|
|
65
|
+
.mockRejectedValueOnce(new Error('canvas not found'))
|
|
66
|
+
|
|
67
|
+
const result = await resolveOrgResources(mockInstance, ORG_ID)
|
|
68
|
+
|
|
69
|
+
expect(result).toEqual({
|
|
70
|
+
mediaLibrary: {mediaLibraryId: 'ml-123'},
|
|
71
|
+
canvas: undefined,
|
|
72
|
+
})
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('returns only canvas when mediaLibrary request fails', async () => {
|
|
76
|
+
mockRequest
|
|
77
|
+
.mockRejectedValueOnce(new Error('media library not found'))
|
|
78
|
+
.mockResolvedValueOnce({data: [{id: 'canvas-456'}]})
|
|
79
|
+
|
|
80
|
+
const result = await resolveOrgResources(mockInstance, ORG_ID)
|
|
81
|
+
|
|
82
|
+
expect(result).toEqual({
|
|
83
|
+
mediaLibrary: undefined,
|
|
84
|
+
canvas: {canvasId: 'canvas-456'},
|
|
85
|
+
})
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('returns undefined for both when both requests fail', async () => {
|
|
89
|
+
mockRequest
|
|
90
|
+
.mockRejectedValueOnce(new Error('error 1'))
|
|
91
|
+
.mockRejectedValueOnce(new Error('error 2'))
|
|
92
|
+
|
|
93
|
+
const result = await resolveOrgResources(mockInstance, ORG_ID)
|
|
94
|
+
|
|
95
|
+
expect(result).toEqual({
|
|
96
|
+
mediaLibrary: undefined,
|
|
97
|
+
canvas: undefined,
|
|
98
|
+
})
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it('returns undefined for both when data arrays are empty', async () => {
|
|
102
|
+
mockRequest.mockResolvedValueOnce({data: []}).mockResolvedValueOnce({data: []})
|
|
103
|
+
|
|
104
|
+
const result = await resolveOrgResources(mockInstance, ORG_ID)
|
|
105
|
+
|
|
106
|
+
expect(result).toEqual({
|
|
107
|
+
mediaLibrary: undefined,
|
|
108
|
+
canvas: undefined,
|
|
109
|
+
})
|
|
110
|
+
})
|
|
111
|
+
})
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type CanvasResource,
|
|
3
|
+
getClientState,
|
|
4
|
+
type MediaLibraryResource,
|
|
5
|
+
type SanityInstance,
|
|
6
|
+
} from '@sanity/sdk'
|
|
7
|
+
import {firstValueFrom} from 'rxjs'
|
|
8
|
+
|
|
9
|
+
const API_VERSION = 'v2026-07-09'
|
|
10
|
+
|
|
11
|
+
interface OrgResourcesApiItem {
|
|
12
|
+
id: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface OrgResourcesApiResponse {
|
|
16
|
+
data: OrgResourcesApiItem[]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface OrgResources {
|
|
20
|
+
mediaLibrary?: MediaLibraryResource
|
|
21
|
+
canvas?: CanvasResource
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Gets the id of the first resource from a settled request result. The request
|
|
26
|
+
* is already scoped to a single organization, so the first item is the one we want.
|
|
27
|
+
*/
|
|
28
|
+
function getFirstResourceId(
|
|
29
|
+
result: PromiseSettledResult<OrgResourcesApiResponse>,
|
|
30
|
+
): string | undefined {
|
|
31
|
+
if (result.status !== 'fulfilled') return undefined
|
|
32
|
+
return result.value.data?.[0]?.id
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Fetches the media library and canvas resources for the given organization.
|
|
37
|
+
* Both requests are scoped to `organizationId` so each response contains only
|
|
38
|
+
* that org's resources. Each resource is fetched independently — a failure for
|
|
39
|
+
* one does not prevent the other from resolving.
|
|
40
|
+
*/
|
|
41
|
+
export async function resolveOrgResources(
|
|
42
|
+
instance: SanityInstance,
|
|
43
|
+
organizationId: string,
|
|
44
|
+
): Promise<OrgResources> {
|
|
45
|
+
const client = await firstValueFrom(
|
|
46
|
+
getClientState(instance, {apiVersion: API_VERSION, scope: 'global'}).observable,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
const [mediaLibrariesResult, canvasesResult] = await Promise.allSettled([
|
|
50
|
+
client.request<OrgResourcesApiResponse>({
|
|
51
|
+
uri: `/media-libraries`,
|
|
52
|
+
query: {organizationId},
|
|
53
|
+
tag: 'org-resources.media-libraries',
|
|
54
|
+
}),
|
|
55
|
+
client.request<OrgResourcesApiResponse>({
|
|
56
|
+
uri: `/canvases`,
|
|
57
|
+
query: {organizationId},
|
|
58
|
+
tag: 'org-resources.canvases',
|
|
59
|
+
}),
|
|
60
|
+
])
|
|
61
|
+
|
|
62
|
+
const mediaLibraryId = getFirstResourceId(mediaLibrariesResult)
|
|
63
|
+
const canvasId = getFirstResourceId(canvasesResult)
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
mediaLibrary: mediaLibraryId ? {mediaLibraryId} : undefined,
|
|
67
|
+
canvas: canvasId ? {canvasId} : undefined,
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import {getDatasetsState, type ProjectHandle, type SanityInstance} from '@sanity/sdk'
|
|
2
|
-
import {beforeEach, describe, expect, it, vi} from 'vitest'
|
|
3
|
-
|
|
4
|
-
import {createStateSourceHook} from '../helpers/createStateSourceHook'
|
|
5
|
-
|
|
6
|
-
// Mock dependencies
|
|
7
|
-
vi.mock('@sanity/sdk', () => ({
|
|
8
|
-
getDatasetsState: vi.fn(() => ({
|
|
9
|
-
getCurrent: vi.fn(() => undefined), // Mocking getCurrent to satisfy the call within shouldSuspend
|
|
10
|
-
})),
|
|
11
|
-
resolveDatasets: vi.fn(),
|
|
12
|
-
}))
|
|
13
|
-
vi.mock('../helpers/createStateSourceHook', () => ({
|
|
14
|
-
createStateSourceHook: vi.fn(),
|
|
15
|
-
}))
|
|
16
|
-
|
|
17
|
-
describe('useDatasets', () => {
|
|
18
|
-
// Use beforeEach to reset modules and ensure mocks are fresh for each test
|
|
19
|
-
beforeEach(() => {
|
|
20
|
-
vi.resetModules()
|
|
21
|
-
// Re-mock dependencies for each test after resetModules
|
|
22
|
-
vi.mock('@sanity/sdk', () => ({
|
|
23
|
-
getDatasetsState: vi.fn(() => ({
|
|
24
|
-
getCurrent: vi.fn(() => undefined),
|
|
25
|
-
})),
|
|
26
|
-
resolveDatasets: vi.fn(),
|
|
27
|
-
}))
|
|
28
|
-
vi.mock('../helpers/createStateSourceHook', () => ({
|
|
29
|
-
createStateSourceHook: vi.fn(),
|
|
30
|
-
}))
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
it('should call createStateSourceHook with correct arguments on import', async () => {
|
|
34
|
-
// Dynamically import the hook *after* mocks are set up and modules reset
|
|
35
|
-
await import('./useDatasets')
|
|
36
|
-
|
|
37
|
-
// Check if createStateSourceHook was called during the module evaluation (import)
|
|
38
|
-
expect(createStateSourceHook).toHaveBeenCalled()
|
|
39
|
-
expect(createStateSourceHook).toHaveBeenCalledWith(
|
|
40
|
-
expect.objectContaining({
|
|
41
|
-
getState: expect.any(Function),
|
|
42
|
-
shouldSuspend: expect.any(Function),
|
|
43
|
-
suspender: expect.any(Function), // Actual function reference doesn't matter here as it's mocked
|
|
44
|
-
getConfig: expect.any(Function), // Actual function reference doesn't matter here
|
|
45
|
-
}),
|
|
46
|
-
)
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
it('shouldSuspend should call getDatasetsState and getCurrent', async () => {
|
|
50
|
-
// Dynamically import the hook *after* mocks are set up and modules reset
|
|
51
|
-
await import('./useDatasets')
|
|
52
|
-
|
|
53
|
-
// Get the arguments passed to createStateSourceHook
|
|
54
|
-
// Need to ensure createStateSourceHook mock is correctly typed for access
|
|
55
|
-
const mockCreateStateSourceHook = createStateSourceHook as ReturnType<typeof vi.fn>
|
|
56
|
-
expect(mockCreateStateSourceHook.mock.calls.length).toBeGreaterThan(0)
|
|
57
|
-
const createStateSourceHookArgs = mockCreateStateSourceHook.mock.calls[0][0]
|
|
58
|
-
const shouldSuspend = createStateSourceHookArgs.shouldSuspend
|
|
59
|
-
|
|
60
|
-
// Mock instance and projectHandle for the test call
|
|
61
|
-
const mockInstance = {} as SanityInstance // Use specific type
|
|
62
|
-
const mockProjectHandle = {} as ProjectHandle // Use specific type
|
|
63
|
-
|
|
64
|
-
// Call the shouldSuspend function
|
|
65
|
-
const result = shouldSuspend(mockInstance, mockProjectHandle)
|
|
66
|
-
|
|
67
|
-
// Assert that getDatasetsState was called with the correct arguments
|
|
68
|
-
// Need to ensure getDatasetsState mock is correctly typed for access
|
|
69
|
-
const mockGetDatasetsState = getDatasetsState as ReturnType<typeof vi.fn>
|
|
70
|
-
expect(mockGetDatasetsState).toHaveBeenCalledWith(mockInstance, mockProjectHandle)
|
|
71
|
-
|
|
72
|
-
// Assert that getCurrent was called on the result of getDatasetsState
|
|
73
|
-
expect(mockGetDatasetsState.mock.results.length).toBeGreaterThan(0)
|
|
74
|
-
const getDatasetsStateMockResult = mockGetDatasetsState.mock.results[0].value
|
|
75
|
-
expect(getDatasetsStateMockResult.getCurrent).toHaveBeenCalled()
|
|
76
|
-
|
|
77
|
-
// Assert the result of shouldSuspend based on the mocked getCurrent value
|
|
78
|
-
expect(result).toBe(true) // Since getCurrent is mocked to return undefined
|
|
79
|
-
})
|
|
80
|
-
})
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import {getProjectState, type ProjectHandle, type SanityInstance} from '@sanity/sdk'
|
|
2
|
-
import {beforeEach, describe, expect, it, vi} from 'vitest'
|
|
3
|
-
|
|
4
|
-
import {createStateSourceHook} from '../helpers/createStateSourceHook'
|
|
5
|
-
|
|
6
|
-
// Mock dependencies
|
|
7
|
-
vi.mock('@sanity/sdk', () => ({
|
|
8
|
-
getProjectState: vi.fn(() => ({
|
|
9
|
-
getCurrent: vi.fn(() => undefined), // Mocking getCurrent to satisfy the call within shouldSuspend
|
|
10
|
-
})),
|
|
11
|
-
resolveProject: vi.fn(),
|
|
12
|
-
}))
|
|
13
|
-
vi.mock('../helpers/createStateSourceHook', () => ({
|
|
14
|
-
createStateSourceHook: vi.fn(),
|
|
15
|
-
}))
|
|
16
|
-
|
|
17
|
-
describe('useProject', () => {
|
|
18
|
-
// Use beforeEach to reset modules and ensure mocks are fresh for each test
|
|
19
|
-
beforeEach(() => {
|
|
20
|
-
vi.resetModules()
|
|
21
|
-
// Re-mock dependencies for each test after resetModules
|
|
22
|
-
vi.mock('@sanity/sdk', () => ({
|
|
23
|
-
getProjectState: vi.fn(() => ({
|
|
24
|
-
getCurrent: vi.fn(() => undefined),
|
|
25
|
-
})),
|
|
26
|
-
resolveProject: vi.fn(),
|
|
27
|
-
}))
|
|
28
|
-
vi.mock('../helpers/createStateSourceHook', () => ({
|
|
29
|
-
createStateSourceHook: vi.fn(),
|
|
30
|
-
}))
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
it('should call createStateSourceHook with correct arguments on import', async () => {
|
|
34
|
-
// Dynamically import the hook *after* mocks are set up and modules reset
|
|
35
|
-
await import('./useProject')
|
|
36
|
-
|
|
37
|
-
// Check if createStateSourceHook was called during the module evaluation (import)
|
|
38
|
-
expect(createStateSourceHook).toHaveBeenCalled()
|
|
39
|
-
expect(createStateSourceHook).toHaveBeenCalledWith(
|
|
40
|
-
expect.objectContaining({
|
|
41
|
-
getState: expect.any(Function),
|
|
42
|
-
shouldSuspend: expect.any(Function),
|
|
43
|
-
suspender: expect.any(Function), // Actual function reference doesn't matter here as it's mocked
|
|
44
|
-
getConfig: expect.any(Function), // Actual function reference doesn't matter here
|
|
45
|
-
}),
|
|
46
|
-
)
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
it('shouldSuspend should call getProjectState and getCurrent', async () => {
|
|
50
|
-
// Dynamically import the hook *after* mocks are set up and modules reset
|
|
51
|
-
await import('./useProject')
|
|
52
|
-
|
|
53
|
-
// Get the arguments passed to createStateSourceHook
|
|
54
|
-
// Need to ensure createStateSourceHook mock is correctly typed for access
|
|
55
|
-
const mockCreateStateSourceHook = createStateSourceHook as ReturnType<typeof vi.fn>
|
|
56
|
-
expect(mockCreateStateSourceHook.mock.calls.length).toBeGreaterThan(0)
|
|
57
|
-
const createStateSourceHookArgs = mockCreateStateSourceHook.mock.calls[0][0]
|
|
58
|
-
const shouldSuspend = createStateSourceHookArgs.shouldSuspend
|
|
59
|
-
|
|
60
|
-
// Mock instance and projectHandle for the test call
|
|
61
|
-
const mockInstance = {} as SanityInstance // Use specific type
|
|
62
|
-
const mockProjectHandle = {} as ProjectHandle // Use specific type
|
|
63
|
-
|
|
64
|
-
// Call the shouldSuspend function
|
|
65
|
-
const result = shouldSuspend(mockInstance, mockProjectHandle)
|
|
66
|
-
|
|
67
|
-
// Assert that getProjectState was called with the correct arguments
|
|
68
|
-
// Need to ensure getProjectState mock is correctly typed for access
|
|
69
|
-
const mockGetProjectState = getProjectState as ReturnType<typeof vi.fn>
|
|
70
|
-
expect(mockGetProjectState).toHaveBeenCalledWith(mockInstance, mockProjectHandle)
|
|
71
|
-
|
|
72
|
-
// Assert that getCurrent was called on the result of getProjectState
|
|
73
|
-
expect(mockGetProjectState.mock.results.length).toBeGreaterThan(0)
|
|
74
|
-
const getProjectStateMockResult = mockGetProjectState.mock.results[0].value
|
|
75
|
-
expect(getProjectStateMockResult.getCurrent).toHaveBeenCalled()
|
|
76
|
-
|
|
77
|
-
// Assert the result of shouldSuspend based on the mocked getCurrent value
|
|
78
|
-
expect(result).toBe(true) // Since getCurrent is mocked to return undefined
|
|
79
|
-
})
|
|
80
|
-
})
|