Skip to content
110 changes: 64 additions & 46 deletions app/components/Package/Versions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -571,14 +571,14 @@ function majorGroupContainsCurrent(group: (typeof otherMajorGroups.value)[0]): b
<!-- Dist-tag rows (limited to MAX_VISIBLE_TAGS) -->
<div v-for="row in visibleTagRows" :key="row.id">
<div
class="flex items-center gap-2 pe-2 px-1"
:class="rowContainsCurrentVersion(row) ? 'bg-bg-subtle rounded-lg' : ''"
class="flex items-center gap-2 pe-2 px-1 relative group/version-row hover:bg-bg-subtle focus-within:bg-bg-subtle transition-colors duration-200 rounded-lg"
:class="rowContainsCurrentVersion(row) ? 'bg-bg-subtle' : ''"
>
<!-- Expand button (only if there are more versions to show) -->
<button
v-if="getTagVersions(row.tag).length > 1 || !hasLoadedAll"
type="button"
class="size-5 -me-1 flex items-center justify-center text-fg-subtle hover:text-fg transition-colors rounded-sm"
class="size-5 -me-1 flex items-center justify-center text-fg-subtle hover:text-fg transition-colors rounded-sm relative z-10"
:aria-expanded="expandedTags.has(row.tag)"
:aria-label="
expandedTags.has(row.tag)
Expand Down Expand Up @@ -611,10 +611,10 @@ function majorGroupContainsCurrent(group: (typeof otherMajorGroups.value)[0]): b
<LinkBase
:to="versionRoute(row.primaryVersion.version)"
block
class="text-sm"
class="text-sm after:absolute after:inset-0 after:content-['']"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Stretched-link coverage is blocked in several row zones, so the full card is not clickable.

On Line 614 the stretched-link overlay is added, but in this block it is still constrained by the overflow-hidden wrapper (Line 610), and many non-interactive siblings are elevated with relative z-10 (for example Lines 633-636 and 648-649). This makes tags/date/provenance areas miss navigation clicks, which regresses the “whole card clickable” goal.

Suggested patch pattern
-            <div class="overflow-hidden">
+            <div class="min-w-0">
...
-              <div
-                v-if="row.tags.length"
-                class="flex items-center gap-1 mt-0.5 flex-wrap relative z-10"
-              >
+              <div
+                v-if="row.tags.length"
+                class="flex items-center gap-1 mt-0.5 flex-wrap"
+              >
...
-            <div class="flex items-center gap-2 shrink-0 relative z-10">
+            <div class="flex items-center gap-2 shrink-0">

Apply the same z-10 removal to equivalent non-interactive wrappers in expanded/hidden/group rows.

Also applies to: 633-636, 648-649, 702-703, 722-723, 814-815, 827-828, 904-905, 924-925, 971-972, 991-992, 1038-1039, 1056-1056

:class="
row.primaryVersion.deprecated
? 'text-red-800 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300'
? 'text-red-800 group-hover/version-row:text-red-700 dark:text-red-400 dark:group-hover/version-row:text-red-300'
: undefined
"
:title="
Expand All @@ -630,18 +630,22 @@ function majorGroupContainsCurrent(group: (typeof otherMajorGroups.value)[0]): b
{{ row.primaryVersion.version }}
</span>
</LinkBase>
<div v-if="row.tags.length" class="flex items-center gap-1 mt-0.5 flex-wrap">
<div
v-if="row.tags.length"
class="flex items-center gap-1 mt-0.5 flex-wrap relative z-10"
>
<span
v-for="tag in row.tags"
:key="tag"
class="text-4xs font-semibold text-fg-subtle uppercase tracking-wide truncate"
class="text-4xs font-semibold uppercase tracking-wide truncate"
:class="tag === 'latest' ? 'text-accent' : 'text-fg-subtle'"
:title="tag"
>
{{ tag }}
</span>
</div>
</div>
<div class="flex items-center gap-2 shrink-0">
<div class="flex items-center gap-2 shrink-0 relative z-10">
<DateTime
v-if="row.primaryVersion.time"
:datetime="row.primaryVersion.time"
Expand All @@ -655,6 +659,7 @@ function majorGroupContainsCurrent(group: (typeof otherMajorGroups.value)[0]): b
:package-name="packageName"
:version="row.primaryVersion.version"
compact
:linked="false"
/>
</div>
</div>
Expand All @@ -668,17 +673,17 @@ function majorGroupContainsCurrent(group: (typeof otherMajorGroups.value)[0]): b
<div
v-for="v in getFilteredTagVersions(row.tag).slice(1)"
:key="v.version"
class="py-1"
:class="v.version === effectiveCurrentVersion ? 'rounded bg-bg-subtle px-2 -mx-2' : ''"
class="py-1 relative group/version-row hover:bg-bg-elevated/20 focus-within:bg-bg-elevated/20 transition-colors duration-200"
:class="v.version === effectiveCurrentVersion ? 'bg-bg-elevated/20 rounded' : ''"
>
<div class="flex items-center justify-between gap-2">
<LinkBase
:to="versionRoute(v.version)"
block
class="text-xs"
class="text-xs after:absolute after:inset-0 after:content-['']"
:class="
v.deprecated
? 'text-red-800 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300'
? 'text-red-800 group-hover/version-row:text-red-700 dark:text-red-400 dark:group-hover/version-row:text-red-300'
: undefined
"
:title="
Expand All @@ -694,7 +699,7 @@ function majorGroupContainsCurrent(group: (typeof otherMajorGroups.value)[0]): b
{{ v.version }}
</span>
</LinkBase>
<div class="flex items-center gap-2 shrink-0">
<div class="flex items-center gap-2 shrink-0 relative z-10">
<DateTime
v-if="v.time"
:datetime="v.time"
Expand All @@ -708,17 +713,19 @@ function majorGroupContainsCurrent(group: (typeof otherMajorGroups.value)[0]): b
:package-name="packageName"
:version="v.version"
compact
:linked="false"
/>
</div>
</div>
<div
v-if="v.tags?.length && filterExcludedTags(v.tags, row.tags).length"
class="flex items-center gap-1 mt-0.5"
class="flex items-center gap-1 mt-0.5 relative z-10"
>
<span
v-for="tag in filterExcludedTags(v.tags, row.tags)"
:key="tag"
class="text-5xs font-semibold text-fg-subtle uppercase tracking-wide truncate max-w-[120px]"
class="text-5xs font-semibold uppercase tracking-wide truncate max-w-[120px]"
:class="tag === 'latest' ? 'text-accent' : 'text-fg-subtle'"
:title="tag"
>
{{ tag }}
Expand Down Expand Up @@ -778,17 +785,17 @@ function majorGroupContainsCurrent(group: (typeof otherMajorGroups.value)[0]): b
<div
v-for="row in hiddenTagRows"
:key="row.id"
class="py-1"
:class="hiddenRowContainsCurrent(row) ? 'rounded bg-bg-subtle px-2 -mx-2' : ''"
class="py-1 relative group/version-row hover:bg-bg-subtle focus-within:bg-bg-subtle transition-colors duration-200 rounded-lg"
:class="hiddenRowContainsCurrent(row) ? 'bg-bg-subtle' : ''"
>
<div class="flex items-center justify-between gap-2">
<LinkBase
:to="versionRoute(row.primaryVersion.version)"
block
class="text-xs"
class="text-xs after:absolute after:inset-0 after:content-['']"
:class="
row.primaryVersion.deprecated
? 'text-red-800 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300'
? 'text-red-800 group-hover/version-row:text-red-700 dark:text-red-400 dark:group-hover/version-row:text-red-300'
: undefined
"
:title="
Expand All @@ -804,7 +811,7 @@ function majorGroupContainsCurrent(group: (typeof otherMajorGroups.value)[0]): b
{{ row.primaryVersion.version }}
</span>
</LinkBase>
<div class="flex items-center gap-2 shrink-0 pe-2">
<div class="flex items-center gap-2 shrink-0 pe-2 relative z-10">
<DateTime
v-if="row.primaryVersion.time"
:datetime="row.primaryVersion.time"
Expand All @@ -815,11 +822,15 @@ function majorGroupContainsCurrent(group: (typeof otherMajorGroups.value)[0]): b
/>
</div>
</div>
<div v-if="row.tags.length" class="flex items-center gap-1 mt-0.5 flex-wrap">
<div
v-if="row.tags.length"
class="flex items-center gap-1 mt-0.5 flex-wrap relative z-10"
>
<span
v-for="tag in row.tags"
:key="tag"
class="text-5xs font-semibold text-fg-subtle uppercase tracking-wide truncate max-w-[120px]"
class="text-5xs font-semibold uppercase tracking-wide truncate max-w-[120px]"
:class="tag === 'latest' ? 'text-accent' : 'text-fg-subtle'"
:title="tag"
>
{{ tag }}
Expand All @@ -833,14 +844,14 @@ function majorGroupContainsCurrent(group: (typeof otherMajorGroups.value)[0]): b
<!-- Version group header -->
<div
v-if="group.versions.length > 1"
class="py-1"
:class="majorGroupContainsCurrent(group) ? 'rounded bg-bg-subtle px-2 -mx-2' : ''"
class="py-1 relative group/version-row hover:bg-bg-subtle focus-within:bg-bg-subtle transition-colors duration-200 rounded-lg"
:class="majorGroupContainsCurrent(group) ? 'bg-bg-subtle' : ''"
>
<div class="flex items-center justify-between gap-2">
<div class="flex items-center gap-2 min-w-0">
<button
type="button"
class="w-4 h-4 flex items-center justify-center text-fg-subtle hover:text-fg transition-colors shrink-0 rounded-sm"
class="w-4 h-4 flex items-center justify-center text-fg-subtle hover:text-fg transition-colors shrink-0 rounded-sm relative z-10"
:aria-expanded="expandedMajorGroups.has(group.groupKey)"
:aria-label="
expandedMajorGroups.has(group.groupKey)
Expand Down Expand Up @@ -868,10 +879,10 @@ function majorGroupContainsCurrent(group: (typeof otherMajorGroups.value)[0]): b
v-if="group.versions[0]?.version"
:to="versionRoute(group.versions[0]?.version)"
block
class="text-xs"
class="text-xs after:absolute after:inset-0 after:content-['']"
:class="
group.versions[0]?.deprecated
? 'text-red-800 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300'
? 'text-red-800 group-hover/version-row:text-red-700 dark:text-red-400 dark:group-hover/version-row:text-red-300'
: undefined
"
:title="
Expand All @@ -890,7 +901,7 @@ function majorGroupContainsCurrent(group: (typeof otherMajorGroups.value)[0]): b
</span>
</LinkBase>
</div>
<div class="flex items-center gap-2 shrink-0 pe-2">
<div class="flex items-center gap-2 shrink-0 pe-2 relative z-10">
<DateTime
v-if="group.versions[0]?.time"
:datetime="group.versions[0]?.time"
Expand All @@ -904,17 +915,19 @@ function majorGroupContainsCurrent(group: (typeof otherMajorGroups.value)[0]): b
:package-name="packageName"
:version="group.versions[0]?.version"
compact
:linked="false"
/>
</div>
</div>
<div
v-if="group.versions[0]?.tags?.length"
class="flex items-center gap-1 ms-5 flex-wrap"
class="flex items-center gap-1 ms-5 flex-wrap relative z-10"
>
<span
v-for="tag in group.versions[0].tags"
:key="tag"
class="text-5xs font-semibold text-fg-subtle uppercase tracking-wide truncate max-w-[120px]"
class="text-5xs font-semibold uppercase tracking-wide truncate max-w-[120px]"
:class="tag === 'latest' ? 'text-accent' : 'text-fg-subtle'"
:title="tag"
>
{{ tag }}
Expand All @@ -924,19 +937,19 @@ function majorGroupContainsCurrent(group: (typeof otherMajorGroups.value)[0]): b
<!-- Single version (no expand needed) -->
<div
v-else
class="py-1"
:class="majorGroupContainsCurrent(group) ? 'rounded bg-bg-subtle px-2 -mx-2' : ''"
class="py-1 relative group/version-row hover:bg-bg-subtle focus-within:bg-bg-subtle transition-colors duration-200 rounded-lg"
:class="majorGroupContainsCurrent(group) ? 'bg-bg-subtle' : ''"
>
<div class="flex items-center justify-between gap-2">
<div class="flex items-center gap-2 min-w-0">
<LinkBase
v-if="group.versions[0]?.version"
:to="versionRoute(group.versions[0]?.version)"
block
class="text-xs ms-6"
class="text-xs ms-6 after:absolute after:inset-0 after:content-['']"
:class="
group.versions[0]?.deprecated
? 'text-red-800 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300'
? 'text-red-800 group-hover/version-row:text-red-700 dark:text-red-400 dark:group-hover/version-row:text-red-300'
: undefined
"
:title="
Expand All @@ -955,7 +968,7 @@ function majorGroupContainsCurrent(group: (typeof otherMajorGroups.value)[0]): b
</span>
</LinkBase>
</div>
<div class="flex items-center gap-2 shrink-0 pe-2">
<div class="flex items-center gap-2 shrink-0 pe-2 relative z-10">
<DateTime
v-if="group.versions[0]?.time"
:datetime="group.versions[0]?.time"
Expand All @@ -969,14 +982,19 @@ function majorGroupContainsCurrent(group: (typeof otherMajorGroups.value)[0]): b
:package-name="packageName"
:version="group.versions[0]?.version"
compact
:linked="false"
/>
</div>
</div>
<div v-if="group.versions[0]?.tags?.length" class="flex items-center gap-1 ms-5">
<div
v-if="group.versions[0]?.tags?.length"
class="flex items-center gap-1 ms-5 relative z-10"
>
<span
v-for="tag in group.versions[0].tags"
:key="tag"
class="text-5xs font-semibold text-fg-subtle uppercase tracking-wide"
class="text-5xs font-semibold uppercase tracking-wide"
:class="tag === 'latest' ? 'text-accent' : 'text-fg-subtle'"
>
{{ tag }}
</span>
Expand All @@ -991,19 +1009,17 @@ function majorGroupContainsCurrent(group: (typeof otherMajorGroups.value)[0]): b
<div
v-for="v in group.versions.slice(1)"
:key="v.version"
class="py-1"
:class="
v.version === effectiveCurrentVersion ? 'rounded bg-bg-subtle px-2 -mx-2' : ''
"
class="py-1 relative group/version-row hover:bg-bg-elevated/20 focus-within:bg-bg-elevated/20 transition-colors duration-200"
:class="v.version === effectiveCurrentVersion ? 'bg-bg-elevated/20 rounded' : ''"
>
<div class="flex items-center justify-between gap-2">
<LinkBase
:to="versionRoute(v.version)"
block
class="text-xs"
class="text-xs after:absolute after:inset-0 after:content-['']"
:class="
v.deprecated
? 'text-red-800 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300'
? 'text-red-800 group-hover/version-row:text-red-700 dark:text-red-400 dark:group-hover/version-row:text-red-300'
: undefined
"
:title="
Expand All @@ -1019,7 +1035,7 @@ function majorGroupContainsCurrent(group: (typeof otherMajorGroups.value)[0]): b
{{ v.version }}
</span>
</LinkBase>
<div class="flex items-center gap-2 shrink-0 pe-2">
<div class="flex items-center gap-2 shrink-0 pe-2 relative z-10">
<DateTime
v-if="v.time"
:datetime="v.time"
Expand All @@ -1033,14 +1049,16 @@ function majorGroupContainsCurrent(group: (typeof otherMajorGroups.value)[0]): b
:package-name="packageName"
:version="v.version"
compact
:linked="false"
/>
</div>
</div>
<div v-if="v.tags?.length" class="flex items-center gap-1 mt-0.5">
<div v-if="v.tags?.length" class="flex items-center gap-1 mt-0.5 relative z-10">
<span
v-for="tag in v.tags"
:key="tag"
class="text-5xs font-semibold text-fg-subtle uppercase tracking-wide"
class="text-5xs font-semibold uppercase tracking-wide"
:class="tag === 'latest' ? 'text-accent' : 'text-fg-subtle'"
>
{{ tag }}
</span>
Expand Down
45 changes: 45 additions & 0 deletions test/nuxt/components/PackageVersions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,51 @@ describe('PackageVersions', () => {
expect(versionLinks.length).toBeGreaterThan(0)
expect(versionLinks[0]?.text()).toBe('1.0.0')
})

it('highlights the current version row when selectedVersion prop matches', async () => {
const component = await mountSuspended(PackageVersions, {
props: {
packageName: 'test-package',
versions: {
'2.0.0': createVersion('2.0.0'),
'1.0.0': createVersion('1.0.0'),
},
distTags: { latest: '2.0.0', stable: '1.0.0' },
time: {
'2.0.0': '2024-01-15T12:00:00.000Z',
'1.0.0': '2024-01-01T12:00:00.000Z',
},
selectedVersion: '1.0.0',
},
})

// Find the version row divs that are direct children of the tag row containers
const versionRows = component.findAll('[class*="group/version-row"]')
const highlightedRows = versionRows.filter(row => row.classes().includes('bg-bg-subtle'))
expect(highlightedRows.length).toBe(1)
expect(highlightedRows[0]!.text()).toContain('1.0.0')
})

it('uses accent color for latest tag', async () => {
const component = await mountSuspended(PackageVersions, {
props: {
packageName: 'test-package',
versions: {
'2.0.0': createVersion('2.0.0'),
'1.0.0': createVersion('1.0.0'),
},
distTags: { latest: '2.0.0', stable: '1.0.0' },
time: {
'2.0.0': '2024-01-15T12:00:00.000Z',
'1.0.0': '2024-01-01T12:00:00.000Z',
},
selectedVersion: '1.0.0',
},
})

const latestTag = component.findAll('span').find(span => span.text() === 'latest')
expect(latestTag?.classes()).toContain('text-accent')
})
})

describe('dist-tag display', () => {
Expand Down
Loading