diff --git a/additional.d.ts b/additional.d.ts index dc6917c89..fb57d2930 100644 --- a/additional.d.ts +++ b/additional.d.ts @@ -22,14 +22,15 @@ declare module 'debug' declare namespace JSX { interface IntrinsicElements { - 'va-alert' - 'va-link' - 'va-icon' - 'va-button' - 'va-breadcrumbs' 'va-accordion' 'va-accordion-item' - 'va-on-this-page' + 'va-alert' 'va-back-to-top' + 'va-breadcrumbs' + 'va-button' + 'va-icon' + 'va-link' + 'va-link-action' + 'va-on-this-page' } } diff --git a/src/templates/common/relatedLinks/index.test.tsx b/src/templates/common/relatedLinks/index.test.tsx new file mode 100644 index 000000000..47ab1e375 --- /dev/null +++ b/src/templates/common/relatedLinks/index.test.tsx @@ -0,0 +1,64 @@ +import { render } from '@testing-library/react' +import { RelatedLinks } from '.' +import { FormattedRelatedLinks } from '@/types/formatted/relatedLinks' + +describe('RelatedLinks Component', () => { + test('renders the correct number of links when there are multiple', () => { + const relatedLinks: FormattedRelatedLinks = { + links: [ + { + uri: 'https://va.gov/burials-memorials/eligibility', + title: 'Eligibility for burial in a VA national cemetery', + summary: + 'Here is a summary for this URL so you can see how it displays underneath.', + }, + { + uri: 'https://va.gov/burials-memorials/schedule-a-burial', + title: 'Schedule a burial for a Veteran or family member', + summary: null, + }, + ], + sectionTitle: 'Related information', + } + + const { container } = render() + + expect(container.innerHTML).toContain( + 'Eligibility for burial in a VA national cemetery' + ) + expect(container.innerHTML).toContain( + 'href="https://va.gov/burials-memorials/eligibility"' + ) + expect(container.innerHTML).toContain( + 'Here is a summary for this URL so you can see how it displays underneath.' + ) + expect(container.innerHTML).toContain( + 'Schedule a burial for a Veteran or family member' + ) + expect(container.innerHTML).toContain( + 'href="https://va.gov/burials-memorials/schedule-a-burial"' + ) + }) + + test('renders link correctly when there is only one', () => { + const oneLink: FormattedRelatedLinks = { + links: [ + { + uri: 'https://va.gov/burials-memorials/schedule-a-burial', + title: 'Schedule a burial for a Veteran or family member', + summary: null, + }, + ], + sectionTitle: 'VA benefits', + } + + const { container } = render() + + expect(container.innerHTML).toContain( + 'Schedule a burial for a Veteran or family member' + ) + expect(container.innerHTML).toContain( + 'href="https://va.gov/burials-memorials/schedule-a-burial"' + ) + }) +}) diff --git a/src/templates/common/relatedLinks/index.tsx b/src/templates/common/relatedLinks/index.tsx new file mode 100644 index 000000000..ee8638df5 --- /dev/null +++ b/src/templates/common/relatedLinks/index.tsx @@ -0,0 +1,51 @@ +import { isEmpty } from 'lodash' +import { FormattedRelatedLinks } from '@/types/formatted/relatedLinks' + +// General component used for one or more links with a line of descriptive text underneath +// Does not map directly to any one Drupal type; it is simply a shared UI component +export const RelatedLinks = ({ + links, + sectionTitle, +}: FormattedRelatedLinks): JSX.Element => { + if (isEmpty(links)) { + return null + } + + let link + const renderLink = (uri, title, summary) => ( + <> +

+ + + +

+ {summary &&

{summary}

} + + ) + + if (links.length === 1) { + link = links[0] + } + + return ( +
+ {sectionTitle && ( +

+ {sectionTitle} +

+ )} + + {links.length > 1 && ( +
    + {links.map((link, index) => ( +
  • + {renderLink(link.uri, link.title, link?.summary)} +
  • + ))} +
+ )} + + {links.length === 1 && renderLink(link.uri, link.title, link?.summary)} +
+ ) +} diff --git a/src/templates/common/relatedLinks/relatedLinks.stories.ts b/src/templates/common/relatedLinks/relatedLinks.stories.ts new file mode 100644 index 000000000..fae1f25d3 --- /dev/null +++ b/src/templates/common/relatedLinks/relatedLinks.stories.ts @@ -0,0 +1,30 @@ +import { Meta, StoryObj } from '@storybook/react' + +import { RelatedLinks } from '.' + +const meta: Meta = { + title: 'Common/Related Links', + component: RelatedLinks, +} +export default meta + +type Story = StoryObj + +export const Example: Story = { + args: { + sectionTitle: 'VA benefits', + links: [ + { + uri: 'https://va.gov/burials-memorials/eligibility', + title: 'Eligibility for burial in a VA national cemetery', + summary: + 'Here is a summary for this URL so you can see how it displays underneath.', + }, + { + uri: 'https://va.gov/burials-memorials/schedule-a-burial', + title: 'Schedule a burial for a Veteran or family member', + summary: null, + }, + ], + }, +} diff --git a/src/templates/common/secondaryButtonGroup/index.test.tsx b/src/templates/common/secondaryButtonGroup/index.test.tsx new file mode 100644 index 000000000..b1889270e --- /dev/null +++ b/src/templates/common/secondaryButtonGroup/index.test.tsx @@ -0,0 +1,51 @@ +import { render } from '@testing-library/react' +import { SecondaryButtonGroup } from './' +import { Button as FormattedButton } from '@/types/formatted/button' +import { ParagraphComponent } from '@/types/formatted/paragraph' + +describe('SecondaryButtonGroup Component', () => { + test('renders action links correctly when there are multiple', () => { + const multipleButtons: ParagraphComponent[] = [ + { + id: '1', + label: 'Button one', + url: 'https://www.va.gov/button-one', + }, + { + id: '2', + label: 'Button two', + url: 'https://www.va.gov/button-two', + }, + ] + + const { container } = render( + + ) + + expect(container.innerHTML).toContain('Button one') + expect(container.innerHTML).toContain( + 'href="https://www.va.gov/button-one"' + ) + expect(container.innerHTML).toContain('Button two') + expect(container.innerHTML).toContain( + 'href="https://www.va.gov/button-two"' + ) + }) + + test('renders action link correctly when there is only one', () => { + const oneButton: ParagraphComponent[] = [ + { + id: '1', + label: 'Single button', + url: 'https://www.va.gov/single-button', + }, + ] + + const { container } = render() + + expect(container.innerHTML).toContain('Single button') + expect(container.innerHTML).toContain( + 'href="https://www.va.gov/single-button"' + ) + }) +}) diff --git a/src/templates/common/secondaryButtonGroup/index.tsx b/src/templates/common/secondaryButtonGroup/index.tsx new file mode 100644 index 000000000..d62eac734 --- /dev/null +++ b/src/templates/common/secondaryButtonGroup/index.tsx @@ -0,0 +1,35 @@ +import { Button as FormattedButton } from '@/types/formatted/button' +import { ParagraphComponent } from '@/types/formatted/paragraph' + +// Used for R&S pages; either a single blue CTA link or multiple +export const SecondaryButtonGroup = ({ + buttons, +}: { + buttons: ParagraphComponent[] +}): JSX.Element => { + if (buttons.length > 1) { + return ( +
    + {buttons?.map((button, index) => ( +
  • + +
  • + ))} +
+ ) + } + + const button = buttons[0] + + if (button) { + return ( + + ) + } + + return null +} diff --git a/src/templates/common/secondaryButtonGroup/secondaryButtonGroup.stories.ts b/src/templates/common/secondaryButtonGroup/secondaryButtonGroup.stories.ts new file mode 100644 index 000000000..d9a8a35d8 --- /dev/null +++ b/src/templates/common/secondaryButtonGroup/secondaryButtonGroup.stories.ts @@ -0,0 +1,30 @@ +import { Meta, StoryObj } from '@storybook/react' + +import { SecondaryButtonGroup } from '.' + +const meta: Meta = { + title: 'Common/Secondary Button Group', + component: SecondaryButtonGroup, +} +export default meta + +type Story = StoryObj + +export const Example: Story = { + args: { + buttons: [ + { + id: '1', + type: 'paragraph--button', + url: 'https://www.va.gov/careers-employment/vocational-rehabilitation/eligibility', + label: 'See eligibility for VR&E benefits', + }, + { + id: '2', + type: 'paragraph--button', + url: 'https://www.va.gov/careers-employment/vocational-rehabilitation/how-to-apply', + label: 'Find out how to apply for VR&E benefits', + }, + ], + }, +} diff --git a/src/types/formatted/relatedLinks.ts b/src/types/formatted/relatedLinks.ts new file mode 100644 index 000000000..724584d5d --- /dev/null +++ b/src/types/formatted/relatedLinks.ts @@ -0,0 +1,10 @@ +export type RelatedLink = { + title: string + uri: string + summary?: string +} + +export type FormattedRelatedLinks = { + sectionTitle: string + links: RelatedLink[] +}