Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@
"jest-matchmedia-mock": "1.1.0",
"jsdom": "29.0.1",
"lint-staged": "16.4.0",
"lit": "^3.3.2",
"npm-run-all": "4.1.5",
"playwright": "^1.58.2",
"postcss": "8.5.8",
Expand All @@ -117,6 +118,7 @@
"typescript-eslint": "^8.57.1",
"vite": "^8.0.1",
"vite-plugin-dts": "^4.5.4",
"vite-plugin-lit-css": "^2.2.2",
"vite-plugin-pwa": "^1.2.0",
"vite-plugin-svgr": "^4.5.0",
"vite-plugin-turbosnap": "^1.0.3",
Expand Down
39 changes: 26 additions & 13 deletions src/components/Banner/banner.scss
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@
}

&__phone {
padding-left: math.div(math.div($grid-gutter-width, 2), $base-font-size-px) +
padding-left: math.div(
math.div($grid-gutter-width, 2),
$base-font-size-px
) +
em;
}

Expand All @@ -40,12 +43,13 @@
padding-bottom: math.div(
math.div($grid-gutter-width, 3),
$base-font-size-px
) + em;
) +
em;
background: var(--gray-5);
border-bottom: 1px solid var(--gray-40);

/* This is to allow vertical overlap with the languages */
.a-tagline {
cfpb-tagline {
float: left;
}

Expand All @@ -67,17 +71,18 @@
display: none;

/* Prevent spacing issues since the languages aren't displayed */
.a-tagline {
cfpb-tagline {
float: none;
}
}
}
}

&--list {
padding: math.div(math.div($grid-gutter-width, 2), $base-font-size-px) +
rem math.div(math.div($grid-gutter-width, 2), $base-font-size-px) +
rem math.div($grid-gutter-width, $base-font-size-px) + rem math.div($grid-gutter-width, $base-font-size-px) + rem;
padding: math.div(math.div($grid-gutter-width, 2), $base-font-size-px) + rem
math.div(math.div($grid-gutter-width, 2), $base-font-size-px) + rem
math.div($grid-gutter-width, $base-font-size-px) + rem
math.div($grid-gutter-width, $base-font-size-px) + rem;

.m-global-eyebrow__actions {
padding: 0;
Expand All @@ -94,27 +99,33 @@

.m-global-eyebrow__phone {
border-top: 1px solid var(--gray-40);
padding-top: math.div(math.div($grid-gutter-width, 2), $base-font-size-px) +
padding-top: math.div(
math.div($grid-gutter-width, 2),
$base-font-size-px
) +
'em';

// Apply padding to the phone number to increase the touch area.
a {
padding-top: math.div(
math.div($grid-gutter-width, 2),
$base-font-size-px
) + rem;
) +
rem;
padding-bottom: math.div(
math.div($grid-gutter-width, 2),
$base-font-size-px
) + rem;
) +
rem;
}
}

.m-global-eyebrow__languages {
margin-bottom: math.div(
math.div($grid-gutter-width, 2),
$base-font-size-px
) + rem;
) +
rem;

.m-list__item {
// Apply padding to the language links to increase their touch area.
Expand All @@ -125,11 +136,13 @@
padding-top: math.div(
math.div($grid-gutter-width, 2),
$base-font-size-px
) + rem;
) +
rem;
padding-bottom: math.div(
math.div($grid-gutter-width, 2),
$base-font-size-px
) + rem;
) +
rem;
}
}
}
Expand Down
6 changes: 4 additions & 2 deletions src/components/Banner/banner.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import classnames from 'classnames';
import { JSX } from 'react';
import { CfpbTagline } from '../../elements/cfpb-tagline';
import type { JSXElement } from '../../types/jsx-element';
import { Tagline } from '../Tagline/tagline';
import './banner.scss';

CfpbTagline.init();

interface BannerProperties extends React.HTMLProps<HTMLDivElement> {
isHorizontal?: boolean;
links?: JSX.Element[];
Expand Down Expand Up @@ -58,7 +60,7 @@ export const Banner = ({
eyebrowClasses.push('m-global-eyebrow--horizontal');
wrapperClasses.push('wrapper--match-content');
linkListClasses.push('m-list--horizontal m-global-eyebrow__languages');
taglineContent = <Tagline>{tagline}</Tagline>;
taglineContent = <cfpb-tagline>{tagline}</cfpb-tagline>;
} else {
eyebrowClasses.push('m-global-eyebrow--list');
}
Expand Down
11 changes: 1 addition & 10 deletions src/components/Footer/footer-banner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,6 @@ import { JSX } from 'react';

export const FooterBanner = (): JSX.Element => (
<div className='o-footer__post'>
<div
className='a-tagline a-tagline--large'
aria-label='Official website of the United States government'
>
<span className='u-usa-flag' />
<div className='a-tagline__text'>
An official website of the&nbsp;
<span className='u-nowrap'>United States government</span>
</div>
</div>
<cfpb-tagline isLarge></cfpb-tagline>
</div>
);
9 changes: 5 additions & 4 deletions src/components/Footer/footer.test.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import '@testing-library/jest-dom';
import { render, screen } from '@testing-library/react';
import Footer from './footer';

describe('Footer', () => {
it('Skips empty lists', () => {
render(<Footer />);
const { container } = render(<Footer />);

// List not rendered
const list = screen.queryAllByRole('list');
Expand All @@ -13,9 +14,9 @@ describe('Footer', () => {
const items = screen.queryAllByRole('listitem');
expect(items.length).toEqual(0);

// Banner displayed (text is broken up, hence the multiple queries)
expect(screen.getByText('An official website of the'));
expect(screen.getByText('United States government'));
// Find tagline web component.
const tagline = container.querySelector('cfpb-tagline');
expect(tagline).toBeInTheDocument();
});

it('Renders NavLinks', () => {
Expand Down
17 changes: 6 additions & 11 deletions src/components/Tagline/tagline.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import type { Meta, StoryObj } from '@storybook/react-vite';
import { Tagline } from '~/src/index';
import { CfpbTagline } from '../../elements/cfpb-tagline';

const meta: Meta<typeof Tagline> = {
CfpbTagline.init();

const meta: Meta<typeof CfpbTagline> = {
title: 'Components (Verified)/Taglines',
tags: ['autodocs'],
component: Tagline,
component: CfpbTagline,
parameters: {
docs: {
description: {
Expand All @@ -24,14 +26,7 @@ export default meta;
type Story = StoryObj<typeof meta>;

export const StandardTagline: Story = {
render: (properties) => (
<Tagline {...properties}>
<>
An official website of the{' '}
<span className='u-nowrap'>United States government</span>
</>
</Tagline>
),
render: (properties) => <cfpb-tagline {...properties}></cfpb-tagline>,
};
StandardTagline.storyName = 'Standard tagline';

Expand Down
32 changes: 24 additions & 8 deletions src/components/Tagline/tagline.test.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,36 @@
import '@testing-library/jest-dom';
import { render, screen } from '@testing-library/react';
import { Tagline } from './tagline';
import { CfpbTagline } from '../../elements/cfpb-tagline';

describe('<Tagline />', () => {
beforeEach(() => {
CfpbTagline.init();
});

it('renders tagline text correctly', () => {
render(<Tagline>Hoboken</Tagline>);
render(<cfpb-tagline>Hoboken</cfpb-tagline>);
expect(screen.getByText('Hoboken')).toBeInTheDocument();
});

it('renders usa flag', () => {
render(<Tagline>tagline</Tagline>);
expect(screen.getByTestId('usa-flag')).toBeInTheDocument();
it('renders usa flag', async () => {
render(<cfpb-tagline isLarge>tagline</cfpb-tagline>);
const elm = screen.getByText('tagline');
const shadowRoot = elm?.shadowRoot;

await new Promise((resolve) => requestAnimationFrame(resolve));

expect(shadowRoot?.querySelector('cfpb-flag-usa')).toBeInTheDocument();
});

it('renders large version', () => {
render(<Tagline isLarge>tagline</Tagline>);
expect(screen.getByTestId('tagline')).toHaveClass('a-tagline--large');
it('renders large version', async () => {
render(<cfpb-tagline isLarge>tagline</cfpb-tagline>);
const elm = screen.getByText('tagline');
const shadowRoot = elm?.shadowRoot;

await new Promise((resolve) => requestAnimationFrame(resolve));

expect(shadowRoot?.querySelector('.a-tagline')).toHaveClass(
'a-tagline--large',
);
});
});
23 changes: 0 additions & 23 deletions src/components/Tagline/tagline.tsx

This file was deleted.

12 changes: 12 additions & 0 deletions src/elements/cfpb-flag-usa/cfpb-flag-usa.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
:host {
display: inline-block;

// Standard flag size.
width: 24px;
height: 13px;

// 48px x 25px USA flag image.
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAZCAMAAABAf11LAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAE5QTFRF////sxlC7MbQ2YyhxlNxCjFhR2WJV3GSKUt1dYumOFh/GT5rhZiwwszYsr/OlKW6Zn6c0djh8PL1iR9Ko7LE4OXrl0pttKC0pXWRtYKbSuJhRQAAANFJREFUeNrkkctuwyAUREnSuW/ApHYf//+jBVdZVcJi3aORgAXcMyLBAAJEzsVG3m8TkifyI3zfPQ6nJJLo421CArSBmkgjNEWtQE4zXJmClXuCWIlU5hdQxCqbqnE1KdIz79CVDvBwZxyKfQfmHTyzl01UZSvOWSTbhZLSWeDMufWLC/1ls3amT4qQq394EjIjApxBT+/nr8eEBNuKcB9SWMpmEXalNOylmlUZNTr4vE/4VdKhpC+leQf6y/e0wzL3RdJtkfUJyzwW+ZcdfgQYAJmJD3zerW6OAAAAAElFTkSuQmCC');
background-size: contain;
background-repeat: no-repeat;
}
14 changes: 14 additions & 0 deletions src/elements/cfpb-flag-usa/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { LitElement } from 'lit';
import styles from './cfpb-flag-usa.component.scss';

/**
* @element cfpb-flag-usa
*/
export class CfpbFlagUsa extends LitElement {
static styles = styles;

static init() {
globalThis.customElements.get('cfpb-flag-usa') ??
globalThis.customElements.define('cfpb-flag-usa', CfpbFlagUsa);
}
}
33 changes: 33 additions & 0 deletions src/elements/cfpb-tagline/cfpb-tagline.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
@use 'sass:math';
@use '@cfpb/cfpb-design-system/src/elements/abstracts' as *;

:host {
.a-tagline {
font-size: math.div(12px, $base-font-size-px) + rem;

display: grid;
grid-template-columns: 22px 1fr;
column-gap: 10px;

&__text {
// Needed for browsers with legacy/no grid support (e.g. IE11).
display: inline-block;
}

& cfpb-flag-usa {
margin-top: 1px;
}

&--large {
font-size: math.div(16px, $base-font-size-px) + rem;

& cfpb-flag-usa {
margin-top: 4px;
}
}
}

.u-nowrap {
white-space: nowrap;
}
}
51 changes: 51 additions & 0 deletions src/elements/cfpb-tagline/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { html, LitElement } from 'lit';
import { CfpbFlagUsa } from '../cfpb-flag-usa';
import styles from './cfpb-tagline.component.scss';

/**
* @element cfpb-icon-text
* @slot - The main content for the tagline.
*/
export class CfpbTagline extends LitElement {
static styles = styles;

/**
* @property {boolean} isLarge - Whether to use the larger tagline appearance.
* @returns {object} The map of properties.
*/
static properties = {
isLarge: { type: Boolean, reflect: true },
};

constructor() {
super();
this.isLarge = false;
}

render() {
const baseClasses = ['a-tagline'];
if (this.isLarge) baseClasses.push('a-tagline--large');

return html`
<div
class="${baseClasses.join(' ')}"
aria-label="Official website of the United States government"
>
<cfpb-flag-usa></cfpb-flag-usa>
<div class="a-tagline__text">
<slot>
An official website of the
<span class="u-nowrap">United States government</span>
</slot>
</div>
</div>
`;
}

static init() {
CfpbFlagUsa.init();

globalThis.customElements.get('cfpb-tagline') ??
globalThis.customElements.define('cfpb-tagline', CfpbTagline);
}
}
Loading
Loading