@sanity/sdk-react 0.0.0-alpha.1 → 0.0.0-alpha.10

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 (87) hide show
  1. package/README.md +163 -0
  2. package/dist/_chunks-es/context.js +8 -0
  3. package/dist/_chunks-es/context.js.map +1 -0
  4. package/dist/_chunks-es/useLogOut.js +29 -20
  5. package/dist/_chunks-es/useLogOut.js.map +1 -1
  6. package/dist/components.d.ts +25 -149
  7. package/dist/components.js +54 -151
  8. package/dist/components.js.map +1 -1
  9. package/dist/context.d.ts +45 -0
  10. package/dist/context.js +5 -0
  11. package/dist/context.js.map +1 -0
  12. package/dist/hooks.d.ts +3401 -12
  13. package/dist/hooks.js +210 -15
  14. package/dist/hooks.js.map +1 -1
  15. package/package.json +43 -32
  16. package/src/_exports/components.ts +2 -12
  17. package/src/_exports/context.ts +2 -0
  18. package/src/_exports/hooks.ts +25 -0
  19. package/src/components/SanityApp.test.tsx +54 -0
  20. package/src/components/SanityApp.tsx +53 -0
  21. package/src/components/auth/AuthBoundary.test.tsx +18 -22
  22. package/src/components/auth/AuthBoundary.tsx +14 -7
  23. package/src/components/auth/Login.test.tsx +3 -17
  24. package/src/components/auth/Login.tsx +27 -31
  25. package/src/components/auth/LoginCallback.test.tsx +2 -17
  26. package/src/components/auth/LoginCallback.tsx +8 -10
  27. package/src/components/auth/LoginError.test.tsx +2 -17
  28. package/src/components/auth/LoginError.tsx +8 -17
  29. package/src/components/auth/LoginFooter.test.tsx +2 -16
  30. package/src/components/auth/LoginFooter.tsx +18 -27
  31. package/src/components/auth/LoginLayout.test.tsx +2 -16
  32. package/src/components/auth/LoginLayout.tsx +8 -37
  33. package/src/components/auth/authTestHelpers.tsx +18 -0
  34. package/src/{components/context → context}/SanityProvider.test.tsx +1 -1
  35. package/src/{components/context → context}/SanityProvider.tsx +12 -6
  36. package/src/hooks/auth/useAuthState.test.tsx +10 -100
  37. package/src/hooks/auth/useAuthState.tsx +5 -10
  38. package/src/hooks/auth/useAuthToken.test.tsx +10 -88
  39. package/src/hooks/auth/useAuthToken.tsx +4 -10
  40. package/src/hooks/auth/useCurrentUser.test.tsx +10 -44
  41. package/src/hooks/auth/useCurrentUser.tsx +22 -22
  42. package/src/hooks/auth/useHandleCallback.test.tsx +10 -19
  43. package/src/hooks/auth/useHandleCallback.tsx +4 -9
  44. package/src/hooks/auth/useLogOut.test.tsx +11 -62
  45. package/src/hooks/auth/useLogOut.tsx +4 -9
  46. package/src/hooks/auth/useLoginUrls.test.tsx +47 -40
  47. package/src/hooks/auth/useLoginUrls.tsx +7 -6
  48. package/src/hooks/client/useClient.test.tsx +1 -1
  49. package/src/hooks/client/useClient.ts +3 -3
  50. package/src/hooks/comlink/useFrameConnection.test.tsx +122 -0
  51. package/src/hooks/comlink/useFrameConnection.ts +111 -0
  52. package/src/hooks/comlink/useWindowConnection.test.ts +94 -0
  53. package/src/hooks/comlink/useWindowConnection.ts +82 -0
  54. package/src/hooks/context/useSanityInstance.test.tsx +1 -1
  55. package/src/hooks/context/useSanityInstance.ts +4 -4
  56. package/src/hooks/document/useApplyActions.test.ts +24 -0
  57. package/src/hooks/document/useApplyActions.ts +24 -0
  58. package/src/hooks/document/useDocument.test.ts +81 -0
  59. package/src/hooks/document/useDocument.ts +38 -0
  60. package/src/hooks/document/useDocumentEvent.test.ts +53 -0
  61. package/src/hooks/document/useDocumentEvent.ts +22 -0
  62. package/src/hooks/document/useDocumentSyncStatus.test.ts +16 -0
  63. package/src/hooks/document/useDocumentSyncStatus.ts +6 -0
  64. package/src/hooks/document/useEditDocument.test.ts +172 -0
  65. package/src/hooks/document/useEditDocument.ts +80 -0
  66. package/src/hooks/documentCollection/useDocuments.test.ts +130 -0
  67. package/src/hooks/documentCollection/useDocuments.ts +135 -0
  68. package/src/hooks/helpers/createCallbackHook.test.tsx +106 -0
  69. package/src/hooks/helpers/createCallbackHook.tsx +15 -0
  70. package/src/hooks/helpers/createStateSourceHook.test.tsx +130 -0
  71. package/src/hooks/helpers/createStateSourceHook.tsx +21 -0
  72. package/src/hooks/preview/usePreview.test.tsx +175 -0
  73. package/src/hooks/preview/usePreview.tsx +120 -0
  74. package/src/vite-env.d.ts +10 -0
  75. package/src/components/DocumentGridLayout/DocumentGridLayout.stories.tsx +0 -95
  76. package/src/components/DocumentGridLayout/DocumentGridLayout.test.tsx +0 -42
  77. package/src/components/DocumentGridLayout/DocumentGridLayout.tsx +0 -23
  78. package/src/components/DocumentListLayout/DocumentListLayout.stories.tsx +0 -95
  79. package/src/components/DocumentListLayout/DocumentListLayout.test.tsx +0 -42
  80. package/src/components/DocumentListLayout/DocumentListLayout.tsx +0 -15
  81. package/src/components/DocumentPreviewLayout/DocumentPreviewLayout.md +0 -49
  82. package/src/components/DocumentPreviewLayout/DocumentPreviewLayout.stories.tsx +0 -34
  83. package/src/components/DocumentPreviewLayout/DocumentPreviewLayout.test.tsx +0 -30
  84. package/src/components/DocumentPreviewLayout/DocumentPreviewLayout.tsx +0 -115
  85. package/src/components/Login/LoginLinks.test.tsx +0 -100
  86. package/src/components/Login/LoginLinks.tsx +0 -73
  87. package/src/hooks/Documents/.keep +0 -0
@@ -1,106 +1,16 @@
1
- import {
2
- type AuthState,
3
- type AuthStore,
4
- createSanityInstance,
5
- getAuthStore,
6
- type SanityInstance,
7
- } from '@sanity/sdk'
8
- import {renderHook} from '@testing-library/react'
9
- import {describe, expect, it, vi} from 'vitest'
1
+ import {getAuthState} from '@sanity/sdk'
2
+ import {identity} from 'rxjs'
3
+ import {describe, it} from 'vitest'
10
4
 
11
- import {SanityProvider} from '../../components/context/SanityProvider'
12
- import * as context from '../context/useSanityInstance'
13
- import {useAuthState} from './useAuthState'
5
+ import {createStateSourceHook} from '../helpers/createStateSourceHook'
14
6
 
15
- // Mock dependencies
16
- vi.mock('@sanity/sdk')
17
- vi.mock('../context/useSanityInstance')
18
-
19
- const createMockAuthStore = (authState: AuthState): AuthStore => ({
20
- authState: {
21
- getState: () => authState,
22
- getInitialState: () => authState,
23
- subscribe: vi.fn(),
24
- },
25
- tokenState: {
26
- getState: vi.fn(),
27
- getInitialState: vi.fn(),
28
- subscribe: vi.fn(),
29
- },
30
- currentUserState: {
31
- getState: vi.fn(),
32
- getInitialState: vi.fn(),
33
- subscribe: vi.fn(),
34
- },
35
- handleCallback: vi.fn(),
36
- logout: vi.fn(),
37
- dispose: vi.fn(),
38
- getLoginUrls: vi.fn(),
39
- })
40
-
41
- const mockUser = {
42
- id: 'user-123',
43
- name: 'Test User',
44
- email: 'test@example.com',
45
- role: 'developer',
46
- roles: [{name: 'developer', title: 'Developer'}],
47
- }
7
+ vi.mock('../helpers/createStateSourceHook', () => ({createStateSourceHook: vi.fn(identity)}))
8
+ vi.mock('@sanity/sdk', () => ({getAuthState: vi.fn()}))
48
9
 
49
10
  describe('useAuthState', () => {
50
- const mockInstance: SanityInstance = {
51
- identity: {
52
- id: 'abc123store',
53
- projectId: 'project-123',
54
- dataset: 'dataset-123',
55
- },
56
- config: {},
57
- }
58
-
59
- // Setup mock for useSanityInstance
60
- beforeEach(() => {
61
- vi.spyOn(context, 'useSanityInstance').mockReturnValue(mockInstance)
62
- })
63
-
64
- it('should return the current auth state', () => {
65
- const mockAuthStore = createMockAuthStore({
66
- type: 'logged-in',
67
- token: 'token-123',
68
- currentUser: mockUser,
69
- })
70
- vi.mocked(getAuthStore).mockReturnValue(mockAuthStore)
71
-
72
- const sanityInstance = createSanityInstance({projectId: 'test', dataset: 'test'})
73
- const {result} = renderHook(() => useAuthState(), {
74
- wrapper: ({children}) => (
75
- <SanityProvider sanityInstance={sanityInstance}>{children}</SanityProvider>
76
- ),
77
- })
78
- const current = result.current as Extract<AuthState, {type: 'logged-in'}>
79
- expect(current.type).toBe('logged-in')
80
- expect(current.token).toBe('token-123')
81
- expect(current.currentUser).toBe(mockUser)
82
- })
83
-
84
- it('should handle signed out state', () => {
85
- const mockAuthStore = createMockAuthStore({type: 'logged-out', isDestroyingSession: false})
86
- vi.mocked(getAuthStore).mockReturnValue(mockAuthStore)
87
-
88
- const {result} = renderHook(() => useAuthState())
89
- expect(result.current.type).toBe('logged-out')
90
- })
91
-
92
- it('should subscribe to auth state changes', () => {
93
- const subscribe = vi.fn()
94
- const mockAuthStore = createMockAuthStore({
95
- type: 'logged-in',
96
- token: 'token-123',
97
- currentUser: null,
98
- })
99
- mockAuthStore.authState.subscribe = subscribe
100
-
101
- vi.mocked(getAuthStore).mockReturnValue(mockAuthStore)
102
-
103
- renderHook(() => useAuthState())
104
- expect(subscribe).toHaveBeenCalled()
11
+ it('calls `createStateSourceHook` with `getAuthState`', async () => {
12
+ const {useAuthState} = await import('./useAuthState')
13
+ expect(createStateSourceHook).toHaveBeenCalledWith(getAuthState)
14
+ expect(useAuthState).toBe(getAuthState)
105
15
  })
106
16
  })
@@ -1,13 +1,13 @@
1
- import {type AuthState, getAuthStore} from '@sanity/sdk'
2
- import {useStore} from 'zustand/react'
1
+ import {type AuthState, getAuthState} from '@sanity/sdk'
3
2
 
4
- import {useSanityInstance} from '../context/useSanityInstance'
3
+ import {createStateSourceHook} from '../helpers/createStateSourceHook'
5
4
 
6
5
  /**
6
+ * @internal
7
7
  * A React hook that subscribes to authentication state changes.
8
8
  *
9
9
  * This hook provides access to the current authentication state type from the Sanity auth store.
10
- * It automatically re-renders the component when the authentication state changes.
10
+ * It automatically re-renders when the authentication state changes.
11
11
  *
12
12
  * @remarks
13
13
  * The hook uses `useSyncExternalStore` to safely subscribe to auth state changes
@@ -25,9 +25,4 @@ import {useSanityInstance} from '../context/useSanityInstance'
25
25
  *
26
26
  * @public
27
27
  */
28
- export function useAuthState(): AuthState {
29
- const instance = useSanityInstance()
30
- const {authState} = getAuthStore(instance)
31
-
32
- return useStore(authState)
33
- }
28
+ export const useAuthState: () => AuthState = createStateSourceHook(getAuthState)
@@ -1,94 +1,16 @@
1
- import {type AuthStore, getAuthStore, type SanityInstance} from '@sanity/sdk'
2
- import {renderHook} from '@testing-library/react'
3
- import {describe, expect, it, vi} from 'vitest'
1
+ import {getTokenState} from '@sanity/sdk'
2
+ import {identity} from 'rxjs'
3
+ import {describe, it} from 'vitest'
4
4
 
5
- import {useSanityInstance} from '../context/useSanityInstance'
6
- import {useAuthToken} from './useAuthToken'
5
+ import {createStateSourceHook} from '../helpers/createStateSourceHook'
7
6
 
8
- // Mock dependencies
9
- vi.mock('@sanity/sdk', () => ({
10
- getAuthStore: vi.fn(),
11
- }))
12
-
13
- vi.mock('../context/useSanityInstance', () => ({
14
- useSanityInstance: vi.fn(),
15
- }))
7
+ vi.mock('../helpers/createStateSourceHook', () => ({createStateSourceHook: vi.fn(identity)}))
8
+ vi.mock('@sanity/sdk', () => ({getTokenState: vi.fn()}))
16
9
 
17
10
  describe('useAuthToken', () => {
18
- // Helper function to create mock instance
19
- const createMockInstance = (): SanityInstance => ({
20
- identity: {
21
- id: 'abc123',
22
- projectId: 'test-project-id',
23
- dataset: 'test-dataset',
24
- },
25
- config: {},
26
- })
27
-
28
- // Helper function to create mock auth store
29
- const createMockAuthStore = (token: string | null) => {
30
- const authType = token ? 'logged-in' : 'logged-out'
31
- return {
32
- tokenState: {
33
- getInitialState: () => token,
34
- getState: () => token,
35
- subscribe: vi.fn(),
36
- },
37
- authState: {
38
- getInitialState: () => ({
39
- type: authType,
40
- isDestroyingSession: false,
41
- ...(token && {token, currentUser: null}),
42
- }),
43
- getState: () => ({
44
- type: authType,
45
- isDestroyingSession: false,
46
- ...(token && {token, currentUser: null}),
47
- }),
48
- subscribe: vi.fn(),
49
- },
50
- currentUserState: {
51
- getInitialState: () => null,
52
- getState: () => null,
53
- subscribe: vi.fn(),
54
- },
55
- handleCallback: vi.fn(),
56
- logout: vi.fn(),
57
- dispose: vi.fn(),
58
- getLoginUrls: vi.fn(),
59
- } as unknown as AuthStore
60
- }
61
-
62
- beforeEach(() => {
63
- vi.clearAllMocks()
64
- })
65
-
66
- it('should return null when no token is present', () => {
67
- const mockInstance = createMockInstance()
68
- const mockAuthStore = createMockAuthStore(null)
69
-
70
- vi.mocked(useSanityInstance).mockReturnValue(mockInstance)
71
- vi.mocked(getAuthStore).mockReturnValue(mockAuthStore)
72
-
73
- const {result} = renderHook(() => useAuthToken())
74
-
75
- expect(result.current).toBeNull()
76
- expect(useSanityInstance).toHaveBeenCalled()
77
- expect(getAuthStore).toHaveBeenCalledWith(mockInstance)
78
- })
79
-
80
- it('should return token when authenticated', () => {
81
- const mockInstance = createMockInstance()
82
- const mockToken = 'test-auth-token'
83
- const mockAuthStore = createMockAuthStore(mockToken)
84
-
85
- vi.mocked(useSanityInstance).mockReturnValue(mockInstance)
86
- vi.mocked(getAuthStore).mockReturnValue(mockAuthStore)
87
-
88
- const {result} = renderHook(() => useAuthToken())
89
-
90
- expect(result.current).toBe(mockToken)
91
- expect(useSanityInstance).toHaveBeenCalled()
92
- expect(getAuthStore).toHaveBeenCalledWith(mockInstance)
11
+ it('calls `createStateSourceHook` with `getTokenState`', async () => {
12
+ const {useAuthToken} = await import('./useAuthToken')
13
+ expect(createStateSourceHook).toHaveBeenCalledWith(getTokenState)
14
+ expect(useAuthToken).toBe(getTokenState)
93
15
  })
94
16
  })
@@ -1,16 +1,10 @@
1
- import {getAuthStore} from '@sanity/sdk'
2
- import {useStore} from 'zustand'
1
+ import {getTokenState} from '@sanity/sdk'
3
2
 
4
- import {useSanityInstance} from '../context/useSanityInstance'
3
+ import {createStateSourceHook} from '../helpers/createStateSourceHook'
5
4
 
6
5
  /**
7
6
  * Hook to get the currently logged in user
8
- * @public
7
+ * @internal
9
8
  * @returns The current user or null if not authenticated
10
9
  */
11
- export const useAuthToken = (): string | null => {
12
- const instance = useSanityInstance()
13
- const {tokenState} = getAuthStore(instance)
14
-
15
- return useStore(tokenState)
16
- }
10
+ export const useAuthToken = createStateSourceHook(getTokenState)
@@ -1,50 +1,16 @@
1
- import {type AuthStore, type CurrentUser, getAuthStore, type SanityInstance} from '@sanity/sdk'
2
- import {renderHook} from '@testing-library/react'
3
- import {describe, expect, it, vi} from 'vitest'
1
+ import {getCurrentUserState} from '@sanity/sdk'
2
+ import {identity} from 'rxjs'
3
+ import {describe, it} from 'vitest'
4
4
 
5
- import {useSanityInstance} from '../context/useSanityInstance'
6
- import {useCurrentUser} from './useCurrentUser'
5
+ import {createStateSourceHook} from '../helpers/createStateSourceHook'
7
6
 
8
- // Mock dependencies
9
- vi.mock('../context/useSanityInstance')
10
- vi.mock('@sanity/sdk')
11
-
12
- const mockUser: CurrentUser = {
13
- id: 'user-123',
14
- name: 'Test User',
15
- email: 'test@example.com',
16
- role: 'admin',
17
- roles: [],
18
- }
7
+ vi.mock('../helpers/createStateSourceHook', () => ({createStateSourceHook: vi.fn(identity)}))
8
+ vi.mock('@sanity/sdk', () => ({getCurrentUserState: vi.fn()}))
19
9
 
20
10
  describe('useCurrentUser', () => {
21
- it('returns the current user when authenticated', () => {
22
- vi.mocked(useSanityInstance).mockReturnValue({} as unknown as SanityInstance)
23
-
24
- // Mock the auth store with an authenticated user
25
- vi.mocked(getAuthStore).mockReturnValue({
26
- currentUserState: {
27
- getState: () => mockUser,
28
- subscribe: vi.fn(),
29
- },
30
- } as unknown as AuthStore)
31
-
32
- const {result} = renderHook(() => useCurrentUser())
33
- expect(result.current).toEqual(mockUser)
34
- })
35
-
36
- it('returns null when not authenticated', () => {
37
- vi.mocked(useSanityInstance).mockReturnValue({} as unknown as SanityInstance)
38
-
39
- // Mock the auth store with no user
40
- vi.mocked(getAuthStore).mockReturnValue({
41
- currentUserState: {
42
- getState: () => null,
43
- subscribe: vi.fn(),
44
- },
45
- } as unknown as AuthStore)
46
-
47
- const {result} = renderHook(() => useCurrentUser())
48
- expect(result.current).toBeNull()
11
+ it('calls `createStateSourceHook` with `getTokenState`', async () => {
12
+ const {useCurrentUser} = await import('./useCurrentUser')
13
+ expect(createStateSourceHook).toHaveBeenCalledWith(getCurrentUserState)
14
+ expect(useCurrentUser).toBe(getCurrentUserState)
49
15
  })
50
16
  })
@@ -1,27 +1,27 @@
1
- import {type CurrentUser, type CurrentUserSlice, getAuthStore} from '@sanity/sdk'
2
- import {useStore} from 'zustand'
1
+ import {type CurrentUser, getCurrentUserState} from '@sanity/sdk'
3
2
 
4
- import {useSanityInstance} from '../context/useSanityInstance'
3
+ import {createStateSourceHook} from '../helpers/createStateSourceHook'
5
4
 
6
5
  /**
7
- * Hook to get the currently logged in user
6
+ * @TODO This should suspend! And possibly not return `null`?
7
+ *
8
8
  * @public
9
- * @returns The current user or null if not authenticated
9
+ *
10
+ * The `useCurrentUser` hook returns the currently authenticated user’s profile information (their name, email, roles, etc).
11
+ * If no users are currently logged in, the hook returns null.
12
+ *
13
+ * @returns The current user data, or `null` if not authenticated
14
+ *
15
+ * @example Rendering a basic user profile
16
+ * ```
17
+ * const user = useCurrentUser()
18
+ *
19
+ * return (
20
+ * <figure>
21
+ * <img src={user?.profileImage} alt=`Profile image for ${user?.name}` />
22
+ * <h2>{user?.name}</h2>
23
+ * </figure>
24
+ * )
25
+ * ```
10
26
  */
11
- export const useCurrentUser = (): CurrentUser | null => {
12
- const instance = useSanityInstance()
13
- const {currentUserState} = getAuthStore(instance)
14
-
15
- // TODO: update this hook so it can never return null
16
- if (!currentUserState.getState())
17
- throw new Promise<void>((resolve) => {
18
- const unsubscribe = currentUserState.subscribe((currentUser) => {
19
- if (currentUser) {
20
- unsubscribe()
21
- resolve()
22
- }
23
- })
24
- })
25
-
26
- return useStore<CurrentUserSlice>(currentUserState)
27
- }
27
+ export const useCurrentUser: () => CurrentUser | null = createStateSourceHook(getCurrentUserState)
@@ -1,25 +1,16 @@
1
- import {getAuthStore} from '@sanity/sdk'
2
- import {renderHook} from '@testing-library/react'
3
- import {type Mock, vi} from 'vitest'
1
+ import {handleCallback} from '@sanity/sdk'
2
+ import {identity} from 'rxjs'
3
+ import {describe, it} from 'vitest'
4
4
 
5
- import {useSanityInstance} from '../context/useSanityInstance'
6
- import {useHandleCallback} from './useHandleCallback'
5
+ import {createCallbackHook} from '../helpers/createCallbackHook'
7
6
 
8
- vi.mock('../context/useSanityInstance')
9
- vi.mock('@sanity/sdk', () => ({getAuthStore: vi.fn()}))
7
+ vi.mock('../helpers/createCallbackHook', () => ({createCallbackHook: vi.fn(identity)}))
8
+ vi.mock('@sanity/sdk', () => ({handleCallback: vi.fn()}))
10
9
 
11
10
  describe('useHandleCallback', () => {
12
- it('returns handleCallback from auth store', () => {
13
- const mockInstance = {id: 'test'}
14
- const mockHandleCallback = vi.fn()
15
- const mockAuthStore = {handleCallback: mockHandleCallback}
16
-
17
- ;(useSanityInstance as Mock).mockReturnValue(mockInstance)
18
- ;(getAuthStore as Mock).mockReturnValue(mockAuthStore)
19
-
20
- const {result} = renderHook(() => useHandleCallback())
21
-
22
- expect(getAuthStore).toHaveBeenCalledWith(mockInstance)
23
- expect(result.current).toBe(mockHandleCallback)
11
+ it('calls `createCallbackHook` with `handleCallback`', async () => {
12
+ const {useHandleCallback} = await import('./useHandleCallback')
13
+ expect(createCallbackHook).toHaveBeenCalledWith(handleCallback)
14
+ expect(useHandleCallback).toBe(handleCallback)
24
15
  })
25
16
  })
@@ -1,9 +1,9 @@
1
- import {type AuthStore, getAuthStore} from '@sanity/sdk'
2
- import {useMemo} from 'react'
1
+ import {handleCallback} from '@sanity/sdk'
3
2
 
4
- import {useSanityInstance} from '../context/useSanityInstance'
3
+ import {createCallbackHook} from '../helpers/createCallbackHook'
5
4
 
6
5
  /**
6
+ * @internal
7
7
  * A React hook that returns a function for handling authentication callbacks.
8
8
  *
9
9
  * @remarks
@@ -42,9 +42,4 @@ import {useSanityInstance} from '../context/useSanityInstance'
42
42
  * @returns A callback handler function that processes OAuth redirects
43
43
  * @public
44
44
  */
45
- export function useHandleCallback(): AuthStore['handleCallback'] {
46
- const instance = useSanityInstance()
47
- const authStore = useMemo(() => getAuthStore(instance), [instance])
48
-
49
- return authStore.handleCallback
50
- }
45
+ export const useHandleCallback = createCallbackHook(handleCallback)
@@ -1,67 +1,16 @@
1
- import {type AuthStore, getAuthStore, type SanityInstance} from '@sanity/sdk'
2
- import {renderHook} from '@testing-library/react'
3
- import {describe, expect, it, vi} from 'vitest'
1
+ import {logout} from '@sanity/sdk'
2
+ import {identity} from 'rxjs'
3
+ import {describe, it} from 'vitest'
4
4
 
5
- import {useSanityInstance} from '../context/useSanityInstance'
6
- import {useLogOut} from './useLogOut'
5
+ import {createCallbackHook} from '../helpers/createCallbackHook'
7
6
 
8
- // Mock dependencies
9
- vi.mock('@sanity/sdk', () => ({
10
- getAuthStore: vi.fn(),
11
- }))
7
+ vi.mock('../helpers/createCallbackHook', () => ({createCallbackHook: vi.fn(identity)}))
8
+ vi.mock('@sanity/sdk', () => ({logout: vi.fn()}))
12
9
 
13
- vi.mock('../context/useSanityInstance', () => ({
14
- useSanityInstance: vi.fn(),
15
- }))
16
-
17
- describe('useLogOut', () => {
18
- it('should return logout function from auth store', () => {
19
- // Setup mocks
20
- const mockInstance: SanityInstance = {
21
- identity: {
22
- id: 'abc123',
23
- projectId: 'test-project-id',
24
- dataset: 'test-dataset',
25
- },
26
- config: {},
27
- }
28
-
29
- const mockLogout = vi.fn()
30
- const mockAuthStore: AuthStore = {
31
- authState: {
32
- getInitialState: vi.fn(),
33
- getState: vi.fn(),
34
- subscribe: vi.fn(),
35
- },
36
- tokenState: {
37
- getInitialState: vi.fn(),
38
- getState: vi.fn(),
39
- subscribe: vi.fn(),
40
- },
41
- currentUserState: {
42
- getInitialState: vi.fn(),
43
- getState: vi.fn(),
44
- subscribe: vi.fn(),
45
- },
46
- handleCallback: vi.fn(),
47
- logout: mockLogout,
48
- dispose: vi.fn(),
49
- getLoginUrls: vi.fn(),
50
- }
51
-
52
- vi.mocked(useSanityInstance).mockReturnValue(mockInstance)
53
- vi.mocked(getAuthStore).mockReturnValue(mockAuthStore)
54
-
55
- // Test the hook
56
- const {result} = renderHook(() => useLogOut())
57
-
58
- // Verify the returned function is the logout function
59
- expect(result.current).toBe(mockLogout)
60
- expect(useSanityInstance).toHaveBeenCalled()
61
- expect(getAuthStore).toHaveBeenCalledWith(mockInstance)
62
-
63
- // Verify the logout function can be called
64
- result.current()
65
- expect(mockLogout).toHaveBeenCalled()
10
+ describe('useHandleCallback', () => {
11
+ it('calls `createCallbackHook` with `handleCallback`', async () => {
12
+ const {useLogOut} = await import('./useLogOut')
13
+ expect(createCallbackHook).toHaveBeenCalledWith(logout)
14
+ expect(useLogOut).toBe(logout)
66
15
  })
67
16
  })
@@ -1,15 +1,10 @@
1
- import {type AuthStore, getAuthStore} from '@sanity/sdk'
1
+ import {logout} from '@sanity/sdk'
2
2
 
3
- import {useSanityInstance} from '../context/useSanityInstance'
3
+ import {createCallbackHook} from '../helpers/createCallbackHook'
4
4
 
5
5
  /**
6
6
  * Hook to log out of the current session
7
- * @public
7
+ * @internal
8
8
  * @returns A function to log out of the current session
9
9
  */
10
- export const useLogOut = (): AuthStore['logout'] => {
11
- const instance = useSanityInstance()
12
- const {logout} = getAuthStore(instance)
13
-
14
- return logout
15
- }
10
+ export const useLogOut = createCallbackHook(logout)
@@ -1,61 +1,68 @@
1
- import {type AuthStore, createSanityInstance, getAuthStore} from '@sanity/sdk'
2
- import {renderHook, waitFor} from '@testing-library/react'
3
- import {Suspense} from 'react'
1
+ import {createSanityInstance, fetchLoginUrls, getLoginUrlsState} from '@sanity/sdk'
2
+ import {renderHook} from '@testing-library/react'
3
+ import {act, Suspense} from 'react'
4
+ import {throwError} from 'rxjs'
4
5
  import {describe, expect, it, vi} from 'vitest'
5
6
 
6
- import {SanityProvider} from '../../components/context/SanityProvider'
7
7
  import {useLoginUrls} from './useLoginUrls'
8
8
 
9
+ vi.mock('../context/useSanityInstance', () => ({
10
+ useSanityInstance: vi.fn().mockReturnValue(createSanityInstance({projectId: 'p', dataset: 'd'})),
11
+ }))
12
+
9
13
  vi.mock(import('@sanity/sdk'), async (importOriginal) => {
10
14
  const actual = await importOriginal()
11
- return {
12
- ...actual,
13
- getAuthStore: vi.fn(),
14
- }
15
+ return {...actual, getLoginUrlsState: vi.fn(), fetchLoginUrls: vi.fn()}
15
16
  })
16
17
 
17
18
  describe('useLoginUrls', () => {
18
- it('should handle synchronous provider URLs', () => {
19
- const mockProviders = [{name: 'google', title: 'Google', url: 'http://test.com/auth/google'}]
20
- const mockAuthStore = {
21
- getLoginUrls: () => mockProviders,
22
- }
23
- vi.mocked(getAuthStore).mockReturnValue(mockAuthStore as AuthStore)
19
+ it('should suspend by throwing `fetchLoginUrls` if `getLoginUrlsState().getCurrent()` is falsy', async () => {
20
+ const subscribe = vi.fn()
21
+ const getCurrent = vi.fn().mockReturnValue(undefined)
24
22
 
25
- const sanityInstance = createSanityInstance({projectId: 'test', dataset: 'test'})
26
- const wrapper = ({children}: {children: React.ReactNode}) => (
27
- <SanityProvider sanityInstance={sanityInstance}>{children}</SanityProvider>
23
+ let resolve: () => void
24
+ const promise = new Promise<void>((thisResolve) => {
25
+ resolve = thisResolve
26
+ })
27
+ vi.mocked(fetchLoginUrls).mockReturnValue(
28
+ promise as unknown as ReturnType<typeof fetchLoginUrls>,
28
29
  )
29
30
 
30
- const {result} = renderHook(() => useLoginUrls(), {wrapper})
31
- expect(result.current).toEqual(mockProviders)
32
- })
33
-
34
- it('should handle asynchronous provider URLs', async () => {
35
- const mockProviders = [{name: 'google', title: 'Google', url: 'http://test.com/auth/google'}]
36
-
37
- const getLoginUrls = vi
38
- .fn<AuthStore['getLoginUrls']>()
39
- .mockResolvedValueOnce(mockProviders)
40
- .mockReturnValueOnce(mockProviders)
41
-
42
- vi.mocked(getAuthStore).mockReturnValue({getLoginUrls} as unknown as AuthStore)
31
+ vi.mocked(getLoginUrlsState).mockReturnValue({
32
+ getCurrent,
33
+ subscribe,
34
+ observable: throwError(() => new Error('Unexpected usage of observable')),
35
+ })
43
36
 
44
- const sanityInstance = createSanityInstance({projectId: 'test', dataset: 'test'})
45
37
  const wrapper = ({children}: {children: React.ReactNode}) => (
46
- <Suspense fallback={<>Loading…</>}>
47
- <SanityProvider sanityInstance={sanityInstance}>{children}</SanityProvider>
48
- </Suspense>
38
+ <Suspense fallback={<>Loading…</>}>{children}</Suspense>
49
39
  )
50
40
 
51
- const {result} = renderHook(() => useLoginUrls(), {wrapper})
52
-
53
- // Initially empty
41
+ const {result, rerender} = renderHook(() => useLoginUrls(), {wrapper})
54
42
  expect(result.current).toEqual(null)
55
43
 
56
- // Wait for providers to load
57
- await waitFor(() => {
58
- expect(result.current).toEqual(mockProviders)
44
+ const mockProviders = [{name: 'google', title: 'Google', url: 'http://test.com/auth/google'}]
45
+
46
+ await act(async () => {
47
+ getCurrent.mockReturnValue(mockProviders)
48
+ resolve()
49
+ rerender()
50
+ await promise
59
51
  })
52
+
53
+ expect(result.current).toEqual(mockProviders)
54
+ })
55
+
56
+ it('should render according to `getLoginUrlsState`', () => {
57
+ const subscribe = vi.fn()
58
+ const mockProviders = [{name: 'google', title: 'Google', url: 'http://test.com/auth/google'}]
59
+ vi.mocked(getLoginUrlsState).mockReturnValue({
60
+ getCurrent: () => mockProviders,
61
+ subscribe,
62
+ observable: throwError(() => new Error('Unexpected usage of observable')),
63
+ })
64
+
65
+ const {result} = renderHook(() => useLoginUrls())
66
+ expect(result.current).toEqual(mockProviders)
60
67
  })
61
68
  })