Skip to content

Commit

Permalink
Add autocomplete
Browse files Browse the repository at this point in the history
Fixes #18
  • Loading branch information
u8sand committed Dec 6, 2023
1 parent 1129f33 commit 46860c6
Show file tree
Hide file tree
Showing 15 changed files with 126 additions and 16 deletions.
2 changes: 1 addition & 1 deletion drc-portals/app/data/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export default async function Home({ searchParams }: { searchParams: { error?: s
<Stack spacing={2} justifyContent={"center"} alignItems={"center"}>
<Typography color="secondary" className="text-center" variant="h1">CFDE DATA PORTAL</Typography>
<Typography color="secondary" className="text-center" sx={{fontSize: 20}} variant="body1">Search Common Fund Programs' Metadata and Processed Datasets.</Typography>
<SearchField q="" error={searchParams.error} width={'544px'}/>
<SearchField q="" error={searchParams.error} autocomplete={{}} width={'544px'} />
<Typography variant="stats_sub">
Try <Stack display="inline-flex" flexDirection="row" divider={<span>,&nbsp;</span>}>
{['MCF7', 'STAT3', 'blood', 'dexamethasone'].map(example => (
Expand Down
68 changes: 55 additions & 13 deletions drc-portals/app/data/processed/SearchField.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,64 @@
'use client'
import React from 'react'
import { mdiMagnify } from "@mdi/js";
import Icon from '@mdi/react';
import { InputAdornment, TextField } from '@mui/material';
import { Autocomplete, InputAdornment, TextField } from '@mui/material';
import trpc from '@/lib/trpc/client'
import parse from 'autosuggest-highlight/parse';
import match from 'autosuggest-highlight/match';
import { type_to_string } from './utils';
import { NodeType } from '@prisma/client';

export default function SearchField({ q, width, placeholder = 'Search', error }: { q: string, width?: number | string, placeholder?: string, error?: string }) {
export default function SearchField({ q, width = '275px', placeholder = 'Search', autocomplete, error }: { q: string, width?: number | string, placeholder?: string, autocomplete?: { type?: NodeType, entity_type?: string }, error?: string }) {
const [search, setSearch] = React.useState(q)
const { data: options } = trpc.autocomplete.useQuery({
q: search.toLocaleLowerCase(),
type: autocomplete?.type,
entity_type: autocomplete?.entity_type,
}, { keepPreviousData: true, enabled: autocomplete !== undefined && search.length >= 3 })
const filteredOptions = React.useMemo(() => (options ?? []).filter(option => option.label.toLowerCase().includes(search.toLowerCase())), [search, options])
return (
<>
<TextField
label={error ? error.split(':')[0] : undefined}
error={!!error}
helperText={error ? error.split(':').slice(1).join(':') : undefined}
name="q"
defaultValue={q}
placeholder={placeholder}
color="secondary"
InputProps={{
sx: {borderRadius: 1, height: 50, width, fieldset: { borderColor: "#336699" }},
endAdornment: <InputAdornment position="end"><Icon path={mdiMagnify} size={1} /></InputAdornment>
<Autocomplete
freeSolo
options={filteredOptions}
renderOption={(props, option, { inputValue }) => {
const matches = match(option.label, inputValue, { insideWords: true })
const parts = parse(option.label, matches)
return (
<li {...props}>
{parts.map((part, index) => (
<span
key={index}
style={{
fontWeight: part.highlight ? 700 : 400,
}}
>
{part.text}
</span>
))}
&nbsp;({type_to_string(option.type, option.entity_type)})
</li>
)
}}
renderInput={params =>
<TextField
{...params}
name="q"
color="secondary"
label={error ? error.split(':')[0] : undefined}
error={!!error}
placeholder={placeholder}
value={search}
onChange={evt => {setSearch(() => evt.target.value)}}
helperText={error ? error.split(':').slice(1).join(':') : undefined}
InputProps={{
...params.InputProps,
sx: {borderRadius: 1, height: 50, width, fieldset: { borderColor: "#336699" }},
endAdornment: <InputAdornment position="end"><Icon path={mdiMagnify} size={1} /></InputAdornment>
}}
/>
}
/>
<input className="hidden" type="submit" />
</>
Expand Down
3 changes: 2 additions & 1 deletion drc-portals/app/data/processed/SearchablePagedTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export function SearchablePagedTableCellIcon(props: {
export default function SearchablePagedTable(props: React.PropsWithChildren<{
label?: string,
q: string, p: number, r: number, count?: number,
autocomplete?: { type?: NodeType, entity_type?: string },
columns: React.ReactNode[],
rows: React.ReactNode[][],
}>) {
Expand All @@ -60,7 +61,7 @@ export default function SearchablePagedTable(props: React.PropsWithChildren<{
<Stack direction={"row"} alignItems={"center"} justifyContent={'space-between'}>
<Typography variant="h2" color="secondary" className="whitespace-nowrap">{props.label}</Typography>
<form action="" method="GET">
<SearchField q={props.q} placeholder={`Search ${props.label}`} />
<SearchField q={props.q} placeholder={`Search ${props.label}`} autocomplete={props.autocomplete} />
</form>
</Stack>
</Grid>
Expand Down
1 change: 1 addition & 0 deletions drc-portals/app/data/processed/c2m2_file/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export default async function Page(props: PageProps) {
p={searchParams.p}
r={searchParams.r}
count={results.count}
autocomplete={{ type: 'c2m2_file' }}
columns={[
<>&nbsp;</>,
<>Label</>,
Expand Down
1 change: 1 addition & 0 deletions drc-portals/app/data/processed/dcc_asset/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export default async function Page(props: PageProps) {
p={searchParams.p}
r={searchParams.r}
count={results.count}
autocomplete={{ type: 'dcc_asset' }}
columns={[
<>&nbsp;</>,
<>Label</>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export default async function Page(props: PageProps) {
p={searchParams.p}
r={searchParams.r}
count={results.count}
autocomplete={{ type: 'entity', entity_type: props.params.entity_type }}
columns={[
<>Label</>,
<>Description</>,
Expand Down
1 change: 1 addition & 0 deletions drc-portals/app/data/processed/entity/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export default async function Page(props: PageProps) {
p={searchParams.p}
r={searchParams.r}
count={results.count}
autocomplete={{ type: 'entity' }}
columns={[
<>Label</>,
<>Description</>,
Expand Down
1 change: 1 addition & 0 deletions drc-portals/app/data/processed/gene_set/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export default async function Page(props: PageProps) {
p={searchParams.p}
r={searchParams.r}
count={results.count}
autocomplete={{ type: 'gene_set' }}
columns={[
<>&nbsp;</>,
<>Label</>,
Expand Down
1 change: 1 addition & 0 deletions drc-portals/app/data/processed/gene_set_library/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export default async function Page(props: PageProps) {
p={searchParams.p}
r={searchParams.r}
count={count}
autocomplete={{ type: 'gene_set_library' }}
columns={[
<></>,
<>Label</>,
Expand Down
1 change: 1 addition & 0 deletions drc-portals/app/data/processed/kg_relation/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export default async function Page(props: PageProps) {
p={searchParams.p}
r={searchParams.r}
count={count}
autocomplete={{ type: 'kg_relation' }}
columns={[
<>Label</>,
<>Description</>,
Expand Down
36 changes: 36 additions & 0 deletions drc-portals/app/data/processed/router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import prisma from "@/lib/prisma";
import { procedure, router } from "@/lib/trpc";
import { NodeType, Prisma } from "@prisma/client";
import { cache } from 'react'
import { z } from 'zod'

export default router({
autocomplete: procedure
.input(z.object({
q: z.string(),
type: z.string().optional(),
entity_type: z.string().optional(),
}))
.query(cache(async (props) => {
if (props.input.q.length < 3) return []
const results = await prisma.$queryRaw<Array<{
id: string,
type: NodeType,
entity_type: string | null,
label: string,
}>>`
select node.id, node.type, entity_node.type as entity_type, node.label
from node
left join entity_node on entity_node.id = node.id
${props.input.type ? Prisma.sql`
where node.type = ${props.input.type}::"NodeType"
${props.input.entity_type ? Prisma.sql`
and entity_node.type = ${props.input.entity_type}
` : Prisma.empty}
` : Prisma.empty}
order by node.label <-> ${props.input.q}
limit 10
`
return results
}))
})
1 change: 1 addition & 0 deletions drc-portals/app/data/processed/search/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ export default async function Page(props: PageProps) {
p={searchParams.p}
r={searchParams.r}
count={results?.count}
autocomplete={{}}
columns={[
<>&nbsp;</>,
<>Label</>,
Expand Down
2 changes: 1 addition & 1 deletion drc-portals/components/Header/data.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export default async function InfoHeader() {
</Stack>
</Grid>
<Grid item>
<SearchParamSearchField />
<SearchParamSearchField autocomplete={{}} />
</Grid>
<Grid item xs={12}>
<NavBreadcrumbs/>
Expand Down
21 changes: 21 additions & 0 deletions drc-portals/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions drc-portals/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"@trpc/next": "^10.44.1",
"@trpc/react-query": "^10.44.1",
"@trpc/server": "^10.44.1",
"autosuggest-highlight": "^3.3.4",
"bootstrap": "^5.3.2",
"classnames": "^2.3.2",
"jszip": "^3.10.1",
Expand All @@ -50,6 +51,7 @@
},
"devDependencies": {
"@tailwindcss/typography": "^0.5.10",
"@types/autosuggest-highlight": "^3.2.3",
"@types/glob": "^8.1.0",
"@types/node": "^20",
"@types/react": "^18",
Expand Down

0 comments on commit 46860c6

Please sign in to comment.