Skip to content

Commit

Permalink
Merge pull request #2069 from raft-tech/feat/1787-site-map-redo
Browse files Browse the repository at this point in the history
Feat/1787 site map redo
  • Loading branch information
andrew-jameson authored Aug 26, 2022
2 parents 4852f10 + 40d3a75 commit c9c11ac
Show file tree
Hide file tree
Showing 8 changed files with 273 additions and 14 deletions.
16 changes: 15 additions & 1 deletion tdrs-frontend/src/components/Footer/Footer.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import React from 'react'
import { useSelector } from 'react-redux'

import ACFLogo from '../../assets/ACFLogo.svg'

function Footer() {
const authenticated = useSelector((state) => state.auth.authenticated)
return (
<footer className="usa-footer usa-footer--slim">
<div className="usa-footer__primary-section">
Expand All @@ -15,11 +17,23 @@ function Footer() {
className="usa-footer__primary-link"
href="https://www.acf.hhs.gov/privacy-policy"
target="_blank"
rel="noreferrer"
rel="noopener noreferrer"
>
Privacy policy
</a>
</li>
{authenticated ? (
<li className="mobile-lg:grid-col-6 desktop:grid-col-auto usa-footer__primary-content">
<a
className="usa-footer__primary-link"
href="/site-map"
target="_self"
rel="noopener noreferrer"
>
Site Map
</a>
</li>
) : null}
</ul>
</nav>
</div>
Expand Down
69 changes: 66 additions & 3 deletions tdrs-frontend/src/components/Footer/Footer.test.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,79 @@
import React from 'react'
import { render } from '@testing-library/react'
import configureStore from 'redux-mock-store'
import thunk from 'redux-thunk'
import { Provider } from 'react-redux'

import Footer from './Footer'
import { permissions } from '../Header/developer_permissions'

describe('Footer', () => {
const unauthenticatedInitialState = {
router: { location: { pathname: '/profile' } },
auth: {
authenticated: false,
},
}
const adminInitialState = {
router: { location: { pathname: '/profile' } },
auth: {
authenticated: true,
roles: [{ id: 1, name: 'Developer', permissions }],
},
}

const basicAuthenticatedInitialState = {
router: { location: { pathname: '/profile' } },
auth: { authenticated: true },
}
const mockStore = configureStore([thunk])
it('renders the children & families logo', () => {
const { container } = render(<Footer />)
const store = mockStore(unauthenticatedInitialState)
const { container } = render(
<Provider store={store}>
<Footer />
</Provider>
)
expect(container.querySelector('img')).toBeInTheDocument()
})

it('renders the privacy policy link', () => {
const { getByText } = render(<Footer />)
it('renders the privacy policy link as an unauthenticated user', () => {
const store = mockStore(unauthenticatedInitialState)
const { getByText } = render(
<Provider store={store}>
<Footer />
</Provider>
)
expect(getByText('Privacy policy')).toBeInTheDocument()
})

it('renders the privacy policy link as an authenticated user', () => {
const store = mockStore(basicAuthenticatedInitialState)
const { getByText } = render(
<Provider store={store}>
<Footer />
</Provider>
)
expect(getByText('Privacy policy')).toBeInTheDocument()
})

it('renders the site map link if a user is authenticated', () => {
const store = mockStore(basicAuthenticatedInitialState)
const { getByText } = render(
<Provider store={store}>
<Footer />
</Provider>
)
expect(getByText('Site Map')).toBeInTheDocument()
})

it('does not render the sitemap link if a user is not authenticated', () => {
const store = mockStore(unauthenticatedInitialState)
const { queryByText } = render(
<Provider store={store}>
<Footer />
</Provider>
)
expect(queryByText('Site Map')).not.toBeInTheDocument()
})
})
11 changes: 2 additions & 9 deletions tdrs-frontend/src/components/Header/Header.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useSelector } from 'react-redux'
import closeIcon from 'uswds/dist/img/close.svg'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faSignOutAlt, faUserCircle } from '@fortawesome/free-solid-svg-icons'
import { canViewAdmin } from '../../utils/canViewAdmin'

import NavItem from '../NavItem/NavItem'

Expand All @@ -21,20 +22,12 @@ function Header() {
const user = useSelector((state) => state.auth.user)
const authenticated = useSelector((state) => state.auth.authenticated)
const userAccessRequestPending = Boolean(user?.['access_request'])
const userAccessRequestApproved =
Boolean(user?.['access_request']) && user.roles.length > 0

const isMemberOfOne = (...groupNames) =>
user?.roles?.some((role) => groupNames.includes(role.name))

const hasPermission = (permissionName) =>
user?.roles?.[0]?.permissions?.some(
(perm) => perm.codename === permissionName
)

const canViewAdmin =
userAccessRequestApproved &&
isMemberOfOne('Developer', 'OFA System Admin', 'ACF OCIO')
const canViewDataFiles = hasPermission('view_datafile')

const menuRef = useRef()
Expand Down Expand Up @@ -134,7 +127,7 @@ function Header() {
href="/profile"
/>
)}
{canViewAdmin && (
{canViewAdmin(user) && (
<NavItem
pathname={pathname}
tabTitle="Admin"
Expand Down
18 changes: 17 additions & 1 deletion tdrs-frontend/src/components/Routes/Routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,24 @@ import Profile from '../Profile'
import PrivateRoute from '../PrivateRoute'
import LoginCallback from '../LoginCallback'
import Reports from '../Reports'
import Home from '../Home'
import { useSelector } from 'react-redux'

import SiteMap from '../SiteMap'

import Home from '../Home'

/**
* This component renders the routes for the app.
* Routes have the 'exact' prop, so the order of routes
* does not matter.
*/
const AppRoutes = () => {
// The logged in user in our Redux state
const user = useSelector((state) => state.auth.user)

const role = user?.roles
const hasRole = Boolean(role?.length > 0)

const userAccessRequestApproved = Boolean(user?.['access_request'])

const homeTitle =
Expand Down Expand Up @@ -47,6 +53,16 @@ const AppRoutes = () => {
</PrivateRoute>
}
/>

<Route
exact
path="/site-map"
element={
<PrivateRoute title="Site Map">
<SiteMap user={user} />
</PrivateRoute>
}
/>
<Route
exact
path="/profile"
Expand Down
40 changes: 40 additions & 0 deletions tdrs-frontend/src/components/SiteMap/SiteMap.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react'
import {
canViewAdmin,
userAccessRequestApproved,
} from '../../utils/canViewAdmin'

const SiteMap = ({ user }) => (
<div className="margin-top-5">
<SiteMap.Link text="Home" link="/home" />
<SiteMap.Link
text="Privacy Policy"
link="https://www.acf.hhs.gov/privacy-policy"
target="_blank"
/>
{userAccessRequestApproved(user) && (
<SiteMap.Link text="Data Files" link="/data-files" />
)}
<SiteMap.Link text="Profile" link="/profile" />

{canViewAdmin(user) && (
<SiteMap.Link
text="Admin"
link={`${process.env.REACT_APP_BACKEND_HOST}/admin/`}
/>
)}
</div>
)

SiteMap.Link = ({ text, link, target = '_self' }) => (
<a
className="usa-footer__primary-link"
href={link}
target={target}
rel="noopener noreferrer"
>
{text}
</a>
)

export default SiteMap
116 changes: 116 additions & 0 deletions tdrs-frontend/src/components/SiteMap/SiteMap.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import React from 'react'
import { render } from '@testing-library/react'
import SiteMap from './SiteMap'
import { mount } from 'enzyme'

describe('SiteMap', () => {
const initialState = {
router: { location: { pathname: '/home' } },
auth: {
user: {
email: 'test@test.com',
roles: [{ id: 1, name: 'Developer', permissions: [] }],
access_request: true,
},
authenticated: true,
},
}

it('When an authenticated Developer visits the sitemap', () => {
const user = {
email: 'hi@bye.com',
roles: [{ id: 1, name: 'Developer', permissions: [] }],
access_request: true,
}

const { getByText } = render(<SiteMap user={user}></SiteMap>)

const locations = [
'Home',
'Privacy Policy',
'Data Files',
'Profile',
'Admin',
]
for (let location of locations) {
expect(getByText(location)).toBeInTheDocument()
}
})

it('When an authenticated OFA System Admin visits the sitemap', () => {
const user = {
email: 'hi@bye.com',
roles: [{ id: 1, name: 'OFA System Admin', permissions: [] }],
access_request: true,
}

const { getByText } = render(<SiteMap user={user}></SiteMap>)

const locations = [
'Home',
'Privacy Policy',
'Data Files',
'Profile',
'Admin',
]
for (let location of locations) {
expect(getByText(location)).toBeInTheDocument()
}
})

it('When an authenticated ACF OCIO visits the sitemap', () => {
const user = {
email: 'hi@bye.com',
roles: [{ id: 1, name: 'ACF OCIO', permissions: [] }],
access_request: true,
}

const { getByText } = render(<SiteMap user={user}></SiteMap>)

const locations = [
'Home',
'Privacy Policy',
'Data Files',
'Profile',
'Admin',
]
for (let location of locations) {
expect(getByText(location)).toBeInTheDocument()
}
})
it('When an authenticated Data Analyst visits the sitemap', () => {
const user = {
email: 'hi@bye.com',
roles: [{ id: 1, name: 'Data Analyst', permissions: [] }],
access_request: true,
}

const { getByText } = render(<SiteMap user={user}></SiteMap>)

const locations = ['Home', 'Privacy Policy', 'Data Files', 'Profile']
for (let location of locations) {
expect(getByText(location)).toBeInTheDocument()
}
const wrapper = mount(<SiteMap user={user}></SiteMap>)
expect(wrapper.html()).not.toContain('Admin')
expect(wrapper.html()).toContain('Home')
})

it('When an authenticated user that does not yet have access visits the sitemap', () => {
const user = {
email: 'hi@bye.com',
roles: [],
access_request: false,
}

const { getByText } = render(<SiteMap user={user}></SiteMap>)

const locations = ['Home', 'Privacy Policy', 'Profile']
for (let location of locations) {
expect(getByText(location)).toBeInTheDocument()
}
const wrapper = mount(<SiteMap user={user}></SiteMap>)
expect(wrapper.html()).not.toContain('Admin')
expect(wrapper.html()).toContain('Home')
})
})
3 changes: 3 additions & 0 deletions tdrs-frontend/src/components/SiteMap/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import SiteMap from './SiteMap'

export default SiteMap
14 changes: 14 additions & 0 deletions tdrs-frontend/src/utils/canViewAdmin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const isMemberOfOne = (user, groupNames) =>
user?.roles?.some((role) => groupNames.includes(role.name))

export const userAccessRequestApproved = (user) =>
user?.['access_request'] && user?.roles?.length > 0

export const canViewAdmin = (user) =>
userAccessRequestApproved(user) &&
isMemberOfOne(user, [
'Developer',
'OFA System Admin',
'ACF OCIO',
'OFA Admin',
])

0 comments on commit c9c11ac

Please sign in to comment.