From 13ff3df727dc6131261dc6a8f92d721658e11e4b Mon Sep 17 00:00:00 2001 From: Richard Dinh <1038306+flacoman91@users.noreply.github.com> Date: Mon, 27 Jul 2026 14:24:20 -0700 Subject: [PATCH 1/2] Fix vite-plugin-dts TypeScript errors so yarn build is clean. Migrate leftover Link href props to to, declare cfpb-tagline for JSX, correct Multiselect/Table/radio/header typings, and tighten story/test types that were failing declaration emit. --- src/components/alert/alert.stories.tsx | 23 ++++++--- src/components/alert/alert.test.tsx | 8 ++- src/components/buttons/buttons.stories.tsx | 2 +- src/components/footer/back-to-top.tsx | 2 +- src/components/footer/footer-cf-gov.tsx | 6 +-- src/components/footer/footer.stories.tsx | 6 +-- src/components/header/header.test.tsx | 6 ++- src/components/header/header.tsx | 3 +- src/components/label/label.test.tsx | 2 +- src/components/list/list.stories.tsx | 2 +- src/components/pagination/pagination.test.tsx | 36 +++++++++++-- src/components/radio-button/radio-button.tsx | 9 +--- src/components/table/table.test.tsx | 4 +- src/components/table/table.tsx | 2 +- .../text-introduction/test-helpers.tsx | 2 +- .../text-introduction/text-introduction.tsx | 51 ++++++++++--------- src/components/well/well.stories.tsx | 2 +- src/components/well/well.test.tsx | 2 +- src/types/cfpb-forms.d.ts | 8 ++- src/types/cfpb-tagline.d.ts | 14 +++++ 20 files changed, 127 insertions(+), 63 deletions(-) create mode 100644 src/types/cfpb-tagline.d.ts diff --git a/src/components/alert/alert.stories.tsx b/src/components/alert/alert.stories.tsx index db1eda4a69..559112f946 100644 --- a/src/components/alert/alert.stories.tsx +++ b/src/components/alert/alert.stories.tsx @@ -1,8 +1,12 @@ import type { Meta, StoryObj } from '@storybook/react-vite'; import type { ReactNode } from 'react'; import { Alert } from './alert'; -import { AlertFieldLevel } from './alert-field-level'; +import { + AlertFieldLevel, + type AlertFieldLevelType, +} from './alert-field-level'; import { TextInput } from '../text-input/text-input'; +import type { TextInputStatusType } from '../text-input/text-input-status'; const meta: Meta = { title: 'Components (Draft)/Alerts', @@ -26,7 +30,7 @@ const alertExplanation = (type: string): string => `This is an optional explanation of the ${type}.`; const externalLinkProperties = { - href: '/', + to: '/', label: 'This is an external link', isExternal: true, }; @@ -61,7 +65,7 @@ export const InformationWithLinks: Story = { children: 'This is the explanation of the alert.', links: [ { - href: '/', + to: '/', label: 'This is a link below the explanation', }, externalLinkProperties, @@ -112,13 +116,20 @@ export const InProgress: Story = { }, }; +const textInputStatus = ( + status?: AlertFieldLevelType, +): TextInputStatusType | undefined => + status === 'error' || status === 'warning' || status === 'success' + ? status + : undefined; + export const SuccessFieldLevel: FieldLevelStory = { render: (_arguments) => (
@@ -138,7 +149,7 @@ export const WarningFieldLevel: FieldLevelStory = { @@ -158,7 +169,7 @@ export const ErrorFieldLevel: FieldLevelStory = { diff --git a/src/components/alert/alert.test.tsx b/src/components/alert/alert.test.tsx index cba883a2da..44aa4a5f1a 100644 --- a/src/components/alert/alert.test.tsx +++ b/src/components/alert/alert.test.tsx @@ -2,7 +2,10 @@ import '@testing-library/jest-dom'; import { render, screen, within } from '@testing-library/react'; import Paragraph from '../paragraph/paragraph'; import { Alert, AlertType } from './alert'; -import { AlertFieldLevel } from './alert-field-level'; +import { + AlertFieldLevel, + type AlertFieldLevelType, +} from './alert-field-level'; const statusesWithModifier: AlertType[] = ['success', 'warning', 'error']; @@ -215,7 +218,8 @@ describe('', () => { render( , ); diff --git a/src/components/buttons/buttons.stories.tsx b/src/components/buttons/buttons.stories.tsx index f825762a55..14034b9fae 100644 --- a/src/components/buttons/buttons.stories.tsx +++ b/src/components/buttons/buttons.stories.tsx @@ -163,7 +163,7 @@ export const ButtonLink: Story = { diff --git a/src/components/footer/back-to-top.tsx b/src/components/footer/back-to-top.tsx index ba5159485d..9b449da3ab 100644 --- a/src/components/footer/back-to-top.tsx +++ b/src/components/footer/back-to-top.tsx @@ -8,7 +8,7 @@ export const BackToTop = (): JSX.Element => ( data-gtm_ignore='true' data-js-hook='behavior_return-to-top' data-testid='back-to-top' - href='#' + to='#' iconRight='arrow-up' /> ); diff --git a/src/components/footer/footer-cf-gov.tsx b/src/components/footer/footer-cf-gov.tsx index 388a655257..ea39c06abc 100644 --- a/src/components/footer/footer-cf-gov.tsx +++ b/src/components/footer/footer-cf-gov.tsx @@ -143,19 +143,19 @@ export const FooterCfGov = ({ const linksCol3 = [ , , , diff --git a/src/components/footer/footer.stories.tsx b/src/components/footer/footer.stories.tsx index e799098c54..16d611af3c 100644 --- a/src/components/footer/footer.stories.tsx +++ b/src/components/footer/footer.stories.tsx @@ -32,8 +32,8 @@ export const App: Story = { render: () => ( , - , + , + , ]} footerContent={ <> @@ -44,7 +44,7 @@ export const App: Story = { ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. - Link + Link } /> diff --git a/src/components/header/header.test.tsx b/src/components/header/header.test.tsx index 3685060e85..8989de339f 100644 --- a/src/components/header/header.test.tsx +++ b/src/components/header/header.test.tsx @@ -21,8 +21,10 @@ describe('Header', () => { it('Renders with bottom border by default', () => { const { container } = render(
); - expect(container.firstChild.className).toBe('o-header-scope'); - expect(container.firstChild.firstChild?.className).toBe( + expect((container.firstChild as HTMLElement).className).toBe( + 'o-header-scope', + ); + expect((container.firstChild?.firstChild as HTMLElement).className).toBe( 'o-header bottom-border', ); }); diff --git a/src/components/header/header.tsx b/src/components/header/header.tsx index eee42af92b..8a0c8230b8 100644 --- a/src/components/header/header.tsx +++ b/src/components/header/header.tsx @@ -1,4 +1,5 @@ import classnames from 'classnames'; +import type { ReactNode } from 'react'; import { JSX } from 'react'; import { Banner } from '../banner/banner'; import type { LogoLanguage } from './logo'; @@ -6,7 +7,7 @@ import ResponsiveMenu from './responsive-menu'; import './header.scss'; export interface HeaderProperties { - links?: JSX.Element[]; + links?: ReactNode[]; href?: string; lang?: LogoLanguage; } diff --git a/src/components/label/label.test.tsx b/src/components/label/label.test.tsx index bb41577557..624f2f3911 100644 --- a/src/components/label/label.test.tsx +++ b/src/components/label/label.test.tsx @@ -41,7 +41,7 @@ describe('
); }; +TextIntroductionContainer.displayName = 'TextIntroduction.Container'; +TextIntroduction.Container = TextIntroductionContainer; -export const TextIntroductionContainer = TextIntroduction.Container; -interface TextIntroductionSubProperties { - children: ReactNode; -} +export { TextIntroductionContainer }; -TextIntroduction.Heading = ({ +const TextIntroductionHeading: FC = ({ children, -}: TextIntroductionSubProperties): JSX.Element => ( - {children} -); +}) => {children}; +TextIntroductionHeading.displayName = 'TextIntroduction.Heading'; +TextIntroduction.Heading = TextIntroductionHeading; -export const TextIntroductionHeading = TextIntroduction.Heading; +export { TextIntroductionHeading }; -TextIntroduction.Description = ({ +const TextIntroductionDescription: FC = ({ children, -}: TextIntroductionSubProperties): ReactNode => renderDescription(children); +}) => renderDescription(children); +TextIntroductionDescription.displayName = 'TextIntroduction.Description'; +TextIntroduction.Description = TextIntroductionDescription; -export const TextIntroductionDescription = TextIntroduction.Description; +export { TextIntroductionDescription }; -TextIntroduction.Subheading = ({ +const TextIntroductionSubheading: FC = ({ children, -}: TextIntroductionSubProperties): JSX.Element => ( - {children} -); - -export const TextIntroductionSubheading = TextIntroduction.Subheading; +}) => {children}; +TextIntroductionSubheading.displayName = 'TextIntroduction.Subheading'; +TextIntroduction.Subheading = TextIntroductionSubheading; -TextIntroduction.Container.displayName = 'TextIntroduction.Container'; -TextIntroduction.Heading.displayName = 'TextIntroduction.Heading'; -TextIntroduction.Description.displayName = 'TextIntroduction.Description'; -TextIntroduction.Subheading.displayName = 'TextIntroduction.Subheading'; +export { TextIntroductionSubheading }; export default TextIntroduction; diff --git a/src/components/well/well.stories.tsx b/src/components/well/well.stories.tsx index 0406284403..0eb4590f1b 100644 --- a/src/components/well/well.stories.tsx +++ b/src/components/well/well.stories.tsx @@ -26,7 +26,7 @@ export const Content: ContentStory = { heading: 'Heading', headingLevel: 'h3', text: 'Lorem ipsum dolor sit amet, ei ius adhuc inani iudico, labitur instructior ex pri. Cu pri inani constituto, cum aeque noster commodo', - links: [], + links: [], }, render: (arguments_) => , }; diff --git a/src/components/well/well.test.tsx b/src/components/well/well.test.tsx index eb12ba86ff..9fbd5b00bd 100644 --- a/src/components/well/well.test.tsx +++ b/src/components/well/well.test.tsx @@ -31,7 +31,7 @@ describe('', () => { it('Displays elements', () => { render(); - const headingText = Content.args!.heading; + const headingText = Content.args!.heading as string; const heading = screen.getByText(headingText); expect(heading).toHaveClass('h3'); diff --git a/src/types/cfpb-forms.d.ts b/src/types/cfpb-forms.d.ts index e802e200ba..00e0525321 100644 --- a/src/types/cfpb-forms.d.ts +++ b/src/types/cfpb-forms.d.ts @@ -1,6 +1,12 @@ declare module '@cfpb/cfpb-design-system/src/components/cfpb-forms' { export class Multiselect { constructor(element: HTMLElement | null); - init(options?: { maxSelections?: number; renderTags?: boolean }): void; + init(options?: { + maxSelections?: number; + renderTags?: boolean; + }): Multiselect; + getModel(): { getSelectedIndices(): number[] }; + addEventListener(type: string, listener: () => void): void; + removeEventListener(type: string, listener: () => void): void; } } diff --git a/src/types/cfpb-tagline.d.ts b/src/types/cfpb-tagline.d.ts new file mode 100644 index 0000000000..9755662060 --- /dev/null +++ b/src/types/cfpb-tagline.d.ts @@ -0,0 +1,14 @@ +import type { DetailedHTMLProps, HTMLAttributes } from 'react'; + +declare module 'react' { + namespace JSX { + interface IntrinsicElements { + 'cfpb-tagline': DetailedHTMLProps< + HTMLAttributes & { isLarge?: boolean }, + HTMLElement + >; + } + } +} + +export {}; From d162d1c72491227eb73af8548ba6b8ab07e1ff6a Mon Sep 17 00:00:00 2001 From: Richard Dinh <1038306+flacoman91@users.noreply.github.com> Date: Mon, 27 Jul 2026 16:06:15 -0700 Subject: [PATCH 2/2] linting fixes --- src/components/alert/alert.stories.tsx | 2 +- src/components/header/header.test.tsx | 9 ++------- src/components/pagination/pagination.test.tsx | 2 +- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/components/alert/alert.stories.tsx b/src/components/alert/alert.stories.tsx index 559112f946..fde5b537c6 100644 --- a/src/components/alert/alert.stories.tsx +++ b/src/components/alert/alert.stories.tsx @@ -119,7 +119,7 @@ export const InProgress: Story = { const textInputStatus = ( status?: AlertFieldLevelType, ): TextInputStatusType | undefined => - status === 'error' || status === 'warning' || status === 'success' + status !== undefined && ['error', 'warning', 'success'].includes(status) ? status : undefined; diff --git a/src/components/header/header.test.tsx b/src/components/header/header.test.tsx index 8989de339f..bee0252231 100644 --- a/src/components/header/header.test.tsx +++ b/src/components/header/header.test.tsx @@ -20,13 +20,8 @@ describe('Header', () => { }); it('Renders with bottom border by default', () => { - const { container } = render(
); - expect((container.firstChild as HTMLElement).className).toBe( - 'o-header-scope', - ); - expect((container.firstChild?.firstChild as HTMLElement).className).toBe( - 'o-header bottom-border', - ); + render(
); + expect(screen.getByRole('banner')).toHaveClass('o-header', 'bottom-border'); }); it('renders the English logo by default', () => { diff --git a/src/components/pagination/pagination.test.tsx b/src/components/pagination/pagination.test.tsx index efeffd0405..daf2855720 100644 --- a/src/components/pagination/pagination.test.tsx +++ b/src/components/pagination/pagination.test.tsx @@ -2,10 +2,10 @@ import '@testing-library/jest-dom'; import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { ReactElement, useState } from 'react'; +import { noOp } from '../../utils/no-op'; import { Pagination } from './pagination'; const user = userEvent.setup(); -const noOp = (): void => {}; const ControlledPagination = (): ReactElement => { const [page, setPage] = useState(2);