@sanity/sdk-react 2.18.0 → 3.0.0-rc.1
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 +267 -112
- package/dist/index.js +131 -89
- package/dist/index.js.map +1 -1
- package/package.json +11 -9
- package/src/_exports/sdk-react.ts +14 -1
- package/src/components/auth/AuthBoundary.tsx +10 -5
- package/src/context/WorkbenchTokenRefresh.test.tsx +106 -0
- package/src/context/WorkbenchTokenRefresh.tsx +61 -0
- package/src/context/workbenchToken.ts +63 -0
- package/src/hooks/access/useCheckPermissions.test-d.ts +15 -0
- package/src/hooks/access/useCheckPermissions.test.tsx +53 -0
- package/src/hooks/access/useCheckPermissions.ts +24 -0
- package/src/hooks/applications/useApplication.test-d.ts +30 -0
- package/src/hooks/applications/useApplication.ts +22 -0
- package/src/hooks/applications/useApplications.test-d.ts +31 -0
- package/src/hooks/applications/useApplications.ts +25 -0
- package/src/hooks/applications/useCreateUserApplication.test-d.ts +14 -0
- package/src/hooks/applications/useCreateUserApplication.ts +11 -0
- package/src/hooks/applications/useDeleteApplication.test-d.ts +12 -0
- package/src/hooks/applications/useDeleteApplication.ts +11 -0
- package/src/hooks/applications/useDeleteUserApplication.test-d.ts +14 -0
- package/src/hooks/applications/useDeleteUserApplication.ts +11 -0
- package/src/hooks/applications/useUpdateApplication.test-d.ts +12 -0
- package/src/hooks/applications/useUpdateApplication.ts +11 -0
- package/src/hooks/applications/useUpdateUserApplication.test-d.ts +14 -0
- package/src/hooks/applications/useUpdateUserApplication.ts +11 -0
- package/src/hooks/applications/useUserApplication.test-d.ts +11 -0
- package/src/hooks/applications/useUserApplication.ts +14 -0
- package/src/hooks/applications/useUserApplications.test-d.ts +11 -0
- package/src/hooks/applications/useUserApplications.ts +14 -0
- package/src/hooks/helpers/createFetcherHook.test.tsx +180 -0
- package/src/hooks/helpers/createFetcherHook.ts +69 -0
- package/src/hooks/helpers/createMutationHook.test.tsx +125 -0
- package/src/hooks/helpers/createMutationHook.tsx +93 -0
- package/src/hooks/installations/useInstallation.test-d.ts +19 -0
- package/src/hooks/installations/useInstallation.ts +22 -0
- package/src/hooks/installations/useInstallations.test-d.ts +26 -0
- package/src/hooks/installations/useInstallations.ts +25 -0
- package/src/hooks/organizations/useOrganization.test-d.ts +19 -12
- package/src/hooks/organizations/useOrganization.test.ts +50 -52
- package/src/hooks/organizations/useOrganization.ts +13 -19
- package/src/hooks/organizations/useOrganizations.test-d.ts +26 -13
- package/src/hooks/organizations/useOrganizations.test.ts +49 -71
- package/src/hooks/organizations/useOrganizations.ts +14 -20
- package/src/hooks/projects/useProject.test-d.ts +18 -11
- package/src/hooks/projects/useProject.test.tsx +29 -23
- package/src/hooks/projects/useProject.ts +12 -16
- package/src/hooks/projects/useProjects.test-d.ts +22 -11
- package/src/hooks/projects/useProjects.test.ts +45 -98
- package/src/hooks/projects/useProjects.ts +16 -17
- package/src/hooks/dashboard/useDispatchIntent.test.ts +0 -251
- package/src/hooks/dashboard/useDispatchIntent.ts +0 -157
- package/src/hooks/dashboard/utils/useResourceIdFromDocumentHandle.test.ts +0 -128
- package/src/hooks/dashboard/utils/useResourceIdFromDocumentHandle.ts +0 -42
|
@@ -1,10 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
getProjectState,
|
|
4
|
-
type Project,
|
|
5
|
-
resolveProject,
|
|
6
|
-
type StateSource,
|
|
7
|
-
} from '@sanity/sdk'
|
|
1
|
+
import {createSanityInstance, type Project, project, type StateSource} from '@sanity/sdk'
|
|
2
|
+
import {type FetcherSnapshot} from '@sanity/sdk/_internal'
|
|
8
3
|
import {type ReactNode} from 'react'
|
|
9
4
|
import {type Observable} from 'rxjs'
|
|
10
5
|
import {beforeEach, describe, expect, it, vi} from 'vitest'
|
|
@@ -16,30 +11,41 @@ import {useProject} from './useProject'
|
|
|
16
11
|
|
|
17
12
|
vi.mock('@sanity/sdk', async (importOriginal) => {
|
|
18
13
|
const original = await importOriginal<typeof import('@sanity/sdk')>()
|
|
19
|
-
return {...original,
|
|
14
|
+
return {...original, project: {getState: vi.fn(), resolveState: vi.fn()}}
|
|
20
15
|
})
|
|
21
16
|
|
|
22
|
-
const stateSource = (current: Project | undefined): StateSource<Project
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
const stateSource = (current: Project | undefined): StateSource<FetcherSnapshot<Project>> => {
|
|
18
|
+
// Cache the snapshot: useSyncExternalStore requires a referentially stable current value.
|
|
19
|
+
const snapshot = current
|
|
20
|
+
? {status: 'success', data: current, error: undefined, isFetching: false, dataUpdatedAt: 1}
|
|
21
|
+
: {
|
|
22
|
+
status: 'pending',
|
|
23
|
+
data: undefined,
|
|
24
|
+
error: undefined,
|
|
25
|
+
isFetching: true,
|
|
26
|
+
dataUpdatedAt: undefined,
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
getCurrent: vi.fn(() => snapshot),
|
|
30
|
+
subscribe: vi.fn(() => () => {}),
|
|
26
31
|
get observable(): Observable<unknown> {
|
|
27
32
|
throw new Error('Not implemented')
|
|
28
33
|
},
|
|
29
|
-
}
|
|
34
|
+
} as unknown as StateSource<FetcherSnapshot<Project>>
|
|
35
|
+
}
|
|
30
36
|
|
|
31
37
|
const sanityInstance = expect.objectContaining({config: expect.any(Object)})
|
|
32
38
|
|
|
33
39
|
describe('useProject', () => {
|
|
34
40
|
beforeEach(() => {
|
|
35
41
|
vi.clearAllMocks()
|
|
36
|
-
vi.mocked(
|
|
42
|
+
vi.mocked(project.getState).mockReturnValue(stateSource({id: 'p'} as unknown as Project))
|
|
37
43
|
})
|
|
38
44
|
|
|
39
45
|
it('resolves the projectId from the instance config resource', () => {
|
|
40
46
|
// test-utils wraps with ResourceProvider projectId="test" dataset="test".
|
|
41
47
|
renderHook(() => useProject())
|
|
42
|
-
expect(
|
|
48
|
+
expect(project.getState).toHaveBeenCalledWith(
|
|
43
49
|
sanityInstance,
|
|
44
50
|
expect.objectContaining({projectId: 'test'}),
|
|
45
51
|
)
|
|
@@ -47,7 +53,7 @@ describe('useProject', () => {
|
|
|
47
53
|
|
|
48
54
|
it('lets an explicit projectId override the ambient resource', () => {
|
|
49
55
|
renderHook(() => useProject({projectId: 'explicit-project'}))
|
|
50
|
-
expect(
|
|
56
|
+
expect(project.getState).toHaveBeenCalledWith(
|
|
51
57
|
sanityInstance,
|
|
52
58
|
expect.objectContaining({projectId: 'explicit-project'}),
|
|
53
59
|
)
|
|
@@ -64,7 +70,7 @@ describe('useProject', () => {
|
|
|
64
70
|
</ResourceProvider>
|
|
65
71
|
),
|
|
66
72
|
})
|
|
67
|
-
expect(
|
|
73
|
+
expect(project.getState).toHaveBeenCalledWith(
|
|
68
74
|
sanityInstance,
|
|
69
75
|
expect.objectContaining({projectId: 'resource-project'}),
|
|
70
76
|
)
|
|
@@ -80,7 +86,7 @@ describe('useProject', () => {
|
|
|
80
86
|
})
|
|
81
87
|
// A dataset-less config can't form a DatasetResource; the projectId is carried
|
|
82
88
|
// via ProjectContext and injected so project-scoped reads still resolve it.
|
|
83
|
-
expect(
|
|
89
|
+
expect(project.getState).toHaveBeenCalledWith(
|
|
84
90
|
sanityInstance,
|
|
85
91
|
expect.objectContaining({projectId: 'config-project'}),
|
|
86
92
|
)
|
|
@@ -99,16 +105,16 @@ describe('useProject', () => {
|
|
|
99
105
|
</SanityInstanceContext.Provider>
|
|
100
106
|
),
|
|
101
107
|
})
|
|
102
|
-
expect(
|
|
108
|
+
expect(project.getState).toHaveBeenCalledWith(
|
|
103
109
|
emptyInstance,
|
|
104
110
|
expect.objectContaining({projectId: 'bare-project'}),
|
|
105
111
|
)
|
|
106
112
|
})
|
|
107
113
|
|
|
108
|
-
it('suspends via
|
|
109
|
-
vi.mocked(
|
|
110
|
-
vi.mocked(
|
|
114
|
+
it('suspends via the project fetcher until project data is available', () => {
|
|
115
|
+
vi.mocked(project.getState).mockReturnValue(stateSource(undefined))
|
|
116
|
+
vi.mocked(project.resolveState).mockReturnValue(new Promise(() => {}))
|
|
111
117
|
renderHook(() => useProject())
|
|
112
|
-
expect(
|
|
118
|
+
expect(project.resolveState).toHaveBeenCalled()
|
|
113
119
|
})
|
|
114
120
|
})
|
|
@@ -1,26 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {type Project, project, type ProjectOptions} from '@sanity/sdk'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {createFetcherHook, type FetcherHookResult} from '../helpers/createFetcherHook'
|
|
4
4
|
import {useResolvedProjectId} from '../helpers/useResolvedProjectId'
|
|
5
5
|
|
|
6
|
-
const useProjectBase =
|
|
7
|
-
getState: getProjectState,
|
|
8
|
-
shouldSuspend: (instance, ...params) =>
|
|
9
|
-
getProjectState(instance, ...params).getCurrent() === undefined,
|
|
10
|
-
suspender: resolveProject,
|
|
11
|
-
})
|
|
6
|
+
const useProjectBase = createFetcherHook(project)
|
|
12
7
|
|
|
13
8
|
/**
|
|
14
9
|
* Returns metadata for a given project.
|
|
15
10
|
*
|
|
16
11
|
* @category Projects
|
|
17
12
|
* @param options - Configuration options
|
|
18
|
-
* @returns
|
|
19
|
-
*
|
|
13
|
+
* @returns A {@link FetcherHookResult} whose `data` is the metadata for the
|
|
14
|
+
* project. `members` is included only when `includeMembers: true`; `features`
|
|
15
|
+
* is included unless `includeFeatures: false`.
|
|
20
16
|
* @example
|
|
21
17
|
* ```tsx
|
|
22
18
|
* function ProjectMetadata({projectId}: {projectId: string}) {
|
|
23
|
-
* const project = useProject({projectId})
|
|
19
|
+
* const {data: project} = useProject({projectId})
|
|
24
20
|
*
|
|
25
21
|
* return (
|
|
26
22
|
* <figure style={{backgroundColor: project.metadata.color || 'lavender'}}>
|
|
@@ -31,10 +27,10 @@ const useProjectBase = createStateSourceHook({
|
|
|
31
27
|
* ```
|
|
32
28
|
* @example
|
|
33
29
|
* ```tsx
|
|
34
|
-
* const projectWithMembersAndFeatures = useProject({projectId})
|
|
35
|
-
* const projectWithMembers = useProject({projectId, includeMembers: true})
|
|
36
|
-
* const projectWithoutMembers = useProject({projectId, includeMembers: false})
|
|
37
|
-
* const projectWithoutFeatures = useProject({projectId, includeFeatures: false})
|
|
30
|
+
* const {data: projectWithMembersAndFeatures} = useProject({projectId})
|
|
31
|
+
* const {data: projectWithMembers} = useProject({projectId, includeMembers: true})
|
|
32
|
+
* const {data: projectWithoutMembers} = useProject({projectId, includeMembers: false})
|
|
33
|
+
* const {data: projectWithoutFeatures} = useProject({projectId, includeFeatures: false})
|
|
38
34
|
* ```
|
|
39
35
|
* @remarks
|
|
40
36
|
* The `projectId` is resolved in order from:
|
|
@@ -50,4 +46,4 @@ export const useProject = ((options?: ProjectOptions<boolean, boolean>) => {
|
|
|
50
46
|
return useProjectBase(projectId ? {...options, projectId} : options)
|
|
51
47
|
}) as <IncludeMembers extends boolean = true, IncludeFeatures extends boolean = true>(
|
|
52
48
|
options?: ProjectOptions<IncludeMembers, IncludeFeatures>,
|
|
53
|
-
) => Project<IncludeMembers, IncludeFeatures
|
|
49
|
+
) => FetcherHookResult<Project<IncludeMembers, IncludeFeatures>>
|
|
@@ -4,31 +4,38 @@ import {expectTypeOf, test} from 'vitest'
|
|
|
4
4
|
import {useProjects} from './useProjects'
|
|
5
5
|
|
|
6
6
|
test('useProjects — no args: features included, members omitted', () => {
|
|
7
|
-
expectTypeOf(useProjects()).toEqualTypeOf<Project<false, true>[]>()
|
|
7
|
+
expectTypeOf(useProjects().data).toEqualTypeOf<Project<false, true>[]>()
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
test('useProjects — returns the FetcherHookResult envelope', () => {
|
|
11
|
+
const result = useProjects()
|
|
12
|
+
expectTypeOf(result.isFetching).toEqualTypeOf<boolean>()
|
|
13
|
+
expectTypeOf(result.error).toEqualTypeOf<unknown>()
|
|
14
|
+
expectTypeOf(result.refetch).toEqualTypeOf<() => Promise<Project<false, true>[]>>()
|
|
8
15
|
})
|
|
9
16
|
|
|
10
17
|
test('useProjects — includeMembers: true adds members to the type', () => {
|
|
11
|
-
expectTypeOf(useProjects({includeMembers: true})).toEqualTypeOf<Project<true, true>[]>()
|
|
18
|
+
expectTypeOf(useProjects({includeMembers: true}).data).toEqualTypeOf<Project<true, true>[]>()
|
|
12
19
|
type Result = ReturnType<typeof useProjects<true, true>>
|
|
13
|
-
expectTypeOf<Result[number]['members']>().toEqualTypeOf<ProjectMember[]>()
|
|
20
|
+
expectTypeOf<Result['data'][number]['members']>().toEqualTypeOf<ProjectMember[]>()
|
|
14
21
|
})
|
|
15
22
|
|
|
16
23
|
test('useProjects — includeFeatures: false drops features from the type', () => {
|
|
17
|
-
expectTypeOf(useProjects({includeFeatures: false})).toEqualTypeOf<Project<false, false>[]>()
|
|
24
|
+
expectTypeOf(useProjects({includeFeatures: false}).data).toEqualTypeOf<Project<false, false>[]>()
|
|
18
25
|
})
|
|
19
26
|
|
|
20
27
|
test('useProjects — both flags true → both arrays present', () => {
|
|
21
|
-
expectTypeOf(useProjects({includeMembers: true, includeFeatures: true})).toEqualTypeOf<
|
|
28
|
+
expectTypeOf(useProjects({includeMembers: true, includeFeatures: true}).data).toEqualTypeOf<
|
|
22
29
|
Project<true, true>[]
|
|
23
30
|
>()
|
|
24
31
|
})
|
|
25
32
|
|
|
26
33
|
test('useProjects — both flags false → bare base shape', () => {
|
|
27
|
-
expectTypeOf(useProjects({includeMembers: false, includeFeatures: false})).toEqualTypeOf<
|
|
34
|
+
expectTypeOf(useProjects({includeMembers: false, includeFeatures: false}).data).toEqualTypeOf<
|
|
28
35
|
Project<false, false>[]
|
|
29
36
|
>()
|
|
30
37
|
type Result = ReturnType<typeof useProjects<false, false>>
|
|
31
|
-
expectTypeOf<Result[number]['id']>().toEqualTypeOf<string>()
|
|
38
|
+
expectTypeOf<Result['data'][number]['id']>().toEqualTypeOf<string>()
|
|
32
39
|
})
|
|
33
40
|
|
|
34
41
|
test('useProjects — rejects non-boolean flag values', () => {
|
|
@@ -37,13 +44,17 @@ test('useProjects — rejects non-boolean flag values', () => {
|
|
|
37
44
|
})
|
|
38
45
|
|
|
39
46
|
test('useProjects — organizationId alone does not change the data shape', () => {
|
|
40
|
-
expectTypeOf(useProjects({organizationId: 'org_123'})).toEqualTypeOf<
|
|
47
|
+
expectTypeOf(useProjects({organizationId: 'org_123'}).data).toEqualTypeOf<
|
|
48
|
+
Project<false, true>[]
|
|
49
|
+
>()
|
|
41
50
|
})
|
|
42
51
|
|
|
43
52
|
test('useProjects — non-literal boolean flag makes members optional', () => {
|
|
44
53
|
const includeMembers = false as boolean
|
|
45
|
-
expectTypeOf(useProjects({includeMembers})).toEqualTypeOf<Project<boolean, true>[]>()
|
|
54
|
+
expectTypeOf(useProjects({includeMembers}).data).toEqualTypeOf<Project<boolean, true>[]>()
|
|
46
55
|
type Result = ReturnType<typeof useProjects<boolean, true>>
|
|
47
|
-
expectTypeOf<Result[number]['members']>().toEqualTypeOf<ProjectMember[] | undefined>()
|
|
48
|
-
expectTypeOf<Pick<Result[number], 'members'>>().toEqualTypeOf<{
|
|
56
|
+
expectTypeOf<Result['data'][number]['members']>().toEqualTypeOf<ProjectMember[] | undefined>()
|
|
57
|
+
expectTypeOf<Pick<Result['data'][number], 'members'>>().toEqualTypeOf<{
|
|
58
|
+
members?: ProjectMember[]
|
|
59
|
+
}>()
|
|
49
60
|
})
|
|
@@ -1,112 +1,59 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {type Project, projects, type StateSource} from '@sanity/sdk'
|
|
2
|
+
import {type FetcherSnapshot} from '@sanity/sdk/_internal'
|
|
3
|
+
import {type Observable} from 'rxjs'
|
|
2
4
|
import {beforeEach, describe, expect, it, vi} from 'vitest'
|
|
3
5
|
|
|
4
|
-
import {
|
|
6
|
+
import {renderHook} from '../../../test/test-utils'
|
|
7
|
+
import {useProjects} from './useProjects'
|
|
5
8
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
vi.mock('@sanity/sdk', async (importOriginal) => {
|
|
10
|
+
const original = await importOriginal<typeof import('@sanity/sdk')>()
|
|
11
|
+
return {...original, projects: {getState: vi.fn(), resolveState: vi.fn(), refetch: vi.fn()}}
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
const stateSource = (current: Project[] | undefined): StateSource<FetcherSnapshot<Project[]>> => {
|
|
15
|
+
const snapshot = current
|
|
16
|
+
? {status: 'success', data: current, error: undefined, isFetching: false, dataUpdatedAt: 1}
|
|
17
|
+
: {
|
|
18
|
+
status: 'pending',
|
|
19
|
+
data: undefined,
|
|
20
|
+
error: undefined,
|
|
21
|
+
isFetching: true,
|
|
22
|
+
dataUpdatedAt: undefined,
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
getCurrent: vi.fn(() => snapshot),
|
|
26
|
+
subscribe: vi.fn(() => () => {}),
|
|
27
|
+
get observable(): Observable<unknown> {
|
|
28
|
+
throw new Error('Not implemented')
|
|
29
|
+
},
|
|
30
|
+
} as unknown as StateSource<FetcherSnapshot<Project[]>>
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const sanityInstance = expect.objectContaining({config: expect.any(Object)})
|
|
16
34
|
|
|
17
35
|
describe('useProjects', () => {
|
|
18
|
-
// Use beforeEach to reset modules and ensure mocks are fresh for each test
|
|
19
36
|
beforeEach(() => {
|
|
20
|
-
vi.
|
|
21
|
-
|
|
22
|
-
vi.mock('@sanity/sdk', () => ({
|
|
23
|
-
getProjectsState: vi.fn(() => ({
|
|
24
|
-
getCurrent: vi.fn(() => undefined),
|
|
25
|
-
})),
|
|
26
|
-
resolveProjects: 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('./useProjects')
|
|
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
|
-
// Note: getConfig is not used in useProjects
|
|
45
|
-
}),
|
|
46
|
-
)
|
|
37
|
+
vi.clearAllMocks()
|
|
38
|
+
vi.mocked(projects.getState).mockReturnValue(stateSource([{id: 'a'}] as unknown as Project[]))
|
|
47
39
|
})
|
|
48
40
|
|
|
49
|
-
it('
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
// Get the arguments passed to createStateSourceHook
|
|
54
|
-
const mockCreateStateSourceHook = createStateSourceHook as ReturnType<typeof vi.fn>
|
|
55
|
-
expect(mockCreateStateSourceHook.mock.calls.length).toBeGreaterThan(0)
|
|
56
|
-
const createStateSourceHookArgs = mockCreateStateSourceHook.mock.calls[0][0]
|
|
57
|
-
const shouldSuspend = createStateSourceHookArgs.shouldSuspend
|
|
58
|
-
|
|
59
|
-
// Mock instance for the test call
|
|
60
|
-
const mockInstance = {} as SanityInstance // Use specific type
|
|
61
|
-
|
|
62
|
-
// Call the shouldSuspend function with both required parameters
|
|
63
|
-
const result = shouldSuspend(mockInstance, undefined) // Pass undefined for options
|
|
64
|
-
|
|
65
|
-
// Assert that getProjectsState was called with the correct arguments
|
|
66
|
-
const mockGetProjectsState = getProjectsState as ReturnType<typeof vi.fn>
|
|
67
|
-
expect(mockGetProjectsState).toHaveBeenCalledWith(mockInstance, undefined)
|
|
68
|
-
|
|
69
|
-
// Assert that getCurrent was called on the result of getProjectsState
|
|
70
|
-
expect(mockGetProjectsState.mock.results.length).toBeGreaterThan(0)
|
|
71
|
-
const getProjectsStateMockResult = mockGetProjectsState.mock.results[0].value
|
|
72
|
-
expect(getProjectsStateMockResult.getCurrent).toHaveBeenCalled()
|
|
73
|
-
|
|
74
|
-
// Assert the result of shouldSuspend based on the mocked getCurrent value
|
|
75
|
-
expect(result).toBe(true) // Since getCurrent is mocked to return undefined
|
|
41
|
+
it('reads the projects fetcher with the passed options', () => {
|
|
42
|
+
renderHook(() => useProjects({organizationId: 'org123'}))
|
|
43
|
+
expect(projects.getState).toHaveBeenCalledWith(sanityInstance, {organizationId: 'org123'})
|
|
76
44
|
})
|
|
77
45
|
|
|
78
|
-
it('
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
expect(
|
|
83
|
-
|
|
84
|
-
const createStateSourceHookArgs = mockCreateStateSourceHook.mock.calls[0][0]
|
|
85
|
-
const getState = createStateSourceHookArgs.getState
|
|
86
|
-
|
|
87
|
-
// Test that getState can handle the new options parameter
|
|
88
|
-
const mockInstance = {} as SanityInstance
|
|
89
|
-
const mockOptions = {organizationId: 'org123', includeMembers: false}
|
|
90
|
-
|
|
91
|
-
// This should not throw
|
|
92
|
-
expect(() => getState(mockInstance, mockOptions)).not.toThrow()
|
|
46
|
+
it('returns the fetcher data in the result envelope', () => {
|
|
47
|
+
const {result} = renderHook(() => useProjects())
|
|
48
|
+
expect(result.current.data).toEqual([{id: 'a'}])
|
|
49
|
+
expect(result.current.isFetching).toBe(false)
|
|
50
|
+
expect(typeof result.current.refetch).toBe('function')
|
|
93
51
|
})
|
|
94
52
|
|
|
95
|
-
it('
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const shouldSuspend = createStateSourceHookArgs.shouldSuspend
|
|
101
|
-
|
|
102
|
-
const mockInstance = {} as SanityInstance
|
|
103
|
-
|
|
104
|
-
// Test with different options
|
|
105
|
-
expect(() => shouldSuspend(mockInstance, undefined)).not.toThrow()
|
|
106
|
-
expect(() => shouldSuspend(mockInstance, {organizationId: 'org123'})).not.toThrow()
|
|
107
|
-
expect(() => shouldSuspend(mockInstance, {includeMembers: false})).not.toThrow()
|
|
108
|
-
expect(() =>
|
|
109
|
-
shouldSuspend(mockInstance, {organizationId: 'org123', includeMembers: false}),
|
|
110
|
-
).not.toThrow()
|
|
53
|
+
it('suspends via the projects fetcher until data is available', () => {
|
|
54
|
+
vi.mocked(projects.getState).mockReturnValue(stateSource(undefined))
|
|
55
|
+
vi.mocked(projects.resolveState).mockReturnValue(new Promise(() => {}))
|
|
56
|
+
renderHook(() => useProjects())
|
|
57
|
+
expect(projects.resolveState).toHaveBeenCalled()
|
|
111
58
|
})
|
|
112
59
|
})
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {type Project, projects, type ProjectsOptions} from '@sanity/sdk'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {createFetcherHook, type FetcherHookResult} from '../helpers/createFetcherHook'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @public
|
|
@@ -15,11 +15,12 @@ export type ProjectWithoutMembers = Project
|
|
|
15
15
|
*
|
|
16
16
|
* @category Projects
|
|
17
17
|
* @param options - Configuration options
|
|
18
|
-
* @returns
|
|
19
|
-
*
|
|
18
|
+
* @returns A {@link FetcherHookResult} whose `data` is an array of project
|
|
19
|
+
* metadata. `members` is included only when `includeMembers: true`; `features`
|
|
20
|
+
* is included unless `includeFeatures: false`.
|
|
20
21
|
* @example
|
|
21
22
|
* ```tsx
|
|
22
|
-
* const projects = useProjects()
|
|
23
|
+
* const {data: projects} = useProjects()
|
|
23
24
|
*
|
|
24
25
|
* return (
|
|
25
26
|
* <select>
|
|
@@ -31,20 +32,18 @@ export type ProjectWithoutMembers = Project
|
|
|
31
32
|
* ```
|
|
32
33
|
* @example
|
|
33
34
|
* ```tsx
|
|
34
|
-
* const projects = useProjects()
|
|
35
|
-
* const projectsWithFeatures = useProjects()
|
|
36
|
-
* const projectsWithMembers = useProjects({includeMembers: true})
|
|
37
|
-
* const projectsWithoutMembers = useProjects({includeMembers: false})
|
|
38
|
-
* const projectsWithoutFeatures = useProjects({includeFeatures: false})
|
|
35
|
+
* const {data: projects} = useProjects()
|
|
36
|
+
* const {data: projectsWithFeatures} = useProjects()
|
|
37
|
+
* const {data: projectsWithMembers} = useProjects({includeMembers: true})
|
|
38
|
+
* const {data: projectsWithoutMembers} = useProjects({includeMembers: false})
|
|
39
|
+
* const {data: projectsWithoutFeatures} = useProjects({includeFeatures: false})
|
|
39
40
|
* ```
|
|
40
41
|
* @public
|
|
41
42
|
* @function
|
|
42
43
|
*/
|
|
43
|
-
export const useProjects =
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
suspender: resolveProjects,
|
|
48
|
-
}) as <IncludeMembers extends boolean = false, IncludeFeatures extends boolean = true>(
|
|
44
|
+
export const useProjects = createFetcherHook(projects) as <
|
|
45
|
+
IncludeMembers extends boolean = false,
|
|
46
|
+
IncludeFeatures extends boolean = true,
|
|
47
|
+
>(
|
|
49
48
|
options?: ProjectsOptions<IncludeMembers, IncludeFeatures>,
|
|
50
|
-
) => Project<IncludeMembers, IncludeFeatures>[]
|
|
49
|
+
) => FetcherHookResult<Project<IncludeMembers, IncludeFeatures>[]>
|