@sanity/sdk-react 0.0.1 → 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.
- package/dist/index.d.ts +62 -14
- package/dist/index.js +112 -143
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/_exports/sdk-react.ts +2 -2
- package/src/hooks/comlink/useWindowConnection.test.tsx +145 -0
- package/src/hooks/comlink/useWindowConnection.ts +32 -30
- package/src/hooks/{comlink → dashboard}/useManageFavorite.test.ts +84 -134
- package/src/hooks/{comlink → dashboard}/useManageFavorite.ts +4 -39
- package/src/hooks/dashboard/useNavigateToStudioDocument.test.ts +2 -73
- package/src/hooks/dashboard/useNavigateToStudioDocument.ts +20 -27
- package/src/hooks/dashboard/useRecordDocumentHistoryEvent.test.ts +69 -0
- package/src/hooks/{comlink → dashboard}/useRecordDocumentHistoryEvent.ts +17 -10
- package/src/hooks/dashboard/useStudioWorkspacesByProjectIdDataset.test.tsx +14 -85
- package/src/hooks/dashboard/useStudioWorkspacesByProjectIdDataset.ts +33 -8
- package/src/hooks/comlink/useRecordDocumentHistoryEvent.test.ts +0 -85
- package/src/hooks/comlink/useWindowConnection.test.ts +0 -135
|
@@ -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
|
|
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/
|
|
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
|
|
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
|
-
})
|