Page MenuHomePhabricator

chips for fixed values (!58)
Closed, ResolvedPublic

Description

Scope: replace the muted "uneditable" rendering of read-only EXIF cells with a fixed (non-removable, non-editable) chip pill, and surface the file's full embedded EXIF in a click-to-open info popover so the user is informed about what the file actually carries.

What's broken

Today the EXIF columns camera, lens, focal, iso, aperture, shutter (src/table.jsx:97-102, tone: "exif" + editable: false + immutable: true) render as muted disabled cells (.tbl__td--immutable, see src/app.css:2360-2382 and :3088-3109). They look greyed-out, the cell click does nothing useful (src/table.jsx:1573 short-circuits, then bubbles to the row's open-detail handler), and the user has no in-app way to discover what other EXIF data the file is actually carrying — even though that other EXIF travels with the file the moment it's published, with no opt-out.

Wanted

Each of those six cells should render the value (when present) as a fixed chip pill:

  • shape & spacing match the chip primitive introduced by T425887 / !54 (so the table's chip family stays visually coherent),
  • distinct subtle / neutral tone (NOT progressive blue) — the user can't act on it, the colour shouldn't promise an action,
  • inline lock indicator (or equivalent), no remove (×) control,
  • cursor: help,
  • click opens an info popover anchored to the chip.

The popover (uses the existing PillInfoPopover infrastructure at src/table.jsx:4438, kind "exif"):

  • name and value of the field this chip represents,
  • explainer copy: "Read from the file's embedded EXIF. The workbench can't suppress it on publish — Commons indexes EXIF and bots may transcribe parts of it to Structured Data later.",
  • a scrollable list of every other EXIF tag/value the API returned for this file (so the user can see the full picture beyond the columns visible in the table).

When the EXIF field has no value, the cell shows the existing placeholder — no chip, no popover.

Where in the code

FileChange
src/api/normalize.js:158-249Add a rawExif array on the normalized item (every {name, value} from info.metadata + info.commonmetadata, post-flattenMetaValue, dropping null/empty/_type). Derived-runtime only — never persisted to user-store.
src/table.jsx:1877-1882 (CellView for camera/lens/focal/iso/aperture/shutter)Render value as <span className="chip-pill chip-pill--fixed">…</span> with inline lock icon. Click → setPillInfo({ kind: "exif", value: { col, item }, anchorEl: e.currentTarget }).
src/table.jsx:4438 (PillInfoPopover)New branch for info.kind === "exif" → renders <ExifChipPopover> analogous to the existing DepictsPopover / CategoryPopover.
src/app.css (near :6683 — the .chip-pill block from T425887)Add .chip-pill--fixed modifier (subtle/neutral colour family, cursor: help). Define a minimal .chip-pill base inline if T425887 hasn't merged yet — same selector / same shape, so the cross-MR merge collapses to a no-op.

Cells in scope

camera, lens, focal, iso, aperture, shutter (the six tone:"exif" + editable:false + immutable:true cells).

Cells NOT in scope

  • dateTaken, cameraLocationtone:"exif" but editable:true (EXIF default, user-overridable); keep the inline editor.
  • size, dimensions, status — immutable but not EXIF (computed from upload state); keep their existing rendering.
  • wikitext — immutable launcher cell that opens a modal; unchanged.

Acceptance criteria

  1. The six EXIF cells render as a chip pill with the value, when the value exists; placeholder when not.
  2. The chip cannot be removed and cannot be edited (no ×, no inline editor opens on click).
  3. Clicking the chip opens an info popover; clicking outside / pressing Esc closes it.
  4. The popover names the field, shows the value, includes the EXIF-stays-in-the-file explainer, and lists every other raw EXIF entry the API returned for the file.
  5. The chip uses the same shape primitive as T425887's chips but a distinct neutral tone.
  6. npm run build (incl. the scripts/check-undefined-refs.mjs scanner) passes.

Notes

  • What actually auto-publishes from this app (src/api/publish.js:270-308 buildSdcClaims): depicts (P180), coordinates (P625), location-of-creation (P1071), date-taken (P571), self-author (P170). Camera/lens/ISO/aperture/shutter/focal are NOT written as SDC by this tool, and not referenced in the wikitext template (buildWikitext); they stay only inside the file's binary EXIF block. The popover copy reflects this accurately ("baked into the file", not "auto-published as SDC by this tool").
  • Co-existence with T425887 (in flight as !54): the .chip-pill base class is shared. If T425887 hasn't merged when this MR lands, this MR defines a minimal compatible base. If it has, this MR only adds the .chip-pill--fixed modifier. No version bump (per merger's-job rule).

Event Timeline

Daanvr moved this task from To do to Doing on the Tool-upload-workbench board.

Grooming pass (AI)

Description rewritten with investigation findings (see task description above). Original preserved here verbatim:

Some XF values have fixed data and we now display them as gray cells where the user cannot edit them. However, the user should be able to remove the data. Ashley let me correct that. The user should not be able to remove the chip. But I wanna uh change the great out sales design by a chip design where you have a chip with the value in it. And you shouldn't be able to remove the chip, but clicking on the chip should inform you that it is this information is in the active data. removing this chip will not remove it from the exiv data and that remove it from the exiv data the file should be uploaded again without the exive data the user wants to remove. And now that I think of it, they can still remove it and it will say that it will only remove it in the Search your data on commas, but not from the XF data. Well no that doesn't make sense because a pot will come by and do it anyway so they should not be able to remove the chip with the exive data When clicking on the chip, it should also show all the other exif data that is available and that will automatically be published.

So this chip design should completely replace the uneditable fields in the table. Only one value is accepted in that cell, in those cells of exif data. So it's also not possible to add a different value. Turn ascent. It is fixed because you cannot remove the chip and you cannot add a different value or chip. However, the user is better informed and visually it's more coherent

Findings

What "fixed-value EXIF cells" means today (src/table.jsx:97-102): the columns that match the original wording — tone: "exif" AND editable: false AND immutable: true — are exactly camera, lens, focal, iso, aperture, shutter (six cells). They render as the muted "disabled" cell (.tbl__td--immutable background, .tbl__td--exif left-border-on-first, Icon name="lock" hover icon at src/table.jsx:1667, CSS at src/app.css:2360-2382 and :3088-3109). Click does nothing — Cell.onClick short-circuits for non-editable cells (src/table.jsx:1573), so the click bubbles to the row's open-detail handler, which is the wrong affordance for a "this is reference data" cell.

Cells NOT in scope (intentionally — they look greyed but ARE editable, so the chip metaphor doesn't fit):

  • dateTaken (src/table.jsx:103) — EXIF-derived default but user-overridable, editable: true.
  • cameraLocation (src/table.jsx:104) — same, user can drag the marker.
  • size, dimensions, status, wikitext — immutable but not EXIF; computed from the file/upload state. These keep their existing immutable rendering.

Where the EXIF data comes from (src/api/normalize.js:158-173): a single extractExif() flattens info.metadata + info.commonmetadata arrays plus extmetadata and pulls only the seven curated names (DateTimeOriginal, Artist, Make, Model, LensModel, ISOSpeedRatings, FNumber, ExposureTime, FocalLength, GPS). The raw metadata array is discarded after extraction, so the popover's "all other EXIF data" section needs the normalize layer to start preserving the full {name, value} list on the item (a derived-runtime field — not persisted to the user-store wiki page; see CLAUDE.md "Don't persist derived data").

What actually auto-publishes (src/api/publish.js:270-308 buildSdcClaims, :157-187 buildWikitext): the workbench's wikitext + SDC writer emits only: depicts (P180), object/camera coordinates (P625), location-of-creation (P1071), date-taken (P571), and self-author (P170). Camera/lens/ISO/aperture/shutter/focal are NOT written as SDC by this tool, and are NOT referenced in the wikitext template either. They live exclusively in the file's binary EXIF block. So the user-facing copy in the popout must be accurate: "this comes from the file's embedded EXIF; Commons indexes EXIF and bots may transcribe it to SDC later — you can't suppress it from this app because it's baked into the file's bytes". (The original voice transcription drifted between "auto-publishes to SDC", "removable from SDC only", and "don't bother because a bot will re-add it"; the resolved framing in the spec — "fixed because you cannot remove the chip" — matches reality regardless.)

Visual primitive to share with T425887 (src/ui/chip-editor.jsx, src/app.css:6683-6738): !54 introduces .chip-pill / .chip-pill__btn — a progressive-blue inline pill (var(--color-progressive) border + var(--background-color-progressive-subtle) fill, border-radius: 10px, padding: 0 6px, click opens .chip-popover). The new fixed-EXIF chip should reuse the same .chip-pill base (so the design stays coherent if/when !54 lands first) but use a neutral / subtle tone instead of progressive blue (the user can't act on it, so the active-CTA progressive colour would over-promise). Plan: introduce a .chip-pill--fixed modifier in app.css that swaps the colour family to --color-subtle / --background-color-disabled-subtle, and add a small lock icon inline. No × button. cursor: help. !54 may land before or after this MR; the modifier-class approach means we don't fight the merge in either order — if .chip-pill doesn't yet exist on main when this MR lands, we define a minimal compatible base ourselves in the same block.

Existing infra to reuse: PillInfoPopover (src/table.jsx:4438, opened via setPillInfo({ kind, value, anchorEl })) already implements the exact "click the cell, get an info popover anchored to it, click outside / Esc to close, fetched data inside" pattern used for category and depicts pills. The CSS class is .pill-info. We add a new kind, exif, that opens a popover with: this-cell value, an "Embedded in the file" explanation, and a key/value list of every other raw EXIF entry the API returned for this file.

Resolved scope

Net visible change: the six immutable EXIF cells (camera, lens, focal, iso, aperture, shutter) stop rendering as muted cells with no obvious affordance, and start rendering their value as a clickable fixed chip that shares the visual language of the upcoming T425887 chip system. The chip cannot be removed and cannot be edited — there's only ever one value per cell. Clicking the chip opens an info popout that:

  • names the field (e.g. "Camera (EXIF Make / Model)"),
  • shows the value clearly,
  • explains the value is read out of the file's embedded EXIF block, that the workbench can't suppress it on publish, and that Commons may surface it as SDC later,
  • lists every other EXIF tag/value the API exposed for this file (so the user understands what else lives in their file beyond the columns visible in the table).

When the EXIF field has no value (the file has no Make/Model, etc.), the cell shows the existing placeholder — no chip, no popover (nothing to inform about).

Plan

  1. src/api/normalize.js — preserve the raw EXIF entries on the item:
    • Add a rawExif field on normalizeStashItem (and the parallel published-item normalizer if any) that holds an array of { name, value } for every entry from info.metadata + info.commonmetadata (post-flattenMetaValue, skipping null/empty/_type entries). Derived-runtime only — never persisted to user-store.
  2. src/table.jsx
    • CellView: for the six target cell keys, render the value (if any) as a <span className="chip-pill chip-pill--fixed">…</span> with an inline lock icon, instead of the current scalarOrDash plain text. Click: setPillInfo({ kind: "exif", value: { col, item }, anchorEl: e.currentTarget }).
    • Cell: drop the click-bubbles-to-row-detail behaviour for these cells — the chip click stops propagation, blank space in the cell still opens row detail (consistent with other immutable cells).
    • PillInfoPopover: new branch if (info.kind === "exif") return <ExifChipPopover value={info.value} popRef={ref} pos={pos} />; (analogous to DepictsPopover). The new component renders the field name, the value, the explainer, and a scrollable list of every item.rawExif entry except the one for THIS cell.
  3. src/app.css — add a .chip-pill--fixed modifier (override colours to neutral/subtle, swap progressive-blue for --color-subtle, add cursor: help). If .chip-pill doesn't exist yet on main (T425887 not merged), define a minimal base ourselves in the same block — same selector / same shape — so the merge with !54 collapses to a no-op.
  4. No version bump (per the merger's-job rule). CHANGELOG.md gets one line under [Unreleased].

Acceptance criteria

  • Camera, Lens, Focal length, ISO, Aperture, Shutter cells render as a chip-shaped pill with the value when the value exists; placeholder when not.
  • The chip has no remove control.
  • Clicking the chip opens the info popover; the popover does NOT also open the row detail.
  • The popover lists the field's value, explains it's baked into the file, and shows every other raw EXIF entry the API returned for the file.
  • Visual language matches the .chip-pill primitive from T425887/!54 (same border-radius, same padding/line-height shape) but uses the subtle/neutral tone — distinct from T425887's actively-editable progressive-blue chips.
  • npm run build (incl. undefined-ref scanner) passes.

Out of scope

  • The dateTaken and cameraLocation cells (editable, EXIF-defaults but overridable) keep their existing inline editor.
  • Custom / template-driven columns are unchanged.
  • No persistence: item.rawExif is derived-only; never written to wiki.
  • No change to what gets written to SDC at publish — the popover only documents what the file already carries.