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
@@ -0,0 +1,296 @@
|
||||
Feature: Media field usage in the Media library
|
||||
As an owner, admin or verified user managing the media library
|
||||
I want to see which entries use an image and be warned before deleting it
|
||||
So that I know what an image is used for and what a delete will break
|
||||
|
||||
Background:
|
||||
Given the SPA calls the API with relative URLs under /v1 and the "jwt" HttpOnly cookie
|
||||
And API errors are plain text
|
||||
And this slice changes only web/ (and web/visual); the backend of features/media_field_type.feature is done and not touched
|
||||
And the design is docs/design/media-field-changes.md section 3.8 and section 4, with the board group "11 Media library: usage" (4 states) of "RenewCMS Screens Schema.dc.html", which draws MediaFrame.dc.html with entries counted as uses
|
||||
And GET /v1/media/ items carry "used_in", which counts articles, authors, sites and entries together, and no split
|
||||
And GET /v1/media/{id}/usage answers an array ordered articles, authors, sites, then entries by content type name then entry id, with no total and no "first few" wrapper
|
||||
And an article item is {"type":"article","id","title","published","fields"}
|
||||
And an entry item is {"type":"entry","id","fields","content_type_key","content_type_name","first_field":{"key","type","value"}} with no title and no published, and first_field absent when the type has no first field
|
||||
And the SPA keeps the first 3 entry items, counts the rest, and derives the entry title from first_field with the title rules of the entry list (see "Entry title in the usage lists")
|
||||
And deleting a used image stays allowed; entries keep the id and show "This image is no longer in the library"
|
||||
And the usage badge, panel and dialog never branch on the role; the Media page is the same for every user who reaches it
|
||||
And sentence case, "image" and never "asset", buttons are verbs
|
||||
And every string goes through t() and has a French entry in i18n/messages.ts
|
||||
And a copy tagged "(proposed copy)" is not in the design and is the coder's single point of change
|
||||
And decisions D1 to D12 of this slice are listed in the QA procedure header and in the conversation
|
||||
And out of scope: filtering the library by media-field use, the read-only view of a custom type, any change to the Go API
|
||||
|
||||
Scenario Outline: The label of the usage badge
|
||||
Given an image with used_in <used_in>
|
||||
Then the badge reads "<badge>"
|
||||
|
||||
Examples:
|
||||
| used_in | badge |
|
||||
| 0 | Unused |
|
||||
| 1 | Used in 1 |
|
||||
| 3 | Used in 3 |
|
||||
| 7 | Used in 7 |
|
||||
|
||||
Scenario: The badge counts articles and entries together
|
||||
Given bathroom-tiles.png has used_in 3 (0 articles, 3 entries) and lime-plaster.jpg has used_in 7 (2 articles, 5 entries)
|
||||
Then the grid card, the list row and the mobile card of bathroom-tiles.png read "Used in 3" and those of lime-plaster.jpg read "Used in 7"
|
||||
And the badge is the outline kind of MediaBadge, without an icon, as today
|
||||
|
||||
Scenario: Tooltip for entries only (design "Grid: usage badge tooltip")
|
||||
Given bathroom-tiles.png has 0 articles and 3 entries, and a desktop viewport
|
||||
When the pointer rests on the badge "Used in 3"
|
||||
Then GET /v1/media/10/usage is sent once
|
||||
And a tooltip (--ui-bg-inverted, --ui-text-inverted, 12/18, 6 px 8 px padding, below the badge, left aligned) reads on one line "Used in entries: 3"
|
||||
|
||||
Scenario: Tooltip for articles and entries
|
||||
Given lime-plaster.jpg has 2 articles and 5 entries, and a desktop viewport
|
||||
When the pointer rests on the badge "Used in 7"
|
||||
Then the tooltip reads "Used in articles: 2" on the first line and "Used in entries: 5" on the second
|
||||
|
||||
Scenario: Tooltip for articles only
|
||||
Given north-wall.jpg has 2 articles and 0 entries, and a desktop viewport
|
||||
When the pointer rests on the badge "Used in 2"
|
||||
Then the tooltip reads "Used in articles: 2" and no "Used in entries" line shows
|
||||
|
||||
Scenario: Tooltip opens on keyboard focus
|
||||
Given a desktop viewport
|
||||
When the keyboard focus reaches the card (grid) of an image that is used
|
||||
Then the same tooltip opens, and it closes on blur or Escape without closing anything else
|
||||
And the focus ring of the card is unchanged
|
||||
|
||||
Scenario: The usage is fetched once per image
|
||||
Given the tooltip of image 10 was opened once
|
||||
When the user hovers it again, or opens its detail panel
|
||||
Then no second GET /v1/media/10/usage is sent while the list is not reloaded
|
||||
And a reload of the list (search, filter, sort, page, retry) clears the cached usage
|
||||
|
||||
Scenario: The tooltip waits for the usage
|
||||
Given GET /v1/media/10/usage has not answered
|
||||
Then no tooltip shows and the badge keeps its label
|
||||
When it answers and the pointer or focus is still on the badge
|
||||
Then the tooltip opens
|
||||
|
||||
Scenario Outline: The tooltip never shows without a useful split
|
||||
Given the pointer rests on the badge of an image and GET /v1/media/{id}/usage <outcome>
|
||||
Then no tooltip shows and nothing else changes
|
||||
|
||||
Examples:
|
||||
| outcome |
|
||||
| answers 500 |
|
||||
| fails on the network |
|
||||
| answers 404 |
|
||||
| answers only authors and sites |
|
||||
|
||||
Scenario: No tooltip on mobile
|
||||
Given a 390 px viewport
|
||||
Then no tooltip opens on tap and the badge is read inside the card as "Used in 3"
|
||||
|
||||
Scenario: No tooltip on an unused image
|
||||
Given an image with used_in 0
|
||||
Then the badge "Unused" has no tooltip and no usage request is sent for it
|
||||
|
||||
Scenario Outline: The accessible name of a card (proposed copy)
|
||||
Given an image "north-wall.jpg", 3.9 MB, with used_in <used_in>
|
||||
Then the card button is named "<name>"
|
||||
|
||||
Examples:
|
||||
| used_in | name |
|
||||
| 0 | north-wall.jpg, 3.9 MB, unused |
|
||||
| 1 | north-wall.jpg, 3.9 MB, used in 1 place |
|
||||
| 7 | north-wall.jpg, 3.9 MB, used in 7 places |
|
||||
|
||||
Scenario: The list table column (design "List view: usage badge", written spec 3.8)
|
||||
Given a desktop viewport and the list view
|
||||
Then the "Used in" column of a used image is the underlined button "Used in 3" that opens the detail panel, as the "N articles" button does today
|
||||
And an image with used_in 0 shows the "Unused" badge
|
||||
And the mobile card shows the "Used in 3" badge and the existing meta line
|
||||
|
||||
Scenario: Panel for an image used by articles and entries (design "Detail panel: used in entries")
|
||||
Given lime-plaster.jpg has 2 articles and 5 entries, whose first three entries are Events "Lime plaster workshop", Events "Heritage stone walk" and Events "Winter open day"
|
||||
When the user opens its panel
|
||||
Then the preview is 16:10 on desktop and 16:3.6 on mobile only when usage holds at least one entry, and 16:10 and 4:3 otherwise
|
||||
And the section "Used in 2 articles" lists the two articles with title, Online or Draft and the arrow-up-right icon, as today, with the note "Counted from the text of your articles each time you open this page."
|
||||
And below it the section "Used in entries" (600 14/22, 8 px gap) holds a bordered list of 3 rows, then the row "and 2 more" in the muted colour, with a muted top border and no link
|
||||
And each entry row is 36 px high on desktop and 48 px on mobile and holds, left to right, the content type name in 12 px muted (52 px wide, one line, ellipsis), the entry title as a link in weight 500 underlined (one line, ellipsis) and the arrow-up-right icon (14 px, muted)
|
||||
And the link of a row goes to /content/events/<id> in the same tab
|
||||
|
||||
Scenario Outline: Counts in the "Used in entries" section
|
||||
Given an image whose usage holds <entries> entry items
|
||||
Then the section shows <rows> rows and <more>
|
||||
|
||||
Examples:
|
||||
| entries | rows | more |
|
||||
| 1 | 1 | no "and N more" row |
|
||||
| 3 | 3 | no "and N more" row |
|
||||
| 4 | 3 | the row "and 1 more" |
|
||||
| 5 | 3 | the row "and 2 more" |
|
||||
| 120 | 3 | the row "and 117 more" |
|
||||
|
||||
Scenario: Entries only, no article
|
||||
Given bathroom-tiles.png has 3 entries and 0 articles
|
||||
When the user opens its panel
|
||||
Then the articles heading reads "Not used in any article"
|
||||
And the line under it reads "No article shows it." and never "This image is safe to delete."
|
||||
And the section "Used in entries" follows
|
||||
|
||||
Scenario: Articles only
|
||||
Given north-wall.jpg has 2 articles and 0 entries
|
||||
Then the panel is unchanged from before this slice and shows no "Used in entries" section
|
||||
|
||||
Scenario: Nothing uses the image
|
||||
Given window-frame.jpg has used_in 0
|
||||
Then the panel reads "Not used in any article" and "This image is safe to delete. No article shows it."
|
||||
And no usage request is sent
|
||||
|
||||
Scenario: Authors and sites only (decision D7)
|
||||
Given an image with used_in 2 and a usage that holds one author and one site
|
||||
Then the articles heading reads "Not used in any article"
|
||||
And the line reads "No article shows it." and no "safe to delete" claim is made
|
||||
And no authors or sites are listed
|
||||
|
||||
Scenario: The articles heading counts articles only
|
||||
Given lime-plaster.jpg has used_in 7 (2 articles, 5 entries)
|
||||
Then the heading reads "Used in 2 articles" and never "Used in 7 articles"
|
||||
|
||||
Scenario: Entry link keyboard and focus
|
||||
Given the panel of lime-plaster.jpg is open
|
||||
Then Tab reaches the articles links, then the entry links, in reading order
|
||||
And an entry link has the 2 px --ui-primary focus ring with a 2 px offset
|
||||
And "and 2 more" is not a stop
|
||||
|
||||
Scenario: Usage loading in the panel (decision D6)
|
||||
Given GET /v1/media/11/usage has not answered
|
||||
Then after 150 ms a skeleton of three rows shows under the heading "Used in" (proposed copy)
|
||||
And before 150 ms nothing shows in the section
|
||||
And Copy URL, Rename and Delete stay enabled
|
||||
When it answers
|
||||
Then the real sections replace the skeleton
|
||||
|
||||
Scenario Outline: Usage failure in the panel (decision D5)
|
||||
Given the panel of an image with used_in 7 is opened
|
||||
When GET /v1/media/{id}/usage <outcome>
|
||||
Then <result>
|
||||
|
||||
Examples:
|
||||
| outcome | result |
|
||||
| answers 500 | the section reads "Couldn't load where this image is used." (proposed copy) with the ghost button "Try again", and Delete stays enabled |
|
||||
| fails on the network | the same |
|
||||
| answers 429 | the same |
|
||||
| answers 404 | the existing gone flow: the panel closes and "That image no longer exists" shows |
|
||||
| answers 401 | the global redirect, no local copy |
|
||||
|
||||
Scenario: Try again in the panel
|
||||
Given the usage failure is shown
|
||||
When the user activates "Try again"
|
||||
Then only GET /v1/media/{id}/usage is sent again and the sections replace the message
|
||||
|
||||
Scenario: Switching images while the usage is loading
|
||||
Given the panel of image 10 is waiting for its usage
|
||||
When the user opens image 11
|
||||
Then the answer of image 10 is ignored and the panel shows only image 11 data
|
||||
|
||||
Scenario Outline: Entry title from first_field
|
||||
Given an entry item with id 42 and first_field {"key":"title","type":"<type>","value":<value>}
|
||||
Then the row title reads "<title>"
|
||||
|
||||
Examples:
|
||||
| type | value | title |
|
||||
| text | "Lime plaster workshop" | Lime plaster workshop |
|
||||
| text | "" | Entry 42 |
|
||||
| text | null | Entry 42 |
|
||||
| richtext | "## Heritage walk\n\nMeet at 9" | the first line as richTextFirstLine gives it (not the Markdown marks of the whole body) |
|
||||
| richtext | "" | Entry 42 |
|
||||
| number | 1500 | 1,500 (as the entry list formats a number) |
|
||||
| number | 0 | 0 |
|
||||
| boolean | true | Yes |
|
||||
| boolean | false | No |
|
||||
| date | "2026-11-21" | the entry list date format of 2026-11-21 |
|
||||
| select | "Workshop" | Workshop |
|
||||
| media | 7 | Entry 42 |
|
||||
| media | null | Entry 42 |
|
||||
|
||||
Scenario: first_field is absent or unknown
|
||||
Given an entry item with id 42 and no first_field, or a first_field of an unknown type
|
||||
Then the row title reads "Entry 42" for no first_field and the String of the value for an unknown type, as describeCell does for the default case
|
||||
|
||||
Scenario: Long titles and long type names (decision D4)
|
||||
Given an entry title of 120 characters and the type name "Press mentions and partner coverage"
|
||||
Then both stay on one line with an ellipsis, the row height does not change, the title link keeps the full title as its accessible name and a native title attribute, and "and N more" is unaffected
|
||||
|
||||
Scenario: The order of the three rows
|
||||
Given the API returns entries ordered by content type name then entry id
|
||||
Then the three kept rows keep that order and the SPA does not sort again
|
||||
|
||||
Scenario: Delete an image used by entries and articles (design "Delete: image used by entries")
|
||||
Given lime-plaster.jpg has 2 articles and 5 entries and the user activates Delete in its panel
|
||||
Then the confirm dialog (ConfirmDialog, tone danger) titled "Delete an image that's in use?" opens
|
||||
And its description reads "\"lime-plaster.jpg\" is used in 5 entries and 2 articles. Those entry fields will show \"This image is no longer in the library\", and the articles will show a broken image. This can't be undone."
|
||||
And the body holds, in this order, the bordered "Used in entries" list (heading "Used in entries", 3 rows, then "and 2 more") and the bordered list of the 2 articles with title and Online or Draft
|
||||
And the buttons are "Cancel" and the destructive "Delete image"
|
||||
And the entry rows show the type name and the title and are not links in the dialog (decision D10)
|
||||
|
||||
Scenario Outline: The wording by what uses the image
|
||||
Given an image "lime-plaster.jpg" with <articles> articles and <entries> entries in its loaded usage
|
||||
Then the title is "<title>"
|
||||
And the description reads <description>
|
||||
|
||||
Examples:
|
||||
| articles | entries | title | description |
|
||||
| 2 | 5 | Delete an image that's in use? | "lime-plaster.jpg" is used in 5 entries and 2 articles. Those entry fields will show "This image is no longer in the library", and the articles will show a broken image. This can't be undone. |
|
||||
| 1 | 1 | Delete an image that's in use? | "lime-plaster.jpg" is used in 1 entry and 1 article. Those entry fields will show "This image is no longer in the library", and the articles will show a broken image. This can't be undone. |
|
||||
| 2 | 0 | Delete an image that's in use? | "lime-plaster.jpg" is used in 2 articles. If you delete it, they will show a broken image. This can't be undone. |
|
||||
| 0 | 3 | Delete an image that's in use? | "lime-plaster.jpg" is used in 3 entries. Those entry fields will show "This image is no longer in the library". This can't be undone. (proposed copy) |
|
||||
| 0 | 0 | Delete this image? | "lime-plaster.jpg" will be deleted from your library. No article uses it. This can't be undone. (used_in is 0) |
|
||||
|
||||
Scenario: Delete an unused image
|
||||
Given window-frame.jpg has used_in 0
|
||||
Then the dialog is titled "Delete this image?" with the unchanged copy, no lists and no usage request
|
||||
|
||||
Scenario: Delete an image used only by authors or sites
|
||||
Given used_in 2 and a usage with no article and no entry
|
||||
Then the existing in-use title shows with the generic fallback description of the next scenario
|
||||
|
||||
Scenario: The usage is not ready when the dialog opens (decision D6)
|
||||
Given the user activates Delete before GET /v1/media/{id}/usage answered, or after it failed
|
||||
Then the dialog is titled "Delete an image that's in use?" because used_in is above 0
|
||||
And the description reads "\"lime-plaster.jpg\" is in use. Articles will show a broken image and entry fields will show \"This image is no longer in the library\". This can't be undone." (proposed copy)
|
||||
And no lists show, "Delete image" is enabled, and when the usage answers while the dialog is open the dialog switches to the full copy and lists without moving the focus
|
||||
|
||||
Scenario: Confirm and results
|
||||
Given the delete dialog is open
|
||||
When the user activates "Delete image"
|
||||
Then DELETE /v1/media/<id> is sent and the existing in-progress, failed, done and 404 states run unchanged
|
||||
And on 204 the toast "Image deleted" / "\"lime-plaster.jpg\" is gone from your library." shows and the focus moves to the next item
|
||||
|
||||
Scenario: Cancel and Escape
|
||||
When the user activates "Cancel" or presses Esc
|
||||
Then nothing is deleted and the focus returns to the Delete button of the panel
|
||||
|
||||
Scenario: The variants note is kept
|
||||
Given an image with cached variants and used by entries
|
||||
Then the existing variant removal note still shows under the lists
|
||||
|
||||
Scenario: Mobile dialog
|
||||
Given a 390 px viewport
|
||||
Then the dialog is a bottom sheet, the entry rows are 48 px high and the buttons are 44 px high
|
||||
|
||||
Scenario: Bulk delete counts uses, not articles (proposed copy)
|
||||
Given 3 images are selected, 2 of them used for 7 places in total (articles and entries)
|
||||
Then each row keeps its badge "Used in N" or "Unused"
|
||||
And the description counts "7 places" in the sentence where it said "7 articles" and reads "which will show a broken image or a missing-image notice." instead of "which will show a broken image."
|
||||
And no usage request is sent for the bulk dialog
|
||||
|
||||
Scenario: Merge counts uses, not articles (proposed copy)
|
||||
Given a merge group whose other copies have used_in 5 and 2
|
||||
Then the dialog says "7 places will be updated" and "Kept · used in N places now"
|
||||
And the toast after the merge keeps "N copies merged. M articles updated." from articles_updated
|
||||
|
||||
Scenario: Accessibility of the usage UI
|
||||
Then the tooltip is exposed with aria-describedby on its trigger and uses role "tooltip"
|
||||
And the entry title link is named "<title>, <content type name>" (proposed)
|
||||
And "and 2 more" is plain text read in order
|
||||
And the arrow-up-right icon is aria-hidden
|
||||
And a skeleton or error line in the panel is inside the section so a screen reader reads it after the heading
|
||||
And the delete dialog moves focus to "Cancel" as it does today, and its lists are read after the description
|
||||
And mobile targets are 44 px, with rows of 48 px, and there is no hover on mobile
|
||||
@@ -0,0 +1,101 @@
|
||||
# QA: Media field usage in the Media library (slice C)
|
||||
|
||||
Runner: Vitest + Vue Test Utils (`task test:web`). No Go test and no Go change: the usage API is covered by features/media_field_type.qa.md. Kit: extend the Media view test kit with
|
||||
- the library list (`GET /v1/media/?page=&size=&q=&filter=` with `items`, `total`, `counts`);
|
||||
- `GET /v1/media/{id}/usage` fixtures:
|
||||
- image 10 `bathroom-tiles.png`: 3 entries (Events), `used_in` 3;
|
||||
- image 11 `lime-plaster.jpg`: 2 articles ("Open day at the workshop" online, "Restoring lime plaster" draft) plus 5 Events entries (first three: first_field text "Lime plaster workshop", "Heritage stone walk", "Winter open day"), `used_in` 7;
|
||||
- image 1 `north-wall.jpg`: 2 articles, `used_in` 2;
|
||||
- image 8 `window-frame.jpg`: `used_in` 0;
|
||||
- an author-and-site-only image, `used_in` 2;
|
||||
- entry items of every `first_field.type`, plus items with no `first_field`;
|
||||
- failures: 500, network, 429, 404, 401.
|
||||
|
||||
Pin the time zone; never freeze the system date. No state of this slice depends on the clock.
|
||||
|
||||
Decisions accepted by the user (all as the feature draft assumes):
|
||||
- D1 split source for the tooltip: lazy `/usage` fetch, SPA-only.
|
||||
- D2 wording of the card name and the table column: "used in N places", underlined "Used in N" button.
|
||||
- D3 title derivation: reuse `describeCell` and `plainCellText`.
|
||||
- D4 truncation: one line, ellipsis, native title.
|
||||
- D5 usage failure: inline message with "Try again" in the panel, silent tooltip.
|
||||
- D6 usage loading: skeleton after 150 ms, delete dialog opens at once with a generic description.
|
||||
- D7 authors and sites in the usage: filtered out, heading counts articles only.
|
||||
- D8 bulk delete: "N places".
|
||||
- D9 merge: "N places".
|
||||
- D10 entry rows in the delete dialog are not links.
|
||||
- D11 mobile panel as drawn.
|
||||
- D12 visual gate setup.
|
||||
|
||||
## Automated
|
||||
|
||||
Pure logic (lib/__tests__, it.each over the feature's Examples):
|
||||
1. `splitUsage(items)`: articles are those with `type === 'article'`, entries those with `type === 'entry'`, authors and sites are counted by neither. Entry rows keep API order, the first 3 are kept, `more` is the rest (0, 1, 2, 117). An absent array gives zeros.
|
||||
2. `usageEntryTitle(item)`: the table "Entry title from first_field" for text, richtext (empty and Markdown), number (0 and 1500), boolean, date, select, media and null, empty string, absent first_field and an unknown type (falls to the default case of `describeCell`). It reuses `describeCell` and `plainCellText`; no second title rule exists. `entryTitle` itself is unchanged and its existing specs pass untouched.
|
||||
3. Wording helpers: pluralEntries ("1 entry", "5 entries"), usage description by case (articles+entries, singular forms, articles only unchanged, entries only, usage unavailable fallback), `usedInLabel` for the card name ("used in 1 place", "used in 7 places", "unused").
|
||||
4. Tooltip lines: entries only gives one line, both gives articles first, articles only gives one line, authors or sites only gives none.
|
||||
|
||||
MediaCardBadges, MediaTable, MediaGrid, MediaCardList (components/__tests__):
|
||||
5. Badge label for used_in 0, 1, 3, 7. Outline kind, no icon. Same label in the grid card, the list row (underlined button in the table, badge in the mobile card) and the mobile card.
|
||||
6. Tooltip: hover on the badge (`pointerenter`) fetches `/usage` once and opens with the right lines for the three splits; focus on the grid card opens it; Escape and blur close it without closing the panel; no fetch for `used_in` 0; no tooltip on mobile (viewport helper 390); nothing opens while the request is pending, and it opens on answer if the pointer is still there; failures (500, network, 404, authors-only) leave no tooltip; the usage is cached per image and cleared on reload of the list.
|
||||
7. Card `aria-label`: the three names of the feature, replacing the "used in 2 articles" assertions in the existing specs; update those specs and change nothing else in them.
|
||||
8. Table: the "Used in" cell is the underlined button "Used in N" (`data-testid="media-used-in"` kept) that opens the panel; "Unused" badge at 0.
|
||||
|
||||
MediaDetailPanel (components/__tests__/MediaDetailPanel.spec.ts, views/__tests__/MediaView*.spec.ts):
|
||||
9. Articles and entries: heading "Used in 2 articles" built from the articles of the usage, not from `used_in`; article rows unchanged; "Used in entries" heading and 3 rows with type name, title link to `/content/<key>/<id>` and the arrow icon; "and 2 more" as plain text; the counts table (1, 3, 4, 5, 120 entries); row heights 36 px (desktop) and 48 px (mobile) as classes.
|
||||
10. Entries only: heading "Not used in any article" with "No article shows it."; articles only: no entries section and the panel as before; unused: "safe to delete" line and no `/usage` request; authors and sites only: "No article shows it." and no list.
|
||||
11. Preview aspect: 16:10 desktop and 4:3 mobile without entries, 16:6 desktop and 16:3.6 mobile with entries (classes, after the usage answers).
|
||||
12. Loading: nothing before 150 ms, the three-row skeleton after, the real sections after the answer; fake timers only for the 150 ms delay, never the date. Failure: 500, network and 429 show "Couldn't load where this image is used." with "Try again" that repeats only `/usage`; 404 runs the existing gone flow; 401 is left to the global redirect; Delete stays enabled; switching images ignores the late answer.
|
||||
13. Long title and long type name: one line, ellipsis classes, native title attribute on the link, row height unchanged.
|
||||
14. Focus and tab order: article links, then entry links; the ring classes on a link; "and 2 more" not focusable.
|
||||
|
||||
DeleteMediaDialog (components/__tests__/DeleteMediaDialog.spec.ts):
|
||||
15. The description table (2/5, 1/1, 2/0, 0/3, 0/0), the title for in use vs unused, the body order (entries list with heading and "and 2 more", then the articles list), the buttons, the variants note kept, no link in the entry rows.
|
||||
16. Usage not ready (pending, failed): the generic description, no lists, "Delete image" enabled; when the usage answers while open the copy and lists switch and the focus stays.
|
||||
17. Delete flow (confirm, in progress, failed, done, 404) runs unchanged; Cancel and Esc return focus to the Delete button of the panel.
|
||||
|
||||
BulkDeleteMediaDialog and MergeMediaDialog:
|
||||
18. Bulk description counts "places" and uses "which will show a broken image or a missing-image notice."; rows and badges unchanged; no `/usage` request.
|
||||
19. Merge dialog says "N places will be updated" and "Kept · used in N places now"; the toast stays "N copies merged. M articles updated.".
|
||||
|
||||
Cross-cutting:
|
||||
20. French: every new string has an entry (the i18n completeness spec passes).
|
||||
21. Regression: the existing Media view specs pass unchanged except the assertions named in 7, 18 and 19; if any other spec needs an edit, stop and report.
|
||||
|
||||
## Visual (task visual:check -- media)
|
||||
|
||||
In scope: the 4 states of the board group "11 Media library: usage" of RenewCMS Screens Schema.dc.html (they draw MediaFrame, and the copy list gives them the sample data). Every capture must be within 10 %, desktop and mobile, light and dark. Out of scope: states whose setups exist already and whose data does not change (they must keep passing untouched), and the picker states of slice B.
|
||||
|
||||
Prerequisites in web/visual, no change under docs/design (decision D12):
|
||||
- `check.ts`: add the source `{ board: 'RenewCMS Screens Schema.dc.html', group: '11 Media library: usage', states: [the four below] }` to `extraSources.media`. The group differs from the screen's primary group ("Media"), so no merge change is needed.
|
||||
- `states.ts`: the four setups, with the media API mock extended by `GET /v1/media/10/usage` and `GET /v1/media/11/usage` (shapes above). Existing media states keep their data, so `used_in` stays articles-only there.
|
||||
- Board data:
|
||||
- image 10 bathroom-tiles.png, 3 entries, `used_in` 3;
|
||||
- image 11 lime-plaster.jpg, 2 articles and 5 entries, `used_in` 7;
|
||||
- the grid also holds an unused image, so "Unused" shows in the captures.
|
||||
|
||||
| Board state | Setup in web/visual/states.ts | Board sample data |
|
||||
|---|---|---|
|
||||
| Grid: usage badge tooltip | /media with ent counts; step: hover the badge of image 10 (desktop), wait for the tooltip; mobile shows the grid with no tooltip | "Used in 3", tooltip "Used in entries: 3", "Unused" |
|
||||
| List view: usage badge | /media?view=list; desktop table, mobile cards | "Used in 3", "Used in 7", "Unused" |
|
||||
| Detail panel: used in entries | step: open image 11; wait for the entries list | "Used in 2 articles", "Used in entries", Events + "Lime plaster workshop", "Heritage stone walk", "Winter open day", "and 2 more" |
|
||||
| Delete: image used by entries | step: open image 11, activate Delete; wait for the lists | the title, the description of 5 entries and 2 articles, both lists, "Cancel", "Delete image" |
|
||||
|
||||
Board caveats to report, never hide:
|
||||
- The desktop table of the board draws `usedLink` with `pl(im.used)` (articles only). It shows "N articles" for lime-plaster.jpg and "Unused" for bathroom-tiles.png, while the written spec 3.8 says the list row carries the same badge. The spec follows the written text (D2); if the desktop capture of "List view: usage badge" exceeds 10 % the exception needs the user's explicit approval (`approvedBy`).
|
||||
- The board draws the entry rows in the delete dialog as underlined links with the arrow icon; the spec renders plain rows (D10). If a capture exceeds 10 %, report it before changing the choice.
|
||||
- The tooltip is drawn at a fixed place (left aligned, 26 px below the badge); a Nuxt UI tooltip may centre it. Use the `align` and offset that match the frame.
|
||||
- The board titles ("Lime plaster workshop") come from the setup's `first_field` values, not from a title field.
|
||||
- Clock-sensitive: none of the four states shows a relative date.
|
||||
|
||||
## Manual
|
||||
|
||||
- Real backend, owner and a second verified user: upload an image, use it in an article and in two entries through a Media field, and read "Used in 3" in the grid, list and mobile card. Hover and focus the badge for the tooltip, and check that a card with no entries shows "Used in articles: N" only.
|
||||
- Open the panel: the entries link opens /content/<key>/<id> and shows the right entry. Add a fourth entry and read "and 1 more". Delete the entry and reload: counts fall.
|
||||
- Delete the image from the panel with the dialog open: read the real counts, confirm, then open one of the entries and see "This image is no longer in the library" with the id kept.
|
||||
- Throttle or kill the network while opening the panel and the dialog: the error line, "Try again" and the generic delete description.
|
||||
- An image used only by an author or a site: no "safe to delete" claim anywhere.
|
||||
- Keyboard: tab order across card, badge tooltip (focus), panel links and dialog buttons; Escape closes the tooltip first, then the panel; the focus return after Cancel; screen reader reads the card name, the tooltip and the lists.
|
||||
- Motion and hover: the tooltip opening and closing, the skeleton fade at 150 ms, no layout jump larger than the preview change when the usage answers.
|
||||
- Mobile: the full-screen panel, 48 px rows, the bottom-sheet dialog, 44 px targets, no tooltip.
|
||||
- Contrast of the tooltip pair and of the muted type name in light and dark.
|
||||
Reference in new issue
Block a user