@sanity/sdk-react 2.11.1 → 2.12.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/dist/index.d.ts +41 -0
- package/dist/index.js +15 -2
- package/dist/index.js.map +1 -1
- package/package.json +10 -10
- package/src/_exports/sdk-react.ts +1 -0
- package/src/hooks/releases/useActiveReleases.test.tsx +11 -5
- package/src/hooks/releases/useActiveReleases.ts +12 -15
- package/src/hooks/releases/useAllReleases.test.tsx +93 -0
- package/src/hooks/releases/useAllReleases.ts +62 -0
- package/src/hooks/releases/usePerspective.test.tsx +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/sdk-react",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.12.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Sanity SDK React toolkit for Content OS",
|
|
6
6
|
"keywords": [
|
|
@@ -45,12 +45,12 @@
|
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@sanity/client": "^7.22.0",
|
|
47
47
|
"@sanity/message-protocol": "^0.23.0",
|
|
48
|
-
"@sanity/types": "^5.
|
|
48
|
+
"@sanity/types": "^5.26.0",
|
|
49
49
|
"groq": "3.88.1-typegen-experimental.0",
|
|
50
50
|
"react-compiler-runtime": "19.1.0-rc.2",
|
|
51
51
|
"react-error-boundary": "^6.1.1",
|
|
52
52
|
"rxjs": "^7.8.2",
|
|
53
|
-
"@sanity/sdk": "2.
|
|
53
|
+
"@sanity/sdk": "2.12.0"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@sanity/browserslist-config": "^1.0.5",
|
|
@@ -59,11 +59,11 @@
|
|
|
59
59
|
"@sanity/prettier-config": "^1.0.6",
|
|
60
60
|
"@testing-library/jest-dom": "^6.9.1",
|
|
61
61
|
"@testing-library/react": "^16.3.2",
|
|
62
|
-
"@types/node": "^24.12.
|
|
63
|
-
"@types/react": "^19.2.
|
|
62
|
+
"@types/node": "^24.12.4",
|
|
63
|
+
"@types/react": "^19.2.15",
|
|
64
64
|
"@types/react-dom": "^19.2.3",
|
|
65
65
|
"@vitejs/plugin-react": "^4.7.0",
|
|
66
|
-
"@vitest/coverage-v8": "4.1.
|
|
66
|
+
"@vitest/coverage-v8": "4.1.6",
|
|
67
67
|
"babel-plugin-react-compiler": "19.1.0-rc.1",
|
|
68
68
|
"eslint": "^9.39.4",
|
|
69
69
|
"groq-js": "^1.30.1",
|
|
@@ -74,12 +74,12 @@
|
|
|
74
74
|
"rollup-plugin-visualizer": "^5.14.0",
|
|
75
75
|
"typescript": "^5.9.3",
|
|
76
76
|
"vite": "^7.3.3",
|
|
77
|
-
"vitest": "^4.1.
|
|
78
|
-
"@repo/
|
|
77
|
+
"vitest": "^4.1.6",
|
|
78
|
+
"@repo/config-eslint": "0.0.0",
|
|
79
79
|
"@repo/config-test": "0.0.1",
|
|
80
80
|
"@repo/tsconfig": "0.0.1",
|
|
81
|
-
"@repo/config
|
|
82
|
-
"@repo/package.
|
|
81
|
+
"@repo/package.config": "0.0.1",
|
|
82
|
+
"@repo/package.bundle": "3.82.0"
|
|
83
83
|
},
|
|
84
84
|
"peerDependencies": {
|
|
85
85
|
"react": "^18.0.0 || ^19.0.0",
|
|
@@ -91,6 +91,7 @@ export {useProject} from '../hooks/projects/useProject'
|
|
|
91
91
|
export {type ProjectWithoutMembers, useProjects} from '../hooks/projects/useProjects'
|
|
92
92
|
export {useQuery} from '../hooks/query/useQuery'
|
|
93
93
|
export {useActiveReleases} from '../hooks/releases/useActiveReleases'
|
|
94
|
+
export {useAllReleases} from '../hooks/releases/useAllReleases'
|
|
94
95
|
export {usePerspective} from '../hooks/releases/usePerspective'
|
|
95
96
|
export {type UserResult, useUser} from '../hooks/users/useUser'
|
|
96
97
|
export {type UsersResult, useUsers} from '../hooks/users/useUsers'
|
|
@@ -20,14 +20,14 @@ describe('useActiveReleases', () => {
|
|
|
20
20
|
vi.clearAllMocks()
|
|
21
21
|
})
|
|
22
22
|
|
|
23
|
-
it('
|
|
23
|
+
it('suspends until the releases state source emits, then resolves with the data', async () => {
|
|
24
24
|
const mockSubject = new BehaviorSubject<ReleaseDocument[] | undefined>(undefined)
|
|
25
25
|
const mockStateSource = {
|
|
26
26
|
subscribe: vi.fn((callback) => {
|
|
27
27
|
const subscription = mockSubject.subscribe(callback)
|
|
28
28
|
return () => subscription.unsubscribe()
|
|
29
29
|
}),
|
|
30
|
-
getCurrent: vi.fn(() =>
|
|
30
|
+
getCurrent: vi.fn(() => mockSubject.getValue()),
|
|
31
31
|
observable: mockSubject,
|
|
32
32
|
}
|
|
33
33
|
|
|
@@ -50,15 +50,21 @@ describe('useActiveReleases', () => {
|
|
|
50
50
|
},
|
|
51
51
|
)
|
|
52
52
|
|
|
53
|
-
// Verify that the hook threw a promise (suspended)
|
|
54
53
|
expect(result.current).toBeInstanceOf(Promise)
|
|
55
54
|
expect(mockStateSource.getCurrent).toHaveBeenCalled()
|
|
55
|
+
|
|
56
|
+
const resolved: ReleaseDocument[] = [
|
|
57
|
+
{_id: 'release1', _type: 'release'} as unknown as ReleaseDocument,
|
|
58
|
+
]
|
|
59
|
+
mockSubject.next(resolved)
|
|
60
|
+
|
|
61
|
+
await expect(result.current).resolves.toEqual(resolved)
|
|
56
62
|
})
|
|
57
63
|
|
|
58
64
|
it('should resolve with releases when data is available', () => {
|
|
59
65
|
const mockReleases: ReleaseDocument[] = [
|
|
60
|
-
{_id: 'release1', _type: 'release'} as ReleaseDocument,
|
|
61
|
-
{_id: 'release2', _type: 'release'} as ReleaseDocument,
|
|
66
|
+
{_id: 'release1', _type: 'release'} as unknown as ReleaseDocument,
|
|
67
|
+
{_id: 'release2', _type: 'release'} as unknown as ReleaseDocument,
|
|
62
68
|
]
|
|
63
69
|
|
|
64
70
|
const mockSubject = new BehaviorSubject<ReleaseDocument[]>(mockReleases)
|
|
@@ -14,21 +14,6 @@ import {
|
|
|
14
14
|
type WithResourceNameSupport,
|
|
15
15
|
} from '../helpers/useNormalizedResourceOptions'
|
|
16
16
|
|
|
17
|
-
/**
|
|
18
|
-
* @public
|
|
19
|
-
|
|
20
|
-
* Returns the active releases for the current project,
|
|
21
|
-
* represented as a list of release documents.
|
|
22
|
-
*
|
|
23
|
-
* @returns The active releases for the current project.
|
|
24
|
-
* @category Projects
|
|
25
|
-
* @example
|
|
26
|
-
* ```tsx
|
|
27
|
-
* import {useActiveReleases} from '@sanity/sdk-react'
|
|
28
|
-
*
|
|
29
|
-
* const activeReleases = useActiveReleases()
|
|
30
|
-
* ```
|
|
31
|
-
*/
|
|
32
17
|
type UseActiveReleasesValue = {
|
|
33
18
|
(options?: {resource?: DocumentResource}): ReleaseDocument[]
|
|
34
19
|
}
|
|
@@ -49,6 +34,18 @@ const useActiveReleasesValue: UseActiveReleasesValue = createStateSourceHook({
|
|
|
49
34
|
/**
|
|
50
35
|
* @public
|
|
51
36
|
* @function
|
|
37
|
+
*
|
|
38
|
+
* Returns the active releases for the current project,
|
|
39
|
+
* represented as a list of release documents.
|
|
40
|
+
*
|
|
41
|
+
* @returns The active releases for the current project.
|
|
42
|
+
* @category Releases
|
|
43
|
+
* @example
|
|
44
|
+
* ```tsx
|
|
45
|
+
* import {useActiveReleases} from '@sanity/sdk-react'
|
|
46
|
+
*
|
|
47
|
+
* const activeReleases = useActiveReleases()
|
|
48
|
+
* ```
|
|
52
49
|
*/
|
|
53
50
|
export function useActiveReleases(
|
|
54
51
|
options?: WithResourceNameSupport<SanityConfig> | undefined,
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import {getAllReleasesState, type ReleaseDocument} from '@sanity/sdk'
|
|
2
|
+
import {renderHook} from '@testing-library/react'
|
|
3
|
+
import {BehaviorSubject} from 'rxjs'
|
|
4
|
+
import {describe, expect, it, vi} from 'vitest'
|
|
5
|
+
|
|
6
|
+
import {ResourceProvider} from '../../context/ResourceProvider'
|
|
7
|
+
import {useAllReleases} from './useAllReleases'
|
|
8
|
+
|
|
9
|
+
vi.mock('@sanity/sdk', async () => {
|
|
10
|
+
const actual = await vi.importActual('@sanity/sdk')
|
|
11
|
+
return {
|
|
12
|
+
...actual,
|
|
13
|
+
getAllReleasesState: vi.fn(),
|
|
14
|
+
}
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
describe('useAllReleases', () => {
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
vi.clearAllMocks()
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('suspends until the releases state source emits, then resolves with the data', async () => {
|
|
23
|
+
const mockSubject = new BehaviorSubject<ReleaseDocument[] | undefined>(undefined)
|
|
24
|
+
const mockStateSource = {
|
|
25
|
+
subscribe: vi.fn((callback) => {
|
|
26
|
+
const subscription = mockSubject.subscribe(callback)
|
|
27
|
+
return () => subscription.unsubscribe()
|
|
28
|
+
}),
|
|
29
|
+
getCurrent: vi.fn(() => mockSubject.getValue()),
|
|
30
|
+
observable: mockSubject,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
vi.mocked(getAllReleasesState).mockReturnValue(mockStateSource)
|
|
34
|
+
|
|
35
|
+
const {result} = renderHook(
|
|
36
|
+
() => {
|
|
37
|
+
try {
|
|
38
|
+
return useAllReleases()
|
|
39
|
+
} catch (e) {
|
|
40
|
+
return e
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
wrapper: ({children}) => (
|
|
45
|
+
<ResourceProvider projectId="p" dataset="d" fallback={<p>Loading...</p>}>
|
|
46
|
+
{children}
|
|
47
|
+
</ResourceProvider>
|
|
48
|
+
),
|
|
49
|
+
},
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
expect(result.current).toBeInstanceOf(Promise)
|
|
53
|
+
expect(mockStateSource.getCurrent).toHaveBeenCalled()
|
|
54
|
+
|
|
55
|
+
const resolved: ReleaseDocument[] = [
|
|
56
|
+
{_id: 'r-active', _type: 'system.release', state: 'active'} as ReleaseDocument,
|
|
57
|
+
]
|
|
58
|
+
mockSubject.next(resolved)
|
|
59
|
+
|
|
60
|
+
await expect(result.current).resolves.toEqual(resolved)
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('returns every release including archived and published once loaded', () => {
|
|
64
|
+
const mockReleases: ReleaseDocument[] = [
|
|
65
|
+
{_id: 'r-active', _type: 'system.release', state: 'active'} as ReleaseDocument,
|
|
66
|
+
{_id: 'r-archived', _type: 'system.release', state: 'archived'} as ReleaseDocument,
|
|
67
|
+
{_id: 'r-published', _type: 'system.release', state: 'published'} as ReleaseDocument,
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
const mockSubject = new BehaviorSubject<ReleaseDocument[]>(mockReleases)
|
|
71
|
+
const mockStateSource = {
|
|
72
|
+
subscribe: vi.fn((callback) => {
|
|
73
|
+
const subscription = mockSubject.subscribe(callback)
|
|
74
|
+
return () => subscription.unsubscribe()
|
|
75
|
+
}),
|
|
76
|
+
getCurrent: vi.fn(() => mockReleases),
|
|
77
|
+
observable: mockSubject,
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
vi.mocked(getAllReleasesState).mockReturnValue(mockStateSource)
|
|
81
|
+
|
|
82
|
+
const {result} = renderHook(() => useAllReleases(), {
|
|
83
|
+
wrapper: ({children}) => (
|
|
84
|
+
<ResourceProvider projectId="p" dataset="d" fallback={<p>Loading...</p>}>
|
|
85
|
+
{children}
|
|
86
|
+
</ResourceProvider>
|
|
87
|
+
),
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
expect(result.current).toEqual(mockReleases)
|
|
91
|
+
expect(mockStateSource.getCurrent).toHaveBeenCalled()
|
|
92
|
+
})
|
|
93
|
+
})
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type DocumentResource,
|
|
3
|
+
getAllReleasesState,
|
|
4
|
+
type ReleaseDocument,
|
|
5
|
+
type SanityConfig,
|
|
6
|
+
type SanityInstance,
|
|
7
|
+
type StateSource,
|
|
8
|
+
} from '@sanity/sdk'
|
|
9
|
+
import {filter, firstValueFrom} from 'rxjs'
|
|
10
|
+
|
|
11
|
+
import {createStateSourceHook} from '../helpers/createStateSourceHook'
|
|
12
|
+
import {
|
|
13
|
+
useNormalizedResourceOptions,
|
|
14
|
+
type WithResourceNameSupport,
|
|
15
|
+
} from '../helpers/useNormalizedResourceOptions'
|
|
16
|
+
|
|
17
|
+
type UseAllReleasesValue = {
|
|
18
|
+
(options?: {resource?: DocumentResource}): ReleaseDocument[]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const useAllReleasesValue: UseAllReleasesValue = createStateSourceHook({
|
|
22
|
+
getState: getAllReleasesState as (
|
|
23
|
+
instance: SanityInstance,
|
|
24
|
+
options?: {resource?: DocumentResource},
|
|
25
|
+
) => StateSource<ReleaseDocument[]>,
|
|
26
|
+
shouldSuspend: (instance: SanityInstance, options?: {resource?: DocumentResource}) =>
|
|
27
|
+
getAllReleasesState(instance, options ?? {}).getCurrent() === undefined,
|
|
28
|
+
suspender: (instance: SanityInstance, options?: {resource?: DocumentResource}) =>
|
|
29
|
+
firstValueFrom(getAllReleasesState(instance, options ?? {}).observable.pipe(filter(Boolean))),
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @public
|
|
34
|
+
* @function
|
|
35
|
+
*
|
|
36
|
+
* Returns every release the dataset has — including `archived`, `published`,
|
|
37
|
+
* and mid-transition states (`archiving`, `unarchiving`, `publishing`,
|
|
38
|
+
* `scheduling`).
|
|
39
|
+
*
|
|
40
|
+
* Use this hook when you're building a release-management UI (listing
|
|
41
|
+
* releases, surfacing lifecycle controls, etc.) so a release stays visible
|
|
42
|
+
* across its full lifecycle — including after it's been published or
|
|
43
|
+
* archived. For perspective / content queries, prefer
|
|
44
|
+
* {@link useActiveReleases}, which filters to releases that still affect
|
|
45
|
+
* what's queryable.
|
|
46
|
+
*
|
|
47
|
+
* @returns Every release for the current project, sorted to match the order
|
|
48
|
+
* used by {@link useActiveReleases}.
|
|
49
|
+
* @category Releases
|
|
50
|
+
* @example
|
|
51
|
+
* ```tsx
|
|
52
|
+
* import {useAllReleases} from '@sanity/sdk-react'
|
|
53
|
+
*
|
|
54
|
+
* const releases = useAllReleases()
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export function useAllReleases(
|
|
58
|
+
options?: WithResourceNameSupport<SanityConfig> | undefined,
|
|
59
|
+
): ReleaseDocument[] {
|
|
60
|
+
const normalizedOptions = useNormalizedResourceOptions(options ?? {})
|
|
61
|
+
return useAllReleasesValue(normalizedOptions)
|
|
62
|
+
}
|
|
@@ -47,7 +47,7 @@ describe('usePerspective', () => {
|
|
|
47
47
|
// Mock the active releases observable for the suspender
|
|
48
48
|
const mockReleaseDoc: ReleaseDocument = {
|
|
49
49
|
_id: 'release1',
|
|
50
|
-
_type: 'release',
|
|
50
|
+
_type: 'system.release',
|
|
51
51
|
_createdAt: '2021-01-01T00:00:00Z',
|
|
52
52
|
_updatedAt: '2021-01-01T00:00:00Z',
|
|
53
53
|
_rev: 'rev1',
|