Add the Overview and Referrers tabs to the manifest page
What does this MR do and why?
Adds a tab bar to manifest_detail.vue with two tabs, Overview and Referrers.
Both are deliberately empty here. Step 5 fills Overview with the pull snippets,
step 8 fills Referrers with its table. This MR is the structure and the routing
only.
The active tab lives in a tab query parameter rather than component state, so a
tab is shareable by URL and the back button works. Selecting a tab pushes the
query instead of holding local state.
tab value |
Renders |
|---|---|
| absent | Overview |
| empty string | Overview |
overview |
Overview |
referrers |
Referrers |
| anything unrecognised | Overview |
referrersCount was added to the get_manifest GraphQL query selection so the
Referrers tab can carry its count badge. The local mock seed already carried the
field, so no seed change was needed.
Live region names the active tab
The page announcement now names the active tab, matching the pattern already on
the Maven/npm version page. Two translated strings changed shape:
ArtifactRegistry|Manifest %{digest} of %{name}. became
ArtifactRegistry|%{tab} tab for manifest %{digest} of %{name}., and the
no-image variant alongside it changed the same way.
Zero renders as 0, not hidden
A zero count renders as an exact 0 rather than being hidden. The merged
manifest-read contract makes the four manifest counts live and exact, and on a
remote repository referrers_count is always 0. That zero is a real reading, not
a missing value, so hiding it would assert something the data does not say.
Every numeric count is paired with tabCountSrText. GlTab renders its count
badge with aria-hidden="true" and warns when a count arrives without
screen-reader text. Confirmed in the accessibility tree: the tab's accessible
name reads Referrers 1 referrer, and 0 referrers on a manifest with none.
TABS is a flat list, not a map
The sibling Maven/npm page (version_detail.vue) keys its tabs by format. TABS
here is a flat list instead, because Docker and OCI carry the same two tabs and
there is no format axis to key off of.
What's not here
The design prototype shows three tabs: Overview, Manifest, Referrers. This ships two. The raw Manifest payload tab is withdrawn by the phase plan, because its backing route serves raw bytes rather than a JSON resource, in an area ADR-009 leaves unreconciled. That withdrawal is recorded on the plan MR, not decided here.
References
- Phase plan: gitlab-org/ops/artifact-registry!2461 (merged)
- Design prototype: https://gitlab-org.gitlab.io/ci-cd/package-stage/artifact-registry-design-proposal/
- Step 1, the manifest detail page this stacks on: !253776 (merged)
Screenshots or screen recordings
Docker index manifest, Overview active, with the Referrers count badge. There is no meaningful "before" — the page had no tab bar at all.
| Before | After |
|---|---|
| No tab bar | ![]() |
How to set up and validate locally
The manifest detail page is not reachable by clicking yet — the container row link on the manifests table is step 11, which is held until the GraphQL integration lands. So reach it by URL.
-
Enable the flags in the rails console:
Feature.enable(:artifact_registry_ui) Feature.enable(:org_stage_experimental) -
The seeded container repositories are
docker-imagesandoci-artifacts. Manifest digests are derived rather than listed, so use one of these, which are stable against the local seed:- Docker index, one referrer:
/o/default/-/artifact_registry/acme/repositories/docker-images/73f2a86e-73f2-a86f-73f2-a87073f2a871/manifests/sha256%3A2ba136e32ba136e42ba136e52ba136e62ba136e72ba136e82ba136e92ba136ea - Docker single-image child, no referrers:
/o/default/-/artifact_registry/acme/repositories/docker-images/73f2a86e-73f2-a86f-73f2-a87073f2a871/manifests/sha256%3A791e860f791e8610791e8611791e8612791e8613791e8614791e8615791e8616 - OCI index, one referrer:
/o/default/-/artifact_registry/acme/repositories/oci-artifacts/36c00923-36c0-0924-36c0-092536c00926/manifests/sha256%3A1460bea91460beaa1460beab1460beac1460bead1460beae1460beaf1460beb0
- Docker index, one referrer:
-
Confirm both tabs render and both panels are empty, that clicking Referrers appends
?tab=referrers, that loading with?tab=referrersselects it, and that?tab=bogusfalls back to Overview.
Verification
Driven in the GDK with all three URLs above, on Docker and OCI, index and
single-image child. Zero console errors on every load. Confirmed: the tab bar
renders Overview and Referrers and no third tab; clicking Referrers appends
?tab=referrers; loading with ?tab=referrers selects it; ?tab=bogus falls
back to Overview; the count badge reads 1 on an index with one referrer and an
exact 0 on the child with none.
The manifest detail spec passes 53 tests on Vue 2 and 53 on Vue 3 (both run). The
full artifact registry Jest suite passes 64 of 65 suites; repository_detail_spec.js
fails with 55 failures. That failure is pre-existing: I ran the same spec at the
base commit with all changes stashed and got the identical 55 failures, so it is
unrelated to this change.
ESLint clean at --max-warnings 0, Prettier clean, and locale/gitlab.pot
regenerated with the gettext extractor.
No changelog entry — the feature is dark behind artifact_registry_ui and
unreleased.
Stacked
Rebased onto master on 2026-09-10. Step 1
(!253776 (merged)) was squash-merged, so this
branch was cut off its old tip with git rebase --onto master rather than a plain rebase —
a squash leaves no matching patch ids, so a plain rebase would have tried to replay Step 1's
commits. This MR now targets master directly and carries only its own commit.
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.
