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
88 changes: 61 additions & 27 deletions app/helpers/react_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,23 @@

module ReactHelper
def search_results_layout(search, params, vertical, affiliate, search_options)
data = {
additionalResults: govbox_set_data(search),
affiliate: affiliate_data(affiliate),
agencyName: agency_name(affiliate.agency),
alert: search_page_alert(affiliate.alert),
extendedHeader: affiliate.use_extended_header,
facetsEnabled: ENV.fetch('FACETED_SEARCH_ENABLED', 'false') == 'true',
fontsAndColors: affiliate.visual_design_json,
footerLinks: links(affiliate, :footer_links),
jobsEnabled: (affiliate.jobs_enabled? and search.modules.include?('JOBS')),
language: affiliate.language.slice(:code, :rtl),
navigationLinks: navigation_links(search, params),
noResultsMessage: no_result_message(search),
page: page_data(affiliate),
params:,
primaryHeaderLinks: links(affiliate, :primary_header_links),
relatedSearches: related_searches(search),
relatedSites: related_sites(search),
relatedSitesDropdownLabel: affiliate.related_sites_dropdown_label,
resultsData: results_data(search),
secondaryHeaderLinks: links(affiliate, :secondary_header_links),
sitelimit: sitelimit_alert(search, params),
spellingSuggestion: spelling_text(search, search_options),
translations: translations(affiliate.locale),
vertical:
}
safe_join([
search_results_header(affiliate),
search_results_main(search, params, vertical, affiliate, search_options),
search_results_footer(affiliate)
])
end

react_component('SearchResultsLayout', data.compact_blank)
def search_results_header(affiliate)
react_component('SearchResultsHeader', search_results_header_props(affiliate).compact_blank)
end

def search_results_main(search, params, vertical, affiliate, search_options)
react_component('SearchResultsLayout', search_results_layout_props(search, params, vertical, affiliate, search_options).compact_blank)
end

def search_results_footer(affiliate)
react_component('SearchResultsFooter', search_results_footer_props(affiliate).compact_blank)
end

def affiliate_data(affiliate)
Expand Down Expand Up @@ -64,6 +53,51 @@ def logo_text(blob)

private

def search_results_header_props(affiliate)
search_results_context_props(affiliate).merge(
extendedHeader: affiliate.use_extended_header,
page: page_data(affiliate),
primaryHeaderLinks: links(affiliate, :primary_header_links),
secondaryHeaderLinks: links(affiliate, :secondary_header_links)
)
end

def search_results_layout_props(search, params, vertical, affiliate, search_options)
search_results_context_props(affiliate).merge(
additionalResults: govbox_set_data(search),
affiliate: affiliate_data(affiliate),
agencyName: agency_name(affiliate.agency),
alert: search_page_alert(affiliate.alert),
facetsEnabled: ENV.fetch('FACETED_SEARCH_ENABLED', 'false') == 'true',
jobsEnabled: (affiliate.jobs_enabled? and search.modules.include?('JOBS')),
navigationLinks: navigation_links(search, params),
noResultsMessage: no_result_message(search),
page: page_data(affiliate),
params:,
relatedSearches: related_searches(search),
relatedSites: related_sites(search),
relatedSitesDropdownLabel: affiliate.related_sites_dropdown_label,
resultsData: results_data(search),
sitelimit: sitelimit_alert(search, params),
spellingSuggestion: spelling_text(search, search_options),
vertical:
)
end

def search_results_footer_props(affiliate)
search_results_context_props(affiliate).merge(
footerLinks: links(affiliate, :footer_links)
)
end

def search_results_context_props(affiliate)
{
fontsAndColors: affiliate.visual_design_json,
language: affiliate.language.slice(:code, :rtl),
translations: translations(affiliate.locale)
}
end

def related_searches(search)
return [] if search.related_search.nil?

Expand Down
39 changes: 39 additions & 0 deletions app/javascript/components/SearchResultsFooter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from 'react';
import { I18n } from 'i18n-js';

import { Footer } from './Footer/Footer';
import { LanguageContext } from '../contexts/LanguageContext';
import { StyleContext, styles } from '../contexts/StyleContext';
import { FontsAndColors, Language } from './SearchResultsLayout';

interface SearchResultsFooterProps {
footerLinks?: {
title: string;
url: string;
}[];
translations: Record<string, unknown>;
language?: Language;
fontsAndColors?: FontsAndColors;
}

const SearchResultsFooter = ({
footerLinks,
translations,
language = { code: 'en', rtl: false },
fontsAndColors
}: SearchResultsFooterProps) => {
const i18n = new I18n(translations);
i18n.defaultLocale = 'en';
i18n.enableFallback = true;
i18n.locale = language.code;

return (
<LanguageContext.Provider value={i18n}>
<StyleContext.Provider value={fontsAndColors || styles}>
<Footer footerLinks={footerLinks} />
</StyleContext.Provider>
</LanguageContext.Provider>
);
};

export default SearchResultsFooter;
53 changes: 53 additions & 0 deletions app/javascript/components/SearchResultsHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React from 'react';
import { I18n } from 'i18n-js';

import { Header } from './Header';
import { LanguageContext } from '../contexts/LanguageContext';
import { StyleContext, styles } from '../contexts/StyleContext';
import { FontsAndColors, Language, PageData } from './SearchResultsLayout';

interface SearchResultsHeaderProps {
page: PageData;
extendedHeader: boolean;
translations: Record<string, unknown>;
language?: Language;
fontsAndColors?: FontsAndColors;
primaryHeaderLinks?: {
title: string;
url: string;
}[];
secondaryHeaderLinks?: {
title: string;
url: string;
}[];
}

const SearchResultsHeader = ({
page,
extendedHeader,
translations,
language = { code: 'en', rtl: false },
fontsAndColors,
primaryHeaderLinks,
secondaryHeaderLinks
}: SearchResultsHeaderProps) => {
const i18n = new I18n(translations);
i18n.defaultLocale = 'en';
i18n.enableFallback = true;
i18n.locale = language.code;

return (
<LanguageContext.Provider value={i18n}>
<StyleContext.Provider value={fontsAndColors || styles}>
<Header
page={page}
isBasic={!extendedHeader}
primaryHeaderLinks={primaryHeaderLinks}
secondaryHeaderLinks={secondaryHeaderLinks}
/>
</StyleContext.Provider>
</LanguageContext.Provider>
);
};

export default SearchResultsHeader;
29 changes: 1 addition & 28 deletions app/javascript/components/SearchResultsLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@ import { I18n } from 'i18n-js';

import './SearchResultsLayout.css';

import { Header } from './Header';
import { Facets, AggregationData } from './Facets/Facets';
import { SearchBar } from './SearchBar/SearchBar';
import { Results } from './Results/Results';
import { Footer } from './Footer/Footer';
import { LanguageContext } from '../contexts/LanguageContext';
import { StyleContext, styles } from '../contexts/StyleContext';
export interface NavigationLink {
Expand Down Expand Up @@ -168,20 +166,7 @@ interface SearchResultsLayoutProps {
url: string;
};
navigationLinks: NavigationLink[];
extendedHeader: boolean;
fontsAndColors: FontsAndColors;
footerLinks?: {
title: string;
url: string;
}[];
primaryHeaderLinks?: {
title: string;
url: string;
}[];
secondaryHeaderLinks?: {
title: string;
url: string;
}[];
relatedSearches?: { label: string; link: string }[];
relatedSitesDropdownLabel?: string;
agencyName?: string;
Expand Down Expand Up @@ -240,12 +225,8 @@ const GlobalStyle = createGlobalStyle<{ styles: { pageBackgroundColor: string; b
}
`;

const isBasicHeader = (extendedHeader: boolean): boolean => {
return !extendedHeader;
};

// eslint-disable-next-line complexity
const SearchResultsLayout = ({ page, resultsData, additionalResults, vertical, params = {}, translations, language = { code: 'en', rtl: false }, relatedSites = [], extendedHeader, footerLinks, primaryHeaderLinks, secondaryHeaderLinks, fontsAndColors, navigationLinks, relatedSitesDropdownLabel = '', alert, spellingSuggestion, relatedSearches, sitelimit, noResultsMessage, jobsEnabled, agencyName, facetsEnabled }: SearchResultsLayoutProps) => {
const SearchResultsLayout = ({ page, resultsData, additionalResults, vertical, params = {}, translations, language = { code: 'en', rtl: false }, relatedSites = [], fontsAndColors, navigationLinks, relatedSitesDropdownLabel = '', alert, spellingSuggestion, relatedSearches, sitelimit, noResultsMessage, jobsEnabled, agencyName, facetsEnabled }: SearchResultsLayoutProps) => {
const [isMobileView, setMobileView] = useState(false);

const i18n = new I18n(translations);
Expand All @@ -266,12 +247,6 @@ const SearchResultsLayout = ({ page, resultsData, additionalResults, vertical, p
<StyleContext.Consumer>
{(value) => <GlobalStyle styles={{ ...value, facetsEnabled }} />}
</StyleContext.Consumer>
<Header
page={page}
isBasic={isBasicHeader(extendedHeader)}
primaryHeaderLinks={primaryHeaderLinks}
secondaryHeaderLinks={secondaryHeaderLinks}
/>

<div className="usa-section serp-result-wrapper">
<GridContainer>
Expand Down Expand Up @@ -333,8 +308,6 @@ const SearchResultsLayout = ({ page, resultsData, additionalResults, vertical, p
</Grid>
</GridContainer>
</div>

<Footer footerLinks={footerLinks} />
</StyleContext.Provider>
</LanguageContext.Provider>
);
Expand Down
55 changes: 55 additions & 0 deletions app/javascript/test/SearchResultsFooter.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import '@testing-library/jest-dom';
import { render, screen } from '@testing-library/react';
import React from 'react';

import SearchResultsFooter from '../components/SearchResultsFooter';

jest.mock('i18n-js', () => jest.requireActual('i18n-js/dist/require/index'));

const fontsAndColors = {
activeSearchTabNavigationColor: '#1f1748',
bannerBackgroundColor: '#643617',
bannerTextColor: '#dacb1b',
bestBetBackgroundColor: '#6e09bf',
buttonBackgroundColor: '#cfcd03',
footerAndResultsFontFamily: '"Helvetica Neue", "Helvetica", "Roboto", "Arial", sans-serif',
footerBackgroundColor: '#5fcfc5',
footerLinksTextColor: '#46f966',
headerBackgroundColor: '#4a402b',
headerLinksFontFamily: '"Georgia", "Cambria", "Times New Roman", "Times", serif',
headerNavigationBackgroundColor: '#83df0a',
headerPrimaryLinkColor: '#594973',
headerSecondaryLinkColor: '#c8155d',
headerTextColor: '#C000FE',
healthBenefitsHeaderBackgroundColor: '#abb178',
pageBackgroundColor: '#761816',
primaryNavigationFontFamily: '"Public Sans Web"',
primaryNavigationFontWeight: 'bold',
resultDescriptionColor: '#2bd4c7',
resultTitleColor: '#33f0aa',
resultTitleLinkVisitedColor: '#4a97ad',
resultUrlColor: '#475830',
searchTabNavigationLinkColor: '#aea9f7',
sectionTitleColor: '#8b4a35'
};

const translations = {
en: {
returnToTop: 'Return to top'
}
};

describe('SearchResultsFooter', () => {
it('renders the SERP footer chrome through its own mount', () => {
render(
<SearchResultsFooter
footerLinks={[{ title: 'first footer link', url: 'https://first.gov' }]}
translations={translations}
fontsAndColors={fontsAndColors}
/>
);

expect(screen.getByText(/first footer link/i)).toHaveAttribute('href', 'https://first.gov');
expect(screen.getByText(/Return to top/i)).toBeInTheDocument();
});
});
70 changes: 70 additions & 0 deletions app/javascript/test/SearchResultsHeader.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import '@testing-library/jest-dom';
import { render, screen } from '@testing-library/react';
import React from 'react';

import SearchResultsHeader from '../components/SearchResultsHeader';

jest.mock('i18n-js', () => jest.requireActual('i18n-js/dist/require/index'));

const fontsAndColors = {
activeSearchTabNavigationColor: '#1f1748',
bannerBackgroundColor: '#643617',
bannerTextColor: '#dacb1b',
bestBetBackgroundColor: '#6e09bf',
buttonBackgroundColor: '#cfcd03',
footerAndResultsFontFamily: '"Helvetica Neue", "Helvetica", "Roboto", "Arial", sans-serif',
footerBackgroundColor: '#5fcfc5',
footerLinksTextColor: '#46f966',
headerBackgroundColor: '#4a402b',
headerLinksFontFamily: '"Georgia", "Cambria", "Times New Roman", "Times", serif',
headerNavigationBackgroundColor: '#83df0a',
headerPrimaryLinkColor: '#594973',
headerSecondaryLinkColor: '#c8155d',
headerTextColor: '#C000FE',
healthBenefitsHeaderBackgroundColor: '#abb178',
pageBackgroundColor: '#761816',
primaryNavigationFontFamily: '"Public Sans Web"',
primaryNavigationFontWeight: 'bold',
resultDescriptionColor: '#2bd4c7',
resultTitleColor: '#33f0aa',
resultTitleLinkVisitedColor: '#4a97ad',
resultUrlColor: '#475830',
searchTabNavigationLinkColor: '#aea9f7',
sectionTitleColor: '#8b4a35'
};

const translations = {
en: {
searches: {
menu: 'Menu',
skipToMainContent: 'Skip to main content'
},
ariaLabelHeader: 'Primary navigation'
}
};

describe('SearchResultsHeader', () => {
it('renders the SERP header chrome through its own mount', () => {
render(
<SearchResultsHeader
page={{
affiliate: 'searchgov',
displayLogoOnly: false,
title: 'Search.gov',
logo: {
url: 'https://search.gov/logo.svg',
text: 'search.gov'
},
homepageUrl: 'https://search.gov',
showVoteOrgLink: false
}}
extendedHeader={false}
translations={translations}
fontsAndColors={fontsAndColors}
/>
);

expect(screen.getByText(/Search.gov/i)).toBeInTheDocument();
expect(screen.getByText(/Skip to main content/i)).toBeInTheDocument();
});
});
Loading