@sanity/sdk-react 0.0.0-alpha.21 → 0.0.0-alpha.23
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 +502 -3460
- package/dist/index.js +400 -465
- package/dist/index.js.map +1 -1
- package/package.json +17 -15
- package/src/_exports/index.ts +4 -5
- package/src/components/SDKProvider.test.tsx +78 -54
- package/src/components/SDKProvider.tsx +31 -26
- package/src/components/SanityApp.test.tsx +121 -15
- package/src/components/SanityApp.tsx +26 -15
- package/src/components/auth/AuthBoundary.test.tsx +32 -14
- package/src/components/auth/AuthBoundary.tsx +53 -23
- package/src/components/auth/LoginCallback.test.tsx +19 -6
- package/src/components/auth/LoginCallback.tsx +2 -11
- package/src/components/auth/LoginError.test.tsx +12 -4
- package/src/components/auth/LoginError.tsx +13 -21
- package/src/components/auth/LoginFooter.test.tsx +7 -3
- package/src/context/ResourceProvider.test.tsx +157 -0
- package/src/context/ResourceProvider.tsx +111 -0
- package/src/context/SanityInstanceContext.ts +1 -1
- package/src/hooks/auth/useLoginUrl.tsx +14 -0
- package/src/hooks/client/useClient.ts +2 -1
- package/src/hooks/comlink/useManageFavorite.test.ts +16 -8
- package/src/hooks/comlink/useManageFavorite.ts +37 -13
- package/src/hooks/comlink/useRecordDocumentHistoryEvent.test.ts +8 -4
- package/src/hooks/comlink/useRecordDocumentHistoryEvent.ts +10 -8
- package/src/hooks/context/useSanityInstance.test.tsx +157 -15
- package/src/hooks/context/useSanityInstance.ts +66 -26
- package/src/hooks/dashboard/useNavigateToStudioDocument.test.ts +13 -31
- package/src/hooks/dashboard/useNavigateToStudioDocument.ts +12 -15
- package/src/hooks/dashboard/{useStudioWorkspacesByResourceId.test.tsx → useStudioWorkspacesByProjectIdDataset.test.tsx} +13 -13
- package/src/hooks/dashboard/{useStudioWorkspacesByResourceId.ts → useStudioWorkspacesByProjectIdDataset.ts} +10 -9
- package/src/hooks/datasets/useDatasets.ts +15 -4
- package/src/hooks/document/useApplyDocumentActions.test.ts +4 -9
- package/src/hooks/document/useApplyDocumentActions.ts +6 -31
- package/src/hooks/document/useDocument.test.ts +2 -2
- package/src/hooks/document/useDocument.ts +40 -19
- package/src/hooks/document/useDocumentEvent.test.ts +2 -3
- package/src/hooks/document/useDocumentEvent.ts +7 -11
- package/src/hooks/document/useDocumentPermissions.test.ts +204 -0
- package/src/hooks/document/useDocumentPermissions.ts +31 -23
- package/src/hooks/document/useDocumentSyncStatus.ts +5 -4
- package/src/hooks/document/useEditDocument.test.ts +2 -3
- package/src/hooks/document/useEditDocument.ts +43 -29
- package/src/hooks/documents/useDocuments.test.tsx +30 -3
- package/src/hooks/documents/useDocuments.ts +20 -7
- package/src/hooks/helpers/createCallbackHook.test.tsx +2 -2
- package/src/hooks/helpers/createCallbackHook.tsx +2 -3
- package/src/hooks/helpers/createStateSourceHook.test.tsx +1 -1
- package/src/hooks/helpers/createStateSourceHook.tsx +5 -8
- package/src/hooks/paginatedDocuments/usePaginatedDocuments.test.tsx +43 -18
- package/src/hooks/paginatedDocuments/usePaginatedDocuments.ts +36 -50
- package/src/hooks/preview/usePreview.test.tsx +66 -7
- package/src/hooks/preview/usePreview.tsx +17 -12
- package/src/hooks/projection/useProjection.test.tsx +68 -3
- package/src/hooks/projection/useProjection.ts +21 -24
- package/src/hooks/projects/useProject.ts +7 -4
- package/src/hooks/query/useQuery.ts +32 -14
- package/src/hooks/users/useUsers.test.tsx +330 -0
- package/src/hooks/users/useUsers.ts +65 -52
- package/src/components/Login/LoginLinks.test.tsx +0 -90
- package/src/components/Login/LoginLinks.tsx +0 -58
- package/src/components/auth/Login.test.tsx +0 -27
- package/src/components/auth/Login.tsx +0 -39
- package/src/components/auth/LoginLayout.test.tsx +0 -19
- package/src/components/auth/LoginLayout.tsx +0 -69
- package/src/components/auth/authTestHelpers.tsx +0 -11
- package/src/context/SanityProvider.test.tsx +0 -25
- package/src/context/SanityProvider.tsx +0 -50
- package/src/hooks/auth/useLoginUrls.test.tsx +0 -68
- package/src/hooks/auth/useLoginUrls.tsx +0 -52
- package/src/hooks/users/useUsers.test.ts +0 -163
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/sdk-react",
|
|
3
|
-
"version": "0.0.0-alpha.
|
|
3
|
+
"version": "0.0.0-alpha.23",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Sanity SDK React toolkit for Content OS",
|
|
6
6
|
"keywords": [
|
|
@@ -43,33 +43,35 @@
|
|
|
43
43
|
"prettier": "@sanity/prettier-config",
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@sanity/logos": "^2.1.13",
|
|
46
|
-
"@sanity/message-protocol": "^0.
|
|
46
|
+
"@sanity/message-protocol": "^0.7.0",
|
|
47
47
|
"@sanity/types": "^3.78.1",
|
|
48
|
+
"@types/lodash-es": "^4.17.12",
|
|
49
|
+
"lodash-es": "^4.17.21",
|
|
48
50
|
"react-error-boundary": "^5.0.0",
|
|
49
51
|
"rxjs": "^7.8.1",
|
|
50
|
-
"@sanity/sdk": "0.0.0-alpha.
|
|
52
|
+
"@sanity/sdk": "0.0.0-alpha.23"
|
|
51
53
|
},
|
|
52
54
|
"devDependencies": {
|
|
53
55
|
"@sanity/browserslist-config": "^1.0.5",
|
|
54
|
-
"@sanity/client": "^6.28.
|
|
56
|
+
"@sanity/client": "^6.28.4",
|
|
55
57
|
"@sanity/comlink": "^3.0.1",
|
|
56
|
-
"@sanity/pkg-utils": "^
|
|
58
|
+
"@sanity/pkg-utils": "^7.2.2",
|
|
57
59
|
"@sanity/prettier-config": "^1.0.3",
|
|
58
60
|
"@testing-library/jest-dom": "^6.6.3",
|
|
59
|
-
"@testing-library/react": "^16.
|
|
60
|
-
"@types/react": "^19.0
|
|
61
|
-
"@types/react-dom": "^19.
|
|
61
|
+
"@testing-library/react": "^16.3.0",
|
|
62
|
+
"@types/react": "^19.1.0",
|
|
63
|
+
"@types/react-dom": "^19.1.1",
|
|
62
64
|
"@vitejs/plugin-react": "^4.3.4",
|
|
63
|
-
"@vitest/coverage-v8": "3.
|
|
64
|
-
"babel-plugin-react-compiler": "19.0.0-beta-
|
|
65
|
+
"@vitest/coverage-v8": "3.1.1",
|
|
66
|
+
"babel-plugin-react-compiler": "19.0.0-beta-e993439-20250328",
|
|
65
67
|
"eslint": "^9.22.0",
|
|
66
68
|
"jsdom": "^25.0.1",
|
|
67
69
|
"prettier": "^3.5.3",
|
|
68
|
-
"react": "^19.
|
|
69
|
-
"react-dom": "^19.
|
|
70
|
+
"react": "^19.1.0",
|
|
71
|
+
"react-dom": "^19.1.0",
|
|
70
72
|
"typescript": "^5.7.3",
|
|
71
|
-
"vite": "^6.2.
|
|
72
|
-
"vitest": "^3.
|
|
73
|
+
"vite": "^6.2.4",
|
|
74
|
+
"vitest": "^3.1.1",
|
|
73
75
|
"@repo/config-eslint": "0.0.0",
|
|
74
76
|
"@repo/config-test": "0.0.1",
|
|
75
77
|
"@repo/package.config": "0.0.1",
|
|
@@ -89,7 +91,7 @@
|
|
|
89
91
|
"build": "pkg build --strict --clean --check",
|
|
90
92
|
"clean": "rimraf dist",
|
|
91
93
|
"dev": "pkg watch",
|
|
92
|
-
"docs": "typedoc --out docs --tsconfig
|
|
94
|
+
"docs": "typedoc --out docs --tsconfig ./tsconfig.dist.json",
|
|
93
95
|
"format": "prettier --write --cache --ignore-unknown .",
|
|
94
96
|
"lint": "eslint .",
|
|
95
97
|
"test": "vitest run",
|
package/src/_exports/index.ts
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
export {AuthBoundary} from '../components/auth/AuthBoundary'
|
|
2
2
|
export {SanityApp, type SanityAppProps} from '../components/SanityApp'
|
|
3
3
|
export {SDKProvider, type SDKProviderProps} from '../components/SDKProvider'
|
|
4
|
-
export type
|
|
5
|
-
export {SanityProvider} from '../context/SanityProvider'
|
|
4
|
+
export {ResourceProvider, type ResourceProviderProps} from '../context/ResourceProvider'
|
|
6
5
|
export {useAuthState} from '../hooks/auth/useAuthState'
|
|
7
6
|
export {useAuthToken} from '../hooks/auth/useAuthToken'
|
|
8
7
|
export {useCurrentUser} from '../hooks/auth/useCurrentUser'
|
|
9
8
|
export {useDashboardOrganizationId} from '../hooks/auth/useDashboardOrganizationId'
|
|
10
9
|
export {useHandleAuthCallback} from '../hooks/auth/useHandleAuthCallback'
|
|
11
|
-
export {
|
|
10
|
+
export {useLoginUrl} from '../hooks/auth/useLoginUrl'
|
|
12
11
|
export {useLogOut} from '../hooks/auth/useLogOut'
|
|
13
12
|
export {useClient} from '../hooks/client/useClient'
|
|
14
13
|
export {
|
|
@@ -27,7 +26,7 @@ export {
|
|
|
27
26
|
} from '../hooks/comlink/useWindowConnection'
|
|
28
27
|
export {useSanityInstance} from '../hooks/context/useSanityInstance'
|
|
29
28
|
export {useNavigateToStudioDocument} from '../hooks/dashboard/useNavigateToStudioDocument'
|
|
30
|
-
export {
|
|
29
|
+
export {useStudioWorkspacesByProjectIdDataset} from '../hooks/dashboard/useStudioWorkspacesByProjectIdDataset'
|
|
31
30
|
export {useDatasets} from '../hooks/datasets/useDatasets'
|
|
32
31
|
export {useApplyDocumentActions} from '../hooks/document/useApplyDocumentActions'
|
|
33
32
|
export {useDocument} from '../hooks/document/useDocument'
|
|
@@ -58,7 +57,7 @@ export {
|
|
|
58
57
|
export {useProject} from '../hooks/projects/useProject'
|
|
59
58
|
export {type ProjectWithoutMembers, useProjects} from '../hooks/projects/useProjects'
|
|
60
59
|
export {useQuery} from '../hooks/query/useQuery'
|
|
61
|
-
export {
|
|
60
|
+
export {type UsersResult, useUsers} from '../hooks/users/useUsers'
|
|
62
61
|
export {REACT_SDK_VERSION} from '../version'
|
|
63
62
|
export {type DatasetsResponse, type SanityProject, type SanityProjectMember} from '@sanity/client'
|
|
64
63
|
export {type Status as ComlinkStatus} from '@sanity/comlink'
|
|
@@ -1,79 +1,103 @@
|
|
|
1
|
-
import * as SanitySDK from '@sanity/sdk'
|
|
2
1
|
import {render} from '@testing-library/react'
|
|
3
|
-
import
|
|
2
|
+
import React from 'react'
|
|
4
3
|
import {describe, expect, it, vi} from 'vitest'
|
|
5
4
|
|
|
6
|
-
import {type SanityProviderProps} from '../context/SanityProvider'
|
|
7
5
|
import {SDKProvider} from './SDKProvider'
|
|
8
6
|
|
|
9
|
-
// Mock
|
|
10
|
-
vi.mock('
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
7
|
+
// Mock ResourceProvider to test nesting behavior
|
|
8
|
+
vi.mock('../context/ResourceProvider', () => ({
|
|
9
|
+
ResourceProvider: ({
|
|
10
|
+
children,
|
|
11
|
+
...props
|
|
12
|
+
}: {
|
|
13
|
+
children: React.ReactNode
|
|
14
|
+
projectId?: string
|
|
15
|
+
dataset?: string
|
|
16
|
+
}) => {
|
|
17
|
+
return (
|
|
18
|
+
<div
|
|
19
|
+
data-testid="resource-provider"
|
|
20
|
+
data-config={JSON.stringify({
|
|
21
|
+
projectId: props.projectId,
|
|
22
|
+
dataset: props.dataset,
|
|
23
|
+
})}
|
|
24
|
+
>
|
|
25
|
+
{children}
|
|
26
|
+
</div>
|
|
27
|
+
)
|
|
28
|
+
},
|
|
25
29
|
}))
|
|
26
30
|
|
|
27
|
-
// Mock
|
|
31
|
+
// Mock AuthBoundary
|
|
28
32
|
vi.mock('./auth/AuthBoundary', () => ({
|
|
29
|
-
AuthBoundary: ({children}: {children: ReactNode}) =>
|
|
30
|
-
<div data-testid="auth-boundary">{children}</div>
|
|
31
|
-
|
|
33
|
+
AuthBoundary: ({children}: {children: React.ReactNode}) => {
|
|
34
|
+
return <div data-testid="auth-boundary">{children}</div>
|
|
35
|
+
},
|
|
32
36
|
}))
|
|
33
37
|
|
|
34
38
|
describe('SDKProvider', () => {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
it('renders single ResourceProvider with AuthBoundary for a single config', () => {
|
|
40
|
+
const config = {
|
|
41
|
+
projectId: 'test-project',
|
|
42
|
+
dataset: 'production',
|
|
43
|
+
}
|
|
39
44
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
<div>Test Child</div>
|
|
45
|
+
const {getAllByTestId, getByTestId} = render(
|
|
46
|
+
<SDKProvider config={[config]} fallback={<div>Loading...</div>}>
|
|
47
|
+
<div>Child Content</div>
|
|
44
48
|
</SDKProvider>,
|
|
45
49
|
)
|
|
46
50
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
it('renders children within SanityProvider and AuthBoundary', () => {
|
|
51
|
-
const {getByText, getByTestId} = render(
|
|
52
|
-
<SDKProvider sanityConfigs={[mockConfig]} fallback={<div>Fallback</div>}>
|
|
53
|
-
<div>Test Child</div>
|
|
54
|
-
</SDKProvider>,
|
|
55
|
-
)
|
|
51
|
+
// Should create a single ResourceProvider
|
|
52
|
+
const providers = getAllByTestId('resource-provider')
|
|
53
|
+
expect(providers.length).toBe(1)
|
|
56
54
|
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
const authBoundary = getByTestId('auth-boundary')
|
|
60
|
-
const childElement = getByText('Test Child')
|
|
55
|
+
// Should create an AuthBoundary inside
|
|
56
|
+
expect(getByTestId('auth-boundary')).toBeInTheDocument()
|
|
61
57
|
|
|
62
|
-
|
|
63
|
-
expect(
|
|
64
|
-
|
|
58
|
+
// Verify provider has the correct config
|
|
59
|
+
expect(JSON.parse(providers[0].getAttribute('data-config') || '{}')).toEqual({
|
|
60
|
+
projectId: 'test-project',
|
|
61
|
+
dataset: 'production',
|
|
62
|
+
})
|
|
65
63
|
})
|
|
66
64
|
|
|
67
|
-
it('
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
65
|
+
it('renders nested ResourceProviders with AuthBoundary for multiple configs', () => {
|
|
66
|
+
const configs = [
|
|
67
|
+
{
|
|
68
|
+
projectId: 'project-1',
|
|
69
|
+
dataset: 'production',
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
projectId: 'project-2',
|
|
73
|
+
dataset: 'staging',
|
|
74
|
+
},
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
const {getAllByTestId, getByTestId} = render(
|
|
78
|
+
<SDKProvider config={configs} fallback={<div>Loading...</div>}>
|
|
79
|
+
<div>Child Content</div>
|
|
71
80
|
</SDKProvider>,
|
|
72
81
|
)
|
|
73
82
|
|
|
74
|
-
|
|
75
|
-
const
|
|
83
|
+
// Should create two nested ResourceProviders
|
|
84
|
+
const providers = getAllByTestId('resource-provider')
|
|
85
|
+
expect(providers.length).toBe(2)
|
|
86
|
+
|
|
87
|
+
// Should create an AuthBoundary inside the innermost provider
|
|
88
|
+
expect(getByTestId('auth-boundary')).toBeInTheDocument()
|
|
89
|
+
|
|
90
|
+
// Verify each provider has the correct config - order is based on how SDKProvider creates nestings
|
|
91
|
+
// The first provider contains config[1]
|
|
92
|
+
expect(JSON.parse(providers[0].getAttribute('data-config') || '{}')).toEqual({
|
|
93
|
+
projectId: 'project-2',
|
|
94
|
+
dataset: 'staging',
|
|
95
|
+
})
|
|
76
96
|
|
|
77
|
-
|
|
97
|
+
// The second provider contains config[0]
|
|
98
|
+
expect(JSON.parse(providers[1].getAttribute('data-config') || '{}')).toEqual({
|
|
99
|
+
projectId: 'project-1',
|
|
100
|
+
dataset: 'production',
|
|
101
|
+
})
|
|
78
102
|
})
|
|
79
103
|
})
|
|
@@ -1,21 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {type ReactElement, type ReactNode
|
|
1
|
+
import {type SanityConfig} from '@sanity/sdk'
|
|
2
|
+
import {type ReactElement, type ReactNode} from 'react'
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import {AuthBoundary} from './auth/AuthBoundary'
|
|
6
|
-
|
|
7
|
-
const DEFAULT_FALLBACK = (
|
|
8
|
-
<>
|
|
9
|
-
Warning: No fallback provided. Please supply a fallback prop to ensure proper Suspense handling.
|
|
10
|
-
</>
|
|
11
|
-
)
|
|
4
|
+
import {ResourceProvider} from '../context/ResourceProvider'
|
|
5
|
+
import {AuthBoundary, type AuthBoundaryProps} from './auth/AuthBoundary'
|
|
12
6
|
|
|
13
7
|
/**
|
|
14
8
|
* @internal
|
|
15
9
|
*/
|
|
16
|
-
export interface SDKProviderProps {
|
|
10
|
+
export interface SDKProviderProps extends AuthBoundaryProps {
|
|
17
11
|
children: ReactNode
|
|
18
|
-
|
|
12
|
+
config: SanityConfig | SanityConfig[]
|
|
19
13
|
fallback: ReactNode
|
|
20
14
|
}
|
|
21
15
|
|
|
@@ -23,20 +17,31 @@ export interface SDKProviderProps {
|
|
|
23
17
|
* @internal
|
|
24
18
|
*
|
|
25
19
|
* Top-level context provider that provides access to the Sanity SDK.
|
|
20
|
+
* Creates a hierarchy of ResourceProviders, each providing a SanityInstance that can be
|
|
21
|
+
* accessed by hooks. The first configuration in the array becomes the default instance.
|
|
26
22
|
*/
|
|
27
|
-
export function SDKProvider({
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
export function SDKProvider({
|
|
24
|
+
children,
|
|
25
|
+
config,
|
|
26
|
+
fallback,
|
|
27
|
+
...props
|
|
28
|
+
}: SDKProviderProps): ReactElement {
|
|
29
|
+
// reverse because we want the first config to be the default, but the
|
|
30
|
+
// ResourceProvider nesting makes the last one the default
|
|
31
|
+
const configs = (Array.isArray(config) ? config : [config]).slice().reverse()
|
|
32
|
+
|
|
33
|
+
// Create a nested structure of ResourceProviders for each config
|
|
34
|
+
const createNestedProviders = (index: number): ReactElement => {
|
|
35
|
+
if (index >= configs.length) {
|
|
36
|
+
return <AuthBoundary {...props}>{children}</AuthBoundary>
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<ResourceProvider {...configs[index]} fallback={fallback}>
|
|
41
|
+
{createNestedProviders(index + 1)}
|
|
42
|
+
</ResourceProvider>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
31
45
|
|
|
32
|
-
return (
|
|
33
|
-
<SanityProvider sanityInstances={sanityInstances}>
|
|
34
|
-
{/* This Suspense boundary is necessary because some hooks may suspend.
|
|
35
|
-
It ensures that the Sanity instance state created above remains stable
|
|
36
|
-
before rendering the AuthBoundary and its children. */}
|
|
37
|
-
<Suspense fallback={fallback ?? DEFAULT_FALLBACK}>
|
|
38
|
-
<AuthBoundary>{children}</AuthBoundary>
|
|
39
|
-
</Suspense>
|
|
40
|
-
</SanityProvider>
|
|
41
|
-
)
|
|
46
|
+
return createNestedProviders(0)
|
|
42
47
|
}
|
|
@@ -3,9 +3,16 @@ import {render, screen} from '@testing-library/react'
|
|
|
3
3
|
import {describe, expect, it, vi} from 'vitest'
|
|
4
4
|
|
|
5
5
|
import {SanityApp} from './SanityApp'
|
|
6
|
+
import {SDKProvider} from './SDKProvider'
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
// Mock SDKProvider to verify it's being used correctly
|
|
9
|
+
vi.mock('./SDKProvider', () => ({
|
|
10
|
+
SDKProvider: vi.fn(() => <div data-testid="sdk-provider">SDKProvider</div>),
|
|
11
|
+
}))
|
|
12
|
+
|
|
13
|
+
// Mock useEffect to prevent redirect logic from running in tests
|
|
14
|
+
vi.mock('react', async () => {
|
|
15
|
+
const actual = await vi.importActual('react')
|
|
9
16
|
return {
|
|
10
17
|
...actual,
|
|
11
18
|
createSanityInstance: vi.fn(() => ({
|
|
@@ -36,20 +43,104 @@ vi.mock('../hooks/auth/useAuthState', () => ({
|
|
|
36
43
|
}))
|
|
37
44
|
|
|
38
45
|
describe('SanityApp', () => {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
46
|
+
beforeEach(() => {
|
|
47
|
+
vi.clearAllMocks()
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('renders SDKProvider with a single config', () => {
|
|
51
|
+
const singleConfig = {
|
|
52
|
+
projectId: 'test-project',
|
|
53
|
+
dataset: 'production',
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
render(
|
|
57
|
+
<SanityApp config={singleConfig} fallback={<div>Loading...</div>}>
|
|
58
|
+
<div>Child Content</div>
|
|
59
|
+
</SanityApp>,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
// Check that the SDKProvider is rendered
|
|
63
|
+
expect(screen.getByTestId('sdk-provider')).toBeInTheDocument()
|
|
64
|
+
|
|
65
|
+
// Verify SDKProvider was called with the correct props
|
|
66
|
+
const sdkProviderCalls = vi.mocked(SDKProvider).mock.calls
|
|
67
|
+
expect(sdkProviderCalls.length).toBe(1)
|
|
68
|
+
|
|
69
|
+
const [props] = sdkProviderCalls[0]
|
|
70
|
+
const {config} = props
|
|
71
|
+
|
|
72
|
+
// Config is now passed directly as an object for single configs
|
|
73
|
+
expect(config).toEqual(singleConfig)
|
|
74
|
+
expect(props.fallback).toBeTruthy()
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('renders SDKProvider with multiple configs in original order', () => {
|
|
78
|
+
const multipleConfigs = [
|
|
79
|
+
{
|
|
80
|
+
projectId: 'project-1',
|
|
81
|
+
dataset: 'production',
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
projectId: 'project-2',
|
|
85
|
+
dataset: 'staging',
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
projectId: 'project-3',
|
|
89
|
+
dataset: 'development',
|
|
90
|
+
},
|
|
91
|
+
]
|
|
92
|
+
|
|
93
|
+
render(
|
|
94
|
+
<SanityApp config={multipleConfigs} fallback={<div>Loading...</div>}>
|
|
95
|
+
<div>Child Content</div>
|
|
96
|
+
</SanityApp>,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
// Check that the SDKProvider is rendered
|
|
100
|
+
expect(screen.getByTestId('sdk-provider')).toBeInTheDocument()
|
|
101
|
+
|
|
102
|
+
// Verify SDKProvider was called with the correct props
|
|
103
|
+
const sdkProviderCalls = vi.mocked(SDKProvider).mock.calls
|
|
104
|
+
expect(sdkProviderCalls.length).toBe(1)
|
|
105
|
+
|
|
106
|
+
const [props] = sdkProviderCalls[0]
|
|
107
|
+
const {config} = props
|
|
108
|
+
|
|
109
|
+
// Config should be passed directly to SDKProvider
|
|
110
|
+
expect(config).toEqual(multipleConfigs)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('supports legacy sanityConfigs prop', () => {
|
|
114
|
+
const legacyConfigs = [
|
|
115
|
+
{
|
|
116
|
+
projectId: 'legacy-project-1',
|
|
117
|
+
dataset: 'production',
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
projectId: 'legacy-project-2',
|
|
121
|
+
dataset: 'staging',
|
|
122
|
+
},
|
|
123
|
+
]
|
|
43
124
|
|
|
44
|
-
it('renders children correctly', async () => {
|
|
45
|
-
const testMessage = 'Test Child Component'
|
|
46
125
|
render(
|
|
47
|
-
|
|
48
|
-
|
|
126
|
+
// @ts-expect-error purposefully using the deprecated prop
|
|
127
|
+
<SanityApp sanityConfigs={legacyConfigs} fallback={<div>Loading...</div>}>
|
|
128
|
+
<div>Child Content</div>
|
|
49
129
|
</SanityApp>,
|
|
50
130
|
)
|
|
51
131
|
|
|
52
|
-
|
|
132
|
+
// Check that the SDKProvider is rendered
|
|
133
|
+
expect(screen.getByTestId('sdk-provider')).toBeInTheDocument()
|
|
134
|
+
|
|
135
|
+
// Verify SDKProvider was called with the correct props
|
|
136
|
+
const sdkProviderCalls = vi.mocked(SDKProvider).mock.calls
|
|
137
|
+
expect(sdkProviderCalls.length).toBe(1)
|
|
138
|
+
|
|
139
|
+
const [props] = sdkProviderCalls[0]
|
|
140
|
+
const {config} = props
|
|
141
|
+
|
|
142
|
+
// Config should be passed to SDKProvider in the same order
|
|
143
|
+
expect(config).toEqual(legacyConfigs)
|
|
53
144
|
})
|
|
54
145
|
|
|
55
146
|
it('handles iframe environment correctly', async () => {
|
|
@@ -57,6 +148,11 @@ describe('SanityApp', () => {
|
|
|
57
148
|
const originalTop = window.top
|
|
58
149
|
const originalSelf = window.self
|
|
59
150
|
|
|
151
|
+
const mockSanityConfig: SanityConfig = {
|
|
152
|
+
projectId: 'test-project',
|
|
153
|
+
dataset: 'test-dataset',
|
|
154
|
+
}
|
|
155
|
+
|
|
60
156
|
const mockTop = {}
|
|
61
157
|
Object.defineProperty(window, 'top', {
|
|
62
158
|
value: mockTop,
|
|
@@ -68,7 +164,7 @@ describe('SanityApp', () => {
|
|
|
68
164
|
})
|
|
69
165
|
|
|
70
166
|
render(
|
|
71
|
-
<SanityApp
|
|
167
|
+
<SanityApp config={[mockSanityConfig]} fallback={<div>Fallback</div>}>
|
|
72
168
|
<div>Test Child</div>
|
|
73
169
|
</SanityApp>,
|
|
74
170
|
)
|
|
@@ -98,13 +194,18 @@ describe('SanityApp', () => {
|
|
|
98
194
|
href: 'http://sanity-test.app',
|
|
99
195
|
}
|
|
100
196
|
|
|
197
|
+
const mockSanityConfig: SanityConfig = {
|
|
198
|
+
projectId: 'test-project',
|
|
199
|
+
dataset: 'test-dataset',
|
|
200
|
+
}
|
|
201
|
+
|
|
101
202
|
Object.defineProperty(window, 'location', {
|
|
102
203
|
value: mockLocation,
|
|
103
204
|
writable: true,
|
|
104
205
|
})
|
|
105
206
|
|
|
106
207
|
render(
|
|
107
|
-
<SanityApp
|
|
208
|
+
<SanityApp config={[mockSanityConfig]} fallback={<div>Fallback</div>}>
|
|
108
209
|
<div>Test Child</div>
|
|
109
210
|
</SanityApp>,
|
|
110
211
|
)
|
|
@@ -113,7 +214,7 @@ describe('SanityApp', () => {
|
|
|
113
214
|
await new Promise((resolve) => setTimeout(resolve, 1010))
|
|
114
215
|
|
|
115
216
|
// Add assertions based on your iframe-specific behavior
|
|
116
|
-
expect(mockLocation.replace).toHaveBeenCalledWith('https://
|
|
217
|
+
expect(mockLocation.replace).toHaveBeenCalledWith('https://sanity.io/welcome')
|
|
117
218
|
|
|
118
219
|
// Clean up the mock
|
|
119
220
|
Object.defineProperty(window, 'location', {
|
|
@@ -125,6 +226,11 @@ describe('SanityApp', () => {
|
|
|
125
226
|
it('does not redirect to core if not inside iframe and local url', async () => {
|
|
126
227
|
const originalLocation = window.location
|
|
127
228
|
|
|
229
|
+
const mockSanityConfig: SanityConfig = {
|
|
230
|
+
projectId: 'test-project',
|
|
231
|
+
dataset: 'test-dataset',
|
|
232
|
+
}
|
|
233
|
+
|
|
128
234
|
const mockLocation = {
|
|
129
235
|
replace: vi.fn(),
|
|
130
236
|
href: 'http://localhost:3000',
|
|
@@ -136,7 +242,7 @@ describe('SanityApp', () => {
|
|
|
136
242
|
})
|
|
137
243
|
|
|
138
244
|
render(
|
|
139
|
-
<SanityApp
|
|
245
|
+
<SanityApp config={[mockSanityConfig]} fallback={<div>Fallback</div>}>
|
|
140
246
|
<div>Test Child</div>
|
|
141
247
|
</SanityApp>,
|
|
142
248
|
)
|
|
@@ -8,12 +8,14 @@ import {isInIframe, isLocalUrl} from './utils'
|
|
|
8
8
|
* @public
|
|
9
9
|
*/
|
|
10
10
|
export interface SanityAppProps {
|
|
11
|
-
|
|
11
|
+
config: SanityConfig | SanityConfig[]
|
|
12
|
+
/** @deprecated use the `config` prop instead. */
|
|
13
|
+
sanityConfigs?: SanityConfig[]
|
|
12
14
|
children: React.ReactNode
|
|
13
15
|
fallback: React.ReactNode
|
|
14
16
|
}
|
|
15
17
|
|
|
16
|
-
const
|
|
18
|
+
const REDIRECT_URL = 'https://sanity.io/welcome'
|
|
17
19
|
|
|
18
20
|
/**
|
|
19
21
|
* @public
|
|
@@ -22,6 +24,9 @@ const CORE_URL = 'https://core.sanity.io'
|
|
|
22
24
|
* as well as application context and state which is used by the Sanity React hooks. Your application
|
|
23
25
|
* must be wrapped with the SanityApp component to function properly.
|
|
24
26
|
*
|
|
27
|
+
* SanityApp creates a hierarchy of ResourceProviders, each providing a SanityInstance that can be
|
|
28
|
+
* accessed by hooks. The first configuration in the array becomes the default instance.
|
|
29
|
+
*
|
|
25
30
|
* @param props - Your Sanity configuration and the React children to render
|
|
26
31
|
* @returns Your Sanity application, integrated with your Sanity configuration and application context
|
|
27
32
|
*
|
|
@@ -32,16 +37,14 @@ const CORE_URL = 'https://core.sanity.io'
|
|
|
32
37
|
* import MyAppRoot from './Root'
|
|
33
38
|
*
|
|
34
39
|
* // Single project configuration
|
|
35
|
-
* const
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* },
|
|
40
|
-
* ]
|
|
40
|
+
* const mySanityConfig = {
|
|
41
|
+
* projectId: 'my-project-id',
|
|
42
|
+
* dataset: 'production',
|
|
43
|
+
* }
|
|
41
44
|
*
|
|
42
45
|
* // Or multiple project configurations
|
|
43
46
|
* const multipleConfigs = [
|
|
44
|
-
* // Configuration for your main project. This will be used as the default project for
|
|
47
|
+
* // Configuration for your main project. This will be used as the default project for hooks.
|
|
45
48
|
* {
|
|
46
49
|
* projectId: 'marketing-website-project',
|
|
47
50
|
* dataset: 'production',
|
|
@@ -60,14 +63,22 @@ const CORE_URL = 'https://core.sanity.io'
|
|
|
60
63
|
*
|
|
61
64
|
* export default function MyApp() {
|
|
62
65
|
* return (
|
|
63
|
-
* <SanityApp
|
|
66
|
+
* <SanityApp config={mySanityConfig} fallback={<LoadingSpinner />}>
|
|
64
67
|
* <MyAppRoot />
|
|
65
68
|
* </SanityApp>
|
|
66
69
|
* )
|
|
67
70
|
* }
|
|
68
71
|
* ```
|
|
69
72
|
*/
|
|
70
|
-
export function SanityApp({
|
|
73
|
+
export function SanityApp({
|
|
74
|
+
children,
|
|
75
|
+
fallback,
|
|
76
|
+
config,
|
|
77
|
+
sanityConfigs,
|
|
78
|
+
...props
|
|
79
|
+
}: SanityAppProps): ReactElement {
|
|
80
|
+
const configs = config ?? sanityConfigs ?? []
|
|
81
|
+
|
|
71
82
|
useEffect(() => {
|
|
72
83
|
let timeout: NodeJS.Timeout | undefined
|
|
73
84
|
|
|
@@ -75,15 +86,15 @@ export function SanityApp({sanityConfigs, children, fallback}: SanityAppProps):
|
|
|
75
86
|
// If the app is not running in an iframe and is not a local url, redirect to core.
|
|
76
87
|
timeout = setTimeout(() => {
|
|
77
88
|
// eslint-disable-next-line no-console
|
|
78
|
-
console.warn('Redirecting to core',
|
|
79
|
-
window.location.replace(
|
|
89
|
+
console.warn('Redirecting to core', REDIRECT_URL)
|
|
90
|
+
window.location.replace(REDIRECT_URL)
|
|
80
91
|
}, 1000)
|
|
81
92
|
}
|
|
82
93
|
return () => clearTimeout(timeout)
|
|
83
|
-
}, [
|
|
94
|
+
}, [])
|
|
84
95
|
|
|
85
96
|
return (
|
|
86
|
-
<SDKProvider
|
|
97
|
+
<SDKProvider {...props} fallback={fallback} config={configs}>
|
|
87
98
|
{children}
|
|
88
99
|
</SDKProvider>
|
|
89
100
|
)
|