Skip to content

Commit

Permalink
Merge pull request #3734 from NCI-Agency/GH-3729-Navigation-menu-is-t…
Browse files Browse the repository at this point in the history
…oo-long

Group some items in the navigation menu
  • Loading branch information
gjvoosten authored Jul 29, 2021
2 parents e41f19c + ed9f491 commit 8b51674
Show file tree
Hide file tree
Showing 13 changed files with 195 additions and 80 deletions.
3 changes: 3 additions & 0 deletions anet-dictionary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ dateFormats:
date: dddd, D MMMM YYYY
withTime: dddd, D MMMM YYYY @ HH:mm

menuOptions:
menuLinksDropdownTitle: My Work

printOptions:
sensitiveInformationText: Sensitive Information
sensitiveInformationTooltipText: Releasability Information
Expand Down
223 changes: 147 additions & 76 deletions client/src/components/Nav.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Collapse } from "@blueprintjs/core"
import { clearSearchQuery, resetPages } from "actions"
import AppContext from "components/AppContext"
import ResponsiveLayoutContext from "components/ResponsiveLayoutContext"
import { Organization } from "models"
import { INSIGHTS, INSIGHT_DETAILS } from "pages/insights/Show"
import pluralize from "pluralize"
import PropTypes from "prop-types"
import React, { useContext, useEffect } from "react"
import React, { useContext, useEffect, useMemo, useState } from "react"
import {
Badge,
MenuItem,
Expand Down Expand Up @@ -56,15 +57,28 @@ AnchorNavItem.propTypes = {
children: PropTypes.node
}

const SidebarLink = ({ linkTo, children, handleOnClick, id }) => (
<Link to={linkTo} onClick={handleOnClick}>
const SidebarLink = ({
linkTo,
children,
handleOnClick,
id,
setIsMenuLinksOpened
}) => (
<Link
to={linkTo}
onClick={() => {
handleOnClick()
setIsMenuLinksOpened && setIsMenuLinksOpened()
}}
>
<NavItem id={id}>{children}</NavItem>
</Link>
)
SidebarLink.propTypes = {
linkTo: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
children: PropTypes.node,
handleOnClick: PropTypes.func,
setIsMenuLinksOpened: PropTypes.func,
id: PropTypes.string
}

Expand All @@ -75,6 +89,7 @@ const Nav = ({
clearSearchQuery
}) => {
const { appSettings, currentUser, notifications } = useContext(AppContext)
const [isMenuLinksOpened, setIsMenuLinksOpened] = useState(false)
useEffect(() => scrollSpy.update(), [])

const externalDocumentationUrl = appSettings.EXTERNAL_DOCUMENTATION_LINK_URL
Expand All @@ -84,99 +99,134 @@ const Nav = ({
const routerLocation = useLocation()
const path = routerLocation.pathname
const inAdmin = path.indexOf("/admin") === 0
const inOrg = path.indexOf("/organizations") === 0

const [orgUuid, inOrg, myOrg, inMyOrg] = useMemo(() => {
const inOrg = path.indexOf("/organizations") === 0
const orgUuid = inOrg ? path.split("/")[2] : null
const myOrg = currentUser.position?.uuid
? currentUser.position?.organization
: null
const inMyOrg = orgUuid === myOrg?.uuid
return [orgUuid, inOrg, myOrg, inMyOrg]
}, [path, currentUser.position?.uuid, currentUser.position?.organization])

const inMyCounterParts = path.indexOf("/positions/counterparts") === 0
const inMyTasks = path.indexOf("/tasks/mine") === 0
const inMyReports = path.indexOf("/reports/mine") === 0
const inInsights = path.indexOf("/insights") === 0
const inDashboards = path.indexOf("/dashboards") === 0

const myOrg = currentUser.position?.uuid
? currentUser.position?.organization
: null
let orgUuid, myOrgUuid
if (inOrg) {
orgUuid = path.split("/")[2]
myOrgUuid = myOrg && myOrg.uuid
}

const advisorOrganizationUuids = advisorOrganizations.map(o => o.uuid)
const principalOrganizationUuids = principalOrganizations.map(o => o.uuid)

const isAdvisor = currentUser.isAdvisor()
const taskShortLabel = Settings.fields.task.shortLabel

useEffect(() => {
if (inMyOrg || inMyCounterParts || inMyReports || inMyTasks) {
setIsMenuLinksOpened(true)
}
}, [inMyOrg, inMyCounterParts, inMyReports, inMyTasks])

return (
<BSNav bsStyle="pills" stacked id="leftNav" className="hide-for-print">
<SidebarLink linkTo="/" handleOnClick={resetPages}>
<SidebarLink
linkTo="/"
handleOnClick={resetPages}
setIsMenuLinksOpened={() => setIsMenuLinksOpened(false)}
>
Home
</SidebarLink>

<BSNav id="search-nav" />

{currentUser.uuid && (
<SidebarLink
linkTo={{ pathname: "/reports/mine" }}
handleOnClick={resetPages}
<NavItem
active={isMenuLinksOpened}
id="nav-links-button"
style={{ paddingTop: "2px" }}
onClick={() => setIsMenuLinksOpened(!isMenuLinksOpened)}
>
{Settings?.menuOptions?.menuLinksDropdownTitle ?? "My Work"}
<span
className={isMenuLinksOpened ? "caret caret-rotate" : "caret"}
style={{ marginLeft: "0.5rem" }}
>
My Reports
</SidebarLink>
)}
</span>
</NavItem>

<BSNav id="reports-nav" />
<Collapse isOpen={isMenuLinksOpened}>
<BSNav
bsStyle="pills"
stacked
style={{ paddingLeft: "1rem", paddingTop: "2px" }}
>
{currentUser.uuid && (
<SidebarLink
linkTo={{ pathname: "/reports/mine" }}
handleOnClick={resetPages}
>
My Reports
</SidebarLink>
)}

{isAdvisor && currentUser.position?.uuid && (
<>
<SidebarLink
linkTo={{ pathname: "/tasks/mine" }}
handleOnClick={resetPages}
id="my-tasks-nav"
>
{`My ${pluralize(taskShortLabel)}`}
{notifications?.tasksWithPendingAssessments?.length ? (
<NotificationBadge>
{notifications.tasksWithPendingAssessments.length}
</NotificationBadge>
) : null}
</SidebarLink>
<SidebarLink
linkTo={{ pathname: "/positions/counterparts" }}
handleOnClick={resetPages}
id="my-counterparts-nav"
>
My Counterparts
{notifications?.counterpartsWithPendingAssessments?.length ? (
<NotificationBadge>
{notifications.counterpartsWithPendingAssessments.length}
</NotificationBadge>
) : null}
</SidebarLink>
</>
)}
<BSNav id="reports-nav" />

{myOrg && (
<SidebarLink
linkTo={Organization.pathFor(myOrg)}
handleOnClick={resetPages}
id="my-organization"
>
My Organization <br />
<small>{myOrg.shortName}</small>
</SidebarLink>
)}
<BSNav id="myorg-nav" />
{isAdvisor && currentUser.position?.uuid && (
<>
<SidebarLink
linkTo={{ pathname: "/tasks/mine" }}
handleOnClick={resetPages}
id="my-tasks-nav"
>
{`My ${pluralize(taskShortLabel)}`}
{notifications?.tasksWithPendingAssessments?.length ? (
<NotificationBadge>
{notifications.tasksWithPendingAssessments.length}
</NotificationBadge>
) : null}
</SidebarLink>
<SidebarLink
linkTo={{ pathname: "/positions/counterparts" }}
handleOnClick={resetPages}
id="my-counterparts-nav"
>
My Counterparts
{notifications?.counterpartsWithPendingAssessments?.length ? (
<NotificationBadge>
{notifications.counterpartsWithPendingAssessments.length}
</NotificationBadge>
) : null}
</SidebarLink>
</>
)}

{myOrg && (
<SidebarLink
linkTo={Organization.pathFor(myOrg)}
handleOnClick={resetPages}
id="my-organization"
>
My Organization <br />
<small>{myOrg.shortName}</small>
</SidebarLink>
)}
<BSNav id="myorg-nav" />
</BSNav>
</Collapse>

<NavDropdown
title={Settings.fields.advisor.org.allOrgName}
id="advisor-organizations"
active={
inOrg &&
advisorOrganizationUuids.includes(orgUuid) &&
orgUuid !== myOrgUuid
}
active={inOrg && advisorOrganizationUuids.includes(orgUuid) && !inMyOrg}
>
{Organization.map(advisorOrganizations, org => (
<Link
to={Organization.pathFor(org)}
key={org.uuid}
onClick={clearSearchQuery}
onClick={() => {
clearSearchQuery()
setIsMenuLinksOpened(false)
}}
>
<MenuItem>{org.shortName}</MenuItem>
</Link>
Expand All @@ -189,16 +239,17 @@ const Nav = ({
title={Settings.fields.principal.org.allOrgName}
id="principal-organizations"
active={
inOrg &&
principalOrganizationUuids.includes(orgUuid) &&
orgUuid !== myOrgUuid
inOrg && principalOrganizationUuids.includes(orgUuid) && !inMyOrg
}
>
{Organization.map(principalOrganizations, org => (
<Link
to={Organization.pathFor(org)}
key={org.uuid}
onClick={clearSearchQuery}
onClick={() => {
clearSearchQuery()
setIsMenuLinksOpened(false)
}}
>
<MenuItem>{org.shortName}</MenuItem>
</Link>
Expand All @@ -207,12 +258,22 @@ const Nav = ({

<BSNav id="principal-org-nav" />

<SidebarLink linkTo="/rollup" handleOnClick={resetPages}>
<SidebarLink
linkTo="/rollup"
handleOnClick={resetPages}
setIsMenuLinksOpened={() => setIsMenuLinksOpened(false)}
>
Daily Rollup
</SidebarLink>

{currentUser.isAdmin() && (
<LinkContainer to="/admin" onClick={clearSearchQuery}>
<LinkContainer
to="/admin"
onClick={() => {
clearSearchQuery()
setIsMenuLinksOpened(false)
}}
>
<NavItem>Admin</NavItem>
</LinkContainer>
)}
Expand Down Expand Up @@ -243,7 +304,11 @@ const Nav = ({
</NavItem>
)}

<SidebarLink linkTo="/help" handleOnClick={resetPages}>
<SidebarLink
linkTo="/help"
handleOnClick={resetPages}
setIsMenuLinksOpened={() => setIsMenuLinksOpened(false)}
>
Help
</SidebarLink>

Expand All @@ -253,7 +318,10 @@ const Nav = ({
<Link
to={"/insights/" + insight}
key={insight}
onClick={clearSearchQuery}
onClick={() => {
clearSearchQuery()
setIsMenuLinksOpened(false)
}}
>
<MenuItem>{INSIGHT_DETAILS[insight].navTitle}</MenuItem>
</Link>
Expand All @@ -267,7 +335,10 @@ const Nav = ({
<Link
to={`/dashboards/${dashboard.type}/${dashboard.label}`}
key={dashboard.label}
onClick={clearSearchQuery}
onClick={() => {
clearSearchQuery()
setIsMenuLinksOpened(false)
}}
>
<MenuItem>{dashboard.label}</MenuItem>
</Link>
Expand Down
19 changes: 16 additions & 3 deletions client/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -324,15 +324,28 @@ button.home-tile.btn {
text-align: center;
}

.caret {
transition: 0.3s ease;
}

.caret-rotate {
transform: rotate(180deg);
transition: transform 0.3s ease;
}

.nav img {
width: 16px;
margin-right: 6px;
}

.nav .nav > li > a {
.nav-pills li.active > #nav-links-button {
background-color: #e0e0e0;
color: #337ab7;
}

.nav > .nav > li > a {
border-left: 4px solid rgba(0, 0, 0, 0);
padding: 4px 0 4px 38px;
padding-right: 4px;
padding: 4px 4px 4px 38px;
}

.nav .nav > li.active > a {
Expand Down
2 changes: 1 addition & 1 deletion client/tests/e2e/home.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ test("Home Page", async t => {
const $hopscotchNext = await $(".hopscotch-next")
await $hopscotchNext.click()

const $myReportsLink = await $("#leftNav > li:nth-child(3) > a")
const $myReportsLink = await $("#leftNav > li:nth-child(10) > a")
await $myReportsLink.click()
await t.context.driver.sleep(shortWaitMs) // wait for transition
await assertElementNotPresent(
Expand Down
Loading

0 comments on commit 8b51674

Please sign in to comment.