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

[Prod] Show user regions in the site navigation #2202

Merged
merged 36 commits into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
d811308
Display regions in AR Landing header/SiteNav
Spanglemonkey Apr 7, 2023
4d471dd
Adding missing All Regions scenario to SiteNav
Spanglemonkey Apr 7, 2023
6b49dc5
Fixed lint issue
Spanglemonkey Apr 7, 2023
e67736f
Fix to test to account for header text change.
Spanglemonkey Apr 7, 2023
b571992
Second fix to frontend test - typo
Spanglemonkey Apr 7, 2023
ea1c812
debug landing
thewatermethod Apr 10, 2023
349ddb2
Missing region 14 scenario in heading
Spanglemonkey Apr 10, 2023
c0938ed
more console log for debugging
thewatermethod Apr 10, 2023
6d1961f
Missing test update to match scenario in header
Spanglemonkey Apr 10, 2023
98156f9
remove logs and fix test setup
thewatermethod Apr 10, 2023
23629da
Merge remote-tracking branch 'origin/lr/ttahub-1521/display-regions-i…
thewatermethod Apr 10, 2023
6267941
add test, bit of cleanup
thewatermethod Apr 10, 2023
8328ef7
Merge remote-tracking branch 'origin/main' into lr/ttahub-1521/displa…
Spanglemonkey Apr 10, 2023
bebbe64
syncing backend changes to branch
Spanglemonkey Apr 10, 2023
8bd0484
fixed logic issue and added test
Spanglemonkey Apr 25, 2023
9237432
Merge remote-tracking branch 'origin/main' into lr/ttahub-1521/displa…
Spanglemonkey Apr 25, 2023
763bcaa
Needed to update constants import source
Spanglemonkey Apr 25, 2023
0d32480
Merge remote-tracking branch 'origin/main' into lr/ttahub-1521/displa…
Spanglemonkey Apr 25, 2023
725ec4e
Merge remote-tracking branch 'origin/main' into lr/ttahub-1521/displa…
thewatermethod Apr 11, 2024
6143636
Update test since we arent varying header text as much
thewatermethod Apr 12, 2024
d0a15b1
Bump @grpc/grpc-js from 1.9.12 to 1.10.9
dependabot[bot] Jun 10, 2024
5984c4a
Merge branch 'main' into dependabot/npm_and_yarn/grpc/grpc-js-1.10.9
kryswisnaskas Jun 11, 2024
5b5908a
Add braces to resolutions
kryswisnaskas Jun 11, 2024
677f0c9
Merge branch 'mb/TTAHUB-2396/side-nav' into lr/ttahub-1521/display-re…
thewatermethod Jun 11, 2024
638bab3
Deploy to dev
thewatermethod Jun 11, 2024
55a105d
Update known issues again
thewatermethod Jun 11, 2024
d689fad
Merge branch 'mb/TTAHUB-2396/side-nav' into lr/ttahub-1521/display-re…
thewatermethod Jun 11, 2024
9d9ea33
Merge branch 'mb/TTAHUB-2396/side-nav' into lr/ttahub-1521/display-re…
thewatermethod Jun 11, 2024
195a0df
Lord above
thewatermethod Jun 11, 2024
13fb899
Update known dependencies
kryswisnaskas Jun 11, 2024
bd6d5aa
Merge branch 'main' into dependabot/npm_and_yarn/grpc/grpc-js-1.10.9
kryswisnaskas Jun 11, 2024
a9ee49f
Update known vulnerabilities
kryswisnaskas Jun 11, 2024
2f7c253
Merge pull request #2199 from HHS/dependabot/npm_and_yarn/grpc/grpc-j…
dependabot[bot] Jun 11, 2024
7d36884
Fix missing variable for production
kryswisnaskas Jun 11, 2024
96b52e4
Merge pull request #1473 from HHS/lr/ttahub-1521/display-regions-in-A…
thewatermethod Jun 11, 2024
e831f8c
Merge pull request #2203 from HHS/kw-fix-prod
kryswisnaskas Jun 11, 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
1 change: 1 addition & 0 deletions deployment_config/prod_vars.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ CLAMAV_ENDPOINT: https://clamapi-ttahub-prod.apps.internal:9443
SEND_NOTIFICATIONS: "true"
SUPPRESS_ERROR_LOGGING: "false"
SIMILARITY_ENDPOINT: https://tta-similarity-api-prod.app.cloud.gov/compute
SMARTSHEET_ENDPOINT: ""
37 changes: 28 additions & 9 deletions frontend/src/components/SiteNav.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
/* eslint-disable react/no-array-index-key, react/jsx-props-no-spreading */
import React, { useState, useEffect, useRef } from 'react';
import React, {
useState, useContext, useEffect, useRef,
} from 'react';
import PropTypes from 'prop-types';
import { NavLink as Link, withRouter } from 'react-router-dom';
import SiteNavDisclosureGroup from './SiteNavDisclosureGroup';
import './SiteNav.scss';
import FeatureFlag from './FeatureFlag';
import UserContext from '../UserContext';
import { allRegionsUserHasPermissionTo } from '../permissions';

const navLinkClasses = [
'display-block',
Expand Down Expand Up @@ -42,10 +46,10 @@ NavLink.defaultProps = {

const SiteNav = ({
authenticated,
user,
location,
hasAlerts,
}) => {
const { user } = useContext(UserContext);
const siteNavContent = useRef(null);
const [showActivityReportSurveyButton, setShowActivityReportSurveyButton] = useState(false);
const [showSidebar, setShowSidebar] = useState(true);
Expand All @@ -70,6 +74,26 @@ const SiteNav = ({
}
}, [hasAlerts]);

// Determine Default Region.
const regions = allRegionsUserHasPermissionTo(user);
const defaultRegion = user.homeRegionId || regions[0] || 0;
const hasMultipleRegions = regions && regions.length > 1;

const regionDisplay = regions.join(', ');

// If user has more than one region, Regions label is plural, else singular
const regionLabel = () => {
if (defaultRegion === 14) {
return 'All Regions';
}

if (hasMultipleRegions) {
return `Regions ${regionDisplay}`;
}

return `Region ${regionDisplay}`;
};

if (!showSidebar) return null;

return (
Expand All @@ -89,8 +113,8 @@ const SiteNav = ({
<div className="smart-hub-sitenav-content-container display-flex flex-column flex-1 overflow-y-scroll">
<div className="width-full smart-hub-sitenav-separator--after">
<div role="complementary" className="padding-2 smart-hub-sitenav-word-wrap--break">
<p className="text-bold margin-top-2 desktop:margin-top-5">{user.name}</p>
<p className="font-sans-3xs margin-bottom-2 desktop:margin-bottom-5">{user.email}</p>
<p className="text-bold margin-top-2 desktop:margin-top-5 margin-bottom-1">{user.name}</p>
<p className="font-sans-3xs margin-bottom-2 desktop:margin-bottom-5 margin-top-1">{regionLabel()}</p>
</div>
</div>
<nav className="display-flex flex-column flex-justify flex-1" aria-label="main navigation">
Expand Down Expand Up @@ -171,16 +195,11 @@ SiteNav.displayName = 'SiteNav';

SiteNav.propTypes = {
authenticated: PropTypes.bool,
user: PropTypes.shape({ name: PropTypes.string, email: PropTypes.string }),
location: PropTypes.shape({ pathname: PropTypes.string }).isRequired,
hasAlerts: PropTypes.bool.isRequired,
};

SiteNav.defaultProps = {
authenticated: false,
user: {
name: '',
email: '',
},
};
export default withRouter(SiteNav);
105 changes: 103 additions & 2 deletions frontend/src/components/__tests__/SiteNav.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import '@testing-library/jest-dom';
import React from 'react';
import join from 'url-join';
import {
screen, render,
screen, render, act,
} from '@testing-library/react';
import fetchMock from 'fetch-mock';
import { MemoryRouter, Router } from 'react-router';
import { createMemoryHistory } from 'history';
import { SCOPE_IDS } from '@ttahub/common';
import SiteNav from '../SiteNav';
import UserContext from '../../UserContext';

Expand Down Expand Up @@ -51,7 +52,18 @@ describe('SiteNav', () => {
const userUrl = join('api', 'user');

beforeEach(() => {
const user = { name: 'name' };
const user = {
// since I have no home region id, the `defaultRegion` falls back
// to `regions.split(', ')[0] (or however else you want to implement this fix)
homeRegionId: null,
permissions: [
{
scopeId: SCOPE_IDS.READ_WRITE_ACTIVITY_REPORTS,
regionId: 10,
},
],
};

fetchMock.get(userUrl, { ...user, permissions: [] });

render(
Expand All @@ -66,6 +78,12 @@ describe('SiteNav', () => {
test('nav items are visible', () => {
expect(screen.queryAllByRole('link').length).not.toBe(0);
});
test('has no home region', async () => {
// this verifies `hasMultipleRegions` works as expected
// because the heading is `Region` and not `Regions`, as we
// only have one region.
expect(await screen.findByText('Region 10')).toBeVisible();
});
});

describe('when unauthenticated', () => {
Expand Down Expand Up @@ -131,4 +149,87 @@ describe('SiteNav', () => {
expect(screen.queryAllByRole('link').length).not.toBe(0);
});
});

describe('site nav label', () => {
afterEach(() => fetchMock.restore());

const userUrl = join('api', 'user');

const renderSiteNav = (u) => {
const user = { ...u, name: 'name' };
fetchMock.get(userUrl, { ...user });

render(
<MemoryRouter>
<UserContext.Provider value={{ user, authenticated: true, logout: () => {} }}>
<header className="smart-hub-header.has-alerts">
<SiteNav authenticated user={user} hasAlerts />
</header>
</UserContext.Provider>
</MemoryRouter>,
);
};

test('has multiple regions', async () => {
const user = {
homeRegionId: 1,
permissions: [
{
scopeId: SCOPE_IDS.READ_WRITE_ACTIVITY_REPORTS,
regionId: 1,
},
{
scopeId: SCOPE_IDS.READ_WRITE_ACTIVITY_REPORTS,
regionId: 2,
},
],
};

act(() => {
renderSiteNav(user);
});

expect(await screen.findByText('Regions 1, 2')).toBeVisible();
});

test('has sinle region', async () => {
const user = {
homeRegionId: 1,
permissions: [
{
scopeId: SCOPE_IDS.READ_WRITE_ACTIVITY_REPORTS,
regionId: 1,
},
],
};

act(() => {
renderSiteNav(user);
});

expect(await screen.findByText('Region 1')).toBeVisible();
});

test('is central office', async () => {
const user = {
homeRegionId: 14,
permissions: [
{
scopeId: SCOPE_IDS.READ_WRITE_ACTIVITY_REPORTS,
regionId: 1,
},
{
scopeId: SCOPE_IDS.READ_WRITE_ACTIVITY_REPORTS,
regionId: 2,
},
],
};

act(() => {
renderSiteNav(user);
});

expect(await screen.findByText('All Regions')).toBeVisible();
});
});
});
39 changes: 38 additions & 1 deletion frontend/src/pages/Landing/__tests__/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import fetchMock from 'fetch-mock';
import { act } from 'react-dom/test-utils';
import userEvent from '@testing-library/user-event';
import { v4 as uuidv4 } from 'uuid';
import { SCOPE_IDS } from '@ttahub/common';
import UserContext from '../../../UserContext';
import AriaLiveContext from '../../../AriaLiveContext';
import Landing, { getAppliedRegion } from '../index';
Expand Down Expand Up @@ -371,7 +372,43 @@ describe('Landing page table menus & selections', () => {
};

renderLanding(user);
expect(await screen.findByRole('heading', { name: /activity reports - all regions/i })).toBeVisible();
expect(await screen.findByRole('heading', { name: /activity reports - your regions/i })).toBeVisible();
});

it('user with one region shows the correct label', async () => {
const user = {
name: '[email protected]',
homeRegionId: 1,
permissions: [
{
scopeId: 3,
regionId: 1,
},
],
};

renderLanding(user);
expect(await screen.findByRole('heading', { name: /activity reports - your region/i })).toBeVisible();
});

it('user with multiple region shows the correct label', async () => {
const user = {
name: '[email protected]',
homeRegionId: 1,
permissions: [
{
scopeId: SCOPE_IDS.READ_WRITE_ACTIVITY_REPORTS,
regionId: 1,
},
{
scopeId: SCOPE_IDS.READ_WRITE_ACTIVITY_REPORTS,
regionId: 3,
},
],
};

renderLanding(user);
expect(await screen.findByRole('heading', { name: /activity reports - your regions/i })).toBeVisible();
});
});
});
Expand Down
19 changes: 8 additions & 11 deletions frontend/src/pages/Landing/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,13 +202,10 @@ function Landing() {
}

const regionLabel = () => {
if (defaultRegion === 14) {
return 'all regions';
if (defaultRegion === 14 || hasMultipleRegions) {
return 'your regions';
}
if (defaultRegion > 0) {
return `region ${defaultRegion.toString()}`;
}
return '';
return 'your region';
};

// Apply filters.
Expand Down Expand Up @@ -290,15 +287,15 @@ function Landing() {
</Alert>
)}
<Grid row gap>
<Grid>
<h1 className="landing margin-top-0 margin-bottom-3">{`Activity reports - ${regionLabel()}`}</h1>
</Grid>
<Grid className="grid-col-2 flex-align-self-center">
{reportAlerts
<Grid col={12} className="display-flex flex-wrap">
<h1 className="landing margin-top-0 margin-bottom-3 margin-right-2">{`Activity reports - ${regionLabel()}`}</h1>
<div className="margin-bottom-2">
{reportAlerts
&& reportAlerts.length > 0
&& hasReadWrite(user)
&& appliedRegionNumber !== 14
&& <NewReport />}
</div>
</Grid>
<Grid col={12} className="display-flex flex-wrap flex-align-center flex-gap-1 margin-bottom-2">
<FilterPanel
Expand Down
1 change: 0 additions & 1 deletion frontend/src/pages/Landing/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ h1.landing {
font-size: 40px;
font-family: "Merriweather", serif;
font-weight: 700;
margin-right: 0px;
}

.landing .usa-table {
Expand Down
27 changes: 23 additions & 4 deletions frontend/src/pages/RegionalDashboard/__tests__/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,32 @@ const lastThirtyDaysParams = `startDate.win=${encodeURIComponent(lastThirtyDays)
const allRegions = 'region.in[]=1&region.in[]=2';
const regionInParams = 'region.in[]=1';

const hoursOfTrainingUrl = '/api/widgets/trHoursOfTrainingByNationalCenter';
const trReasonListUrl = '/api/widgets/trReasonList';
const overviewUrl = '/api/widgets/trOverview';
const sessionsByTopicUrl = '/api/widgets/trSessionsByTopic';

describe('Regional Dashboard page', () => {
beforeEach(async () => {
fetchMock.get(overViewUrl, overViewResponse);
fetchMock.get(reasonListUrl, reasonListResponse);
fetchMock.get(totalHrsAndRecipientGraphUrl, totalHoursResponse);
fetchMock.get(topicFrequencyGraphUrl, topicFrequencyResponse);
fetchMock.get(`${activityReportsUrl}?sortBy=updatedAt&sortDir=desc&offset=0&limit=10`, activityReportsResponse);

fetchMock.get(overviewUrl, {
numReports: '0',
totalRecipients: '0',
recipientPercentage: '0%',
numGrants: '0',
numRecipients: '0',
sumDuration: '0',
numParticipants: '0',
numSessions: '0',
});
fetchMock.get(trReasonListUrl, []);
fetchMock.get(hoursOfTrainingUrl, []);
fetchMock.get(sessionsByTopicUrl, []);
});

afterEach(() => fetchMock.restore());
Expand Down Expand Up @@ -143,7 +162,7 @@ describe('Regional Dashboard page', () => {
fetchMock.get(`${activityReportsUrl}?sortBy=updatedAt&sortDir=desc&offset=0&limit=10&${regionInParams}&${lastThirtyDaysParams}`, activityReportsResponse);

renderDashboard(user);
const heading = await screen.findByText(/region 1 tta activity dashboard/i);
const heading = await screen.findByText(/Regional TTA activity dashboard/i);
expect(heading).toBeVisible();
});

Expand All @@ -163,7 +182,7 @@ describe('Regional Dashboard page', () => {
fetchMock.get(`${activityReportsUrl}?sortBy=updatedAt&sortDir=desc&offset=0&limit=10&${regionInParams}&${lastThirtyDaysParams}`, activityReportsResponse);

renderDashboard(user, 'activity-reports');
const heading = await screen.findByText(/region 1 dashboard - activity reports/i);
const heading = await screen.findByText(/regional dashboard - activity reports/i);
expect(heading).toBeVisible();
});

Expand All @@ -183,7 +202,7 @@ describe('Regional Dashboard page', () => {
fetchMock.get(`${activityReportsUrl}?sortBy=updatedAt&sortDir=desc&offset=0&limit=10&${regionInParams}&${lastThirtyDaysParams}`, activityReportsResponse);

renderDashboard(user, 'training-reports');
const heading = await screen.findByText(/region 1 dashboard - training reports/i);
const heading = await screen.findByText(/regional dashboard - training reports/i);
expect(heading).toBeVisible();
});

Expand All @@ -203,7 +222,7 @@ describe('Regional Dashboard page', () => {
fetchMock.get(`${activityReportsUrl}?sortBy=updatedAt&sortDir=desc&offset=0&limit=10&${regionInParams}&${lastThirtyDaysParams}`, activityReportsResponse);

renderDashboard(user, 'all-reports');
const heading = await screen.findByText(/region 1 dashboard - all reports/i);
const heading = await screen.findByText(/regional dashboard - all reports/i);
expect(heading).toBeVisible();
});

Expand Down
Loading