feat(media): align visual checks and variant UI with merged design

This commit is contained in:
Florian Sylvain committed 2026-10-13 02:35:20 +02:00
1 parent e6746f2be7
commit 160c0a49ce
23 files changed
+606 -99

No files matched your search

+36 -31
View File
@@ -3722,8 +3722,8 @@ const cacheBoardStats = {
by_variant: [
{ name: 'hero', enabled: true, files: 244, bytes: 88080384 },
{ name: 'card', enabled: true, files: 318, bytes: 60817408 },
{ name: 'print', enabled: false, files: 74, bytes: 22020096 },
{ name: 'thumb', enabled: true, files: 412, bytes: 22020096 },
{ name: 'print', enabled: false, files: 74, bytes: 22020096 },
{ name: 'square', enabled: true, files: 140, bytes: 19922944 },
{ name: 'og', enabled: true, files: 96, bytes: 9437184 },
],
@@ -3777,23 +3777,23 @@ async function confirmPurgeAll(page: Page) {
}
const cacheStates: Record<string, VisualState> = {
'5a · Loaded': cacheState(),
'5b · Loading': cacheState({ status: 200, hang: true }),
'5c · Empty cache': cacheState({ status: 200, body: cacheEmptyStats }),
'5d · Purging': cacheState(
'V5a · Loaded': cacheState(),
'V5b · Loading': cacheState({ status: 200, hang: true }),
'V5c · Empty cache': cacheState({ status: 200, body: cacheEmptyStats }),
'V5d · Purging': cacheState(
{ status: 200, body: cacheBoardStats },
{ [cachePurgeKey]: { status: 200, hang: true } },
confirmPurgeAll,
),
'5e · Purge done': cacheState(
'V5e · Purge done': cacheState(
{
status: 200,
bodyFor: (_url, call) => (call === 0 ? cacheBoardStats : cacheEmptyStats),
bodyFor: (_url, call) => (call === 1 ? cacheBoardStats : cacheEmptyStats),
},
{ [cachePurgeKey]: { status: 200, body: cachePurgeAllResult } },
confirmPurgeAll,
),
'5f · Purge failed': cacheState(
'V5f · Purge failed': cacheState(
{ status: 200, body: cacheBoardStats },
{
[cachePurgeKey]: {
@@ -3808,20 +3808,20 @@ const cacheStates: Record<string, VisualState> = {
},
confirmPurgeAll,
),
'5g · Error loading': cacheState({ status: 500 }),
'5h · Purge all: confirm': cacheState(undefined, {}, async (page) => {
'V5g · Error loading': cacheState({ status: 500 }),
'V5h · Purge all: confirm': cacheState(undefined, {}, async (page) => {
await page.getByRole('button', { name: 'Purge all variants', exact: true }).first().click()
await page.getByRole('alertdialog').waitFor()
await page.getByRole('alertdialog').getByRole('textbox').fill('pur')
await page.mouse.move(0, 0)
}),
'5i · Purge all: confirm, typed': cacheState(undefined, {}, async (page) => {
'V5i · Purge all: confirm, typed': cacheState(undefined, {}, async (page) => {
await page.getByRole('button', { name: 'Purge all variants', exact: true }).first().click()
await page.getByRole('alertdialog').waitFor()
await page.getByRole('alertdialog').getByRole('textbox').fill('purge')
await page.mouse.move(0, 0)
}),
'5j · Purge unused: confirm': cacheState(undefined, {}, async (page) => {
'V5j · Purge unused: confirm': cacheState(undefined, {}, async (page) => {
await page.getByRole('button', { name: 'Purge unused', exact: true }).first().click()
await page.getByRole('alertdialog').waitFor()
await page.mouse.move(0, 0)
@@ -4094,6 +4094,11 @@ const variantsMediaStates: Record<string, VisualState> = {
'Entry: sidebar': {
path: '/media',
origin: mediaOrigin,
exception: {
maxPercent: { desktop: 27, mobile: 10 },
reason: 'board Media page behind the sidebar is a simplified mock (no tabs, Select all, view toggle, Duplicate badges)',
approvedBy: 'user',
},
api: mediaApi({ 'GET /v1/auth/me': variantsSession }),
steps: async (page) => {
if (isMobileViewport(page)) {
@@ -4108,8 +4113,8 @@ const variantsMediaStates: Record<string, VisualState> = {
await page.waitForTimeout(250)
},
},
'3a · Grid with badges': variantsMediaState(),
'3b · Sort menu': variantsMediaState(
'V3a · Grid with badges': variantsMediaState(),
'V3b · Sort menu': variantsMediaState(
{},
async (page) => {
await openSortMenuOf(page)
@@ -4117,28 +4122,28 @@ const variantsMediaStates: Record<string, VisualState> = {
},
'/media?sort=total_size',
),
'3c · Filter: With variants, sorted by total size': variantsMediaState(
'V3c · Filter: With variants, sorted by total size': variantsMediaState(
{},
undefined,
'/media?variants=with&sort=total_size',
[1, 7, 9, 12, 5, 11, 2, 3, 10],
),
'3d · Filter: Without variants': variantsMediaState(
'V3d · Filter: Without variants': variantsMediaState(
{},
undefined,
'/media?variants=without',
[4, 6, 8],
),
'3e · Filter: nothing to show': variantsMediaState({}, undefined, '/media?variants=with', []),
'3f · Delete confirm': variantsMediaState({}, async (page) => {
'V3e · Filter: nothing to show': variantsMediaState({}, undefined, '/media?variants=with', []),
'V3f · Delete confirm': variantsMediaState({}, async (page) => {
await openImagePanel(page)
await page.getByRole('dialog').getByRole('button', { name: 'Delete', exact: true }).click()
await page.getByRole('alertdialog').waitFor()
await page.mouse.move(0, 0)
}),
'4a · Partly generated': variantsTabState(variantsPartlyGenerated),
'4b · Nothing generated yet': variantsTabState([]),
'4c · Generating': variantsTabState(
'V4a · Partly generated': variantsTabState(variantsPartlyGenerated),
'V4b · Nothing generated yet': variantsTabState([]),
'V4c · Generating': variantsTabState(
['thumb'],
{
'POST /v1/media/1/variants/card': { status: 200, hang: true },
@@ -4150,7 +4155,7 @@ const variantsMediaStates: Record<string, VisualState> = {
await page.mouse.move(0, 0)
},
),
'4d · Generation failed': variantsTabState(
'V4d · Generation failed': variantsTabState(
['thumb'],
{ 'POST /v1/media/1/variants': variantsFailedGeneration },
async (page) => {
@@ -4159,7 +4164,7 @@ const variantsMediaStates: Record<string, VisualState> = {
await page.mouse.move(0, 0)
},
),
'4e · No variants defined': {
'V4e · No variants defined': {
path: variantsMediaPath,
api: variantsMediaApi(undefined, {
'GET /v1/media/1/variants': { status: 200, body: { unsupported: null, items: [] } },
@@ -4168,7 +4173,7 @@ const variantsMediaStates: Record<string, VisualState> = {
await openVariantsTabOf(page)
},
},
'4f · Source is an SVG': {
'V4f · Source is an SVG': {
path: variantsMediaPath,
api: variantsMediaApi(undefined, {
'GET /v1/media/4/variants': { status: 200, body: { unsupported: 'svg', items: [] } },
@@ -4178,7 +4183,7 @@ const variantsMediaStates: Record<string, VisualState> = {
await openVariantsTabOf(page, variantsSvgId)
},
},
'4h · Preview dialog': variantsTabState(variantsPartlyGenerated, {}, async (page) => {
'V4h · Preview dialog': variantsTabState(variantsPartlyGenerated, {}, async (page) => {
await variantRow(page, 'thumb').getByRole('button', { name: 'Preview' }).click()
await page.getByRole('dialog').filter({ hasText: 'Preview: thumb' }).waitFor()
await page.mouse.move(0, 0)
@@ -4186,26 +4191,26 @@ const variantsMediaStates: Record<string, VisualState> = {
}
const variantsEditorStates: Record<string, VisualState> = {
'6a · Variant select': variantsPickerState(variantsBoardList, async (page) => {
'V6a · Variant select': variantsPickerState(variantsBoardList, async (page) => {
await openImageUploader(page)
await selectPickerTile(page, 'north-wall.jpg')
await chooseBoardVariant(page, 'Hero')
}),
'6b · Select open': variantsPickerState(variantsBoardList, async (page) => {
'V6b · Select open': variantsPickerState(variantsBoardList, async (page) => {
await openImageUploader(page)
await selectPickerTile(page, 'north-wall.jpg')
await page.getByTestId('variant-select').click()
await page.getByRole('listbox').waitFor()
await page.mouse.move(0, 0)
}),
'6c · No variants defined': variantsPickerState([], async (page) => {
'V6c · No variants defined': variantsPickerState([], async (page) => {
await openImageUploader(page)
await selectPickerTile(page, 'north-wall.jpg')
await page.getByTestId('variant-help').waitFor()
await page.mouse.move(0, 0)
}),
'6d · Variant deleted': variantsBlockState(variantsWithoutHero),
'6e · Variant disabled': variantsBlockState(variantsHeroDisabled),
'V6d · Variant deleted': variantsBlockState(variantsWithoutHero),
'V6e · Variant disabled': variantsBlockState(variantsHeroDisabled),
}
const boardPredatesPages = 'board predates current Media page and editor'
@@ -4232,7 +4237,7 @@ const variantExceptionCaps: Record<string, { desktop: number; mobile: number }>
function withVariantExceptions(states: Record<string, VisualState>) {
return Object.fromEntries(
Object.entries(states).map(([name, state]) => {
const cap = variantExceptionCaps[name.slice(0, 2)]
const cap = variantExceptionCaps[name.slice(1, 3)]
return [
name,
cap