@startsimpli/hooks 0.4.6 → 0.4.8
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/package.json +7 -2
- package/src/__tests__/useAsyncOptions.test.ts +136 -0
- package/src/__tests__/useEntityTable.test.ts +322 -0
- package/src/__tests__/useWizard.test.ts +201 -9
- package/src/index.ts +32 -1
- package/src/useAsyncOptions.ts +60 -0
- package/src/useCSV.ts +10 -10
- package/src/useEnrichment.ts +156 -0
- package/src/useEntityTable.ts +134 -0
- package/src/useMessageTemplates.ts +67 -0
- package/src/useMessages.ts +86 -0
- package/src/useTargetLists.ts +132 -0
- package/src/useWizard.ts +39 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@startsimpli/hooks",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.8",
|
|
4
4
|
"description": "Shared React hooks for StartSimpli apps",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -24,14 +24,19 @@
|
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
26
|
"react": ">=18.0.0",
|
|
27
|
-
"@tanstack/react-query": ">=5.0.0"
|
|
27
|
+
"@tanstack/react-query": ">=5.0.0",
|
|
28
|
+
"@startsimpli/api": "workspace:*"
|
|
28
29
|
},
|
|
29
30
|
"peerDependenciesMeta": {
|
|
30
31
|
"@tanstack/react-query": {
|
|
31
32
|
"optional": true
|
|
33
|
+
},
|
|
34
|
+
"@startsimpli/api": {
|
|
35
|
+
"optional": true
|
|
32
36
|
}
|
|
33
37
|
},
|
|
34
38
|
"devDependencies": {
|
|
39
|
+
"@startsimpli/api": "workspace:*",
|
|
35
40
|
"@tanstack/react-query": "^5.0.0",
|
|
36
41
|
"@testing-library/react": "^14.0.0",
|
|
37
42
|
"@types/node": "^20.11.0",
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest'
|
|
2
|
+
import { renderHook, act, waitFor } from '@testing-library/react'
|
|
3
|
+
import { useAsyncOptions } from '../useAsyncOptions'
|
|
4
|
+
|
|
5
|
+
describe('useAsyncOptions', () => {
|
|
6
|
+
it('fetches data on mount and returns it', async () => {
|
|
7
|
+
const items = [{ id: 1 }, { id: 2 }]
|
|
8
|
+
const fetcher = vi.fn().mockResolvedValue(items)
|
|
9
|
+
|
|
10
|
+
const { result } = renderHook(() => useAsyncOptions(fetcher))
|
|
11
|
+
|
|
12
|
+
expect(result.current.loading).toBe(true)
|
|
13
|
+
expect(result.current.data).toEqual([])
|
|
14
|
+
|
|
15
|
+
await waitFor(() => expect(result.current.loading).toBe(false))
|
|
16
|
+
|
|
17
|
+
expect(result.current.data).toEqual(items)
|
|
18
|
+
expect(result.current.error).toBeNull()
|
|
19
|
+
expect(fetcher).toHaveBeenCalledOnce()
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('returns defaultValue while loading', async () => {
|
|
23
|
+
const defaultValue = [{ id: 0 }]
|
|
24
|
+
const fetcher = vi.fn().mockResolvedValue([{ id: 1 }])
|
|
25
|
+
|
|
26
|
+
const { result } = renderHook(() =>
|
|
27
|
+
useAsyncOptions(fetcher, { defaultValue })
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
expect(result.current.data).toEqual(defaultValue)
|
|
31
|
+
|
|
32
|
+
await waitFor(() => expect(result.current.loading).toBe(false))
|
|
33
|
+
expect(result.current.data).toEqual([{ id: 1 }])
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('does not fetch when enabled is false', async () => {
|
|
37
|
+
const fetcher = vi.fn().mockResolvedValue([])
|
|
38
|
+
|
|
39
|
+
const { result } = renderHook(() =>
|
|
40
|
+
useAsyncOptions(fetcher, { enabled: false })
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
// Give it a tick to ensure nothing fires
|
|
44
|
+
await act(async () => {
|
|
45
|
+
await new Promise((r) => setTimeout(r, 10))
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
expect(fetcher).not.toHaveBeenCalled()
|
|
49
|
+
expect(result.current.loading).toBe(false)
|
|
50
|
+
expect(result.current.data).toEqual([])
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('fetches when enabled changes from false to true', async () => {
|
|
54
|
+
const items = [{ id: 1 }]
|
|
55
|
+
const fetcher = vi.fn().mockResolvedValue(items)
|
|
56
|
+
|
|
57
|
+
const { result, rerender } = renderHook(
|
|
58
|
+
({ enabled }) => useAsyncOptions(fetcher, { enabled }),
|
|
59
|
+
{ initialProps: { enabled: false } }
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
expect(fetcher).not.toHaveBeenCalled()
|
|
63
|
+
|
|
64
|
+
rerender({ enabled: true })
|
|
65
|
+
|
|
66
|
+
await waitFor(() => expect(result.current.loading).toBe(false))
|
|
67
|
+
|
|
68
|
+
expect(result.current.data).toEqual(items)
|
|
69
|
+
expect(fetcher).toHaveBeenCalledOnce()
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('captures errors', async () => {
|
|
73
|
+
const fetcher = vi.fn().mockRejectedValue(new Error('Network error'))
|
|
74
|
+
|
|
75
|
+
const { result } = renderHook(() => useAsyncOptions(fetcher))
|
|
76
|
+
|
|
77
|
+
await waitFor(() => expect(result.current.loading).toBe(false))
|
|
78
|
+
|
|
79
|
+
expect(result.current.error).toBeInstanceOf(Error)
|
|
80
|
+
expect(result.current.error?.message).toBe('Network error')
|
|
81
|
+
expect(result.current.data).toEqual([])
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('wraps non-Error rejections', async () => {
|
|
85
|
+
const fetcher = vi.fn().mockRejectedValue('string error')
|
|
86
|
+
|
|
87
|
+
const { result } = renderHook(() => useAsyncOptions(fetcher))
|
|
88
|
+
|
|
89
|
+
await waitFor(() => expect(result.current.loading).toBe(false))
|
|
90
|
+
|
|
91
|
+
expect(result.current.error).toBeInstanceOf(Error)
|
|
92
|
+
expect(result.current.error?.message).toBe('string error')
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('refresh re-triggers the fetch', async () => {
|
|
96
|
+
let callCount = 0
|
|
97
|
+
const fetcher = vi.fn().mockImplementation(() => {
|
|
98
|
+
callCount++
|
|
99
|
+
return Promise.resolve([{ id: callCount }])
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
const { result } = renderHook(() => useAsyncOptions(fetcher))
|
|
103
|
+
|
|
104
|
+
await waitFor(() => expect(result.current.loading).toBe(false))
|
|
105
|
+
expect(result.current.data).toEqual([{ id: 1 }])
|
|
106
|
+
|
|
107
|
+
act(() => {
|
|
108
|
+
result.current.refresh()
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
await waitFor(() => expect(result.current.loading).toBe(false))
|
|
112
|
+
expect(result.current.data).toEqual([{ id: 2 }])
|
|
113
|
+
expect(fetcher).toHaveBeenCalledTimes(2)
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it('does not set state after unmount', async () => {
|
|
117
|
+
let resolvePromise: (value: unknown[]) => void
|
|
118
|
+
const fetcher = vi.fn().mockImplementation(
|
|
119
|
+
() => new Promise<unknown[]>((resolve) => { resolvePromise = resolve })
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
const { result, unmount } = renderHook(() => useAsyncOptions(fetcher))
|
|
123
|
+
|
|
124
|
+
expect(result.current.loading).toBe(true)
|
|
125
|
+
|
|
126
|
+
unmount()
|
|
127
|
+
|
|
128
|
+
// Resolve after unmount — should not throw or update state
|
|
129
|
+
await act(async () => {
|
|
130
|
+
resolvePromise!([{ id: 1 }])
|
|
131
|
+
await new Promise((r) => setTimeout(r, 10))
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
// No error thrown = success (React would warn on set-state-after-unmount)
|
|
135
|
+
})
|
|
136
|
+
})
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
|
2
|
+
import { renderHook, act } from '@testing-library/react'
|
|
3
|
+
import { useEntityTable } from '../useEntityTable'
|
|
4
|
+
|
|
5
|
+
interface TestEntity {
|
|
6
|
+
id: string
|
|
7
|
+
name: string
|
|
8
|
+
value: number
|
|
9
|
+
createdAt: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function makeEntity(id: string, name: string, value: number): TestEntity {
|
|
13
|
+
return { id, name, value, createdAt: '2026-01-15T00:00:00Z' }
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function makeOptions(items: TestEntity[]) {
|
|
17
|
+
return {
|
|
18
|
+
items,
|
|
19
|
+
csvFilename: 'test-export',
|
|
20
|
+
csvColumns: ['Name', 'Value', 'Created'],
|
|
21
|
+
csvRowMapper: (item: TestEntity) => [
|
|
22
|
+
item.name,
|
|
23
|
+
item.value.toString(),
|
|
24
|
+
new Date(item.createdAt).toLocaleDateString(),
|
|
25
|
+
],
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Mock URL APIs for CSV export
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
vi.clearAllMocks()
|
|
32
|
+
vi.stubGlobal('URL', {
|
|
33
|
+
createObjectURL: vi.fn().mockReturnValue('blob:test'),
|
|
34
|
+
revokeObjectURL: vi.fn(),
|
|
35
|
+
})
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Patches document.createElement so only 'a' tags return a fake anchor,
|
|
40
|
+
* while React's internal DOM calls go through the real implementation.
|
|
41
|
+
*/
|
|
42
|
+
function mockAnchorElement() {
|
|
43
|
+
const clickSpy = vi.fn()
|
|
44
|
+
const realCreateElement = document.createElement.bind(document)
|
|
45
|
+
const spy = vi.spyOn(document, 'createElement').mockImplementation((tag: string, options?: ElementCreationOptions) => {
|
|
46
|
+
if (tag === 'a') {
|
|
47
|
+
return { href: '', download: '', click: clickSpy } as unknown as HTMLAnchorElement
|
|
48
|
+
}
|
|
49
|
+
return realCreateElement(tag, options)
|
|
50
|
+
})
|
|
51
|
+
return { clickSpy, restore: () => spy.mockRestore() }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
describe('useEntityTable', () => {
|
|
55
|
+
describe('modal state', () => {
|
|
56
|
+
it('initializes with null/closed state', () => {
|
|
57
|
+
const { result } = renderHook(() =>
|
|
58
|
+
useEntityTable<TestEntity>(makeOptions([]))
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
expect(result.current.selectedItem).toBeNull()
|
|
62
|
+
expect(result.current.editingItem).toBeNull()
|
|
63
|
+
expect(result.current.viewMode).toBeNull()
|
|
64
|
+
expect(result.current.showCreateForm).toBe(false)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('openView sets selectedItem and viewMode to view', () => {
|
|
68
|
+
const entity = makeEntity('1', 'Acme', 100)
|
|
69
|
+
const { result } = renderHook(() =>
|
|
70
|
+
useEntityTable<TestEntity>(makeOptions([entity]))
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
act(() => {
|
|
74
|
+
result.current.openView(entity)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
expect(result.current.selectedItem).toBe(entity)
|
|
78
|
+
expect(result.current.editingItem).toBeNull()
|
|
79
|
+
expect(result.current.viewMode).toBe('view')
|
|
80
|
+
expect(result.current.showCreateForm).toBe(false)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('openEdit sets editingItem and viewMode to edit', () => {
|
|
84
|
+
const entity = makeEntity('1', 'Acme', 100)
|
|
85
|
+
const { result } = renderHook(() =>
|
|
86
|
+
useEntityTable<TestEntity>(makeOptions([entity]))
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
act(() => {
|
|
90
|
+
result.current.openEdit(entity)
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
expect(result.current.selectedItem).toBeNull()
|
|
94
|
+
expect(result.current.editingItem).toBe(entity)
|
|
95
|
+
expect(result.current.viewMode).toBe('edit')
|
|
96
|
+
expect(result.current.showCreateForm).toBe(false)
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
it('openCreate shows create form and clears other state', () => {
|
|
100
|
+
const entity = makeEntity('1', 'Acme', 100)
|
|
101
|
+
const { result } = renderHook(() =>
|
|
102
|
+
useEntityTable<TestEntity>(makeOptions([entity]))
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
act(() => {
|
|
106
|
+
result.current.openView(entity)
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
act(() => {
|
|
110
|
+
result.current.openCreate()
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
expect(result.current.selectedItem).toBeNull()
|
|
114
|
+
expect(result.current.editingItem).toBeNull()
|
|
115
|
+
expect(result.current.viewMode).toBeNull()
|
|
116
|
+
expect(result.current.showCreateForm).toBe(true)
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it('closePanel resets all modal state', () => {
|
|
120
|
+
const entity = makeEntity('1', 'Acme', 100)
|
|
121
|
+
const { result } = renderHook(() =>
|
|
122
|
+
useEntityTable<TestEntity>(makeOptions([entity]))
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
act(() => {
|
|
126
|
+
result.current.openEdit(entity)
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
act(() => {
|
|
130
|
+
result.current.closePanel()
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
expect(result.current.selectedItem).toBeNull()
|
|
134
|
+
expect(result.current.editingItem).toBeNull()
|
|
135
|
+
expect(result.current.viewMode).toBeNull()
|
|
136
|
+
expect(result.current.showCreateForm).toBe(false)
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
it('openView clears create form', () => {
|
|
140
|
+
const entity = makeEntity('1', 'Acme', 100)
|
|
141
|
+
const { result } = renderHook(() =>
|
|
142
|
+
useEntityTable<TestEntity>(makeOptions([entity]))
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
act(() => {
|
|
146
|
+
result.current.openCreate()
|
|
147
|
+
})
|
|
148
|
+
expect(result.current.showCreateForm).toBe(true)
|
|
149
|
+
|
|
150
|
+
act(() => {
|
|
151
|
+
result.current.openView(entity)
|
|
152
|
+
})
|
|
153
|
+
expect(result.current.showCreateForm).toBe(false)
|
|
154
|
+
expect(result.current.viewMode).toBe('view')
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
it('openEdit clears create form', () => {
|
|
158
|
+
const entity = makeEntity('1', 'Acme', 100)
|
|
159
|
+
const { result } = renderHook(() =>
|
|
160
|
+
useEntityTable<TestEntity>(makeOptions([entity]))
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
act(() => {
|
|
164
|
+
result.current.openCreate()
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
act(() => {
|
|
168
|
+
result.current.openEdit(entity)
|
|
169
|
+
})
|
|
170
|
+
expect(result.current.showCreateForm).toBe(false)
|
|
171
|
+
expect(result.current.viewMode).toBe('edit')
|
|
172
|
+
})
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
describe('CSV export', () => {
|
|
176
|
+
let anchorMock: { clickSpy: ReturnType<typeof vi.fn>; restore: () => void }
|
|
177
|
+
|
|
178
|
+
afterEach(() => {
|
|
179
|
+
anchorMock?.restore()
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
it('exports all items when no IDs provided', async () => {
|
|
183
|
+
const items = [
|
|
184
|
+
makeEntity('1', 'Acme', 100),
|
|
185
|
+
makeEntity('2', 'Beta', 200),
|
|
186
|
+
]
|
|
187
|
+
const { result } = renderHook(() =>
|
|
188
|
+
useEntityTable<TestEntity>(makeOptions(items))
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
anchorMock = mockAnchorElement()
|
|
192
|
+
|
|
193
|
+
await act(async () => {
|
|
194
|
+
await result.current.exportCSV()
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
expect(anchorMock.clickSpy).toHaveBeenCalledOnce()
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
it('exports only selected items when IDs provided', async () => {
|
|
201
|
+
const items = [
|
|
202
|
+
makeEntity('1', 'Acme', 100),
|
|
203
|
+
makeEntity('2', 'Beta', 200),
|
|
204
|
+
makeEntity('3', 'Gamma', 300),
|
|
205
|
+
]
|
|
206
|
+
const { result } = renderHook(() =>
|
|
207
|
+
useEntityTable<TestEntity>(makeOptions(items))
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
anchorMock = mockAnchorElement()
|
|
211
|
+
|
|
212
|
+
await act(async () => {
|
|
213
|
+
await result.current.exportCSV(new Set(['1', '3']))
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
expect(anchorMock.clickSpy).toHaveBeenCalledOnce()
|
|
217
|
+
// After export, exportIdsRef should be cleared
|
|
218
|
+
expect(result.current.exportIdsRef.current).toBeNull()
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
it('clears exportIdsRef after export completes', async () => {
|
|
222
|
+
const items = [makeEntity('1', 'Acme', 100)]
|
|
223
|
+
const { result } = renderHook(() =>
|
|
224
|
+
useEntityTable<TestEntity>(makeOptions(items))
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
anchorMock = mockAnchorElement()
|
|
228
|
+
result.current.exportIdsRef.current = new Set(['1'])
|
|
229
|
+
|
|
230
|
+
await act(async () => {
|
|
231
|
+
await result.current.exportCSV()
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
expect(result.current.exportIdsRef.current).toBeNull()
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
it('provides exportIdsRef for external use', () => {
|
|
238
|
+
const { result } = renderHook(() =>
|
|
239
|
+
useEntityTable<TestEntity>(makeOptions([]))
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
expect(result.current.exportIdsRef).toBeDefined()
|
|
243
|
+
expect(result.current.exportIdsRef.current).toBeNull()
|
|
244
|
+
})
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
describe('custom idField', () => {
|
|
248
|
+
interface CustomIdEntity {
|
|
249
|
+
internalId: string
|
|
250
|
+
label: string
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
let anchorMock: { clickSpy: ReturnType<typeof vi.fn>; restore: () => void }
|
|
254
|
+
|
|
255
|
+
afterEach(() => {
|
|
256
|
+
anchorMock?.restore()
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
it('uses custom idField for CSV filtering', async () => {
|
|
260
|
+
const items: CustomIdEntity[] = [
|
|
261
|
+
{ internalId: 'a', label: 'Alpha' },
|
|
262
|
+
{ internalId: 'b', label: 'Bravo' },
|
|
263
|
+
]
|
|
264
|
+
|
|
265
|
+
const { result } = renderHook(() =>
|
|
266
|
+
useEntityTable<CustomIdEntity>({
|
|
267
|
+
items,
|
|
268
|
+
idField: 'internalId',
|
|
269
|
+
csvFilename: 'custom',
|
|
270
|
+
csvColumns: ['Label'],
|
|
271
|
+
csvRowMapper: (item) => [item.label],
|
|
272
|
+
})
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
anchorMock = mockAnchorElement()
|
|
276
|
+
|
|
277
|
+
await act(async () => {
|
|
278
|
+
await result.current.exportCSV(new Set(['a']))
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
// Should not throw; idField 'internalId' was used for filtering
|
|
282
|
+
expect(result.current.exportIdsRef.current).toBeNull()
|
|
283
|
+
})
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
describe('generic typing', () => {
|
|
287
|
+
it('preserves entity type in selectedItem', () => {
|
|
288
|
+
const entity = makeEntity('1', 'Acme', 100)
|
|
289
|
+
const { result } = renderHook(() =>
|
|
290
|
+
useEntityTable<TestEntity>(makeOptions([entity]))
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
act(() => {
|
|
294
|
+
result.current.openView(entity)
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
// TypeScript compile-time check: selectedItem is TestEntity | null
|
|
298
|
+
const item = result.current.selectedItem
|
|
299
|
+
if (item) {
|
|
300
|
+
expect(item.name).toBe('Acme')
|
|
301
|
+
expect(item.value).toBe(100)
|
|
302
|
+
}
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
it('preserves entity type in editingItem', () => {
|
|
306
|
+
const entity = makeEntity('1', 'Acme', 100)
|
|
307
|
+
const { result } = renderHook(() =>
|
|
308
|
+
useEntityTable<TestEntity>(makeOptions([entity]))
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
act(() => {
|
|
312
|
+
result.current.openEdit(entity)
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
const item = result.current.editingItem
|
|
316
|
+
if (item) {
|
|
317
|
+
expect(item.name).toBe('Acme')
|
|
318
|
+
expect(item.value).toBe(100)
|
|
319
|
+
}
|
|
320
|
+
})
|
|
321
|
+
})
|
|
322
|
+
})
|