@sanity/sdk-react 0.0.0 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,3 @@
1
- import {type Status} from '@sanity/comlink'
2
1
  import {SDK_CHANNEL_NAME, SDK_NODE_NAME} from '@sanity/message-protocol'
3
2
  import {useEffect, useState} from 'react'
4
3
 
@@ -23,35 +22,62 @@ interface WorkspacesByProjectIdDataset {
23
22
  interface StudioWorkspacesResult {
24
23
  workspacesByProjectIdAndDataset: WorkspacesByProjectIdDataset
25
24
  error: string | null
26
- isConnected: boolean
27
25
  }
28
26
 
29
27
  /**
30
28
  * Hook that fetches studio workspaces and organizes them by projectId:dataset
31
29
  * @internal
30
+ *
31
+ * @example
32
+ * ```tsx
33
+ * import {useStudioWorkspacesByProjectIdDataset} from '@sanity/sdk-react'
34
+ * import {Card, Code, Button} from '@sanity/ui'
35
+ * import {Suspense} from 'react'
36
+ *
37
+ * function WorkspacesCard() {
38
+ * const {workspacesByProjectIdAndDataset, error} = useStudioWorkspacesByProjectIdDataset()
39
+ * if (error) {
40
+ * return <div>Error: {error}</div>
41
+ * }
42
+ * return (
43
+ * <Card padding={4} radius={2} shadow={1}>
44
+ * <Code language="json">
45
+ * {JSON.stringify(workspacesByProjectIdAndDataset, null, 2)}
46
+ * </Code>
47
+ * </Card>
48
+ * )
49
+ * }
50
+ *
51
+ * // Wrap the component with Suspense since the hook may suspend
52
+ * function DashboardWorkspaces() {
53
+ * return (
54
+ * <Suspense fallback={<Button text="Loading..." disabled />}>
55
+ * <WorkspacesCard />
56
+ * </Suspense>
57
+ * )
58
+ * }
59
+ * ```
32
60
  */
33
61
  export function useStudioWorkspacesByProjectIdDataset(): StudioWorkspacesResult {
34
62
  const [workspacesByProjectIdAndDataset, setWorkspacesByProjectIdAndDataset] =
35
63
  useState<WorkspacesByProjectIdDataset>({})
36
- const [status, setStatus] = useState<Status>('idle')
37
64
  const [error, setError] = useState<string | null>(null)
38
65
 
39
66
  const {fetch} = useWindowConnection({
40
67
  name: SDK_NODE_NAME,
41
68
  connectTo: SDK_CHANNEL_NAME,
42
- onStatus: setStatus,
43
69
  })
44
70
 
45
71
  // Once computed, this should probably be in a store and poll for changes
46
72
  // However, our stores are currently being refactored
47
73
  useEffect(() => {
48
- if (!fetch || status !== 'connected') return
74
+ if (!fetch) return
49
75
 
50
76
  async function fetchWorkspaces(signal: AbortSignal) {
51
77
  try {
52
78
  const data = await fetch<{
53
79
  context: {availableResources: Array<DashboardResource>}
54
- }>('dashboard/v1/bridge/context', undefined, {signal})
80
+ }>('dashboard/v1/context', undefined, {signal})
55
81
 
56
82
  const workspaceMap: WorkspacesByProjectIdDataset = {}
57
83
  const noProjectIdAndDataset: DashboardResource[] = []
@@ -91,11 +117,10 @@ export function useStudioWorkspacesByProjectIdDataset(): StudioWorkspacesResult
91
117
  return () => {
92
118
  controller.abort()
93
119
  }
94
- }, [fetch, status])
120
+ }, [fetch])
95
121
 
96
122
  return {
97
123
  workspacesByProjectIdAndDataset,
98
124
  error,
99
- isConnected: status === 'connected',
100
125
  }
101
126
  }
@@ -1,85 +0,0 @@
1
- import {type Message, type Node, type Status} from '@sanity/comlink'
2
- import {getOrCreateNode} from '@sanity/sdk'
3
- import {beforeEach, describe, expect, it, vi} from 'vitest'
4
-
5
- import {act, renderHook} from '../../../test/test-utils'
6
- import {useRecordDocumentHistoryEvent} from './useRecordDocumentHistoryEvent'
7
-
8
- vi.mock(import('@sanity/sdk'), async (importOriginal) => {
9
- const actual = await importOriginal()
10
- return {
11
- ...actual,
12
- getOrCreateNode: vi.fn(),
13
- releaseNode: vi.fn(),
14
- }
15
- })
16
-
17
- describe('useRecordDocumentHistoryEvent', () => {
18
- let node: Node<Message, Message>
19
- let statusCallback: ((status: Status) => void) | null = null
20
-
21
- const mockDocumentHandle = {
22
- documentId: 'mock-id',
23
- documentType: 'mock-type',
24
- resourceType: 'studio' as const,
25
- resourceId: 'mock-resource-id',
26
- }
27
-
28
- function createMockNode() {
29
- return {
30
- on: vi.fn(() => () => {}),
31
- post: vi.fn(),
32
- stop: vi.fn(),
33
- onStatus: vi.fn((callback) => {
34
- statusCallback = callback
35
- return () => {}
36
- }),
37
- } as unknown as Node<Message, Message>
38
- }
39
-
40
- beforeEach(() => {
41
- statusCallback = null
42
- node = createMockNode()
43
- vi.mocked(getOrCreateNode).mockReturnValue(node)
44
- })
45
-
46
- it('should initialize with correct connection status', () => {
47
- const {result} = renderHook(() => useRecordDocumentHistoryEvent(mockDocumentHandle))
48
-
49
- expect(result.current.isConnected).toBe(false)
50
-
51
- act(() => {
52
- statusCallback?.('connected')
53
- })
54
-
55
- expect(result.current.isConnected).toBe(true)
56
- })
57
-
58
- it('should send correct message when recording events', () => {
59
- const {result} = renderHook(() => useRecordDocumentHistoryEvent(mockDocumentHandle))
60
-
61
- result.current.recordEvent('viewed')
62
-
63
- expect(node.post).toHaveBeenCalledWith('dashboard/v1/events/history', {
64
- eventType: 'viewed',
65
- document: {
66
- id: 'mock-id',
67
- type: 'mock-type',
68
- resource: {
69
- id: 'mock-resource-id',
70
- type: 'studio',
71
- },
72
- },
73
- })
74
- })
75
-
76
- it('should handle errors when sending messages', () => {
77
- vi.mocked(node.post).mockImplementation(() => {
78
- throw new Error('Failed to send message')
79
- })
80
-
81
- const {result} = renderHook(() => useRecordDocumentHistoryEvent(mockDocumentHandle))
82
-
83
- expect(() => result.current.recordEvent('viewed')).toThrow('Failed to send message')
84
- })
85
- })
@@ -1,135 +0,0 @@
1
- import {type Message, type Node, type Status} from '@sanity/comlink'
2
- import {getOrCreateNode, releaseNode} from '@sanity/sdk'
3
- import {beforeEach, describe, expect, it, vi} from 'vitest'
4
-
5
- import {act, renderHook} from '../../../test/test-utils'
6
- import {useWindowConnection} from './useWindowConnection'
7
-
8
- vi.mock(import('@sanity/sdk'), async (importOriginal) => {
9
- const actual = await importOriginal()
10
- return {
11
- ...actual,
12
- getOrCreateNode: vi.fn(),
13
- createNode: vi.fn(),
14
- releaseNode: vi.fn(),
15
- }
16
- })
17
- interface TestMessage {
18
- type: 'TEST_MESSAGE'
19
- data: {someData: string}
20
- }
21
-
22
- interface AnotherMessage {
23
- type: 'ANOTHER_MESSAGE'
24
- data: {otherData: number}
25
- }
26
-
27
- type TestMessages = TestMessage | AnotherMessage
28
-
29
- describe('useWindowConnection', () => {
30
- let node: Node<Message, Message>
31
- let statusCallback: ((status: Status) => void) | null = null
32
-
33
- function createMockNode() {
34
- return {
35
- // return unsubscribe function
36
- on: vi.fn(() => () => {}),
37
- post: vi.fn(),
38
- stop: vi.fn(),
39
- onStatus: vi.fn((callback) => {
40
- statusCallback = callback
41
- return () => {}
42
- }),
43
- } as unknown as Node<Message, Message>
44
- }
45
-
46
- beforeEach(() => {
47
- statusCallback = null
48
- node = createMockNode()
49
- vi.mocked(getOrCreateNode).mockReturnValue(node as unknown as Node<Message, Message>)
50
- })
51
-
52
- it('should call onStatus callback when status changes', () => {
53
- const onStatusMock = vi.fn()
54
- renderHook(() =>
55
- useWindowConnection<TestMessages, TestMessages>({
56
- name: 'test',
57
- connectTo: 'window',
58
- onStatus: onStatusMock,
59
- }),
60
- )
61
-
62
- act(() => {
63
- statusCallback?.('connected')
64
- })
65
- expect(onStatusMock).toHaveBeenCalledWith('connected')
66
-
67
- act(() => {
68
- statusCallback?.('disconnected')
69
- })
70
- expect(onStatusMock).toHaveBeenCalledWith('disconnected')
71
- })
72
-
73
- it('should not throw if onStatus is not provided', () => {
74
- renderHook(() =>
75
- useWindowConnection<TestMessages, TestMessages>({
76
- name: 'test',
77
- connectTo: 'window',
78
- }),
79
- )
80
-
81
- expect(() => {
82
- act(() => {
83
- statusCallback?.('connected')
84
- })
85
- }).not.toThrow()
86
- })
87
-
88
- it('should register message handlers', () => {
89
- const mockHandler = vi.fn()
90
- const mockData = {someData: 'test'}
91
-
92
- renderHook(() =>
93
- useWindowConnection<TestMessages, TestMessages>({
94
- name: 'test',
95
- connectTo: 'window',
96
- onMessage: {
97
- TEST_MESSAGE: mockHandler,
98
- ANOTHER_MESSAGE: vi.fn(),
99
- },
100
- }),
101
- )
102
-
103
- const onCallback = vi.mocked(node.on).mock.calls[0][1]
104
- onCallback(mockData)
105
-
106
- expect(mockHandler).toHaveBeenCalledWith(mockData)
107
- })
108
-
109
- it('should send messages through the node', () => {
110
- const {result} = renderHook(() =>
111
- useWindowConnection<TestMessages, TestMessages>({
112
- name: 'test',
113
- connectTo: 'window',
114
- }),
115
- )
116
-
117
- result.current.sendMessage('TEST_MESSAGE', {someData: 'test'})
118
- expect(node.post).toHaveBeenCalledWith('TEST_MESSAGE', {someData: 'test'})
119
-
120
- result.current.sendMessage('ANOTHER_MESSAGE', {otherData: 123})
121
- expect(node.post).toHaveBeenCalledWith('ANOTHER_MESSAGE', {otherData: 123})
122
- })
123
-
124
- it('should cleanup on unmount', () => {
125
- const {unmount} = renderHook(() =>
126
- useWindowConnection<TestMessages, TestMessages>({
127
- name: 'test',
128
- connectTo: 'window',
129
- }),
130
- )
131
-
132
- unmount()
133
- expect(releaseNode).toHaveBeenCalled()
134
- })
135
- })