Skip to content

Commit

Permalink
Migrate Footer, Heading, DonateSection, HeroBanner, and Modal compone…
Browse files Browse the repository at this point in the history
…nts to Typescript (#1805)
  • Loading branch information
recondesigns authored Mar 5, 2024
1 parent 86554f1 commit 0fef2a4
Show file tree
Hide file tree
Showing 22 changed files with 192 additions and 136 deletions.
17 changes: 16 additions & 1 deletion components/Footer/Footer.js → components/Footer/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<li key={href}>
{analyticsEventLabel ? (
Expand Down
File renamed without changes.
48 changes: 33 additions & 15 deletions components/Heading/Heading.js → components/Heading/Heading.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className={styles.headingContainer}>
Expand Down
22 changes: 0 additions & 22 deletions components/Heading/__stories__/Heading.stories.js

This file was deleted.

18 changes: 18 additions & 0 deletions components/Heading/__stories__/Heading.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Meta, StoryObj } from '@storybook/react';
import Heading from '../Heading';

type HeadingStoryType = StoryObj<typeof Heading>;

const meta: Meta<typeof Heading> = {
title: 'Heading',
component: Heading,
args: {
text: 'Heading text',
},
};

export default meta;

export const Default: HeadingStoryType = {
render: args => <Heading {...args} />,
};
Original file line number Diff line number Diff line change
@@ -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 (
<Container
backgroundImageSource={backgroundImageSource}
Expand Down
14 changes: 0 additions & 14 deletions components/HeroBanner/__stories__/HeroBanner.stories.js

This file was deleted.

18 changes: 18 additions & 0 deletions components/HeroBanner/__stories__/HeroBanner.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Meta, StoryObj } from '@storybook/react';
import HeroBanner from '../HeroBanner';

type HeroBannerStoryType = StoryObj<typeof HeroBanner>;

const meta: Meta<typeof HeroBanner> = {
title: 'HeroBanner',
component: HeroBanner,
args: {
title: 'Banner Title',
},
};

export default meta;

export const Default: HeroBannerStoryType = {
render: args => <HeroBanner {...args} />,
};
57 changes: 37 additions & 20 deletions components/Modal/Modal.js → components/Modal/Modal.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<Dialog.Root defaultOpen={false} open={isOpen}>
Expand Down
Original file line number Diff line number Diff line change
@@ -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<typeof Modal>;

const meta: Meta<typeof Modal> = {
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 (
<>
<Button onClick={() => setIsDemoModalOpen(true)}>Open Modal</Button>
<Button onClick={() => setIsDemoModalOpen(true)}>Open modal</Button>

<Modal
{...args}
Expand All @@ -28,22 +47,8 @@ export const Default = {
},
};

export const NonDismissableModal = {
render: args => {
const [isDemoModalOpen, setIsDemoModalOpen] = useState(args.isOpen);

return (
<>
<Button onClick={() => setIsDemoModalOpen(true)}>Open Modal</Button>

<Modal
{...args}
isOpen={isDemoModalOpen}
onRequestClose={prevValue => setIsDemoModalOpen(!prevValue)}
/>
</>
);
},
export const NonDismissableModal: ModalStoryType = {
...Default,
args: {
canClose: false,
isOpen: false,
Expand All @@ -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;
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ describe('Modal', () => {

it('should render with many props assigned', () => {
const { container } = render(
<Modal {...requiredProps} className="test-class" isOpen shouldCloseOnOverlayClick={false}>
<Modal {...requiredProps} className="test-class" isOpen>
Test
</Modal>,
);
Expand Down
Loading

0 comments on commit 0fef2a4

Please sign in to comment.