Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert Alert, Accordion, and Button components to Typescript #1800

Merged
merged 24 commits into from
Mar 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
f98a88b
changed LinkButton.js to .tsx types
recondesigns Feb 29, 2024
63e0e2d
changed LinkButton.stories.js to .tsx and updated stories to CSF
recondesigns Feb 29, 2024
7fc9c4b
changed LinkButton.test.js to .tsx and updated DonateSection snapshot
recondesigns Feb 29, 2024
e87ecde
updated DonateSection snapshot
recondesigns Feb 29, 2024
0433878
converted CloseButton.js to .tsx
recondesigns Feb 29, 2024
87b79a4
converted CloseButton.stories.js to .tsx and updated stories to CSF
recondesigns Feb 29, 2024
844a062
cleaned up CloseButton.tsx and added a story arg for theme
recondesigns Feb 29, 2024
979d80f
changed CloseButton test file to .tsx and update snapshot
recondesigns Feb 29, 2024
6ebdf9e
converted Accordion.js to .tsx and added prop types
recondesigns Feb 29, 2024
4a2954b
converted Accordion.stories.js to .tsx and updated stories to new CSF
recondesigns Feb 29, 2024
ce28998
converted Accordion.test.js to .jsx and updated tests
recondesigns Feb 29, 2024
5658081
converted Alert.js to .tsx and added prop types
recondesigns Feb 29, 2024
aeafd89
converted Alert.stories.js to .tsx and updated stories to new CSF
recondesigns Feb 29, 2024
92957fc
converted Alert test files to .tsx and updated snapshot
recondesigns Feb 29, 2024
a90909a
added required prop to Accordion in slack_guide.tsx page
recondesigns Feb 29, 2024
49bda67
included default value in props type description and removed default …
recondesigns Mar 2, 2024
8343f57
Switched to Alert stories in Alert.test.tsx and moved stories after d…
recondesigns Mar 2, 2024
6b00763
moved Accordion stories below default export
recondesigns Mar 2, 2024
1216b34
switched to Accordion stories in Accordion.test.tsx and removed undef…
recondesigns Mar 2, 2024
c436cdd
moved Button stories below default export
recondesigns Mar 2, 2024
b202ab2
removed undefined default prop values and moved LinkButton stories be…
recondesigns Mar 2, 2024
c0aaa0f
moved CloseButton stories below default export
recondesigns Mar 2, 2024
bc3a33a
mergin in main
recondesigns Mar 2, 2024
e66b0c6
added className and children to LinkButton props type
recondesigns Mar 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useState } from 'react';
import { arrayOf, bool, node, number, shape, string, oneOfType } from 'prop-types';
import classNames from 'classnames';
import Chevron from 'public/static/images/icons/FontAwesome/angle-right-solid.svg';
import { ACCORDION_CONTENT, ACCORDION_TOGGLE_BUTTON } from 'common/constants/testIDs';
Expand All @@ -10,33 +9,47 @@ import styles from './Accordion.module.css';
const ChevronRight = () => <Chevron className={styles.icon} />;
const ChevronDown = () => <Chevron className={classNames(styles.icon, styles.rotate90)} />;

Accordion.propTypes = {
/** Accessibility ID to use for joining elements together
* with ARIA attributes */
accessibilityId: oneOfType([number, string]).isRequired,
/** Name of style class to use */
className: string,
/** Composition of the Accordion */
content: shape({
/** Labels or thumbnails representing sections of content */
headingChildren: oneOfType([node, arrayOf(node)]).isRequired,
/** Section of content associated with header */
bodyChildren: oneOfType([node, arrayOf(node)]).isRequired,
}).isRequired,
/** Should Accordion have animation on hover */
hasAnimationOnHover: bool,
type ContentPropType = {
/**
* Labels or thumbnails representing sections of content.
*/
headingChildren: React.ReactNode | React.ReactNode[];
/**
* Section of content associated with header.
*/
bodyChildren: React.ReactNode | React.ReactNode[];
};

Accordion.defaultProps = {
className: undefined,
hasAnimationOnHover: false,
export type AccordionPropsType = {
/**
* Accessibility ID to use for joining elements together with ARIA attributes
*/
accessibilityId: number | string;
/**
* Composition of the Accordion.
*/
content: ContentPropType;
/**
* Name of style class to use.
*/
className?: string;
/**
* Should Accordion have animation on hover.
* @default - false
*/
hasAnimationOnHover?: boolean;
};

/**
* @description A component whose main content is invisible until revealed by the user
* @see http://web-accessibility.carnegiemuseums.org/code/accordions/
*/
function Accordion({ accessibilityId, className, content, hasAnimationOnHover }) {
function Accordion({
accessibilityId,
className,
content,
hasAnimationOnHover = false,
}: AccordionPropsType) {
const [isContentVisible, setContentVisibility] = useState(false);

const toggleAccordionContent = () => setContentVisibility(previousState => !previousState);
Expand Down
18 changes: 0 additions & 18 deletions components/Accordion/__stories__/Accordion.stories.js

This file was deleted.

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

type AccordionStoryType = StoryObj<typeof Accordion>;

const meta: Meta<typeof Accordion> = {
title: 'Accordion',
component: Accordion,
args: {
accessibilityId: '1',
content: {
headingChildren: <h5>Can be JSX</h5>,
bodyChildren: <p>Can also be JSX</p>,
},
},
};

export default meta;

export const Default: AccordionStoryType = {
render: args => <Accordion {...args} />,
};
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
import { fireEvent, render } from '@testing-library/react';

import { composeStory } from '@storybook/react';
import {
ACCORDION_CONTENT,
ACCORDION_TOGGLE_BUTTON,
SCREEN_READER_ONLY,
} from 'common/constants/testIDs';
import { Default } from '../__stories__/Accordion.stories';
import { toggleMessages } from '../../ScreenReaderOnly/ScreenReaderOnly';
import meta, { Default } from '../__stories__/Accordion.stories';

const AccordionStory = composeStory(Default, meta);

describe('Accordion', () => {
it('should render invisible text that turns visible on toggle click', async () => {
const component = render(<Default {...Default.args} />);
recondesigns marked this conversation as resolved.
Show resolved Hide resolved
const component = render(<AccordionStory />);
const Content = component.queryByTestId(ACCORDION_CONTENT);

expect(Content).not.toBeVisible();

// @ts-expect-error
fireEvent.click(component.queryByTestId(ACCORDION_TOGGLE_BUTTON));

expect(Content).toBeVisible();
Expand All @@ -23,13 +26,14 @@ describe('Accordion', () => {

describe('Accordion Accessibility', () => {
it('should display the correct screenReader text for toggle button', async () => {
const component = render(<Default {...Default.args} />);
const component = render(<AccordionStory />);
const Button = component.queryByTestId(SCREEN_READER_ONLY);

//@ts-expect-error
expect(Button.textContent).toBe(toggleMessages.open);

//@ts-expect-error
fireEvent.click(Button);

//@ts-expect-error
expect(Button.textContent).toBe(toggleMessages.close);
});
});
27 changes: 13 additions & 14 deletions components/Alert/Alert.js → components/Alert/Alert.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
import { func, node, oneOf, string } from 'prop-types';
import classNames from 'classnames';
import { ALERT, ALERT_CLOSE_BUTTON } from 'common/constants/testIDs';
import ScreenReaderOnly from 'components/ScreenReaderOnly/ScreenReaderOnly';
import styles from './Alert.module.css';

Alert.propTypes = {
children: node.isRequired,
className: string,
'data-testid': string,
onClose: func,
type: oneOf(['error', 'success', 'warning']).isRequired,
export type AlertPropsType = {
type: 'error' | 'success' | 'warning';
children: React.ReactNode;
className?: string;
'data-testid'?: string;
onClose?: () => void;
};

Alert.defaultProps = {
className: undefined,
'data-testid': ALERT,
onClose: undefined,
};

function Alert({ children, className, 'data-testid': testID, onClose, type }) {
function Alert({
children,
className,
'data-testid': testID = ALERT,
onClose,
type,
}: AlertPropsType) {
return (
<div
className={classNames(styles.Alert, className, {
Expand Down
26 changes: 0 additions & 26 deletions components/Alert/__stories__/Alert.stories.js

This file was deleted.

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

type AlertStoryType = StoryObj<typeof Alert>;

const meta: Meta<typeof Alert> = {
title: 'Alert',
component: Alert,
};

export default meta;

const AlertStoryTemplate: AlertStoryType = {
render: args => <Alert {...args} />,
};

export const ErrorAlert: AlertStoryType = {
...AlertStoryTemplate,
args: {
children: 'Error Alert JSX or Text',
type: 'error',
},
};

export const SuccessAlert: AlertStoryType = {
...AlertStoryTemplate,
args: {
children: 'Success Alert JSX or Text',
type: 'success',
},
};

export const WarningAlert: AlertStoryType = {
...AlertStoryTemplate,
args: {
children: 'Warning Alert JSX or Text',
type: 'warning',
},
};
Original file line number Diff line number Diff line change
@@ -1,28 +1,33 @@
import { fireEvent, render } from '@testing-library/react';
import { composeStory } from '@storybook/react';
import createSnapshotTest from 'test-utils/createSnapshotTest';
import { ALERT_CLOSE_BUTTON } from 'common/constants/testIDs';
import meta, { ErrorAlert, SuccessAlert, WarningAlert } from '../__stories__/Alert.stories';

import { ErrorAlert, SuccessAlert, WarningAlert } from '../__stories__/Alert.stories';
recondesigns marked this conversation as resolved.
Show resolved Hide resolved
const ErrorAlertStory = composeStory(ErrorAlert, meta);
const SuccessAlertStory = composeStory(SuccessAlert, meta);
const WarningAlertStory = composeStory(WarningAlert, meta);

describe('Alert', () => {
it('should render error alert with required props', () => {
createSnapshotTest(<ErrorAlert {...ErrorAlert.args} />);
createSnapshotTest(<ErrorAlertStory />);
});

it('should call close handler when close alert button clicked', () => {
const onCloseMock = vi.fn();

const { queryByTestId } = render(<SuccessAlert {...SuccessAlert.args} onClose={onCloseMock} />);
const { queryByTestId } = render(<SuccessAlertStory onClose={onCloseMock} />);

expect(onCloseMock).toHaveBeenCalledTimes(0);

// @ts-expect-error
fireEvent.click(queryByTestId(ALERT_CLOSE_BUTTON));

expect(onCloseMock).toHaveBeenCalledTimes(1);
});

it('should NOT render button if close handler not provided', () => {
const { queryByTestId } = render(<WarningAlert {...WarningAlert.args} />);
const { queryByTestId } = render(<WarningAlertStory />);

expect(queryByTestId(ALERT_CLOSE_BUTTON)).toBeNull();
});
Expand Down
14 changes: 7 additions & 7 deletions components/Buttons/Button/__stories__/Button.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,6 @@ import Button from '../Button';

type ButtonStoryType = StoryObj<typeof Button>;

export const Default: ButtonStoryType = {
render: args => <Button {...args} />,
args: {
children: 'Button',
},
};

const meta: Meta<typeof Button> = {
title: 'Buttons/Button',
component: Button,
Expand All @@ -21,3 +14,10 @@ const meta: Meta<typeof Button> = {
};

export default meta;

export const Default: ButtonStoryType = {
render: args => <Button {...args} />,
args: {
children: 'Button',
},
};
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
import { bool, func, oneOf } from 'prop-types';
import classNames from 'classnames';
import { CLOSE_BUTTON } from 'common/constants/testIDs';
import ScreenReaderOnly from 'components/ScreenReaderOnly/ScreenReaderOnly';
import PlusIcon from 'static/images/icons/plus.svg';
import styles from './CloseButton.module.css';

CloseButton.propTypes = {
disabled: bool,
onClick: func.isRequired,
theme: oneOf(['primary', 'secondary', 'white']),
};
export type CloseButtonProps = {
/**
* Sets the button color theme.
*/
theme?: 'primary' | 'secondary' | 'white';
} & React.ButtonHTMLAttributes<HTMLButtonElement>;

CloseButton.defaultProps = {
disabled: false,
theme: 'primary',
};

export default function CloseButton({ disabled, onClick, theme }) {
export default function CloseButton({
disabled = false,
onClick,
theme = 'primary',
}: CloseButtonProps) {
return (
<button
className={styles.CloseButton}
Expand Down
11 changes: 0 additions & 11 deletions components/Buttons/CloseButton/__stories__/CloseButton.stories.js

This file was deleted.

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

type CloseButtonStoryType = StoryObj<typeof CloseButton>;

const meta: Meta<typeof CloseButton> = {
title: 'Buttons/Closebutton',
component: CloseButton,
};

export default meta;

export const Default: CloseButtonStoryType = {
render: args => <CloseButton {...args} />,
args: {
theme: 'primary',
},
};
Loading
Loading