feat(common): improve API documentation publishing UX (#6116)

Co-authored-by: James George <25279263+jamesgeorge007@users.noreply.github.com>
This commit is contained in:
Nivedin 2026-04-22 23:05:40 +05:30 committed by GitHub
parent bc3dbdea42
commit 30df20ea7a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 307 additions and 220 deletions

View file

@ -538,7 +538,6 @@
"versions": "Versions",
"create_new_version": "Create New Version",
"invalid_version": "Version must only contain alphanumeric characters, dots, and hyphens",
"snapshot_description": "This will snapshot the current documentation as this version",
"live": "Live",
"snapshot": "Snapshot",
"version_immutable": "Published versions are read-only snapshots",
@ -556,8 +555,9 @@
"snapshot_empty": "No requests or folders in this snapshot",
"snapshot_item_count": "{count} items",
"auto_sync_live_notice": "This version auto-syncs with the live collection",
"snapshot_promote_warning": "Enabling auto-sync will replace this frozen snapshot with the live collection tree. This cannot be undone.",
"live_freeze_notice": "Auto-sync will be turned off and this version will be frozen at the current collection state.",
"untitled_project": "Untitled Project",
"first_publish_hint": "Your documentation will be published as a live version that automatically stays in sync with your collection",
"environment": "Environment",
"no_environment": "No environment",
"environment_description": "Attach an environment to resolve variables in the published documentation"

View file

@ -1,55 +1,32 @@
<template>
<div class="flex flex-col space-y-6">
<div>
<HoppSmartInput
v-model="titleModel"
:label="t('documentation.publish.doc_title')"
type="text"
input-styles="floating-input"
/>
</div>
<HoppSmartInput
v-model="titleModel"
:label="t('documentation.publish.doc_title')"
type="text"
input-styles="floating-input"
/>
<div
v-if="isFirstPublish"
class="flex items-start space-x-2 px-3 py-2.5 rounded-md bg-green-500/5 border border-green-500/15"
<!-- Version Input -->
<HoppSmartInput
v-model="versionModel"
:label="t('documentation.publish.doc_version')"
:input-styles="[
'floating-input',
!isValidVersion && versionModel.length > 0
? '!border-red-500 !focus:border-red-500'
: '',
]"
/>
<span
v-if="!isValidVersion && versionModel.length > 0"
class="text-xs text-red-500 mt-1 block"
>
<icon-lucide-info
class="w-3.5 h-3.5 text-green-600 flex-shrink-0 mt-0.5"
/>
<span class="text-xs text-green-600 leading-relaxed">
{{ t("documentation.publish.first_publish_hint") }}
</span>
</div>
{{ t("documentation.publish.invalid_version") }}
</span>
<!-- Version Input (hidden for first publish) -->
<div v-if="!isFirstPublish">
<HoppSmartInput
v-model="versionModel"
:label="t('documentation.publish.doc_version')"
:disabled="mode === 'update'"
:input-styles="[
'floating-input',
!isValidVersion && versionModel.length > 0
? '!border-red-500 !focus:border-red-500'
: '',
]"
/>
<span
v-if="!isValidVersion && versionModel.length > 0"
class="text-xs text-red-500 mt-1 block"
>
{{ t("documentation.publish.invalid_version") }}
</span>
<span
v-if="mode === 'create' && isValidVersion"
class="text-xs text-secondaryLight mt-1 block"
>
{{ t("documentation.publish.snapshot_description") }}
</span>
</div>
<!-- Auto-sync Toggle (hidden for first publish and for live versions) -->
<div v-if="!isFirstPublish && !isAutoSyncLocked" class="flex items-start">
<!-- Auto-sync Toggle -->
<div class="flex items-start">
<HoppSmartCheckbox
:on="autoSyncModel"
@change="autoSyncModel = !autoSyncModel"
@ -65,6 +42,19 @@
</HoppSmartCheckbox>
</div>
<!-- Info notice: turning off auto-sync on a live version will freeze it -->
<div
v-if="mode === 'update' && !autoSyncModel"
class="flex items-start space-x-2 px-3 py-2.5 rounded-md bg-blue-500/5 border border-blue-500/20"
>
<icon-lucide-info
class="w-3.5 h-3.5 text-blue-600 flex-shrink-0 mt-0.5"
/>
<span class="text-xs text-blue-600 leading-relaxed">
{{ t("documentation.publish.live_freeze_notice") }}
</span>
</div>
<!-- Environment Selector -->
<div class="space-y-2">
<span class="block text-sm font-medium text-secondaryDark">
@ -130,8 +120,6 @@ const props = defineProps<{
autoSync: boolean
selectedEnvironmentID: string | null
publishedUrl: string | null
isFirstPublish: boolean
isAutoSyncLocked: boolean
isValidVersion: boolean
workspaceType: WorkspaceType
workspaceID: string

View file

@ -3,15 +3,26 @@
v-if="show"
dialog
:title="modalTitle"
:styles="mode === 'view' ? 'sm:max-w-6xl' : 'sm:max-w-2xl'"
:styles="
mode === 'view'
? 'sm:max-w-6xl xl:max-w-7xl 2xl:max-w-[80vw]'
: 'sm:max-w-2xl'
"
@close="hideModal"
>
<template #body>
<CollectionsDocumentationPublishDocSnapshotPreview
v-if="mode === 'view'"
v-model:publish-title="publishTitle"
v-model:publish-version="publishVersion"
v-model:auto-sync="autoSync"
v-model:selected-environment-i-d="selectedEnvironmentID"
:existing-data="existingData"
:published-url="publishedUrl"
:show="show && mode === 'view'"
:is-valid-version="isValidVersion"
:workspace-type="workspaceType"
:workspace-i-d="workspaceID"
@copy-url="copyUrl"
@view-published="viewPublished"
/>
@ -24,8 +35,6 @@
v-model:auto-sync="autoSync"
v-model:selected-environment-i-d="selectedEnvironmentID"
:published-url="publishedUrl"
:is-first-publish="isFirstPublish ?? false"
:is-auto-sync-locked="isAutoSyncLocked ?? false"
:is-valid-version="isValidVersion"
:workspace-type="workspaceType"
:workspace-i-d="workspaceID"
@ -46,7 +55,7 @@
@click="handlePublish"
/>
<HoppButtonPrimary
v-else-if="mode === 'update'"
v-else-if="mode === 'update' || mode === 'view'"
:label="t('documentation.publish.update_button')"
:disabled="!canPublish || loading || !hasChanges"
:loading="loading"
@ -112,9 +121,9 @@ const props = defineProps<{
workspaceID: string
mode?: "create" | "update" | "view"
isFirstPublish?: boolean
isAutoSyncLocked?: boolean
publishedDocId?: string
existingData?: {
id: string
title: string
version: string
autoSync: boolean
@ -168,17 +177,30 @@ const initializeFormData = () => {
}
}
// Watch for modal open/close
watch(
[() => props.existingData, () => props.show],
([, isOpen]) => {
if (isOpen) {
initializeFormData()
}
() => props.show,
(isOpen) => {
if (isOpen) initializeFormData()
},
{ immediate: true }
)
// Reinitialize only on doc switches (snapshot live) same-doc refreshes must not clobber in-flight edits.
watch(
() => props.existingData?.id,
(newId, oldId) => {
if (props.show && newId && newId !== oldId) initializeFormData()
}
)
// Same-doc URL can change when the backend rebuilds it (version rename). Sync display only.
watch(
() => props.existingData?.url,
(newUrl) => {
if (props.show && newUrl) publishedUrl.value = newUrl
}
)
const modalTitle = computed(() => {
if (props.mode === "update") return t("documentation.publish.update_title")
if (props.mode === "view") return t("documentation.publish.view_title")

View file

@ -1,37 +1,67 @@
<template>
<div class="flex flex-col lg:flex-row gap-4 flex-1">
<!-- Left Metadata Panel -->
<div class="lg:w-72 flex-shrink-0 flex flex-col space-y-3">
<!-- Title & version header -->
<div class="space-y-2">
<h3 class="text-sm font-semibold text-secondaryDark truncate">
{{ existingData?.title }}
</h3>
<div class="flex items-center space-x-2">
<div
class="lg:w-96 flex-shrink-0 flex flex-col divide-y divide-divider space-y-4"
>
<div class="space-y-4">
<HoppSmartInput
v-model="titleModel"
:label="t('documentation.publish.doc_title')"
type="text"
input-styles="floating-input"
/>
<div>
<HoppSmartInput
v-model="versionModel"
:label="t('documentation.publish.doc_version')"
:input-styles="[
'floating-input',
!isValidVersion && versionModel.length > 0
? '!border-red-500 !focus:border-red-500'
: '',
]"
/>
<span
class="text-xs text-secondaryLight rounded border border-dividerDark px-2 py-0.5"
v-if="!isValidVersion && versionModel.length > 0"
class="text-xs text-red-500 mt-1 block"
>
{{ existingData?.version }}
{{ t("documentation.publish.invalid_version") }}
</span>
</div>
<!-- Environment badge -->
<div
v-if="existingData?.environmentName"
class="flex items-center space-x-1.5"
>
<icon-lucide-layers
class="w-3 h-3 text-secondaryLight flex-shrink-0"
/>
<span class="text-xs text-secondaryLight">
{{ existingData.environmentName }}
<div class="flex items-start">
<HoppSmartCheckbox
:on="autoSyncModel"
@change="autoSyncModel = !autoSyncModel"
>
<div>
<span class="text-sm text-secondaryDark">
{{ t("documentation.publish.auto_sync") }}
</span>
</div>
</HoppSmartCheckbox>
</div>
<!-- Environment Selector -->
<div class="space-y-2">
<span class="block text-sm font-medium text-secondaryDark">
{{ t("documentation.publish.environment") }}
</span>
<p class="text-xs text-secondaryLight">
{{ t("documentation.publish.environment_description") }}
</p>
<CollectionsDocumentationEnvironmentPicker
v-model="environmentModel"
:workspace-type="workspaceType"
:workspace-i-d="workspaceID"
/>
</div>
</div>
<hr class="border-divider" />
<!-- Published URL -->
<div class="space-y-3">
<div class="space-y-3 py-4">
<div v-if="publishedUrl" class="space-y-1">
<label
class="text-[10px] font-semibold uppercase tracking-wider text-secondaryLight"
@ -66,20 +96,9 @@
</div>
</div>
<!-- Status notice -->
<!-- Status notice: version is already live -->
<div
v-if="existingData && !isLive"
class="flex items-start space-x-2 px-3 py-2.5 rounded-md bg-primaryLight border border-divider"
>
<icon-lucide-lock
class="w-3.5 h-3.5 text-secondaryLight flex-shrink-0 mt-0.5"
/>
<span class="text-xs text-secondaryLight leading-relaxed">
{{ t("documentation.publish.version_immutable") }}
</span>
</div>
<div
v-else-if="existingData && isLive"
v-if="existingData?.autoSync && autoSyncModel"
class="flex items-start space-x-2 px-3 py-2.5 rounded-md bg-green-500/5 border border-green-500/15"
>
<icon-lucide-refresh-cw
@ -89,6 +108,32 @@
{{ t("documentation.publish.auto_sync_live_notice") }}
</span>
</div>
<!-- Info notice: turning off auto-sync on a live version will freeze it -->
<div
v-else-if="existingData?.autoSync && !autoSyncModel"
class="flex items-start space-x-2 px-3 py-2.5 rounded-md bg-blue-500/5 border border-blue-500/20"
>
<icon-lucide-info
class="w-3.5 h-3.5 text-blue-600 flex-shrink-0 mt-0.5"
/>
<span class="text-xs text-blue-600 leading-relaxed">
{{ t("documentation.publish.live_freeze_notice") }}
</span>
</div>
<!-- Destructive warning: promoting a snapshot to live will overwrite the frozen tree -->
<div
v-else-if="existingData?.autoSync === false && autoSyncModel"
class="flex items-start space-x-2 px-3 py-2.5 rounded-md bg-yellow-500/5 border border-yellow-500/20"
>
<icon-lucide-alert-triangle
class="w-3.5 h-3.5 text-yellow-600 flex-shrink-0 mt-0.5"
/>
<span class="text-xs text-yellow-600 leading-relaxed">
{{ t("documentation.publish.snapshot_promote_warning") }}
</span>
</div>
</div>
<!-- Right: Snapshot Preview -->
@ -141,7 +186,10 @@
</div>
<!-- Snapshot content -->
<div v-else-if="snapshotCollectionData" class="h-[60vh] flex flex-col">
<div
v-else-if="snapshotCollectionData"
class="flex-1 flex flex-col overflow-hidden max-h-[55vh]"
>
<DocumentationContent
:collection-data="snapshotCollectionData"
:all-items="snapshotItems"
@ -176,7 +224,7 @@ import IconCopy from "~icons/lucide/copy"
import IconCheck from "~icons/lucide/check"
import IconExternalLink from "~icons/lucide/external-link"
import IconRefreshCw from "~icons/lucide/refresh-cw"
import { isLiveVersion } from "~/services/documentation.service"
import { WorkspaceType } from "~/helpers/backend/graphql"
const t = useI18n()
@ -193,13 +241,44 @@ const props = defineProps<{
existingData?: ExistingData
publishedUrl: string | null
show: boolean
publishTitle: string
publishVersion: string
autoSync: boolean
selectedEnvironmentID: string | null
isValidVersion: boolean
workspaceType: WorkspaceType
workspaceID: string
}>()
const emit = defineEmits<{
(e: "copyUrl"): void
(e: "viewPublished"): void
(e: "update:publishTitle", value: string): void
(e: "update:publishVersion", value: string): void
(e: "update:autoSync", value: boolean): void
(e: "update:selectedEnvironmentID", value: string | null): void
}>()
const titleModel = computed({
get: () => props.publishTitle,
set: (v) => emit("update:publishTitle", v),
})
const versionModel = computed({
get: () => props.publishVersion,
set: (v) => emit("update:publishVersion", v),
})
const autoSyncModel = computed({
get: () => props.autoSync,
set: (v) => emit("update:autoSync", v),
})
const environmentModel = computed({
get: () => props.selectedEnvironmentID,
set: (v) => emit("update:selectedEnvironmentID", v),
})
const copyIcon = refAutoReset(markRaw(IconCopy), 3000)
const handleCopyUrl = () => {
@ -221,14 +300,6 @@ const snapshotCollectionData = ref<HoppCollection | null>(null)
const snapshotItems = ref<SnapshotDocumentationItem[]>([])
const snapshotEnvironmentVariables = ref<Environment["variables"]>([])
/**
* Checks whether the currently displayed published doc is the live (current) version.
*/
const isLive = computed(() => {
if (!props.existingData) return true
return isLiveVersion(props.existingData)
})
/**
* Extracts slug and version from a published doc URL
*/

View file

@ -102,8 +102,13 @@
<div
class="flex items-center border border-accent pl-4 pr-2 rounded cursor-pointer"
>
<icon-lucide-globe class="svg-icons" />
<div class="relative flex items-center">
<icon-lucide-globe class="svg-icons" />
<span
v-if="selectedVersionDoc?.autoSync"
class="absolute -top-0.5 -right-0.5 w-1.5 h-1.5 rounded-full bg-green-500 ring-1 ring-primary animate-pulse"
/>
</div>
<HoppButtonSecondary
:icon="IconCheveronDown"
reverse
@ -237,11 +242,6 @@
:workspace-i-d="isTeamCollection ? teamID || '' : ''"
:mode="publishModalMode"
:is-first-publish="!isCollectionPublished && !isCreatingNewVersion"
:is-auto-sync-locked="
!!selectedVersionDoc &&
isLiveVersion(selectedVersionDoc) &&
!isCreatingNewVersion
"
:published-doc-id="publishedDocId"
:existing-data="existingPublishedData"
:loading="isProcessingPublish"
@ -383,9 +383,12 @@ const publishedDocs = computed(() => {
const selectedVersionDoc = ref<PublishedDocInfo | null>(null)
// When viewing a snapshot from the dropdown, we use a separate ref so the
// selected (dropdown) version isn't changed. The modal reads from this ref.
const viewingSnapshotDoc = ref<PublishedDocInfo | null>(null)
/**
* Finds the CURRENT version from the published docs list.
* The CURRENT version is the initial publish identified by version string "CURRENT" (case-insensitive).
* Finds the live (auto-synced) version from the published docs list.
* Falls back to the last doc (oldest, since the list is in descending order).
*/
const findCurrentVersion = (docs: PublishedDocInfo[]): PublishedDocInfo => {
@ -395,6 +398,14 @@ const findCurrentVersion = (docs: PublishedDocInfo[]): PublishedDocInfo => {
watch(
publishedDocs,
(docs) => {
// Keep the snapshot-viewing doc in sync with the latest data (by ID)
if (viewingSnapshotDoc.value) {
const foundViewing = docs?.find(
(d) => d.id === viewingSnapshotDoc.value?.id
)
viewingSnapshotDoc.value = foundViewing ?? null
}
if (docs && docs.length > 0) {
// If we already have a selected version, try to keep it (by ID)
if (selectedVersionDoc.value) {
@ -414,17 +425,25 @@ watch(
)
const isCollectionPublished = computed(() => publishedDocs.value.length > 0)
const publishedDocId = computed(() => selectedVersionDoc.value?.id)
// The doc that the publish modal should operate on the snapshot-being-viewed
// takes precedence, otherwise fall back to the selected (dropdown) version.
const activeModalDoc = computed<PublishedDocInfo | null>(
() => viewingSnapshotDoc.value || selectedVersionDoc.value
)
const publishedDocId = computed(() => activeModalDoc.value?.id)
const existingPublishedData = computed(() => {
if (isCreatingNewVersion.value) return undefined
if (!selectedVersionDoc.value) return undefined
if (!activeModalDoc.value) return undefined
return {
title: selectedVersionDoc.value.title,
version: selectedVersionDoc.value.version,
autoSync: selectedVersionDoc.value.autoSync,
url: selectedVersionDoc.value.url,
environmentName: selectedVersionDoc.value.environmentName ?? null,
environmentID: selectedVersionDoc.value.environmentID ?? null,
id: activeModalDoc.value.id,
title: activeModalDoc.value.title,
version: activeModalDoc.value.version,
autoSync: activeModalDoc.value.autoSync,
url: activeModalDoc.value.url,
environmentName: activeModalDoc.value.environmentName ?? null,
environmentID: activeModalDoc.value.environmentID ?? null,
}
})
@ -592,18 +611,22 @@ const openPublishModalForView = () => {
/**
* Handles version selection from the dropdown.
* For frozen (snapshot) versions, auto-opens the snapshot view modal.
* For frozen (snapshot) versions, auto-opens the snapshot view modal without
* changing the currently selected dropdown version.
* For live versions, just selects them (user can then click Edit).
*/
const handleVersionSelect = (
doc: PublishedDocInfo,
hideDropdown: () => void
) => {
selectedVersionDoc.value = doc
if (!isLiveVersion(doc)) {
viewingSnapshotDoc.value = doc
hideDropdown()
openPublishModalForView()
return
}
selectedVersionDoc.value = doc
hideDropdown()
}
const createNewVersion = () => {
@ -616,11 +639,10 @@ watch(showPublishModal, (isOpen) => {
if (!isOpen) {
// Reset selection back to the CURRENT version so the dropdown
// label matches what the editor is actually showing
if (isViewingSnapshot.value || isCreatingNewVersion.value) {
if (publishedDocs.value.length > 0) {
selectedVersionDoc.value = findCurrentVersion(publishedDocs.value)
}
if (isCreatingNewVersion.value && publishedDocs.value.length > 0) {
selectedVersionDoc.value = findCurrentVersion(publishedDocs.value)
}
viewingSnapshotDoc.value = null
isCreatingNewVersion.value = false
isViewingSnapshot.value = false
}
@ -1005,9 +1027,17 @@ const handlePublish = async (
)
}
// Select the new version and exit create mode
selectedVersionDoc.value = newDocInfo
isCreatingNewVersion.value = false
// Only select the new version if it's live; for non-live (snapshot)
// versions, keep the previously selected live version in the dropdown
// and close the modal (otherwise the mode would recompute to "update"
// for the still-selected live version).
if (isLiveVersion(newDocInfo)) {
selectedVersionDoc.value = newDocInfo
isCreatingNewVersion.value = false
} else {
isCreatingNewVersion.value = false
showPublishModal.value = false
}
}
)
)()

View file

@ -11,14 +11,32 @@
</div>
<div class="flex items-center gap-4">
<span
class="text-md font-bold text-secondaryDark px-6 py-1 rounded-full border border-dividerDark shadow"
class="text-md font-bold text-secondaryDark px-6 py-1 rounded-full"
>
{{
publishedDoc?.title || t("documentation.publish.untitled_project")
}}
</span>
<div>
<div class="flex items-center gap-2">
<!-- Live indicator pill -->
<div
v-if="isCurrentDocLive"
class="flex items-center gap-1 px-1.5 py-0.5 rounded-full bg-gradient-to-r from-green-500/10 to-emerald-500/10 shadow-sm shadow-green-500/10"
>
<span class="relative flex items-center justify-center">
<span
class="absolute w-2 h-2 rounded-full bg-green-500/40 animate-ping"
/>
<span class="relative w-1 h-1 rounded-full bg-green-500" />
</span>
<span
class="text-[9px] font-bold uppercase tracking-wider text-green-600"
>
{{ t("documentation.publish.live") }}
</span>
</div>
<!-- Version dropdown (when multiple versions exist) -->
<tippy
v-if="versions.length"
@ -28,17 +46,11 @@
:on-shown="() => versionDropdownRef?.focus()"
>
<button
class="flex items-center gap-1.5 px-2.5 py-1 text-xs font-medium rounded-md cursor-pointer transition-colors"
:class="
isCurrentDocLive
? 'bg-green-500/10 text-green-600 hover:bg-green-500/20'
: 'bg-accent/10 text-accent hover:bg-accent/20'
"
class="flex items-center gap-1.5 px-2.5 py-1 text-xs font-medium rounded-md cursor-pointer transition-colors bg-accent/10 text-accent hover:bg-accent/20 border border-dividerDark"
>
<icon-lucide-globe class="w-3.5 h-3.5" />
{{
isCurrentDocLive
? t("documentation.publish.live")
: `${publishedDoc?.version}`
publishedDoc?.version || t("documentation.publish.published")
}}
<icon-lucide-chevron-down class="w-3 h-3" />
</button>
@ -51,40 +63,30 @@
tabindex="0"
@keyup.escape="hide()"
>
<HoppSmartItem
<div
v-for="ver in versions"
:key="ver.id"
:label="getVersionLabel(ver)"
:info-icon="
ver.version === publishedDoc?.version
? IconCheck
: undefined
"
:active-info-icon="ver.version === publishedDoc?.version"
@click="
() => {
navigateToVersion(ver)
hide()
}
"
:class="{ 'version-item--live': isLiveVersion(ver) }"
class="flex-1"
>
<template #prefix>
<span
class="px-1.5 py-0.5 text-[10px] font-semibold uppercase rounded mr-2"
:class="
isLiveVersion(ver)
? 'bg-green-500/10 text-green-600'
: 'bg-accent/10 text-accent'
"
>
{{
isLiveVersion(ver)
? t("documentation.publish.live")
: t("documentation.publish.snapshot")
}}
</span>
</template>
</HoppSmartItem>
<HoppSmartItem
:icon="IconGlobe"
:label="ver.version"
:info-icon="
ver.version === publishedDoc?.version
? IconCheck
: undefined
"
:active-info-icon="ver.version === publishedDoc?.version"
class="w-full"
@click="
() => {
navigateToVersion(ver)
hide()
}
"
/>
</div>
</div>
</template>
</tippy>
@ -157,6 +159,7 @@ import { useRouter } from "vue-router"
import { computed, PropType, ref } from "vue"
import { PublishedDocs } from "~/helpers/backend/graphql"
import IconCheck from "~icons/lucide/check"
import IconGlobe from "~icons/lucide/globe"
import IconLayers from "~icons/lucide/layers"
import { isLiveVersion } from "~/services/documentation.service"
@ -203,22 +206,16 @@ const versionDropdownRef = ref<HTMLElement | null>(null)
const envDropdownRef = ref<HTMLElement | null>(null)
/**
* Checks whether the currently displayed published doc is the live (current) version.
* This is true if the doc is auto-synced, has the CURRENT version identifier, or has version 1.0.0 (legacy).
* Checks whether the currently displayed published doc is the live version
* this is purely based on the auto-sync flag.
*/
const isCurrentDocLive = computed(() => {
if (!props.publishedDoc?.version) return true
if (!props.publishedDoc) return false
return isLiveVersion({
autoSync: props.publishedDoc.autoSync ?? false,
version: props.publishedDoc.version,
})
})
const getVersionLabel = (ver: PublishedDocVersion): string => {
if (isLiveVersion(ver)) return t("documentation.publish.live")
return `${ver.version}`
}
const navigateToVersion = (ver: PublishedDocVersion) => {
if (ver.version === props.publishedDoc?.version) return
@ -235,3 +232,10 @@ const navigateToVersion = (ver: PublishedDocVersion) => {
}
}
</script>
<style scoped>
/* Color the leading globe icon green for live versions */
.version-item--live :deep(.svg-icons.mr-4) {
@apply text-green-500;
}
</style>

View file

@ -14,7 +14,6 @@ import {
SetCollectionDocumentationOptions,
SetRequestDocumentationOptions,
isLiveVersion,
CURRENT_VERSION_TAG,
} from "../documentation.service"
import { platform } from "~/platform"
@ -750,33 +749,11 @@ describe("DocumentationService", () => {
})
describe("isLiveVersion", () => {
it("returns true when autoSync is true and version is CURRENT", () => {
expect(
isLiveVersion({ autoSync: true, version: CURRENT_VERSION_TAG })
).toBe(true)
it("returns true when autoSync is true", () => {
expect(isLiveVersion({ autoSync: true })).toBe(true)
})
it("is case-insensitive for CURRENT tag", () => {
expect(isLiveVersion({ autoSync: true, version: "current" })).toBe(true)
expect(isLiveVersion({ autoSync: true, version: "Current" })).toBe(true)
})
it("returns true for legacy 1.0.0 version with autoSync", () => {
expect(isLiveVersion({ autoSync: true, version: "1.0.0" })).toBe(true)
})
it("returns false when autoSync is false even if version is CURRENT", () => {
expect(
isLiveVersion({ autoSync: false, version: CURRENT_VERSION_TAG })
).toBe(false)
})
it("returns false when autoSync is false for legacy 1.0.0", () => {
expect(isLiveVersion({ autoSync: false, version: "1.0.0" })).toBe(false)
})
it("returns false for a snapshot version string", () => {
expect(isLiveVersion({ autoSync: true, version: "2.0.0" })).toBe(false)
expect(isLiveVersion({ autoSync: false, version: "2.0.0" })).toBe(false)
it("returns false when autoSync is false", () => {
expect(isLiveVersion({ autoSync: false })).toBe(false)
})
})

View file

@ -100,16 +100,11 @@ export const CURRENT_VERSION_TAG = "CURRENT"
/**
* Checks whether a published doc version is the live (current) version.
* A live version is auto-synced, has the CURRENT version identifier,
* or has version 1.0.0 (used in older versions of the project).
* This version is in sync with the particular collection and will update if the collection is updated.
* A live version is one that has auto-sync enabled it stays in sync with
* the collection and will update whenever the collection is updated.
*/
export const isLiveVersion = (doc: {
autoSync: boolean
version: string
}): boolean =>
doc.autoSync &&
(doc.version.toUpperCase() === CURRENT_VERSION_TAG || doc.version === "1.0.0")
export const isLiveVersion = (doc: { autoSync: boolean }): boolean =>
doc.autoSync
/**
* This service manages edited documentation for collections and requests.