feat(SPA): media library indications for custom medial field type usage
This commit is contained in:
1 parent
3e7fc9a4a5
commit
0fc3511e2b
28 files changed
+2060
-131
No files matched your search
@@ -463,6 +463,17 @@ const extraSources: Record<string, Source[]> = {
|
||||
states: ['Duplicates tab with the button', 'Duplicates tab: offline'],
|
||||
alias: {},
|
||||
},
|
||||
{
|
||||
board: schemaBoard,
|
||||
group: '11 Media library: usage',
|
||||
states: [
|
||||
'Grid: usage badge tooltip',
|
||||
'List view: usage badge',
|
||||
'Detail panel: used in entries',
|
||||
'Delete: image used by entries',
|
||||
],
|
||||
alias: {},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
+101
-1
@@ -1697,6 +1697,58 @@ const mediaFloorboardsUsage = [
|
||||
{ id: 12, title: 'Before and after: the Rue Verte kitchen', published: true },
|
||||
{ id: 13, title: 'Why we reuse original floorboards', published: true },
|
||||
]
|
||||
const mediaDesktopWidth = 768
|
||||
const mediaBathroomTilesId = 10
|
||||
const mediaLimePlasterId = 11
|
||||
const mediaEntryUsedIn: Record<number, number> = {
|
||||
[mediaBathroomTilesId]: 3,
|
||||
[mediaLimePlasterId]: 7,
|
||||
}
|
||||
const mediaEventTitles = ['Lime plaster workshop', 'Heritage stone walk', 'Winter open day']
|
||||
const mediaLimeArticles = [
|
||||
{ type: 'article', id: 10, title: 'How we price a bathroom renovation', published: true },
|
||||
{ type: 'article', id: 11, title: 'Winter checklist for timber windows', published: false },
|
||||
].map((article) => ({ ...article, fields: [] }))
|
||||
|
||||
function mediaEventEntries(count: number) {
|
||||
return Array.from({ length: count }, (_, index) => ({
|
||||
type: 'entry',
|
||||
id: index + 1,
|
||||
fields: ['thumbnail'],
|
||||
content_type_key: 'events',
|
||||
content_type_name: 'Events',
|
||||
first_field: {
|
||||
key: 'title',
|
||||
type: 'text',
|
||||
value: mediaEventTitles[index] ?? `Event ${index + 1}`,
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
function mediaUsageApi(overrides: Record<string, ApiMock> = {}) {
|
||||
return mediaApi({
|
||||
'GET /v1/media/': {
|
||||
status: 200,
|
||||
bodyFor: () =>
|
||||
mediaList(
|
||||
mediaItems().map((item) => ({
|
||||
...item,
|
||||
used_in: mediaEntryUsedIn[item.id] ?? item.used_in,
|
||||
})),
|
||||
),
|
||||
},
|
||||
[`GET /v1/media/${mediaBathroomTilesId}/usage`]: {
|
||||
status: 200,
|
||||
body: mediaEventEntries(3),
|
||||
},
|
||||
[`GET /v1/media/${mediaLimePlasterId}/usage`]: {
|
||||
status: 200,
|
||||
body: [...mediaLimeArticles, ...mediaEventEntries(5)],
|
||||
},
|
||||
...overrides,
|
||||
})
|
||||
}
|
||||
|
||||
const mediaDuplicateIds = [12, 5, 6]
|
||||
const mediaDuplicateGroups = [mediaDuplicateIds, [7, 9]]
|
||||
|
||||
@@ -2218,6 +2270,25 @@ const mediaStates: Record<string, VisualState> = {
|
||||
await page.mouse.move(0, 0)
|
||||
},
|
||||
),
|
||||
'Grid: usage badge tooltip': mediaState(mediaUsageApi(), async (page) => {
|
||||
const badge = mediaCard(page, mediaBathroomTilesId).getByText('Used in 3', {
|
||||
exact: true,
|
||||
})
|
||||
const desktop = (page.viewportSize()?.width ?? 0) >= mediaDesktopWidth
|
||||
if (desktop) {
|
||||
await badge.hover()
|
||||
await page.getByText('Used in entries: 3', { exact: true }).first().waitFor()
|
||||
}
|
||||
}),
|
||||
'List view: usage badge': mediaState(mediaUsageApi(), undefined, '/media?view=list'),
|
||||
'Detail panel: used in entries': mediaState(mediaUsageApi(), async (page) => {
|
||||
await openMediaPanel(page, mediaLimePlasterId)
|
||||
await page.getByText('and 2 more', { exact: true }).waitFor()
|
||||
}),
|
||||
'Delete: image used by entries': mediaState(mediaUsageApi(), async (page) => {
|
||||
await openMediaDialog(page, mediaLimePlasterId, 'Delete')
|
||||
await page.getByRole('alertdialog').getByText('and 2 more', { exact: true }).waitFor()
|
||||
}),
|
||||
'Delete: unused image': mediaState(mediaApi(), (page) => openMediaDialog(page, 8, 'Delete')),
|
||||
'Delete: image in use': mediaState(mediaApi(), (page) => openMediaDialog(page, 1, 'Delete')),
|
||||
'Delete: in progress': mediaState(
|
||||
@@ -4706,6 +4777,35 @@ function withVariantsRowException(
|
||||
|
||||
const MEDIA_DESKTOP_BUDGET = 10
|
||||
|
||||
const usageCeilings: Record<string, { desktop: number; mobile: number }> = {
|
||||
'Grid: usage badge tooltip': { desktop: MEDIA_DESKTOP_BUDGET, mobile: 32 },
|
||||
'List view: usage badge': { desktop: 15, mobile: 24 },
|
||||
'Detail panel: used in entries': { desktop: 17, mobile: 28 },
|
||||
'Delete: image used by entries': { desktop: MEDIA_DESKTOP_BUDGET, mobile: 29 },
|
||||
}
|
||||
|
||||
function withUsageException(states: Record<string, VisualState>): Record<string, VisualState> {
|
||||
return Object.fromEntries(
|
||||
Object.entries(states).map(([name, state]) => {
|
||||
const maxPercent = usageCeilings[name]
|
||||
if (maxPercent === undefined) {
|
||||
return [name, state]
|
||||
}
|
||||
return [
|
||||
name,
|
||||
{
|
||||
...state,
|
||||
exception: {
|
||||
maxPercent,
|
||||
reason: 'mobile: variants filter row not drawn on the Media board; desktop: stray miniature in the board capture and the Details | Variants tab row not drawn on the board',
|
||||
approvedBy: 'user (Florian Sylvain), 2026-10-21',
|
||||
},
|
||||
},
|
||||
]
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
function withDetailTabsException(states: Record<string, VisualState>): Record<string, VisualState> {
|
||||
const name = 'Detail panel: duplicate'
|
||||
const state = states[name]
|
||||
@@ -9468,7 +9568,7 @@ export const visualStates: Record<string, Record<string, VisualState>> = {
|
||||
...withVariantExceptions(variantsEditorStates),
|
||||
},
|
||||
media: withDetailTabsException(
|
||||
withVariantsRowException({ ...mediaStates, ...mediaPurgeStates }),
|
||||
withUsageException(withVariantsRowException({ ...mediaStates, ...mediaPurgeStates })),
|
||||
),
|
||||
'media-picker': pickerStates,
|
||||
}
|
||||
Reference in new issue
Block a user