diff --git a/docroot/profiles/custom/cgov_site/modules/custom/cgov_yaml_content/content/999_ncids_mini_landing_imageless_cards.content.yml b/docroot/profiles/custom/cgov_site/modules/custom/cgov_yaml_content/content/999_ncids_mini_landing_imageless_cards.content.yml index d3b1aacd2..9d6298f03 100644 --- a/docroot/profiles/custom/cgov_site/modules/custom/cgov_yaml_content/content/999_ncids_mini_landing_imageless_cards.content.yml +++ b/docroot/profiles/custom/cgov_site/modules/custom/cgov_yaml_content/content/999_ncids_mini_landing_imageless_cards.content.yml @@ -3,24 +3,37 @@ langcode: en status: 1 moderation_state: - value: 'published' + value: "published" title: "Key Initiatives" + title__ES: + value: "Spanish Key Initiatives" field_page_description: value: "Learn about major research initiatives NCI has developed to address specific issues of high priority for the cancer research community." + field_page_description__ES: + value: "(Spanish translation) Learn about major research initiatives NCI has developed to address specific issues of high priority for the cancer research community." field_browser_title: value: "Key Initiatives" + field_browser_title__ES: + value: "Spanish Key Initiatives" field_site_section: - - '#process': - callback: 'reference' + - "#process": + callback: "reference" args: - - 'taxonomy_term' - - vid: 'cgov_site_sections' - computed_path: '/research/key-initiatives' + - "taxonomy_term" + - vid: "cgov_site_sections" + computed_path: "/research/key-initiatives" + field_site_section__ES: + - "#process": + callback: "reference" + args: + - "taxonomy_term" + - vid: "cgov_site_sections" + computed_path: "/spanish/research/key-initiatives" field_mlp_page_style: value: "ncids_default" ### English Contents field_landing_contents: - ######## Begin Feature Row ########### + ######## Begin Feature Row ########### - entity: "paragraph" type: "ncids_page_title_block" - entity: "paragraph" @@ -38,19 +51,19 @@ value: |

NCI is supporting foundational research undertaken through the Cancer Moonshot and launching new initiatives to build on those successes with the aim of achieving bold new goals.

field_image_guide_card: - - '#process': - callback: 'file' + - "#process": + callback: "file" args: - - 'image' - - type: 'module' - filename: 'wide-guide-card-image.png' + - "image" + - type: "module" + filename: "wide-guide-card-image.png" field_three_link_buttons: - - entity: 'paragraph' - type: "ncids_link_button_external" - field_external_link: - - uri: 'https://www.cancer.gov/research/key-initiatives/moonshot-cancer-initiative/funding' - field_button_text: - - value: "See Funding Opportunities" + - entity: "paragraph" + type: "ncids_link_button_external" + field_external_link: + - uri: "https://www.cancer.gov/research/key-initiatives/moonshot-cancer-initiative/funding" + field_button_text: + - value: "See Funding Opportunities" - entity: "paragraph" type: "ncids_imageless_card_group" field_container_heading: @@ -98,32 +111,112 @@ - "node" - type: "cgov_mini_landing" title: "The RAS Initiative" + ### Spanish Contents + field_landing_contents__ES: + ######## Begin Feature Row ########### + - entity: "paragraph" + type: "ncids_page_title_block" + - entity: "paragraph" + type: "ncids_content_block" + field_html_content: + - format: "ncids_streamlined" + value: | +

(Spanish) NCI has worked to leverage its role as the leader of the National Cancer Program to expand our understanding of cancer and to translate new knowledge into better cancer prevention and treatment. Although the majority of NCI's funding supports investigator-initiated science, the institute also invests in major research initiatives to facilitate and support research on specific issues of importance to the cancer research enterprise.

+ - entity: "paragraph" + type: "ncids_wide_guide_card" + field_container_heading: + - value: "Cancer Moonshot" + field_html_content: + - format: "simple" + value: | +

(Spanish) NCI is supporting foundational research undertaken through the Cancer Moonshot and launching new initiatives to build on those successes with the aim of achieving bold new goals.

+ field_image_guide_card: + - "#process": + callback: "file" + args: + - "image" + - type: "module" + filename: "wide-guide-card-image.png" + field_three_link_buttons: + - entity: "paragraph" + type: "ncids_link_button_external" + field_external_link: + - uri: "https://www.cancer.gov/research/key-initiatives/moonshot-cancer-initiative/funding" + field_button_text: + - value: "(Spanish) See Funding Opportunities" + - entity: "paragraph" + type: "ncids_imageless_card_group" + field_container_heading: + - value: "(Spanish) The Optional Imageless Card Group Heading" + field_heading_visibility: + value: sr-only + field_card_layout_display: + - value: cgdp-imageless-card-group--two-card + field_row_cards_unlimited: + - entity: "paragraph" + type: "ncids_imageless_card_external" + field_override_card_title: + - value: "(Spanish) NCI Equity and Inclusion Program" + field_override_card_description: + - value: "(Spanish) Learn how NCI will help end structural racism in biomedical research and why the program was launched." + field_featured_url: + - uri: "https://www.cancer.gov/research/key-initiatives/nci-equity-inclusion-program" + - entity: "paragraph" + type: "ncids_imageless_card_external" + field_override_card_title: + - value: "(Spanish) NCI COVID-19 Research Initiatives" + field_override_card_description: + - value: "(Spanish) NCI is mobilizing its experts and resources to study COVID-19." + field_featured_url: + - uri: "https://www.cancer.gov/research/key-initiatives/covid-19" + - entity: "paragraph" + type: "ncids_imageless_card_external" + field_override_card_title: + - value: "(Spanish) Childhood Cancer Data Initiative (CCDI)" + field_override_card_description: + - value: "(Spanish) CCDI addresses the critical need to collect, analyze, and share childhood cancer data." + field_featured_url: + - uri: "https://www.cancer.gov/research/areas/childhood/childhood-cancer-data-initiative" + - entity: "paragraph" + type: "ncids_imageless_card_internal" + field_override_card_title: + - value: "(Spanish) The RAS Initiative" + field_override_card_description: + - value: "(Spanish) NCI's RAS Initiative explores innovative approaches for attacking the proteins encoded by mutant forms of the KRAS gene. " + field_featured_item: + - target_type: "node" + "#process": + callback: "reference" + args: + - "node" + - type: "cgov_mini_landing" + title: "The RAS Initiative" - entity: "node" type: "cgov_mini_landing" langcode: en status: 1 moderation_state: - value: 'published' + value: "published" title: "NCIDS Imageless Card Test Page" field_page_description: value: "NCIDS Imageless Card Test Page - Page Description" field_browser_title: value: "NCIDS Imageless Card Test Page" field_site_section: - - '#process': - callback: 'reference' + - "#process": + callback: "reference" args: - - 'taxonomy_term' - - vid: 'cgov_site_sections' - computed_path: '/test' + - "taxonomy_term" + - vid: "cgov_site_sections" + computed_path: "/test" field_pretty_url: value: "ncids-imageless-card-test" field_mlp_page_style: value: "ncids_default" ### English Contents field_landing_contents: - ######## Begin Feature Row ########### + ######## Begin Feature Row ########### - entity: "paragraph" type: "ncids_content_block" field_html_content: @@ -228,36 +321,36 @@ field_heading_visibility: value: sr-only field_row_cards: - - entity: 'paragraph' + - entity: "paragraph" type: "ncids_feature_card_internal" field_featured_item: - target_type: "node" "#process": callback: "reference" args: - - 'node' - - type: 'cgov_home_landing' - title: 'Cancer Research' - - entity: 'paragraph' + - "node" + - type: "cgov_home_landing" + title: "Cancer Research" + - entity: "paragraph" type: "ncids_feature_card_internal" field_featured_item: - target_type: "node" "#process": callback: "reference" args: - - 'node' - - type: 'cgov_home_landing' - title: 'Grants & Training' - - entity: 'paragraph' + - "node" + - type: "cgov_home_landing" + title: "Grants & Training" + - entity: "paragraph" type: "ncids_feature_card_internal" field_featured_item: - target_type: "node" "#process": callback: "reference" args: - - 'node' - - type: 'cgov_home_landing' - title: 'News & Events' + - "node" + - type: "cgov_home_landing" + title: "News & Events" - entity: "paragraph" type: "ncids_imageless_card_group" field_container_heading: diff --git a/docroot/profiles/custom/cgov_site/themes/custom/ncids_trans/front-end/src/entrypoints/ncids-mini-landing/ncids-mini-landing.ts b/docroot/profiles/custom/cgov_site/themes/custom/ncids_trans/front-end/src/entrypoints/ncids-mini-landing/ncids-mini-landing.ts index 5a2e77536..4f58f8819 100644 --- a/docroot/profiles/custom/cgov_site/themes/custom/ncids_trans/front-end/src/entrypoints/ncids-mini-landing/ncids-mini-landing.ts +++ b/docroot/profiles/custom/cgov_site/themes/custom/ncids_trans/front-end/src/entrypoints/ncids-mini-landing/ncids-mini-landing.ts @@ -10,6 +10,7 @@ import cgdpListInit from '../../lib/components/cgdp-list'; import cgdpSummaryBox from '../../lib/components/cgdp-summary-box'; import cgdpFlagCardGroupInit from '../../lib/components/cgdp-flag-card-group'; import cgdpWideGuideCard from '../../lib/components/cgdp-wide-guide-card'; +import cgdpImagelessCardRow from '../../lib/components/cgdp-imageless-card-group'; const onDOMContentLoaded = () => { // Init feature cards @@ -32,6 +33,8 @@ const onDOMContentLoaded = () => { cgdpFlagCardGroupInit(); // Init the wide guide card cgdpWideGuideCard(); + // Init the imageless card row + cgdpImagelessCardRow(); }; window.addEventListener('DOMContentLoaded', onDOMContentLoaded); diff --git a/docroot/profiles/custom/cgov_site/themes/custom/ncids_trans/front-end/src/lib/components/cgdp-imageless-card-group/__tests__/cgdp-imageless-card-group.bad.dom.ts b/docroot/profiles/custom/cgov_site/themes/custom/ncids_trans/front-end/src/lib/components/cgdp-imageless-card-group/__tests__/cgdp-imageless-card-group.bad.dom.ts new file mode 100644 index 000000000..aadcd05b6 --- /dev/null +++ b/docroot/profiles/custom/cgov_site/themes/custom/ncids_trans/front-end/src/lib/components/cgdp-imageless-card-group/__tests__/cgdp-imageless-card-group.bad.dom.ts @@ -0,0 +1,40 @@ +export const cgdpImagelessCardRowBadDom = () => { + const div = document.createElement('div'); + + // language=HTML + div.innerHTML = ` +
+
+

+ +
+
+ `; + + return div; +}; diff --git a/docroot/profiles/custom/cgov_site/themes/custom/ncids_trans/front-end/src/lib/components/cgdp-imageless-card-group/__tests__/cgdp-imageless-card-group.dom.ts b/docroot/profiles/custom/cgov_site/themes/custom/ncids_trans/front-end/src/lib/components/cgdp-imageless-card-group/__tests__/cgdp-imageless-card-group.dom.ts new file mode 100644 index 000000000..9d9b3f3b0 --- /dev/null +++ b/docroot/profiles/custom/cgov_site/themes/custom/ncids_trans/front-end/src/lib/components/cgdp-imageless-card-group/__tests__/cgdp-imageless-card-group.dom.ts @@ -0,0 +1,40 @@ +export const cgdpImagelessCardRowDom = () => { + const div = document.createElement('div'); + + // language=HTML + div.innerHTML = ` +
+
+

The Optional Imageless Card Group Heading - two Card

+ +
+
+ `; + + return div; +}; diff --git a/docroot/profiles/custom/cgov_site/themes/custom/ncids_trans/front-end/src/lib/components/cgdp-imageless-card-group/__tests__/cgdp-imageless-card-group.test.ts b/docroot/profiles/custom/cgov_site/themes/custom/ncids_trans/front-end/src/lib/components/cgdp-imageless-card-group/__tests__/cgdp-imageless-card-group.test.ts new file mode 100644 index 000000000..473763f67 --- /dev/null +++ b/docroot/profiles/custom/cgov_site/themes/custom/ncids_trans/front-end/src/lib/components/cgdp-imageless-card-group/__tests__/cgdp-imageless-card-group.test.ts @@ -0,0 +1,218 @@ +import '@testing-library/jest-dom'; +import '@testing-library/jest-dom/extend-expect'; + +import { fireEvent, screen } from '@testing-library/dom'; + +import * as eddlUtil from '../../../core/analytics/eddl-util'; + +import cgdpImagelessCardInit from '../cgdp-imageless-card-group'; +import { cgdpImagelessCardRowDom } from './cgdp-imageless-card-group.dom'; +import { cgdpImagelessCardRowBadDom } from './cgdp-imageless-card-group.bad.dom'; + +jest.mock('../../../core/analytics/eddl-util'); + +describe('CGDP Imageless Cards', () => { + beforeEach(() => { + window.print = jest.fn(); + document.head.insertAdjacentHTML( + 'beforeend', + ` + + + ` + ); + }); + afterEach(() => { + // Hack to clean out the dom. + document.getElementsByTagName('body')[0].innerHTML = ''; + document.head.innerHTML = ''; + jest.resetAllMocks(); + }); + + it('does not blow up when cards do not exist', () => { + const dom = `
`; + + // Lets make a spy to ensure that trackOther is called correctly + const spy = jest.spyOn(eddlUtil, 'trackOther'); + + // Inject the HTML into the dom. + document.body.insertAdjacentHTML('beforeend', dom); + + // Create the JS + cgdpImagelessCardInit(); + + expect(spy).not.toHaveBeenCalled(); + }); + + it('external imageless card', () => { + const dom = cgdpImagelessCardRowDom(); + + // Lets make a spy to ensure that trackOther is called correctly + const trackOtherSpy = jest.spyOn(eddlUtil, 'trackOther'); + + // Inject the HTML into the dom. + document.body.insertAdjacentHTML('beforeend', dom.outerHTML); + + // Create the JS + cgdpImagelessCardInit(); + + const card = screen.getAllByRole('link'); + + fireEvent.click(card[0]); + + expect(trackOtherSpy).toHaveBeenCalledWith( + 'MLP:ImagelessCard:LinkClick', + 'MLP:ImagelessCard:LinkClick', + { + location: 'Body', + pageType: 'cgvMiniLanding', + pageTemplate: 'ncids_default', + pageRowVariant: 'Not Defined', + pageRows: 1, + pageRowIndex: 1, + pageRowCols: 0, + pageRowColIndex: 0, + containerItems: 3, + containerItemIndex: 1, + componentType: 'Imageless Card', + componentTheme: 'Not Defined', + componentVariant: 'Not Defined', + title: 'The Optional Imageless Card Group Heading - two Ca', + linkType: 'External', + linkText: 'Not Defined', + linkArea: 'Not Defined', + totalLinks: 1, + linkPosition: 1, + } + ); + }); + + it('internal imageless card', () => { + const dom = cgdpImagelessCardRowDom(); + + // Lets make a spy to ensure that trackOther is called correctly + const trackOtherSpy = jest.spyOn(eddlUtil, 'trackOther'); + + // Inject the HTML into the dom. + document.body.insertAdjacentHTML('beforeend', dom.outerHTML); + + // Create the JS + cgdpImagelessCardInit(); + + const card = screen.getAllByRole('link'); + + fireEvent.click(card[1]); + + expect(trackOtherSpy).toHaveBeenCalledWith( + 'MLP:ImagelessCard:LinkClick', + 'MLP:ImagelessCard:LinkClick', + { + location: 'Body', + pageType: 'cgvMiniLanding', + pageTemplate: 'ncids_default', + pageRowVariant: 'Not Defined', + pageRows: 1, + pageRowIndex: 1, + pageRowCols: 0, + pageRowColIndex: 0, + containerItems: 3, + containerItemIndex: 2, + componentType: 'Imageless Card', + componentTheme: 'Not Defined', + componentVariant: 'Not Defined', + title: 'The Optional Imageless Card Group Heading - two Ca', + linkType: 'Internal', + linkText: 'Not Defined', + linkArea: 'Not Defined', + totalLinks: 1, + linkPosition: 1, + } + ); + }); + + it('multimedia imageless card', () => { + const dom = cgdpImagelessCardRowDom(); + + // Lets make a spy to ensure that trackOther is called correctly + const trackOtherSpy = jest.spyOn(eddlUtil, 'trackOther'); + + // Inject the HTML into the dom. + document.body.insertAdjacentHTML('beforeend', dom.outerHTML); + + // Create the JS + cgdpImagelessCardInit(); + + const card = screen.getAllByRole('link'); + + fireEvent.click(card[2]); + + expect(trackOtherSpy).toHaveBeenCalledWith( + 'MLP:ImagelessCard:LinkClick', + 'MLP:ImagelessCard:LinkClick', + { + location: 'Body', + pageType: 'cgvMiniLanding', + pageTemplate: 'ncids_default', + pageRowVariant: 'Not Defined', + pageRows: 1, + pageRowIndex: 1, + pageRowCols: 0, + pageRowColIndex: 0, + containerItems: 3, + containerItemIndex: 3, + componentType: 'Imageless Card', + componentTheme: 'Not Defined', + componentVariant: 'Not Defined', + title: 'The Optional Imageless Card Group Heading - two Ca', + linkType: 'Media', + linkText: 'Not Defined', + linkArea: 'Not Defined', + totalLinks: 1, + linkPosition: 1, + } + ); + }); + + it('bad DOM imageless card', () => { + const dom = cgdpImagelessCardRowBadDom(); + + // Lets make a spy to ensure that trackOther is called correctly + const trackOtherSpy = jest.spyOn(eddlUtil, 'trackOther'); + + // Inject the HTML into the dom. + document.body.insertAdjacentHTML('beforeend', dom.outerHTML); + + // Create the JS + cgdpImagelessCardInit(); + + const card = screen.getAllByRole('link'); + + fireEvent.click(card[2]); + + expect(trackOtherSpy).toHaveBeenCalledWith( + 'MLP:ImagelessCard:LinkClick', + 'MLP:ImagelessCard:LinkClick', + { + location: 'Body', + pageType: 'cgvMiniLanding', + pageTemplate: 'ncids_default', + pageRowVariant: 'Not Defined', + pageRows: 0, + pageRowIndex: '_ERROR_', + pageRowCols: 0, + pageRowColIndex: 0, + containerItems: 0, + containerItemIndex: 0, + componentType: 'Imageless Card', + componentTheme: 'Not Defined', + componentVariant: 'Not Defined', + title: 'Not Defined', + linkType: '_ERROR_', + linkText: 'Not Defined', + linkArea: 'Not Defined', + totalLinks: 1, + linkPosition: 1, + } + ); + }); +}); diff --git a/docroot/profiles/custom/cgov_site/themes/custom/ncids_trans/front-end/src/lib/components/cgdp-imageless-card-group/cgdp-imageless-card-group.ts b/docroot/profiles/custom/cgov_site/themes/custom/ncids_trans/front-end/src/lib/components/cgdp-imageless-card-group/cgdp-imageless-card-group.ts new file mode 100644 index 000000000..e7624b9a0 --- /dev/null +++ b/docroot/profiles/custom/cgov_site/themes/custom/ncids_trans/front-end/src/lib/components/cgdp-imageless-card-group/cgdp-imageless-card-group.ts @@ -0,0 +1,122 @@ +import { landingClickTracker } from '../../core/analytics/landing-page-contents-helper'; + +/** + * Gets the title of the imageless card group. + * @param {HTMLElement} link - selected card. + */ +const getTitle = (link: HTMLElement): string => { + const parentSection = link.closest('.usa-section'); + const title = parentSection?.querySelector( + '.cgdp-imageless-card-group__heading' + )?.textContent; + return title?.trim() || 'Not Defined'; +}; + +/** + * Gets text content by selector. + * @param {HTMLElement} link - Selected card. + * @param {string} selector - Selector for query. + */ +const getText = (link: HTMLElement, selector: string): string => { + const element = link.querySelector(selector); + const text = element?.textContent?.trim(); + + return text || '_ERROR_'; +}; + +/** + * Gets the exact location clicked that triggered the event. + * @param {Event} evt - Click event + */ +const getLinkArea = (evt: Event): string => { + const link = evt.target as HTMLElement; + const tag = link.tagName; + + const tags: { [key: string]: string } = { + P: 'Description', + SPAN: 'Title', + }; + + return tags[tag] || 'Not Defined'; +}; + +/** + * Gets text content of the exact link area. + * @param {Event} evt - Click event + */ +const getLinkText = (evt: Event): string => { + const link = evt.currentTarget as HTMLElement; + const linkArea = getLinkArea(evt); + + const textOptions: { [key: string]: string } = { + Image: 'Image', + Description: getText(link, '.nci-card__description'), + Title: getText(link, '.nci-card__title'), + }; + + return textOptions[linkArea] || 'Not Defined'; +}; + +/** + * Feature card on click handler. + * @param {number} containerItems - Number of cards in row. + * @param {number} containerItemIndex - Index of card selected in row. + */ +const imagelessCardLinkClickHandler = + (containerItems: number, containerItemIndex: number | '_ERROR_') => + (evt: Event): void => { + const link = evt.currentTarget as HTMLElement; + + landingClickTracker( + link, + 'ImagelessCard', // linkName + containerItems, // containerItems + containerItemIndex, // containerItemsIndex + 'Imageless Card', // componentType + 'Not Defined', // componentTheme + 'Not Defined', // componentVariant + getTitle(link), // title + link.dataset.eddlLandingItemLinkType || '_ERROR_', // linkType + getLinkText(evt), // linkText + getLinkArea(evt), // linkArea + 1, // totalLinks + 1 // linkPosition + ); + }; + +/** + * Gathers data to pass through to event listener. + * @param {HTMLElement} card - The Feature Card element. + */ +const imagelessCardHelper = (card: HTMLElement): void => { + const parentRowContainer = card.closest('[data-eddl-landing-row]'); + const allCards = parentRowContainer + ? Array.from( + parentRowContainer.querySelectorAll('[data-eddl-landing-item]') + ) + : []; + const containerItemIndex = allCards.indexOf(card) + 1; + const containerItems = allCards.length; + + card.addEventListener( + 'click', + imagelessCardLinkClickHandler(containerItems, containerItemIndex) + ); +}; + +/** + * Wire up component per cgdp requirements. + */ +const initialize = (): void => { + const imagelessCardEls = document.querySelectorAll( + '[data-eddl-landing-item="imageless_card"]' + ); + if (!imagelessCardEls.length) return; + + imagelessCardEls.forEach((imagelessCardEl) => { + const imagelessCard = imagelessCardEl as HTMLElement; + imagelessCardHelper(imagelessCard); + }); +}; + +export default initialize; diff --git a/docroot/profiles/custom/cgov_site/themes/custom/ncids_trans/front-end/src/lib/components/cgdp-imageless-card-group/index.ts b/docroot/profiles/custom/cgov_site/themes/custom/ncids_trans/front-end/src/lib/components/cgdp-imageless-card-group/index.ts new file mode 100644 index 000000000..879d9677d --- /dev/null +++ b/docroot/profiles/custom/cgov_site/themes/custom/ncids_trans/front-end/src/lib/components/cgdp-imageless-card-group/index.ts @@ -0,0 +1,2 @@ +import initialize from './cgdp-imageless-card-group'; +export default initialize;