Skip to content

Commit

Permalink
Added server-side text search for curated lists on data lists page (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
zachsa committed Jan 9, 2023
1 parent 145a9f8 commit f9b9b5d
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 43 deletions.
7 changes: 5 additions & 2 deletions api/src/graphql/resolvers/queries/lists/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
export default async (self, args, ctx) => {
export default async (self, { filter: $search }, ctx) => {
const filter = $search ? { $text: { $search, $caseSensitive: false } } : {}
const { Lists } = await ctx.mongo.collections
const lists = Lists.find({ type: 'curated' }).sort({ title: 1 })
const lists = Lists.find({ type: 'curated', ...filter }).sort({
title: 1,
})
return await lists.toArray()
}
2 changes: 1 addition & 1 deletion api/src/graphql/schema/_schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ type Query {
"""
Retrieve many search contexts (i.e. many dynamic lists)
"""
lists: [List!]!
lists(filter: String): [List!]!
downloadsReport(sort: SortConfig): [DownloadSummary]!
roles: [Role]!
permissions: [Permission]!
Expand Down
14 changes: 9 additions & 5 deletions clients/src/modules/data-lists/context/index.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { createContext } from 'react'
import { createContext, useState } from 'react'
import { gql, useQuery } from '@apollo/client'
import Loading from '../../../components/loading'

export const context = createContext()

export default ({ children }) => {
const [filter, setFilter] = useState('')

const { error, loading, data } = useQuery(
gql`
query {
lists {
query lists($filter: String) {
lists(filter: $filter) {
id
filter
title
Expand All @@ -19,7 +21,7 @@ export default ({ children }) => {
}
}
`,
{ variables: {} }
{ variables: { filter }, fetchPolicy: 'cache-and-network' }
)

if (loading) {
Expand All @@ -30,5 +32,7 @@ export default ({ children }) => {
throw error
}

return <context.Provider value={{ lists: data.lists }}>{children}</context.Provider>
return (
<context.Provider value={{ lists: data.lists, filter, setFilter }}>{children}</context.Provider>
)
}
56 changes: 56 additions & 0 deletions clients/src/modules/data-lists/header/filter/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { useContext } from 'react'
import { context as filterContext } from '../../context'
import TextField from '@mui/material/TextField'
import InputAdornment from '@mui/material/InputAdornment'
import { Magnify as SearchIcon } from '../../../../components/icons'
import QuickForm from '../../../../components/quick-form'
import debounce from '../../../../lib/fns/debounce'
import useMediaQuery from '@mui/material/useMediaQuery'

export default () => {
const smUp = useMediaQuery(theme => theme.breakpoints.up('sm'))
const { filter, setFilter } = useContext(filterContext)

return (
<QuickForm text={filter} effects={[debounce(({ text }) => setFilter(text), 500)]}>
{(update, { text }) => {
return (
<TextField
autoComplete="off"
fullWidth
id="saeon-data-search"
size="small"
onChange={e => update({ text: e.target.value })}
variant="standard"
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchIcon fontSize="small" />
</InputAdornment>
),
inputProps: {
'aria-label': 'Enter search text and press enter',
sx: {
p: 0,
lineHeight: '100%',
},
},
}}
value={text}
margin="none"
placeholder={smUp ? 'Filter lists ...' : ''}
sx={{
backgroundColor: theme => theme.palette.grey[200],
p: theme => theme.spacing(0.5),
'& .MuiInput-root': {
'&:before, :after, :hover:not(.Mui-disabled):before': {
borderBottom: 0,
},
},
}}
/>
)
}}
</QuickForm>
)
}
29 changes: 15 additions & 14 deletions clients/src/modules/data-lists/header/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,23 @@ import NewList from './new-list'
import DownloadDetails from './_download'
import Divider from '@mui/material/Divider'
import { Div } from '../../../components/html-tags'
import Filter from './filter'

export default () => {
return (
<>
<Header sx={{ display: 'flex' }}>
<Div sx={{ marginLeft: 'auto' }} />
<Divider flexItem orientation="vertical" sx={{ mr: theme => theme.spacing(1) }} />
<DownloadDetails />
<Divider
flexItem
orientation="vertical"
sx={{ mr: theme => theme.spacing(1), ml: theme => theme.spacing(1) }}
/>
<NewList />
<Div sx={{ mr: theme => theme.spacing(1) }} />
</Header>
</>
<Header sx={{ display: 'flex' }}>
<Div sx={{ mx: theme => theme.spacing(1), flex: 1 }}>
<Filter />
</Div>
<Divider flexItem orientation="vertical" sx={{ mr: theme => theme.spacing(1) }} />
<DownloadDetails />
<Divider
flexItem
orientation="vertical"
sx={{ mr: theme => theme.spacing(1), ml: theme => theme.spacing(1) }}
/>
<NewList />
<Div sx={{ mr: theme => theme.spacing(1) }} />
</Header>
)
}
34 changes: 22 additions & 12 deletions clients/src/modules/data-lists/header/new-list/_save-list.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,25 +27,35 @@ export default ({ closeFn, title, description, createdBy }) => {
}
`,
{
refetchQueries: ['lists'],
update: (cache, { data: { saveList: newList } }) => {
const query = gql`
query {
lists {
id
title
description
const { lists: existingLists } = cache.readQuery({
query: gql`
query ($filter: String) {
lists(filter: $filter) {
id
title
description
}
}
}
`

const { lists: existingLists } = cache.read({
query,
`,
variables: {
filter: '',
},
})

const merged = [...existingLists, newList]

cache.writeQuery({
query,
query: gql`
query {
lists {
id
title
description
}
}
`,
data: {
lists: merged,
},
Expand Down
13 changes: 4 additions & 9 deletions clients/src/modules/data-lists/layout/index.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useContext, useMemo, useState, useEffect } from 'react'
import { useContext, useMemo } from 'react'
import { context as ListsContext } from '../context'
import ContentNav from '../../../components/content-nav'
import Fade from '@mui/material/Fade'
Expand All @@ -9,7 +9,6 @@ import { Div, Span } from '../../../components/html-tags'
import Container from '@mui/material/Container'

export default () => {
const [renderCount, setRenderCount] = useState(0)
const { lists } = useContext(ListsContext)

const navItems = useMemo(
Expand All @@ -26,22 +25,18 @@ export default () => {
[lists]
)

useEffect(() => {
setRenderCount(c => c + 1)
}, [navItems])

if (!navItems.length) {
return (
<Fade unmountOnExit mountOnEnter in={true}>
<Span>
<Container sx={{ mt: theme => theme.spacing(2) }}>
<NoItems />
</Span>
</Container>
</Fade>
)
}

return (
<ContentNav activeIndex={renderCount === 1 ? 0 : lists.length - 1} navItems={navItems}>
<ContentNav activeIndex={lists.length - 1} navItems={navItems}>
{({ activeIndex }) => {
return (
<Container style={{ minHeight: 1000 }}>
Expand Down

0 comments on commit f9b9b5d

Please sign in to comment.