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

feat: universal empty states #1295

Merged
merged 3 commits into from
Nov 5, 2024
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
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { InputSearch } from '@czi-sds/components'
import { useDebouncedEffect } from '@react-hookz/web'
import { useSearchParams } from '@remix-run/react'
import { useRef, useState } from 'react'
import { useAtom } from 'jotai'
import { useHydrateAtoms } from 'jotai/utils'
import { useEffect, useRef } from 'react'

import { QueryParams } from 'app/constants/query'
import { i18n } from 'app/i18n'
import { searchQueryAtom } from 'app/state/search'

/**
* The amount of time to wait after the user has typed in a query before
Expand All @@ -15,7 +18,14 @@ const SEARCH_QUERY_DEBOUNCE_TIME_MS = 500

export function BrowseDataSearch() {
const [searchParams, setSearchParams] = useSearchParams()
const [query, setQuery] = useState(searchParams.get(QueryParams.Search) ?? '')
const [query, setQuery] = useAtom(searchQueryAtom)

useHydrateAtoms([
[searchQueryAtom, searchParams.get(QueryParams.Search) ?? ''],
])

// Reset when navigating away
useEffect(() => () => setQuery(''), [setQuery])

// If the user hasn't typed in a key for 500ms, then update the search params.
const initialLoadRef = useRef(true)
Expand Down
46 changes: 35 additions & 11 deletions frontend/packages/data-portal/app/components/NoFilteredResults.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { ReactNode } from 'react'
import { Button } from '@czi-sds/components'
import { useSetAtom } from 'jotai'

import { useFilter } from 'app/hooks/useFilter'
import { useI18n } from 'app/hooks/useI18n'
import { searchQueryAtom } from 'app/state/search'

function NoResultsImage() {
return (
Expand All @@ -20,25 +25,44 @@ function NoResultsImage() {
}

export function NoFilteredResults({
actions,
description,
title,
showSearchTip,
}: {
actions?: ReactNode
description: string
title: string
showSearchTip?: boolean
}) {
const setSearchQuery = useSetAtom(searchQueryAtom)
const { reset } = useFilter()
const { t } = useI18n()

return (
<div className="flex items-center justify-center h-full">
<div className="flex gap-sds-xxl">
<div className="flex flex-col gap-sds-l">
<div className="flex flex-col max-w-[324px]">
<p className="font-semibold text-sds-header-l leading-sds-header-l">
{title}
{t('noResultsFound')}
</p>

<p className="text-sds-body-s leading-sds-body-s">{description}</p>
<div className="mt-3.5 text-sds-body-s leading-sds-body-s">
<p>{t('noResultsBeforeYouTryAgain')}</p>

<ul className="list-disc pl-sds-xxl">
{showSearchTip && <li>{t('noResultsSearch')}</li>}
<li>{t('noResultsCorrectSpelling')}</li>
<li>{t('noResultsRemoveFilters')}</li>
</ul>
</div>

<div className="flex items-center gap-sds-xxs">{actions}</div>
<div className="mt-3.5">
<Button
onClick={() => {
reset()
setSearchQuery('')
}}
sdsType="primary"
sdsStyle="minimal"
>
{t('resetAll')}
</Button>
</div>
</div>

<NoResultsImage />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,9 @@ function TablePageTabContent({
</ErrorBoundary>

<div className="px-sds-xl">
{filteredCount === 0 && noFilteredResults}
{filteredCount === 0 && (
<div className="mt-[100px]">{noFilteredResults}</div>
)}

{filteredCount > MAX_PER_PAGE && (
<div
Expand Down
2 changes: 0 additions & 2 deletions frontend/packages/data-portal/app/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ export const i18n = {
cellName: 'Cell Name',
cellularComponent: 'Cellular Component',
citations: 'Citations',
clearFilters: 'Clear Filters',
confidence: 'confidence',
contributeCta:
'We encourage you to share datasets and/or annotations to existing data. Click below to fill out the inquiry form.',
Expand Down Expand Up @@ -79,7 +78,6 @@ export const i18n = {
filterByAnyOfTheFollowing: 'Filter by any of the following',
filterCount: (count: number, max: number, type: string) =>
`${count} of ${max} ${type}`,
filterNoResultsFound: 'No results were found',
filterRange: 'Filter Range',
filterTooRestrictive: 'The applied filters may be too restrictive.',
frames: 'Frames',
Expand Down
12 changes: 2 additions & 10 deletions frontend/packages/data-portal/app/routes/browse-data.datasets.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Button, CellHeaderDirection } from '@czi-sds/components'
import { CellHeaderDirection } from '@czi-sds/components'
import { json, LoaderFunctionArgs } from '@remix-run/node'

import { Order_By } from 'app/__generated__/graphql'
Expand All @@ -12,7 +12,6 @@ import { QueryParams } from 'app/constants/query'
import { getBrowseDatasets } from 'app/graphql/getBrowseDatasets.server'
import { getDatasetsFilterData } from 'app/graphql/getDatasetsFilterData.server'
import { useDatasets } from 'app/hooks/useDatasets'
import { useFilter } from 'app/hooks/useFilter'
import { useI18n } from 'app/hooks/useI18n'
import {
useBrowseDatasetFilterHistory,
Expand Down Expand Up @@ -52,7 +51,6 @@ export async function loader({ request }: LoaderFunctionArgs) {

export default function BrowseDatasetsPage() {
const { datasetCount, filteredDatasetCount } = useDatasets()
const { reset } = useFilter()
const { t } = useI18n()

const { setPreviousBrowseDatasetParams } = useBrowseDatasetFilterHistory()
Expand All @@ -72,13 +70,7 @@ export default function BrowseDatasetsPage() {
'https://chanzuckerberg.github.io/cryoet-data-portal/cryoet_data_portal_docsite_data.html#datasets',
filterPanel: <DatasetFilter />,
table: <DatasetTable />,
noFilteredResults: (
<NoFilteredResults
title={t('filterNoResultsFound')}
description={t('filterTooRestrictive')}
actions={<Button onClick={reset}>{t('clearFilters')}</Button>}
/>
),
noFilteredResults: <NoFilteredResults showSearchTip />,
filteredCount: filteredDatasetCount,
totalCount: datasetCount,
countLabel: t('datasets'),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { Button, CellHeaderDirection } from '@czi-sds/components'
import { CellHeaderDirection } from '@czi-sds/components'
import { json, LoaderFunctionArgs, redirect } from '@remix-run/node'

import { Order_By } from 'app/__generated__/graphql'
import { apolloClient } from 'app/apollo.server'
import { DepositionTable } from 'app/components/BrowseData/DepositionTable'
import { NoFilteredResults } from 'app/components/NoFilteredResults'
import { TablePageLayout } from 'app/components/TablePageLayout'
import { QueryParams } from 'app/constants/query'
import { getBrowseDepositions } from 'app/graphql/getBrowseDepositions.server'
import { useDepositions } from 'app/hooks/useDepositions'
import { useFilter } from 'app/hooks/useFilter'
import { useI18n } from 'app/hooks/useI18n'
import { getFeatureFlag } from 'app/utils/featureFlags'

Expand Down Expand Up @@ -51,7 +49,6 @@ export async function loader({ request }: LoaderFunctionArgs) {

export default function BrowseDepositionsPage() {
const { depositionCount, filteredDepositionCount } = useDepositions()
const { reset } = useFilter()
const { t } = useI18n()

return (
Expand All @@ -63,13 +60,6 @@ export default function BrowseDepositionsPage() {
learnMoreLink:
'https://chanzuckerberg.github.io/cryoet-data-portal/cryoet_data_portal_docsite_data.html#depositions',
table: <DepositionTable />,
noFilteredResults: (
<NoFilteredResults
title={t('filterNoResultsFound')}
description={t('filterTooRestrictive')}
actions={<Button onClick={reset}>{t('clearFilters')}</Button>}
/>
),
filteredCount: filteredDepositionCount,
totalCount: depositionCount,
countLabel: t('depositions'),
Expand Down
2 changes: 2 additions & 0 deletions frontend/packages/data-portal/app/routes/datasets.$id.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { DatasetHeader } from 'app/components/Dataset/DatasetHeader'
import { RunsTable } from 'app/components/Dataset/RunsTable'
import { DepositionFilterBanner } from 'app/components/DepositionFilterBanner'
import { DownloadModal } from 'app/components/Download'
import { NoFilteredResults } from 'app/components/NoFilteredResults'
import { RunFilter } from 'app/components/RunFilter'
import { TablePageLayout } from 'app/components/TablePageLayout'
import { RUN_FILTERS } from 'app/constants/filterQueryParams'
Expand Down Expand Up @@ -86,6 +87,7 @@ export default function DatasetByIdPage() {
filteredCount: dataset.filtered_runs_count.aggregate?.count ?? 0,
totalCount: dataset.runs_aggregate.aggregate?.count ?? 0,
countLabel: i18n.runs,
noFilteredResults: <NoFilteredResults />,
},
]}
downloadModal={
Expand Down
2 changes: 2 additions & 0 deletions frontend/packages/data-portal/app/routes/depositions.$id.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { DatasetFilter } from 'app/components/DatasetFilter'
import { DepositionMetadataDrawer } from 'app/components/Deposition'
import { DatasetsTable } from 'app/components/Deposition/DatasetsTable'
import { DepositionHeader } from 'app/components/Deposition/DepositionHeader'
import { NoFilteredResults } from 'app/components/NoFilteredResults'
import { TablePageLayout } from 'app/components/TablePageLayout'
import { DEPOSITION_FILTERS } from 'app/constants/filterQueryParams'
import { QueryParams } from 'app/constants/query'
Expand Down Expand Up @@ -164,6 +165,7 @@ export default function DepositionByIdPage() {
filteredCount: filteredDatasetsCount,
filterPanel: <DatasetFilter depositionPageVariant />,
countLabel: t('datasets'),
noFilteredResults: <NoFilteredResults />,
},
]}
drawers={<DepositionMetadataDrawer />}
Expand Down
2 changes: 2 additions & 0 deletions frontend/packages/data-portal/app/routes/runs.$id.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { apolloClient, apolloClientV2 } from 'app/apollo.server'
import { AnnotationFilter } from 'app/components/AnnotationFilter/AnnotationFilter'
import { DepositionFilterBanner } from 'app/components/DepositionFilterBanner'
import { DownloadModal } from 'app/components/Download'
import { NoFilteredResults } from 'app/components/NoFilteredResults'
import { NoTotalResults } from 'app/components/NoTotalResults'
import { RunHeader } from 'app/components/Run'
import { AnnotationDrawer } from 'app/components/Run/AnnotationDrawer'
Expand Down Expand Up @@ -211,6 +212,7 @@ export default function RunByIdPage() {
]}
/>
),
noFilteredResults: <NoFilteredResults />,
},
...(multipleTomogramsEnabled
? [
Expand Down
3 changes: 3 additions & 0 deletions frontend/packages/data-portal/app/state/search.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { atom } from 'jotai'

export const searchQueryAtom = atom('')
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@
"cellLineOrStrainName": "Cell Line or Strain Name",
"cellName": "Cell Name",
"cellularComponent": "Cellular Component",
"clearFilters": "Clear Filters",
"clickToDownloadViaBrowser": "Click to download via your browser",
"close": "Close",
"comingFall2024": "Coming in Fall 2024",
Expand Down Expand Up @@ -173,7 +172,6 @@
"filterByDepositionId": "Filter by Deposition ID",
"filterByObjectId": "Filter by Object ID",
"filterCountOfMaxType": "{{count}} of {{max}} {{type}}",
"filterNoResultsFound": "No results were found",
"filterRange": "Filter Range",
"filterTooRestrictive": "The applied filters may be too restrictive.",
"frames": "Frames",
Expand Down Expand Up @@ -248,6 +246,11 @@
"no": "No",
"noAnnotationsAvailable": "No Annotations Available",
"noAnnotationsAvailableToDownload": "No annotations available to download",
"noResultsBeforeYouTryAgain": "Before you try again, here are some tips:",
"noResultsCorrectSpelling": "Ensure your spelling is correct.",
"noResultsFound": "No results found",
"noResultsRemoveFilters": "Consider removing or adjusting filters for broader results.",
"noResultsSearch": "Search only looks for words in dataset names.",
"noTomogramAvailable": "No tomogram available",
"noTomogramsAvailable": "No tomograms available",
"noTomogramsAvailableToDownload": "No tomograms available to download",
Expand Down Expand Up @@ -318,6 +321,7 @@
"relatedEmpiarEntry": "Related EMPIAR Entry",
"releaseDate": "Release Date",
"reportIssueOnGithub": "Report Issue on GitHub",
"resetAll": "Reset All",
"resolutionsAvailable": "Samplings Available",
"resultsMustIncludeAllFileTypes": "Results must include all selected file types",
"returnToDeposition": "Return To Deposition",
Expand Down
Loading