@sanity/sdk-react 0.0.0-alpha.3 → 0.0.0-alpha.30
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 +6 -100
- package/dist/index.d.ts +2390 -2
- package/dist/index.js +1119 -2
- package/dist/index.js.map +1 -1
- package/package.json +35 -49
- package/src/_exports/index.ts +2 -10
- package/src/_exports/sdk-react.ts +73 -0
- package/src/components/SDKProvider.test.tsx +103 -0
- package/src/components/SDKProvider.tsx +52 -0
- package/src/components/SanityApp.test.tsx +244 -0
- package/src/components/SanityApp.tsx +106 -0
- package/src/components/auth/AuthBoundary.test.tsx +204 -29
- package/src/components/auth/AuthBoundary.tsx +96 -19
- package/src/components/auth/ConfigurationError.ts +22 -0
- package/src/components/auth/LoginCallback.test.tsx +22 -24
- package/src/components/auth/LoginCallback.tsx +6 -16
- package/src/components/auth/LoginError.test.tsx +11 -18
- package/src/components/auth/LoginError.tsx +43 -25
- package/src/components/utils.ts +22 -0
- package/src/context/ResourceProvider.test.tsx +157 -0
- package/src/context/ResourceProvider.tsx +111 -0
- package/src/context/SanityInstanceContext.ts +4 -0
- package/src/hooks/_synchronous-groq-js.mjs +4 -0
- package/src/hooks/auth/useAuthState.tsx +4 -5
- package/src/hooks/auth/useAuthToken.tsx +1 -1
- package/src/hooks/auth/useCurrentUser.tsx +28 -4
- package/src/hooks/auth/useDashboardOrganizationId.test.tsx +42 -0
- package/src/hooks/auth/useDashboardOrganizationId.tsx +30 -0
- package/src/hooks/auth/useHandleAuthCallback.test.tsx +16 -0
- package/src/hooks/auth/{useHandleCallback.tsx → useHandleAuthCallback.tsx} +7 -6
- package/src/hooks/auth/useLogOut.test.tsx +2 -2
- package/src/hooks/auth/useLogOut.tsx +1 -1
- package/src/hooks/auth/useLoginUrl.tsx +14 -0
- package/src/hooks/auth/useVerifyOrgProjects.test.tsx +136 -0
- package/src/hooks/auth/useVerifyOrgProjects.tsx +48 -0
- package/src/hooks/client/useClient.ts +13 -33
- package/src/hooks/comlink/useFrameConnection.test.tsx +167 -0
- package/src/hooks/comlink/useFrameConnection.ts +107 -0
- package/src/hooks/comlink/useManageFavorite.test.ts +368 -0
- package/src/hooks/comlink/useManageFavorite.ts +210 -0
- package/src/hooks/comlink/useRecordDocumentHistoryEvent.test.ts +85 -0
- package/src/hooks/comlink/useRecordDocumentHistoryEvent.ts +115 -0
- package/src/hooks/comlink/useWindowConnection.test.ts +135 -0
- package/src/hooks/comlink/useWindowConnection.ts +123 -0
- package/src/hooks/context/useSanityInstance.test.tsx +157 -15
- package/src/hooks/context/useSanityInstance.ts +68 -11
- package/src/hooks/dashboard/useNavigateToStudioDocument.test.ts +276 -0
- package/src/hooks/dashboard/useNavigateToStudioDocument.ts +139 -0
- package/src/hooks/dashboard/useStudioWorkspacesByProjectIdDataset.test.tsx +291 -0
- package/src/hooks/dashboard/useStudioWorkspacesByProjectIdDataset.ts +101 -0
- package/src/hooks/datasets/useDatasets.test.ts +80 -0
- package/src/hooks/datasets/useDatasets.ts +52 -0
- package/src/hooks/document/useApplyDocumentActions.test.ts +20 -0
- package/src/hooks/document/useApplyDocumentActions.ts +124 -0
- package/src/hooks/document/useDocument.test.ts +118 -0
- package/src/hooks/document/useDocument.ts +212 -0
- package/src/hooks/document/useDocumentEvent.test.ts +62 -0
- package/src/hooks/document/useDocumentEvent.ts +94 -0
- package/src/hooks/document/useDocumentPermissions.test.ts +204 -0
- package/src/hooks/document/useDocumentPermissions.ts +131 -0
- package/src/hooks/document/useDocumentSyncStatus.test.ts +23 -0
- package/src/hooks/document/useDocumentSyncStatus.ts +61 -0
- package/src/hooks/document/useEditDocument.test.ts +196 -0
- package/src/hooks/document/useEditDocument.ts +314 -0
- package/src/hooks/documents/useDocuments.test.tsx +179 -0
- package/src/hooks/documents/useDocuments.ts +300 -0
- package/src/hooks/helpers/createCallbackHook.test.tsx +2 -2
- package/src/hooks/helpers/createCallbackHook.tsx +1 -1
- package/src/hooks/helpers/createStateSourceHook.test.tsx +67 -1
- package/src/hooks/helpers/createStateSourceHook.tsx +27 -11
- package/src/hooks/paginatedDocuments/usePaginatedDocuments.test.tsx +284 -0
- package/src/hooks/paginatedDocuments/usePaginatedDocuments.ts +353 -0
- package/src/hooks/preview/usePreview.test.tsx +85 -17
- package/src/hooks/preview/usePreview.tsx +81 -22
- package/src/hooks/projection/useProjection.test.tsx +283 -0
- package/src/hooks/projection/useProjection.ts +232 -0
- package/src/hooks/projects/useProject.test.ts +80 -0
- package/src/hooks/projects/useProject.ts +51 -0
- package/src/hooks/projects/useProjects.test.ts +77 -0
- package/src/hooks/projects/useProjects.ts +45 -0
- package/src/hooks/query/useQuery.test.tsx +188 -0
- package/src/hooks/query/useQuery.ts +193 -0
- package/src/hooks/releases/useActiveReleases.test.tsx +84 -0
- package/src/hooks/releases/useActiveReleases.ts +39 -0
- package/src/hooks/releases/usePerspective.test.tsx +120 -0
- package/src/hooks/releases/usePerspective.ts +49 -0
- package/src/hooks/users/useUsers.test.tsx +330 -0
- package/src/hooks/users/useUsers.ts +120 -0
- package/src/utils/getEnv.ts +21 -0
- package/src/version.ts +8 -0
- package/src/vite-env.d.ts +10 -0
- package/dist/_chunks-es/useLogOut.js +0 -44
- package/dist/_chunks-es/useLogOut.js.map +0 -1
- package/dist/assets/bundle-CcAyERuZ.css +0 -11
- package/dist/components.d.ts +0 -259
- package/dist/components.js +0 -301
- package/dist/components.js.map +0 -1
- package/dist/hooks.d.ts +0 -186
- package/dist/hooks.js +0 -81
- package/dist/hooks.js.map +0 -1
- package/src/_exports/components.ts +0 -13
- package/src/_exports/hooks.ts +0 -9
- package/src/components/DocumentGridLayout/DocumentGridLayout.stories.tsx +0 -113
- package/src/components/DocumentGridLayout/DocumentGridLayout.test.tsx +0 -42
- package/src/components/DocumentGridLayout/DocumentGridLayout.tsx +0 -21
- package/src/components/DocumentListLayout/DocumentListLayout.stories.tsx +0 -105
- package/src/components/DocumentListLayout/DocumentListLayout.test.tsx +0 -42
- package/src/components/DocumentListLayout/DocumentListLayout.tsx +0 -12
- package/src/components/DocumentPreviewLayout/DocumentPreviewLayout.md +0 -49
- package/src/components/DocumentPreviewLayout/DocumentPreviewLayout.stories.tsx +0 -39
- package/src/components/DocumentPreviewLayout/DocumentPreviewLayout.test.tsx +0 -30
- package/src/components/DocumentPreviewLayout/DocumentPreviewLayout.tsx +0 -171
- package/src/components/Login/LoginLinks.test.tsx +0 -100
- package/src/components/Login/LoginLinks.tsx +0 -73
- package/src/components/auth/Login.test.tsx +0 -41
- package/src/components/auth/Login.tsx +0 -45
- package/src/components/auth/LoginFooter.test.tsx +0 -29
- package/src/components/auth/LoginFooter.tsx +0 -65
- package/src/components/auth/LoginLayout.test.tsx +0 -33
- package/src/components/auth/LoginLayout.tsx +0 -81
- package/src/components/context/SanityProvider.test.tsx +0 -25
- package/src/components/context/SanityProvider.tsx +0 -42
- package/src/css/css.config.js +0 -220
- package/src/css/paramour.css +0 -2347
- package/src/css/styles.css +0 -11
- package/src/hooks/auth/useHandleCallback.test.tsx +0 -16
- package/src/hooks/auth/useLoginUrls.test.tsx +0 -68
- package/src/hooks/auth/useLoginUrls.tsx +0 -51
- package/src/hooks/client/useClient.test.tsx +0 -130
- package/src/hooks/documentCollection/useDocuments.test.ts +0 -130
- package/src/hooks/documentCollection/useDocuments.ts +0 -87
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.30",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Sanity SDK React toolkit for Content OS",
|
|
6
6
|
"keywords": [
|
|
@@ -30,31 +30,11 @@
|
|
|
30
30
|
"import": "./dist/index.js",
|
|
31
31
|
"default": "./dist/index.js"
|
|
32
32
|
},
|
|
33
|
-
"./components": {
|
|
34
|
-
"source": "./src/_exports/components.ts",
|
|
35
|
-
"import": "./dist/components.js",
|
|
36
|
-
"default": "./dist/components.js"
|
|
37
|
-
},
|
|
38
|
-
"./hooks": {
|
|
39
|
-
"source": "./src/_exports/hooks.ts",
|
|
40
|
-
"import": "./dist/hooks.js",
|
|
41
|
-
"default": "./dist/hooks.js"
|
|
42
|
-
},
|
|
43
33
|
"./package.json": "./package.json"
|
|
44
34
|
},
|
|
45
35
|
"main": "./dist/index.js",
|
|
46
36
|
"module": "./dist/index.js",
|
|
47
37
|
"types": "./dist/index.d.ts",
|
|
48
|
-
"typesVersions": {
|
|
49
|
-
"*": {
|
|
50
|
-
"components": [
|
|
51
|
-
"./dist/components.d.ts"
|
|
52
|
-
],
|
|
53
|
-
"hooks": [
|
|
54
|
-
"./dist/hooks.d.ts"
|
|
55
|
-
]
|
|
56
|
-
}
|
|
57
|
-
},
|
|
58
38
|
"files": [
|
|
59
39
|
"dist",
|
|
60
40
|
"src"
|
|
@@ -62,37 +42,43 @@
|
|
|
62
42
|
"browserslist": "extends @sanity/browserslist-config",
|
|
63
43
|
"prettier": "@sanity/prettier-config",
|
|
64
44
|
"dependencies": {
|
|
65
|
-
"@
|
|
66
|
-
"@sanity/
|
|
67
|
-
"@sanity/
|
|
68
|
-
"@
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
45
|
+
"@sanity/client": "^7.0.0",
|
|
46
|
+
"@sanity/message-protocol": "^0.8.0",
|
|
47
|
+
"@sanity/types": "^3.83.0",
|
|
48
|
+
"@types/lodash-es": "^4.17.12",
|
|
49
|
+
"groq": "3.86.2-experimental.0",
|
|
50
|
+
"lodash-es": "^4.17.21",
|
|
51
|
+
"react-compiler-runtime": "19.0.0-beta-ebf51a3-20250411",
|
|
52
|
+
"react-error-boundary": "^5.0.0",
|
|
53
|
+
"rxjs": "^7.8.2",
|
|
54
|
+
"@sanity/sdk": "0.0.0-alpha.30"
|
|
74
55
|
},
|
|
75
56
|
"devDependencies": {
|
|
76
|
-
"@sanity/
|
|
77
|
-
"@sanity/
|
|
57
|
+
"@sanity/browserslist-config": "^1.0.5",
|
|
58
|
+
"@sanity/comlink": "^3.0.2",
|
|
59
|
+
"@sanity/pkg-utils": "^7.2.2",
|
|
78
60
|
"@sanity/prettier-config": "^1.0.3",
|
|
79
|
-
"@storybook/react": "^8.4.7",
|
|
80
61
|
"@testing-library/jest-dom": "^6.6.3",
|
|
81
|
-
"@testing-library/react": "^16.
|
|
82
|
-
"@types/react": "^19.
|
|
83
|
-
"@types/react-dom": "^19.
|
|
84
|
-
"@vitejs/plugin-react": "^4.
|
|
85
|
-
"@vitest/coverage-v8": "
|
|
86
|
-
"
|
|
62
|
+
"@testing-library/react": "^16.3.0",
|
|
63
|
+
"@types/react": "^19.1.2",
|
|
64
|
+
"@types/react-dom": "^19.1.3",
|
|
65
|
+
"@vitejs/plugin-react": "^4.4.1",
|
|
66
|
+
"@vitest/coverage-v8": "3.1.2",
|
|
67
|
+
"babel-plugin-react-compiler": "19.1.0-rc.1",
|
|
68
|
+
"eslint": "^9.22.0",
|
|
87
69
|
"jsdom": "^25.0.1",
|
|
88
|
-
"prettier": "^3.
|
|
89
|
-
"react": "^19.
|
|
90
|
-
"react-dom": "^19.
|
|
91
|
-
"
|
|
92
|
-
"
|
|
93
|
-
"
|
|
70
|
+
"prettier": "^3.5.3",
|
|
71
|
+
"react": "^19.1.0",
|
|
72
|
+
"react-dom": "^19.1.0",
|
|
73
|
+
"rollup-plugin-visualizer": "^5.14.0",
|
|
74
|
+
"typescript": "^5.8.3",
|
|
75
|
+
"vite": "^6.3.4",
|
|
76
|
+
"vitest": "^3.1.2",
|
|
94
77
|
"@repo/config-eslint": "0.0.0",
|
|
95
|
-
"@repo/
|
|
78
|
+
"@repo/config-test": "0.0.1",
|
|
79
|
+
"@repo/package.bundle": "3.82.0",
|
|
80
|
+
"@repo/package.config": "0.0.1",
|
|
81
|
+
"@repo/tsconfig": "0.0.1"
|
|
96
82
|
},
|
|
97
83
|
"peerDependencies": {
|
|
98
84
|
"react": "^18.0.0 || ^19.0.0",
|
|
@@ -102,16 +88,16 @@
|
|
|
102
88
|
"node": ">=20.0.0"
|
|
103
89
|
},
|
|
104
90
|
"publishConfig": {
|
|
105
|
-
"access": "
|
|
91
|
+
"access": "public"
|
|
106
92
|
},
|
|
107
93
|
"scripts": {
|
|
108
94
|
"build": "pkg build --strict --clean --check",
|
|
95
|
+
"build:bundle": "vite build --configLoader runner --config package.bundle.ts",
|
|
109
96
|
"clean": "rimraf dist",
|
|
110
97
|
"dev": "pkg watch",
|
|
111
|
-
"docs": "typedoc --
|
|
98
|
+
"docs": "typedoc --json docs/typedoc.json --tsconfig ./tsconfig.dist.json",
|
|
112
99
|
"format": "prettier --write --cache --ignore-unknown .",
|
|
113
100
|
"lint": "eslint .",
|
|
114
|
-
"paramour": "npx paramour --config=./src/css/css.config.js --output=./src/css/paramour.css",
|
|
115
101
|
"test": "vitest run",
|
|
116
102
|
"test:coverage": "vitest run --coverage",
|
|
117
103
|
"test:watch": "vitest",
|
package/src/_exports/index.ts
CHANGED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module exports
|
|
3
|
+
*/
|
|
4
|
+
export {AuthBoundary, type AuthBoundaryProps} from '../components/auth/AuthBoundary'
|
|
5
|
+
export {SanityApp, type SanityAppProps} from '../components/SanityApp'
|
|
6
|
+
export {SDKProvider, type SDKProviderProps} from '../components/SDKProvider'
|
|
7
|
+
export {ResourceProvider, type ResourceProviderProps} from '../context/ResourceProvider'
|
|
8
|
+
export {useAuthState} from '../hooks/auth/useAuthState'
|
|
9
|
+
export {useAuthToken} from '../hooks/auth/useAuthToken'
|
|
10
|
+
export {useCurrentUser} from '../hooks/auth/useCurrentUser'
|
|
11
|
+
export {useDashboardOrganizationId} from '../hooks/auth/useDashboardOrganizationId'
|
|
12
|
+
export {useHandleAuthCallback} from '../hooks/auth/useHandleAuthCallback'
|
|
13
|
+
export {useLoginUrl} from '../hooks/auth/useLoginUrl'
|
|
14
|
+
export {useLogOut} from '../hooks/auth/useLogOut'
|
|
15
|
+
export {useVerifyOrgProjects} from '../hooks/auth/useVerifyOrgProjects'
|
|
16
|
+
export {useClient} from '../hooks/client/useClient'
|
|
17
|
+
export {
|
|
18
|
+
type FrameConnection,
|
|
19
|
+
type FrameMessageHandler as MessageHandler,
|
|
20
|
+
useFrameConnection,
|
|
21
|
+
type UseFrameConnectionOptions,
|
|
22
|
+
} from '../hooks/comlink/useFrameConnection'
|
|
23
|
+
export {useManageFavorite} from '../hooks/comlink/useManageFavorite'
|
|
24
|
+
export {useRecordDocumentHistoryEvent} from '../hooks/comlink/useRecordDocumentHistoryEvent'
|
|
25
|
+
export {
|
|
26
|
+
useWindowConnection,
|
|
27
|
+
type UseWindowConnectionOptions,
|
|
28
|
+
type WindowConnection,
|
|
29
|
+
type WindowMessageHandler,
|
|
30
|
+
} from '../hooks/comlink/useWindowConnection'
|
|
31
|
+
export {useSanityInstance} from '../hooks/context/useSanityInstance'
|
|
32
|
+
export {
|
|
33
|
+
type NavigateToStudioResult,
|
|
34
|
+
useNavigateToStudioDocument,
|
|
35
|
+
} from '../hooks/dashboard/useNavigateToStudioDocument'
|
|
36
|
+
export {useStudioWorkspacesByProjectIdDataset} from '../hooks/dashboard/useStudioWorkspacesByProjectIdDataset'
|
|
37
|
+
export {useDatasets} from '../hooks/datasets/useDatasets'
|
|
38
|
+
export {useApplyDocumentActions} from '../hooks/document/useApplyDocumentActions'
|
|
39
|
+
export {useDocument} from '../hooks/document/useDocument'
|
|
40
|
+
export {useDocumentEvent} from '../hooks/document/useDocumentEvent'
|
|
41
|
+
export {useDocumentPermissions} from '../hooks/document/useDocumentPermissions'
|
|
42
|
+
export {useDocumentSyncStatus} from '../hooks/document/useDocumentSyncStatus'
|
|
43
|
+
export {useEditDocument} from '../hooks/document/useEditDocument'
|
|
44
|
+
export {
|
|
45
|
+
type DocumentsOptions,
|
|
46
|
+
type DocumentsResponse,
|
|
47
|
+
useDocuments,
|
|
48
|
+
} from '../hooks/documents/useDocuments'
|
|
49
|
+
export {
|
|
50
|
+
type PaginatedDocumentsOptions,
|
|
51
|
+
type PaginatedDocumentsResponse,
|
|
52
|
+
usePaginatedDocuments,
|
|
53
|
+
} from '../hooks/paginatedDocuments/usePaginatedDocuments'
|
|
54
|
+
export {
|
|
55
|
+
usePreview,
|
|
56
|
+
type UsePreviewOptions,
|
|
57
|
+
type UsePreviewResults,
|
|
58
|
+
} from '../hooks/preview/usePreview'
|
|
59
|
+
export {
|
|
60
|
+
useProjection,
|
|
61
|
+
type UseProjectionOptions,
|
|
62
|
+
type UseProjectionResults,
|
|
63
|
+
} from '../hooks/projection/useProjection'
|
|
64
|
+
export {useProject} from '../hooks/projects/useProject'
|
|
65
|
+
export {type ProjectWithoutMembers, useProjects} from '../hooks/projects/useProjects'
|
|
66
|
+
export {useQuery} from '../hooks/query/useQuery'
|
|
67
|
+
export {useActiveReleases} from '../hooks/releases/useActiveReleases'
|
|
68
|
+
export {usePerspective} from '../hooks/releases/usePerspective'
|
|
69
|
+
export {type UsersResult, useUsers} from '../hooks/users/useUsers'
|
|
70
|
+
export {REACT_SDK_VERSION} from '../version'
|
|
71
|
+
export {type DatasetsResponse, type SanityProjectMember} from '@sanity/client'
|
|
72
|
+
export type {Status as ComlinkStatus} from '@sanity/comlink'
|
|
73
|
+
export {type SanityDocument, type SortOrderingItem} from '@sanity/types'
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import {render} from '@testing-library/react'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import {describe, expect, it, vi} from 'vitest'
|
|
4
|
+
|
|
5
|
+
import {SDKProvider} from './SDKProvider'
|
|
6
|
+
|
|
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
|
+
},
|
|
29
|
+
}))
|
|
30
|
+
|
|
31
|
+
// Mock AuthBoundary
|
|
32
|
+
vi.mock('./auth/AuthBoundary', () => ({
|
|
33
|
+
AuthBoundary: ({children}: {children: React.ReactNode}) => {
|
|
34
|
+
return <div data-testid="auth-boundary">{children}</div>
|
|
35
|
+
},
|
|
36
|
+
}))
|
|
37
|
+
|
|
38
|
+
describe('SDKProvider', () => {
|
|
39
|
+
it('renders single ResourceProvider with AuthBoundary for a single config', () => {
|
|
40
|
+
const config = {
|
|
41
|
+
projectId: 'test-project',
|
|
42
|
+
dataset: 'production',
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const {getAllByTestId, getByTestId} = render(
|
|
46
|
+
<SDKProvider config={[config]} fallback={<div>Loading...</div>}>
|
|
47
|
+
<div>Child Content</div>
|
|
48
|
+
</SDKProvider>,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
// Should create a single ResourceProvider
|
|
52
|
+
const providers = getAllByTestId('resource-provider')
|
|
53
|
+
expect(providers.length).toBe(1)
|
|
54
|
+
|
|
55
|
+
// Should create an AuthBoundary inside
|
|
56
|
+
expect(getByTestId('auth-boundary')).toBeInTheDocument()
|
|
57
|
+
|
|
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
|
+
})
|
|
63
|
+
})
|
|
64
|
+
|
|
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>
|
|
80
|
+
</SDKProvider>,
|
|
81
|
+
)
|
|
82
|
+
|
|
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
|
+
})
|
|
96
|
+
|
|
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
|
+
})
|
|
102
|
+
})
|
|
103
|
+
})
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import {type SanityConfig} from '@sanity/sdk'
|
|
2
|
+
import {type ReactElement, type ReactNode} from 'react'
|
|
3
|
+
|
|
4
|
+
import {ResourceProvider} from '../context/ResourceProvider'
|
|
5
|
+
import {AuthBoundary, type AuthBoundaryProps} from './auth/AuthBoundary'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
export interface SDKProviderProps extends AuthBoundaryProps {
|
|
11
|
+
children: ReactNode
|
|
12
|
+
config: SanityConfig | SanityConfig[]
|
|
13
|
+
fallback: ReactNode
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @internal
|
|
18
|
+
*
|
|
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.
|
|
22
|
+
*/
|
|
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
|
+
const projectIds = configs.map((c) => c.projectId).filter((id): id is string => !!id)
|
|
33
|
+
|
|
34
|
+
// Create a nested structure of ResourceProviders for each config
|
|
35
|
+
const createNestedProviders = (index: number): ReactElement => {
|
|
36
|
+
if (index >= configs.length) {
|
|
37
|
+
return (
|
|
38
|
+
<AuthBoundary {...props} projectIds={projectIds}>
|
|
39
|
+
{children}
|
|
40
|
+
</AuthBoundary>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<ResourceProvider {...configs[index]} fallback={fallback}>
|
|
46
|
+
{createNestedProviders(index + 1)}
|
|
47
|
+
</ResourceProvider>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return createNestedProviders(0)
|
|
52
|
+
}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import {AuthStateType, type SanityConfig} from '@sanity/sdk'
|
|
2
|
+
import {render, screen} from '@testing-library/react'
|
|
3
|
+
import {describe, expect, it, vi} from 'vitest'
|
|
4
|
+
|
|
5
|
+
import {SanityApp} from './SanityApp'
|
|
6
|
+
import {type SDKProviderProps} from './SDKProvider'
|
|
7
|
+
|
|
8
|
+
// Hoist the mock function definition
|
|
9
|
+
// Rely on vi.fn type inference
|
|
10
|
+
const mockSDKProviderComponent = vi.hoisted(() =>
|
|
11
|
+
vi.fn((_props: SDKProviderProps) => (
|
|
12
|
+
// Simplified mock, doesn't access config directly to avoid type issues
|
|
13
|
+
<div data-testid="sdk-provider">SDKProvider Mock</div>
|
|
14
|
+
)),
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
// Use the hoisted mock in the factory
|
|
18
|
+
vi.mock('./SDKProvider', () => ({
|
|
19
|
+
SDKProvider: mockSDKProviderComponent,
|
|
20
|
+
}))
|
|
21
|
+
|
|
22
|
+
// Mock useEffect to prevent redirect logic from running in tests
|
|
23
|
+
vi.mock('react', async () => {
|
|
24
|
+
const actual = await vi.importActual('react')
|
|
25
|
+
return {
|
|
26
|
+
...actual,
|
|
27
|
+
createSanityInstance: vi.fn(() => ({
|
|
28
|
+
config: {},
|
|
29
|
+
auth: {
|
|
30
|
+
getSession: vi.fn(),
|
|
31
|
+
signIn: vi.fn(),
|
|
32
|
+
signOut: vi.fn(),
|
|
33
|
+
},
|
|
34
|
+
identity: {
|
|
35
|
+
projectId: 'test-project',
|
|
36
|
+
dataset: 'test-dataset',
|
|
37
|
+
},
|
|
38
|
+
dispose: vi.fn(),
|
|
39
|
+
})),
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
vi.mock('../hooks/auth/useAuthState', () => ({
|
|
44
|
+
useAuthState: () => ({
|
|
45
|
+
type: AuthStateType.LOGGED_IN,
|
|
46
|
+
session: {
|
|
47
|
+
user: {
|
|
48
|
+
id: 'test-user',
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
}),
|
|
52
|
+
}))
|
|
53
|
+
|
|
54
|
+
describe('SanityApp', () => {
|
|
55
|
+
beforeEach(() => {
|
|
56
|
+
vi.clearAllMocks()
|
|
57
|
+
// Access the mock instance correctly
|
|
58
|
+
mockSDKProviderComponent.mockClear()
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('renders SDKProvider with a single config', () => {
|
|
62
|
+
const singleConfig = {
|
|
63
|
+
projectId: 'test-project',
|
|
64
|
+
dataset: 'production',
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
render(
|
|
68
|
+
<SanityApp config={singleConfig} fallback={<div>Loading...</div>}>
|
|
69
|
+
<div>Child Content</div>
|
|
70
|
+
</SanityApp>,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
// Check that the SDKProvider is rendered
|
|
74
|
+
expect(screen.getByTestId('sdk-provider')).toBeInTheDocument()
|
|
75
|
+
|
|
76
|
+
// Verify SDKProvider was called with the correct props
|
|
77
|
+
expect(mockSDKProviderComponent).toHaveBeenCalledTimes(1)
|
|
78
|
+
const sdkProviderCalls = mockSDKProviderComponent.mock.calls
|
|
79
|
+
const firstCallArgs1 = sdkProviderCalls[0]
|
|
80
|
+
expect(firstCallArgs1).toBeDefined()
|
|
81
|
+
expect(firstCallArgs1.length).toBeGreaterThan(0)
|
|
82
|
+
const props = firstCallArgs1[0] as unknown as SDKProviderProps
|
|
83
|
+
const config = props?.config
|
|
84
|
+
|
|
85
|
+
// Config is now passed directly as an object for single configs
|
|
86
|
+
expect(config).toEqual(singleConfig)
|
|
87
|
+
expect(props.fallback).toBeTruthy()
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it('renders SDKProvider with multiple configs in original order', () => {
|
|
91
|
+
const multipleConfigs = [
|
|
92
|
+
{
|
|
93
|
+
projectId: 'project-1',
|
|
94
|
+
dataset: 'production',
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
projectId: 'project-2',
|
|
98
|
+
dataset: 'staging',
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
projectId: 'project-3',
|
|
102
|
+
dataset: 'development',
|
|
103
|
+
},
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
render(
|
|
107
|
+
<SanityApp config={multipleConfigs} fallback={<div>Loading...</div>}>
|
|
108
|
+
<div>Child Content</div>
|
|
109
|
+
</SanityApp>,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
// Check that the SDKProvider is rendered
|
|
113
|
+
expect(screen.getByTestId('sdk-provider')).toBeInTheDocument()
|
|
114
|
+
|
|
115
|
+
// Verify SDKProvider was called with the correct props
|
|
116
|
+
expect(mockSDKProviderComponent).toHaveBeenCalledTimes(1)
|
|
117
|
+
const sdkProviderCalls = mockSDKProviderComponent.mock.calls
|
|
118
|
+
const firstCallArgs2 = sdkProviderCalls[0]
|
|
119
|
+
expect(firstCallArgs2).toBeDefined()
|
|
120
|
+
expect(firstCallArgs2.length).toBeGreaterThan(0)
|
|
121
|
+
const props = firstCallArgs2[0] as unknown as SDKProviderProps
|
|
122
|
+
const config = props?.config
|
|
123
|
+
|
|
124
|
+
// Config should be passed directly to SDKProvider
|
|
125
|
+
expect(config).toEqual(multipleConfigs)
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
it('handles iframe environment correctly', async () => {
|
|
129
|
+
// Mock window.self and window.top to simulate iframe environment
|
|
130
|
+
const originalTop = window.top
|
|
131
|
+
const originalSelf = window.self
|
|
132
|
+
|
|
133
|
+
const mockSanityConfig: SanityConfig = {
|
|
134
|
+
projectId: 'test-project',
|
|
135
|
+
dataset: 'test-dataset',
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const mockTop = {}
|
|
139
|
+
Object.defineProperty(window, 'top', {
|
|
140
|
+
value: mockTop,
|
|
141
|
+
writable: true,
|
|
142
|
+
})
|
|
143
|
+
Object.defineProperty(window, 'self', {
|
|
144
|
+
value: window,
|
|
145
|
+
writable: true,
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
render(
|
|
149
|
+
<SanityApp config={[mockSanityConfig]} fallback={<div>Fallback</div>}>
|
|
150
|
+
<div>Test Child</div>
|
|
151
|
+
</SanityApp>,
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
// Wait for 1 second
|
|
155
|
+
await new Promise((resolve) => setTimeout(resolve, 1010))
|
|
156
|
+
|
|
157
|
+
// Add assertions based on your iframe-specific behavior
|
|
158
|
+
expect(window.location.href).toBe('http://localhost:3000/')
|
|
159
|
+
|
|
160
|
+
// Clean up the mock
|
|
161
|
+
Object.defineProperty(window, 'top', {
|
|
162
|
+
value: originalTop,
|
|
163
|
+
writable: true,
|
|
164
|
+
})
|
|
165
|
+
Object.defineProperty(window, 'self', {
|
|
166
|
+
value: originalSelf,
|
|
167
|
+
writable: true,
|
|
168
|
+
})
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
it('redirects to core if not inside iframe and not local url', async () => {
|
|
172
|
+
const originalLocation = window.location
|
|
173
|
+
|
|
174
|
+
const mockLocation = {
|
|
175
|
+
replace: vi.fn(),
|
|
176
|
+
href: 'http://sanity-test.app',
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const mockSanityConfig: SanityConfig = {
|
|
180
|
+
projectId: 'test-project',
|
|
181
|
+
dataset: 'test-dataset',
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
Object.defineProperty(window, 'location', {
|
|
185
|
+
value: mockLocation,
|
|
186
|
+
writable: true,
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
render(
|
|
190
|
+
<SanityApp config={[mockSanityConfig]} fallback={<div>Fallback</div>}>
|
|
191
|
+
<div>Test Child</div>
|
|
192
|
+
</SanityApp>,
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
// Wait for 1 second
|
|
196
|
+
await new Promise((resolve) => setTimeout(resolve, 1010))
|
|
197
|
+
|
|
198
|
+
// Add assertions based on your iframe-specific behavior
|
|
199
|
+
expect(mockLocation.replace).toHaveBeenCalledWith('https://sanity.io/welcome')
|
|
200
|
+
|
|
201
|
+
// Clean up the mock
|
|
202
|
+
Object.defineProperty(window, 'location', {
|
|
203
|
+
value: originalLocation,
|
|
204
|
+
writable: true,
|
|
205
|
+
})
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
it('does not redirect to core if not inside iframe and local url', async () => {
|
|
209
|
+
const originalLocation = window.location
|
|
210
|
+
|
|
211
|
+
const mockSanityConfig: SanityConfig = {
|
|
212
|
+
projectId: 'test-project',
|
|
213
|
+
dataset: 'test-dataset',
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const mockLocation = {
|
|
217
|
+
replace: vi.fn(),
|
|
218
|
+
href: 'http://localhost:3000',
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
Object.defineProperty(window, 'location', {
|
|
222
|
+
value: mockLocation,
|
|
223
|
+
writable: true,
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
render(
|
|
227
|
+
<SanityApp config={[mockSanityConfig]} fallback={<div>Fallback</div>}>
|
|
228
|
+
<div>Test Child</div>
|
|
229
|
+
</SanityApp>,
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
// Wait for 1 second
|
|
233
|
+
await new Promise((resolve) => setTimeout(resolve, 1010))
|
|
234
|
+
|
|
235
|
+
// Add assertions based on your iframe-specific behavior
|
|
236
|
+
expect(mockLocation.replace).not.toHaveBeenCalled()
|
|
237
|
+
|
|
238
|
+
// Clean up the mock
|
|
239
|
+
Object.defineProperty(window, 'location', {
|
|
240
|
+
value: originalLocation,
|
|
241
|
+
writable: true,
|
|
242
|
+
})
|
|
243
|
+
})
|
|
244
|
+
})
|