@sanity/sdk-react 2.5.0 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +164 -19
- package/dist/index.d.ts +571 -26
- package/dist/index.js +149 -78
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/src/_exports/sdk-react.ts +2 -0
- package/src/components/SDKProvider.tsx +8 -3
- package/src/components/SanityApp.test.tsx +72 -2
- package/src/components/SanityApp.tsx +53 -10
- package/src/components/auth/AuthBoundary.tsx +5 -5
- package/src/context/ComlinkTokenRefresh.test.tsx +2 -2
- package/src/context/ComlinkTokenRefresh.tsx +3 -2
- package/src/context/SDKStudioContext.test.tsx +126 -0
- package/src/context/SDKStudioContext.ts +65 -0
- package/src/context/SourcesContext.tsx +7 -0
- package/src/context/renderSanityApp.test.tsx +355 -0
- package/src/context/renderSanityApp.tsx +48 -0
- package/src/hooks/agent/agentActions.ts +436 -21
- package/src/hooks/dashboard/useDispatchIntent.test.ts +26 -20
- package/src/hooks/dashboard/useDispatchIntent.ts +10 -11
- package/src/hooks/dashboard/utils/{getResourceIdFromDocumentHandle.test.ts → useResourceIdFromDocumentHandle.test.ts} +33 -60
- package/src/hooks/dashboard/utils/useResourceIdFromDocumentHandle.ts +46 -0
- package/src/hooks/document/useEditDocument.ts +3 -0
- package/src/hooks/documents/useDocuments.ts +3 -2
- package/src/hooks/helpers/useNormalizedSourceOptions.ts +85 -0
- package/src/hooks/paginatedDocuments/usePaginatedDocuments.ts +1 -0
- package/src/hooks/projection/useDocumentProjection.ts +15 -4
- package/src/hooks/query/useQuery.ts +30 -11
- package/src/hooks/dashboard/types.ts +0 -12
- package/src/hooks/dashboard/utils/getResourceIdFromDocumentHandle.ts +0 -53
package/README.md
CHANGED
|
@@ -322,6 +322,29 @@ The SDK handles updating the document state automatically:
|
|
|
322
322
|
- `publishDocument()` copies draft → published, deletes draft
|
|
323
323
|
- `discardDraft()` deletes draft, reverts to published
|
|
324
324
|
|
|
325
|
+
#### LiveEdit Documents
|
|
326
|
+
|
|
327
|
+
For documents that don't need the draft/published workflow (such as settings, configuration, or real-time collaborative documents), you can use **liveEdit mode** by setting `liveEdit: true` in the document handle:
|
|
328
|
+
|
|
329
|
+
```tsx
|
|
330
|
+
const settingsHandle: DocumentHandle = {
|
|
331
|
+
documentId: 'site-settings',
|
|
332
|
+
documentType: 'settings',
|
|
333
|
+
liveEdit: true, // Edits apply directly without creating a draft
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Edits are applied immediately to the published document
|
|
337
|
+
const editSettings = useEditDocument(settingsHandle)
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
**When using liveEdit documents:**
|
|
341
|
+
|
|
342
|
+
- Drafts will not be created when the document is edited
|
|
343
|
+
- Edits will be applied directly to the published document
|
|
344
|
+
- `publishDocument()`, `unpublishDocument()`, and `discardDraft()` actions cannot be used (since liveEdit documents are always published and do not have drafts)
|
|
345
|
+
|
|
346
|
+
For more details, see the [Sanity documentation on liveEdit documents](https://www.sanity.io/docs/content-lake/drafts).
|
|
347
|
+
|
|
325
348
|
---
|
|
326
349
|
|
|
327
350
|
### Real-Time Behavior
|
|
@@ -340,43 +363,165 @@ Any mutation to a subscribed document (even fields you don't display) will trigg
|
|
|
340
363
|
|
|
341
364
|
### Multi-Project Access
|
|
342
365
|
|
|
343
|
-
|
|
366
|
+
The SDK supports accessing documents from multiple projects and datasets simultaneously. There are two main approaches:
|
|
367
|
+
|
|
368
|
+
#### Approach 1: Specify Project/Dataset Directly in the Handle
|
|
369
|
+
|
|
370
|
+
Pass `projectId` and `dataset` directly in document handles to fetch data from specific projects (note that any `projectId` and `dataset` pair you pass must be defined in your application’s array of [SanityConfig objects](https://www.sanity.io/docs/app-sdk/sdk-configuration#d95b8773097c)):
|
|
344
371
|
|
|
345
372
|
```tsx
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
373
|
+
import {useDocument} from '@sanity/sdk-react'
|
|
374
|
+
|
|
375
|
+
function MultiProjectComponent() {
|
|
376
|
+
// Fetch from Project A
|
|
377
|
+
const {data: productA} = useDocument({
|
|
378
|
+
documentId: 'product-123',
|
|
379
|
+
documentType: 'product',
|
|
380
|
+
projectId: 'project-a',
|
|
381
|
+
dataset: 'production',
|
|
382
|
+
})
|
|
383
|
+
|
|
384
|
+
// Fetch from Project B
|
|
385
|
+
const {data: productB} = useDocument({
|
|
386
|
+
documentId: 'product-456',
|
|
387
|
+
documentType: 'product',
|
|
388
|
+
projectId: 'project-b',
|
|
389
|
+
dataset: 'staging',
|
|
390
|
+
})
|
|
391
|
+
|
|
392
|
+
return (
|
|
393
|
+
<div>
|
|
394
|
+
<h2>{productA?.title} (Project A)</h2>
|
|
395
|
+
<h2>{productB?.title} (Project B)</h2>
|
|
396
|
+
</div>
|
|
397
|
+
)
|
|
351
398
|
}
|
|
352
399
|
```
|
|
353
400
|
|
|
354
|
-
#### Use
|
|
401
|
+
#### Approach 2: Use ResourceProvider to Set Context
|
|
402
|
+
|
|
403
|
+
Wrap components in `ResourceProvider` to set default project/dataset values for all child components:
|
|
355
404
|
|
|
356
405
|
```tsx
|
|
357
406
|
// App.tsx
|
|
358
|
-
import {ResourceProvider} from '@sanity/sdk-react'
|
|
407
|
+
import {ResourceProvider, useDocument, useSanityInstance} from '@sanity/sdk-react'
|
|
359
408
|
|
|
360
|
-
|
|
409
|
+
function ProductCard({productId}: {productId: string}) {
|
|
410
|
+
// Get the current project/dataset from context
|
|
411
|
+
const {config} = useSanityInstance()
|
|
412
|
+
|
|
413
|
+
// No need to specify projectId/dataset - inherited from ResourceProvider
|
|
414
|
+
const {data: product} = useDocument({
|
|
415
|
+
documentId: productId,
|
|
416
|
+
documentType: 'product',
|
|
417
|
+
})
|
|
361
418
|
|
|
362
|
-
export function WrappedProductCard() {
|
|
363
419
|
return (
|
|
364
|
-
<
|
|
365
|
-
<
|
|
366
|
-
|
|
420
|
+
<div>
|
|
421
|
+
<h3>{product?.title}</h3>
|
|
422
|
+
<p>
|
|
423
|
+
From: {config.projectId}.{config.dataset}
|
|
424
|
+
</p>
|
|
425
|
+
</div>
|
|
367
426
|
)
|
|
368
427
|
}
|
|
369
428
|
|
|
370
|
-
|
|
371
|
-
|
|
429
|
+
export function MultiProjectApp() {
|
|
430
|
+
return (
|
|
431
|
+
<div>
|
|
432
|
+
{/* Products from Project A */}
|
|
433
|
+
<ResourceProvider projectId="project-a" dataset="production" fallback={<div>Loading...</div>}>
|
|
434
|
+
<h2>Project A Products</h2>
|
|
435
|
+
<ProductCard productId="product-123" />
|
|
436
|
+
<ProductCard productId="product-456" />
|
|
437
|
+
</ResourceProvider>
|
|
438
|
+
|
|
439
|
+
{/* Products from Project B */}
|
|
440
|
+
<ResourceProvider projectId="project-b" dataset="staging" fallback={<div>Loading...</div>}>
|
|
441
|
+
<h2>Project B Products</h2>
|
|
442
|
+
<ProductCard productId="product-789" />
|
|
443
|
+
</ResourceProvider>
|
|
444
|
+
</div>
|
|
445
|
+
)
|
|
446
|
+
}
|
|
447
|
+
```
|
|
372
448
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
449
|
+
**Key Points:**
|
|
450
|
+
|
|
451
|
+
- When using hooks that take document handles as arguments (such useDocument, useEditDocument, useQuery, etc.), the document handles’ `projectId` and `dataset` values can be explicitly set to fetch documents from arbitrary projects and datasets
|
|
452
|
+
- The ResourceProvider component is used to create a project ID and dataset context that child components will inherit from; this can negate the need to specify the project ID and dataset values for document handles in hooks called by child components
|
|
453
|
+
- Use `useSanityInstance()` to access the context configuration for the current component: `const {config} = useSanityInstance()`
|
|
454
|
+
- You can nest ResourceProvider components to create component trees with different project/dataset configurations — but be aware that, when the project ID and dataset values for document handles are _not_ specified, the project ID and dataset from the closest ResourceProvider context will be used
|
|
455
|
+
- Regardless of the approach you use, the project IDs and dataset names you reference (whether in document handles or ResourceProviders) must be enumerated in your application’s [SanityConfig objects](https://www.sanity.io/docs/app-sdk/sdk-configuration#d95b8773097c)
|
|
456
|
+
|
|
457
|
+
---
|
|
458
|
+
|
|
459
|
+
### Using the SDK inside Sanity Studio
|
|
460
|
+
|
|
461
|
+
The SDK can be embedded directly inside a Sanity Studio with zero manual configuration. Sanity Studio provides `SDKStudioContext` automatically, so `SanityApp` derives `projectId`, `dataset`, and auth from the Studio's workspace without any setup.
|
|
462
|
+
|
|
463
|
+
#### Zero-config setup (recommended)
|
|
464
|
+
|
|
465
|
+
Sanity Studio automatically provides `SDKStudioContext` to SDK components, so your SDK component needs no `config` prop at all:
|
|
466
|
+
|
|
467
|
+
```tsx
|
|
468
|
+
import {SanityApp} from '@sanity/sdk-react'
|
|
469
|
+
|
|
470
|
+
// Inside a Sanity Studio — no config needed:
|
|
471
|
+
function MyStudioTool() {
|
|
472
|
+
return (
|
|
473
|
+
<SanityApp fallback={<div>Loading...</div>}>
|
|
474
|
+
<MyComponent />
|
|
475
|
+
</SanityApp>
|
|
476
|
+
)
|
|
377
477
|
}
|
|
378
478
|
```
|
|
379
479
|
|
|
480
|
+
Under the hood, the Studio wraps its component tree with `SDKStudioContext.Provider`, passing its workspace to the SDK:
|
|
481
|
+
|
|
482
|
+
```tsx
|
|
483
|
+
import {SDKStudioContext} from '@sanity/sdk-react'
|
|
484
|
+
import {useWorkspace} from 'sanity'
|
|
485
|
+
|
|
486
|
+
// This is done automatically by Sanity Studio — shown here for reference only
|
|
487
|
+
function StudioSDKWrapper({children}) {
|
|
488
|
+
const workspace = useWorkspace()
|
|
489
|
+
return <SDKStudioContext.Provider value={workspace}>{children}</SDKStudioContext.Provider>
|
|
490
|
+
}
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
#### Explicit config takes precedence
|
|
494
|
+
|
|
495
|
+
If you pass a `config` prop to `SanityApp`, this config will take precedence over any workspace config picked up by `SDKStudioContext`:
|
|
496
|
+
|
|
497
|
+
```tsx
|
|
498
|
+
// This uses the explicit config, not the Studio workspace
|
|
499
|
+
<SanityApp config={{projectId: 'other-project', dataset: 'staging'}} fallback={<Loading />}>
|
|
500
|
+
<MyComponent />
|
|
501
|
+
</SanityApp>
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
#### Reactive auth token sync
|
|
505
|
+
|
|
506
|
+
If the Studio provides a reactive token source via `workspace.auth.token`, the SDK subscribes to it and stays in sync automatically. The Studio remains the single authority for auth — the SDK does not perform its own token refresh.
|
|
507
|
+
|
|
508
|
+
For older Studios that don't expose a token source, the SDK falls back to discovering the auth token from `localStorage` or cookie auth.
|
|
509
|
+
|
|
510
|
+
#### Migrating from `studioMode`
|
|
511
|
+
|
|
512
|
+
The `studioMode` config field is deprecated. If you are currently using it, the recommended replacement is to use the zero-config `SDKStudioContext` approach described above — which requires no `config` prop at all.
|
|
513
|
+
|
|
514
|
+
If you need to pass an explicit config, replace `studioMode` with `studio`:
|
|
515
|
+
|
|
516
|
+
```diff
|
|
517
|
+
const config: SanityConfig = {
|
|
518
|
+
projectId: 'my-project',
|
|
519
|
+
dataset: 'production',
|
|
520
|
+
- studioMode: { enabled: true },
|
|
521
|
+
+ studio: {},
|
|
522
|
+
}
|
|
523
|
+
```
|
|
524
|
+
|
|
380
525
|
---
|
|
381
526
|
|
|
382
527
|
### TypeScript & TypeGen
|