From 0fef2a4be64fdcb1fc0250de6e013ce4d3f03cf3 Mon Sep 17 00:00:00 2001
From: The Stedman <46408716+recondesigns@users.noreply.github.com>
Date: Mon, 4 Mar 2024 20:57:47 -0600
Subject: [PATCH] Migrate Footer, Heading, DonateSection, HeroBanner, and Modal
components to Typescript (#1805)
---
components/Footer/{Footer.js => Footer.tsx} | 17 +++++-
.../{Footer.test.js => Footer.test.tsx} | 0
...oter.test.js.snap => Footer.test.tsx.snap} | 0
.../Heading/{Heading.js => Heading.tsx} | 48 ++++++++++-----
.../Heading/__stories__/Heading.stories.js | 22 -------
.../Heading/__stories__/Heading.stories.tsx | 18 ++++++
.../{Heading.test.js => Heading.test.tsx} | 0
...ing.test.js.snap => Heading.test.tsx.snap} | 0
.../{HeroBanner.js => HeroBanner.tsx} | 44 +++++++++-----
.../__stories__/HeroBanner.stories.js | 14 -----
.../__stories__/HeroBanner.stories.tsx | 18 ++++++
...HeroBanner.test.js => HeroBanner.test.tsx} | 0
....test.js.snap => HeroBanner.test.tsx.snap} | 0
components/Modal/{Modal.js => Modal.tsx} | 57 +++++++++++-------
.../{Modal.stories.js => Modal.stories.tsx} | 60 ++++++++-----------
.../{Modal.test.js => Modal.test.tsx} | 2 +-
...Modal.test.js.snap => Modal.test.tsx.snap} | 0
.../{DonateSection.js => DonateSection.tsx} | 0
.../__stories__/DonateSection.stories.js | 10 ----
.../__stories__/DonateSection.stories.tsx | 16 +++++
...Section.test.js => DonateSection.test.tsx} | 0
...st.js.snap => DonateSection.test.tsx.snap} | 2 -
22 files changed, 192 insertions(+), 136 deletions(-)
rename components/Footer/{Footer.js => Footer.tsx} (90%)
rename components/Footer/__tests__/{Footer.test.js => Footer.test.tsx} (100%)
rename components/Footer/__tests__/__snapshots__/{Footer.test.js.snap => Footer.test.tsx.snap} (100%)
rename components/Heading/{Heading.js => Heading.tsx} (56%)
delete mode 100644 components/Heading/__stories__/Heading.stories.js
create mode 100644 components/Heading/__stories__/Heading.stories.tsx
rename components/Heading/__tests__/{Heading.test.js => Heading.test.tsx} (100%)
rename components/Heading/__tests__/__snapshots__/{Heading.test.js.snap => Heading.test.tsx.snap} (100%)
rename components/HeroBanner/{HeroBanner.js => HeroBanner.tsx} (51%)
delete mode 100644 components/HeroBanner/__stories__/HeroBanner.stories.js
create mode 100644 components/HeroBanner/__stories__/HeroBanner.stories.tsx
rename components/HeroBanner/__tests__/{HeroBanner.test.js => HeroBanner.test.tsx} (100%)
rename components/HeroBanner/__tests__/__snapshots__/{HeroBanner.test.js.snap => HeroBanner.test.tsx.snap} (100%)
rename components/Modal/{Modal.js => Modal.tsx} (67%)
rename components/Modal/__stories__/{Modal.stories.js => Modal.stories.tsx} (71%)
rename components/Modal/__tests__/{Modal.test.js => Modal.test.tsx} (93%)
rename components/Modal/__tests__/__snapshots__/{Modal.test.js.snap => Modal.test.tsx.snap} (100%)
rename components/ReusableSections/DonateSection/{DonateSection.js => DonateSection.tsx} (100%)
delete mode 100644 components/ReusableSections/DonateSection/__stories__/DonateSection.stories.js
create mode 100644 components/ReusableSections/DonateSection/__stories__/DonateSection.stories.tsx
rename components/ReusableSections/DonateSection/__tests__/{DonateSection.test.js => DonateSection.test.tsx} (100%)
rename components/ReusableSections/DonateSection/__tests__/__snapshots__/{DonateSection.test.js.snap => DonateSection.test.tsx.snap} (93%)
diff --git a/components/Footer/Footer.js b/components/Footer/Footer.tsx
similarity index 90%
rename from components/Footer/Footer.js
rename to components/Footer/Footer.tsx
index a21be88f0..ccd316239 100644
--- a/components/Footer/Footer.js
+++ b/components/Footer/Footer.tsx
@@ -7,11 +7,26 @@ import Image from 'next/image';
import Logo from 'public/static/images/logo.svg';
import styles from './Footer.module.css';
+export type FooterPropsType = {
+ /**
+ * Url string applied ot the link.
+ */
+ href: string;
+ /**
+ * String applied to the link label.
+ */
+ name: string;
+ /**
+ * Only pass analytics event label if you're href is to an external website
+ */
+ analyticsEventLabel?: string;
+};
+
function Footer() {
const currentYear = new Date().getFullYear();
// eslint-disable-next-line react/prop-types
- const renderLink = ({ href, name, analyticsEventLabel }) => {
+ const renderLink = ({ href, name, analyticsEventLabel }: FooterPropsType) => {
return (
{analyticsEventLabel ? (
diff --git a/components/Footer/__tests__/Footer.test.js b/components/Footer/__tests__/Footer.test.tsx
similarity index 100%
rename from components/Footer/__tests__/Footer.test.js
rename to components/Footer/__tests__/Footer.test.tsx
diff --git a/components/Footer/__tests__/__snapshots__/Footer.test.js.snap b/components/Footer/__tests__/__snapshots__/Footer.test.tsx.snap
similarity index 100%
rename from components/Footer/__tests__/__snapshots__/Footer.test.js.snap
rename to components/Footer/__tests__/__snapshots__/Footer.test.tsx.snap
diff --git a/components/Heading/Heading.js b/components/Heading/Heading.tsx
similarity index 56%
rename from components/Heading/Heading.js
rename to components/Heading/Heading.tsx
index 2f0623a65..2c6aa8953 100644
--- a/components/Heading/Heading.js
+++ b/components/Heading/Heading.tsx
@@ -1,28 +1,46 @@
-import { string, bool, oneOf } from 'prop-types';
import classNames from 'classnames';
import kebabCase from 'lodash/kebabCase';
import ScreenReaderOnly from 'components/ScreenReaderOnly/ScreenReaderOnly';
import LinkIcon from 'static/images/icons/FontAwesome/link-solid.svg';
import styles from './Heading.module.css';
-Heading.propTypes = {
- className: string,
- hasHashLink: bool,
- hasTitleUnderline: bool,
- headingLevel: oneOf([1, 2, 3, 4, 5, 6]),
- text: string.isRequired,
-};
+type HeadingLevelType = 1 | 2 | 3 | 4 | 5 | 6;
-Heading.defaultProps = {
- className: undefined,
- hasHashLink: true,
- hasTitleUnderline: false,
- headingLevel: 2,
+export type HeadingPropsType = {
+ /**
+ * Text to be rendered in the heading element.
+ */
+ text: string;
+ /**
+ * Applies classnames to the base `figure` element for styling.
+ */
+ className?: string;
+ /**
+ * Applies an anchor as the base element if true.
+ * @default true
+ */
+ hasHashLink?: boolean;
+ /**
+ * Displays an optional line under the title.
+ * @default false
+ */
+ hasTitleUnderline?: boolean;
+ /**
+ * Sets the heading level (h1, h2, etc)
+ * @default 2
+ */
+ headingLevel?: HeadingLevelType;
};
-function Heading({ className, hasHashLink, hasTitleUnderline, headingLevel, text }) {
+function Heading({
+ className,
+ hasHashLink = true,
+ hasTitleUnderline = false,
+ headingLevel = 2,
+ text,
+}: HeadingPropsType) {
const anchorId = `${kebabCase(text)}-link`;
- const HeadingElement = `h${headingLevel}`;
+ const HeadingElement = `h${headingLevel}` as keyof JSX.IntrinsicElements;
return (
diff --git a/components/Heading/__stories__/Heading.stories.js b/components/Heading/__stories__/Heading.stories.js
deleted file mode 100644
index fad03c73d..000000000
--- a/components/Heading/__stories__/Heading.stories.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import Heading from '../Heading';
-
-export default {
- component: Heading,
- title: 'Heading',
- argTypes: {
- headingLevel: {
- control: {
- type: 'number',
- min: 1,
- max: 6,
- },
- },
- },
-};
-
-const Template = arguments_ => ;
-
-export const Default = Template.bind({});
-Default.args = {
- text: `Heading Text`,
-};
diff --git a/components/Heading/__stories__/Heading.stories.tsx b/components/Heading/__stories__/Heading.stories.tsx
new file mode 100644
index 000000000..2d105b5ed
--- /dev/null
+++ b/components/Heading/__stories__/Heading.stories.tsx
@@ -0,0 +1,18 @@
+import { Meta, StoryObj } from '@storybook/react';
+import Heading from '../Heading';
+
+type HeadingStoryType = StoryObj;
+
+const meta: Meta = {
+ title: 'Heading',
+ component: Heading,
+ args: {
+ text: 'Heading text',
+ },
+};
+
+export default meta;
+
+export const Default: HeadingStoryType = {
+ render: args => ,
+};
diff --git a/components/Heading/__tests__/Heading.test.js b/components/Heading/__tests__/Heading.test.tsx
similarity index 100%
rename from components/Heading/__tests__/Heading.test.js
rename to components/Heading/__tests__/Heading.test.tsx
diff --git a/components/Heading/__tests__/__snapshots__/Heading.test.js.snap b/components/Heading/__tests__/__snapshots__/Heading.test.tsx.snap
similarity index 100%
rename from components/Heading/__tests__/__snapshots__/Heading.test.js.snap
rename to components/Heading/__tests__/__snapshots__/Heading.test.tsx.snap
diff --git a/components/HeroBanner/HeroBanner.js b/components/HeroBanner/HeroBanner.tsx
similarity index 51%
rename from components/HeroBanner/HeroBanner.js
rename to components/HeroBanner/HeroBanner.tsx
index a7405b927..37df4ab15 100644
--- a/components/HeroBanner/HeroBanner.js
+++ b/components/HeroBanner/HeroBanner.tsx
@@ -1,24 +1,38 @@
-import { string, node, bool } from 'prop-types';
import classNames from 'classnames';
import Container from 'components/Container/Container';
import { HERO_BANNER_H1 } from 'common/constants/testIDs';
-HeroBanner.propTypes = {
- backgroundImageSource: string,
- className: string,
- children: node,
- isFullViewportHeight: bool,
- title: string.isRequired,
+export type HeroBannerPropsType = {
+ /**
+ * Renders a title for the banner.
+ */
+ title: string;
+ /**
+ * Sets the path for an optional background image.
+ */
+ backgroundImageSource?: string;
+ /**
+ * Applies classnames to the base `figure` element for styling.
+ */
+ className?: string;
+ /**
+ * Content to be rendered in the Container.
+ */
+ children?: React.ReactNode;
+ /**
+ * Sets the height of the container to be full viewport height.
+ * @default false
+ */
+ isFullViewportHeight?: boolean;
};
-HeroBanner.defaultProps = {
- backgroundImageSource: '',
- className: undefined,
- children: undefined,
- isFullViewportHeight: false,
-};
-
-function HeroBanner({ backgroundImageSource, children, className, isFullViewportHeight, title }) {
+function HeroBanner({
+ backgroundImageSource,
+ children,
+ className,
+ isFullViewportHeight = false,
+ title,
+}: HeroBannerPropsType) {
return (
;
-
-// Default HeroBanner supplied with only required args
-export const Default = Template.bind({});
-Default.args = {
- title: 'Banner Title',
-};
diff --git a/components/HeroBanner/__stories__/HeroBanner.stories.tsx b/components/HeroBanner/__stories__/HeroBanner.stories.tsx
new file mode 100644
index 000000000..470dcf8a8
--- /dev/null
+++ b/components/HeroBanner/__stories__/HeroBanner.stories.tsx
@@ -0,0 +1,18 @@
+import { Meta, StoryObj } from '@storybook/react';
+import HeroBanner from '../HeroBanner';
+
+type HeroBannerStoryType = StoryObj;
+
+const meta: Meta = {
+ title: 'HeroBanner',
+ component: HeroBanner,
+ args: {
+ title: 'Banner Title',
+ },
+};
+
+export default meta;
+
+export const Default: HeroBannerStoryType = {
+ render: args => ,
+};
diff --git a/components/HeroBanner/__tests__/HeroBanner.test.js b/components/HeroBanner/__tests__/HeroBanner.test.tsx
similarity index 100%
rename from components/HeroBanner/__tests__/HeroBanner.test.js
rename to components/HeroBanner/__tests__/HeroBanner.test.tsx
diff --git a/components/HeroBanner/__tests__/__snapshots__/HeroBanner.test.js.snap b/components/HeroBanner/__tests__/__snapshots__/HeroBanner.test.tsx.snap
similarity index 100%
rename from components/HeroBanner/__tests__/__snapshots__/HeroBanner.test.js.snap
rename to components/HeroBanner/__tests__/__snapshots__/HeroBanner.test.tsx.snap
diff --git a/components/Modal/Modal.js b/components/Modal/Modal.tsx
similarity index 67%
rename from components/Modal/Modal.js
rename to components/Modal/Modal.tsx
index e766e7db7..ae685fccc 100644
--- a/components/Modal/Modal.js
+++ b/components/Modal/Modal.tsx
@@ -1,42 +1,59 @@
-import { node, string, bool, func } from 'prop-types';
import classNames from 'classnames';
import * as Dialog from '@radix-ui/react-dialog';
import { gtag } from 'common/utils/thirdParty/gtag';
import CloseButton from 'components/Buttons/CloseButton/CloseButton';
import { MODAL_CONTENT, MODAL_OVERLAY } from 'common/constants/testIDs';
-Modal.propTypes = {
- children: node.isRequired,
- className: string,
- isOpen: bool,
- onRequestClose: func.isRequired,
- screenReaderLabel: string.isRequired, // basically a summarizing title
- canClose: bool,
- childrenClassName: string,
-};
-
-Modal.defaultProps = {
- className: undefined,
- isOpen: false,
- canClose: true,
- childrenClassName: undefined,
+export type ModalPropsType = {
+ /**
+ * Content to be rendered in the modal.
+ */
+ children: React.ReactNode;
+ /**
+ * Function that is called when the user clicks the close button.
+ */
+ onRequestClose: (arg1: any) => void;
+ /**
+ * Applies a label for the screen reader.
+ */
+ screenReaderLabel: string;
+ /**
+ * Applies style classes to the wrapping div.
+ */
+ className?: string;
+ /**
+ * Sets if the modal is open an visible (or not)
+ * @default false
+ */
+ isOpen?: boolean;
+ /**
+ * Sets if the modal can be closed by the user
+ * @default true
+ */
+ canClose?: boolean;
+ /**
+ * Applies style classes to the child content.
+ */
+ childrenClassName?: string;
};
function Modal({
children,
className,
- isOpen,
+ isOpen = false,
onRequestClose,
screenReaderLabel,
- canClose,
+ canClose = true,
childrenClassName,
-}) {
+}: ModalPropsType) {
if (isOpen) {
gtag.modalView(screenReaderLabel);
}
const portalContainer =
- typeof window !== 'undefined' ? document.querySelector('#__next') ?? undefined : undefined;
+ typeof window !== 'undefined'
+ ? (document.querySelector('#__next') as HTMLElement) ?? undefined
+ : undefined;
return (
diff --git a/components/Modal/__stories__/Modal.stories.js b/components/Modal/__stories__/Modal.stories.tsx
similarity index 71%
rename from components/Modal/__stories__/Modal.stories.js
rename to components/Modal/__stories__/Modal.stories.tsx
index 5601a268f..8a1374920 100644
--- a/components/Modal/__stories__/Modal.stories.js
+++ b/components/Modal/__stories__/Modal.stories.tsx
@@ -1,17 +1,36 @@
+import { Meta, StoryObj } from '@storybook/react';
import { useState } from 'react';
import isChromatic from 'chromatic/isChromatic';
-
import { descriptions } from 'common/constants/descriptions';
import Button from 'components/Buttons/Button/Button';
import Modal from '../Modal';
-export const Default = {
+type ModalStoryType = StoryObj;
+
+const meta: Meta = {
+ title: 'Modal',
+ component: Modal,
+ parameters: {
+ previewTabs: {
+ 'storybook/docs/panel': { hidden: true },
+ },
+ docs: {
+ autodocs: false,
+ disable: true,
+ page: null,
+ },
+ },
+};
+
+export default meta;
+
+export const Default: ModalStoryType = {
render: args => {
const [isDemoModalOpen, setIsDemoModalOpen] = useState(args.isOpen);
return (
<>
-
+
{
- const [isDemoModalOpen, setIsDemoModalOpen] = useState(args.isOpen);
-
- return (
- <>
-
-
- setIsDemoModalOpen(!prevValue)}
- />
- >
- );
- },
+export const NonDismissableModal: ModalStoryType = {
+ ...Default,
args: {
canClose: false,
isOpen: false,
@@ -59,20 +64,3 @@ export const NonDismissableModal = {
screenReaderLabel: 'You have completed the form.',
},
};
-
-const meta = {
- title: 'Modal',
- component: Modal,
- parameters: {
- previewTabs: {
- 'storybook/docs/panel': { hidden: true },
- },
- docs: {
- autodocs: false,
- disable: true,
- page: null,
- },
- },
-};
-
-export default meta;
diff --git a/components/Modal/__tests__/Modal.test.js b/components/Modal/__tests__/Modal.test.tsx
similarity index 93%
rename from components/Modal/__tests__/Modal.test.js
rename to components/Modal/__tests__/Modal.test.tsx
index 4b4af43d2..6907bd97f 100644
--- a/components/Modal/__tests__/Modal.test.js
+++ b/components/Modal/__tests__/Modal.test.tsx
@@ -24,7 +24,7 @@ describe('Modal', () => {
it('should render with many props assigned', () => {
const { container } = render(
-
+
Test
,
);
diff --git a/components/Modal/__tests__/__snapshots__/Modal.test.js.snap b/components/Modal/__tests__/__snapshots__/Modal.test.tsx.snap
similarity index 100%
rename from components/Modal/__tests__/__snapshots__/Modal.test.js.snap
rename to components/Modal/__tests__/__snapshots__/Modal.test.tsx.snap
diff --git a/components/ReusableSections/DonateSection/DonateSection.js b/components/ReusableSections/DonateSection/DonateSection.tsx
similarity index 100%
rename from components/ReusableSections/DonateSection/DonateSection.js
rename to components/ReusableSections/DonateSection/DonateSection.tsx
diff --git a/components/ReusableSections/DonateSection/__stories__/DonateSection.stories.js b/components/ReusableSections/DonateSection/__stories__/DonateSection.stories.js
deleted file mode 100644
index 8e1e04467..000000000
--- a/components/ReusableSections/DonateSection/__stories__/DonateSection.stories.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import DonateSection from '../DonateSection';
-
-export default {
- component: DonateSection,
- title: 'Reusable/DonateSection',
-};
-
-const Template = arguments_ => ;
-
-export const Default = Template.bind({});
diff --git a/components/ReusableSections/DonateSection/__stories__/DonateSection.stories.tsx b/components/ReusableSections/DonateSection/__stories__/DonateSection.stories.tsx
new file mode 100644
index 000000000..6b3c14900
--- /dev/null
+++ b/components/ReusableSections/DonateSection/__stories__/DonateSection.stories.tsx
@@ -0,0 +1,16 @@
+import React from 'react';
+import { Meta, StoryObj } from '@storybook/react';
+import DonateSection from '../DonateSection';
+
+type DonateSectionStoryType = StoryObj;
+
+const meta: Meta = {
+ title: 'Reusable/DonateSection',
+ component: DonateSection,
+};
+
+export default meta;
+
+export const Default: DonateSectionStoryType = {
+ render: args => ,
+};
diff --git a/components/ReusableSections/DonateSection/__tests__/DonateSection.test.js b/components/ReusableSections/DonateSection/__tests__/DonateSection.test.tsx
similarity index 100%
rename from components/ReusableSections/DonateSection/__tests__/DonateSection.test.js
rename to components/ReusableSections/DonateSection/__tests__/DonateSection.test.tsx
diff --git a/components/ReusableSections/DonateSection/__tests__/__snapshots__/DonateSection.test.js.snap b/components/ReusableSections/DonateSection/__tests__/__snapshots__/DonateSection.test.tsx.snap
similarity index 93%
rename from components/ReusableSections/DonateSection/__tests__/__snapshots__/DonateSection.test.js.snap
rename to components/ReusableSections/DonateSection/__tests__/__snapshots__/DonateSection.test.tsx.snap
index 4722fbb74..308afefa9 100644
--- a/components/ReusableSections/DonateSection/__tests__/__snapshots__/DonateSection.test.js.snap
+++ b/components/ReusableSections/DonateSection/__tests__/__snapshots__/DonateSection.test.tsx.snap
@@ -5,8 +5,6 @@ exports[`DonateSection > should render with no props passed passed 1`] = `
backgroundImageSource="https://operation-code-assets.s3.us-east-2.amazonaws.com/background_flag.jpg"
>