Repository: sanity-io/sanity
Pull request #8989: feat: releases tool tables show skeletons when loading rather than spinner
Author: login: jordanl17
Created: 2025-03-19T17:02:51Z | Merged: 2025-03-21T16:33:26Z


### Description

https://github.com/user-attachments/assets/8d919234-4e22-413f-9b03-2bb0f2b609c1


<!--
What changes are introduced?
Why are these changes introduced?
What issue(s) does this solve? (with link, if possible)
-->

### What to review

<!--
What steps should the reviewer take in order to review?
What parts/flows of the application/packages/tooling is affected?
-->

### Testing
Existing tests are fine
<!--
Did you add sufficient testing for this change?
If not, please explain how you tested this change and why it was not
possible/practical for writing an automated test
-->

### Notes for release
N/A
<!--
Engineers do not need to worry about the final copy,
but they must provide the docs team with enough context on:

* What changed
* How does one use it (code snippets, etc)
* Are there limitations we should be aware of

If this is PR is a partial implementation of a feature and is not enabled by default or if
this PR does not contain changes that needs mention in the release notes (tooling chores etc),
please call this out explicitly by writing "Part of feature X" or "Not required" in this section.
-->



Diff:
diff --git a/packages/sanity/src/core/releases/tool/components/Table/Table.tsx b/packages/sanity/src/core/releases/tool/components/Table/Table.tsx
index c02563c0dbdc..48dbe7120a11 100644
--- a/packages/sanity/src/core/releases/tool/components/Table/Table.tsx
+++ b/packages/sanity/src/core/releases/tool/components/Table/Table.tsx
@@ -22,14 +22,13 @@ import {
 } from 'react'
 
 import {TooltipDelayGroupProvider} from '../../../../../ui-components'
-import {LoadingBlock} from '../../../../components'
 import {TableHeader} from './TableHeader'
 import {TableProvider, type TableSort, useTableContext} from './TableProvider'
 import {type Column} from './types'
 
-type RowDatum<TableData, AdditionalRowTableData> = AdditionalRowTableData extends undefined
+type RowDatum<TableData, AdditionalRowTableData> = (AdditionalRowTableData extends undefined
   ? TableData
-  : TableData & AdditionalRowTableData
+  : TableData & AdditionalRowTableData) & {isLoading?: boolean}
 
 export type TableRowProps = Omit<
   CardProps & Omit<HTMLProps<HTMLDivElement>, 'height' | 'as'>,
@@ -37,6 +36,13 @@ export type TableRowProps = Omit<
 > &
   RefAttributes<HTMLDivElement>
 
+type VirtualDatum = {
+  virtualRow: VirtualItem
+  index: number
+  isFirst: boolean
+  isLast: boolean
+}
+
 export interface TableProps<TableData, AdditionalRowTableData> {
   columnDefs: Column<RowDatum<TableData, AdditionalRowTableData>>[]
   searchFilter?: (data: TableData[], searchTerm: string) => TableData[]
@@ -58,6 +64,7 @@ export interface TableProps<TableData, AdditionalRowTableData> {
 }
 
 const ITEM_HEIGHT = 59
+const LOADING_ROW_COUNT = 3
 
 /**
  * This function modifies the rangeExtractor to account for the offset of the virtualizer
@@ -160,7 +167,7 @@ const TableInner = <TableData, AdditionalRowTableData>({
       ),
       cell: ({datum, cellProps: {id}}) => (
         <Flex as="td" id={id} align="center" flex="none" padding={3} style={{width: '25px'}}>
-          {rowActions?.({datum}) || <Box style={{width: '25px'}} />}
+          {(!datum.isLoading && rowActions?.({datum})) || <Box style={{width: '25px'}} />}
         </Flex>
       ),
     }),
@@ -175,18 +182,15 @@ const TableInner = <TableData, AdditionalRowTableData>({
   const renderRow = useMemo(
     () =>
       function TableRow(
-        datum: (TableData | (TableData & AdditionalRowTableData)) & {
-          virtualRow: VirtualItem
-          index: number
-          isFirst: boolean
-          isLast: boolean
-        },
+        datum: VirtualDatum &
+          (TableData | (TableData & AdditionalRowTableData) | {_id: string; isLoading: boolean}),
       ) {
         const cardRowProps = rowProps(datum as TableData)
+        const cardKey = loading ? `skeleton-${datum.index}` : String(get(datum, rowId))
 
         return (
           <Card
-            key={String(get(datum, rowId))}
+            key={cardKey}
             data-testid="table-row"
             as="tr"
             borderBottom
@@ -204,7 +208,9 @@ const TableInner = <TableData, AdditionalRowTableData>({
             {amalgamatedColumnDefs.map(({cell: Cell, style, width, id, sorting = false}) => (
               <Fragment key={String(id)}>
                 <Cell
-                  datum={datum as RowDatum<TableData, AdditionalRowTableData>}
+                  datum={
+                    {...datum, isLoading: loading} as RowDatum<TableData, AdditionalRowTableData>
+                  }
                   cellProps={{
                     as: 'td',
                     id: String(id),
@@ -217,7 +223,7 @@ const TableInner = <TableData, AdditionalRowTableData>({
           </Card>
         )
       },
-    [amalgamatedColumnDefs, rowId, rowProps],
+    [amalgamatedColumnDefs, loading, rowId, rowProps],
   )
 
   const emptyContent = useMemo(() => {
@@ -252,11 +258,56 @@ const TableInner = <TableData, AdditionalRowTableData>({
 
   const theme = useTheme()
 
-  if (loading) {
-    return <LoadingBlock fill data-testid="table-loading" />
+  const maxInlineSize = (!hideTableInlinePadding && theme.sanity.v2?.container[3]) || 0
+
+  const renderLoadingRows = (
+    rowRenderer: (
+      datum: VirtualDatum &
+        ({_id: string; isLoading: boolean} | TableData | (TableData & AdditionalRowTableData)),
+    ) => React.ReactNode,
+  ) => {
+    return Array.from({length: LOADING_ROW_COUNT}).map((el, index) => {
+      const cardKey = `skeleton-${index}`
+      const virtualRow: VirtualItem = {
+        index,
+        start: index * ITEM_HEIGHT,
+        size: ITEM_HEIGHT,
+        lane: 0,
+        key: cardKey,
+        end: index * ITEM_HEIGHT + ITEM_HEIGHT,
+      }
+
+      return rowRenderer({
+        _id: cardKey,
+        isLoading: true,
+        virtualRow,
+        index,
+        isFirst: index === 0,
+        isLast: index === LOADING_ROW_COUNT - 1,
+      })
+    })
   }
 
-  const maxInlineSize = (!hideTableInlinePadding && theme.sanity.v2?.container[3]) || 0
+  const tableContent = () => {
+    if (loading) {
+      return renderLoadingRows(renderRow)
+    }
+
+    if (filteredData.length === 0) {
+      return emptyContent
+    }
+
+    return rowVirtualizer.getVirtualItems().map((virtualRow, index) => {
+      const datum = filteredData[virtualRow.index]
+      return renderRow({
+        ...datum,
+        virtualRow,
+        index,
+        isFirst: virtualRow.index === 0,
+        isLast: virtualRow.index === filteredData.length - 1,
+      })
+    })
+  }
 
   return (
     <div ref={virtualizerContainerRef}>
@@ -271,21 +322,11 @@ const TableInner = <TableData, AdditionalRowTableData>({
         }
       >
         <Stack as="table">
-          <TableHeader headers={headers} searchDisabled={!searchTerm && !data.length} />
-          <Stack as="tbody">
-            {filteredData.length === 0
-              ? emptyContent
-              : rowVirtualizer.getVirtualItems().map((virtualRow, index) => {
-                  const datum = filteredData[virtualRow.index]
-                  return renderRow({
-                    ...datum,
-                    virtualRow,
-                    index,
-                    isFirst: virtualRow.index === 0,
-                    isLast: virtualRow.index === filteredData.length - 1,
-                  })
-                })}
-          </Stack>
+          <TableHeader
+            headers={headers}
+            searchDisabled={loading || (!searchTerm && !data.length)}
+          />
+          <Stack as="tbody">{tableContent()}</Stack>
         </Stack>
       </div>
     </div>
diff --git a/packages/sanity/src/core/releases/tool/components/Table/types.ts b/packages/sanity/src/core/releases/tool/components/Table/types.ts
index 3dd1be4a6613..00720b8d7864 100644
--- a/packages/sanity/src/core/releases/tool/components/Table/types.ts
+++ b/packages/sanity/src/core/releases/tool/components/Table/types.ts
@@ -9,7 +9,7 @@ export interface InjectedTableProps {
 export interface Column<TableData = unknown> {
   header: (props: HeaderProps) => React.JSX.Element
   cell: (props: {
-    datum: TableData
+    datum: TableData & {isLoading?: boolean}
     cellProps: InjectedTableProps
     sorting: boolean
   }) => React.ReactNode
diff --git a/packages/sanity/src/core/releases/tool/detail/ReleaseDetail.tsx b/packages/sanity/src/core/releases/tool/detail/ReleaseDetail.tsx
index 5da495c2122a..a34377860ded 100644
--- a/packages/sanity/src/core/releases/tool/detail/ReleaseDetail.tsx
+++ b/packages/sanity/src/core/releases/tool/detail/ReleaseDetail.tsx
@@ -74,13 +74,11 @@ export const ReleaseDetail = () => {
         </Box>
       )
     }
-    if (documentsLoading) {
-      return <LoadingBlock title={t('loading-release-documents')} showText />
-    }
     if (!releaseInDetail) return null
 
     return (
       <ReleaseSummary
+        isLoading={documentsLoading}
         documents={results}
         release={releaseInDetail}
         documentsHistory={history.documentsHistory}
diff --git a/packages/sanity/src/core/releases/tool/detail/ReleaseSummary.tsx b/packages/sanity/src/core/releases/tool/detail/ReleaseSummary.tsx
index c476732aace0..30af985ce54d 100644
--- a/packages/sanity/src/core/releases/tool/detail/ReleaseSummary.tsx
+++ b/packages/sanity/src/core/releases/tool/detail/ReleaseSummary.tsx
@@ -32,6 +32,7 @@ export interface ReleaseSummaryProps {
   documentsHistory: Record<string, DocumentHistory>
   scrollContainerRef: RefObject<HTMLDivElement | null>
   release: ReleaseDocument
+  isLoading?: boolean
 }
 
 const isBundleDocumentRow = (
@@ -46,7 +47,7 @@ const isBundleDocumentRow = (
   'history' in maybeBundleDocumentRow
 
 export function ReleaseSummary(props: ReleaseSummaryProps) {
-  const {documents, documentsHistory, release, scrollContainerRef} = props
+  const {documents, documentsHistory, isLoading = false, release, scrollContainerRef} = props
   const toast = useToast()
   const {createVersion} = useReleaseOperations()
   const telemetry = useTelemetry()
@@ -176,6 +177,7 @@ export function ReleaseSummary(props: ReleaseSummaryProps) {
   return (
     <Card borderTop data-testid="document-table-card" ref={scrollContainerRef}>
       <Table<DocumentWithHistory>
+        loading={isLoading}
         data={tableData}
         emptyState={t('summary.no-documents')}
         // eslint-disable-next-line @sanity/i18n/no-attribute-string-literals
@@ -191,6 +193,7 @@ export function ReleaseSummary(props: ReleaseSummaryProps) {
           <Card padding={3}>
             <Button
               icon={AddIcon}
+              disabled={isLoading}
               mode="bleed"
               onClick={() => setAddDocumentDialog(true)}
               text={t('action.add-document')}
diff --git a/packages/sanity/src/core/releases/tool/detail/__tests__/ReleaseDetail.test.tsx b/packages/sanity/src/core/releases/tool/detail/__tests__/ReleaseDetail.test.tsx
index b2f30b51576e..aa8767bb11f7 100644
--- a/packages/sanity/src/core/releases/tool/detail/__tests__/ReleaseDetail.test.tsx
+++ b/packages/sanity/src/core/releases/tool/detail/__tests__/ReleaseDetail.test.tsx
@@ -161,10 +161,6 @@ describe('ReleaseDetail', () => {
       await renderTest()
     })
 
-    it('should show loading spinner', () => {
-      screen.getByTestId('loading-block')
-    })
-
     it('should show the header', () => {
       screen.getByText(activeASAPRelease.metadata.title)
       screen.getByTestId('release-menu-button')
diff --git a/packages/sanity/src/core/releases/tool/detail/documentTable/DocumentTableColumnDefs.tsx b/packages/sanity/src/core/releases/tool/detail/documentTable/DocumentTableColumnDefs.tsx
index c232eeb2c762..d5cd96457291 100644
--- a/packages/sanity/src/core/releases/tool/detail/documentTable/DocumentTableColumnDefs.tsx
+++ b/packages/sanity/src/core/releases/tool/detail/documentTable/DocumentTableColumnDefs.tsx
@@ -65,7 +65,7 @@ const documentActionColumn: (t: TFunction<'releases', undefined>) => Column<Bund
   ),
   cell: ({cellProps, datum}) => {
     const actionBadge = () => {
-      if (datum.isPending) return null
+      if (datum.isPending || datum.isLoading) return null
 
       const willBeUnpublished = isGoingToUnpublish(datum.document)
       if (willBeUnpublished) {
@@ -120,7 +120,7 @@ export const getDocumentTableColumnDefs: (
     cell: ({cellProps, datum}) => (
       <Flex align="center" {...cellProps}>
         <Box paddingX={2}>
-          <MemoDocumentType type={datum.document._type} />
+          {!datum.isLoading && <MemoDocumentType type={datum.document._type} />}
         </Box>
       </Flex>
     ),
@@ -139,7 +139,7 @@ export const getDocumentTableColumnDefs: (
     ),
     cell: ({cellProps, datum}) => (
       <Box {...cellProps} flex={1} padding={1} paddingRight={2} sizing="border">
-        {datum.isPending ? (
+        {datum.isPending || datum.isLoading ? (
           <SanityDefaultPreview isPlaceholder />
         ) : (
           <MemoReleaseDocumentPreview
@@ -161,7 +161,7 @@ export const getDocumentTableColumnDefs: (
         <Headers.SortHeaderButton text={t('table-header.edited')} {...props} />
       </Flex>
     ),
-    cell: ({cellProps, datum: {document, history}}) => (
+    cell: ({cellProps, datum: {document, history, isLoading}}) => (
       <Flex
         {...cellProps}
         align="center"
@@ -170,7 +170,7 @@ export const getDocumentTableColumnDefs: (
         style={{minWidth: 130}}
         sizing="border"
       >
-        {document._updatedAt && (
+        {!isLoading && document._updatedAt && (
           <Flex align="center" gap={2}>
             {history?.lastEditedBy && <UserAvatar size={0} user={history.lastEditedBy} />}
             <Text muted size={1}>
@@ -191,6 +191,8 @@ export const getDocumentTableColumnDefs: (
       </Flex>
     ),
     cell: ({cellProps, datum}) => {
+      if (datum.isLoading) return null
+
       const validationErrorCount = datum.validation.validation.filter(
         (validation) => validation.level === 'error',
       ).length
diff --git a/packages/sanity/src/core/releases/tool/overview/ReleasesOverview.tsx b/packages/sanity/src/core/releases/tool/overview/ReleasesOverview.tsx
index 085528f8db95..0a9a19a65b2e 100644
--- a/packages/sanity/src/core/releases/tool/overview/ReleasesOverview.tsx
+++ b/packages/sanity/src/core/releases/tool/overview/ReleasesOverview.tsx
@@ -300,7 +300,7 @@ export function ReleasesOverview() {
     ({datum}: {datum: TableRelease | unknown}) => {
       const release = datum as TableRelease
 
-      if (release.isDeleted) return null
+      if (release.isDeleted || release.isLoading) return null
 
       const documentsCount =
         (releaseGroupMode === 'active'
diff --git a/packages/sanity/src/core/releases/tool/overview/ReleasesOverviewColumnDefs.tsx b/packages/sanity/src/core/releases/tool/overview/ReleasesOverviewColumnDefs.tsx
index 45bd56f19545..ede9bb35c772 100644
--- a/packages/sanity/src/core/releases/tool/overview/ReleasesOverviewColumnDefs.tsx
+++ b/packages/sanity/src/core/releases/tool/overview/ReleasesOverviewColumnDefs.tsx
@@ -71,18 +71,22 @@ export const releasesOverviewColumnDefs: (
             <Headers.SortHeaderButton text={t('table-header.time')} {...props} />
           </Flex>
         ),
-        cell: ({cellProps, datum: release}) => (
-          <Flex {...cellProps} align="center" paddingX={2} paddingY={3} gap={2} sizing="border">
-            <Text muted size={1}>
-              <ReleaseTime release={release} />
-            </Text>
-            {isReleaseScheduledOrScheduling(release) && (
-              <Text size={1} data-testid="release-lock-icon">
-                <LockIcon />
+        cell: ({cellProps, datum: release}) => {
+          if (release.isLoading) return null
+
+          return (
+            <Flex {...cellProps} align="center" paddingX={2} paddingY={3} gap={2} sizing="border">
+              <Text muted size={1}>
+                <ReleaseTime release={release} />
               </Text>
-            )}
-          </Flex>
-        ),
+              {isReleaseScheduledOrScheduling(release) && (
+                <Text size={1} data-testid="release-lock-icon">
+                  <LockIcon />
+                </Text>
+              )}
+            </Flex>
+          )
+        },
       },
       'active',
     ),
diff --git a/packages/sanity/src/core/releases/tool/overview/columnCells/ReleaseName.tsx b/packages/sanity/src/core/releases/tool/overview/columnCells/ReleaseName.tsx
index 8e62d6f660e7..7d90733464ff 100644
--- a/packages/sanity/src/core/releases/tool/overview/columnCells/ReleaseName.tsx
+++ b/packages/sanity/src/core/releases/tool/overview/columnCells/ReleaseName.tsx
@@ -1,10 +1,12 @@
 import {PinFilledIcon, PinIcon} from '@sanity/icons'
-import {Box, Card, Flex, Stack, Text} from '@sanity/ui'
+import {Box, Card, Flex, Skeleton, Stack, Text} from '@sanity/ui'
 import {useCallback} from 'react'
 import {useTranslation} from 'react-i18next'
 import {useRouter} from 'sanity/router'
 
 import {Button, Tooltip} from '../../../../../ui-components'
+import {PREVIEW_SIZES} from '../../../../components/previews/constants'
+import {TitleSkeleton} from '../../../../components/previews/general/DetailPreview.styled'
 import {Translate} from '../../../../i18n'
 import {usePerspective} from '../../../../perspective/usePerspective'
 import {useSetPerspective} from '../../../../perspective/useSetPerspective'
@@ -23,7 +25,7 @@ export const ReleaseNameCell: Column<TableRelease>['cell'] = ({cellProps, datum:
   const {selectedReleaseId} = usePerspective()
   const setPerspective = useSetPerspective()
   const {state} = release
-  const releaseId = getReleaseIdFromReleaseDocumentId(release._id)
+  const releaseId = release.isLoading ? 'loading' : getReleaseIdFromReleaseDocumentId(release._id)
   const isArchived = state === 'archived'
   const isReleasePinned = releaseId === selectedReleaseId
 
@@ -35,6 +37,28 @@ export const ReleaseNameCell: Column<TableRelease>['cell'] = ({cellProps, datum:
     }
   }, [isReleasePinned, releaseId, setPerspective])
 
+  const WrapperBox = useCallback(
+    ({children}: {children: React.ReactNode}) => {
+      return (
+        <Box {...cellProps} paddingLeft={3} flex={1} paddingY={1} paddingRight={2} sizing="border">
+          {children}
+        </Box>
+      )
+    },
+    [cellProps],
+  )
+
+  if (release.isLoading) {
+    return (
+      <WrapperBox>
+        <Flex align="center" gap={2}>
+          <Skeleton animated radius={1} style={PREVIEW_SIZES.default.media} />
+          <TitleSkeleton />
+        </Flex>
+      </WrapperBox>
+    )
+  }
+
   const cardProps: TableRowProps = release.isDeleted
     ? {tone: 'transparent'}
     : {
@@ -48,7 +72,7 @@ export const ReleaseNameCell: Column<TableRelease>['cell'] = ({cellProps, datum:
   const displayTitle = release.metadata.title || tCore('release.placeholder-untitled-release')
 
   return (
-    <Box {...cellProps} paddingLeft={3} flex={1} paddingY={1} paddingRight={2} sizing={'border'}>
+    <WrapperBox>
       <Tooltip
         disabled={!release.isDeleted}
         content={
@@ -95,6 +119,6 @@ export const ReleaseNameCell: Column<TableRelease>['cell'] = ({cellProps, datum:
           </Card>
         </Flex>
       </Tooltip>
-    </Box>
+    </WrapperBox>
   )
 }


Reviews:
user: login: bjoerge
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-21T12:45:54Z
user: login: jordanl17
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-21T14:47:22Z
user: login: jordanl17
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-21T15:10:22Z
user: login: bjoerge
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-21T15:38:01Z
user: login: bjoerge
body: Nice work!
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-21T15:39:10Z

Review comments:
diff_hunk: @@ -252,11 +255,50 @@ const TableInner = <TableData, AdditionalRowTableData>({
 
   const theme = useTheme()
 
-  if (loading) {
-    return <LoadingBlock fill data-testid="table-loading" />
+  const maxInlineSize = (!hideTableInlinePadding && theme.sanity.v2?.container[3]) || 0
path: packages/sanity/src/core/releases/tool/components/Table/Table.tsx
user: login: bjoerge
body: [side note as I realize it's not part of these changes, but] wondering if we could get rid of this reference to v2 themes somehow?
created_at: 2025-03-21T12:41:28Z
author_association: MEMBER
diff_hunk: @@ -252,11 +255,50 @@ const TableInner = <TableData, AdditionalRowTableData>({
 
   const theme = useTheme()
 
-  if (loading) {
-    return <LoadingBlock fill data-testid="table-loading" />
+  const maxInlineSize = (!hideTableInlinePadding && theme.sanity.v2?.container[3]) || 0
+
+  const renderLoadingRows = (rowRenderer: (datum: any) => React.ReactNode) => {
+    return Array.from({length: LOADING_ROW_COUNT}).map((el, index) => {
+      const cardKey = `skeleton-${index}`
+      const virtualRow = {
+        index,
+        start: index * ITEM_HEIGHT,
+        size: ITEM_HEIGHT,
+        lane: 0,
+        key: cardKey,
+      }
+
+      return rowRenderer({
+        _id: cardKey,
+        isLoading: true,
+        virtualRow,
+        index,
+        isFirst: index === 0,
+        isLast: index === LOADING_ROW_COUNT - 1,
+      })
+    })
   }
 
-  const maxInlineSize = (!hideTableInlinePadding && theme.sanity.v2?.container[3]) || 0
+  const tableContent = () => {
+    if (loading) {
+      return renderLoadingRows(renderRow as (datum: any) => React.ReactNode)
path: packages/sanity/src/core/releases/tool/components/Table/Table.tsx
user: login: bjoerge
body: could we type `datum` as `object` or `Record<string, unknown>` here instead of `any`? Looks like we expect it to be spreadable, at least?
created_at: 2025-03-21T12:44:57Z
author_association: MEMBER
diff_hunk: @@ -252,11 +255,50 @@ const TableInner = <TableData, AdditionalRowTableData>({
 
   const theme = useTheme()
 
-  if (loading) {
-    return <LoadingBlock fill data-testid="table-loading" />
+  const maxInlineSize = (!hideTableInlinePadding && theme.sanity.v2?.container[3]) || 0
+
+  const renderLoadingRows = (rowRenderer: (datum: any) => React.ReactNode) => {
+    return Array.from({length: LOADING_ROW_COUNT}).map((el, index) => {
+      const cardKey = `skeleton-${index}`
+      const virtualRow = {
+        index,
+        start: index * ITEM_HEIGHT,
+        size: ITEM_HEIGHT,
+        lane: 0,
+        key: cardKey,
+      }
+
+      return rowRenderer({
+        _id: cardKey,
+        isLoading: true,
+        virtualRow,
+        index,
+        isFirst: index === 0,
+        isLast: index === LOADING_ROW_COUNT - 1,
+      })
+    })
   }
 
-  const maxInlineSize = (!hideTableInlinePadding && theme.sanity.v2?.container[3]) || 0
+  const tableContent = () => {
+    if (loading) {
+      return renderLoadingRows(renderRow as (datum: any) => React.ReactNode)
path: packages/sanity/src/core/releases/tool/components/Table/Table.tsx
user: login: jordanl17
body: Oh yes, good callout - I'll improve these types to make them more appropriate

- [x] Avoid `any` and improve types
created_at: 2025-03-21T14:47:22Z
author_association: MEMBER
diff_hunk: @@ -252,11 +255,50 @@ const TableInner = <TableData, AdditionalRowTableData>({
 
   const theme = useTheme()
 
-  if (loading) {
-    return <LoadingBlock fill data-testid="table-loading" />
+  const maxInlineSize = (!hideTableInlinePadding && theme.sanity.v2?.container[3]) || 0
path: packages/sanity/src/core/releases/tool/components/Table/Table.tsx
user: login: jordanl17
body: I'm not sure myself- perhaps you have more context, but this seems to be referring to sanity-ui v2, not studio v2. `useTheme` returns a `Theme_2` type. Is this legacy?
created_at: 2025-03-21T15:10:22Z
author_association: MEMBER
diff_hunk: @@ -252,11 +255,50 @@ const TableInner = <TableData, AdditionalRowTableData>({
 
   const theme = useTheme()
 
-  if (loading) {
-    return <LoadingBlock fill data-testid="table-loading" />
+  const maxInlineSize = (!hideTableInlinePadding && theme.sanity.v2?.container[3]) || 0
path: packages/sanity/src/core/releases/tool/components/Table/Table.tsx
user: login: bjoerge
body: > but this seems to be referring to sanity-ui v2, not studio v2.

that's right, yeah, it's legacy, so would be good to remove it at some point :)
created_at: 2025-03-21T15:38:01Z
author_association: MEMBER

PR comments:
user: login: vercel[bot]
created_at: 2025-03-19T17:02:56Z
body: [vc]: #Vteccy5bVI6YnSWyiSfh+8lBWI1a5KNIsS/sekkIYa4=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJ0ZXN0LXN0dWRpbyIsInJvb3REaXJlY3RvcnkiOm51bGwsImluc3BlY3RvclVybCI6Imh0dHBzOi8vdmVyY2VsLmNvbS9zYW5pdHktc2FuZGJveC90ZXN0LXN0dWRpby82bzJZeWRLNkpDU0F5WXY0akY1VnBMYlV5djdjIiwicHJldmlld1VybCI6InRlc3Qtc3R1ZGlvLWdpdC1mZWF0LXNhcHAtMjUxOC1za2VsZXRvbi1yZWxlYXNlLXRhYmxlLnNhbml0eS5kZXYiLCJuZXh0Q29tbWl0U3RhdHVzIjoiREVQTE9ZRUQiLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoidGVzdC1zdHVkaW8tZ2l0LWZlYXQtc2FwcC0yNTE4LXNrZWxldG9uLXJlbGVhc2UtdGFibGUuc2FuaXR5LmRldiJ9fSx7Im5hbWUiOiJzdHVkaW8td29ya3Nob3AiLCJyb290RGlyZWN0b3J5IjpudWxsLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoiIn0sImluc3BlY3RvclVybCI6Imh0dHBzOi8vdmVyY2VsLmNvbS9zYW5pdHktc2FuZGJveC9zdHVkaW8td29ya3Nob3AvQWpXeUhQNktzYXREU1Y3M3Y5TTVITFVaYmtTayIsIm5leHRDb21taXRTdGF0dXMiOiJJR05PUkVEIiwicHJldmlld1VybCI6InN0dWRpby13b3Jrc2hvcC1naXQtZmVhdC1zYXBwLTI1MTgtc2tlbGV0b24tcmVsZWFzZS10YWJsZS5zYW5pdHkuZGV2In0seyJuYW1lIjoicGVyZm9ybWFuY2Utc3R1ZGlvIiwicm9vdERpcmVjdG9yeSI6bnVsbCwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL3Nhbml0eS1zYW5kYm94L3BlcmZvcm1hbmNlLXN0dWRpby9LQ1d6TldOSFFXdU0yMnVkMVJxWlpCR2Vpa0JUIiwicHJldmlld1VybCI6InBlcmZvcm1hbmNlLXN0dWRpby1naXQtZmVhdC1zYXBwLTI1MTgtc2tlbGV0b24tcmVsZWFzZS10YWJsZS5zYW5pdHkuZGV2IiwibmV4dENvbW1pdFN0YXR1cyI6IkRFUExPWUVEIiwibGl2ZUZlZWRiYWNrIjp7InJlc29sdmVkIjowLCJ1bnJlc29sdmVkIjowLCJ0b3RhbCI6MCwibGluayI6InBlcmZvcm1hbmNlLXN0dWRpby1naXQtZmVhdC1zYXBwLTI1MTgtc2tlbGV0b24tcmVsZWFzZS10YWJsZS5zYW5pdHkuZGV2In19LHsibmFtZSI6InRlc3QtbmV4dC1zdHVkaW8iLCJyb290RGlyZWN0b3J5IjoiZGV2L3Rlc3QtbmV4dC1zdHVkaW8iLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoiIn0sImluc3BlY3RvclVybCI6Imh0dHBzOi8vdmVyY2VsLmNvbS9zYW5pdHktc2FuZGJveC90ZXN0LW5leHQtc3R1ZGlvL0F0Z0F0dVpNVTZKVnhqMmFqWVh1YlFwc0NKS3oiLCJuZXh0Q29tbWl0U3RhdHVzIjoiSUdOT1JFRCJ9LHsibmFtZSI6InBhZ2UtYnVpbGRpbmctc3R1ZGlvIiwicm9vdERpcmVjdG9yeSI6ImRldi9wYWdlLWJ1aWxkaW5nLXN0dWRpbyIsImluc3BlY3RvclVybCI6Imh0dHBzOi8vdmVyY2VsLmNvbS9zYW5pdHktc2FuZGJveC9wYWdlLWJ1aWxkaW5nLXN0dWRpby84WEh4U0FMSmtHTjhnMk1nZnhla1JXeDZOYzhtIiwicHJldmlld1VybCI6InBhZ2UtYnVpbGRpbmctc3R1ZGlvLWdpdC1mZWF0LXNhcHAtMjUxOC1za2VsZXRvbi1yZWxlYXNlLXRhYmxlLnNhbml0eS5kZXYiLCJuZXh0Q29tbWl0U3RhdHVzIjoiREVQTE9ZRUQiLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoicGFnZS1idWlsZGluZy1zdHVkaW8tZ2l0LWZlYXQtc2FwcC0yNTE4LXNrZWxldG9uLXJlbGVhc2UtdGFibGUuc2FuaXR5LmRldiJ9fV19
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **page-building-studio** | ✅ Ready ([Inspect](https://vercel.com/sanity-sandbox/page-building-studio/8XHxSALJkGN8g2MgfxekRWx6Nc8m)) | [Visit Preview](https://page-building-studio-git-feat-sapp-2518-skeleton-release-table.sanity.dev) | 💬 [**Add feedback**](https://vercel.live/open-feedback/page-building-studio-git-feat-sapp-2518-skeleton-release-table.sanity.dev?via=pr-comment-feedback-link) | Mar 21, 2025 4:27pm |
| **performance-studio** | ✅ Ready ([Inspect](https://vercel.com/sanity-sandbox/performance-studio/KCWzNWNHQWuM22ud1RqZZBGeikBT)) | [Visit Preview](https://performance-studio-git-feat-sapp-2518-skeleton-release-table.sanity.dev) | 💬 [**Add feedback**](https://vercel.live/open-feedback/performance-studio-git-feat-sapp-2518-skeleton-release-table.sanity.dev?via=pr-comment-feedback-link) | Mar 21, 2025 4:27pm |
| **test-studio** | ✅ Ready ([Inspect](https://vercel.com/sanity-sandbox/test-studio/6o2YydK6JCSAyYv4jF5VpLbUyv7c)) | [Visit Preview](https://test-studio-git-feat-sapp-2518-skeleton-release-table.sanity.dev) | 💬 [**Add feedback**](https://vercel.live/open-feedback/test-studio-git-feat-sapp-2518-skeleton-release-table.sanity.dev?via=pr-comment-feedback-link) | Mar 21, 2025 4:27pm |

<details><summary>2 Skipped Deployments</summary>

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **studio-workshop** | ⬜️ Ignored ([Inspect](https://vercel.com/sanity-sandbox/studio-workshop/AjWyHP6KsatDSV73v9M5HLUZbkSk)) | [Visit Preview](https://studio-workshop-git-feat-sapp-2518-skeleton-release-table.sanity.dev) |  | Mar 21, 2025 4:27pm |
| **test-next-studio** | ⬜️ Ignored ([Inspect](https://vercel.com/sanity-sandbox/test-next-studio/AtgAtuZMU6JVxj2ajYXubQpsCJKz)) |  |  | Mar 21, 2025 4:27pm |
</details>






author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-19T18:05:57Z
body: <h2>Coverage Report</h2>
<table> <thead> <tr> <th align="center">Status</th> <th align="left">Category</th> <th align="right">Percentage</th> <th align="right">Covered / Total</th> </tr> </thead> <tbody> <tr> <td align="center">🔵</td> <td align="left">Lines</td> <td align="right">43.26%</td> <td align="right">55960 / 129355</td> </tr> <tr> <td align="center">🔵</td> <td align="left">Statements</td> <td align="right">43.26%</td> <td align="right">55960 / 129355</td> </tr> <tr> <td align="center">🔵</td> <td align="left">Functions</td> <td align="right">47.22%</td> <td align="right">2783 / 5893</td> </tr> <tr> <td align="center">🔵</td> <td align="left">Branches</td> <td align="right">79.74%</td> <td align="right">10472 / 13132</td> </tr> </tbody> </table><details><summary>File Coverage</summary><table> <thead> <tr> <th align="left">File</th> <th align="right">Stmts</th> <th align="right">Branches</th> <th align="right">Functions</th> <th align="right">Lines</th> <th align="left">Uncovered Lines</th> </tr> </thead> <tbody> <tr> <td align="left" colspan="6"><b>Changed Files</b></td> </tr> <tr> <td align="left"><a href="https://github.com/sanity-io/sanity/blob/8c39fdb5b0ddac2e2a85bc5664d80f2e3b68e59b/packages/sanity/src/core/releases/tool/components/Table/Table.tsx">packages/sanity/src/core/releases/tool/components/Table/Table.tsx</a></td> <td align="right">32.34%</td> <td align="right">87.09%</td> <td align="right">76.47%</td> <td align="right">32.34%</td> <td align="left"><a href="https://github.com/sanity-io/sanity/blob/8c39fdb5b0ddac2e2a85bc5664d80f2e3b68e59b/packages/sanity/src/core/releases/tool/components/Table/Table.tsx#L97-L288" class="text-red">97-288</a></td> </tr> <tr> <td align="left"><a href="https://github.com/sanity-io/sanity/blob/8c39fdb5b0ddac2e2a85bc5664d80f2e3b68e59b/packages/sanity/src/core/releases/tool/components/Table/types.ts">packages/sanity/src/core/releases/tool/components/Table/types.ts</a></td> <td align="right">0%</td> <td align="right">0%</td> <td align="right">0%</td> <td align="right">0%</td> <td align="left"></td> </tr> <tr> <td align="left"><a href="https://github.com/sanity-io/sanity/blob/8c39fdb5b0ddac2e2a85bc5664d80f2e3b68e59b/packages/sanity/src/core/releases/tool/detail/ReleaseDetail.tsx">packages/sanity/src/core/releases/tool/detail/ReleaseDetail.tsx</a></td> <td align="right">74.1%</td> <td align="right">77.27%</td> <td align="right">100%</td> <td align="right">74.1%</td> <td align="left"><a href="https://github.com/sanity-io/sanity/blob/8c39fdb5b0ddac2e2a85bc5664d80f2e3b68e59b/packages/sanity/src/core/releases/tool/detail/ReleaseDetail.tsx#L53-L75" class="text-red">53-75</a>, <a href="https://github.com/sanity-io/sanity/blob/8c39fdb5b0ddac2e2a85bc5664d80f2e3b68e59b/packages/sanity/src/core/releases/tool/detail/ReleaseDetail.tsx#L143-L149" class="text-red">143-149</a></td> </tr> <tr> <td align="left"><a href="https://github.com/sanity-io/sanity/blob/8c39fdb5b0ddac2e2a85bc5664d80f2e3b68e59b/packages/sanity/src/core/releases/tool/detail/ReleaseSummary.tsx">packages/sanity/src/core/releases/tool/detail/ReleaseSummary.tsx</a></td> <td align="right">70.06%</td> <td align="right">75%</td> <td align="right">66.66%</td> <td align="right">70.06%</td> <td align="left"><a href="https://github.com/sanity-io/sanity/blob/8c39fdb5b0ddac2e2a85bc5664d80f2e3b68e59b/packages/sanity/src/core/releases/tool/detail/ReleaseSummary.tsx#L93" class="text-red">93</a>, <a href="https://github.com/sanity-io/sanity/blob/8c39fdb5b0ddac2e2a85bc5664d80f2e3b68e59b/packages/sanity/src/core/releases/tool/detail/ReleaseSummary.tsx#L103-L148" class="text-red">103-148</a>, <a href="https://github.com/sanity-io/sanity/blob/8c39fdb5b0ddac2e2a85bc5664d80f2e3b68e59b/packages/sanity/src/core/releases/tool/detail/ReleaseSummary.tsx#L157-L161" class="text-red">157-161</a>, <a href="https://github.com/sanity-io/sanity/blob/8c39fdb5b0ddac2e2a85bc5664d80f2e3b68e59b/packages/sanity/src/core/releases/tool/detail/ReleaseSummary.tsx#L167-L168" class="text-red">167-168</a></td> </tr> <tr> <td align="left"><a href="https://github.com/sanity-io/sanity/blob/8c39fdb5b0ddac2e2a85bc5664d80f2e3b68e59b/packages/sanity/src/core/releases/tool/detail/documentTable/DocumentTableColumnDefs.tsx">packages/sanity/src/core/releases/tool/detail/documentTable/DocumentTableColumnDefs.tsx</a></td> <td align="right">85.38%</td> <td align="right">71.87%</td> <td align="right">100%</td> <td align="right">85.38%</td> <td align="left"><a href="https://github.com/sanity-io/sanity/blob/8c39fdb5b0ddac2e2a85bc5664d80f2e3b68e59b/packages/sanity/src/core/releases/tool/detail/documentTable/DocumentTableColumnDefs.tsx#L72-L77" class="text-red">72-77</a>, <a href="https://github.com/sanity-io/sanity/blob/8c39fdb5b0ddac2e2a85bc5664d80f2e3b68e59b/packages/sanity/src/core/releases/tool/detail/documentTable/DocumentTableColumnDefs.tsx#L143" class="text-red">143</a>, <a href="https://github.com/sanity-io/sanity/blob/8c39fdb5b0ddac2e2a85bc5664d80f2e3b68e59b/packages/sanity/src/core/releases/tool/detail/documentTable/DocumentTableColumnDefs.tsx#L203-L223" class="text-red">203-223</a></td> </tr> <tr> <td align="left"><a href="https://github.com/sanity-io/sanity/blob/8c39fdb5b0ddac2e2a85bc5664d80f2e3b68e59b/packages/sanity/src/core/releases/tool/overview/ReleasesOverview.tsx">packages/sanity/src/core/releases/tool/overview/ReleasesOverview.tsx</a></td> <td align="right">93.08%</td> <td align="right">84.88%</td> <td align="right">80%</td> <td align="right">93.08%</td> <td align="left"><a href="https://github.com/sanity-io/sanity/blob/8c39fdb5b0ddac2e2a85bc5664d80f2e3b68e59b/packages/sanity/src/core/releases/tool/overview/ReleasesOverview.tsx#L89" class="text-red">89</a>, <a href="https://github.com/sanity-io/sanity/blob/8c39fdb5b0ddac2e2a85bc5664d80f2e3b68e59b/packages/sanity/src/core/releases/tool/overview/ReleasesOverview.tsx#L131-L132" class="text-red">131-132</a>, <a href="https://github.com/sanity-io/sanity/blob/8c39fdb5b0ddac2e2a85bc5664d80f2e3b68e59b/packages/sanity/src/core/releases/tool/overview/ReleasesOverview.tsx#L150" class="text-red">150</a>, <a href="https://github.com/sanity-io/sanity/blob/8c39fdb5b0ddac2e2a85bc5664d80f2e3b68e59b/packages/sanity/src/core/releases/tool/overview/ReleasesOverview.tsx#L166" class="text-red">166</a>, <a href="https://github.com/sanity-io/sanity/blob/8c39fdb5b0ddac2e2a85bc5664d80f2e3b68e59b/packages/sanity/src/core/releases/tool/overview/ReleasesOverview.tsx#L235-L239" class="text-red">235-239</a>, <a href="https://github.com/sanity-io/sanity/blob/8c39fdb5b0ddac2e2a85bc5664d80f2e3b68e59b/packages/sanity/src/core/releases/tool/overview/ReleasesOverview.tsx#L272-L283" class="text-red">272-283</a>, <a href="https://github.com/sanity-io/sanity/blob/8c39fdb5b0ddac2e2a85bc5664d80f2e3b68e59b/packages/sanity/src/core/releases/tool/overview/ReleasesOverview.tsx#L290-L294" class="text-red">290-294</a></td> </tr> <tr> <td align="left"><a href="https://github.com/sanity-io/sanity/blob/8c39fdb5b0ddac2e2a85bc5664d80f2e3b68e59b/packages/sanity/src/core/releases/tool/overview/ReleasesOverviewColumnDefs.tsx">packages/sanity/src/core/releases/tool/overview/ReleasesOverviewColumnDefs.tsx</a></td> <td align="right">98.45%</td> <td align="right">95.55%</td> <td align="right">94.44%</td> <td align="right">98.45%</td> <td align="left"><a href="https://github.com/sanity-io/sanity/blob/8c39fdb5b0ddac2e2a85bc5664d80f2e3b68e59b/packages/sanity/src/core/releases/tool/overview/ReleasesOverviewColumnDefs.tsx#L129-L131" class="text-red">129-131</a></td> </tr> <tr> <td align="left"><a href="https://github.com/sanity-io/sanity/blob/8c39fdb5b0ddac2e2a85bc5664d80f2e3b68e59b/packages/sanity/src/core/releases/tool/overview/columnCells/ReleaseName.tsx">packages/sanity/src/core/releases/tool/overview/columnCells/ReleaseName.tsx</a></td> <td align="right">98.93%</td> <td align="right">96.07%</td> <td align="right">100%</td> <td align="right">98.93%</td> <td align="left"><a href="https://github.com/sanity-io/sanity/blob/8c39fdb5b0ddac2e2a85bc5664d80f2e3b68e59b/packages/sanity/src/core/releases/tool/overview/columnCells/ReleaseName.tsx#L63" class="text-red">63</a></td> </tr> </tbody> </table></details>
<em>Generated in workflow <a href=https://github.com/sanity-io/sanity/actions/runs/13996214766>#32656</a> for commit <a href="https://github.com/sanity-io/sanity/commit/8c39fdb5b0ddac2e2a85bc5664d80f2e3b68e59b">8c39fdb</a> by the <a href="https://github.com/davelosert/vitest-coverage-report-action">Vitest Coverage Report Action</a></em>

<!-- vitest-coverage-report-marker-root -->
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-03-19T18:06:26Z
body: No changes to documentation
<!-- thollander/actions-comment-pull-request "docs-report" -->
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-03-19T18:14:13Z
body: ### ⚡️ Editor Performance Report

Updated Fri, 21 Mar 2025 16:32:44 GMT


| Benchmark | reference<br/><sup>latency of `sanity@latest`</sup> | experiment<br/><sup>latency of this branch</sup> | Δ (%)<br/><sup>latency difference</sup> | |
| :-- | :-- | :-- | :-- | --- |
| article (title) | 21.5 efps (47ms) | 17.5 efps (57ms) | +11ms (+22.6%) | 🔴 |
| article (body) | 67.6 efps (15ms) | 67.1 efps (15ms) | +0ms (-/-%) | ✅ |
| article (string inside object) | 22.7 efps (44ms) | 21.5 efps (47ms) | +3ms (+5.7%) | ✅ |
| article (string inside array) | 20.8 efps (48ms) | 19.2 efps (52ms) | +4ms (+8.3%) | ✅ |
| recipe (name) | 45.5 efps (22ms) | 31.3 efps (32ms) | +10ms (+45.5%) | 🔴 |
| recipe (description) | 50.0 efps (20ms) | 33.3 efps (30ms) | +10ms (+50.0%) | 🔴 |
| recipe (instructions) | 99.9+ efps (5ms) | 99.9+ efps (5ms) | +0ms (-/-%) | ✅ |
| synthetic (title) | 18.2 efps (55ms) | 18.5 efps (54ms) | -1ms (-1.8%) | ✅ |
| synthetic (string inside object) | 19.8 efps (51ms) | 18.5 efps (54ms) | +4ms (+6.9%) | ✅ |


> **efps** — editor "frames per second". The number of updates assumed to be possible within a second.
>
> Derived from input latency. `efps = 1000 / input_latency`

<details>

<summary><strong>Detailed information</strong></summary>

### 🏠 Reference result

The performance result of `sanity@latest`



| Benchmark | latency | p75 | p90 | p99 | blocking time | test duration |
| --------- | ------: | --: | --: | --: | ------------: | ------------: |
| article (title) | 47ms | 69ms | 90ms | 394ms | 863ms | 11.2s |
| article (body) | 15ms | 16ms | 18ms | 184ms | 199ms | 5.6s |
| article (string inside object) | 44ms | 45ms | 51ms | 208ms | 333ms | 7.3s |
| article (string inside array) | 48ms | 50ms | 52ms | 55ms | 257ms | 7.3s |
| recipe (name) | 22ms | 24ms | 27ms | 42ms | 10ms | 7.4s |
| recipe (description) | 20ms | 22ms | 23ms | 38ms | 0ms | 4.8s |
| recipe (instructions) | 5ms | 7ms | 8ms | 10ms | 0ms | 3.1s |
| synthetic (title) | 55ms | 60ms | 63ms | 228ms | 1263ms | 13.2s |
| synthetic (string inside object) | 51ms | 52ms | 59ms | 229ms | 666ms | 8.1s |


### 🧪 Experiment result

The performance result of this branch


| Benchmark | latency | p75 | p90 | p99 | blocking time | test duration |
| --------- | ------: | --: | --: | --: | ------------: | ------------: |
| article (title) | 57ms | 89ms | 100ms | 405ms | 1790ms | 13.3s |
| article (body) | 15ms | 18ms | 21ms | 235ms | 389ms | 5.9s |
| article (string inside object) | 47ms | 50ms | 51ms | 54ms | 116ms | 7.3s |
| article (string inside array) | 52ms | 55ms | 58ms | 242ms | 697ms | 8.3s |
| recipe (name) | 32ms | 34ms | 39ms | 50ms | 65ms | 10.2s |
| recipe (description) | 30ms | 31ms | 33ms | 49ms | 65ms | 6.3s |
| recipe (instructions) | 5ms | 7ms | 8ms | 54ms | 3ms | 3.2s |
| synthetic (title) | 54ms | 58ms | 79ms | 404ms | 1529ms | 14.0s |
| synthetic (string inside object) | 54ms | 55ms | 57ms | 379ms | 1177ms | 8.3s |


### 📚 Glossary

> #### column definitions
>
> - **benchmark** — the name of the test, e.g. "article", followed by the label of the field being measured, e.g. "(title)".
> - **latency** — the time between when a key was pressed and when it was rendered. derived from a set of samples. the median (p50) is shown to show the most common latency.
> - **p75** — the 75th percentile of the input latency in the test run. 75% of the sampled inputs in this benchmark were processed faster than this value. this provides insight into the upper range of typical performance.
> - **p90** — the 90th percentile of the input latency in the test run. 90% of the sampled inputs were faster than this. this metric helps identify slower interactions that occurred less frequently during the benchmark.
> - **p99** — the 99th percentile of the input latency in the test run. only 1% of sampled inputs were slower than this. this represents the worst-case scenarios encountered during the benchmark, useful for identifying potential performance outliers.
> - **blocking time** — the total time during which the main thread was blocked, preventing user input and UI updates. this metric helps identify performance bottlenecks that may cause the interface to feel unresponsive.
> - **test duration** — how long the test run took to complete.

</details>

<!-- thollander/actions-comment-pull-request "efps-report" -->
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-03-21T15:27:05Z
body: ### 📊 Playwright Test Report

**[Download Full E2E Report](https://github.com/sanity-io/sanity/actions/runs/13996214760/artifacts/2797388127)**

This report contains test results, including videos of failing tests.
<!-- thollander/actions-comment-pull-request "playwright-e2e-report" -->
author_association: CONTRIBUTOR