-
Notifications
You must be signed in to change notification settings - Fork 125
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* 81499 Create User Nav * 81499 Integrate UserNav into Header * Styling fixes, DRY code, revert icon autofix * Add TODO comment and included in issue * Add unit testing * Add user nav dropdown and update styling * Update testing for user nav dropdown * Set default state to not logged in and not loading * Update so event listener only present when dropdown is open * Add white-space: nowrap * Update LogoRow unit tests based on UserNav addition * Update header e2e tests with UserNav changes
- Loading branch information
1 parent
976275c
commit 0216853
Showing
8 changed files
with
262 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
134 changes: 134 additions & 0 deletions
134
...applications/accredited-representative-portal/components/common/Header/common/UserNav.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
import React, { useState, useRef, useEffect } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
|
||
import { SIGN_IN_URL, SIGN_OUT_URL } from '../../../../constants'; | ||
|
||
const testProfile = null; | ||
// const testProfile = { firstName: 'James', lastName: 'Smith' }; | ||
|
||
const UserNav = ({ isMobile, isLoading = false, profile = testProfile }) => { | ||
let content = null; | ||
|
||
const [isDropdownOpen, setDropdownOpen] = useState(false); | ||
const dropdownRef = useRef(null); | ||
|
||
const toggleDropdown = () => setDropdownOpen(!isDropdownOpen); | ||
|
||
useEffect( | ||
() => { | ||
const handleClickOutside = event => { | ||
if ( | ||
dropdownRef.current && | ||
!dropdownRef.current.contains(event.target) | ||
) { | ||
setDropdownOpen(false); | ||
} | ||
}; | ||
|
||
if (isDropdownOpen) { | ||
document.addEventListener('mousedown', handleClickOutside); | ||
} | ||
|
||
return () => { | ||
document.removeEventListener('mousedown', handleClickOutside); | ||
}; | ||
}, | ||
[isDropdownOpen], | ||
); | ||
|
||
if (isLoading) { | ||
content = ( | ||
<div className="loading-icon-container"> | ||
{/* eslint-disable-next-line @department-of-veterans-affairs/prefer-icon-component */} | ||
<i | ||
data-testid="user-nav-loading-icon" | ||
className="fa fa-spinner fa-spin fa-lg" | ||
aria-hidden="true" | ||
role="presentation" | ||
/> | ||
</div> | ||
); | ||
} else if (!profile && isMobile) { | ||
content = ( | ||
<a | ||
href={SIGN_IN_URL} | ||
data-testid="user-nav-mobile-sign-in-link" | ||
className="sign-in-link" | ||
> | ||
Sign in | ||
</a> | ||
); | ||
} else if (!profile && !isMobile) { | ||
content = ( | ||
<a | ||
data-testid="user-nav-wider-than-mobile-sign-in-link" | ||
className="usa-button usa-button-primary" | ||
href={SIGN_IN_URL} | ||
> | ||
Sign in | ||
</a> | ||
); | ||
} else if (profile) { | ||
content = ( | ||
<div className="va-dropdown" ref={dropdownRef}> | ||
{/* eslint-disable-next-line @department-of-veterans-affairs/prefer-button-component, react/button-has-type */} | ||
<button | ||
data-testid="user-nav-dropdown-panel-button" | ||
className="sign-in-drop-down-panel-button va-btn-withicon va-dropdown-trigger" | ||
aria-controls="account-menu" | ||
aria-expanded={isDropdownOpen} | ||
onClick={toggleDropdown} | ||
type="button" | ||
> | ||
<span> | ||
<svg | ||
aria-hidden="true" | ||
focusable="false" | ||
className="vads-u-display--block vads-u-margin-right--0 medium-screen:vads-u-margin-right--0p5 icon" | ||
viewBox="0 2 21 21" | ||
xmlns="http://www.w3.org/2000/svg" | ||
> | ||
<path | ||
fill="#fff" | ||
d="M12 12c-1.1 0-2.04-.4-2.82-1.18A3.85 3.85 0 0 1 8 8c0-1.1.4-2.04 1.18-2.83A3.85 3.85 0 0 1 12 4c1.1 0 2.04.4 2.82 1.17A3.85 3.85 0 0 1 16 8c0 1.1-.4 2.04-1.18 2.82A3.85 3.85 0 0 1 12 12Zm-8 8v-2.8c0-.57.15-1.09.44-1.56a2.9 2.9 0 0 1 1.16-1.09 13.76 13.76 0 0 1 9.65-1.16c1.07.26 2.12.64 3.15 1.16.48.25.87.61 1.16 1.09.3.47.44 1 .44 1.56V20H4Z" | ||
/> | ||
</svg> | ||
<div | ||
data-testid="user-nav-user-name" | ||
className="user-dropdown-email" | ||
data-dd-privacy="mask" | ||
data-dd-action-name="First Name" | ||
> | ||
{`${profile.firstName}${isMobile ? '' : ` ${profile.lastName}`}`} | ||
</div> | ||
</span> | ||
</button> | ||
<div | ||
className={`va-dropdown-panel ${isDropdownOpen ? '' : 'hidden'}`} | ||
id="account-menu" | ||
> | ||
<ul> | ||
<li> | ||
<a data-testid="user-nav-sign-out-link" href={SIGN_OUT_URL}> | ||
Sign Out | ||
</a> | ||
</li> | ||
</ul> | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
return <div className="user-nav">{content}</div>; | ||
}; | ||
|
||
UserNav.propTypes = { | ||
isLoading: PropTypes.bool, | ||
isMobile: PropTypes.bool, | ||
profile: PropTypes.shape({ | ||
firstName: PropTypes.string, | ||
lastName: PropTypes.string, | ||
}), | ||
}; | ||
|
||
export default UserNav; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
70 changes: 70 additions & 0 deletions
70
...credited-representative-portal/tests/components/common/Header/common/UserNav.unit.spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import React from 'react'; | ||
import { fireEvent, render } from '@testing-library/react'; | ||
import { expect } from 'chai'; | ||
|
||
import UserNav from '../../../../../components/common/Header/common/UserNav'; | ||
import { SIGN_IN_URL, SIGN_OUT_URL } from '../../../../../constants'; | ||
|
||
describe('UserNav mobile', () => { | ||
const getUserNavMobile = (isLoading, profile) => | ||
render(<UserNav isMobile isLoading={isLoading} profile={profile} />); | ||
|
||
it('renders as Loading', () => { | ||
const { getByTestId } = getUserNavMobile(true, null); | ||
expect(getByTestId('user-nav-loading-icon')).to.exist; | ||
}); | ||
|
||
it('renders as Sign in link when no profile exists', () => { | ||
const { getByTestId } = getUserNavMobile(false, null); | ||
const signInLink = getByTestId('user-nav-mobile-sign-in-link'); | ||
|
||
expect(signInLink.textContent).to.eq('Sign in'); | ||
expect(signInLink.getAttribute('href')).to.eq(SIGN_IN_URL.toString()); | ||
}); | ||
|
||
it('renders with first name when has profile', () => { | ||
const profile = { firstName: 'First', lastName: 'Last' }; | ||
const { getByTestId } = getUserNavMobile(false, profile); | ||
|
||
expect(getByTestId('user-nav-user-name').textContent).to.eq( | ||
profile.firstName, | ||
expect, | ||
); | ||
fireEvent.click(getByTestId('user-nav-dropdown-panel-button')); | ||
expect(getByTestId('user-nav-sign-out-link').getAttribute('href')).to.eq( | ||
SIGN_OUT_URL.toString(), | ||
); | ||
}); | ||
}); | ||
|
||
describe('UserNav wider than mobile', () => { | ||
const getUserNavWider = (isLoading, profile) => | ||
render(<UserNav isLoading={isLoading} profile={profile} />); | ||
|
||
it('renders as Loading', () => { | ||
const { getByTestId } = getUserNavWider(true, null); | ||
expect(getByTestId('user-nav-loading-icon')).to.exist; | ||
}); | ||
|
||
it('renders as Sign in link when no profile exists', () => { | ||
const { getByTestId } = getUserNavWider(false, null); | ||
const signInLink = getByTestId('user-nav-wider-than-mobile-sign-in-link'); | ||
|
||
expect(signInLink.textContent).to.eq('Sign in'); | ||
expect(signInLink.getAttribute('href')).to.eq(SIGN_IN_URL.toString()); | ||
}); | ||
|
||
it('renders with first and last name when has profile', () => { | ||
const profile = { firstName: 'First', lastName: 'Last' }; | ||
const { getByTestId } = getUserNavWider(false, profile); | ||
|
||
expect(getByTestId('user-nav-user-name').textContent).to.eq( | ||
`${profile.firstName} ${profile.lastName}`, | ||
expect, | ||
); | ||
fireEvent.click(getByTestId('user-nav-dropdown-panel-button')); | ||
expect(getByTestId('user-nav-sign-out-link').getAttribute('href')).to.eq( | ||
SIGN_OUT_URL.toString(), | ||
); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters