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

VACMS-16290 Event Listing #322

Merged
merged 29 commits into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
e194438
wip
jtmst Jan 4, 2024
33802e0
Adds SSG as CLI option (#318)
ryguyk Jan 3, 2024
4583ed7
fix unused vars in code scan alerts (#319)
tjheffner Jan 3, 2024
3de79ea
Fix preview (#320)
tjheffner Jan 3, 2024
44d6454
build the preview server output as part of CI (#321)
tjheffner Jan 4, 2024
5e3f12a
Bump eslint-plugin-testing-library from 6.1.0 to 6.2.0 (#324)
dependabot[bot] Jan 8, 2024
f6e03a3
Bump @department-of-veterans-affairs/component-library from 31.0.0 to…
dependabot[bot] Jan 8, 2024
e9d3017
Bump @typescript-eslint/eslint-plugin from 6.14.0 to 6.18.0 (#326)
dependabot[bot] Jan 8, 2024
3d01e7b
Bump @department-of-veterans-affairs/formation from 7.1.0 to 10.1.0 (…
dependabot[bot] Jan 8, 2024
a9082de
update event listing mock & tests, generator message for mocking
tjheffner Jan 8, 2024
624b4a9
update snapshot
tjheffner Jan 8, 2024
7c76a74
event listing + query test, stub event calls
tjheffner Jan 9, 2024
22e9c16
fix test errors
tjheffner Jan 9, 2024
3c44955
event listing layout looks pretty close
tjheffner Jan 9, 2024
049f045
event listing has events on it
tjheffner Jan 9, 2024
69f58c3
update event listing so lovell menus render properly
tjheffner Jan 9, 2024
506c363
Merge branch 'main' into 16290-event-listings
tjheffner Jan 9, 2024
66fe4fa
clean up types a bit. events are a lovell resource too
tjheffner Jan 10, 2024
db3aeb5
dont change api-explorer yet
tjheffner Jan 10, 2024
53f3c68
update test, snapshot
tjheffner Jan 10, 2024
1337823
add guard for menu
tjheffner Jan 10, 2024
3cf296d
conditional menu render on template page
tjheffner Jan 10, 2024
250b8dd
fix logic checks
tjheffner Jan 10, 2024
22e69b3
Merge branch 'main' into 16290-event-listings
timcosgrove Jan 10, 2024
85bd049
Merge branch 'main' into 16290-event-listings
tjheffner Jan 11, 2024
2542d71
Merge branch 'main' into 16290-event-listings
tjheffner Jan 12, 2024
3034187
Merge branch 'main' into 16290-event-listings
tjheffner Jan 22, 2024
70b8e38
extra check for event listing
tjheffner Jan 22, 2024
1e0e994
Merge branch 'main' into 16290-event-listings
tjheffner Jan 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions playwright/tests/eventListing.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('eventListing', () => {
test('Event Listing page renders with events that can be navigated to', async ({
page,
}) => {
await page.goto('/butler-health-care/events')
await page.locator('.events a').first().click()
await expect(page).toHaveURL(/\/butler-health-care\/events\//)
})

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

const accessibilityScanResults = await makeAxeBuilder().analyze()

expect(accessibilityScanResults.violations).toEqual([])
})
})
4 changes: 2 additions & 2 deletions plopfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ module.exports = function (plop) {
'You will need to do a few steps manually:',
'- Import & add your query to src/data/queries/index.ts',
'- Add your resource type to src/lib/constants/resourceTypes.ts',
'- Update the mock.json with correct data',
'- Update the mock.json with correct data. See src/pages/_playgroud/api-explorer.tsx',
'- Run `yarn test:u` to update test snapshots for your new query!',
],
})
Expand Down Expand Up @@ -107,7 +107,7 @@ module.exports = function (plop) {
'You will need to do a few steps manually:',
'- Import & add your query to src/data/queries/index.ts',
'- Add your resource type to src/lib/constants/resourceTypes.ts',
'- Update the mock.json with correct data',
'- Update the mock.json with correct data. See src/pages/_playgroud/api-explorer.tsx',
'- Run `yarn test:u` to update test snapshots for your new query!',
// Create react component + test files for new Page type.
{
Expand Down
105 changes: 105 additions & 0 deletions src/data/queries/eventListing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { QueryData, QueryFormatter, QueryParams } from 'next-drupal-query'
import { queries } from '.'
import { NodeEvent, NodeEventListing } from '@/types/drupal/node'
import { Menu } from '@/types/drupal/menu'
import { EventListing } from '@/types/formatted/eventListing'
import { RESOURCE_TYPES } from '@/lib/constants/resourceTypes'
import { PAGE_SIZES } from '@/lib/constants/pageSizes'
import { ExpandedStaticPropsContext } from '@/lib/drupal/staticProps'
import {
entityBaseFields,
fetchAndConcatAllResourceCollectionPages,
fetchSingleEntityOrPreview,
getMenu,
} from '@/lib/drupal/query'
import { buildSideNavDataFromMenu } from '@/lib/drupal/facilitySideNav'

const PAGE_SIZE = PAGE_SIZES[RESOURCE_TYPES.EVENT_LISTING]

// Define the query params for fetching node--event_listing.
export const params: QueryParams<null> = () => {
return queries.getParams().addInclude(['field_office'])
}

// Define the option types for the data loader.
export type EventListingDataOpts = {
id: string
context?: ExpandedStaticPropsContext
}

type EventListingData = {
entity: NodeEventListing
events: NodeEvent[]
menu?: Menu
totalItems: number
totalPages: number
}

const listingParams: QueryParams<string> = (listingEntityId: string) => {
return queries
.getParams('node--event--teaser')
.addFilter('field_listing.id', listingEntityId)
.addSort('-created')
}

// Implement the data loader.
export const data: QueryData<EventListingDataOpts, EventListingData> = async (
opts
) => {
const entity = (await fetchSingleEntityOrPreview(
opts,
RESOURCE_TYPES.EVENT_LISTING,
params
)) as NodeEventListing

// Fetch list of events related to this listing
const { data: events } =
await fetchAndConcatAllResourceCollectionPages<NodeEvent>(
RESOURCE_TYPES.EVENT,
listingParams(entity.id),
PAGE_SIZE
)

// Fetch the menu name dynamically off of the field_office reference if available.
// The `/outreach-and-events/events` event listing page has no menu attached to it.
let menu = null
if (entity.field_office.field_system_menu) {
menu = await getMenu(
entity.field_office.field_system_menu.resourceIdObjMeta
.drupal_internal__target_id
)
}

return {
entity,
events,
menu,
totalItems: events.length,
totalPages: 1, // We don't want to paginate event listing pages. The widget handles pagination.
}
}

export const formatter: QueryFormatter<EventListingData, EventListing> = ({
entity,
events,
menu,
totalItems,
totalPages,
}) => {
const formattedEvents = events.map((event) => {
return queries.formatData('node--event--teaser', event)
})

let formattedMenu = null
if (menu !== null)
formattedMenu = buildSideNavDataFromMenu(entity.path.alias, menu)

return {
...entityBaseFields(entity),
introText: entity.field_intro_text,
events: formattedEvents,
menu: formattedMenu,
totalItems,
totalPages,
}
}
97 changes: 97 additions & 0 deletions src/data/queries/eventTeaser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { QueryFormatter, QueryParams } from 'next-drupal-query'
import { queries } from '.'
import { NodeEvent } from '@/types/drupal/node'
import { EventWidgetTeaser } from '@/types/formatted/event'
import { formatDateObject } from '@/lib/utils/date'

// Define the query params for fetching node--event--teaser.
export const params: QueryParams<null> = () => {
return queries
.getParams()
.addInclude([
'field_listing',
'field_administration',
'field_facility_location',
])
}

// This formats Event nodes in the manner that the events widget from vets-website expects.
export const formatter: QueryFormatter<NodeEvent, EventWidgetTeaser> = (
entity: NodeEvent
) => {
const time = formatDateObject(entity.field_datetime_range_timezone)

return {
changed: entity.changed,
entityBundle: entity.type,
entityId: entity.id,
entityPublished: entity.status,
entityUrl: {
path: entity.path.alias,
},
fieldAdditionalInformationAbo: entity.field_additional_information_abo,
fieldAdditionalListings: null,
fieldAddress: {
addressLine1: entity.field_address?.address_line1 || null,
addressLine2: entity.field_address?.address_line2 || null,
administrativeArea: entity.field_address?.administrative_area || null,
countryCode: entity.field_address?.country_code || null,
locality: entity.field_address?.locality || null,
postalCode: entity.field_address?.postal_code || null,
},
fieldAdministration: {
entity: {
entityId: entity.field_administration.id,
},
},
fieldBody: entity.field_body,
fieldCtaEmail: entity.field_cta_email,
fieldDatetimeRangeTimezone: time,
fieldDescription: entity.field_description,
fieldEventCost: entity.field_event_cost,
fieldEventCta: entity.field_event_cta,
fieldEventRegistrationrequired: entity.field_event_registrationrequired,
fieldFacilityLocation: entity.field_facility_location
? {
entity: {
entityUrl: {
path: entity.field_facility_location.path?.alias || null,
},
fieldAddress: {
addressLine1:
entity.field_facility_location.field_address?.address_line1 ||
null,
addressLine2:
entity.field_facility_location.field_address?.address_line2 ||
null,
administrativeArea:
entity.field_facility_location.field_address
?.administrative_area || null,
countryCode:
entity.field_facility_location.field_address?.country_code ||
null,
locality:
entity.field_facility_location.field_address?.locality || null,
postalCode:
entity.field_facility_location.field_address?.postal_code ||
null,
},
title: entity.field_facility_location?.title || null,
},
}
: null,
fieldFeatured: entity.field_featured,
fieldHowToSignUp: entity.field_how_to_sign_up,
fieldLink: entity.field_link,
fieldListing: {
entity: {
entityId: entity.field_listing.id,
},
},
fieldLocationHumanreadable: entity.field_location_humanreadable,
fieldLocationType: entity.field_location_type,
fieldOrder: entity.field_order,
fieldUrlOfAnOnlineEvent: entity.field_url_of_an_online_event,
title: entity.title,
}
}
4 changes: 4 additions & 0 deletions src/data/queries/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import * as StaticPathResources from './staticPathResources'
import * as HeaderFooter from './headerFooter'
import * as PromoBlock from './promoBlock'
import * as Event from './event'
import * as EventTeaser from './eventTeaser'
import * as EventListing from './eventListing'
import { RESOURCE_TYPES } from '@/lib/constants/resourceTypes'

export const QUERIES_MAP = {
Expand All @@ -27,6 +29,8 @@ export const QUERIES_MAP = {
'node--story_listing': StoryListing,
'node--q_a': QuestionAnswer,
'node--event': Event,
'node--event--teaser': EventTeaser,
'node--event_listing': EventListing,
'node--person_profile': PersonProfile,
'node--landing_page': BenefitsHub, // "Benefits Hub Landing Page"
'paragraph--audience_topics': AudienceTopics,
Expand Down
2 changes: 1 addition & 1 deletion src/data/queries/storyListing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {

const PAGE_SIZE = PAGE_SIZES[RESOURCE_TYPES.STORY_LISTING]

// Define the query params for fetching node--news_story.
// Define the query params for fetching node--story_listing.
export const params: QueryParams<null> = () => {
return queries.getParams().addInclude(['field_office'])
}
Expand Down
Loading
Loading