sanity-plugin-media 4.3.6 → 5.0.1
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 +8 -17
- 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,323 +0,0 @@
|
|
|
1
|
-
import {screen} from '@testing-library/react'
|
|
2
|
-
import userEvent from '@testing-library/user-event'
|
|
3
|
-
import type {RefObject} from 'react'
|
|
4
|
-
import {beforeEach, describe, expect, it, vi} from 'vitest'
|
|
5
|
-
|
|
6
|
-
import {renderWithProviders} from '../../__tests__/fixtures/renderWithProviders'
|
|
7
|
-
import {initialState as assetsInitialState} from '../../modules/assets'
|
|
8
|
-
import type {AssetItem, AssetType, FileAsset, ImageAsset} from '../../types'
|
|
9
|
-
import CardAsset from './index'
|
|
10
|
-
|
|
11
|
-
const SHIFT_FLAG = '__CARD_ASSET_TEST_SHIFT__'
|
|
12
|
-
|
|
13
|
-
function setShiftPressed(on: boolean) {
|
|
14
|
-
const g = globalThis as unknown as Record<string, boolean | undefined>
|
|
15
|
-
if (on) {
|
|
16
|
-
g[SHIFT_FLAG] = true
|
|
17
|
-
} else {
|
|
18
|
-
delete g[SHIFT_FLAG]
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
vi.mock('../../hooks/useKeyPress', () => ({
|
|
23
|
-
default: (): RefObject<boolean> =>
|
|
24
|
-
({
|
|
25
|
-
get current() {
|
|
26
|
-
return Boolean((globalThis as unknown as Record<string, unknown>)[SHIFT_FLAG])
|
|
27
|
-
},
|
|
28
|
-
}) as RefObject<boolean>,
|
|
29
|
-
}))
|
|
30
|
-
|
|
31
|
-
vi.mock('../Image', () => ({
|
|
32
|
-
default: () => <div data-testid="card-image" />,
|
|
33
|
-
}))
|
|
34
|
-
|
|
35
|
-
vi.mock('../FileIcon', () => ({
|
|
36
|
-
default: ({extension}: {extension?: string}) => (
|
|
37
|
-
<div data-testid="card-file-icon" data-extension={extension ?? ''} />
|
|
38
|
-
),
|
|
39
|
-
}))
|
|
40
|
-
|
|
41
|
-
vi.mock('sanity', async (importOriginal) => {
|
|
42
|
-
const actual = await importOriginal<typeof import('sanity')>()
|
|
43
|
-
return {
|
|
44
|
-
...actual,
|
|
45
|
-
useColorSchemeValue: () => 'light',
|
|
46
|
-
}
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
const imageAsset = {
|
|
50
|
-
_id: 'img-1',
|
|
51
|
-
_type: 'sanity.imageAsset',
|
|
52
|
-
_createdAt: '',
|
|
53
|
-
_updatedAt: '',
|
|
54
|
-
_rev: 'r1',
|
|
55
|
-
originalFilename: 'photo.png',
|
|
56
|
-
size: 1,
|
|
57
|
-
mimeType: 'image/png',
|
|
58
|
-
url: 'https://example.com/photo.png',
|
|
59
|
-
metadata: {dimensions: {width: 100, height: 100}, isOpaque: true},
|
|
60
|
-
} as ImageAsset
|
|
61
|
-
|
|
62
|
-
const fileAsset = {
|
|
63
|
-
_id: 'file-1',
|
|
64
|
-
_type: 'sanity.fileAsset',
|
|
65
|
-
_createdAt: '',
|
|
66
|
-
_updatedAt: '',
|
|
67
|
-
_rev: 'r1',
|
|
68
|
-
originalFilename: 'doc.pdf',
|
|
69
|
-
extension: 'pdf',
|
|
70
|
-
size: 1,
|
|
71
|
-
mimeType: 'application/pdf',
|
|
72
|
-
url: 'https://example.com/doc.pdf',
|
|
73
|
-
} as FileAsset
|
|
74
|
-
|
|
75
|
-
function assetItem(asset: ImageAsset | FileAsset, partial?: Partial<AssetItem>): AssetItem {
|
|
76
|
-
return {
|
|
77
|
-
_type: 'asset',
|
|
78
|
-
asset,
|
|
79
|
-
picked: false,
|
|
80
|
-
updating: false,
|
|
81
|
-
...partial,
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function assetsState(byIds: Record<string, AssetItem>, extra?: Partial<typeof assetsInitialState>) {
|
|
86
|
-
return {
|
|
87
|
-
...assetsInitialState,
|
|
88
|
-
assetTypes: ['file', 'image'] as AssetType[],
|
|
89
|
-
allIds: Object.keys(byIds),
|
|
90
|
-
byIds,
|
|
91
|
-
...extra,
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function clickPreview() {
|
|
96
|
-
const imgs = screen.getAllByTestId('card-image')
|
|
97
|
-
const img = imgs.at(-1)
|
|
98
|
-
if (!img) {
|
|
99
|
-
throw new Error('card-image missing')
|
|
100
|
-
}
|
|
101
|
-
const target = img.parentElement
|
|
102
|
-
if (!target) {
|
|
103
|
-
throw new Error('preview wrapper missing')
|
|
104
|
-
}
|
|
105
|
-
return target
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function clickFooterFilename(text: string) {
|
|
109
|
-
const nodes = screen.getAllByText(text)
|
|
110
|
-
const el = nodes.at(-1)
|
|
111
|
-
if (!el) {
|
|
112
|
-
throw new Error(`footer text missing: ${text}`)
|
|
113
|
-
}
|
|
114
|
-
return el
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
beforeEach(() => {
|
|
118
|
-
setShiftPressed(false)
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
describe('CardAsset', () => {
|
|
122
|
-
it('renders nothing when the asset id is not in the store', () => {
|
|
123
|
-
renderWithProviders(<CardAsset id="missing" selected={false} />, {
|
|
124
|
-
preloaded: {
|
|
125
|
-
assets: assetsState({}),
|
|
126
|
-
},
|
|
127
|
-
})
|
|
128
|
-
expect(screen.queryAllByTestId('card-image')).toHaveLength(0)
|
|
129
|
-
expect(screen.queryAllByTestId('card-file-icon')).toHaveLength(0)
|
|
130
|
-
})
|
|
131
|
-
|
|
132
|
-
it('renders image preview and original filename for an image asset', () => {
|
|
133
|
-
renderWithProviders(<CardAsset id="img-1" selected={false} />, {
|
|
134
|
-
preloaded: {
|
|
135
|
-
assets: assetsState({'img-1': assetItem(imageAsset)}),
|
|
136
|
-
},
|
|
137
|
-
})
|
|
138
|
-
expect(screen.getAllByTestId('card-image').length).toBeGreaterThan(0)
|
|
139
|
-
expect(screen.getAllByText('photo.png').length).toBeGreaterThan(0)
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
it('renders file icon with extension for a file asset', () => {
|
|
143
|
-
renderWithProviders(<CardAsset id="file-1" selected={false} />, {
|
|
144
|
-
preloaded: {
|
|
145
|
-
assets: assetsState({'file-1': assetItem(fileAsset)}),
|
|
146
|
-
},
|
|
147
|
-
})
|
|
148
|
-
const icon = screen.getAllByTestId('card-file-icon').at(-1)!
|
|
149
|
-
expect(icon).toHaveAttribute('data-extension', 'pdf')
|
|
150
|
-
expect(screen.getAllByText('doc.pdf').length).toBeGreaterThan(0)
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
it('opens the asset edit dialog when the preview is clicked in browse mode', async () => {
|
|
154
|
-
const user = userEvent.setup()
|
|
155
|
-
const {store} = renderWithProviders(<CardAsset id="img-1" selected={false} />, {
|
|
156
|
-
preloaded: {
|
|
157
|
-
assets: assetsState({'img-1': assetItem(imageAsset)}),
|
|
158
|
-
},
|
|
159
|
-
})
|
|
160
|
-
|
|
161
|
-
await user.click(clickPreview())
|
|
162
|
-
|
|
163
|
-
expect(
|
|
164
|
-
store.getState().dialog.items.some((d) => d.type === 'assetEdit' && d.assetId === 'img-1'),
|
|
165
|
-
).toBe(true)
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
it('calls onSelect with the asset document id when the preview is clicked in picker mode', async () => {
|
|
169
|
-
const user = userEvent.setup()
|
|
170
|
-
const onSelect = vi.fn()
|
|
171
|
-
renderWithProviders(<CardAsset id="img-1" selected={false} />, {
|
|
172
|
-
onSelect,
|
|
173
|
-
preloaded: {
|
|
174
|
-
assets: assetsState({'img-1': assetItem(imageAsset)}),
|
|
175
|
-
},
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
await user.click(clickPreview())
|
|
179
|
-
|
|
180
|
-
expect(onSelect).toHaveBeenCalledWith([
|
|
181
|
-
{
|
|
182
|
-
kind: 'assetDocumentId',
|
|
183
|
-
value: 'img-1',
|
|
184
|
-
},
|
|
185
|
-
])
|
|
186
|
-
})
|
|
187
|
-
|
|
188
|
-
it('toggles pick when the footer is clicked in browse mode', async () => {
|
|
189
|
-
const user = userEvent.setup()
|
|
190
|
-
const {store} = renderWithProviders(<CardAsset id="img-1" selected={false} />, {
|
|
191
|
-
preloaded: {
|
|
192
|
-
assets: assetsState({'img-1': assetItem(imageAsset, {picked: false})}),
|
|
193
|
-
},
|
|
194
|
-
})
|
|
195
|
-
|
|
196
|
-
await user.click(clickFooterFilename('photo.png'))
|
|
197
|
-
|
|
198
|
-
expect(store.getState().assets.byIds['img-1']!.picked).toBe(true)
|
|
199
|
-
})
|
|
200
|
-
|
|
201
|
-
it('opens asset edit from the footer when in picker mode', async () => {
|
|
202
|
-
const user = userEvent.setup()
|
|
203
|
-
const onSelect = vi.fn()
|
|
204
|
-
const {store} = renderWithProviders(<CardAsset id="img-1" selected={false} />, {
|
|
205
|
-
onSelect,
|
|
206
|
-
preloaded: {
|
|
207
|
-
assets: assetsState({'img-1': assetItem(imageAsset)}),
|
|
208
|
-
},
|
|
209
|
-
})
|
|
210
|
-
|
|
211
|
-
await user.click(clickFooterFilename('photo.png'))
|
|
212
|
-
|
|
213
|
-
expect(onSelect).not.toHaveBeenCalled()
|
|
214
|
-
expect(
|
|
215
|
-
store.getState().dialog.items.some((d) => d.type === 'assetEdit' && d.assetId === 'img-1'),
|
|
216
|
-
).toBe(true)
|
|
217
|
-
})
|
|
218
|
-
|
|
219
|
-
it('shift-clicks on preview to unpick when the asset is already picked', async () => {
|
|
220
|
-
const user = userEvent.setup()
|
|
221
|
-
const {store} = renderWithProviders(<CardAsset id="img-1" selected={false} />, {
|
|
222
|
-
preloaded: {
|
|
223
|
-
assets: assetsState({'img-1': assetItem(imageAsset, {picked: true})}),
|
|
224
|
-
},
|
|
225
|
-
})
|
|
226
|
-
|
|
227
|
-
setShiftPressed(true)
|
|
228
|
-
await user.click(clickPreview())
|
|
229
|
-
setShiftPressed(false)
|
|
230
|
-
|
|
231
|
-
expect(store.getState().assets.byIds['img-1']!.picked).toBe(false)
|
|
232
|
-
})
|
|
233
|
-
|
|
234
|
-
it('shift-clicks on preview to pick a range when not picked and lastPicked is set', async () => {
|
|
235
|
-
const user = userEvent.setup()
|
|
236
|
-
const prevAsset = {...imageAsset, _id: 'prev-1', originalFilename: 'prev.png'} as ImageAsset
|
|
237
|
-
const {store} = renderWithProviders(<CardAsset id="img-1" selected={false} />, {
|
|
238
|
-
preloaded: {
|
|
239
|
-
assets: assetsState(
|
|
240
|
-
{
|
|
241
|
-
'prev-1': assetItem(prevAsset),
|
|
242
|
-
'img-1': assetItem(imageAsset, {picked: false}),
|
|
243
|
-
},
|
|
244
|
-
{lastPicked: 'prev-1'},
|
|
245
|
-
),
|
|
246
|
-
},
|
|
247
|
-
})
|
|
248
|
-
|
|
249
|
-
setShiftPressed(true)
|
|
250
|
-
await user.click(clickPreview())
|
|
251
|
-
setShiftPressed(false)
|
|
252
|
-
|
|
253
|
-
expect(store.getState().assets.byIds['img-1']!.picked).toBe(true)
|
|
254
|
-
expect(store.getState().assets.byIds['prev-1']!.picked).toBe(true)
|
|
255
|
-
})
|
|
256
|
-
|
|
257
|
-
it('shift-clicks on footer to pick a range when not picked', async () => {
|
|
258
|
-
const user = userEvent.setup()
|
|
259
|
-
const anchorAsset = {
|
|
260
|
-
...imageAsset,
|
|
261
|
-
_id: 'anchor-9',
|
|
262
|
-
originalFilename: 'anchor.png',
|
|
263
|
-
} as ImageAsset
|
|
264
|
-
const {store} = renderWithProviders(<CardAsset id="img-1" selected={false} />, {
|
|
265
|
-
preloaded: {
|
|
266
|
-
assets: assetsState(
|
|
267
|
-
{
|
|
268
|
-
'anchor-9': assetItem(anchorAsset),
|
|
269
|
-
'img-1': assetItem(imageAsset, {picked: false}),
|
|
270
|
-
},
|
|
271
|
-
{lastPicked: 'anchor-9'},
|
|
272
|
-
),
|
|
273
|
-
},
|
|
274
|
-
})
|
|
275
|
-
|
|
276
|
-
setShiftPressed(true)
|
|
277
|
-
await user.click(clickFooterFilename('photo.png'))
|
|
278
|
-
setShiftPressed(false)
|
|
279
|
-
|
|
280
|
-
expect(store.getState().assets.byIds['img-1']!.picked).toBe(true)
|
|
281
|
-
expect(store.getState().assets.byIds['anchor-9']!.picked).toBe(true)
|
|
282
|
-
})
|
|
283
|
-
|
|
284
|
-
it('shows the selection checkmark when selected and not updating', () => {
|
|
285
|
-
const {container} = renderWithProviders(<CardAsset id="img-1" selected />, {
|
|
286
|
-
preloaded: {
|
|
287
|
-
assets: assetsState({'img-1': assetItem(imageAsset, {updating: false})}),
|
|
288
|
-
},
|
|
289
|
-
})
|
|
290
|
-
expect(
|
|
291
|
-
container.querySelectorAll('[data-sanity-icon="checkmark-circle"]').length,
|
|
292
|
-
).toBeGreaterThan(0)
|
|
293
|
-
})
|
|
294
|
-
|
|
295
|
-
it('does not show the checkmark overlay while updating even if selected', () => {
|
|
296
|
-
const {container} = renderWithProviders(<CardAsset id="img-1" selected />, {
|
|
297
|
-
preloaded: {
|
|
298
|
-
assets: assetsState({'img-1': assetItem(imageAsset, {updating: true})}),
|
|
299
|
-
},
|
|
300
|
-
})
|
|
301
|
-
expect(container.querySelectorAll('[data-sanity-icon="checkmark-circle"]')).toHaveLength(0)
|
|
302
|
-
})
|
|
303
|
-
|
|
304
|
-
it('shows a spinner while updating', () => {
|
|
305
|
-
renderWithProviders(<CardAsset id="img-1" selected={false} />, {
|
|
306
|
-
preloaded: {
|
|
307
|
-
assets: assetsState({'img-1': assetItem(imageAsset, {updating: true})}),
|
|
308
|
-
},
|
|
309
|
-
})
|
|
310
|
-
expect(document.body.querySelectorAll('[data-ui="Spinner"]').length).toBeGreaterThan(0)
|
|
311
|
-
})
|
|
312
|
-
|
|
313
|
-
it('shows a warning icon when the asset item has an error', () => {
|
|
314
|
-
const {container} = renderWithProviders(<CardAsset id="img-1" selected={false} />, {
|
|
315
|
-
preloaded: {
|
|
316
|
-
assets: assetsState({'img-1': assetItem(imageAsset, {error: 'Upload failed'})}),
|
|
317
|
-
},
|
|
318
|
-
})
|
|
319
|
-
expect(
|
|
320
|
-
container.querySelectorAll('[data-sanity-icon="warning-filled"]').length,
|
|
321
|
-
).toBeGreaterThan(0)
|
|
322
|
-
})
|
|
323
|
-
})
|
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
import {CheckmarkCircleIcon, EditIcon, WarningFilledIcon} from '@sanity/icons'
|
|
2
|
-
import {
|
|
3
|
-
Box,
|
|
4
|
-
Checkbox,
|
|
5
|
-
Container,
|
|
6
|
-
Flex,
|
|
7
|
-
Spinner,
|
|
8
|
-
Text,
|
|
9
|
-
type Theme,
|
|
10
|
-
type ThemeColorSchemeKey,
|
|
11
|
-
Tooltip,
|
|
12
|
-
} from '@sanity/ui'
|
|
13
|
-
import {memo, type MouseEvent, type RefObject} from 'react'
|
|
14
|
-
import {useDispatch} from 'react-redux'
|
|
15
|
-
import {useColorSchemeValue} from 'sanity'
|
|
16
|
-
import {styled, css} from 'styled-components'
|
|
17
|
-
|
|
18
|
-
import {PANEL_HEIGHT} from '../../constants'
|
|
19
|
-
import {useAssetSourceActions} from '../../contexts/AssetSourceDispatchContext'
|
|
20
|
-
import useKeyPress from '../../hooks/useKeyPress'
|
|
21
|
-
import useTypedSelector from '../../hooks/useTypedSelector'
|
|
22
|
-
import {assetsActions, selectAssetById} from '../../modules/assets'
|
|
23
|
-
import {dialogActions} from '../../modules/dialog'
|
|
24
|
-
import {getSchemeColor} from '../../utils/getSchemeColor'
|
|
25
|
-
import imageDprUrl from '../../utils/imageDprUrl'
|
|
26
|
-
import {isFileAsset, isImageAsset} from '../../utils/typeGuards'
|
|
27
|
-
import FileIcon from '../FileIcon'
|
|
28
|
-
import Image from '../Image'
|
|
29
|
-
|
|
30
|
-
type Props = {
|
|
31
|
-
id: string
|
|
32
|
-
selected: boolean
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const CardWrapper = styled(Flex)`
|
|
36
|
-
box-sizing: border-box;
|
|
37
|
-
height: 100%;
|
|
38
|
-
overflow: hidden;
|
|
39
|
-
position: relative;
|
|
40
|
-
width: 100%;
|
|
41
|
-
`
|
|
42
|
-
|
|
43
|
-
const CardContainer = styled(Flex)<{$picked?: boolean; theme: Theme; $updating?: boolean}>(({
|
|
44
|
-
$picked,
|
|
45
|
-
theme,
|
|
46
|
-
$updating,
|
|
47
|
-
}) => {
|
|
48
|
-
return css`
|
|
49
|
-
border: 1px solid transparent;
|
|
50
|
-
height: 100%;
|
|
51
|
-
pointer-events: ${$updating ? 'none' : 'auto'};
|
|
52
|
-
position: relative;
|
|
53
|
-
transition: all 300ms;
|
|
54
|
-
user-select: none;
|
|
55
|
-
width: 100%;
|
|
56
|
-
|
|
57
|
-
border: ${$picked
|
|
58
|
-
? `1px solid ${theme.sanity.color.spot.orange} !important`
|
|
59
|
-
: '1px solid inherit'};
|
|
60
|
-
|
|
61
|
-
${!$updating &&
|
|
62
|
-
css`
|
|
63
|
-
@media (hover: hover) and (pointer: fine) {
|
|
64
|
-
&:hover {
|
|
65
|
-
border: 1px solid var(--card-border-color);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
`}
|
|
69
|
-
`
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
const ContextActionContainer = styled<typeof Flex, {$scheme: ThemeColorSchemeKey}>(Flex)(({
|
|
73
|
-
$scheme,
|
|
74
|
-
}) => {
|
|
75
|
-
return css`
|
|
76
|
-
cursor: pointer;
|
|
77
|
-
height: ${PANEL_HEIGHT}px;
|
|
78
|
-
transition: all 300ms;
|
|
79
|
-
@media (hover: hover) and (pointer: fine) {
|
|
80
|
-
&:hover {
|
|
81
|
-
background: ${getSchemeColor($scheme, 'bg')};
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
`
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
const StyledWarningOutlineIcon = styled(WarningFilledIcon)(({theme}) => {
|
|
88
|
-
return {
|
|
89
|
-
color: theme.sanity.color.spot.red,
|
|
90
|
-
}
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
const CardAsset = (props: Props) => {
|
|
94
|
-
const {id, selected} = props
|
|
95
|
-
|
|
96
|
-
const scheme = useColorSchemeValue()
|
|
97
|
-
|
|
98
|
-
// Refs
|
|
99
|
-
const shiftPressed: RefObject<boolean> = useKeyPress('shift')
|
|
100
|
-
|
|
101
|
-
// Redux
|
|
102
|
-
const dispatch = useDispatch()
|
|
103
|
-
const lastPicked = useTypedSelector((state) => state.assets.lastPicked)
|
|
104
|
-
const item = useTypedSelector((state) => selectAssetById(state, id))
|
|
105
|
-
|
|
106
|
-
const asset = item?.asset
|
|
107
|
-
const error = item?.error
|
|
108
|
-
const isOpaque = item?.asset?.metadata?.isOpaque
|
|
109
|
-
const picked = item?.picked
|
|
110
|
-
const updating = item?.updating
|
|
111
|
-
|
|
112
|
-
const {onSelect} = useAssetSourceActions()
|
|
113
|
-
|
|
114
|
-
// Short circuit if no asset is available
|
|
115
|
-
if (!asset) {
|
|
116
|
-
return null
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Callbacks
|
|
120
|
-
const handleAssetClick = (e: MouseEvent<HTMLDivElement>) => {
|
|
121
|
-
e.stopPropagation()
|
|
122
|
-
|
|
123
|
-
if (onSelect) {
|
|
124
|
-
onSelect([
|
|
125
|
-
{
|
|
126
|
-
kind: 'assetDocumentId',
|
|
127
|
-
value: asset._id,
|
|
128
|
-
},
|
|
129
|
-
])
|
|
130
|
-
} else if (shiftPressed.current) {
|
|
131
|
-
if (picked) {
|
|
132
|
-
dispatch(assetsActions.pick({assetId: asset._id, picked: !picked}))
|
|
133
|
-
} else {
|
|
134
|
-
dispatch(assetsActions.pickRange({startId: lastPicked || asset._id, endId: asset._id}))
|
|
135
|
-
}
|
|
136
|
-
} else {
|
|
137
|
-
dispatch(dialogActions.showAssetEdit({assetId: asset._id}))
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const handleContextActionClick = (e: MouseEvent) => {
|
|
142
|
-
e.stopPropagation()
|
|
143
|
-
|
|
144
|
-
if (onSelect) {
|
|
145
|
-
dispatch(dialogActions.showAssetEdit({assetId: asset._id}))
|
|
146
|
-
} else if (shiftPressed.current && !picked) {
|
|
147
|
-
dispatch(assetsActions.pickRange({startId: lastPicked || asset._id, endId: asset._id}))
|
|
148
|
-
} else {
|
|
149
|
-
dispatch(assetsActions.pick({assetId: asset._id, picked: !picked}))
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const opacityContainer = updating ? 0.5 : 1
|
|
154
|
-
const opacityPreview = selected || updating ? 0.25 : 1
|
|
155
|
-
|
|
156
|
-
return (
|
|
157
|
-
<CardWrapper padding={1}>
|
|
158
|
-
<CardContainer direction="column" $picked={picked} $updating={item.updating}>
|
|
159
|
-
{/* Image */}
|
|
160
|
-
<Box
|
|
161
|
-
flex={1}
|
|
162
|
-
style={{
|
|
163
|
-
cursor: selected ? 'default' : 'pointer',
|
|
164
|
-
position: 'relative',
|
|
165
|
-
}}
|
|
166
|
-
>
|
|
167
|
-
<div onClick={handleAssetClick} style={{height: '100%', opacity: opacityPreview}}>
|
|
168
|
-
{/* File icon */}
|
|
169
|
-
{isFileAsset(asset) && <FileIcon extension={asset.extension} width="80px" />}
|
|
170
|
-
|
|
171
|
-
{/* Image */}
|
|
172
|
-
{isImageAsset(asset) && (
|
|
173
|
-
<Image
|
|
174
|
-
draggable={false}
|
|
175
|
-
$scheme={scheme}
|
|
176
|
-
$showCheckerboard={!isOpaque}
|
|
177
|
-
src={imageDprUrl(asset, {height: 250, width: 250})}
|
|
178
|
-
style={{
|
|
179
|
-
draggable: false,
|
|
180
|
-
transition: 'opacity 1000ms',
|
|
181
|
-
}}
|
|
182
|
-
/>
|
|
183
|
-
)}
|
|
184
|
-
</div>
|
|
185
|
-
|
|
186
|
-
{/* Selected check icon */}
|
|
187
|
-
{selected && !updating && (
|
|
188
|
-
<Flex
|
|
189
|
-
align="center"
|
|
190
|
-
justify="center"
|
|
191
|
-
style={{
|
|
192
|
-
height: '100%',
|
|
193
|
-
left: 0,
|
|
194
|
-
opacity: opacityContainer,
|
|
195
|
-
position: 'absolute',
|
|
196
|
-
top: 0,
|
|
197
|
-
width: '100%',
|
|
198
|
-
}}
|
|
199
|
-
>
|
|
200
|
-
<Text size={2}>
|
|
201
|
-
<CheckmarkCircleIcon />
|
|
202
|
-
</Text>
|
|
203
|
-
</Flex>
|
|
204
|
-
)}
|
|
205
|
-
|
|
206
|
-
{/* Spinner */}
|
|
207
|
-
{updating && (
|
|
208
|
-
<Flex
|
|
209
|
-
align="center"
|
|
210
|
-
justify="center"
|
|
211
|
-
style={{
|
|
212
|
-
height: '100%',
|
|
213
|
-
left: 0,
|
|
214
|
-
position: 'absolute',
|
|
215
|
-
top: 0,
|
|
216
|
-
width: '100%',
|
|
217
|
-
}}
|
|
218
|
-
>
|
|
219
|
-
<Spinner />
|
|
220
|
-
</Flex>
|
|
221
|
-
)}
|
|
222
|
-
</Box>
|
|
223
|
-
|
|
224
|
-
{/* Footer */}
|
|
225
|
-
<ContextActionContainer
|
|
226
|
-
align="center"
|
|
227
|
-
onClick={handleContextActionClick}
|
|
228
|
-
paddingX={1}
|
|
229
|
-
$scheme={scheme}
|
|
230
|
-
style={{opacity: opacityContainer}}
|
|
231
|
-
>
|
|
232
|
-
{onSelect ? (
|
|
233
|
-
<EditIcon
|
|
234
|
-
style={{
|
|
235
|
-
flexShrink: 0,
|
|
236
|
-
opacity: 0.5,
|
|
237
|
-
}}
|
|
238
|
-
/>
|
|
239
|
-
) : (
|
|
240
|
-
<Checkbox
|
|
241
|
-
checked={picked}
|
|
242
|
-
readOnly
|
|
243
|
-
style={{
|
|
244
|
-
flexShrink: 0,
|
|
245
|
-
pointerEvents: 'none',
|
|
246
|
-
transform: 'scale(0.8)',
|
|
247
|
-
}}
|
|
248
|
-
/>
|
|
249
|
-
)}
|
|
250
|
-
|
|
251
|
-
<Box marginLeft={2}>
|
|
252
|
-
<Text muted size={0} textOverflow="ellipsis">
|
|
253
|
-
{asset.originalFilename}
|
|
254
|
-
</Text>
|
|
255
|
-
</Box>
|
|
256
|
-
</ContextActionContainer>
|
|
257
|
-
|
|
258
|
-
{/* TODO: DRY */}
|
|
259
|
-
{/* Error button */}
|
|
260
|
-
{error && (
|
|
261
|
-
<Box
|
|
262
|
-
padding={3}
|
|
263
|
-
style={{
|
|
264
|
-
position: 'absolute',
|
|
265
|
-
right: 0,
|
|
266
|
-
top: 0,
|
|
267
|
-
}}
|
|
268
|
-
>
|
|
269
|
-
<Tooltip
|
|
270
|
-
animate
|
|
271
|
-
content={
|
|
272
|
-
<Container padding={2} width={0}>
|
|
273
|
-
<Text size={1}>{error}</Text>
|
|
274
|
-
</Container>
|
|
275
|
-
}
|
|
276
|
-
placement="left"
|
|
277
|
-
portal
|
|
278
|
-
>
|
|
279
|
-
<Text size={1}>
|
|
280
|
-
<StyledWarningOutlineIcon color="critical" />
|
|
281
|
-
</Text>
|
|
282
|
-
</Tooltip>
|
|
283
|
-
</Box>
|
|
284
|
-
)}
|
|
285
|
-
</CardContainer>
|
|
286
|
-
</CardWrapper>
|
|
287
|
-
)
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
export default memo(CardAsset)
|