diff --git a/cypress/e2e/about-us/about-us-links.cy.ts b/cypress/e2e/about-us/about-us-links.cy.ts new file mode 100644 index 000000000..0e9b306e7 --- /dev/null +++ b/cypress/e2e/about-us/about-us-links.cy.ts @@ -0,0 +1,130 @@ +describe('About Us Links interaction flow', () => { + beforeEach(() => { + cy.visit('/about-us/'); + }); + + it('Navigates to the About Us page', () => { + cy.findAllByText('About Fiscal Data').should('exist'); + }); + + it('Validates all links on the page function as intended', () => { + const links: object[] = [ + { + name: 'Bureau of the Fiscal Service (Fiscal Service)', + url: 'https://fiscal.treasury.gov/about.html', + external: true, + }, + { + name: 'getting started section', + url: 'api-documentation/#getting-started', + external: false, + }, + { + name: 'International Monetary Fund (IMF)', + url: 'https://www.imf.org/en/home', + external: true, + }, + { + name: 'Special Data Dissemination Standard (SDDS)', + url: + 'https://www.imf.org/en/About/Factsheets/Sheets/2023/Standards-for-data-dissemination#:~:text=The%20Special%20Data%20Dissemination%20System,access%20to%20international%20capital%20markets.', + external: true, + }, + { + name: 'Monthly Statement of the Public Debt (MSPD)', + url: 'datasets/monthly-statement-public-debt/summary-of-treasury-securities-outstanding', + external: false, + }, + { + name: 'Monthly Treasury Statement (MTS)', + url: 'datasets/monthly-treasury-statement/summary-of-receipts-outlays-and-the-deficit-surplus-of-the-u-s-government', + external: false, + }, + { + name: 'IMF SDDS website', + url: + 'https://www.imf.org/en/About/Factsheets/Sheets/2023/Standards-for-data-dissemination#:~:text=The%20Special%20Data%20Dissemination%20System,access%20to%20international%20capital%20markets.', + external: true, + }, + { + name: 'API Documentation', + url: 'api-documentation/', + external: false, + }, + ]; + + links.forEach(link => { + if (link.external) { + cy.findByRole('link', { name: link.name }).should('have.attr', 'href', link.url); + } else { + cy.findByRole('link', { name: link.name }).click(); + cy.url().should('include', link.url); + cy.visit('/about-us/'); + } + }); + }); + + it('Validates duplicate links', () => { + const duplicateLinks: object[] = [ + { + name: 'visit their Contact Us page', + url: 'https://fiscal.treasury.gov/top/contact.html', + external: true, + }, + { + name: 'Economic Impact Payments Page', + url: 'http://www.irs.gov/coronavirus-tax-relief-and-economic-impact-payments', + external: true, + }, + { + name: "Where's My Refund?", + url: 'https://www.irs.gov/refunds', + external: true, + }, + { + name: 'contact the Bureau of the Fiscal Service', + url: 'https://fiscal.treasury.gov/contact/', + external: true, + }, + { + name: 'visit their Contact Us page', + url: 'https://fiscal.treasury.gov/top/contact.html', + external: true, + }, + { + name: 'Economic Impact Payments Page', + url: 'http://www.irs.gov/coronavirus-tax-relief-and-economic-impact-payments', + external: true, + }, + ]; + + duplicateLinks.forEach(link => { + const dupes = cy.findAllByRole('link', { name: link.name }); + + dupes.each(dupe => { + cy.wrap(dupe).should('have.attr', 'href', link.url); + }); + }); + }); + + it('Validates download links', () => { + const downloadLinks: object[] = [ + { + name: 'Open Data Policy', + url: '/data/about-us/901-1 Open Data Policy.pdf', + }, + { + name: 'https://fiscaldata.treasury.gov/static-data/reports-statements/mts/imf/cgd.xml', + url: 'https://fiscaldata.treasury.gov/static-data/reports-statements/mts/imf/cgd.xml', + }, + { + name: 'https://fiscaldata.treasury.gov/static-data/reports-statements/mts/imf/cgo.xml', + url: 'https://fiscaldata.treasury.gov/static-data/reports-statements/mts/imf/cgo.xml', + }, + ]; + + downloadLinks.forEach(link => { + cy.findByRole('link', { name: link.name }).should('have.attr', 'href', link.url); + }); + }); +}); diff --git a/cypress/e2e/about-us/about-us-sub-nav.cy.ts b/cypress/e2e/about-us/about-us-sub-nav.cy.ts new file mode 100644 index 000000000..8fc92e638 --- /dev/null +++ b/cypress/e2e/about-us/about-us-sub-nav.cy.ts @@ -0,0 +1,48 @@ +describe('About us interaction flow', () => { + beforeEach(() => { + cy.visit('/about-us/').wait(1500); + }); + + it('Verifies about us page loads', () => { + cy.findAllByText('About Fiscal Data').should('exist'); + }); + it('Verifies about us sub nav interaction flow FAQ', () => { + cy.findByTitle('Why do some datasets...') + .should('exist') + .click(); + cy.url().should('include', '/#why-datasets-go-further-than-others'); + }); + it('Verifies keyboard interaction about us sub nav interaction flow FAQ', () => { + cy.findByTitle('I’m new to using APIs...') + .should('exist') + .focus() + .type('{enter}'); + cy.url().should('include', '/#new-to-apis'); + }); + it('Verifies about us sub nav interaction flow About Fiscal Data', () => { + cy.findByTitle('Background') + .should('exist') + .click(); + cy.url().should('include', '/#background'); + }); + it('Verifies keyboard interaction about us sub nav interaction flow About Fiscal Data', () => { + cy.findByTitle('Mission') + .should('exist') + .focus() + .type('{enter}'); + cy.url().should('include', '/#mission'); + }); + it('Verifies about us sub nav interaction flow Contact Us', () => { + cy.findByTitle('Contact Us') + .should('exist') + .click(); + cy.url().should('include', '/#contact-us'); + }); + it('Verifies keyboard interaction about us sub nav interaction flow About Contact Us', () => { + cy.findByTitle('Contact Us') + .should('exist') + .focus() + .type('{enter}'); + cy.url().should('include', '/#contact-us'); + }); +}); diff --git a/cypress/e2e/dataset-detail-page/ddp-related-dataset.cy.ts b/cypress/e2e/dataset-detail-page/ddp-related-dataset.cy.ts new file mode 100644 index 000000000..00b862cf5 --- /dev/null +++ b/cypress/e2e/dataset-detail-page/ddp-related-dataset.cy.ts @@ -0,0 +1,20 @@ +describe('DDP related dataset go to the correct datasets', () => { + beforeEach(() => { + cy.visit('/datasets/monthly-treasury-statement/summary-of-receipts-outlays-and-the-deficit-surplus-of-the-u-s-government').wait(3000); + }); + it('Validates the DDP page loads', () => { + cy.findByTitle('Monthly Treasury Statement (MTS)').should('be.visible'); + cy.findAllByText('Debt to the Penny') + .eq(1) + .click(); + cy.url().should('include', '/debt-to-the-penny'); + cy.findAllByText('Daily Treasury Statement (DTS)') + .eq(1) + .click(); + cy.url().should('include', '/daily-treasury-statement/operating-cash-balance'); + cy.findAllByText('Monthly Treasury Statement (MTS)') + .eq(1) + .click(); + cy.url().should('include', '/summary-of-receipts-outlays-and-the-deficit-surplus-of-the-u-s-government'); + }); +}); diff --git a/cypress/e2e/glossary terms/glossary-terms.cy.ts b/cypress/e2e/glossary terms/glossary-terms.cy.ts new file mode 100644 index 000000000..acc997684 --- /dev/null +++ b/cypress/e2e/glossary terms/glossary-terms.cy.ts @@ -0,0 +1,59 @@ +describe('Glossary terms interaction flow', () => { + beforeEach(() => { + cy.visit('/').wait(3000); + cy.findAllByText('Resources').click(); + cy.findAllByText('Glossary') + .click() + .wait(3000); + }); + it('Verifies Glossary opens and closes correctly', () => { + cy.findByText('Search the glossary').should('exist'); + cy.findByLabelText('Close glossary').click(); + cy.findByText('Search the glossary').should('not.exist'); + }); + + it('Search bar search and Back to list interaction', () => { + cy.findAllByLabelText('Search the glossary').should('be.visible'); + cy.findAllByLabelText('Search the glossary') + .eq(1) + .type('Revenue'); + cy.findAllByText('Back to list') + .should('exist') + .click(); + cy.findAllByLabelText('Search the glossary') + .eq(1) + .type('Debt Ceiling') + .click(); + cy.findAllByText('Back to list') + .should('exist') + .click(); + }); + it('Search bar click into a selected search', () => { + cy.findAllByLabelText('Search the glossary') + .eq(1) + .type('Debt Ceiling'); + cy.findAllByText('Debt Ceiling') + .should('exist') + .click(); + cy.findAllByText( + 'This is the maximum amount of money the federal government is allowed to borrow without receiving additional authority from Congress.' + ).should('exist'); + }); + it('Search bar interaction with Floating Rate Notes and external link', () => { + cy.findAllByLabelText('Search the glossary') + .eq(1) // Target the correct search bar + .should('be.visible') + .type('Floating Rate Notes'); + cy.findAllByText('Floating Rate Notes') + .should('exist') + .click(); + cy.findAllByText('TreasuryDirect') + .should('exist') + .invoke('attr', 'href') + .should('include', '/marketable-securities/floating-rate-notes/'); + + cy.findAllByText('TreasuryDirect') + .invoke('removeAttr', 'target') + .click(); + }); +}); diff --git a/src/components/anchor-text/anchor-text.tsx b/src/components/anchor-text/anchor-text.tsx index bf9304ff5..9128d26c5 100644 --- a/src/components/anchor-text/anchor-text.tsx +++ b/src/components/anchor-text/anchor-text.tsx @@ -4,12 +4,19 @@ import CustomLink from '../links/custom-link/custom-link'; type IAnchorText = { link: string; text: string; + onAnchorClick?: (anchorId: string) => void; }; -const AnchorText = ({ link, text }: IAnchorText): JSX.Element => { +const AnchorText = ({ link, text, onAnchorClick }: IAnchorText): JSX.Element => { + const handleClick = () => { + if (onAnchorClick) { + onAnchorClick(link); + } + }; + return ( - + {text} diff --git a/src/components/anchor-text/anchor.spec.js b/src/components/anchor-text/anchor.spec.js index 8623fb35b..a61d52b1c 100644 --- a/src/components/anchor-text/anchor.spec.js +++ b/src/components/anchor-text/anchor.spec.js @@ -1,17 +1,29 @@ import React from 'react'; -import { render } from '@testing-library/react'; +import { render, fireEvent } from '@testing-library/react'; import AnchorText from './anchor-text'; -const mockFootnote = { - text: 'Test Footnote Text', - link: 'testFootnote', - body: - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque vulputate odio a enim hendrerit interdum. Duis volutpat, nibh porttitor pellentesque mattis, mi justo', -}; +jest.mock('../links/custom-link/custom-link', () => { + return ({ onClick, children, 'data-testid': dataTestId }) => { + return ( + + ); + }; +}); -describe('Anchor Text', () => { - it('it renders the Anchor Text component', () => { - const { getByTestId } = render(); +describe('AnchorText', () => { + it('renders the Anchor Text component', () => { + const { getByTestId } = render(); expect(getByTestId('anchor-text')).toBeInTheDocument(); }); + + it('calls onAnchorClick with the link when clicked', () => { + const mockClickHandler = jest.fn(); + const { getByTestId } = render(); + fireEvent.click(getByTestId('anchor-text')); + + expect(mockClickHandler).toHaveBeenCalledWith('testFootnote'); + expect(mockClickHandler).toHaveBeenCalledTimes(1); + }); }); diff --git a/src/components/api-documentation/aggregation/aggregation.jsx b/src/components/api-documentation/aggregation/aggregation.jsx index 479b8a0c2..cb9d65d17 100644 --- a/src/components/api-documentation/aggregation/aggregation.jsx +++ b/src/components/api-documentation/aggregation/aggregation.jsx @@ -13,7 +13,9 @@ const Aggregation = () => { and summing of their numeric values, etc. You should use this when searching for the sum total of a specific field.

For example, the API call for the sum total of the opening monthly balance within the Daily Treasury Statement dataset would read as:

- {baseApiUrl}/v1/accounting/dts/dts_table_2?fields=record_date,transaction_today_amt + + {baseApiUrl}/v1/accounting/dts/deposits_withdrawals_operating_cash?fields=record_date,transaction_today_amt +

Running this API call will yield a sum of all the totals in the selected field. In this case, the call yields the total sum of all opening monthly balances over the course of all dates available in the dataset. diff --git a/src/components/api-documentation/examples/examples.jsx b/src/components/api-documentation/examples/examples.jsx index 9ed812ae7..2661a1bee 100644 --- a/src/components/api-documentation/examples/examples.jsx +++ b/src/components/api-documentation/examples/examples.jsx @@ -36,7 +36,7 @@ const Examples = () => { {baseApiUrl} /v1/accounting/od/rates_of_exchange?fields=country_currency_desc,exchange_rate, - record_date&filter=country_currency_desc:in:(Canada-Dollar,Mexico-Peso), record_date:gte:2020-01-01 + record_date&filter=country_currency_desc:in:(Canada-Dollar,Mexico-Peso),record_date:gte:2020-01-01 @@ -49,8 +49,8 @@ const Examples = () => {

  • return the most recent data first , i.e., return data sorted by year (descending order) and then month (descending order)
  • - {baseApiUrl}/v2/accounting/od/debt_to_penny?fields=record_calendar_year, - record_calendar_month&sort=-record_calendar_year,-record_calendar_month + {baseApiUrl} + /v2/accounting/od/debt_to_penny?fields=record_calendar_year,record_calendar_month&sort=-record_calendar_year,-record_calendar_month @@ -69,13 +69,19 @@ const Examples = () => { {baseApiUrl}/v1/debt/top/top_state?page[number]=10&page[size]=50 - More documentation is expected to be added about aggregation. - - - More documentation is expected to be added about pivoting. + For the Daily Treasury Statement dataset, +
      +
    • Return the sum of all transactions today amounts for each transaction type on each record date
    • +
    + + {baseApiUrl}/v1/accounting/dts/deposits_withdrawals_operating_cash?fields=record_date,transaction_type,transaction_today_amt +
    - More documentation is expected to be added about multi-dimension datasets. + + Many Fiscal Data datasets contain multiple tables or APIs, which relate to each other. Please see the Data Dictionary, Data Tables, + Metadata, and Notes & Known Limitations tabs within the dataset properties section of each dataset page for more information. + ); diff --git a/src/components/api-documentation/examples/examples.spec.js b/src/components/api-documentation/examples/examples.spec.js index d328afba1..f96fe94ec 100644 --- a/src/components/api-documentation/examples/examples.spec.js +++ b/src/components/api-documentation/examples/examples.spec.js @@ -56,12 +56,6 @@ describe('API Documentation Examples', () => { expect(heading.children[0]).toBe(title); }); - it('creates the Pivoting section with the desired id, heading tag and title', () => { - const title = 'Pivoting'; - const heading = instance.findByProps({ id: 'examples-pivoting' }).findByType('h3'); - expect(heading.children[0]).toBe(title); - }); - it('creates the Multi-dimension Datasets section with the desired id, heading tag and title', () => { const title = 'Multi-dimension Datasets'; const heading = instance.findByProps({ id: 'examples-multi-dimension-datasets' }).findByType('h3'); diff --git a/src/components/api-documentation/filters/filters.jsx b/src/components/api-documentation/filters/filters.jsx index f6fdff809..b63b350e5 100644 --- a/src/components/api-documentation/filters/filters.jsx +++ b/src/components/api-documentation/filters/filters.jsx @@ -24,7 +24,8 @@ const Filters = () => ( Accepts: The filter parameter filter= accepts filters from the list below, as well as specified filter criteria. Use a colon at the end of a filter parameter to pass a value or list of values. For lists passed as filter criteria, use a comma-separated list within parentheses. Filter for specific dates using the format YYYY-MM-DD - . + . To filter by multiple fields in a single request, do not repeat a filter call. Instead, apply an additional field to include in the filter + separated by a comma, as shown in the following template: &filter=field:prm:value,field:prm:value

    Required: No, filters are not required to make an API request. diff --git a/src/components/api-documentation/getting-started/getting-started.jsx b/src/components/api-documentation/getting-started/getting-started.jsx index c8100a3a3..64541e14e 100644 --- a/src/components/api-documentation/getting-started/getting-started.jsx +++ b/src/components/api-documentation/getting-started/getting-started.jsx @@ -88,7 +88,7 @@ const GettingStarted = () => {

    FULL API REQUEST EXAMPLE:
    {baseApiUrl} - /v1/accounting/od/rates_of_exchange?fields=country_currency_desc,exchange_rate, record_date&filter=record_date:gte:2015-01-01 + /v1/accounting/od/rates_of_exchange?fields=country_currency_desc,exchange_rate,record_date&filter=record_date:gte:2015-01-01 diff --git a/src/components/data-preview/data-preview-filter-section/data-preview-download/data-preview-download-button/data-preview-download-button.tsx b/src/components/data-preview/data-preview-filter-section/data-preview-download/data-preview-download-button/data-preview-download-button.tsx index 1db93c928..692ce51d2 100644 --- a/src/components/data-preview/data-preview-filter-section/data-preview-download/data-preview-download-button/data-preview-download-button.tsx +++ b/src/components/data-preview/data-preview-filter-section/data-preview-download/data-preview-download-button/data-preview-download-button.tsx @@ -29,7 +29,7 @@ const DataPreviewDownloadButton: FunctionComponent = ({ ac = pxToNumber(breakpointLg))} /> - {active && } + ); }; diff --git a/src/components/download-dialog/download-dialog.spec.js b/src/components/download-dialog/download-dialog.spec.js index 4e4e68bde..1b8da661a 100644 --- a/src/components/download-dialog/download-dialog.spec.js +++ b/src/components/download-dialog/download-dialog.spec.js @@ -1,13 +1,20 @@ import React from 'react'; import { render } from '@testing-library/react'; import { DownloadDialog } from './download-dialog'; +import userEvent from '@testing-library/user-event'; describe('Download dialog', () => { it('download dialog renders with download options', () => { - const { getByText } = render(); + const { getByText } = render(); expect(getByText('CSV')).toBeInTheDocument(); expect(getByText('JSON')).toBeInTheDocument(); expect(getByText('XML')).toBeInTheDocument(); expect(getByText('Data Dictionary')).toBeInTheDocument(); }); + + it('keyboard interaction', () => { + const { getByText } = render(); + userEvent.tab(getByText('CSV')); + userEvent.tab(getByText('JSON')); + }); }); diff --git a/src/components/download-dialog/download-dialog.tsx b/src/components/download-dialog/download-dialog.tsx index 98574351f..0f405a937 100644 --- a/src/components/download-dialog/download-dialog.tsx +++ b/src/components/download-dialog/download-dialog.tsx @@ -1,27 +1,52 @@ -import React, { FunctionComponent } from 'react'; +import React, { FunctionComponent, useEffect, useRef, useState } from 'react'; import { container, item, border } from './download-dialog.module.scss'; -export const DownloadDialog: FunctionComponent = () => { +export const DownloadDialog: FunctionComponent = ({ active, setActive }) => { + const containerRef = useRef(null); + const [inFocus, setInFocus] = useState(false); + + useEffect(() => { + if (!inFocus) { + setActive(false); + } + }, [inFocus]); + return ( -
    -
    - CSV - 84 KB -
    -
    - JSON - 84 KB -
    -
    - XML - 84 KB -
    -
    -
    - Data Dictionary - 24 KB -
    -
    + <> + {active ? ( +
    setInFocus(true)} + onBlur={e => { + if (!e.relatedTarget.className.includes('download-dialog')) { + setInFocus(false); + } + }} + > +
    + CSV + 84 KB +
    +
    + JSON + 84 KB +
    +
    + XML + 84 KB +
    +
    +
    + Data Dictionary + 24 KB +
    +
    + ) : ( + <> + )} + ); }; diff --git a/src/components/footnote/footnote.spec.js b/src/components/footnote/footnote.spec.js index 133f32acd..b7559327a 100644 --- a/src/components/footnote/footnote.spec.js +++ b/src/components/footnote/footnote.spec.js @@ -1,25 +1,39 @@ import React from 'react'; -import { render } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import Footnote from './footnote'; const mockFootnotes = [ { anchors: [ - { text: ['1'], link: ['mock-link1'] }, - { text: ['2'], link: ['mock-link2'] }, + { text: '1', link: 'mock-link1' }, + { text: '2', link: 'mock-link2' }, ], definition: 'Mock Definition', }, { - anchors: [{ text: ['3'], link: ['mock-link3'] }], + anchors: [{ text: '3', link: 'mock-link3' }], definition: 'Mock Definition2', }, ]; describe('Footnote Section', () => { - it('it renders the footnote section and items', () => { + it('renders the footnote section and items', () => { const { getByTestId, getAllByTestId } = render(); expect(getByTestId('footnote-section')).toBeInTheDocument(); expect(getAllByTestId('footnote-item')).toHaveLength(2); }); + + it('renders with custom width', () => { + render(); + + screen.getAllByTestId('footnote-item').forEach(item => { + expect(item).toHaveStyle({ width: '50%' }); + }); + }); + + it('renders safely with no footnotes', () => { + render(); + expect(screen.getByTestId('footnote-section')).toBeInTheDocument(); + expect(screen.queryByTestId('footnote-item')).toBeNull(); + }); }); diff --git a/src/components/footnote/footnote.tsx b/src/components/footnote/footnote.tsx index d552304ca..bbd5b2e15 100644 --- a/src/components/footnote/footnote.tsx +++ b/src/components/footnote/footnote.tsx @@ -2,40 +2,38 @@ import React, { FunctionComponent } from 'react'; import { footnoteHeading, footnoteBody, footnoteContainer } from './footnote.module.scss'; import CustomLink from '../links/custom-link/custom-link'; +type FootnoteAnchor = { + text?: string; + link?: string; +}; + +type FootnoteItem = { + anchors: FootnoteAnchor[]; + definition?: JSX.Element | (() => JSX.Element); +}; + type FootnoteProps = { - footnotes: [ - { - anchors: [ - { - text?: string; - link?: string; - } - ]; - definition?: JSX.Element | (() => JSX.Element); - } - ]; + footnotes: FootnoteItem[]; width?: string; + onBackToContentClick?: () => void; }; -const Footnote: FunctionComponent = ({ footnotes, width = '80%' }): JSX.Element => { +const Footnote: FunctionComponent = ({ footnotes, width = '80%', onBackToContentClick, lastAnchorClicked }) => { return ( -
    +
    Footnotes
    - {footnotes && - footnotes.map((footnote, idx) => { - return ( -
    - {footnote.anchors.map((anchor, index) => ( - - - {anchor.text} - - - ))} - {footnote.definition} -
    - ); - })} + {footnotes?.map((footnote, idx) => ( +
    + {footnote.anchors.map((anchor, index) => ( + + + {anchor.text} + + + ))} + {footnote.definition} Back to content +
    + ))}
    ); }; diff --git a/src/components/links/custom-link/custom-link.tsx b/src/components/links/custom-link/custom-link.tsx index 6e90106bd..f714a1eda 100644 --- a/src/components/links/custom-link/custom-link.tsx +++ b/src/components/links/custom-link/custom-link.tsx @@ -90,7 +90,7 @@ const CustomLink: FunctionComponent = ({ case urlOrHref.startsWith('#'): return ( - + {children} ); diff --git a/src/components/links/page-scroll-link/page-scroll-link.jsx b/src/components/links/page-scroll-link/page-scroll-link.jsx index 6ffd68af3..a68a41500 100644 --- a/src/components/links/page-scroll-link/page-scroll-link.jsx +++ b/src/components/links/page-scroll-link/page-scroll-link.jsx @@ -6,7 +6,7 @@ import { scrollLink } from './page-scroll-link.module.scss'; const scrollDelay = globalConstants.config.smooth_scroll.delay; const scrollDuration = globalConstants.config.smooth_scroll.duration; -const scrollOffset = -50; +const scrollOffset = -150; const scrollOptions = { smooth: true, spy: true, @@ -19,15 +19,22 @@ const scrollOptionsOffset = { offset: scrollOffset, }; -const PageScrollLink = ({ url, dataTestId, id, tabindex = 0, children }) => { +const PageScrollLink = ({ url, dataTestId, id, tabindex = 0, children, handleClick }) => { const handleInteraction = (e, url) => { //only proceed on mouse click or Enter key press if (e?.key && e.key !== 'Enter') { return; } + if (e) { + e.preventDefault(); + e.stopPropagation(); + } + if (url) { + const footnoteElem = document.getElementById(url.substr(1)); scroller.scrollTo(url.substr(1), scrollOptionsOffset); + footnoteElem?.focus({ preventScroll: true }); } }; @@ -37,9 +44,10 @@ const PageScrollLink = ({ url, dataTestId, id, tabindex = 0, children }) => { onKeyDown={e => handleInteraction(e, url)} onClick={() => handleInteraction(null, url)} className={scrollLink} - role="link" - id={id} + id={`${id}-footnote`} tabIndex={tabindex} + role="link" + // href={url} > {children} diff --git a/src/components/links/page-scroll-link/page-scroll-link.module.scss b/src/components/links/page-scroll-link/page-scroll-link.module.scss index 347bd1634..05e126bd9 100644 --- a/src/components/links/page-scroll-link/page-scroll-link.module.scss +++ b/src/components/links/page-scroll-link/page-scroll-link.module.scss @@ -1,7 +1,8 @@ -@import "src/variables.module"; -@import "src/styles"; +@import 'src/variables.module'; +@import 'src/styles'; .scrollLink { display: inline; @include primaryLink; + scroll-margin-top: 100px; } diff --git a/src/components/links/page-scroll-link/page-scroll-link.spec.js b/src/components/links/page-scroll-link/page-scroll-link.spec.js index 438045341..2b157d094 100644 --- a/src/components/links/page-scroll-link/page-scroll-link.spec.js +++ b/src/components/links/page-scroll-link/page-scroll-link.spec.js @@ -7,7 +7,7 @@ import userEvent from '@testing-library/user-event'; describe('Custom Link', () => { const text = 'sample content'; const content =
    {text}
    ; - const scrollConfigs = { delay: 200, duration: 600, offset: -50, smooth: true, spy: true }; + const scrollConfigs = { delay: 200, duration: 600, offset: -150, spy: true, smooth: true }; afterEach(() => { jest.clearAllMocks(); diff --git a/src/components/published-reports/download-report-table/download-report-table-row/download-report-table-row.spec.js b/src/components/published-reports/download-report-table/download-report-table-row/download-report-table-row.spec.js index e656fdfbf..7d69f8a70 100644 --- a/src/components/published-reports/download-report-table/download-report-table-row/download-report-table-row.spec.js +++ b/src/components/published-reports/download-report-table/download-report-table-row/download-report-table-row.spec.js @@ -1,5 +1,5 @@ import React from 'react'; -import { render, act, fireEvent } from '@testing-library/react'; +import { render, act, fireEvent, getByText } from '@testing-library/react'; import DownloadReportTableRow from './download-report-table-row'; import userEvent from '@testing-library/user-event'; @@ -9,6 +9,7 @@ describe('Download report table row component', () => { { path: '/test/file/path/another_file.xml', report_date: 'Fri Jul 19 2024 00:00:00 GMT-0500', report_group_desc: 'Another Download File (.xml)' }, { path: '/test/file/path/another_file.xls', report_date: 'Fri Jul 19 2024 00:00:00 GMT-0500', report_group_desc: 'Another Download File (.xls)' }, { path: '/test/file/path/another_file.txt', report_date: 'Fri Jul 19 2024 00:00:00 GMT-0500', report_group_desc: 'Another Download File (.txt)' }, + { path: '/test/file/path/another_file.txt', report_date: 'Fri Jul 19 2024 00:00:00 GMT-0500', report_group_desc: 'TST (.txt)' }, ]; beforeEach(() => { @@ -20,6 +21,11 @@ describe('Download report table row component', () => { expect(getByTestId('file-download-row')).toBeInTheDocument(); }); + it('renders a short file name in the row', () => { + const { getByText } = render(); + expect(getByText('TST.txt')).toBeInTheDocument(); + }); + it('renders a pdf icon with a pdf filename', () => { const { getByAltText } = render(); expect(getByAltText('.pdf icon')).toBeInTheDocument(); diff --git a/src/components/published-reports/download-report-table/download-report-table-row/download-report-table-row.tsx b/src/components/published-reports/download-report-table/download-report-table-row/download-report-table-row.tsx index 8ceccb1d6..e2dcd6f7e 100644 --- a/src/components/published-reports/download-report-table/download-report-table-row/download-report-table-row.tsx +++ b/src/components/published-reports/download-report-table/download-report-table-row/download-report-table-row.tsx @@ -99,7 +99,7 @@ const DownloadReportTableRow: FunctionComponent<{ reportFile: IPublishedReportDa return ( <> - {displayName?.start && displayName?.end && ( + {displayName?.end && (
    {`${fileType} - {displayName.start} + {displayName?.start && {displayName.start}} {displayName.end}
    {publishedDate}
    @@ -132,7 +132,7 @@ const DownloadReportTableRow: FunctionComponent<{ reportFile: IPublishedReportDa {`${fileType}
    -
    {displayName.start}
    + {displayName?.start &&
    {displayName.start}
    }
    {displayName.end}
    diff --git a/src/layouts/explainer/sections/treasury-savings-bonds/learn-more/learn-more-helper.js b/src/layouts/explainer/sections/treasury-savings-bonds/learn-more/learn-more-helper.js index b1a737730..ceb470ded 100644 --- a/src/layouts/explainer/sections/treasury-savings-bonds/learn-more/learn-more-helper.js +++ b/src/layouts/explainer/sections/treasury-savings-bonds/learn-more/learn-more-helper.js @@ -1,27 +1,29 @@ import React from 'react'; -import CustomLink from "../../../../../components/links/custom-link/custom-link"; +import CustomLink from '../../../../../components/links/custom-link/custom-link'; export const getSaleBondsFootNotes = () => { return [ { - anchors: [{ text: ['1'],link: ['savings-bonds-overview'] }], + anchors: [{ text: '1', link: 'savings-bonds-overview' }], definition: ( <> - - A History of the United States Savings Bonds Program - , page 5 + + A History of the United States Savings Bonds Program + + , page 5 ), }, { - anchors: [{ text: ['2'], link: ['what-influences-the-purchase-of-savings-bonds'] }], + anchors: [{ text: '2', link: 'what-influences-the-purchase-of-savings-bonds' }], definition: ( <> - - A History of the United States Savings Bonds Program - , page 3 + + A History of the United States Savings Bonds Program + + , page 3 - ) + ), }, ]; }; diff --git a/src/layouts/explainer/sections/treasury-savings-bonds/learn-more/learn-more.spec.js b/src/layouts/explainer/sections/treasury-savings-bonds/learn-more/learn-more.spec.js index 94499bbf0..27d415196 100644 --- a/src/layouts/explainer/sections/treasury-savings-bonds/learn-more/learn-more.spec.js +++ b/src/layouts/explainer/sections/treasury-savings-bonds/learn-more/learn-more.spec.js @@ -6,7 +6,6 @@ import LearnMore from './learn-more'; describe('Learn More Section', () => { it('renders the section', () => { render(); - expect(screen.getByText('Today, individuals can buy Series I and Series EE bonds online through', - {exact: false})).toBeInTheDocument(); + expect(screen.getByText('Today, individuals can buy Series I and Series EE bonds online through', { exact: false })).toBeInTheDocument(); }); }); diff --git a/src/layouts/explainer/sections/treasury-savings-bonds/learn-more/learn-more.tsx b/src/layouts/explainer/sections/treasury-savings-bonds/learn-more/learn-more.tsx index 92be8a2b6..64aef83f9 100644 --- a/src/layouts/explainer/sections/treasury-savings-bonds/learn-more/learn-more.tsx +++ b/src/layouts/explainer/sections/treasury-savings-bonds/learn-more/learn-more.tsx @@ -1,9 +1,27 @@ -import React, { FunctionComponent } from 'react'; +import React, { useState } from 'react'; import CustomLink from '../../../../../components/links/custom-link/custom-link'; import Footnote from '../../../../../components/footnote/footnote'; import { getSaleBondsFootNotes } from './learn-more-helper'; +import AnchorText from '../../../../../components/anchor-text/anchor-text'; + +const LearnMore: React.FC = () => { + const [lastAnchorClicked, setLastAnchorClicked] = useState(null); + + const handleAnchorClick = (anchorId: string) => { + setLastAnchorClicked(anchorId); + }; + + const handleBackToContentClick = () => { + if (lastAnchorClicked) { + const anchorEl = document.getElementById(lastAnchorClicked); + if (anchorEl) { + anchorEl.scrollIntoView({ behavior: 'smooth' }); + anchorEl.focus(); + } + setLastAnchorClicked(null); + } + }; -const LearnMore: FunctionComponent = () => { return ( <>

    @@ -12,7 +30,13 @@ const LearnMore: FunctionComponent = () => { called Treasury Hunt, which allows users to search to see if there are unredeemed bonds in their name.

    - + +

    + This is some text referencing a footnote + +

    + + ); }; diff --git a/src/layouts/explainer/sections/treasury-savings-bonds/purchase-of-savings-bonds/what-influences-purchase-of-savings-bonds.tsx b/src/layouts/explainer/sections/treasury-savings-bonds/purchase-of-savings-bonds/what-influences-purchase-of-savings-bonds.tsx index 8d49b060b..445482dd7 100644 --- a/src/layouts/explainer/sections/treasury-savings-bonds/purchase-of-savings-bonds/what-influences-purchase-of-savings-bonds.tsx +++ b/src/layouts/explainer/sections/treasury-savings-bonds/purchase-of-savings-bonds/what-influences-purchase-of-savings-bonds.tsx @@ -125,7 +125,7 @@ const WhatInfluencesPurchaseOfSavingsBonds: FunctionComponent = ({ cpi12MonthPer

    The sale of U.S. Treasury marketable securities began with the nation’s founding, where private citizens purchased $27 million in government bonds to finance the Revolutionary War. - These early loans to the government were introduced to raise funds + These early loans to the government were introduced to raise funds from the American public to support war efforts as well as other national projects like the construction of the Panama Canal.

    diff --git a/src/layouts/explainer/sections/treasury-savings-bonds/savings-bonds-overview/savings-bonds-overview.tsx b/src/layouts/explainer/sections/treasury-savings-bonds/savings-bonds-overview/savings-bonds-overview.tsx index cc427c43e..957ffd873 100644 --- a/src/layouts/explainer/sections/treasury-savings-bonds/savings-bonds-overview/savings-bonds-overview.tsx +++ b/src/layouts/explainer/sections/treasury-savings-bonds/savings-bonds-overview/savings-bonds-overview.tsx @@ -26,7 +26,7 @@ const SavingsBondsOverview: FunctionComponent = () => { safe investment opportunity to ordinary Americans with the hope that by owning shares in their country, they may become more interested in national policy. - +