@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
|
@@ -4,37 +4,44 @@ import {expectTypeOf, test} from 'vitest'
|
|
|
4
4
|
import {useOrganization} from './useOrganization'
|
|
5
5
|
|
|
6
6
|
test('useOrganization — no flags: members and features both omitted', () => {
|
|
7
|
-
expectTypeOf(useOrganization({organizationId: 'org_1'})).toEqualTypeOf<
|
|
7
|
+
expectTypeOf(useOrganization({organizationId: 'org_1'}).data).toEqualTypeOf<
|
|
8
8
|
Organization<false, false>
|
|
9
9
|
>()
|
|
10
10
|
})
|
|
11
11
|
|
|
12
|
+
test('useOrganization — returns the FetcherHookResult envelope', () => {
|
|
13
|
+
const result = useOrganization({organizationId: 'org_1'})
|
|
14
|
+
expectTypeOf(result.isFetching).toEqualTypeOf<boolean>()
|
|
15
|
+
expectTypeOf(result.error).toEqualTypeOf<unknown>()
|
|
16
|
+
expectTypeOf(result.refetch).toEqualTypeOf<() => Promise<Organization<false, false>>>()
|
|
17
|
+
})
|
|
18
|
+
|
|
12
19
|
test('useOrganization — includeMembers: true adds members to the type', () => {
|
|
13
|
-
expectTypeOf(useOrganization({organizationId: 'org_1', includeMembers: true})).toEqualTypeOf<
|
|
20
|
+
expectTypeOf(useOrganization({organizationId: 'org_1', includeMembers: true}).data).toEqualTypeOf<
|
|
14
21
|
Organization<true, false>
|
|
15
22
|
>()
|
|
16
23
|
type Result = ReturnType<typeof useOrganization<true, false>>
|
|
17
|
-
expectTypeOf<Result['members']>().toEqualTypeOf<OrganizationMember[]>()
|
|
24
|
+
expectTypeOf<Result['data']['members']>().toEqualTypeOf<OrganizationMember[]>()
|
|
18
25
|
})
|
|
19
26
|
|
|
20
27
|
test('useOrganization — includeFeatures: true adds features to the type', () => {
|
|
21
|
-
expectTypeOf(
|
|
22
|
-
|
|
23
|
-
|
|
28
|
+
expectTypeOf(
|
|
29
|
+
useOrganization({organizationId: 'org_1', includeFeatures: true}).data,
|
|
30
|
+
).toEqualTypeOf<Organization<false, true>>()
|
|
24
31
|
})
|
|
25
32
|
|
|
26
33
|
test('useOrganization — both flags true → both arrays present', () => {
|
|
27
34
|
expectTypeOf(
|
|
28
|
-
useOrganization({organizationId: 'org_1', includeMembers: true, includeFeatures: true}),
|
|
35
|
+
useOrganization({organizationId: 'org_1', includeMembers: true, includeFeatures: true}).data,
|
|
29
36
|
).toEqualTypeOf<Organization<true, true>>()
|
|
30
37
|
})
|
|
31
38
|
|
|
32
39
|
test('useOrganization — both flags false → bare base shape', () => {
|
|
33
40
|
expectTypeOf(
|
|
34
|
-
useOrganization({organizationId: 'org_1', includeMembers: false, includeFeatures: false}),
|
|
41
|
+
useOrganization({organizationId: 'org_1', includeMembers: false, includeFeatures: false}).data,
|
|
35
42
|
).toEqualTypeOf<Organization<false, false>>()
|
|
36
43
|
type Result = ReturnType<typeof useOrganization<false, false>>
|
|
37
|
-
expectTypeOf<Result['id']>().toEqualTypeOf<string>()
|
|
44
|
+
expectTypeOf<Result['data']['id']>().toEqualTypeOf<string>()
|
|
38
45
|
})
|
|
39
46
|
|
|
40
47
|
test('useOrganization — rejects non-boolean flag values', () => {
|
|
@@ -44,10 +51,10 @@ test('useOrganization — rejects non-boolean flag values', () => {
|
|
|
44
51
|
|
|
45
52
|
test('useOrganization — non-literal boolean flag makes members optional', () => {
|
|
46
53
|
const includeMembers = false as boolean
|
|
47
|
-
expectTypeOf(useOrganization({organizationId: 'org_1', includeMembers})).toEqualTypeOf<
|
|
54
|
+
expectTypeOf(useOrganization({organizationId: 'org_1', includeMembers}).data).toEqualTypeOf<
|
|
48
55
|
Organization<boolean, false>
|
|
49
56
|
>()
|
|
50
57
|
type Result = ReturnType<typeof useOrganization<boolean, false>>
|
|
51
|
-
expectTypeOf<Result['members']>().toEqualTypeOf<OrganizationMember[] | undefined>()
|
|
52
|
-
expectTypeOf<Pick<Result, 'members'>>().toEqualTypeOf<{members?: OrganizationMember[]}>()
|
|
58
|
+
expectTypeOf<Result['data']['members']>().toEqualTypeOf<OrganizationMember[] | undefined>()
|
|
59
|
+
expectTypeOf<Pick<Result['data'], 'members'>>().toEqualTypeOf<{members?: OrganizationMember[]}>()
|
|
53
60
|
})
|
|
@@ -1,65 +1,63 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {type Organization, organization, 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 {useOrganization} from './useOrganization'
|
|
5
8
|
|
|
6
|
-
vi.mock('@sanity/sdk', () =>
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
vi.mock('@sanity/sdk', async (importOriginal) => {
|
|
10
|
+
const original = await importOriginal<typeof import('@sanity/sdk')>()
|
|
11
|
+
return {...original, organization: {getState: vi.fn(), resolveState: vi.fn(), refetch: vi.fn()}}
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
const stateSource = (
|
|
15
|
+
current: Organization | undefined,
|
|
16
|
+
): StateSource<FetcherSnapshot<Organization>> => {
|
|
17
|
+
const snapshot = current
|
|
18
|
+
? {status: 'success', data: current, error: undefined, isFetching: false, dataUpdatedAt: 1}
|
|
19
|
+
: {
|
|
20
|
+
status: 'pending',
|
|
21
|
+
data: undefined,
|
|
22
|
+
error: undefined,
|
|
23
|
+
isFetching: true,
|
|
24
|
+
dataUpdatedAt: undefined,
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
getCurrent: vi.fn(() => snapshot),
|
|
28
|
+
subscribe: vi.fn(() => () => {}),
|
|
29
|
+
get observable(): Observable<unknown> {
|
|
30
|
+
throw new Error('Not implemented')
|
|
31
|
+
},
|
|
32
|
+
} as unknown as StateSource<FetcherSnapshot<Organization>>
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const sanityInstance = expect.objectContaining({config: expect.any(Object)})
|
|
15
36
|
|
|
16
37
|
describe('useOrganization', () => {
|
|
17
38
|
beforeEach(() => {
|
|
18
|
-
vi.
|
|
19
|
-
vi.
|
|
20
|
-
|
|
21
|
-
getCurrent: vi.fn(() => undefined),
|
|
22
|
-
})),
|
|
23
|
-
resolveOrganization: vi.fn(),
|
|
24
|
-
}))
|
|
25
|
-
vi.mock('../helpers/createStateSourceHook', () => ({
|
|
26
|
-
createStateSourceHook: vi.fn(),
|
|
27
|
-
}))
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
it('should call createStateSourceHook with correct arguments on import', async () => {
|
|
31
|
-
await import('./useOrganization')
|
|
32
|
-
|
|
33
|
-
expect(createStateSourceHook).toHaveBeenCalled()
|
|
34
|
-
expect(createStateSourceHook).toHaveBeenCalledWith(
|
|
35
|
-
expect.objectContaining({
|
|
36
|
-
getState: expect.any(Function),
|
|
37
|
-
shouldSuspend: expect.any(Function),
|
|
38
|
-
suspender: expect.any(Function),
|
|
39
|
-
}),
|
|
39
|
+
vi.clearAllMocks()
|
|
40
|
+
vi.mocked(organization.getState).mockReturnValue(
|
|
41
|
+
stateSource({id: 'org_1'} as unknown as Organization),
|
|
40
42
|
)
|
|
41
43
|
})
|
|
42
44
|
|
|
43
|
-
it('
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
expect(mockCreateStateSourceHook.mock.calls.length).toBeGreaterThan(0)
|
|
48
|
-
const createStateSourceHookArgs = mockCreateStateSourceHook.mock.calls[0][0]
|
|
49
|
-
const shouldSuspend = createStateSourceHookArgs.shouldSuspend
|
|
50
|
-
|
|
51
|
-
const mockInstance = {} as SanityInstance
|
|
52
|
-
const mockOptions: OrganizationOptions = {organizationId: 'org_1'}
|
|
53
|
-
|
|
54
|
-
const result = shouldSuspend(mockInstance, mockOptions)
|
|
55
|
-
|
|
56
|
-
const mockGetOrganizationState = getOrganizationState as ReturnType<typeof vi.fn>
|
|
57
|
-
expect(mockGetOrganizationState).toHaveBeenCalledWith(mockInstance, mockOptions)
|
|
45
|
+
it('reads the organization fetcher with the passed options', () => {
|
|
46
|
+
renderHook(() => useOrganization({organizationId: 'org_1'}))
|
|
47
|
+
expect(organization.getState).toHaveBeenCalledWith(sanityInstance, {organizationId: 'org_1'})
|
|
48
|
+
})
|
|
58
49
|
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
expect(
|
|
50
|
+
it('returns the fetcher data in the result envelope', () => {
|
|
51
|
+
const {result} = renderHook(() => useOrganization({organizationId: 'org_1'}))
|
|
52
|
+
expect(result.current.data).toEqual({id: 'org_1'})
|
|
53
|
+
expect(result.current.isFetching).toBe(false)
|
|
54
|
+
expect(typeof result.current.refetch).toBe('function')
|
|
55
|
+
})
|
|
62
56
|
|
|
63
|
-
|
|
57
|
+
it('suspends via the organization fetcher until data is available', () => {
|
|
58
|
+
vi.mocked(organization.getState).mockReturnValue(stateSource(undefined))
|
|
59
|
+
vi.mocked(organization.resolveState).mockReturnValue(new Promise(() => {}))
|
|
60
|
+
renderHook(() => useOrganization({organizationId: 'org_1'}))
|
|
61
|
+
expect(organization.resolveState).toHaveBeenCalled()
|
|
64
62
|
})
|
|
65
63
|
})
|
|
@@ -1,40 +1,34 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getOrganizationState,
|
|
3
|
-
type Organization,
|
|
4
|
-
type OrganizationOptions,
|
|
5
|
-
resolveOrganization,
|
|
6
|
-
} from '@sanity/sdk'
|
|
1
|
+
import {type Organization, organization, type OrganizationOptions} from '@sanity/sdk'
|
|
7
2
|
|
|
8
|
-
import {
|
|
3
|
+
import {createFetcherHook, type FetcherHookResult} from '../helpers/createFetcherHook'
|
|
9
4
|
|
|
10
5
|
/**
|
|
11
6
|
* Returns metadata for a given organisation.
|
|
12
7
|
*
|
|
13
8
|
* @category Organizations
|
|
14
9
|
* @param options - Configuration options
|
|
15
|
-
* @returns
|
|
16
|
-
*
|
|
10
|
+
* @returns A {@link FetcherHookResult} whose `data` is the metadata for the
|
|
11
|
+
* organisation. `members` is included only when `includeMembers: true`;
|
|
12
|
+
* `features` is included only when `includeFeatures: true`.
|
|
17
13
|
* @example
|
|
18
14
|
* ```tsx
|
|
19
15
|
* function OrganizationName({organizationId}: {organizationId: string}) {
|
|
20
|
-
* const organization = useOrganization({organizationId})
|
|
16
|
+
* const {data: organization} = useOrganization({organizationId})
|
|
21
17
|
*
|
|
22
18
|
* return <h1>{organization.name}</h1>
|
|
23
19
|
* }
|
|
24
20
|
* ```
|
|
25
21
|
* @example
|
|
26
22
|
* ```tsx
|
|
27
|
-
* const organizationWithMembers = useOrganization({organizationId, includeMembers: true})
|
|
28
|
-
* const organizationWithFeatures = useOrganization({organizationId, includeFeatures: true})
|
|
23
|
+
* const {data: organizationWithMembers} = useOrganization({organizationId, includeMembers: true})
|
|
24
|
+
* const {data: organizationWithFeatures} = useOrganization({organizationId, includeFeatures: true})
|
|
29
25
|
* ```
|
|
30
26
|
* @public
|
|
31
27
|
* @function
|
|
32
28
|
*/
|
|
33
|
-
export const useOrganization =
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
suspender: resolveOrganization,
|
|
38
|
-
}) as <IncludeMembers extends boolean = false, IncludeFeatures extends boolean = false>(
|
|
29
|
+
export const useOrganization = createFetcherHook(organization) as <
|
|
30
|
+
IncludeMembers extends boolean = false,
|
|
31
|
+
IncludeFeatures extends boolean = false,
|
|
32
|
+
>(
|
|
39
33
|
options: OrganizationOptions<IncludeMembers, IncludeFeatures>,
|
|
40
|
-
) => Organization<IncludeMembers, IncludeFeatures
|
|
34
|
+
) => FetcherHookResult<Organization<IncludeMembers, IncludeFeatures>>
|
|
@@ -4,33 +4,42 @@ import {expectTypeOf, test} from 'vitest'
|
|
|
4
4
|
import {useOrganizations} from './useOrganizations'
|
|
5
5
|
|
|
6
6
|
test('useOrganizations — no args: members and features both omitted', () => {
|
|
7
|
-
expectTypeOf(useOrganizations()).toEqualTypeOf<Organizations<false, false>>()
|
|
7
|
+
expectTypeOf(useOrganizations().data).toEqualTypeOf<Organizations<false, false>>()
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
test('useOrganizations — returns the FetcherHookResult envelope', () => {
|
|
11
|
+
const result = useOrganizations()
|
|
12
|
+
expectTypeOf(result.isFetching).toEqualTypeOf<boolean>()
|
|
13
|
+
expectTypeOf(result.error).toEqualTypeOf<unknown>()
|
|
14
|
+
expectTypeOf(result.refetch).toEqualTypeOf<() => Promise<Organizations<false, false>>>()
|
|
8
15
|
})
|
|
9
16
|
|
|
10
17
|
test('useOrganizations — includeMembers: true adds members to the type', () => {
|
|
11
|
-
expectTypeOf(useOrganizations({includeMembers: true})).toEqualTypeOf<
|
|
18
|
+
expectTypeOf(useOrganizations({includeMembers: true}).data).toEqualTypeOf<
|
|
19
|
+
Organizations<true, false>
|
|
20
|
+
>()
|
|
12
21
|
type Result = ReturnType<typeof useOrganizations<true, false>>
|
|
13
|
-
expectTypeOf<Result[number]['members']>().toEqualTypeOf<OrganizationMember[]>()
|
|
22
|
+
expectTypeOf<Result['data'][number]['members']>().toEqualTypeOf<OrganizationMember[]>()
|
|
14
23
|
})
|
|
15
24
|
|
|
16
25
|
test('useOrganizations — includeFeatures: true adds features to the type', () => {
|
|
17
|
-
expectTypeOf(useOrganizations({includeFeatures: true})).toEqualTypeOf<
|
|
26
|
+
expectTypeOf(useOrganizations({includeFeatures: true}).data).toEqualTypeOf<
|
|
18
27
|
Organizations<false, true>
|
|
19
28
|
>()
|
|
20
29
|
})
|
|
21
30
|
|
|
22
31
|
test('useOrganizations — both flags true → both arrays present', () => {
|
|
23
|
-
expectTypeOf(useOrganizations({includeMembers: true, includeFeatures: true})).toEqualTypeOf<
|
|
32
|
+
expectTypeOf(useOrganizations({includeMembers: true, includeFeatures: true}).data).toEqualTypeOf<
|
|
24
33
|
Organizations<true, true>
|
|
25
34
|
>()
|
|
26
35
|
})
|
|
27
36
|
|
|
28
37
|
test('useOrganizations — both flags false → bare base shape', () => {
|
|
29
|
-
expectTypeOf(
|
|
30
|
-
|
|
31
|
-
|
|
38
|
+
expectTypeOf(
|
|
39
|
+
useOrganizations({includeMembers: false, includeFeatures: false}).data,
|
|
40
|
+
).toEqualTypeOf<Organizations<false, false>>()
|
|
32
41
|
type Result = ReturnType<typeof useOrganizations<false, false>>
|
|
33
|
-
expectTypeOf<Result[number]['id']>().toEqualTypeOf<string>()
|
|
42
|
+
expectTypeOf<Result['data'][number]['id']>().toEqualTypeOf<string>()
|
|
34
43
|
})
|
|
35
44
|
|
|
36
45
|
test('useOrganizations — rejects non-boolean flag values', () => {
|
|
@@ -39,17 +48,21 @@ test('useOrganizations — rejects non-boolean flag values', () => {
|
|
|
39
48
|
})
|
|
40
49
|
|
|
41
50
|
test('useOrganizations — includeImplicitMemberships does not change the data shape', () => {
|
|
42
|
-
expectTypeOf(useOrganizations({includeImplicitMemberships: true})).toEqualTypeOf<
|
|
51
|
+
expectTypeOf(useOrganizations({includeImplicitMemberships: true}).data).toEqualTypeOf<
|
|
43
52
|
Organizations<false, false>
|
|
44
53
|
>()
|
|
45
54
|
})
|
|
46
55
|
|
|
47
56
|
test('useOrganizations — non-literal boolean flag makes members optional', () => {
|
|
48
57
|
const includeMembers = false as boolean
|
|
49
|
-
expectTypeOf(useOrganizations({includeMembers})).toEqualTypeOf<
|
|
58
|
+
expectTypeOf(useOrganizations({includeMembers}).data).toEqualTypeOf<
|
|
59
|
+
Organizations<boolean, false>
|
|
60
|
+
>()
|
|
50
61
|
type Result = ReturnType<typeof useOrganizations<boolean, false>>
|
|
51
|
-
expectTypeOf<Result[number]['members']>().toEqualTypeOf<
|
|
52
|
-
|
|
62
|
+
expectTypeOf<Result['data'][number]['members']>().toEqualTypeOf<
|
|
63
|
+
OrganizationMember[] | undefined
|
|
64
|
+
>()
|
|
65
|
+
expectTypeOf<Pick<Result['data'][number], 'members'>>().toEqualTypeOf<{
|
|
53
66
|
members?: OrganizationMember[]
|
|
54
67
|
}>()
|
|
55
68
|
})
|
|
@@ -1,85 +1,63 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {type Organizations, organizations, 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 {useOrganizations} from './useOrganizations'
|
|
5
8
|
|
|
6
|
-
vi.mock('@sanity/sdk', () =>
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
vi.mock('@sanity/sdk', async (importOriginal) => {
|
|
10
|
+
const original = await importOriginal<typeof import('@sanity/sdk')>()
|
|
11
|
+
return {...original, organizations: {getState: vi.fn(), resolveState: vi.fn(), refetch: vi.fn()}}
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
const stateSource = (
|
|
15
|
+
current: Organizations | undefined,
|
|
16
|
+
): StateSource<FetcherSnapshot<Organizations>> => {
|
|
17
|
+
const snapshot = current
|
|
18
|
+
? {status: 'success', data: current, error: undefined, isFetching: false, dataUpdatedAt: 1}
|
|
19
|
+
: {
|
|
20
|
+
status: 'pending',
|
|
21
|
+
data: undefined,
|
|
22
|
+
error: undefined,
|
|
23
|
+
isFetching: true,
|
|
24
|
+
dataUpdatedAt: undefined,
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
getCurrent: vi.fn(() => snapshot),
|
|
28
|
+
subscribe: vi.fn(() => () => {}),
|
|
29
|
+
get observable(): Observable<unknown> {
|
|
30
|
+
throw new Error('Not implemented')
|
|
31
|
+
},
|
|
32
|
+
} as unknown as StateSource<FetcherSnapshot<Organizations>>
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const sanityInstance = expect.objectContaining({config: expect.any(Object)})
|
|
15
36
|
|
|
16
37
|
describe('useOrganizations', () => {
|
|
17
38
|
beforeEach(() => {
|
|
18
|
-
vi.
|
|
19
|
-
vi.
|
|
20
|
-
|
|
21
|
-
getCurrent: vi.fn(() => undefined),
|
|
22
|
-
})),
|
|
23
|
-
resolveOrganizations: vi.fn(),
|
|
24
|
-
}))
|
|
25
|
-
vi.mock('../helpers/createStateSourceHook', () => ({
|
|
26
|
-
createStateSourceHook: vi.fn(),
|
|
27
|
-
}))
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
it('should call createStateSourceHook with correct arguments on import', async () => {
|
|
31
|
-
await import('./useOrganizations')
|
|
32
|
-
|
|
33
|
-
expect(createStateSourceHook).toHaveBeenCalled()
|
|
34
|
-
expect(createStateSourceHook).toHaveBeenCalledWith(
|
|
35
|
-
expect.objectContaining({
|
|
36
|
-
getState: expect.any(Function),
|
|
37
|
-
shouldSuspend: expect.any(Function),
|
|
38
|
-
suspender: expect.any(Function),
|
|
39
|
-
}),
|
|
39
|
+
vi.clearAllMocks()
|
|
40
|
+
vi.mocked(organizations.getState).mockReturnValue(
|
|
41
|
+
stateSource([{id: 'org_1'}] as unknown as Organizations),
|
|
40
42
|
)
|
|
41
43
|
})
|
|
42
44
|
|
|
43
|
-
it('
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const mockCreateStateSourceHook = createStateSourceHook as ReturnType<typeof vi.fn>
|
|
47
|
-
expect(mockCreateStateSourceHook.mock.calls.length).toBeGreaterThan(0)
|
|
48
|
-
const createStateSourceHookArgs = mockCreateStateSourceHook.mock.calls[0][0]
|
|
49
|
-
const shouldSuspend = createStateSourceHookArgs.shouldSuspend
|
|
50
|
-
|
|
51
|
-
const mockInstance = {} as SanityInstance
|
|
52
|
-
|
|
53
|
-
const result = shouldSuspend(mockInstance, undefined)
|
|
54
|
-
|
|
55
|
-
const mockGetOrganizationsState = getOrganizationsState as ReturnType<typeof vi.fn>
|
|
56
|
-
expect(mockGetOrganizationsState).toHaveBeenCalledWith(mockInstance, undefined)
|
|
57
|
-
|
|
58
|
-
expect(mockGetOrganizationsState.mock.results.length).toBeGreaterThan(0)
|
|
59
|
-
const getOrganizationsStateMockResult = mockGetOrganizationsState.mock.results[0].value
|
|
60
|
-
expect(getOrganizationsStateMockResult.getCurrent).toHaveBeenCalled()
|
|
61
|
-
|
|
62
|
-
expect(result).toBe(true)
|
|
45
|
+
it('reads the organizations fetcher with the passed options', () => {
|
|
46
|
+
renderHook(() => useOrganizations({includeMembers: true}))
|
|
47
|
+
expect(organizations.getState).toHaveBeenCalledWith(sanityInstance, {includeMembers: true})
|
|
63
48
|
})
|
|
64
49
|
|
|
65
|
-
it('
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const mockInstance = {} as SanityInstance
|
|
50
|
+
it('returns the fetcher data in the result envelope', () => {
|
|
51
|
+
const {result} = renderHook(() => useOrganizations())
|
|
52
|
+
expect(result.current.data).toEqual([{id: 'org_1'}])
|
|
53
|
+
expect(result.current.isFetching).toBe(false)
|
|
54
|
+
expect(typeof result.current.refetch).toBe('function')
|
|
55
|
+
})
|
|
73
56
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
includeMembers: true,
|
|
80
|
-
includeFeatures: true,
|
|
81
|
-
includeImplicitMemberships: true,
|
|
82
|
-
}),
|
|
83
|
-
).not.toThrow()
|
|
57
|
+
it('suspends via the organizations fetcher until data is available', () => {
|
|
58
|
+
vi.mocked(organizations.getState).mockReturnValue(stateSource(undefined))
|
|
59
|
+
vi.mocked(organizations.resolveState).mockReturnValue(new Promise(() => {}))
|
|
60
|
+
renderHook(() => useOrganizations())
|
|
61
|
+
expect(organizations.resolveState).toHaveBeenCalled()
|
|
84
62
|
})
|
|
85
63
|
})
|
|
@@ -1,22 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getOrganizationsState,
|
|
3
|
-
type Organizations,
|
|
4
|
-
type OrganizationsOptions,
|
|
5
|
-
resolveOrganizations,
|
|
6
|
-
} from '@sanity/sdk'
|
|
1
|
+
import {type Organizations, organizations, type OrganizationsOptions} from '@sanity/sdk'
|
|
7
2
|
|
|
8
|
-
import {
|
|
3
|
+
import {createFetcherHook, type FetcherHookResult} from '../helpers/createFetcherHook'
|
|
9
4
|
|
|
10
5
|
/**
|
|
11
6
|
* Returns metadata for each organisation the current user has access to.
|
|
12
7
|
*
|
|
13
8
|
* @category Organizations
|
|
14
9
|
* @param options - Configuration options
|
|
15
|
-
* @returns
|
|
16
|
-
*
|
|
10
|
+
* @returns A {@link FetcherHookResult} whose `data` is an array of organisation
|
|
11
|
+
* metadata. `members` is included only when `includeMembers: true`; `features`
|
|
12
|
+
* is included only when `includeFeatures: true`.
|
|
17
13
|
* @example
|
|
18
14
|
* ```tsx
|
|
19
|
-
* const organizations = useOrganizations()
|
|
15
|
+
* const {data: organizations} = useOrganizations()
|
|
20
16
|
*
|
|
21
17
|
* return (
|
|
22
18
|
* <select>
|
|
@@ -28,18 +24,16 @@ import {createStateSourceHook} from '../helpers/createStateSourceHook'
|
|
|
28
24
|
* ```
|
|
29
25
|
* @example
|
|
30
26
|
* ```tsx
|
|
31
|
-
* const organizationsWithMembers = useOrganizations({includeMembers: true})
|
|
32
|
-
* const organizationsWithFeatures = useOrganizations({includeFeatures: true})
|
|
33
|
-
* const organizationsIncludingImplicit = useOrganizations({includeImplicitMemberships: true})
|
|
27
|
+
* const {data: organizationsWithMembers} = useOrganizations({includeMembers: true})
|
|
28
|
+
* const {data: organizationsWithFeatures} = useOrganizations({includeFeatures: true})
|
|
29
|
+
* const {data: organizationsIncludingImplicit} = useOrganizations({includeImplicitMemberships: true})
|
|
34
30
|
* ```
|
|
35
31
|
* @public
|
|
36
32
|
* @function
|
|
37
33
|
*/
|
|
38
|
-
export const useOrganizations =
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
suspender: resolveOrganizations,
|
|
43
|
-
}) as <IncludeMembers extends boolean = false, IncludeFeatures extends boolean = false>(
|
|
34
|
+
export const useOrganizations = createFetcherHook(organizations) as <
|
|
35
|
+
IncludeMembers extends boolean = false,
|
|
36
|
+
IncludeFeatures extends boolean = false,
|
|
37
|
+
>(
|
|
44
38
|
options?: OrganizationsOptions<IncludeMembers, IncludeFeatures>,
|
|
45
|
-
) => Organizations<IncludeMembers, IncludeFeatures
|
|
39
|
+
) => FetcherHookResult<Organizations<IncludeMembers, IncludeFeatures>>
|
|
@@ -4,31 +4,38 @@ import {expectTypeOf, test} from 'vitest'
|
|
|
4
4
|
import {useProject} from './useProject'
|
|
5
5
|
|
|
6
6
|
test('useProject — no args: members and features both included by default', () => {
|
|
7
|
-
expectTypeOf(useProject()).toEqualTypeOf<Project<true, true>>()
|
|
7
|
+
expectTypeOf(useProject().data).toEqualTypeOf<Project<true, true>>()
|
|
8
8
|
type Result = ReturnType<typeof useProject<true, true>>
|
|
9
|
-
expectTypeOf<Result['members']>().toEqualTypeOf<ProjectMember[]>()
|
|
9
|
+
expectTypeOf<Result['data']['members']>().toEqualTypeOf<ProjectMember[]>()
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
test('useProject — returns the FetcherHookResult envelope', () => {
|
|
13
|
+
const result = useProject()
|
|
14
|
+
expectTypeOf(result.isFetching).toEqualTypeOf<boolean>()
|
|
15
|
+
expectTypeOf(result.error).toEqualTypeOf<unknown>()
|
|
16
|
+
expectTypeOf(result.refetch).toEqualTypeOf<() => Promise<Project<true, true>>>()
|
|
10
17
|
})
|
|
11
18
|
|
|
12
19
|
test('useProject — includeMembers: false drops members from the type', () => {
|
|
13
|
-
expectTypeOf(useProject({includeMembers: false})).toEqualTypeOf<Project<false, true>>()
|
|
20
|
+
expectTypeOf(useProject({includeMembers: false}).data).toEqualTypeOf<Project<false, true>>()
|
|
14
21
|
})
|
|
15
22
|
|
|
16
23
|
test('useProject — includeFeatures: false drops features from the type', () => {
|
|
17
|
-
expectTypeOf(useProject({includeFeatures: false})).toEqualTypeOf<Project<true, false>>()
|
|
24
|
+
expectTypeOf(useProject({includeFeatures: false}).data).toEqualTypeOf<Project<true, false>>()
|
|
18
25
|
})
|
|
19
26
|
|
|
20
27
|
test('useProject — both flags true → both arrays present', () => {
|
|
21
|
-
expectTypeOf(useProject({includeMembers: true, includeFeatures: true})).toEqualTypeOf<
|
|
28
|
+
expectTypeOf(useProject({includeMembers: true, includeFeatures: true}).data).toEqualTypeOf<
|
|
22
29
|
Project<true, true>
|
|
23
30
|
>()
|
|
24
31
|
})
|
|
25
32
|
|
|
26
33
|
test('useProject — both flags false → bare base shape', () => {
|
|
27
|
-
expectTypeOf(useProject({includeMembers: false, includeFeatures: false})).toEqualTypeOf<
|
|
34
|
+
expectTypeOf(useProject({includeMembers: false, includeFeatures: false}).data).toEqualTypeOf<
|
|
28
35
|
Project<false, false>
|
|
29
36
|
>()
|
|
30
37
|
type Result = ReturnType<typeof useProject<false, false>>
|
|
31
|
-
expectTypeOf<Result['id']>().toEqualTypeOf<string>()
|
|
38
|
+
expectTypeOf<Result['data']['id']>().toEqualTypeOf<string>()
|
|
32
39
|
})
|
|
33
40
|
|
|
34
41
|
test('useProject — rejects non-boolean flag values', () => {
|
|
@@ -37,13 +44,13 @@ test('useProject — rejects non-boolean flag values', () => {
|
|
|
37
44
|
})
|
|
38
45
|
|
|
39
46
|
test('useProject — projectId alone does not change the data shape', () => {
|
|
40
|
-
expectTypeOf(useProject({projectId: 'p'})).toEqualTypeOf<Project<true, true>>()
|
|
47
|
+
expectTypeOf(useProject({projectId: 'p'}).data).toEqualTypeOf<Project<true, true>>()
|
|
41
48
|
})
|
|
42
49
|
|
|
43
50
|
test('useProject — non-literal boolean flag makes members optional', () => {
|
|
44
51
|
const includeMembers = false as boolean
|
|
45
|
-
expectTypeOf(useProject({includeMembers})).toEqualTypeOf<Project<boolean, true>>()
|
|
52
|
+
expectTypeOf(useProject({includeMembers}).data).toEqualTypeOf<Project<boolean, true>>()
|
|
46
53
|
type Result = ReturnType<typeof useProject<boolean, true>>
|
|
47
|
-
expectTypeOf<Result['members']>().toEqualTypeOf<ProjectMember[] | undefined>()
|
|
48
|
-
expectTypeOf<Pick<Result, 'members'>>().toEqualTypeOf<{members?: ProjectMember[]}>()
|
|
54
|
+
expectTypeOf<Result['data']['members']>().toEqualTypeOf<ProjectMember[] | undefined>()
|
|
55
|
+
expectTypeOf<Pick<Result['data'], 'members'>>().toEqualTypeOf<{members?: ProjectMember[]}>()
|
|
49
56
|
})
|