Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

run page annotations table #123

Merged
merged 9 commits into from
Nov 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import { BrowseDataTabs } from './BrowseDataTabs'

export function BrowseDataHeader() {
return (
<div className="px-sds-xl py-sds-l flex items-center gap-sds-xl border-b border-gray-300">
<BrowseDataSearch />
<BrowseDataTabs />
<div className="px-sds-xl py-sds-l flex justify-center border-b border-gray-300">
<div className="flex gap-sds-xl w-full max-w-content">
<BrowseDataSearch />
<BrowseDataTabs />
</div>
</div>
)
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
export * from './BrowseDataFilterCount'
export * from './BrowseDataHeader'
export * from './DatasetTable'
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { Button } from '@czi-sds/components'
import clsx from 'clsx'
import { useState } from 'react'

import { DatabaseEntry } from 'app/components/DatabaseEntry'
import { DOI_ID } from 'app/constants/external-dbs'
import { useDatasetById } from 'app/hooks/useDatasetById'
import { i18n } from 'app/i18n'
import { cns, cnsNoMerge } from 'app/utils/cns'

import { DatasetAuthors } from './DatasetAuthors'

// use clsx here instead of cns since it erroneously merges text-sds-gray-500 and text-sds-caps-xxxs
const sectionHeaderStyles = clsx(
const sectionHeaderStyles = cnsNoMerge(
'font-semibold uppercase',
'text-sds-gray-500',
'text-sds-caps-xxxs leading-sds-caps-xxxs tracking-sds-caps',
Expand All @@ -33,10 +33,15 @@ function DatabaseList(props: DatabaseListProps) {
const [collapsed, setCollapsed] = useState(true)

return (
<div className={clsx(className, 'flex flex-col gap-sds-xs')}>
<div className={cns(className, 'flex flex-col gap-sds-xs')}>
<h3 className={sectionHeaderStyles}>{title}</h3>
{entries ? (
<ul className="flex flex-col gap-sds-xxs">
<ul
className={cns(
'flex flex-col gap-sds-xxs',
collapsible && 'transition-[max-height_0.2s_ease-out]',
)}
>
{entries.map(
(e, i) =>
!(collapsible && collapsed && i + 1 > collapseAfter) && (
Expand Down
15 changes: 0 additions & 15 deletions frontend/packages/data-portal/app/components/Dataset/RunCount.tsx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ export function PageHeader({
sdsStyle="rounded"
onClick={onMoreInfoClick}
>
More Info
{i18n.moreInfo}
</Button>
</div>
</div>
Expand Down
172 changes: 172 additions & 0 deletions frontend/packages/data-portal/app/components/Run/AnnotationTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
/* eslint-disable react/no-unstable-nested-components */

import { Button, CellHeader } from '@czi-sds/components'
import { ColumnDef, createColumnHelper } from '@tanstack/react-table'
import { range } from 'lodash-es'
import { useMemo } from 'react'

import { GetRunByIdQuery } from 'app/__generated__/graphql'
import { MAX_PER_PAGE } from 'app/constants/pagination'
import { useIsLoading } from 'app/hooks/useIsLoading'
import { useRunById } from 'app/hooks/useRunById'
import { i18n } from 'app/i18n'
import { useDrawer } from 'app/state/drawer'
import { cnsNoMerge } from 'app/utils/cns'

import { Table, TableCell } from '../Table'

type Annotation =
GetRunByIdQuery['runs'][number]['annotation_table'][number]['annotations'][number]

const LOADING_ANNOTATIONS = range(0, MAX_PER_PAGE).map(() => ({}) as Annotation)

function ConfidenceValue({ value }: { value: number }) {
return (
<div className="flex flex-col gap-sds-xxxs">
<p className="text-sds-header-s leading-sds-header-s">{value}%</p>
<p className="text-sds-body-xxs leading-sds-body-xxs text-sds-gray-600">
{i18n.confidence}
</p>
</div>
)
}

export function AnnotationTable() {
const { isLoadingDebounced } = useIsLoading()
const { run } = useRunById()
const drawer = useDrawer()

const columns = useMemo(() => {
const columnHelper = createColumnHelper<Annotation>()

function getConfidenceCell(key: keyof Annotation, header: string) {
return columnHelper.accessor(key, {
header: () => <CellHeader horizontalAlign="right">{header}</CellHeader>,
cell: ({ getValue }) => {
const value = getValue() as number | null

return (
<TableCell horizontalAlign="right" minWidth={85} maxWidth={120}>
{typeof value === 'number' ? (
<ConfidenceValue value={value} />
) : (
<p className="text-sds-body-xs leading-sds-body-xs">
{i18n.na}
</p>
)}
</TableCell>
)
},
})
}

return [
columnHelper.accessor('s3_annotations_path', {
header: i18n.annotations,
cell: ({ row: { original: annotation } }) => (
<TableCell
className="flex flex-col !items-start"
minWidth={250}
renderLoadingSkeleton={false}
>
<div className="flex gap-sds-xs">
<p className="text-sds-header-s leading-sds-header-s">
{annotation.s3_annotations_path?.split('/').at(-1) ?? '--'}
</p>

{annotation.ground_truth_status && (
<div
className={cnsNoMerge(
'px-sds-xs py-sds-xxxs',
'flex items-center justify-center',
'rounded-sds-m bg-sds-info-400',
'text-sds-body-xxxs leading-sds-body-xxxs text-sds-gray-white whitespace-nowrap',
)}
>
{i18n.groundTruth}
</div>
)}
</div>

<ul className="list-none flex gap-1 text-sds-gray-600 text-sds-body-xxs leading-sds-header-xxs">
{annotation.authors.map((author, idx) => {
const authorLength = annotation.authors.length
const totalAuthorCount =
annotation.authors_aggregate.aggregate?.count ?? 0

return (
<li className="flex items-center" key={author.name}>
<span>{author.name}</span>
<span>{idx < authorLength - 1 && ', '}</span>

{idx === authorLength - 1 && idx < totalAuthorCount - 1 && (
<Button
sdsType="primary"
sdsStyle="minimal"
onClick={drawer.toggle}
>
{i18n.plusMore(totalAuthorCount - authorLength)}
</Button>
)}
</li>
)
})}
</ul>
</TableCell>
),
}),

columnHelper.accessor('object_name', {
header: i18n.annotationObject,
cell: ({ getValue }) => (
<TableCell minWidth={120} maxWidth={250}>
{getValue()}
</TableCell>
),
}),

columnHelper.accessor('object_count', {
header: () => (
<CellHeader horizontalAlign="right">{i18n.objectCount}</CellHeader>
),
cell: ({ getValue }) => (
<TableCell horizontalAlign="right" minWidth={100} maxWidth={120}>
{getValue()}
</TableCell>
),
}),

getConfidenceCell('confidence_precision', i18n.precision),
getConfidenceCell('confidence_recall', i18n.recall),

columnHelper.display({
id: 'annotation-actions',
// Render empty cell header so that it doesn't break the table layout
header: () => <CellHeader>{null}</CellHeader>,
cell: () => (
<TableCell minWidth={85} maxWidth={120}>
<Button
sdsType="primary"
sdsStyle="minimal"
onClick={drawer.toggle}
>
{i18n.moreInfo}
</Button>
</TableCell>
),
}),
] as ColumnDef<Annotation>[]
}, [drawer.toggle])

const annotations = run.annotation_table.flatMap(
(data) => data.annotations,
) as unknown as Annotation[]

return (
<Table
data={isLoadingDebounced ? LOADING_ANNOTATIONS : annotations}
columns={columns}
withFiltersSidebar
/>
)
}
14 changes: 13 additions & 1 deletion frontend/packages/data-portal/app/components/Table/TableCell.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
import { CellBasic, CellComponent } from '@czi-sds/components'
import Skeleton from '@mui/material/Skeleton'
import { ReactNode } from 'react'
import { match } from 'ts-pattern'

import { useIsLoading } from 'app/hooks/useIsLoading'
import { cns } from 'app/utils/cns'

export function TableCell({
children,
className,
horizontalAlign,
maxWidth,
minWidth,
primaryText,
renderLoadingSkeleton = () => <Skeleton variant="text" />,
}: {
children?: ReactNode
className?: string
horizontalAlign?: 'left' | 'center' | 'right'
loadingSkeleton?: boolean
maxWidth?: number
minWidth?: number
Expand All @@ -22,7 +26,15 @@ export function TableCell({
}) {
const { isLoadingDebounced } = useIsLoading()
const cellProps = {
className,
className: cns(
match(horizontalAlign)
.with('left', () => '!text-left')
.with('center', () => '!text-center')
.with('right', () => '!text-right !pr-8')
.otherwise(() => ''),

className,
),
style: {
maxWidth,
minWidth,
Expand Down
17 changes: 17 additions & 0 deletions frontend/packages/data-portal/app/components/Table/TableCount.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { i18n } from 'app/i18n'

export function TableCount({
filteredCount,
totalCount,
type,
}: {
filteredCount: number
totalCount: number
type: string
}) {
return (
<p className="text-sds-body-xs text-sds-gray-500 whitespace-nowrap">
{i18n.filterCount(filteredCount, totalCount, type)}
</p>
)
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './MetadataTable'
export * from './Table'
export * from './TableCell'
export * from './TableCount'
Loading