Skip to content

Commit

Permalink
feat: Filter search results client (#1027)
Browse files Browse the repository at this point in the history
* Implement basic component

* Style updates

* Update to use form

* Add function to build queries

* Disable default dropdown value

* Add labels query and add labels to query building

* Add query for tags

* Remove tags query and form element

* Move SearchFilter component

* Store filter options in an array

* Remove console.log

* Add SearchContext

* Add SearchFilter component back

* Add searchPageFilters to searchContext

* Clear the searchQuery when not on /search

* Update form submission to include searchQuery

* Add filter reset

* Move searchPageFilters from context to component

* Save filter to local storage

* Style updates

* Dark mode styles

* Fix button style

* Add storybook component and set max-width

* Add back missing SearchBanner, update styles and fix tests

* Add tests

* Update test

* Add test to bump coverage

* Add test to bump coverage

* Change it to test

* Update headers

* Accessibility updates

* Add LD flag with tests

* Remove disabled prop

* Fix what I broke

* Updates to label search

* Allow user to select default option

* Move searchPageFilters to context and update param name

* Update form submit

* Wrap category value in double quotes if more than one word

* Trim search query to 200 characters before submitting

* Update test

* Add tests and remove localStorage for storing filter items

* Add tests

* Remove unnecessary form and set searchQuery when component mounts

* Ensure filter items remain selected after search

* Place EPubs component above SearchFilter

* Update tests and add renderWithSearchContext helper

* Remove searchPageFilters array and add filter items directly to searchQuery

* Update test

* Accessibility updates

* Fix multi-word labels

* Remove console.logs

* Remove unused icon

* Remove unnecessary code

---------

Co-authored-by: Shauna Keating <[email protected]>
Co-authored-by: John Gedeon <[email protected]>
  • Loading branch information
3 people authored Jun 14, 2023
1 parent 4c6d1aa commit 35f6f90
Show file tree
Hide file tree
Showing 21 changed files with 1,116 additions and 212 deletions.
81 changes: 69 additions & 12 deletions src/__tests__/pages/search.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import { screen, waitFor } from '@testing-library/react'
import axios from 'axios'
import { useRouter } from 'next/router'
import { mockFlags } from 'jest-launchdarkly-mock'
import type { GetServerSidePropsContext } from 'next'

import { renderWithAuth } from '../../testHelpers'
Expand Down Expand Up @@ -52,7 +53,7 @@ mockedUseRouter.mockReturnValue({
})

describe('Search page getServerSideProps', () => {
it('returns no query and no results if there was no query', async () => {
test('returns no query and no results if there was no query', async () => {
const testContext = {
query: {},
} as unknown as GetServerSidePropsContext
Expand All @@ -65,7 +66,7 @@ describe('Search page getServerSideProps', () => {
})
})

it('returns the query and results if there was a query', async () => {
test('returns the query and results if there was a query', async () => {
const testContext = {
query: { q: 'fitness' },
} as unknown as GetServerSidePropsContext
Expand Down Expand Up @@ -95,20 +96,27 @@ describe('Search page', () => {
})
})

it('renders the loader while fetching the user', () => {
test('renders the loader while fetching the user', () => {
expect(screen.getByText('Content is loading...')).toBeInTheDocument()
})

it('redirects to the login page if not logged in', async () => {
test('redirects to the login page if not logged in', async () => {
await waitFor(() => {
expect(mockReplace).toHaveBeenCalledWith('/login')
})
})
})

describe('when logged in', () => {
it('renders an empty state if there is no query', async () => {
renderWithAuth(<SearchPage />)
test('renders an empty state if there is no query', async () => {
renderWithAuth(
<SearchPage
query={''}
results={[]}
labels={[{ name: 'label1' }, { name: 'label2' }, { name: 'label3' }]}
/>
)

expect(
await screen.findByRole('heading', { level: 2 })
).toHaveTextContent('There are 0 results for ‘’')
Expand All @@ -118,8 +126,13 @@ describe('Search page', () => {
).toHaveLength(1)
})

it('renders no results if there were no matches for the query', async () => {
renderWithAuth(<SearchPage query="nomatches" />)
test('renders no results if there were no matches for the query', async () => {
renderWithAuth(
<SearchPage
query="nomatches"
labels={[{ name: 'label1' }, { name: 'label2' }, { name: 'label3' }]}
/>
)
expect(
await screen.findByRole('heading', { level: 2 })
).toHaveTextContent('There are 0 results for ‘nomatches’')
Expand All @@ -129,15 +142,53 @@ describe('Search page', () => {
).toHaveLength(1)
})

it('renders the results if there were matches for the query', async () => {
test('renders the SearchFilter component', () => {
mockFlags({
searchPageFilter: true,
})

renderWithAuth(
<SearchPage
query="fitness"
results={[]}
labels={[{ name: 'label1' }, { name: 'label2' }, { name: 'label3' }]}
/>
)

expect(screen.getByText('Filter Search')).toBeInTheDocument()
})

test('does not render the SearchFilter component if the flag is off', () => {
mockFlags({
searchPageFilter: false,
})

renderWithAuth(
<SearchPage
query="fitness"
results={[]}
labels={[{ name: 'label1' }, { name: 'label2' }, { name: 'label3' }]}
/>
)

expect(screen.queryByText('Filter Search')).not.toBeInTheDocument()
})

test('renders the results if there were matches for the query', async () => {
const mockResults = mockCmsSearchResults.map((r) => ({
...r,
permalink:
r.type === 'Article'
? `http://localhost/articles/${r.permalink}`
: r.permalink,
}))
renderWithAuth(<SearchPage query="fitness" results={mockResults} />)
renderWithAuth(
<SearchPage
query="fitness"
results={mockResults}
labels={[{ name: 'label1' }, { name: 'label2' }, { name: 'label3' }]}
/>
)
expect(
await screen.findByRole('heading', { level: 2 })
).toHaveTextContent('There are 3 results for ‘fitness’')
Expand All @@ -148,7 +199,7 @@ describe('Search page', () => {
).toBeInTheDocument()
})

it('renders the correct text if there is only one result', async () => {
test('renders the correct text if there is only one result', async () => {
const mockResults = [mockCmsSearchResults[0]].map((r) => ({
...r,
permalink:
Expand All @@ -157,7 +208,13 @@ describe('Search page', () => {
: r.permalink,
}))

renderWithAuth(<SearchPage query="fitness" results={mockResults} />)
renderWithAuth(
<SearchPage
query="fitness"
results={mockResults}
labels={[{ name: 'label1' }, { name: 'label2' }, { name: 'label3' }]}
/>
)
expect(
await screen.findByRole('heading', { level: 2 })
).toHaveTextContent('There is 1 result for ‘fitness’')
Expand Down
60 changes: 55 additions & 5 deletions src/components/Bookmark/Bookmark.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import { act, render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { axe } from 'jest-axe'
import React from 'react'
import { cmsBookmarksMock } from '../../__fixtures__/data/cmsBookmarks'
import Bookmark from './Bookmark'

describe('Bookmark component', () => {
it('renders an anchor link', () => {
test('renders an anchor link', () => {
render(<Bookmark href="/home">Home</Bookmark>)

const link = screen.getByRole('link', {
Expand All @@ -21,7 +22,7 @@ describe('Bookmark component', () => {
expect(link).toBeInstanceOf(HTMLAnchorElement)
})

it('renders a delete button if a handler is provided', async () => {
test('renders a delete button if a handler is provided', async () => {
const user = userEvent.setup()
const mockOnDelete = jest.fn()

Expand All @@ -36,7 +37,56 @@ describe('Bookmark component', () => {
expect(mockOnDelete).toHaveBeenCalled()
})

it('has no a11y violations', async () => {
test('displays tooltip when focused', async () => {
const user = userEvent.setup()

render(
<Bookmark
href="/home"
bookmarkDescription={cmsBookmarksMock[0].description}>
Home
</Bookmark>
)
const link = screen.getByRole('link', {
name: 'Home (opens in a new window)',
})

await user.tab()
expect(link).toHaveFocus()

expect(
screen.queryByText(cmsBookmarksMock[0].description)
).toBeInTheDocument()
})

test('displays tooltip when hovering', async () => {
const user = userEvent.setup()

render(
<Bookmark
href="/home"
bookmarkDescription={cmsBookmarksMock[0].description}>
Home
</Bookmark>
)
const link = screen.getByRole('link', {
name: 'Home (opens in a new window)',
})

await user.hover(link)

expect(
screen.queryByText(cmsBookmarksMock[0].description)
).toBeInTheDocument()

await user.unhover(link)

expect(
screen.queryByText(cmsBookmarksMock[0].description)
).not.toBeInTheDocument()
})

test('has no a11y violations', async () => {
// Bug with NextJS Link + axe :(
// https://github.com/nickcolley/jest-axe/issues/95#issuecomment-758921334
await act(async () => {
Expand All @@ -53,7 +103,7 @@ describe('Bookmark component', () => {
})

describe('when disabled', () => {
it('renders static text instead of a link', () => {
test('renders static text instead of a link', () => {
render(
<Bookmark href="/home" disabled>
Home
Expand All @@ -64,7 +114,7 @@ describe('Bookmark component', () => {
expect(screen.getByText('Home')).toBeInstanceOf(HTMLSpanElement)
})

it('has no a11y violations', async () => {
test('has no a11y violations', async () => {
const { container } = render(
<Bookmark href="/home" disabled>
Home
Expand Down
3 changes: 1 addition & 2 deletions src/components/PageHeader/PageHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import styles from './PageHeader.module.scss'
import Search from 'components/Search/Search'

const PageHeader = ({
searchQuery,
children,
}: {
searchQuery?: string
Expand All @@ -21,7 +20,7 @@ const PageHeader = ({
</Grid>

<Grid col="auto" desktop={{ col: 6 }}>
<Search query={searchQuery} />
<Search />
</Grid>
</Grid>
</GridContainer>
Expand Down
Loading

0 comments on commit 35f6f90

Please sign in to comment.