Skip to content

Commit

Permalink
feat: Add hero image support for landing page (#1189)
Browse files Browse the repository at this point in the history
* Add hero image to landing page

* Update mockLandingPage to include image

* Add test to bump coverage

* Add badge to landing page header

* Style updates

* Add default badge

* Add badge to landing page index

* Update mock data to include badge
  • Loading branch information
jcbcapps authored Jan 13, 2024
1 parent 219b968 commit 141a348
Show file tree
Hide file tree
Showing 11 changed files with 158 additions and 21 deletions.
Binary file added public/img/default_badge.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/__fixtures__/data/landingPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import { DateTime } from 'luxon'
export const mockLandingPage = {
__typename: 'LandingPage',
pageTitle: 'Test Landing Page',
hero: {
url: 'http://cms.example.com/images/image.png',
},
pageDescription:
'em ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque ac odio ultrices, varius diam at, iaculis sapien. Integer risus quam, congue quis nibh in, iaculis ultrices justo. Sed viverra, massa in finibus vehicula, odio dui fringilla tellus, nec consequat arcu nulla eu augue. Maecenas at ornare orci. Aenean mattis et sapien at vulputate. Sed vel arcu at lorem consequat pulvinar quis ac ante. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Praesent commodo eros eget gravida hendrerit. Suspendisse facilisis odio vel lacus mollis condimentum. Proin in lectus et erat congue luctus non et ligula. Aenean elementum, risus quis tristique cursus, metus leo ornare sem, ut convallis dui velit sit amet mauris.',
slug: 'a-page',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@

> tbody > tr {
border-bottom: 1px solid var(--lp-td-border-color);

> td {
display: flex;
align-items: center;
justify-content: space-between;
}
}

> tbody > tr:nth-child(odd) > td {
Expand All @@ -33,3 +39,9 @@
@include u-bg('green-cool-20');
}
}

.badge {
max-height: 25px;
max-width: 25px;
margin-right: 0.25rem;
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,37 +17,55 @@ const testLandingPages = [
slug: 'test-landing-page-1',
publishedDate: DateTime.now().toISO()!,
status: 'Published' as 'Draft' | 'Published' | 'Archived',
badge: {
url: '/img/default_badge.png',
},
},
{
pageTitle: 'Test Landing Page 4',
slug: 'test-landing-page-4',
publishedDate: DateTime.now().toISO()!,
status: 'Archived' as 'Draft' | 'Published' | 'Archived',
badge: {
url: '/img/default_badge.png',
},
},
{
pageTitle: 'Test Landing Page 2',
slug: 'test-landing-page-2',
publishedDate: DateTime.now().toISO()!,
status: 'Published' as 'Draft' | 'Published' | 'Archived',
badge: {
url: '/img/default_badge.png',
},
},
{
pageTitle: 'Test Landing Page 3',
slug: 'test-landing-page-3',
publishedDate: DateTime.now().toISO()!,
status: 'Draft' as 'Draft' | 'Published' | 'Archived',
badge: {
url: '/img/default_badge.png',
},
},
{
pageTitle: 'Test Landing Page 5',
slug: 'test-landing-page-5',
publishedDate: DateTime.now().plus({ weeks: 2 }).toISO()!,
status: 'Published' as 'Draft' | 'Published' | 'Archived',
badge: {
url: '/img/default_badge.png',
},
},
{
pageTitle:
'Test Landing Page 6 with a really long title to ensure that the table is responsive',
slug: 'test-landing-page-6',
publishedDate: DateTime.now().plus({ weeks: 3 }).toISO()!,
status: 'Published' as 'Draft' | 'Published' | 'Archived',
badge: {
url: '/img/default_badge.png',
},
},
]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,45 @@ const testLandingPages = [
slug: 'test-landing-page-1',
publishedDate: DateTime.now().toISO()!,
status: 'Published' as 'Draft' | 'Published' | 'Archived',
badge: {
url: '/img/default_badge.png',
},
},
{
pageTitle: 'Test Landing Page 2',
slug: 'test-landing-page-2',
publishedDate: expectedPastDate.toISO()!,
status: 'Published' as 'Draft' | 'Published' | 'Archived',
badge: {
url: '/img/default_badge.png',
},
},
{
pageTitle: 'Test Landing Page 3',
slug: 'test-landing-page-3',
publishedDate: DateTime.now().toISO()!,
status: 'Draft' as 'Draft' | 'Published' | 'Archived',
badge: {
url: '/img/default_badge.png',
},
},
{
pageTitle: 'Test Landing Page 4',
slug: 'test-landing-page-4',
publishedDate: DateTime.now().toISO()!,
status: 'Archived' as 'Draft' | 'Published' | 'Archived',
badge: {
url: '/img/default_badge.png',
},
},
{
pageTitle: 'Test Landing Page 5',
slug: 'test-landing-page-5',
publishedDate: expectedFutueDate.toISO()!,
status: 'Published' as 'Draft' | 'Published' | 'Archived',
badge: {
url: '/img/default_badge.png',
},
},
]

Expand Down
42 changes: 28 additions & 14 deletions src/components/LandingPageIndexTable/LandingPageIndexTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import { PublishableItemType } from 'types'
type LandingPage = {
pageTitle: string
slug: string
badge: {
url: string
}
} & PublishableItemType

type LandingPageIndexTableProps = {
Expand All @@ -23,32 +26,43 @@ const LandingPageIndexTable = ({
<Table bordered striped className={`usa-table--borderless ${styles.table}`}>
<tbody>
{landingPages.map((landingPage: LandingPage) => {
const rowId = `landing_page_${landingPage.slug}`
const { pageTitle, slug, badge, publishedDate, status } = landingPage

const rowId = `landing_page_${slug}`
// if the date is in the past we want to show past tense text
const publishText =
landingPage.publishedDate &&
DateTime.fromISO(landingPage.publishedDate) > DateTime.now()
publishedDate && DateTime.fromISO(publishedDate) > DateTime.now()
? 'Publishing'
: 'Published'
// if a future publish date is set format the date for display
// we don't need to check if the date is in the future as the
// isPublished helper function will return false if the date is in the future
const status =
landingPage.status === 'Published'
? `${publishText} on: ${DateTime.fromISO(
landingPage.publishedDate
).toFormat('dd MMM yyyy HH:mm')}`
: landingPage.status
const landingPageStatus =
status === 'Published'
? `${publishText} on: ${DateTime.fromISO(publishedDate).toFormat(
'dd MMM yyyy HH:mm'
)}`
: status

const badgeImage = badge?.url.length
? badge.url
: '/img/default_badge.png'
return (
<tr id={rowId} key={rowId}>
<td>
<Link href={`/landing/${landingPage.slug}`}>
{landingPage.pageTitle}
</Link>
<div style={{ display: 'flex', alignContent: 'center' }}>
<img
src={badgeImage}
alt="landing page badge"
className={styles.badge}
/>

<Link href={`/landing/${landingPage.slug}`}>{pageTitle}</Link>
</div>

{showStatus ? (
<Tag className={`${styles.status} ${landingPage.status}`}>
{status}
<Tag className={`${styles.status} ${status}`}>
{landingPageStatus}
</Tag>
) : null}
</td>
Expand Down
21 changes: 17 additions & 4 deletions src/components/PageNav/PageNav.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/
import React from 'react'
import { render, screen } from '@testing-library/react'

import { mockFlags } from 'jest-launchdarkly-mock'
import PageNav from './PageNav'

jest.mock('next/router', () => ({
Expand All @@ -17,17 +17,30 @@ jest.mock('next/router', () => ({

describe('PageNav component', () => {
describe('on any page', () => {
beforeEach(() => {
test('renders links for the nav items', () => {
render(<PageNav />)
})

test('renders links for the nav items', () => {
const links = screen.getAllByRole('link')

links.forEach((link, index) => {
const navItem = links[parseInt(`${index}`)]
expect(link).toHaveAttribute('href', navItem.getAttribute('href'))
})

expect(links).toHaveLength(3)
})

test('renders nav items behind LaunchDarkly flags', () => {
mockFlags({
landingPageIndex: true,
guardianDirectory: true,
})

render(<PageNav />)

const links = screen.getAllByRole('link')

expect(links).toHaveLength(5)
})
})
})
6 changes: 6 additions & 0 deletions src/operations/cms/queries/getLandingPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ export const GET_LANDING_PAGE = gql`
query GetLandingPage($slug: String) {
landingPage(where: { slug: $slug }) {
pageTitle
badge {
url
}
hero {
url
}
pageDescription
slug
status
Expand Down
3 changes: 3 additions & 0 deletions src/operations/cms/queries/getLandingPages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ export const GET_LANDING_PAGES = gql`
slug
status
publishedDate
badge {
url
}
}
}
`
34 changes: 31 additions & 3 deletions src/pages/landing/[landingPage]/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,42 @@ type DocumentsType = {
const LandingPage = ({
landingPage,
}: InferGetServerSidePropsType<typeof getServerSideProps>) => {
const { pageTitle, pageDescription, documents, collections, articles } =
landingPage
const {
pageTitle,
badge,
hero,
pageDescription,
documents,
collections,
articles,
} = landingPage

const badgeImage = badge?.url.length ? badge.url : '/img/default_badge.png'
const heroImage = hero?.url.length ? hero.url : ''

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const pageContent = (): any => {
return (
<>
<h1 className={styles.pageTitle}>{pageTitle}</h1>
<div className={styles.pageTitleContainer}>
<img
src={badgeImage}
alt="landing page badge"
className={styles.badge}
/>

<h1 className={styles.pageTitle}>{pageTitle}</h1>
</div>

{heroImage && (
<div className={styles.heroContainer}>
<img
src={hero.url}
alt="landing page hero graphic"
className={styles.hero}
/>
</div>
)}
<p className={styles.pageDescription}>{pageDescription}</p>
{documents.length >= 1 && <h2 id="documentation">Documentation</h2>}
{documents.length >= 1 && (
Expand Down
25 changes: 25 additions & 0 deletions src/styles/pages/landingPage.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,36 @@
.pageTitle {
text-transform: uppercase;
margin-top: 0;
margin-bottom: 0;
}

.pageTitleContainer {
display: flex;
align-items: center;
}

.badge {
max-height: 50px;
max-width: 50px;
margin-right: 0.25rem;
}

.heroContainer {
padding-top: 32px;
padding-bottom: 32px;
}

.hero {
width: 100%;
max-height: 340px;
object-fit: cover;
}

.pageDescription {
line-height: 26px;
max-width: 700px;
margin-top: 0;
margin-bottom: 0;
}

.pageMeta {
Expand Down

0 comments on commit 141a348

Please sign in to comment.