Skip to content

Commit

Permalink
GN-117 Remove need to assert on dom (#148)
Browse files Browse the repository at this point in the history
* context experiments

* basic context setup

* tidy up debugging

* uncommen event listener for expected closing behaviour

* make debug clearer

* clean up console logging for clarity

* move globalnavcontext provider up to header component

* GN-117 add useCallback to clickOutside hook to maintain reference to id of open dropdown

* GN-117 Refactor Nav component to class. Add custom hook and context for open dropdown state and nav area click outside detection

* GN-117 Begin conversion of Account class component to functional component

* GN-117 Remove commented out lines

* GN-117 Reinstate propTypes for Naccount placeholder component

* Side effect sideline (#150)

* side effect shenanigans

* Magical useRef()

* update key event to use e.key rather than e.keycode for better cross platform

* Move myAccount isExpanded state into GlobalNavContext

* Remove redundant event binding

* Make megamenu click detection more 'reacty'

* Manage context of opened menus

* Rename Naccount component to Account

Co-authored-by: John Davey <[email protected]>

* Tests needed

* GN-117 Tidy up redundant arguments and commented out logic for useClickOutside hook

* GN-117 Change directory structure for hooks and react context. Make useClickOutside re-useable.

* GN-117 Import GlobalNavContextProvider from new location for Account tests

* GN-117 Fixing broken unit tests

* GN-117 Fixing broken tests

* GN-117 Fixing broken tests

* Replace simulate click

* GN-117 Revert some tests to shallow rendering after adding defaults to context

* GN-117 Revert to shallow mounting for a previously failing unit test

* GN-117 Convert unit test from shallow to mount due to issues around enzyme support of useContext 'enzymejs/enzyme#2176'

* GN-117 Rename GlobalNavContext to HeaderContext

* GN-117 remove unused dependencies from account test

* GN-117 Tidy up unit tests

* testing custom hook useClickOutside

* basic setup

* Custom hook test

* Update test

* Rename test

Co-authored-by: w@rren <[email protected]>
  • Loading branch information
johndavey72 and wa-rren-dev authored Nov 22, 2021
1 parent 5102461 commit 482e52e
Show file tree
Hide file tree
Showing 11 changed files with 614 additions and 309 deletions.
245 changes: 111 additions & 134 deletions src/Header/Account/Account.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import React, { Component } from "react";
import React, {
useContext,
useEffect,
useState,
useCallback,
useRef,
} from "react";
import { HeaderContext } from "../context/HeaderContext";
import PropTypes from "prop-types";
import classnames from "classnames";

Expand All @@ -13,63 +20,42 @@ import {
headerClickEventAction,
} from "../../tracker";

const escapeKeyCode = 27;
function Account(props) {
// We've left this is state as per the pre-hook implementation
const [doesUseIdAM] = useState(props.provider == Account.providers.idam);

export default class Account extends Component {
constructor(props) {
super(props);
const { accountMenuIsExpanded, setAccountMenuIsExpanded } =
useContext(HeaderContext);

this.state = {
isExpanded: false,
useIdAM: this.props.provider == Account.providers.idam,
};
const keypress = useRef(),
myAccountButton = useRef(),
myAccountMenu = useRef();

this.handleMyAccountButtonClick =
this.handleMyAccountButtonClick.bind(this);
this.handleKeyDown = this.handleKeyDown.bind(this);
this.handleMenuItemClick = this.handleMenuItemClick.bind(this);
this.handleMegaMenuClick = this.handleMegaMenuClick.bind(this);
}
const handleMyAccountButtonClick = useCallback((e) => {
keypress.current = e.pageX;
setAccountMenuIsExpanded((prevState) => !prevState);
}, []);

handleMyAccountButtonClick(e) {
const isKeyboardEvent = !e.pageX;
this.setState(
function (prevState) {
return { isExpanded: !prevState.isExpanded };
},
function () {
if (this.state.isExpanded && isKeyboardEvent) {
const accountMenu = document.getElementById("my-account");
accountMenu.setAttribute("tabIndex", -1);
accountMenu.focus();
}
}.bind(this)
);
function focusAccount() {
myAccountMenu && myAccountMenu.current.setAttribute("tabIndex", -1);
myAccountMenu && myAccountMenu.current.focus();
}

handleKeyDown(event) {
if (event.keyCode === escapeKeyCode) {
event.preventDefault();
this.setState({
isExpanded: false,
});
document.getElementById("my-account-button").focus();
useEffect(() => {
if (!keypress.current && accountMenuIsExpanded) {
focusAccount();
}
}
}, [accountMenuIsExpanded]);

// NOTE: We would benefit from managing the state higher up
handleMegaMenuClick(event) {
let megaMenu = document.querySelector("#header-menu");

if (megaMenu.contains(event.target)) {
this.setState({
isExpanded: false,
});
megaMenu.focus();
function handleKeyDown(e) {
if (e.key === "Escape") {
e.preventDefault();
setAccountMenuIsExpanded(false);
myAccountButton && myAccountButton.current.focus();
}
}

handleMenuItemClick(e) {
function handleMenuItemClick(e) {
const href = e.currentTarget.getAttribute("href");

let eventLabel;
Expand Down Expand Up @@ -98,20 +84,20 @@ export default class Account extends Component {
}
}

componentDidMount() {
useEffect(() => {
const consultationsResponsesLink = {
"Consultation responses": "https://www.nice.org.uk/consultations/",
};

if (this.state.useIdAM) {
if (doesUseIdAM) {
//nice accounts supplies links like: {"John Holland":"https://accounts.nice.org.uk/users/143980/editprofile","Sign out":"https://accounts.nice.org.uk/signout"}
//idam supplies links like:[{ key: "My profile", value: "/Account/todo" },{ key: "Sign out", value: "/Account/Logout" }]
//the following just converts the idam format to the nice accounts format.

const { displayName } = this.props;
const { displayName } = props;
const isLoggedIn = !!displayName;

let links = this.props.links.reduce(function (links, link) {
let links = props.links.reduce(function (links, link) {
links[link.text] = link.url;
return links;
}, {});
Expand All @@ -125,99 +111,90 @@ export default class Account extends Component {
links: links,
};

if (this.props.onLoginStatusChecked) {
this.props.onLoginStatusChecked(convertedData);
if (props.onLoginStatusChecked) {
props.onLoginStatusChecked(convertedData);
}
} else {
//NICE accounts
niceAccountsLoggedIn(this.props.environment)
.then(
function (data) {
if (this.props.onLoginStatusChecked) {
data.links = { ...consultationsResponsesLink, ...data.links };

this.props.onLoginStatusChecked(data);
}
}.bind(this)
)
.catch(
function (e) {
console.warn("Couldn't load account data from NICE accounts", e);
}.bind(this)
);
niceAccountsLoggedIn(props.environment)
.then(function (data) {
if (props.onLoginStatusChecked) {
data.links = { ...consultationsResponsesLink, ...data.links };
props.onLoginStatusChecked(data);
}
})
.catch(function (e) {
console.warn("Couldn't load account data from NICE accounts", e);
});
}
}, []);

document.addEventListener("click", this.handleMegaMenuClick);
}

render() {
const { accountsData, environment } = this.props;
const { accountsData, environment } = props;

let signInLink = {};
if (this.state.useIdAM) {
signInLink = this.props.links[0];
} else {
signInLink["text"] = "Sign in";
signInLink["url"] = getDomainBaseUrl(environment) + "signin";
}
let signInLink = {};
if (doesUseIdAM) {
signInLink = props.links[0];
} else {
signInLink["text"] = "Sign in";
signInLink["url"] = getDomainBaseUrl(environment) + "signin";
}

return this.props.isLoggedIn ? (
<div className={styles.account}>
<button
className={classnames(styles.button, styles.myAccount)}
id="my-account-button"
aria-controls="my-account"
aria-haspopup="menu"
aria-expanded={this.state.isExpanded}
onClick={this.handleMyAccountButtonClick}
onKeyDown={this.handleKeyDown}
>
My account
</button>
<ul
className={styles.menu}
id="my-account"
role="menu"
aria-hidden={!this.state.isExpanded}
aria-labelledby="my-account-button"
onKeyDown={this.handleKeyDown}
>
{accountsData.links &&
Object.keys(accountsData.links).map(
function (text, i) {
return (
<li key={i} role="presentation">
<a
href={accountsData.links[text]}
role="menuitem"
onClick={this.handleMenuItemClick}
onKeyDown={this.handleKeyDown}
data-hj-suppress={
accountsData.links[text].indexOf("profile") > -1
? ""
: null
}
>
{text}
</a>
</li>
);
}.bind(this)
)}
</ul>
</div>
) : (
<a
href={signInLink.url}
className={styles.button}
onClick={this.handleMenuItemClick}
return props.isLoggedIn ? (
<div className={styles.account}>
<button
className={classnames(styles.button, styles.myAccount)}
id="my-account-button"
aria-controls="my-account"
aria-haspopup="menu"
aria-expanded={accountMenuIsExpanded}
onClick={handleMyAccountButtonClick}
onKeyDown={handleKeyDown}
ref={myAccountButton}
>
{signInLink.text}
</a>
);
}
My account
</button>
<ul
className={styles.menu}
id="my-account"
role="menu"
aria-hidden={!accountMenuIsExpanded}
aria-labelledby="my-account-button"
onKeyDown={handleKeyDown}
ref={myAccountMenu}
>
{accountsData.links &&
Object.keys(accountsData.links).map(function (text, i) {
return (
<li key={i} role="presentation">
<a
href={accountsData.links[text]}
role="menuitem"
onClick={handleMenuItemClick}
onKeyDown={handleKeyDown}
data-hj-suppress={
accountsData.links[text].indexOf("profile") > -1 ? "" : null
}
>
{text}
</a>
</li>
);
})}
</ul>
</div>
) : (
<a
href={signInLink.url}
className={styles.button}
onClick={handleMenuItemClick}
>
{signInLink.text}
</a>
);
}

export default Account;

Account.providers = {
idam: "idam",
niceAccounts: "niceAccounts",
Expand Down
Loading

0 comments on commit 482e52e

Please sign in to comment.