@sanity/sdk-react 0.0.0-rc.0 → 0.0.0-rc.2

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.
Files changed (49) hide show
  1. package/README.md +1 -1
  2. package/dist/index.d.ts +398 -228
  3. package/dist/index.js +275 -166
  4. package/dist/index.js.map +1 -1
  5. package/package.json +11 -11
  6. package/src/_exports/index.ts +20 -12
  7. package/src/components/Login/LoginLinks.test.tsx +2 -2
  8. package/src/components/Login/LoginLinks.tsx +2 -2
  9. package/src/components/auth/AuthBoundary.test.tsx +2 -2
  10. package/src/components/auth/LoginCallback.test.tsx +3 -3
  11. package/src/components/auth/LoginCallback.tsx +4 -4
  12. package/src/hooks/auth/useCurrentUser.tsx +1 -0
  13. package/src/hooks/auth/useDashboardOrganizationId.test.tsx +42 -0
  14. package/src/hooks/auth/useDashboardOrganizationId.tsx +29 -0
  15. package/src/hooks/auth/useHandleAuthCallback.test.tsx +16 -0
  16. package/src/hooks/auth/{useHandleCallback.tsx → useHandleAuthCallback.tsx} +6 -6
  17. package/src/hooks/auth/useLogOut.test.tsx +2 -2
  18. package/src/hooks/client/useClient.ts +1 -0
  19. package/src/hooks/comlink/useFrameConnection.test.tsx +20 -10
  20. package/src/hooks/comlink/useFrameConnection.ts +33 -56
  21. package/src/hooks/comlink/useManageFavorite.test.ts +9 -4
  22. package/src/hooks/comlink/useManageFavorite.ts +46 -14
  23. package/src/hooks/comlink/useRecordDocumentHistoryEvent.test.ts +7 -3
  24. package/src/hooks/comlink/useRecordDocumentHistoryEvent.ts +45 -14
  25. package/src/hooks/comlink/useWindowConnection.test.ts +20 -10
  26. package/src/hooks/comlink/useWindowConnection.ts +69 -41
  27. package/src/hooks/dashboard/useNavigateToStudioDocument.test.ts +178 -0
  28. package/src/hooks/dashboard/useNavigateToStudioDocument.ts +123 -0
  29. package/src/hooks/dashboard/useStudioWorkspacesByResourceId.test.tsx +278 -0
  30. package/src/hooks/dashboard/useStudioWorkspacesByResourceId.ts +92 -0
  31. package/src/hooks/datasets/useDatasets.ts +6 -3
  32. package/src/hooks/document/useApplyDocumentActions.test.ts +25 -0
  33. package/src/hooks/document/{useApplyActions.ts → useApplyDocumentActions.ts} +13 -12
  34. package/src/hooks/document/{usePermissions.ts → useDocumentPermissions.ts} +12 -10
  35. package/src/hooks/document/useDocumentSyncStatus.ts +4 -1
  36. package/src/hooks/document/useEditDocument.test.ts +8 -8
  37. package/src/hooks/document/useEditDocument.ts +2 -2
  38. package/src/hooks/{infiniteList/useInfiniteList.test.tsx → documents/useDocuments.test.tsx} +9 -9
  39. package/src/hooks/{infiniteList/useInfiniteList.ts → documents/useDocuments.ts} +10 -10
  40. package/src/hooks/{paginatedList/usePaginatedList.test.tsx → paginatedDocuments/usePaginatedDocuments.test.tsx} +14 -14
  41. package/src/hooks/{paginatedList/usePaginatedList.ts → paginatedDocuments/usePaginatedDocuments.ts} +7 -7
  42. package/src/hooks/preview/usePreview.test.tsx +6 -6
  43. package/src/hooks/preview/usePreview.tsx +5 -5
  44. package/src/hooks/projection/useProjection.test.tsx +9 -9
  45. package/src/hooks/projection/useProjection.ts +27 -15
  46. package/src/hooks/projects/useProject.ts +4 -1
  47. package/src/hooks/projects/useProjects.ts +7 -3
  48. package/src/hooks/auth/useHandleCallback.test.tsx +0 -16
  49. package/src/hooks/document/useApplyActions.test.ts +0 -25
@@ -12,7 +12,7 @@ import {renderHook} from '@testing-library/react'
12
12
  import {beforeEach, describe, expect, it, vi} from 'vitest'
13
13
 
14
14
  import {useSanityInstance} from '../context/useSanityInstance'
15
- import {useApplyActions} from './useApplyActions'
15
+ import {useApplyDocumentActions} from './useApplyDocumentActions'
16
16
  import {useEditDocument} from './useEditDocument'
17
17
 
18
18
  vi.mock('@sanity/sdk', async (importOriginal) => {
@@ -29,8 +29,8 @@ vi.mock('../context/useSanityInstance', () => ({
29
29
  useSanityInstance: vi.fn(),
30
30
  }))
31
31
 
32
- vi.mock('./useApplyActions', () => ({
33
- useApplyActions: vi.fn(),
32
+ vi.mock('./useApplyDocumentActions', () => ({
33
+ useApplyDocumentActions: vi.fn(),
34
34
  }))
35
35
 
36
36
  // Create a fake instance to be returned by useSanityInstance.
@@ -66,7 +66,7 @@ describe('useEditDocument hook', () => {
66
66
  } as unknown as StateSource<SanityDocument>)
67
67
 
68
68
  const apply = vi.fn().mockResolvedValue({transactionId: 'tx1'})
69
- vi.mocked(useApplyActions).mockReturnValue(apply)
69
+ vi.mocked(useApplyDocumentActions).mockReturnValue(apply)
70
70
 
71
71
  const {result} = renderHook(() => useEditDocument(docHandle, 'foo'))
72
72
  const promise = result.current('newValue')
@@ -87,7 +87,7 @@ describe('useEditDocument hook', () => {
87
87
  } as unknown as StateSource<SanityDocument>)
88
88
 
89
89
  const apply = vi.fn().mockResolvedValue({transactionId: 'tx2'})
90
- vi.mocked(useApplyActions).mockReturnValue(apply)
90
+ vi.mocked(useApplyDocumentActions).mockReturnValue(apply)
91
91
 
92
92
  const {result} = renderHook(() => useEditDocument(docHandle))
93
93
  const promise = result.current({...doc, foo: 'baz', extra: 'old', _id: 'doc1'})
@@ -105,7 +105,7 @@ describe('useEditDocument hook', () => {
105
105
  } as unknown as StateSource<SanityDocument>)
106
106
 
107
107
  const apply = vi.fn().mockResolvedValue({transactionId: 'tx3'})
108
- vi.mocked(useApplyActions).mockReturnValue(apply)
108
+ vi.mocked(useApplyDocumentActions).mockReturnValue(apply)
109
109
 
110
110
  const {result} = renderHook(() => useEditDocument(docHandle, 'foo'))
111
111
  const promise = result.current((prev: unknown) => `${prev}Updated`) // 'bar' becomes 'barUpdated'
@@ -125,7 +125,7 @@ describe('useEditDocument hook', () => {
125
125
  } as unknown as StateSource<SanityDocument>)
126
126
 
127
127
  const apply = vi.fn().mockResolvedValue({transactionId: 'tx4'})
128
- vi.mocked(useApplyActions).mockReturnValue(apply)
128
+ vi.mocked(useApplyDocumentActions).mockReturnValue(apply)
129
129
 
130
130
  const {result} = renderHook(() => useEditDocument(docHandle))
131
131
  const promise = result.current((prevDoc) => ({...prevDoc, foo: 'baz'}))
@@ -143,7 +143,7 @@ describe('useEditDocument hook', () => {
143
143
  } as unknown as StateSource<SanityDocument>)
144
144
 
145
145
  const fakeApply = vi.fn()
146
- vi.mocked(useApplyActions).mockReturnValue(fakeApply)
146
+ vi.mocked(useApplyDocumentActions).mockReturnValue(fakeApply)
147
147
 
148
148
  const {result} = renderHook(() => useEditDocument(docHandle))
149
149
  expect(() => result.current('notAnObject' as unknown as SanityDocument)).toThrowError(
@@ -12,7 +12,7 @@ import {type SanityDocument} from '@sanity/types'
12
12
  import {useCallback} from 'react'
13
13
 
14
14
  import {useSanityInstance} from '../context/useSanityInstance'
15
- import {useApplyActions} from './useApplyActions'
15
+ import {useApplyDocumentActions} from './useApplyDocumentActions'
16
16
 
17
17
  const ignoredKeys = ['_id', '_type', '_createdAt', '_updatedAt', '_rev']
18
18
 
@@ -154,7 +154,7 @@ export function useEditDocument(
154
154
  const resourceId = getResourceId(doc.resourceId)!
155
155
  const documentId = doc._id
156
156
  const instance = useSanityInstance(resourceId)
157
- const apply = useApplyActions(resourceId)
157
+ const apply = useApplyDocumentActions(resourceId)
158
158
  const isDocumentReady = useCallback(
159
159
  () => getDocumentState(instance, documentId).getCurrent() !== undefined,
160
160
  [instance, documentId],
@@ -3,11 +3,11 @@ import {describe, vi} from 'vitest'
3
3
 
4
4
  import {evaluateSync, parse} from '../_synchronous-groq-js.mjs'
5
5
  import {useQuery} from '../query/useQuery'
6
- import {useInfiniteList} from './useInfiniteList'
6
+ import {useDocuments} from './useDocuments'
7
7
 
8
8
  vi.mock('../query/useQuery')
9
9
 
10
- describe('useInfiniteList', () => {
10
+ describe('useDocuments', () => {
11
11
  beforeEach(() => {
12
12
  const dataset = [
13
13
  {
@@ -76,13 +76,13 @@ describe('useInfiniteList', () => {
76
76
 
77
77
  it('should respect custom page size', () => {
78
78
  const customBatchSize = 2
79
- const {result} = renderHook(() => useInfiniteList({batchSize: customBatchSize}))
79
+ const {result} = renderHook(() => useDocuments({batchSize: customBatchSize}))
80
80
 
81
81
  expect(result.current.data.length).toBe(customBatchSize)
82
82
  })
83
83
 
84
84
  it('should filter by document type', () => {
85
- const {result} = renderHook(() => useInfiniteList({filter: '_type == "movie"'}))
85
+ const {result} = renderHook(() => useDocuments({filter: '_type == "movie"'}))
86
86
 
87
87
  expect(result.current.data.every((doc) => doc._type === 'movie')).toBe(true)
88
88
  expect(result.current.count).toBe(5) // 5 movies in the dataset
@@ -90,7 +90,7 @@ describe('useInfiniteList', () => {
90
90
 
91
91
  // groq-js doesn't support search filters yet
92
92
  it.skip('should apply search filter', () => {
93
- const {result} = renderHook(() => useInfiniteList({search: 'inter'}))
93
+ const {result} = renderHook(() => useDocuments({search: 'inter'}))
94
94
 
95
95
  // Should match "Interstellar"
96
96
  expect(result.current.data.some((doc) => doc._id === 'movie3')).toBe(true)
@@ -98,7 +98,7 @@ describe('useInfiniteList', () => {
98
98
 
99
99
  it('should apply ordering', () => {
100
100
  const {result} = renderHook(() =>
101
- useInfiniteList({
101
+ useDocuments({
102
102
  filter: '_type == "movie"',
103
103
  orderings: [{field: 'releaseYear', direction: 'desc'}],
104
104
  }),
@@ -110,7 +110,7 @@ describe('useInfiniteList', () => {
110
110
 
111
111
  it('should load more data when loadMore is called', () => {
112
112
  const batchSize = 2
113
- const {result} = renderHook(() => useInfiniteList({batchSize: batchSize}))
113
+ const {result} = renderHook(() => useDocuments({batchSize: batchSize}))
114
114
 
115
115
  expect(result.current.data.length).toBe(batchSize)
116
116
 
@@ -122,7 +122,7 @@ describe('useInfiniteList', () => {
122
122
  })
123
123
 
124
124
  it('should indicate when there is more data to load', () => {
125
- const {result} = renderHook(() => useInfiniteList({batchSize: 3}))
125
+ const {result} = renderHook(() => useDocuments({batchSize: 3}))
126
126
  expect(result.current.hasMore).toBe(true)
127
127
  // Load all remaining data
128
128
  act(() => {
@@ -133,7 +133,7 @@ describe('useInfiniteList', () => {
133
133
 
134
134
  // New test case for resetting limit when filter changes
135
135
  it('should reset limit when filter changes', () => {
136
- const {result, rerender} = renderHook((props) => useInfiniteList(props), {
136
+ const {result, rerender} = renderHook((props) => useDocuments(props), {
137
137
  initialProps: {batchSize: 2, filter: ''},
138
138
  })
139
139
  // Initially, data length equals pageSize (2)
@@ -11,18 +11,18 @@ const DEFAULT_PERSPECTIVE = 'drafts'
11
11
  * Result structure returned from the infinite list query
12
12
  * @internal
13
13
  */
14
- interface InfiniteListQueryResult {
14
+ interface UseDocumentsQueryResult {
15
15
  count: number
16
16
  data: DocumentHandle[]
17
17
  }
18
18
 
19
19
  /**
20
- * Configuration options for the useInfiniteList hook
20
+ * Configuration options for the useDocuments hook
21
21
  *
22
22
  * @beta
23
23
  * @category Types
24
24
  */
25
- export interface InfiniteListOptions extends QueryOptions {
25
+ export interface DocumentsOptions extends QueryOptions {
26
26
  /**
27
27
  * GROQ filter expression to apply to the query
28
28
  */
@@ -42,12 +42,12 @@ export interface InfiniteListOptions extends QueryOptions {
42
42
  }
43
43
 
44
44
  /**
45
- * Return value from the useInfiniteList hook
45
+ * Return value from the useDocuments hook
46
46
  *
47
47
  * @beta
48
48
  * @category Types
49
49
  */
50
- export interface InfiniteList {
50
+ export interface DocumentsResponse {
51
51
  /**
52
52
  * Array of document handles for the current batch
53
53
  */
@@ -78,10 +78,10 @@ export interface InfiniteList {
78
78
  * @beta
79
79
  * @category Documents
80
80
  * @param options - Configuration options for the infinite list
81
- * @returns An object containing the list of document handles, the loading state, the total count of retrived document handles, and a function to load more
81
+ * @returns An object containing the list of document handles, the loading state, the total count of retrieved document handles, and a function to load more
82
82
  * @example
83
83
  * ```tsx
84
- * const {data, hasMore, isPending, loadMore} = useInfiniteList({
84
+ * const {data, hasMore, isPending, loadMore} = useDocuments({
85
85
  * filter: '_type == "post"',
86
86
  * search: searchTerm,
87
87
  * batchSize: 10,
@@ -104,14 +104,14 @@ export interface InfiniteList {
104
104
  * ```
105
105
  *
106
106
  */
107
- export function useInfiniteList({
107
+ export function useDocuments({
108
108
  batchSize = DEFAULT_BATCH_SIZE,
109
109
  params,
110
110
  search,
111
111
  filter,
112
112
  orderings,
113
113
  ...options
114
- }: InfiniteListOptions): InfiniteList {
114
+ }: DocumentsOptions): DocumentsResponse {
115
115
  const perspective = options.perspective ?? DEFAULT_PERSPECTIVE
116
116
  const [limit, setLimit] = useState(batchSize)
117
117
 
@@ -155,7 +155,7 @@ export function useInfiniteList({
155
155
  const {
156
156
  data: {count, data},
157
157
  isPending,
158
- } = useQuery<InfiniteListQueryResult>(`{"count":${countQuery},"data":${dataQuery}}`, {
158
+ } = useQuery<UseDocumentsQueryResult>(`{"count":${countQuery},"data":${dataQuery}}`, {
159
159
  ...options,
160
160
  params,
161
161
  perspective,
@@ -3,11 +3,11 @@ import {describe, vi} from 'vitest'
3
3
 
4
4
  import {evaluateSync, parse} from '../_synchronous-groq-js.mjs'
5
5
  import {useQuery} from '../query/useQuery'
6
- import {usePaginatedList} from './usePaginatedList'
6
+ import {usePaginatedDocuments} from './usePaginatedDocuments'
7
7
 
8
8
  vi.mock('../query/useQuery')
9
9
 
10
- describe('usePaginatedList', () => {
10
+ describe('usePaginatedDocuments', () => {
11
11
  beforeEach(() => {
12
12
  const dataset = [
13
13
  {
@@ -76,14 +76,14 @@ describe('usePaginatedList', () => {
76
76
 
77
77
  it('should respect custom page size', () => {
78
78
  const customPageSize = 2
79
- const {result} = renderHook(() => usePaginatedList({pageSize: customPageSize}))
79
+ const {result} = renderHook(() => usePaginatedDocuments({pageSize: customPageSize}))
80
80
 
81
81
  expect(result.current.pageSize).toBe(customPageSize)
82
82
  expect(result.current.data.length).toBeLessThanOrEqual(customPageSize)
83
83
  })
84
84
 
85
85
  it('should filter by document type', () => {
86
- const {result} = renderHook(() => usePaginatedList({filter: '_type == "movie"'}))
86
+ const {result} = renderHook(() => usePaginatedDocuments({filter: '_type == "movie"'}))
87
87
 
88
88
  expect(result.current.data.every((doc) => doc._type === 'movie')).toBe(true)
89
89
  expect(result.current.count).toBe(5) // 5 movies in the dataset
@@ -91,7 +91,7 @@ describe('usePaginatedList', () => {
91
91
 
92
92
  // groq-js doesn't support search filters yet
93
93
  it.skip('should apply search filter', () => {
94
- const {result} = renderHook(() => usePaginatedList({search: 'inter'}))
94
+ const {result} = renderHook(() => usePaginatedDocuments({search: 'inter'}))
95
95
 
96
96
  // Should match "Interstellar"
97
97
  expect(result.current.data.some((doc) => doc._id === 'movie3')).toBe(true)
@@ -99,7 +99,7 @@ describe('usePaginatedList', () => {
99
99
 
100
100
  it('should apply ordering', () => {
101
101
  const {result} = renderHook(() =>
102
- usePaginatedList({
102
+ usePaginatedDocuments({
103
103
  filter: '_type == "movie"',
104
104
  orderings: [{field: 'releaseYear', direction: 'desc'}],
105
105
  }),
@@ -111,7 +111,7 @@ describe('usePaginatedList', () => {
111
111
 
112
112
  it('should calculate pagination values correctly', () => {
113
113
  const pageSize = 2
114
- const {result} = renderHook(() => usePaginatedList({pageSize}))
114
+ const {result} = renderHook(() => usePaginatedDocuments({pageSize}))
115
115
 
116
116
  expect(result.current.currentPage).toBe(1)
117
117
  expect(result.current.totalPages).toBe(3) // 6 items with page size 2
@@ -122,7 +122,7 @@ describe('usePaginatedList', () => {
122
122
 
123
123
  it('should navigate to next page', () => {
124
124
  const pageSize = 2
125
- const {result} = renderHook(() => usePaginatedList({pageSize}))
125
+ const {result} = renderHook(() => usePaginatedDocuments({pageSize}))
126
126
 
127
127
  expect(result.current.currentPage).toBe(1)
128
128
  expect(result.current.data.length).toBe(pageSize)
@@ -138,7 +138,7 @@ describe('usePaginatedList', () => {
138
138
 
139
139
  it('should navigate to previous page', () => {
140
140
  const pageSize = 2
141
- const {result} = renderHook(() => usePaginatedList({pageSize}))
141
+ const {result} = renderHook(() => usePaginatedDocuments({pageSize}))
142
142
 
143
143
  // Go to page 2 first
144
144
  act(() => {
@@ -158,7 +158,7 @@ describe('usePaginatedList', () => {
158
158
 
159
159
  it('should navigate to first page', () => {
160
160
  const pageSize = 2
161
- const {result} = renderHook(() => usePaginatedList({pageSize}))
161
+ const {result} = renderHook(() => usePaginatedDocuments({pageSize}))
162
162
 
163
163
  // Go to last page first
164
164
  act(() => {
@@ -178,7 +178,7 @@ describe('usePaginatedList', () => {
178
178
 
179
179
  it('should navigate to last page', () => {
180
180
  const pageSize = 2
181
- const {result} = renderHook(() => usePaginatedList({pageSize}))
181
+ const {result} = renderHook(() => usePaginatedDocuments({pageSize}))
182
182
 
183
183
  act(() => {
184
184
  result.current.lastPage()
@@ -190,7 +190,7 @@ describe('usePaginatedList', () => {
190
190
 
191
191
  it('should navigate to specific page', () => {
192
192
  const pageSize = 2
193
- const {result} = renderHook(() => usePaginatedList({pageSize}))
193
+ const {result} = renderHook(() => usePaginatedDocuments({pageSize}))
194
194
 
195
195
  act(() => {
196
196
  result.current.goToPage(2) // Go to page 2
@@ -215,7 +215,7 @@ describe('usePaginatedList', () => {
215
215
 
216
216
  it('should set page availability flags correctly', () => {
217
217
  const pageSize = 2
218
- const {result} = renderHook(() => usePaginatedList({pageSize}))
218
+ const {result} = renderHook(() => usePaginatedDocuments({pageSize}))
219
219
  // On first page
220
220
  expect(result.current.hasFirstPage).toBe(false)
221
221
  expect(result.current.hasPreviousPage).toBe(false)
@@ -241,7 +241,7 @@ describe('usePaginatedList', () => {
241
241
 
242
242
  // New test case for resetting the current page when filter changes
243
243
  it('should reset current page when filter changes', () => {
244
- const {result, rerender} = renderHook((props) => usePaginatedList(props), {
244
+ const {result, rerender} = renderHook((props) => usePaginatedDocuments(props), {
245
245
  initialProps: {pageSize: 2, filter: ''},
246
246
  })
247
247
  // Initially, current page should be 1
@@ -7,12 +7,12 @@ import {useQuery} from '../query/useQuery'
7
7
  const DEFAULT_PERSPECTIVE = 'drafts'
8
8
 
9
9
  /**
10
- * Configuration options for the usePaginatedList hook
10
+ * Configuration options for the usePaginatedDocuments hook
11
11
  *
12
12
  * @beta
13
13
  * @category Types
14
14
  */
15
- export interface PaginatedListOptions extends QueryOptions {
15
+ export interface PaginatedDocumentsOptions extends QueryOptions {
16
16
  /**
17
17
  * GROQ filter expression to apply to the query
18
18
  */
@@ -32,12 +32,12 @@ export interface PaginatedListOptions extends QueryOptions {
32
32
  }
33
33
 
34
34
  /**
35
- * Return value from the usePaginatedList hook
35
+ * Return value from the usePaginatedDocuments hook
36
36
  *
37
37
  * @beta
38
38
  * @category Types
39
39
  */
40
- export interface PaginatedList {
40
+ export interface PaginatedDocumentsResponse {
41
41
  /**
42
42
  * Array of document handles for the current page
43
43
  */
@@ -136,7 +136,7 @@ export interface PaginatedList {
136
136
  * previousPage,
137
137
  * hasNextPage,
138
138
  * hasPreviousPage
139
- * } = usePaginatedList({
139
+ * } = usePaginatedDocuments({
140
140
  * filter: '_type == "post"',
141
141
  * search: searchTerm,
142
142
  * pageSize: 10,
@@ -160,14 +160,14 @@ export interface PaginatedList {
160
160
  * ```
161
161
  *
162
162
  */
163
- export function usePaginatedList({
163
+ export function usePaginatedDocuments({
164
164
  filter = '',
165
165
  pageSize = 25,
166
166
  params = {},
167
167
  orderings,
168
168
  search,
169
169
  ...options
170
- }: PaginatedListOptions = {}): PaginatedList {
170
+ }: PaginatedDocumentsOptions = {}): PaginatedDocumentsResponse {
171
171
  const [pageIndex, setPageIndex] = useState(0)
172
172
  const key = JSON.stringify({filter, search, params, orderings, pageSize})
173
173
  // Reset the pageIndex to 0 whenever any query parameters (filter, search,
@@ -45,12 +45,12 @@ const mockDocument: DocumentHandle = {
45
45
 
46
46
  function TestComponent({document}: {document: DocumentHandle}) {
47
47
  const ref = useRef(null)
48
- const {results, isPending} = usePreview({document, ref})
48
+ const {data, isPending} = usePreview({document, ref})
49
49
 
50
50
  return (
51
51
  <div ref={ref}>
52
- <h1>{results?.title}</h1>
53
- <p>{results?.subtitle}</p>
52
+ <h1>{data?.title}</h1>
53
+ <p>{data?.subtitle}</p>
54
54
  {isPending && <div>Pending...</div>}
55
55
  </div>
56
56
  )
@@ -75,7 +75,7 @@ describe('usePreview', () => {
75
75
  test('it only subscribes when element is visible', async () => {
76
76
  // Setup initial state
77
77
  getCurrent.mockReturnValue({
78
- results: {title: 'Initial Title', subtitle: 'Initial Subtitle'},
78
+ data: {title: 'Initial Title', subtitle: 'Initial Subtitle'},
79
79
  isPending: false,
80
80
  })
81
81
  const eventsUnsubscribe = vi.fn()
@@ -139,7 +139,7 @@ describe('usePreview', () => {
139
139
  intersectionObserverCallback([{isIntersecting: true} as IntersectionObserverEntry])
140
140
  await resolvePromise
141
141
  getCurrent.mockReturnValue({
142
- results: {title: 'Resolved Title', subtitle: 'Resolved Subtitle'},
142
+ data: {title: 'Resolved Title', subtitle: 'Resolved Subtitle'},
143
143
  isPending: false,
144
144
  })
145
145
  subscriber?.()
@@ -156,7 +156,7 @@ describe('usePreview', () => {
156
156
  delete window.IntersectionObserver
157
157
 
158
158
  getCurrent.mockReturnValue({
159
- results: {title: 'Fallback Title', subtitle: 'Fallback Subtitle'},
159
+ data: {title: 'Fallback Title', subtitle: 'Fallback Subtitle'},
160
160
  isPending: false,
161
161
  })
162
162
  subscribe.mockImplementation(() => vi.fn())
@@ -19,7 +19,7 @@ export interface UsePreviewOptions {
19
19
  */
20
20
  export interface UsePreviewResults {
21
21
  /** The results of resolving the document’s preview values */
22
- results: PreviewValue
22
+ data: PreviewValue
23
23
  /** True when preview values are being refreshed */
24
24
  isPending: boolean
25
25
  }
@@ -40,7 +40,7 @@ export interface UsePreviewResults {
40
40
  * ```
41
41
  * // PreviewComponent.jsx
42
42
  * export default function PreviewComponent({ document }) {
43
- * const { results: { title, subtitle, media }, isPending } = usePreview({ document })
43
+ * const { data: { title, subtitle, media }, isPending } = usePreview({ document })
44
44
  * return (
45
45
  * <article style={{ opacity: isPending ? 0.5 : 1}}>
46
46
  * {media?.type === 'image-asset' ? <img src={media.url} alt='' /> : ''}
@@ -51,12 +51,12 @@ export interface UsePreviewResults {
51
51
  * }
52
52
  *
53
53
  * // DocumentList.jsx
54
- * const { results, isPending } = useDocuments({ filter: '_type == "movie"' })
54
+ * const { data } = useDocuments({ filter: '_type == "movie"' })
55
55
  * return (
56
56
  * <div>
57
57
  * <h1>Movies</h1>
58
58
  * <ul>
59
- * {isPending ? 'Loading…' : results.map(movie => (
59
+ * {data.map(movie => (
60
60
  * <li key={movie._id}>
61
61
  * <Suspense fallback='Loading…'>
62
62
  * <PreviewComponent document={movie} />
@@ -115,7 +115,7 @@ export function usePreview({document: {_id, _type}, ref}: UsePreviewOptions): Us
115
115
  // Create getSnapshot function to return current state
116
116
  const getSnapshot = useCallback(() => {
117
117
  const currentState = stateSource.getCurrent()
118
- if (currentState.results === null) throw resolvePreview(instance, {document: {_id, _type}})
118
+ if (currentState.data === null) throw resolvePreview(instance, {document: {_id, _type}})
119
119
  return currentState as UsePreviewResults
120
120
  }, [_id, _type, instance, stateSource])
121
121
 
@@ -61,12 +61,12 @@ function TestComponent({
61
61
  projection: ValidProjection
62
62
  }) {
63
63
  const ref = useRef(null)
64
- const {results, isPending} = useProjection<ProjectionResult>({document, projection, ref})
64
+ const {data, isPending} = useProjection<ProjectionResult>({document, projection, ref})
65
65
 
66
66
  return (
67
67
  <div ref={ref}>
68
- <h1>{results.title}</h1>
69
- <p>{results.description}</p>
68
+ <h1>{data.title}</h1>
69
+ <p>{data.description}</p>
70
70
  {isPending && <div>Pending...</div>}
71
71
  </div>
72
72
  )
@@ -91,7 +91,7 @@ describe('useProjection', () => {
91
91
  test('it only subscribes when element is visible', async () => {
92
92
  // Setup initial state
93
93
  getCurrent.mockReturnValue({
94
- results: {title: 'Initial Title', description: 'Initial Description'},
94
+ data: {title: 'Initial Title', description: 'Initial Description'},
95
95
  isPending: false,
96
96
  })
97
97
  const eventsUnsubscribe = vi.fn()
@@ -129,12 +129,12 @@ describe('useProjection', () => {
129
129
  test('it suspends and resolves data when element becomes visible', async () => {
130
130
  // Mock the initial state to trigger suspense
131
131
  getCurrent.mockReturnValueOnce({
132
- results: null,
132
+ data: null,
133
133
  isPending: true,
134
134
  })
135
135
 
136
136
  const resolvedData = {
137
- results: {title: 'Resolved Title', description: 'Resolved Description'},
137
+ data: {title: 'Resolved Title', description: 'Resolved Description'},
138
138
  isPending: false,
139
139
  }
140
140
 
@@ -169,7 +169,7 @@ describe('useProjection', () => {
169
169
  delete window.IntersectionObserver
170
170
 
171
171
  getCurrent.mockReturnValue({
172
- results: {title: 'Fallback Title', description: 'Fallback Description'},
172
+ data: {title: 'Fallback Title', description: 'Fallback Description'},
173
173
  isPending: false,
174
174
  })
175
175
  subscribe.mockImplementation(() => vi.fn())
@@ -188,7 +188,7 @@ describe('useProjection', () => {
188
188
 
189
189
  test('it updates when projection changes', async () => {
190
190
  getCurrent.mockReturnValue({
191
- results: {title: 'Initial Title'},
191
+ data: {title: 'Initial Title'},
192
192
  isPending: false,
193
193
  })
194
194
  const eventsUnsubscribe = vi.fn()
@@ -202,7 +202,7 @@ describe('useProjection', () => {
202
202
 
203
203
  // Change projection
204
204
  getCurrent.mockReturnValue({
205
- results: {title: 'Updated Title', description: 'Added Description'},
205
+ data: {title: 'Updated Title', description: 'Added Description'},
206
206
  isPending: false,
207
207
  })
208
208
 
@@ -9,14 +9,22 @@ import {distinctUntilChanged, EMPTY, Observable, startWith, switchMap} from 'rxj
9
9
 
10
10
  import {useSanityInstance} from '../context/useSanityInstance'
11
11
 
12
- interface UseProjectionOptions {
12
+ /**
13
+ * @public
14
+ * @category Types
15
+ */
16
+ export interface UseProjectionOptions {
13
17
  document: DocumentHandle
14
18
  projection: ValidProjection
15
19
  ref?: React.RefObject<unknown>
16
20
  }
17
21
 
18
- interface UseProjectionResults<TResult extends object> {
19
- results: TResult
22
+ /**
23
+ * @public
24
+ * @category Types
25
+ */
26
+ export interface UseProjectionResults<TResult extends object> {
27
+ data: TResult
20
28
  isPending: boolean
21
29
  }
22
30
 
@@ -32,40 +40,44 @@ interface UseProjectionResults<TResult extends object> {
32
40
  * @param options - The document handle for the document you want to project values from, the projection string, and an optional ref
33
41
  * @returns The projection values for the given document and a boolean to indicate whether the resolution is pending
34
42
  *
35
- * @example Using a projection to display specific document fields
43
+ * @example Using a projection to render a preview of document
36
44
  * ```
37
45
  * // ProjectionComponent.jsx
38
46
  * export default function ProjectionComponent({ document }) {
39
47
  * const ref = useRef(null)
40
- * const { results: { title, description, authors }, isPending } = useProjection({
48
+ * const { results: { title, coverImage, authors }, isPending } = useProjection({
41
49
  * document,
42
- * projection: '{title, "description": pt::text("description"), "authors": array::join(authors[]->name, ", ")}',
43
- * ref
50
+ * ref,
51
+ * projection: `{
52
+ * title,
53
+ * 'coverImage': cover.asset->url,
54
+ * 'authors': array::join(authors[]->{'name': firstName + ' ' + lastName + ' '}.name, ', ')
55
+ * }`,
44
56
  * })
45
57
  *
46
58
  * return (
47
59
  * <article ref={ref} style={{ opacity: isPending ? 0.5 : 1}}>
48
60
  * <h2>{title}</h2>
49
- * <p>{description}</p>
61
+ * <img src={coverImage} alt={title} />
50
62
  * <p>{authors}</p>
51
63
  * </article>
52
64
  * )
53
65
  * }
54
66
  * ```
55
67
  *
56
- * @example Combining with useInfiniteList to render a collection with specific fields
68
+ * @example Combining with useDocuments to render a collection with specific fields
57
69
  * ```
58
70
  * // DocumentList.jsx
59
- * const { data } = useInfiniteList({ filter: '_type == "article"' })
71
+ * const { data } = useDocuments({ filter: '_type == "article"' })
60
72
  * return (
61
73
  * <div>
62
- * <h1>Articles</h1>
74
+ * <h1>Books</h1>
63
75
  * <ul>
64
- * {data.map(article => (
65
- * <li key={article._id}>
76
+ * {data.map(book => (
77
+ * <li key={book._id}>
66
78
  * <Suspense fallback='Loading…'>
67
79
  * <ProjectionComponent
68
- * document={article}
80
+ * document={book}
69
81
  * />
70
82
  * </Suspense>
71
83
  * </li>
@@ -126,7 +138,7 @@ export function useProjection<TResult extends object>({
126
138
  // Create getSnapshot function to return current state
127
139
  const getSnapshot = useCallback(() => {
128
140
  const currentState = stateSource.getCurrent()
129
- if (currentState.results === null)
141
+ if (currentState.data === null)
130
142
  throw resolveProjection(instance, {document: {_id, _type}, projection})
131
143
  return currentState as UseProjectionResults<TResult>
132
144
  }, [_id, _type, projection, instance, stateSource])
@@ -32,7 +32,10 @@ type UseProject = {
32
32
  (projectId: string): SanityProject
33
33
  }
34
34
 
35
- /** @public */
35
+ /**
36
+ * @public
37
+ * @function
38
+ */
36
39
  export const useProject: UseProject = createStateSourceHook({
37
40
  // remove `undefined` since we're suspending when that is the case
38
41
  getState: getProjectState as (