Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add events query/formatter/layout #264

Merged
merged 19 commits into from
Dec 12, 2023
22 changes: 22 additions & 0 deletions playwright/tests/event.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const { test, expect } = require('../utils/next-test')

test.describe('Event Page', () => {
test('Event page renders with correct title and details', async ({
page,
}) => {
await page.goto('/central-iowa-health-care/events/52265/')
await expect(page.locator('h1')).toHaveText('Pickleball Club') // Replace with actual event title
await expect(page.locator('.va-introtext')).toContainText('Pickleball') // Replace with actual event description
})

test('Should render without a11y errors', async ({
page,
makeAxeBuilder,
}) => {
await page.goto('/central-iowa-health-care/events/52265/')

const accessibilityScanResults = await makeAxeBuilder().analyze()

expect(accessibilityScanResults.violations).toEqual([])
})
})
4 changes: 4 additions & 0 deletions src/assets/styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,7 @@ body {
margin-top: 0;
padding-top: 1.25rem;
}

.recurring-event {
max-width: 140px;
}
86 changes: 86 additions & 0 deletions src/data/queries/event.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import {
QueryData,
QueryFormatter,
QueryOpts,
QueryParams,
} from 'next-drupal-query'
import { drupalClient } from '@/lib/drupal/drupalClient'
import { queries } from '.'
import { NodeEvent } from '@/types/dataTypes/drupal/node'
import { Event } from '@/types/dataTypes/formatted/event'
import { GetServerSidePropsContext } from 'next'
import { MediaImage } from '@/types/dataTypes/formatted/media'

export const params: QueryParams<null> = () => {
return queries
.getParams()
.addInclude([
'field_media',
'field_media.image',
'field_listing',
'field_administration',
'field_facility_location',
])
}

// Define the option types for the data loader.
export type EventDataOpts = QueryOpts<{
id: string
context?: GetServerSidePropsContext
}>

export const data: QueryData<EventDataOpts, NodeEvent> = async (
opts
): Promise<NodeEvent> => {
const entity = opts?.context?.preview
? // need to use getResourceFromContext for unpublished revisions
await drupalClient.getResourceFromContext<NodeEvent>(
'node--event',
opts.context,
{
params: params().getQueryObject(),
}
)
: // otherwise just lookup by uuid
await drupalClient.getResource<NodeEvent>('node--event', opts.id, {
params: params().getQueryObject(),
})
return entity
}

export const formatter: QueryFormatter<NodeEvent, Event> = (
entity: NodeEvent
) => {
return {
id: entity.id,
entityId: entity.drupal_internal__nid,
entityPath: entity.path.alias,
type: entity.type,
published: entity.status,
title: entity.title,
image: queries.formatData('media--image', {
entity: entity.field_media,
cropType: '2_1_large',
}) as MediaImage,
date: entity.created,
breadcrumbs: entity.breadcrumbs,
socialLinks: {
path: entity.path.alias,
title: entity.title,
},
metatags: entity.metatag,
listing: entity.field_listing.path.alias,
additionalInfo: entity.field_additional_information_abo,
address: entity.field_address,
locationHumanReadable: entity.field_location_humanreadable,
eventCTA: entity.field_event_cta,
cost: entity.field_event_cost,
datetimeRange: entity.field_datetime_range_timezone,
facilityLocation: entity.field_facility_location,
body: entity.field_body,
locationType: entity.field_location_type,
description: entity.field_description,
link: entity.field_link,
urlOfOnlineEvent: entity.field_url_of_an_online_event,
}
}
2 changes: 2 additions & 0 deletions src/data/queries/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import * as Wysiwyg from './wysiwyg'
import * as StaticPathResources from './staticPathResources'
import * as HeaderFooter from './headerFooter'
import * as PromoBlock from './promoBlock'
import * as Event from './event'
import { RESOURCE_TYPES } from '@/lib/constants/resourceTypes'

export const QUERIES_MAP = {
Expand All @@ -25,6 +26,7 @@ export const QUERIES_MAP = {
'node--news_story--teaser': NewsStoryTeaser,
'node--story_listing': StoryListing,
'node--q_a': QuestionAnswer,
'node--event': Event,
'node--person_profile': PersonProfile,
'node--landing_page': BenefitsHub, // "Benefits Hub Landing Page"
'paragraph--audience_topics': AudienceTopics,
Expand Down
91 changes: 91 additions & 0 deletions src/data/queries/tests/__snapshots__/event.test.tsx.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`node--event formatData outputs formatted data 1`] = `
{
"additionalInfo": null,
"address": {
"address_line1": "9000 Douglas Ave",
"address_line2": "",
"administrative_area": "IA",
"country_code": "US",
"langcode": "en",
"locality": "Urbandale",
},
"body": {
"format": "rich_text",
"processed": "<p>Pickleball Club</p>

<p>Meets Thursdays from 9 to 11 a.m. </p>

<p>Contact Kay Queck (515) 214-4578</p>",
"value": "<p>Pickleball Club</p>

<p>Meets Thursdays from 9 to 11 a.m.&nbsp;</p>

<p>Contact Kay Queck (515) 214-4578</p>
",
},
"breadcrumbs": [
{
"options": [],
"title": "Home",
"uri": "https://content-build-medc0xjkxm4jmpzxl3tfbcs7qcddsivh.ci.cms.va.gov/",
},
{
"options": [],
"title": "VA Central Iowa health care",
"uri": "https://content-build-medc0xjkxm4jmpzxl3tfbcs7qcddsivh.ci.cms.va.gov/central-iowa-health-care",
},
{
"options": [],
"title": "Events",
"uri": "https://content-build-medc0xjkxm4jmpzxl3tfbcs7qcddsivh.ci.cms.va.gov/central-iowa-health-care/events",
},
{
"options": [],
"title": "Pickleball Club",
"uri": "https://content-build-medc0xjkxm4jmpzxl3tfbcs7qcddsivh.ci.cms.va.gov/central-iowa-health-care/events/52265",
},
],
"cost": "Free",
"date": "2022-12-30T14:49:05+00:00",
"datetimeRange": [
{
"duration": 120,
"end_value": "2023-09-07T16:00:00+00:00",
"rrule": 180,
"rrule_index": 1,
"timezone": "America/Chicago",
"value": "2023-09-07T14:00:00+00:00",
},
{
"duration": 120,
"end_value": "2023-09-14T16:00:00+00:00",
"rrule": 180,
"rrule_index": 2,
"timezone": "America/Chicago",
"value": "2023-09-14T14:00:00+00:00",
},
],
"description": "Pickleball ",
"entityId": 52265,
"entityPath": "/central-iowa-health-care/events/52265",
"eventCTA": null,
"facilityLocation": null,
"id": "16349f16-be65-46e3-9660-1d3d598a4a0b",
"image": null,
"link": null,
"listing": "/central-iowa-health-care/events",
"locationHumanReadable": "Walker Johnston Park",
"locationType": "non_facility",
"metatags": null,
"published": true,
"socialLinks": {
"path": "/central-iowa-health-care/events/52265",
"title": "Pickleball Club",
},
"title": "Pickleball Club",
"type": "node--event",
"urlOfOnlineEvent": null,
}
`;
23 changes: 23 additions & 0 deletions src/data/queries/tests/event.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { NodeEvent } from '@/types/dataTypes/drupal/node'
import { queries } from '@/data/queries'
import mockData from '@/mocks/event.mock.json'

const nodeEventMock: NodeEvent = mockData

describe('node--event formatData', () => {
let windowSpy

beforeEach(() => {
windowSpy = jest.spyOn(window, 'window', 'get')
})

afterEach(() => {
windowSpy.mockRestore()
})

test('outputs formatted data', () => {
windowSpy.mockImplementation(() => undefined)
const formattedData = queries.formatData('node--event', nodeEventMock)
expect(formattedData).toMatchSnapshot()
})
})
1 change: 1 addition & 0 deletions src/lib/constants/resourceTypes.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export const RESOURCE_TYPES = {
STORY_LISTING: 'node--story_listing',
STORY: 'node--news_story',
EVENT: 'node--event',
// QA: 'node--q_a',
} as const

Expand Down
1 change: 0 additions & 1 deletion src/lib/drupal/staticProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@ export async function getStaticPropsResource(
context
)
}

// All others
return getDefaultStaticPropsResource(resourceType, pathInfo, context)
}
78 changes: 78 additions & 0 deletions src/lib/utils/date.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import {
parseSeconds,
parseDate,
getDateParts,
formatDateObject,
deriveMostRecentDate,
deriveFormattedTimestamp,
isEventInPast,
} from './date'

describe('isISOString', () => {
Expand Down Expand Up @@ -224,3 +228,77 @@ describe('getDateParts', () => {
})
})
})

describe('formatDateObject', () => {
it('should add startTime, endTime, value, and endValue to each date object', () => {
const datetimeRange = [
{
value: new Date('2023-09-07T14:00:00Z'),
end_value: new Date('2023-09-07T16:00:00Z'),
},
]
const result = formatDateObject(datetimeRange)
expect(result[0]).toHaveProperty('startTime')
expect(result[0]).toHaveProperty('endTime')
expect(result[0]).toHaveProperty('value')
expect(result[0]).toHaveProperty('endValue')
})
})

describe('deriveMostRecentDate', () => {
it('should return the closest future event', () => {
const mockCurrentTime = new Date('2023-08-01T12:00:00Z').getTime()
jest.spyOn(Date, 'now').mockImplementation(() => mockCurrentTime)

const datetimeRange = [
{ value: new Date('2023-07-01T14:00:00Z').getTime() / 1000 }, // Past event
{ value: new Date('2023-09-01T14:00:00Z').getTime() / 1000 }, // Closest future event
{ value: new Date('2023-10-01T14:00:00Z').getTime() / 1000 }, // Later future event
]

const closestEvent = deriveMostRecentDate(datetimeRange)
expect(closestEvent.value).toEqual(datetimeRange[1].value)

jest.restoreAllMocks()
})

it('should return the most recent past event if no future events', () => {
const mockCurrentTime = new Date('2023-12-01T12:00:00Z').getTime()
jest.spyOn(Date, 'now').mockImplementation(() => mockCurrentTime)

const datetimeRange = [
{ value: new Date('2023-07-01T14:00:00Z').getTime() / 1000 }, // Earlier past event
{ value: new Date('2023-11-01T14:00:00Z').getTime() / 1000 }, // Most recent past event
]

const closestEvent = deriveMostRecentDate(datetimeRange)
expect(closestEvent.value).toEqual(datetimeRange[1].value)

jest.restoreAllMocks()
})
})
describe('deriveFormattedTimestamp', () => {
it('should correctly format the event timestamp', () => {
const datetime = {
startTime: new Date('2023-09-07T14:00:00Z'),
endTime: new Date('2023-09-07T16:00:00Z'),
}
const result = deriveFormattedTimestamp(datetime)

expect(result).toMatch(
/Thu, Sep 7, 2023, \d{1,2}:\d{2} [APM]{2} [A-Z]{2,4} – \d{1,2}:\d{2} [APM]{2} [A-Z]{2,4}/
)
})
})

describe('isEventInPast', () => {
it('should return true for past events', () => {
const pastEventTime = new Date('2020-01-01').getTime() / 1000
expect(isEventInPast(pastEventTime)).toBeTruthy()
})

it('should return false for future events', () => {
const futureEventTime = new Date('2099-01-01').getTime() / 1000
expect(isEventInPast(futureEventTime)).toBeFalsy()
})
})
Loading
Loading