Skip to content

Commit

Permalink
feat: deposition filter banner (#1040)
Browse files Browse the repository at this point in the history
#925

- deposition ID query parameter: `deposition-id`
- prop `banner?: ReactNode` to render banner at the top of
`<TablePageLayout />`
- implements deposition banner component and renders at top of dataset
or run pages

## Demos

https://dev-depo-filter-banner.cryoet.dev.si.czi.technology/

### Dataset Page

<img width="1728" alt="image"
src="https://github.com/user-attachments/assets/f5ab5f2f-fb44-4cbd-a67a-5a9033937eee">

### Run Page

<img width="1728" alt="image"
src="https://github.com/user-attachments/assets/c6707c90-bc51-4b31-a2a1-711707bd65e8">
  • Loading branch information
codemonkey800 authored Aug 29, 2024
1 parent 427c97d commit bb83312
Show file tree
Hide file tree
Showing 21 changed files with 327 additions and 34 deletions.
6 changes: 3 additions & 3 deletions frontend/packages/data-portal/app/components/Breadcrumbs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ export function Breadcrumbs({

const { browseDatasetHistory } = useBrowseDatasetFilterHistory()
const { singleDatasetHistory } = useSingleDatasetFilterHistory()
const { previousDepositionId } = useDepositionHistory()
const { previousDepositionId, previousSingleDepositionParams } =
useDepositionHistory()

const browseAllLink = useMemo(() => {
const url =
Expand Down Expand Up @@ -83,8 +84,7 @@ export function Breadcrumbs({
{previousDepositionId != null && variant !== 'deposition' && (
<Link
className="uppercase font-semibold text-sds-caps-xxxs leading-sds-caps-xxxs text-sds-primary-400"
// TODO add filter carry back behavior
to={`/depositions/${previousDepositionId}`}
to={`/depositions/${previousDepositionId}?${previousSingleDepositionParams}`}
>
{t('returnToDeposition')}
</Link>
Expand Down
36 changes: 29 additions & 7 deletions frontend/packages/data-portal/app/components/Dataset/RunsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Skeleton from '@mui/material/Skeleton'
import { useNavigate, useSearchParams } from '@remix-run/react'
import { ColumnDef, createColumnHelper } from '@tanstack/react-table'
import { range } from 'lodash-es'
import { useEffect, useMemo, useState } from 'react'
import { useCallback, useEffect, useMemo, useState } from 'react'

import { GetDatasetByIdQuery } from 'app/__generated__/graphql'
import { AnnotatedObjectsList } from 'app/components/AnnotatedObjectsList'
Expand All @@ -16,6 +16,7 @@ import { TiltSeriesQualityScoreBadge } from 'app/components/TiltSeriesQualitySco
import { ViewTomogramButton } from 'app/components/ViewTomogramButton'
import { RUN_FILTERS } from 'app/constants/filterQueryParams'
import { MAX_PER_PAGE } from 'app/constants/pagination'
import { QueryParams } from 'app/constants/query'
import { RunTableWidths } from 'app/constants/table'
import { TiltSeriesScore } from 'app/constants/tiltSeries'
import { useDatasetById } from 'app/hooks/useDatasetById'
Expand All @@ -27,6 +28,7 @@ import {
} from 'app/state/filterHistory'
import { cnsNoMerge } from 'app/utils/cns'
import { inQualityScoreRange } from 'app/utils/tiltSeries'
import { carryOverFilterParams, createUrl } from 'app/utils/url'

type Run = GetDatasetByIdQuery['datasets'][number]['runs'][number]

Expand All @@ -39,7 +41,7 @@ const LOADING_RUNS = range(0, MAX_PER_PAGE).map<Run>(() => ({

export function RunsTable() {
const { isLoadingDebounced } = useIsLoading()
const { dataset } = useDatasetById()
const { dataset, deposition } = useDatasetById()
const runs = dataset.runs as unknown as Run[]
const { t } = useI18n()
const { setSingleDatasetHistory } = useSingleDatasetFilterHistory()
Expand All @@ -61,6 +63,25 @@ export function RunsTable() {
[searchParams, setSingleDatasetHistory],
)

const getRunUrl = useCallback(
(id: number) => {
const url = createUrl(`/runs/${id}`)

carryOverFilterParams({
filters: RUN_FILTERS,
params: url.searchParams,
prevParams: searchParams,
})

if (deposition && searchParams.has(QueryParams.DepositionId)) {
url.searchParams.set(QueryParams.DepositionId, `${deposition.id}`)
}

return url.pathname + url.search
},
[deposition, searchParams],
)

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

Expand Down Expand Up @@ -106,7 +127,7 @@ export function RunsTable() {
</CellHeader>
),
cell({ row: { original: run } }) {
const runUrl = `/runs/${run.id}`
const runUrl = getRunUrl(run.id)

return (
<TableCell
Expand Down Expand Up @@ -248,19 +269,20 @@ export function RunsTable() {
),
] as ColumnDef<Run>[]
}, [
dataset.id,
dataset.organism_name,
isLoadingDebounced,
t,
isHoveringOverInteractable,
t,
getRunUrl,
dataset.id,
dataset.organism_name,
])

return (
<PageTable
data={isLoadingDebounced ? LOADING_RUNS : runs}
columns={columns}
onTableRowClick={(row) =>
!isHoveringOverInteractable && navigate(`/runs/${row.original.id}`)
!isHoveringOverInteractable && navigate(getRunUrl(row.original.id))
}
hoverType="group"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@ import Skeleton from '@mui/material/Skeleton'
import { useSearchParams } from '@remix-run/react'
import { ColumnDef, createColumnHelper } from '@tanstack/react-table'
import { range, sum } from 'lodash-es'
import { useMemo } from 'react'
import { useCallback, useMemo } from 'react'

import { AnnotatedObjectsList } from 'app/components/AnnotatedObjectsList'
import { AuthorList } from 'app/components/AuthorList'
import { I18n } from 'app/components/I18n'
import { KeyPhoto } from 'app/components/KeyPhoto'
import { Link } from 'app/components/Link'
import { CellHeader, PageTable, TableCell } from 'app/components/Table'
import { DATASET_FILTERS } from 'app/constants/filterQueryParams'
import { ANNOTATED_OBJECTS_MAX, MAX_PER_PAGE } from 'app/constants/pagination'
import { QueryParams } from 'app/constants/query'
import { DepositionPageDatasetTableWidths } from 'app/constants/table'
import { Dataset, useDepositionById } from 'app/hooks/useDepositionById'
import { useI18n } from 'app/hooks/useI18n'
Expand All @@ -22,6 +24,7 @@ import { LogLevel } from 'app/types/logging'
import { cnsNoMerge } from 'app/utils/cns'
import { sendLogs } from 'app/utils/logging'
import { getErrorMessage } from 'app/utils/string'
import { carryOverFilterParams, createUrl } from 'app/utils/url'

const LOADING_DATASETS = range(0, MAX_PER_PAGE).map(
(value) =>
Expand All @@ -45,6 +48,23 @@ export function DatasetsTable() {

const { isLoadingDebounced } = useIsLoading()

const getDatasetUrl = useCallback(
(id: number) => {
const url = createUrl(`/datasets/${id}`)

carryOverFilterParams({
filters: DATASET_FILTERS,
params: url.searchParams,
prevParams: searchParams,
})

url.searchParams.set(QueryParams.DepositionId, `${id}`)

return url.pathname + url.search
},
[searchParams],
)

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

Expand All @@ -54,7 +74,7 @@ export function DatasetsTable() {
header: () => <p />,

cell({ row: { original: dataset } }) {
const datasetUrl = `/datasets/${dataset.id}`
const datasetUrl = getDatasetUrl(dataset.id)

return (
<TableCell
Expand Down Expand Up @@ -103,7 +123,7 @@ export function DatasetsTable() {
),

cell({ row: { original: dataset } }) {
const datasetUrl = `/datasets/${dataset.id}`
const datasetUrl = getDatasetUrl(dataset.id)

return (
<TableCell
Expand Down Expand Up @@ -295,7 +315,14 @@ export function DatasetsTable() {

throw err
}
}, [datasetSort, isLoadingDebounced, searchParams, setSearchParams, t])
}, [
datasetSort,
getDatasetUrl,
isLoadingDebounced,
searchParams,
setSearchParams,
t,
])

return (
<PageTable
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { Button, Callout } from '@czi-sds/components'

import { I18n } from 'app/components/I18n'
import { QueryParams } from 'app/constants/query'
import { useQueryParam } from 'app/hooks/useQueryParam'
import {
useDepositionHistory,
useSingleDatasetFilterHistory,
} from 'app/state/filterHistory'
import { I18nKeys } from 'app/types/i18n'

interface Deposition {
id: number
title: string
}

export function DepositionFilterBanner({
deposition,
labelI18n,
}: {
deposition: Deposition
labelI18n: I18nKeys
}) {
const { singleDatasetHistory, setSingleDatasetHistory } =
useSingleDatasetFilterHistory()
const { previousSingleDepositionParams } = useDepositionHistory()
const [, setDepositionId] = useQueryParam<string>(QueryParams.DepositionId)

return (
<Callout
className="!w-full !mx-sds-xl"
classes={{ message: 'w-full' }}
intent="info"
>
<div className="flex w-full items-center justify-between">
<p className="text-sds-body-xs leading-sds-body-xs">
<I18n
i18nKey={labelI18n}
values={{
...deposition,
url: `/depositions/${deposition.id}${previousSingleDepositionParams}`,
}}
tOptions={{ interpolation: { escapeValue: false } }}
/>
</p>

<Button
onClick={() => {
setDepositionId(null)

const nextHistory = new Map(singleDatasetHistory)
nextHistory.delete(QueryParams.DepositionId)
setSingleDatasetHistory(nextHistory)
}}
sdsStyle="minimal"
sdsType="secondary"
>
Remove Filter
</Button>
</div>
</Callout>
)
}
10 changes: 4 additions & 6 deletions frontend/packages/data-portal/app/components/Link/Link.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { Link as RemixLink, LinkProps } from '@remix-run/react'
import { ForwardedRef, forwardRef } from 'react'

import {
DASHED_BORDERED_CLASSES,
DASHED_UNDERLINED_CLASSES,
} from 'app/utils/classNames'
import { cnsNoMerge } from 'app/utils/cns'
import { isExternalUrl } from 'app/utils/url'

export const DASHED_BORDERED_CLASSES =
'border-b border-dashed hover:border-solid border-current'

export const DASHED_UNDERLINED_CLASSES =
'underline underline-offset-[3px] decoration-dashed hover:decoration-solid'

export type VariantLinkProps = LinkProps & {
variant?: 'dashed-bordered' | 'dashed-underlined'
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import { ComponentProps, ReactNode, useCallback, useMemo } from 'react'

import { AuthorList } from 'app/components/AuthorList'
import { I18n } from 'app/components/I18n'
import { DASHED_BORDERED_CLASSES } from 'app/components/Link'
import { CellHeader, PageTable, TableCell } from 'app/components/Table'
import { Tooltip } from 'app/components/Tooltip'
import {
Expand All @@ -35,6 +34,7 @@ import {
import { useRunById } from 'app/hooks/useRunById'
import { AnnotationRow, useAnnotation } from 'app/state/annotation'
import { I18nKeys } from 'app/types/i18n'
import { DASHED_BORDERED_CLASSES } from 'app/utils/classNames'
import { cns, cnsNoMerge } from 'app/utils/cns'

const LOADING_ANNOTATIONS = range(0, MAX_PER_PAGE).map<AnnotationRow>(() => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { TableCount } from './Table/TableCount'
import { Tabs } from './Tabs'

export interface TablePageLayoutProps {
banner?: ReactNode
header?: ReactNode

tabs: TableLayoutTab[] // If there is only 1 tab, the tab selector will not show.
Expand All @@ -26,6 +27,7 @@ export interface TablePageLayoutProps {
export interface TableLayoutTab {
title: string

banner?: ReactNode
filterPanel?: ReactNode

table: ReactNode
Expand All @@ -44,6 +46,7 @@ export function TablePageLayout({
tabsTitle,
downloadModal,
drawers,
banner,
}: TablePageLayoutProps) {
const [searchParams, setSearchParams] = useSearchParams()

Expand Down Expand Up @@ -87,7 +90,7 @@ export function TablePageLayout({
</div>
)}

<TablePageTabContent {...activeTab} />
<TablePageTabContent banner={banner} {...activeTab} />

{drawers}
</div>
Expand All @@ -105,6 +108,7 @@ function TablePageTabContent({
pageQueryParamKey = QueryParams.Page,
totalCount,
countLabel,
banner,
}: TableLayoutTab) {
const [searchParams, setSearchParams] = useSearchParams()
const pageQueryParamValue = +(searchParams.get(pageQueryParamKey) ?? '1')
Expand Down Expand Up @@ -165,6 +169,8 @@ function TablePageTabContent({
filterPanel && 'screen-2040:translate-x-[-100px] max-w-content',
)}
>
<div className="flex mb-sds-xl">{banner}</div>

<div className="px-sds-xl flex items-center gap-x-sds-xl">
<p className="text-sds-header-l leading-sds-header-l font-semibold">
{title}
Expand Down
17 changes: 14 additions & 3 deletions frontend/packages/data-portal/app/constants/filterQueryParams.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { QueryParams } from './query'

export const DATASET_FILTERS = [
const COMMON_DATASET_FILTERS = [
QueryParams.GroundTruthAnnotation,
QueryParams.AvailableFiles,
QueryParams.NumberOfRuns,
QueryParams.DatasetId,
QueryParams.EmpiarId,
QueryParams.EmdbId,
QueryParams.AuthorName,
QueryParams.AuthorOrcid,
QueryParams.Organism,
Expand All @@ -20,13 +18,21 @@ export const DATASET_FILTERS = [
QueryParams.ObjectShapeType,
] as const

export const DATASET_FILTERS = [
...COMMON_DATASET_FILTERS,
QueryParams.EmpiarId,
QueryParams.EmdbId,
QueryParams.DepositionId,
] as const

export const RUN_FILTERS = [
QueryParams.GroundTruthAnnotation,
QueryParams.QualityScore,
QueryParams.TiltRangeMin,
QueryParams.TiltRangeMax,
QueryParams.ObjectName,
QueryParams.ObjectShapeType,
QueryParams.DepositionId,
] as const

export const ANNOTATION_FILTERS = [
Expand All @@ -38,3 +44,8 @@ export const ANNOTATION_FILTERS = [
QueryParams.MethodType,
QueryParams.AnnotationSoftware,
] as const

export const DEPOSITION_FILTERS = [
...COMMON_DATASET_FILTERS,
QueryParams.ObjectId,
] as const
Loading

0 comments on commit bb83312

Please sign in to comment.