sanity-plugin-media 4.3.6 → 5.0.0
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 +6 -15
- package/dist/index.cjs +0 -4721
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -239
- package/dist/index.d.cts.map +0 -1
- package/sanity.json +0 -8
- package/src/__tests__/fixtures/createEpicTestStore.ts +0 -28
- package/src/__tests__/fixtures/listenMock.ts +0 -9
- package/src/__tests__/fixtures/mockSanityClient.ts +0 -84
- package/src/__tests__/fixtures/renderWithProviders.tsx +0 -55
- package/src/__tests__/fixtures/rootState.ts +0 -27
- package/src/__tests__/fixtures/withinDialog.ts +0 -28
- package/src/components/AssetGridVirtualized/index.tsx +0 -94
- package/src/components/AssetMetadata/index.tsx +0 -122
- package/src/components/AssetTableVirtualized/index.tsx +0 -73
- package/src/components/AutoTagInputWrapper/index.tsx +0 -85
- package/src/components/Browser/Browser.test.tsx +0 -45
- package/src/components/Browser/index.tsx +0 -90
- package/src/components/Browser/useBrowserInit.ts +0 -126
- package/src/components/ButtonAssetCopy/index.tsx +0 -65
- package/src/components/ButtonViewGroup/index.tsx +0 -39
- package/src/components/CardAsset/CardAsset.test.tsx +0 -323
- package/src/components/CardAsset/index.tsx +0 -290
- package/src/components/CardUpload/index.tsx +0 -161
- package/src/components/Controls/index.tsx +0 -136
- package/src/components/DebugControls/index.tsx +0 -80
- package/src/components/Dialog/index.tsx +0 -11
- package/src/components/DialogAssetEdit/Details.tsx +0 -181
- package/src/components/DialogAssetEdit/DialogAssetEdit.test.tsx +0 -216
- package/src/components/DialogAssetEdit/index.tsx +0 -493
- package/src/components/DialogConfirm/index.tsx +0 -90
- package/src/components/DialogSearchFacets/index.tsx +0 -42
- package/src/components/DialogTagCreate/DialogTagCreate.test.tsx +0 -121
- package/src/components/DialogTagCreate/index.tsx +0 -111
- package/src/components/DialogTagEdit/DialogTagEdit.test.tsx +0 -165
- package/src/components/DialogTagEdit/index.tsx +0 -201
- package/src/components/DialogTags/index.tsx +0 -45
- package/src/components/Dialogs/index.tsx +0 -76
- package/src/components/DocumentList/index.tsx +0 -62
- package/src/components/FileAssetPreview/index.tsx +0 -37
- package/src/components/FileIcon/index.tsx +0 -43
- package/src/components/FormBuilderTool/FormBuilderTool.test.tsx +0 -63
- package/src/components/FormBuilderTool/index.tsx +0 -69
- package/src/components/FormFieldInputLabel/index.tsx +0 -66
- package/src/components/FormFieldInputTags/index.tsx +0 -98
- package/src/components/FormFieldInputText/index.tsx +0 -41
- package/src/components/FormFieldInputTextarea/index.tsx +0 -43
- package/src/components/FormSubmitButton/index.tsx +0 -59
- package/src/components/Header/index.tsx +0 -80
- package/src/components/Image/index.tsx +0 -41
- package/src/components/Items/index.tsx +0 -68
- package/src/components/Notifications/index.tsx +0 -24
- package/src/components/OrderSelect/index.tsx +0 -66
- package/src/components/PickedBar/index.tsx +0 -77
- package/src/components/Progress/index.tsx +0 -38
- package/src/components/ReduxProvider/index.tsx +0 -96
- package/src/components/SearchFacet/index.tsx +0 -66
- package/src/components/SearchFacetNumber/index.tsx +0 -133
- package/src/components/SearchFacetSelect/index.tsx +0 -110
- package/src/components/SearchFacetString/index.tsx +0 -88
- package/src/components/SearchFacetTags/index.tsx +0 -121
- package/src/components/SearchFacets/index.tsx +0 -72
- package/src/components/SearchFacetsControl/index.tsx +0 -140
- package/src/components/TableHeader/index.tsx +0 -110
- package/src/components/TableHeaderItem/index.tsx +0 -61
- package/src/components/TableRowAsset/index.tsx +0 -419
- package/src/components/TableRowUpload/index.tsx +0 -164
- package/src/components/Tag/index.tsx +0 -200
- package/src/components/TagIcon/index.tsx +0 -22
- package/src/components/TagView/index.tsx +0 -39
- package/src/components/TagViewHeader/index.tsx +0 -70
- package/src/components/TagsPanel/index.tsx +0 -40
- package/src/components/TagsVirtualized/index.tsx +0 -160
- package/src/components/TextInputNumber/index.tsx +0 -32
- package/src/components/TextInputSearch/index.tsx +0 -60
- package/src/components/Tool/index.tsx +0 -13
- package/src/components/UploadDropzone/UploadDropzone.test.tsx +0 -40
- package/src/components/UploadDropzone/index.tsx +0 -173
- package/src/config/orders.ts +0 -28
- package/src/config/searchFacets.ts +0 -312
- package/src/constants.ts +0 -87
- package/src/contexts/AssetSourceDispatchContext.tsx +0 -38
- package/src/contexts/DropzoneDispatchContext.tsx +0 -32
- package/src/contexts/ToolOptionsContext.tsx +0 -66
- package/src/formSchema/index.test.ts +0 -56
- package/src/formSchema/index.ts +0 -39
- package/src/hooks/useBreakpointIndex.ts +0 -50
- package/src/hooks/useKeyPress.ts +0 -39
- package/src/hooks/usePortalPopoverProps.ts +0 -13
- package/src/hooks/useTypedSelector.ts +0 -7
- package/src/hooks/useVersionedClient.ts +0 -6
- package/src/index.ts +0 -5
- package/src/modules/assets/actions.ts +0 -42
- package/src/modules/assets/deleteAndUpdateEpics.test.ts +0 -87
- package/src/modules/assets/fetchEpic.test.ts +0 -73
- package/src/modules/assets/index.ts +0 -782
- package/src/modules/assets/reducer.test.ts +0 -91
- package/src/modules/assets/tagsAndListenerEpics.test.ts +0 -206
- package/src/modules/debug/index.ts +0 -28
- package/src/modules/dialog/actions.ts +0 -10
- package/src/modules/dialog/epics.test.ts +0 -168
- package/src/modules/dialog/index.ts +0 -238
- package/src/modules/dialog/reducer.test.ts +0 -185
- package/src/modules/index.ts +0 -117
- package/src/modules/notifications/epics.test.ts +0 -374
- package/src/modules/notifications/index.ts +0 -199
- package/src/modules/notifications/reducer.test.ts +0 -54
- package/src/modules/search/index.test.ts +0 -36
- package/src/modules/search/index.ts +0 -167
- package/src/modules/selected/index.ts +0 -22
- package/src/modules/selectors.test.ts +0 -21
- package/src/modules/selectors.ts +0 -17
- package/src/modules/tags/epics.test.ts +0 -96
- package/src/modules/tags/index.test.ts +0 -42
- package/src/modules/tags/index.ts +0 -540
- package/src/modules/types.ts +0 -3
- package/src/modules/uploads/actions.ts +0 -13
- package/src/modules/uploads/epics.test.ts +0 -109
- package/src/modules/uploads/index.test.ts +0 -59
- package/src/modules/uploads/index.ts +0 -272
- package/src/operators/checkTagName.test.ts +0 -29
- package/src/operators/checkTagName.ts +0 -33
- package/src/operators/debugThrottle.ts +0 -25
- package/src/plugin.tsx +0 -54
- package/src/schemas/tag.ts +0 -28
- package/src/styled/GlobalStyles/index.tsx +0 -40
- package/src/styled/react-select/creatable.tsx +0 -184
- package/src/styled/react-select/single.tsx +0 -184
- package/src/types/index.ts +0 -346
- package/src/types/sanity-ui.d.ts +0 -5
- package/src/utils/applyMediaTags.ts +0 -87
- package/src/utils/blocksToText.test.ts +0 -43
- package/src/utils/blocksToText.ts +0 -27
- package/src/utils/constructFilter.test.ts +0 -120
- package/src/utils/constructFilter.ts +0 -98
- package/src/utils/generatePreviewBlobUrl.test.ts +0 -68
- package/src/utils/generatePreviewBlobUrl.ts +0 -53
- package/src/utils/getAssetResolution.test.ts +0 -13
- package/src/utils/getAssetResolution.ts +0 -7
- package/src/utils/getDocumentAssetIds.test.ts +0 -50
- package/src/utils/getDocumentAssetIds.ts +0 -35
- package/src/utils/getSchemeColor.test.ts +0 -12
- package/src/utils/getSchemeColor.ts +0 -43
- package/src/utils/getTagSelectOptions.test.ts +0 -44
- package/src/utils/getTagSelectOptions.ts +0 -16
- package/src/utils/getUniqueDocuments.test.ts +0 -26
- package/src/utils/getUniqueDocuments.ts +0 -15
- package/src/utils/imageDprUrl.test.ts +0 -46
- package/src/utils/imageDprUrl.ts +0 -27
- package/src/utils/isSupportedAssetType.test.ts +0 -16
- package/src/utils/isSupportedAssetType.ts +0 -15
- package/src/utils/mediaField.ts +0 -73
- package/src/utils/sanitizeFormData.test.ts +0 -59
- package/src/utils/sanitizeFormData.ts +0 -26
- package/src/utils/typeGuards.test.ts +0 -18
- package/src/utils/typeGuards.ts +0 -9
- package/src/utils/uploadSanityAsset.test.ts +0 -29
- package/src/utils/uploadSanityAsset.ts +0 -97
- package/src/utils/withMaxConcurrency.test.ts +0 -43
- package/src/utils/withMaxConcurrency.ts +0 -55
- package/src/utils/zodFormResolver.ts +0 -17
- package/v2-incompatible.js +0 -11
|
@@ -1,374 +0,0 @@
|
|
|
1
|
-
// @vitest-environment node
|
|
2
|
-
|
|
3
|
-
import {describe, expect, it, vi} from 'vitest'
|
|
4
|
-
|
|
5
|
-
import {createEpicTestStore} from '../../__tests__/fixtures/createEpicTestStore'
|
|
6
|
-
import {createMockSanityClient} from '../../__tests__/fixtures/mockSanityClient'
|
|
7
|
-
import type {AssetItem, AssetType, ImageAsset, Tag} from '../../types'
|
|
8
|
-
import {assetsActions, initialState as assetsInitialState} from '../assets'
|
|
9
|
-
import {ASSETS_ACTIONS} from '../assets/actions'
|
|
10
|
-
import {tagsActions} from '../tags'
|
|
11
|
-
import {uploadsActions} from '../uploads'
|
|
12
|
-
import {
|
|
13
|
-
notificationsAssetsDeleteCompleteEpic,
|
|
14
|
-
notificationsAssetsDeleteErrorEpic,
|
|
15
|
-
notificationsAssetsTagsAddCompleteEpic,
|
|
16
|
-
notificationsAssetsTagsRemoveCompleteEpic,
|
|
17
|
-
notificationsAssetsUpdateCompleteEpic,
|
|
18
|
-
notificationsAssetsUploadCompleteEpic,
|
|
19
|
-
notificationsGenericErrorEpic,
|
|
20
|
-
notificationsTagCreateCompleteEpic,
|
|
21
|
-
notificationsTagDeleteCompleteEpic,
|
|
22
|
-
notificationsTagUpdateCompleteEpic,
|
|
23
|
-
} from './index'
|
|
24
|
-
|
|
25
|
-
const sampleAsset = {
|
|
26
|
-
_id: 'a1',
|
|
27
|
-
_type: 'sanity.imageAsset',
|
|
28
|
-
_createdAt: '',
|
|
29
|
-
_updatedAt: '',
|
|
30
|
-
_rev: 'r1',
|
|
31
|
-
originalFilename: 'x.png',
|
|
32
|
-
size: 1,
|
|
33
|
-
mimeType: 'image/png',
|
|
34
|
-
url: 'https://example.com/x.png',
|
|
35
|
-
} as ImageAsset
|
|
36
|
-
|
|
37
|
-
const sampleAsset2 = {
|
|
38
|
-
...sampleAsset,
|
|
39
|
-
_id: 'a2',
|
|
40
|
-
} as ImageAsset
|
|
41
|
-
|
|
42
|
-
const sampleTag: Tag = {
|
|
43
|
-
_id: 't1',
|
|
44
|
-
_type: 'media.tag',
|
|
45
|
-
_createdAt: '',
|
|
46
|
-
_updatedAt: '',
|
|
47
|
-
_rev: 'tr',
|
|
48
|
-
name: {_type: 'slug', current: 'alpha'},
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const assetItem = (asset: ImageAsset): AssetItem => ({
|
|
52
|
-
_type: 'asset',
|
|
53
|
-
asset,
|
|
54
|
-
picked: false,
|
|
55
|
-
updating: false,
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
function assetsWithRows(rows: Record<string, AssetItem>) {
|
|
59
|
-
return {
|
|
60
|
-
...assetsInitialState,
|
|
61
|
-
assetTypes: ['image'] as AssetType[],
|
|
62
|
-
allIds: Object.keys(rows),
|
|
63
|
-
byIds: rows,
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
describe('notificationsAssetsDeleteCompleteEpic', () => {
|
|
68
|
-
it('adds info notification with pluralized asset count', async () => {
|
|
69
|
-
const store = createEpicTestStore(
|
|
70
|
-
notificationsAssetsDeleteCompleteEpic,
|
|
71
|
-
createMockSanityClient({}),
|
|
72
|
-
{},
|
|
73
|
-
)
|
|
74
|
-
store.dispatch(assetsActions.deleteComplete({assetIds: ['x', 'y']}))
|
|
75
|
-
await vi.waitFor(() => {
|
|
76
|
-
expect(store.getState().notifications.items).toEqual([
|
|
77
|
-
{asset: undefined, status: 'info', title: '2 assets deleted'},
|
|
78
|
-
])
|
|
79
|
-
})
|
|
80
|
-
})
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
describe('notificationsAssetsDeleteErrorEpic', () => {
|
|
84
|
-
it('adds error notification with count', async () => {
|
|
85
|
-
const store = createEpicTestStore(
|
|
86
|
-
notificationsAssetsDeleteErrorEpic,
|
|
87
|
-
createMockSanityClient({}),
|
|
88
|
-
{
|
|
89
|
-
assets: assetsWithRows({
|
|
90
|
-
a1: {...assetItem(sampleAsset), updating: true},
|
|
91
|
-
}),
|
|
92
|
-
},
|
|
93
|
-
)
|
|
94
|
-
store.dispatch(assetsActions.deleteError({assetIds: ['a1'], error: {} as any}))
|
|
95
|
-
await vi.waitFor(() => {
|
|
96
|
-
const [n] = store.getState().notifications.items
|
|
97
|
-
expect(n!.status).toBe('error')
|
|
98
|
-
expect(n!.title).toBe(
|
|
99
|
-
'Unable to delete 1 asset. Please review any asset errors and try again.',
|
|
100
|
-
)
|
|
101
|
-
})
|
|
102
|
-
})
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
describe('notificationsAssetsUploadCompleteEpic', () => {
|
|
106
|
-
it('adds info notification from upload check results count', async () => {
|
|
107
|
-
const store = createEpicTestStore(
|
|
108
|
-
notificationsAssetsUploadCompleteEpic,
|
|
109
|
-
createMockSanityClient({}),
|
|
110
|
-
{},
|
|
111
|
-
)
|
|
112
|
-
store.dispatch(
|
|
113
|
-
uploadsActions.checkComplete({
|
|
114
|
-
results: {h1: 'id1', h2: null},
|
|
115
|
-
}),
|
|
116
|
-
)
|
|
117
|
-
await vi.waitFor(() => {
|
|
118
|
-
expect(store.getState().notifications.items).toEqual([
|
|
119
|
-
{asset: undefined, status: 'info', title: 'Uploaded 2 assets'},
|
|
120
|
-
])
|
|
121
|
-
})
|
|
122
|
-
})
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
const tagsWithTagUpdating = {
|
|
126
|
-
allIds: ['t1'],
|
|
127
|
-
byIds: {
|
|
128
|
-
t1: {_type: 'tag' as const, tag: sampleTag, picked: false, updating: true},
|
|
129
|
-
},
|
|
130
|
-
creating: false,
|
|
131
|
-
fetchCount: -1,
|
|
132
|
-
fetching: false,
|
|
133
|
-
panelVisible: true,
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
describe('notificationsAssetsTagsAddCompleteEpic', () => {
|
|
137
|
-
it('adds info notification with asset count', async () => {
|
|
138
|
-
const store = createEpicTestStore(
|
|
139
|
-
notificationsAssetsTagsAddCompleteEpic,
|
|
140
|
-
createMockSanityClient({}),
|
|
141
|
-
{
|
|
142
|
-
assets: assetsWithRows({
|
|
143
|
-
a1: {...assetItem(sampleAsset), updating: true},
|
|
144
|
-
a2: {...assetItem(sampleAsset2), updating: true},
|
|
145
|
-
}),
|
|
146
|
-
tags: tagsWithTagUpdating,
|
|
147
|
-
},
|
|
148
|
-
)
|
|
149
|
-
store.dispatch(
|
|
150
|
-
ASSETS_ACTIONS.tagsAddComplete({
|
|
151
|
-
assets: [assetItem(sampleAsset), assetItem(sampleAsset2)],
|
|
152
|
-
tag: sampleTag,
|
|
153
|
-
}),
|
|
154
|
-
)
|
|
155
|
-
await vi.waitFor(() => {
|
|
156
|
-
expect(store.getState().notifications.items).toEqual([
|
|
157
|
-
{asset: undefined, status: 'info', title: 'Tag added to 2 assets'},
|
|
158
|
-
])
|
|
159
|
-
})
|
|
160
|
-
})
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
describe('notificationsAssetsTagsRemoveCompleteEpic', () => {
|
|
164
|
-
it('adds info notification with asset count', async () => {
|
|
165
|
-
const store = createEpicTestStore(
|
|
166
|
-
notificationsAssetsTagsRemoveCompleteEpic,
|
|
167
|
-
createMockSanityClient({}),
|
|
168
|
-
{
|
|
169
|
-
assets: assetsWithRows({
|
|
170
|
-
a1: {...assetItem(sampleAsset), updating: true},
|
|
171
|
-
}),
|
|
172
|
-
tags: tagsWithTagUpdating,
|
|
173
|
-
},
|
|
174
|
-
)
|
|
175
|
-
store.dispatch(
|
|
176
|
-
ASSETS_ACTIONS.tagsRemoveComplete({
|
|
177
|
-
assets: [assetItem(sampleAsset)],
|
|
178
|
-
tag: sampleTag,
|
|
179
|
-
}),
|
|
180
|
-
)
|
|
181
|
-
await vi.waitFor(() => {
|
|
182
|
-
expect(store.getState().notifications.items).toEqual([
|
|
183
|
-
{asset: undefined, status: 'info', title: 'Tag removed from 1 asset'},
|
|
184
|
-
])
|
|
185
|
-
})
|
|
186
|
-
})
|
|
187
|
-
})
|
|
188
|
-
|
|
189
|
-
describe('notificationsAssetsUpdateCompleteEpic', () => {
|
|
190
|
-
it('batches multiple updateComplete actions into one notification after buffer window', async () => {
|
|
191
|
-
vi.useFakeTimers()
|
|
192
|
-
const store = createEpicTestStore(
|
|
193
|
-
notificationsAssetsUpdateCompleteEpic,
|
|
194
|
-
createMockSanityClient({}),
|
|
195
|
-
{
|
|
196
|
-
assets: assetsWithRows({
|
|
197
|
-
a1: {...assetItem(sampleAsset), updating: true},
|
|
198
|
-
a2: {...assetItem(sampleAsset2), updating: true},
|
|
199
|
-
}),
|
|
200
|
-
},
|
|
201
|
-
)
|
|
202
|
-
|
|
203
|
-
store.dispatch(assetsActions.updateComplete({asset: sampleAsset}))
|
|
204
|
-
store.dispatch(assetsActions.updateComplete({asset: sampleAsset2}))
|
|
205
|
-
await vi.advanceTimersByTimeAsync(2000)
|
|
206
|
-
|
|
207
|
-
expect(store.getState().notifications.items).toEqual([
|
|
208
|
-
{asset: undefined, status: 'info', title: '2 assets updated'},
|
|
209
|
-
])
|
|
210
|
-
vi.useRealTimers()
|
|
211
|
-
})
|
|
212
|
-
|
|
213
|
-
it('emits separate notifications when updates fall in different buffer windows', async () => {
|
|
214
|
-
vi.useFakeTimers()
|
|
215
|
-
const store = createEpicTestStore(
|
|
216
|
-
notificationsAssetsUpdateCompleteEpic,
|
|
217
|
-
createMockSanityClient({}),
|
|
218
|
-
{
|
|
219
|
-
assets: assetsWithRows({
|
|
220
|
-
a1: {...assetItem(sampleAsset), updating: true},
|
|
221
|
-
a2: {...assetItem(sampleAsset2), updating: true},
|
|
222
|
-
}),
|
|
223
|
-
},
|
|
224
|
-
)
|
|
225
|
-
|
|
226
|
-
store.dispatch(assetsActions.updateComplete({asset: sampleAsset}))
|
|
227
|
-
await vi.advanceTimersByTimeAsync(2000)
|
|
228
|
-
store.dispatch(assetsActions.updateComplete({asset: sampleAsset2}))
|
|
229
|
-
await vi.advanceTimersByTimeAsync(2000)
|
|
230
|
-
|
|
231
|
-
expect(store.getState().notifications.items).toEqual([
|
|
232
|
-
{asset: undefined, status: 'info', title: '1 asset updated'},
|
|
233
|
-
{asset: undefined, status: 'info', title: '1 asset updated'},
|
|
234
|
-
])
|
|
235
|
-
vi.useRealTimers()
|
|
236
|
-
})
|
|
237
|
-
})
|
|
238
|
-
|
|
239
|
-
describe('notificationsGenericErrorEpic', () => {
|
|
240
|
-
it('maps assets.updateError to error notification title', async () => {
|
|
241
|
-
const store = createEpicTestStore(notificationsGenericErrorEpic, createMockSanityClient({}), {
|
|
242
|
-
assets: assetsWithRows({
|
|
243
|
-
a1: {...assetItem(sampleAsset), updating: true},
|
|
244
|
-
}),
|
|
245
|
-
})
|
|
246
|
-
store.dispatch(
|
|
247
|
-
assetsActions.updateError({
|
|
248
|
-
asset: sampleAsset,
|
|
249
|
-
error: {message: 'patch failed', statusCode: 500},
|
|
250
|
-
}),
|
|
251
|
-
)
|
|
252
|
-
await vi.waitFor(() => {
|
|
253
|
-
expect(store.getState().notifications.items).toEqual([
|
|
254
|
-
{asset: undefined, status: 'error', title: 'An error occurred: patch failed'},
|
|
255
|
-
])
|
|
256
|
-
})
|
|
257
|
-
})
|
|
258
|
-
|
|
259
|
-
it('maps assets.fetchError (bare HttpError payload) to error notification title', async () => {
|
|
260
|
-
const store = createEpicTestStore(notificationsGenericErrorEpic, createMockSanityClient({}), {
|
|
261
|
-
assets: {
|
|
262
|
-
...assetsInitialState,
|
|
263
|
-
assetTypes: ['image'] as AssetType[],
|
|
264
|
-
fetching: true,
|
|
265
|
-
},
|
|
266
|
-
})
|
|
267
|
-
store.dispatch(
|
|
268
|
-
assetsActions.fetchError({
|
|
269
|
-
message: 'fetch failed',
|
|
270
|
-
statusCode: 503,
|
|
271
|
-
}),
|
|
272
|
-
)
|
|
273
|
-
await vi.waitFor(() => {
|
|
274
|
-
expect(store.getState().notifications.items[0]!.title).toBe('An error occurred: fetch failed')
|
|
275
|
-
})
|
|
276
|
-
})
|
|
277
|
-
|
|
278
|
-
it('maps tags.createError to error notification title', async () => {
|
|
279
|
-
const store = createEpicTestStore(notificationsGenericErrorEpic, createMockSanityClient({}), {
|
|
280
|
-
tags: {
|
|
281
|
-
creating: true,
|
|
282
|
-
creatingError: undefined,
|
|
283
|
-
allIds: [],
|
|
284
|
-
byIds: {},
|
|
285
|
-
fetchCount: -1,
|
|
286
|
-
fetching: false,
|
|
287
|
-
panelVisible: true,
|
|
288
|
-
} as any,
|
|
289
|
-
})
|
|
290
|
-
store.dispatch(
|
|
291
|
-
tagsActions.createError({
|
|
292
|
-
name: 'n',
|
|
293
|
-
error: {message: 'tag create', statusCode: 400},
|
|
294
|
-
}),
|
|
295
|
-
)
|
|
296
|
-
await vi.waitFor(() => {
|
|
297
|
-
expect(store.getState().notifications.items[0]!.title).toBe('An error occurred: tag create')
|
|
298
|
-
})
|
|
299
|
-
})
|
|
300
|
-
|
|
301
|
-
it('maps uploads.uploadError to error notification title', async () => {
|
|
302
|
-
const store = createEpicTestStore(notificationsGenericErrorEpic, createMockSanityClient({}), {
|
|
303
|
-
uploads: {allIds: ['h'], byIds: {h: {} as any}},
|
|
304
|
-
})
|
|
305
|
-
store.dispatch(
|
|
306
|
-
uploadsActions.uploadError({
|
|
307
|
-
hash: 'h',
|
|
308
|
-
error: {message: 'upload bad', statusCode: 413},
|
|
309
|
-
}),
|
|
310
|
-
)
|
|
311
|
-
await vi.waitFor(() => {
|
|
312
|
-
expect(store.getState().notifications.items[0]!.title).toBe('An error occurred: upload bad')
|
|
313
|
-
})
|
|
314
|
-
})
|
|
315
|
-
})
|
|
316
|
-
|
|
317
|
-
describe('notificationsTagCreateCompleteEpic', () => {
|
|
318
|
-
it('adds tag created notification', async () => {
|
|
319
|
-
const store = createEpicTestStore(
|
|
320
|
-
notificationsTagCreateCompleteEpic,
|
|
321
|
-
createMockSanityClient({}),
|
|
322
|
-
{},
|
|
323
|
-
)
|
|
324
|
-
store.dispatch(tagsActions.createComplete({tag: sampleTag}))
|
|
325
|
-
await vi.waitFor(() => {
|
|
326
|
-
expect(store.getState().notifications.items).toEqual([
|
|
327
|
-
{asset: undefined, status: 'info', title: 'Tag created'},
|
|
328
|
-
])
|
|
329
|
-
})
|
|
330
|
-
})
|
|
331
|
-
})
|
|
332
|
-
|
|
333
|
-
describe('notificationsTagDeleteCompleteEpic', () => {
|
|
334
|
-
it('adds tag deleted notification', async () => {
|
|
335
|
-
const store = createEpicTestStore(
|
|
336
|
-
notificationsTagDeleteCompleteEpic,
|
|
337
|
-
createMockSanityClient({}),
|
|
338
|
-
{},
|
|
339
|
-
)
|
|
340
|
-
store.dispatch(tagsActions.deleteComplete({tagId: 't1'}))
|
|
341
|
-
await vi.waitFor(() => {
|
|
342
|
-
expect(store.getState().notifications.items).toEqual([
|
|
343
|
-
{asset: undefined, status: 'info', title: 'Tag deleted'},
|
|
344
|
-
])
|
|
345
|
-
})
|
|
346
|
-
})
|
|
347
|
-
})
|
|
348
|
-
|
|
349
|
-
describe('notificationsTagUpdateCompleteEpic', () => {
|
|
350
|
-
it('adds tag updated notification', async () => {
|
|
351
|
-
const store = createEpicTestStore(
|
|
352
|
-
notificationsTagUpdateCompleteEpic,
|
|
353
|
-
createMockSanityClient({}),
|
|
354
|
-
{
|
|
355
|
-
tags: {
|
|
356
|
-
allIds: ['t1'],
|
|
357
|
-
byIds: {
|
|
358
|
-
t1: {_type: 'tag', tag: sampleTag, picked: false, updating: true},
|
|
359
|
-
},
|
|
360
|
-
creating: false,
|
|
361
|
-
fetchCount: -1,
|
|
362
|
-
fetching: false,
|
|
363
|
-
panelVisible: true,
|
|
364
|
-
},
|
|
365
|
-
},
|
|
366
|
-
)
|
|
367
|
-
store.dispatch(tagsActions.updateComplete({tag: sampleTag}))
|
|
368
|
-
await vi.waitFor(() => {
|
|
369
|
-
expect(store.getState().notifications.items).toEqual([
|
|
370
|
-
{asset: undefined, status: 'info', title: 'Tag updated'},
|
|
371
|
-
])
|
|
372
|
-
})
|
|
373
|
-
})
|
|
374
|
-
})
|
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
import {type PayloadAction, createSlice} from '@reduxjs/toolkit'
|
|
2
|
-
import pluralize from 'pluralize'
|
|
3
|
-
import type {AnyAction} from 'redux'
|
|
4
|
-
import {ofType} from 'redux-observable'
|
|
5
|
-
import {of} from 'rxjs'
|
|
6
|
-
import {bufferTime, filter, mergeMap} from 'rxjs/operators'
|
|
7
|
-
|
|
8
|
-
import type {HttpError, ImageAsset, MyEpic} from '../../types'
|
|
9
|
-
import {assetsActions} from '../assets'
|
|
10
|
-
import {ASSETS_ACTIONS} from '../assets/actions'
|
|
11
|
-
import {tagsActions} from '../tags'
|
|
12
|
-
import {uploadsActions} from '../uploads'
|
|
13
|
-
|
|
14
|
-
type Notification = {
|
|
15
|
-
asset?: ImageAsset
|
|
16
|
-
status?: 'error' | 'warning' | 'success' | 'info'
|
|
17
|
-
title?: string
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
type NotificationsReducerState = {
|
|
21
|
-
items: Notification[]
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function messageFromGenericErrorPayload(payload: unknown): string {
|
|
25
|
-
if (!payload || typeof payload !== 'object') {
|
|
26
|
-
return 'Unknown error'
|
|
27
|
-
}
|
|
28
|
-
if (
|
|
29
|
-
'error' in payload &&
|
|
30
|
-
payload.error &&
|
|
31
|
-
typeof payload.error === 'object' &&
|
|
32
|
-
payload.error !== null &&
|
|
33
|
-
'message' in payload.error
|
|
34
|
-
) {
|
|
35
|
-
return String((payload.error as HttpError).message)
|
|
36
|
-
}
|
|
37
|
-
if ('message' in payload && typeof (payload as HttpError).message === 'string') {
|
|
38
|
-
return String((payload as HttpError).message)
|
|
39
|
-
}
|
|
40
|
-
return 'Unknown error'
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const initialState = {
|
|
44
|
-
items: [],
|
|
45
|
-
} as NotificationsReducerState
|
|
46
|
-
|
|
47
|
-
const notificationsSlice = createSlice({
|
|
48
|
-
name: 'notifications',
|
|
49
|
-
initialState,
|
|
50
|
-
reducers: {
|
|
51
|
-
add(state, action: PayloadAction<Notification>) {
|
|
52
|
-
const {asset, status, title} = action.payload
|
|
53
|
-
state.items.push({
|
|
54
|
-
asset,
|
|
55
|
-
status,
|
|
56
|
-
title,
|
|
57
|
-
})
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
// Epics
|
|
63
|
-
|
|
64
|
-
export const notificationsAssetsDeleteCompleteEpic: MyEpic = (action$) =>
|
|
65
|
-
action$.pipe(
|
|
66
|
-
filter(assetsActions.deleteComplete.match),
|
|
67
|
-
mergeMap((action) => {
|
|
68
|
-
const {assetIds} = action.payload
|
|
69
|
-
const deletedCount = assetIds.length
|
|
70
|
-
return of(
|
|
71
|
-
notificationsSlice.actions.add({
|
|
72
|
-
status: 'info',
|
|
73
|
-
title: `${deletedCount} ${pluralize('asset', deletedCount)} deleted`,
|
|
74
|
-
}),
|
|
75
|
-
)
|
|
76
|
-
}),
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
export const notificationsAssetsDeleteErrorEpic: MyEpic = (action$) =>
|
|
80
|
-
action$.pipe(
|
|
81
|
-
filter(assetsActions.deleteError.match),
|
|
82
|
-
mergeMap((action) => {
|
|
83
|
-
const {assetIds} = action.payload
|
|
84
|
-
const count = assetIds.length
|
|
85
|
-
return of(
|
|
86
|
-
notificationsSlice.actions.add({
|
|
87
|
-
status: 'error',
|
|
88
|
-
title: `Unable to delete ${count} ${pluralize(
|
|
89
|
-
'asset',
|
|
90
|
-
count,
|
|
91
|
-
)}. Please review any asset errors and try again.`,
|
|
92
|
-
}),
|
|
93
|
-
)
|
|
94
|
-
}),
|
|
95
|
-
)
|
|
96
|
-
|
|
97
|
-
export const notificationsAssetsUploadCompleteEpic: MyEpic = (action$) =>
|
|
98
|
-
action$.pipe(
|
|
99
|
-
filter(uploadsActions.checkComplete.match),
|
|
100
|
-
mergeMap((action) => {
|
|
101
|
-
const {results} = action.payload
|
|
102
|
-
|
|
103
|
-
const count = Object.keys(results).length
|
|
104
|
-
return of(
|
|
105
|
-
notificationsSlice.actions.add({
|
|
106
|
-
status: 'info',
|
|
107
|
-
title: `Uploaded ${count} ${pluralize('asset', count)}`,
|
|
108
|
-
}),
|
|
109
|
-
)
|
|
110
|
-
}),
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
export const notificationsAssetsTagsAddCompleteEpic: MyEpic = (action$) =>
|
|
114
|
-
action$.pipe(
|
|
115
|
-
filter(ASSETS_ACTIONS.tagsAddComplete.match),
|
|
116
|
-
mergeMap((action) => {
|
|
117
|
-
const count = action?.payload?.assets?.length
|
|
118
|
-
return of(
|
|
119
|
-
notificationsSlice.actions.add({
|
|
120
|
-
status: 'info',
|
|
121
|
-
title: `Tag added to ${count} ${pluralize('asset', count)}`,
|
|
122
|
-
}),
|
|
123
|
-
)
|
|
124
|
-
}),
|
|
125
|
-
)
|
|
126
|
-
|
|
127
|
-
export const notificationsAssetsTagsRemoveCompleteEpic: MyEpic = (action$) =>
|
|
128
|
-
action$.pipe(
|
|
129
|
-
filter(ASSETS_ACTIONS.tagsRemoveComplete.match),
|
|
130
|
-
mergeMap((action) => {
|
|
131
|
-
const count = action?.payload?.assets?.length
|
|
132
|
-
return of(
|
|
133
|
-
notificationsSlice.actions.add({
|
|
134
|
-
status: 'info',
|
|
135
|
-
title: `Tag removed from ${count} ${pluralize('asset', count)}`,
|
|
136
|
-
}),
|
|
137
|
-
)
|
|
138
|
-
}),
|
|
139
|
-
)
|
|
140
|
-
|
|
141
|
-
export const notificationsAssetsUpdateCompleteEpic: MyEpic = (action$) =>
|
|
142
|
-
action$.pipe(
|
|
143
|
-
filter(assetsActions.updateComplete.match),
|
|
144
|
-
bufferTime(2000),
|
|
145
|
-
filter((actions) => actions.length > 0),
|
|
146
|
-
mergeMap((actions) => {
|
|
147
|
-
const updatedCount = actions.length
|
|
148
|
-
return of(
|
|
149
|
-
notificationsSlice.actions.add({
|
|
150
|
-
status: 'info',
|
|
151
|
-
title: `${updatedCount} ${pluralize('asset', updatedCount)} updated`,
|
|
152
|
-
}),
|
|
153
|
-
)
|
|
154
|
-
}),
|
|
155
|
-
)
|
|
156
|
-
|
|
157
|
-
export const notificationsGenericErrorEpic: MyEpic = (action$) =>
|
|
158
|
-
action$.pipe(
|
|
159
|
-
ofType(
|
|
160
|
-
assetsActions.fetchError.type,
|
|
161
|
-
assetsActions.updateError.type,
|
|
162
|
-
tagsActions.createError.type,
|
|
163
|
-
tagsActions.deleteError.type,
|
|
164
|
-
tagsActions.fetchError.type,
|
|
165
|
-
tagsActions.updateError.type,
|
|
166
|
-
uploadsActions.uploadError.type,
|
|
167
|
-
),
|
|
168
|
-
mergeMap((action: AnyAction) => {
|
|
169
|
-
const title = `An error occurred: ${messageFromGenericErrorPayload(action['payload'])}`
|
|
170
|
-
return of(
|
|
171
|
-
notificationsSlice.actions.add({
|
|
172
|
-
status: 'error',
|
|
173
|
-
title,
|
|
174
|
-
}),
|
|
175
|
-
)
|
|
176
|
-
}),
|
|
177
|
-
)
|
|
178
|
-
|
|
179
|
-
export const notificationsTagCreateCompleteEpic: MyEpic = (action$) =>
|
|
180
|
-
action$.pipe(
|
|
181
|
-
filter(tagsActions.createComplete.match),
|
|
182
|
-
mergeMap(() => of(notificationsSlice.actions.add({status: 'info', title: `Tag created`}))),
|
|
183
|
-
)
|
|
184
|
-
|
|
185
|
-
export const notificationsTagDeleteCompleteEpic: MyEpic = (action$) =>
|
|
186
|
-
action$.pipe(
|
|
187
|
-
filter(tagsActions.deleteComplete.match),
|
|
188
|
-
mergeMap(() => of(notificationsSlice.actions.add({status: 'info', title: `Tag deleted`}))),
|
|
189
|
-
)
|
|
190
|
-
|
|
191
|
-
export const notificationsTagUpdateCompleteEpic: MyEpic = (action$) =>
|
|
192
|
-
action$.pipe(
|
|
193
|
-
filter(tagsActions.updateComplete.match),
|
|
194
|
-
mergeMap(() => of(notificationsSlice.actions.add({status: 'info', title: `Tag updated`}))),
|
|
195
|
-
)
|
|
196
|
-
|
|
197
|
-
export const notificationsActions = {...notificationsSlice.actions}
|
|
198
|
-
|
|
199
|
-
export default notificationsSlice.reducer
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
// @vitest-environment node
|
|
2
|
-
|
|
3
|
-
import {describe, expect, it} from 'vitest'
|
|
4
|
-
|
|
5
|
-
import {createTestRootState} from '../../__tests__/fixtures/rootState'
|
|
6
|
-
import type {ImageAsset} from '../../types'
|
|
7
|
-
import notificationsReducer, {notificationsActions} from './index'
|
|
8
|
-
|
|
9
|
-
const sampleAsset = {
|
|
10
|
-
_id: 'a1',
|
|
11
|
-
_type: 'sanity.imageAsset',
|
|
12
|
-
_createdAt: '',
|
|
13
|
-
_updatedAt: '',
|
|
14
|
-
_rev: 'r1',
|
|
15
|
-
originalFilename: 'x.png',
|
|
16
|
-
size: 1,
|
|
17
|
-
mimeType: 'image/png',
|
|
18
|
-
url: 'https://example.com/x.png',
|
|
19
|
-
} as ImageAsset
|
|
20
|
-
|
|
21
|
-
describe('notificationsReducer', () => {
|
|
22
|
-
it('starts with no items', () => {
|
|
23
|
-
const state = notificationsReducer(undefined, {type: '@@init'})
|
|
24
|
-
expect(state.items).toEqual([])
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
it('add appends a notification with asset, status, and title', () => {
|
|
28
|
-
const prev = createTestRootState().notifications
|
|
29
|
-
const next = notificationsReducer(
|
|
30
|
-
prev,
|
|
31
|
-
notificationsActions.add({
|
|
32
|
-
asset: sampleAsset,
|
|
33
|
-
status: 'success',
|
|
34
|
-
title: 'Done',
|
|
35
|
-
}),
|
|
36
|
-
)
|
|
37
|
-
expect(next.items).toHaveLength(1)
|
|
38
|
-
expect(next.items[0]).toEqual({
|
|
39
|
-
asset: sampleAsset,
|
|
40
|
-
status: 'success',
|
|
41
|
-
title: 'Done',
|
|
42
|
-
})
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
it('add allows partial payloads', () => {
|
|
46
|
-
let state = createTestRootState().notifications
|
|
47
|
-
state = notificationsReducer(state, notificationsActions.add({status: 'error', title: 'X'}))
|
|
48
|
-
state = notificationsReducer(state, notificationsActions.add({title: 'Y'}))
|
|
49
|
-
expect(state.items).toEqual([
|
|
50
|
-
{asset: undefined, status: 'error', title: 'X'},
|
|
51
|
-
{asset: undefined, status: undefined, title: 'Y'},
|
|
52
|
-
])
|
|
53
|
-
})
|
|
54
|
-
})
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
// @vitest-environment node
|
|
2
|
-
|
|
3
|
-
import {describe, expect, it} from 'vitest'
|
|
4
|
-
|
|
5
|
-
import {inputs} from '../../config/searchFacets'
|
|
6
|
-
import searchReducer, {searchActions} from './index'
|
|
7
|
-
|
|
8
|
-
describe('search slice', () => {
|
|
9
|
-
it('facetsAdd assigns an id and appends facet', () => {
|
|
10
|
-
let state = searchReducer(undefined, {type: '@@INIT'} as never)
|
|
11
|
-
state = searchReducer(state, searchActions.facetsAdd({facet: {...inputs.title}}))
|
|
12
|
-
expect(state.facets).toHaveLength(1)
|
|
13
|
-
expect(state.facets[0]!.name).toBe('title')
|
|
14
|
-
expect(state.facets[0]!.id).toBeDefined()
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
it('querySet updates search string', () => {
|
|
18
|
-
let state = searchReducer(undefined, {type: '@@INIT'} as never)
|
|
19
|
-
state = searchReducer(state, searchActions.querySet({searchQuery: 'cats'}))
|
|
20
|
-
expect(state.query).toBe('cats')
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
it('facetsClear removes all facets', () => {
|
|
24
|
-
let state = searchReducer(undefined, {type: '@@INIT'} as never)
|
|
25
|
-
state = searchReducer(state, searchActions.facetsAdd({facet: {...inputs.title}}))
|
|
26
|
-
state = searchReducer(state, searchActions.facetsClear())
|
|
27
|
-
expect(state.facets).toHaveLength(0)
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
it('facetsRemoveByName filters facets', () => {
|
|
31
|
-
let state = searchReducer(undefined, {type: '@@INIT'} as never)
|
|
32
|
-
state = searchReducer(state, searchActions.facetsAdd({facet: {...inputs.title}}))
|
|
33
|
-
state = searchReducer(state, searchActions.facetsRemoveByName({facetName: 'title'}))
|
|
34
|
-
expect(state.facets).toHaveLength(0)
|
|
35
|
-
})
|
|
36
|
-
})
|