Files
supabase/apps/studio/components/interfaces/Database/Replication/PipelineStatePill.tsx
T
Danny White e5dde899cd fix(studio): stabilise pipeline header loading (#50964)
## Problem

The pipeline detail header shifts slightly as its loading placeholders
become text.

- Resolves
[DEPR-683](https://linear.app/supabase/issue/DEPR-683/reduce-layout-shift-in-pipeline-header-loading-states)

## Solution

Match the breadcrumb, title, status, and destination placeholders to
their loaded line heights.

## Review instructions

1. Open a pipeline detail page and reload it. Check that the header
height stays steady as its data arrives.

## Checklist

- [x] I have read
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Style**
* Increased the height of loading placeholders for replication pipeline
status, breadcrumbs, page titles, and destination names.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-30 09:11:19 +10:00

92 lines
3.1 KiB
TypeScript

import { cn, Tooltip, TooltipContent, TooltipTrigger } from 'ui'
import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader'
import { TOOLTIP_UNDERLINE_CLASS_NAME } from './DetailSubtext'
import { getPipelineDisplayState, getStatusName } from './Pipeline.utils'
import type { PipelineDisplayType } from './Pipeline.utils'
import { PipelineStatusName } from './Replication.constants'
import { StateDot, type StateDotVariant } from '@/components/ui/StateDot'
import { ReplicationPipelineStatusData } from '@/data/replication/pipeline-status-query'
import { PipelineStatusRequestStatus } from '@/state/replication-pipeline-request-status'
import type { ResponseError } from '@/types'
const VARIANT_BY_TYPE: Record<PipelineDisplayType, StateDotVariant> = {
success: 'success',
failure: 'destructive',
loading: 'warning',
idle: 'default',
}
interface PipelineStatePillProps {
pipelineStatus: ReplicationPipelineStatusData['status'] | undefined
error: ResponseError | null
isLoading: boolean
isError: boolean
isSuccess: boolean
requestStatus: PipelineStatusRequestStatus
projectRef?: string
pipelineId?: number
}
/**
* Status of a pipeline as a dot and label. The label carries a dotted underline because its
* explanation lives in the tooltip. Tooltips stay plain text (no links).
*/
export const PipelineStatePill = ({
pipelineStatus,
error,
isLoading,
isError,
isSuccess,
requestStatus,
}: PipelineStatePillProps) => {
const statusName = getStatusName(pipelineStatus)
const { type, message, label } = getPipelineDisplayState(requestStatus, statusName)
const isRequestPending = requestStatus !== PipelineStatusRequestStatus.None
const shouldShowError = isError && !isRequestPending
const shouldShowLogsHint =
isSuccess &&
!isRequestPending &&
[PipelineStatusName.UNKNOWN, PipelineStatusName.FAILED].includes(
statusName as PipelineStatusName
)
if (isLoading && !isRequestPending) {
return (
<span className="inline-flex" aria-live="polite" aria-atomic="true">
<span className="sr-only">Loading pipeline status</span>
<ShimmeringLoader className="h-5 w-20 py-0" />
</span>
)
}
let tooltipMessage = message
if (shouldShowError) {
tooltipMessage = `Unable to retrieve status: ${error?.message}`
} else if (shouldShowLogsHint) {
tooltipMessage = `${message}. Check the logs for more information.`
}
return (
<span className="inline-flex" aria-live="polite" aria-atomic="true">
<Tooltip>
<TooltipTrigger asChild>
<StateDot
tabIndex={0}
variant={shouldShowError ? 'default' : VARIANT_BY_TYPE[type]}
isPulsing={!shouldShowError && type === 'loading'}
labelClassName={cn('text-foreground-light', TOOLTIP_UNDERLINE_CLASS_NAME)}
>
{shouldShowError ? 'Unknown' : label}
</StateDot>
</TooltipTrigger>
<TooltipContent side="bottom" className="max-w-64">
{tooltipMessage}
{isError && isRequestPending && ` Unable to refresh status: ${error?.message}.`}
</TooltipContent>
</Tooltip>
</span>
)
}