Skip to content

Commit

Permalink
feat: add landing pages to search results (#1190)
Browse files Browse the repository at this point in the history
* cms now returns article permalinks

* add category to documentation in search

* pull fixture data into one file add documentation to storybook

* add landing page to search results

* add data-testid for search result preview

* show landing publishedDate or updatedDate

(which ever is later)
  • Loading branch information
gidjin authored Jan 18, 2024
1 parent 247a079 commit 9e83db2
Show file tree
Hide file tree
Showing 14 changed files with 219 additions and 143 deletions.
2 changes: 1 addition & 1 deletion src/__fixtures__/data/cmsSearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ export const mockCmsSearchResults = [
__typename: 'ArticleResult',
type: 'Article',
id: 'cl3buldda0247riyt6h85cpgc',
permalink: 'my-fitness-article',
permalink: 'http://example.com/article/my-fitness-article',
title: 'My Fitness Article',
preview: 'This is the preview text!',
date: '2022-05-18T17:18:40.802Z',
Expand Down
58 changes: 58 additions & 0 deletions src/__fixtures__/data/searchResults.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import type { SearchResultRecord } from 'types/index'

export const testApplicationResult: SearchResultRecord = {
id: 'testBookmark123',
type: 'Bookmark',
title: 'MyFSS',
preview:
'How is Air Force Information Management System abbreviated? AFIMS stands for Air Force Information Management System. For those who have seen the Earth from space, and for the hundreds and perhaps thousands more who will, the experience most certainly changes your perspective.',
permalink: 'https://www.url.com/myfss',
}

export const testDocumentationResult: SearchResultRecord = {
id: 'testDocumentation123',
type: 'Documentation',
title: 'Some Documentation',
preview:
'Documentation posted about some important topic that you should read about.',
permalink: 'https://localhost:3000/path/to/documentation',
}

export const testLandingPageResult: SearchResultRecord = {
id: 'testLandingPage123',
type: 'LandingPage',
title: 'Some Landing Page',
preview:
'Our team landing page where you can find all the information you need to know about our team.',
permalink: 'https://localhost:3000/landing/team',
}

export const testArticleResultNoLabels: SearchResultRecord = {
id: 'testArticle124',
type: 'Article',
title:
'Physical training: but without labels Everything you need to know about the update in requirements',
preview:
'There are no labels. As a steward of almost 9 million acres encompassing forests, prairies, deserts, wetlands, and coastal habitats, the Department of the Air Force recognizes the importance of protecting and sustaining the natural environment.',
permalink:
'https://localhost:3000/articles/physical-training-everything-you-need-to-know-no-labels',
date: '2022-05-17T13:44:39.796Z',
}
export const testArticleResult: SearchResultRecord = {
id: 'testArticle123',
type: 'Article',
title:
'Physical training: Everything you need to know about the update in requirements',
preview:
'As a steward of almost 9 million acres encompassing forests, prairies, deserts, wetlands, and coastal habitats, the Department of the Air Force recognizes the importance of protecting and sustaining the natural environment.',
permalink:
'https://localhost:3000/articles/physical-training-everything-you-need-to-know',
date: '2022-05-17T13:44:39.796Z',
labels: [
{
id: 'testLabel',
name: 'All Guardians',
type: 'Audience',
},
],
}
8 changes: 1 addition & 7 deletions src/__tests__/pages/search.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,7 @@ describe('Search page getServerSideProps', () => {
props: {
query: 'fitness',
pageTitle: 'fitness Search Results',
results: mockCmsSearchResults.map((r) => ({
...r,
permalink:
r.type === 'Article'
? `http://example.com/articles/${r.permalink}`
: r.permalink,
})),
results: mockCmsSearchResults,
},
})
})
Expand Down
43 changes: 14 additions & 29 deletions src/components/SearchResultItem/SearchResultItem.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,22 @@ import React from 'react'
import { Meta } from '@storybook/react'

import { SearchResultItem } from './SearchResultItem'
import type { SearchResultRecord } from 'types/index'
import {
testArticleResult,
testApplicationResult,
testDocumentationResult,
testLandingPageResult,
} from '__fixtures__/data/searchResults'

export default {
title: 'Components/SearchResults/SearchResultItem',
component: SearchResultItem,
} as Meta

const testApplicationResult: SearchResultRecord = {
id: 'testBookmark123',
type: 'Bookmark',
title: 'MyFSS',
preview:
'How is Air Force Information Management System abbreviated? AFIMS stands for Air Force Information Management System. For those who have seen the Earth from space, and for the hundreds and perhaps thousands more who will, the experience most certainly changes your perspective.',
permalink: 'https://www.url.com/myfss',
}

export const ApplicationResult = () => (
<SearchResultItem item={testApplicationResult} />
)

const testArticleResult: SearchResultRecord = {
id: 'testArticle123',
type: 'Article',
title:
'Physical training: Everything you need to know about the update in requirements',
preview:
'As a steward of almost 9 million acres encompassing forests, prairies, deserts, wetlands, and coastal habitats, the Department of the Air Force recognizes the importance of protecting and sustaining the natural environment.',
permalink:
'https://localhost:3000/articles/physical-training-everything-you-need-to-know',
date: '2022-05-17T13:44:39.796Z',
labels: [
{
id: 'testLabel',
name: 'All Guardians',
type: 'Audience',
},
],
}

export const ArticleResult = () => <SearchResultItem item={testArticleResult} />

export const ArticleResultLineClamp = () => (
Expand All @@ -54,3 +31,11 @@ export const ArticleResultLineClamp = () => (
}}
/>
)

export const DocumentationResult = () => (
<SearchResultItem item={testDocumentationResult} />
)

export const LandingPageResult = () => (
<SearchResultItem item={testLandingPageResult} />
)
150 changes: 93 additions & 57 deletions src/components/SearchResultItem/SearchResultItem.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,81 +7,117 @@ import { axe } from 'jest-axe'
import React from 'react'

import { SearchResultItem } from './SearchResultItem'
import type { SearchResultRecord } from 'types/index'
import {
testApplicationResult,
testArticleResultNoLabels,
testDocumentationResult,
testLandingPageResult,
} from '__fixtures__/data/searchResults'

const testApplicationResult: SearchResultRecord = {
id: 'testBookmark123',
type: 'Bookmark',
title: 'MyFSS',
preview:
'How is Air Force Information Management System abbreviated? AFIMS stands for Air Force Information Management System. For those who have seen the Earth from space, and for the hundreds and perhaps thousands more who will, the experience most certainly changes your perspective.',
permalink: 'https://www.url.com/myfss',
}
describe('SearchResultItem component', () => {
test('renders an Application result with no a11y violations', async () => {
const { container } = render(
<SearchResultItem item={testApplicationResult} />
)

const testArticleResult: SearchResultRecord = {
id: 'testArticle123',
type: 'Article',
title:
'Physical training: Everything you need to know about the update in requirements',
preview:
'As a steward of almost 9 million acres encompassing forests, prairies, deserts, wetlands, and coastal habitats, the Department of the Air Force recognizes the importance of protecting and sustaining the natural environment.',
permalink:
'https://localhost:3000/articles/physical-training-everything-you-need-to-know',
date: '2022-05-17T13:44:39.796Z',
}
expect(screen.queryByRole('img')).not.toBeInTheDocument()
expect(screen.queryAllByRole('link')).toHaveLength(1)
expect(screen.queryAllByRole('link')[0]).toHaveAttribute(
'href',
testApplicationResult.permalink
)
expect(screen.queryByRole('heading', { level: 3 })).toHaveTextContent(
testApplicationResult.title
)
expect(
screen.queryByText(testApplicationResult.preview)
).toBeInTheDocument()
expect(screen.queryByText('Application')).toHaveClass('usa-tag')

describe('SearchResultItem component', () => {
it('renders an Application result with no a11y violations', async () => {
// Bug with NextJS Link + axe :(
// https://github.com/nickcolley/jest-axe/issues/95#issuecomment-758921334
await act(async () => {
const { container } = render(
<SearchResultItem item={testApplicationResult} />,
{ legacyRoot: true }
)
expect(await axe(container)).toHaveNoViolations()
})
})

expect(screen.queryByRole('img')).not.toBeInTheDocument()
expect(screen.queryAllByRole('link')).toHaveLength(1)
expect(screen.queryAllByRole('link')[0]).toHaveAttribute(
'href',
testApplicationResult.permalink
)
expect(screen.queryByRole('heading', { level: 3 })).toHaveTextContent(
testApplicationResult.title
)
expect(
screen.queryByText(testApplicationResult.preview)
).toBeInTheDocument()
expect(screen.queryByText('Application')).toHaveClass('usa-tag')
test('renders an Article result with no a11y violations', async () => {
const { container } = render(
<SearchResultItem item={testArticleResultNoLabels} />
)

expect(screen.queryByText('May')).toBeInTheDocument()
expect(screen.queryByText('17')).toBeInTheDocument()

expect(screen.queryByRole('img')).not.toBeInTheDocument()
expect(screen.queryAllByRole('link')).toHaveLength(1)
expect(screen.queryAllByRole('link')[0]).toHaveAttribute(
'href',
testArticleResultNoLabels.permalink
)
expect(screen.queryByRole('heading', { level: 3 })).toHaveTextContent(
testArticleResultNoLabels.title
)
expect(
screen.queryByText(testArticleResultNoLabels.preview)
).toBeInTheDocument()
expect(screen.queryByText('USSF News')).toHaveClass('usa-tag')

// Bug with NextJS Link + axe :(
// https://github.com/nickcolley/jest-axe/issues/95#issuecomment-758921334
await act(async () => {
expect(await axe(container)).toHaveNoViolations()
})
})

it('renders an Article result with no a11y violations', async () => {
test('renders a Documentation result with no a11y violations', async () => {
const { container } = render(
<SearchResultItem item={testDocumentationResult} />
)

expect(screen.queryByRole('img')).not.toBeInTheDocument()
expect(screen.queryAllByRole('link')).toHaveLength(1)
expect(screen.queryAllByRole('link')[0]).toHaveAttribute(
'href',
testDocumentationResult.permalink
)
expect(screen.queryByRole('heading', { level: 3 })).toHaveTextContent(
testDocumentationResult.title
)
expect(
screen.queryByText(testDocumentationResult.preview)
).toBeInTheDocument()
expect(screen.queryByText('USSF Documentation')).toHaveClass('usa-tag')

// Bug with NextJS Link + axe :(
// https://github.com/nickcolley/jest-axe/issues/95#issuecomment-758921334
await act(async () => {
const { container } = render(
<SearchResultItem item={testArticleResult} />,
{ legacyRoot: true }
)
expect(await axe(container)).toHaveNoViolations()
})
})

expect(screen.queryByText('May')).toBeInTheDocument()
expect(screen.queryByText('17')).toBeInTheDocument()
test('renders a Landing Page result with no a11y violations', async () => {
const { container } = render(
<SearchResultItem item={testLandingPageResult} />
)

expect(screen.queryByRole('img')).not.toBeInTheDocument()
expect(screen.queryAllByRole('link')).toHaveLength(1)
expect(screen.queryAllByRole('link')[0]).toHaveAttribute(
'href',
testArticleResult.permalink
)
expect(screen.queryByRole('heading', { level: 3 })).toHaveTextContent(
testArticleResult.title
)
expect(screen.queryByText(testArticleResult.preview)).toBeInTheDocument()
expect(screen.queryByText('USSF News')).toHaveClass('usa-tag')
expect(screen.queryByRole('img')).not.toBeInTheDocument()
expect(screen.queryAllByRole('link')).toHaveLength(1)
expect(screen.queryAllByRole('link')[0]).toHaveAttribute(
'href',
testLandingPageResult.permalink
)
expect(screen.queryByRole('heading', { level: 3 })).toHaveTextContent(
testLandingPageResult.title
)
expect(
screen.queryByText(testLandingPageResult.preview)
).toBeInTheDocument()
expect(screen.queryByText('Landing Page')).toHaveClass('usa-tag')

// Bug with NextJS Link + axe :(
// https://github.com/nickcolley/jest-axe/issues/95#issuecomment-758921334
await act(async () => {
expect(await axe(container)).toHaveNoViolations()
})
})
Expand Down
8 changes: 7 additions & 1 deletion src/components/SearchResultItem/SearchResultItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ export const SearchResultItem = ({ item }: { item: SearchResultRecord }) => {
itemIcon = dateObj && <ArticleDateIcon date={dateObj} />
break
}
case 'Documentation':
itemCategory = CONTENT_CATEGORIES.DOCUMENTATION
break
case 'LandingPage':
itemCategory = CONTENT_CATEGORIES.LANDING_PAGE
break
}

return (
Expand All @@ -34,7 +40,7 @@ export const SearchResultItem = ({ item }: { item: SearchResultRecord }) => {
</Link>
</h3>

<p>{preview}</p>
<p data-testid="result-preview">{preview}</p>

<div className={styles.metadata}>
{itemCategory && <Category category={itemCategory} />}
Expand Down
4 changes: 4 additions & 0 deletions src/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ export const CONTENT_CATEGORIES = {
value: 'Documentation',
label: 'USSF Documentation',
},
LANDING_PAGE: {
value: 'LandingPage',
label: 'Landing Page',
},
APPLICATION: {
value: 'Application',
label: 'Application',
Expand Down
9 changes: 9 additions & 0 deletions src/helpers/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
isCmsUser,
getYouTubeEmbedId,
handleRedirectTo,
formatDisplayDate,
} from './index'

import type { RSSNewsItem, PublishableItemType } from 'types'
Expand Down Expand Up @@ -217,3 +218,11 @@ describe('handleRedirectTo', () => {
expect(handleRedirectTo(mockReq)).toEqual('/redirect?redirectPath=/users')
})
})

describe('formatDisplayDate', () => {
test('returns formatted date string', () => {
const testDate = new Date('2021-05-17T13:44:39.796Z')
const expectedDate = 'May 17, 2021'
expect(formatDisplayDate(testDate)).toEqual(expectedDate)
})
})
8 changes: 8 additions & 0 deletions src/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,11 @@ export const handleRedirectTo = (req: PassportRequest) => {
return '/'
}
}

export const formatDisplayDate = (date: Date) => {
return new Date(date).toLocaleDateString('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric',
})
}
1 change: 1 addition & 0 deletions src/operations/cms/queries/getLandingPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const GET_LANDING_PAGE = gql`
slug
status
publishedDate
updatedAt
documents {
title
document {
Expand Down
Loading

0 comments on commit 9e83db2

Please sign in to comment.