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[]
+}