Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(context-menu): add props.onClose
Browse files Browse the repository at this point in the history
janhassel committed Nov 30, 2020

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 0f6c22d commit fdb5d39
Showing 2 changed files with 41 additions and 21 deletions.
10 changes: 8 additions & 2 deletions packages/react/src/components/ContextMenu/ContextMenu-story.js
Original file line number Diff line number Diff line change
@@ -23,7 +23,7 @@ export default {
};

export const _ContextMenu = () => {
const [open, setOpen] = useState(true);
const [open, setOpen] = useState(false);
const [position, setPosition] = useState([0, 0]);

function openContextMenu(e) {
@@ -44,7 +44,13 @@ export const _ContextMenu = () => {
});

return (
<ContextMenu open={open} x={position[0]} y={position[1]}>
<ContextMenu
open={open}
x={position[0]}
y={position[1]}
onClose={() => {
setOpen(false);
}}>
<ContextMenuOption label="Share with" renderIcon={FolderShared16}>
<ContextMenuRadioGroup
label="Share with"
52 changes: 33 additions & 19 deletions packages/react/src/components/ContextMenu/ContextMenu.js
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@ import classnames from 'classnames';
import PropTypes from 'prop-types';
import { settings } from 'carbon-components';
import { keys, match } from '../../internal/keyboard';
import ClickListener from '../../internal/ClickListener';

import SelectableContextMenuOption from './SelectableContextMenuOption';
import ContextMenuRadioGroup from './ContextMenuRadioGroup';
@@ -24,32 +25,31 @@ const ContextMenu = function ContextMenu({
level = 1,
x = 0,
y = 0,
onClose = () => {},
...rest
}) {
const rootRef = useRef(null);
const [shouldReverse, setShouldReverse] = useState(false);
const isRootMenu = open !== undefined;

function resetFocus() {
Array.from(
rootRef?.current?.querySelectorAll('[tabindex="0"]') ?? []
rootRef?.current?.element.querySelectorAll('[tabindex="0"]') ?? []
).forEach((node) => {
node.tabIndex = -1;
});
}

function focusNode(node, focus = true) {
function focusNode(node) {
if (node) {
resetFocus();
node.tabIndex = 0;

if (focus) {
node.focus();
}
node.focus();
}
}

function getValidNodes(list) {
const nodes = Array.from(list.childNodes ?? []).reduce((acc, child) => {
const nodes = Array.from(list?.childNodes ?? []).reduce((acc, child) => {
if (child.tagName === 'LI') {
return [...acc, child];
}
@@ -100,6 +100,10 @@ const ContextMenu = function ContextMenu({
function handleKeyDown(event) {
event.stopPropagation();

if (match(event, keys.Escape)) {
onClose();
}

const currentNode = event.target.parentNode;
let nodeToFocus;

@@ -120,8 +124,12 @@ const ContextMenu = function ContextMenu({
}
}

function handleClickOutside() {
onClose();
}

function willFit() {
if (rootRef?.current) {
if (rootRef?.current?.element) {
const bodyWidth = document.body.clientWidth;

const reverseMap = [...Array(level)].reduce(
@@ -151,10 +159,10 @@ const ContextMenu = function ContextMenu({
}

useEffect(() => {
const topLevelNodes = getValidNodes(rootRef?.current);
const topLevelNodes = getValidNodes(rootRef?.current?.element);

if (topLevelNodes && topLevelNodes.length > 0) {
focusNode(topLevelNodes[0].firstChild, false);
focusNode(topLevelNodes[0].firstChild);
}

setShouldReverse(!willFit());
@@ -183,15 +191,16 @@ const ContextMenu = function ContextMenu({
});

return (
<ul
ref={rootRef}
className={classes}
onKeyDown={handleKeyDown}
data-level={level}
style={open ? { left: `${x}px`, top: `${y}px` } : null}
role="menu">
{options}
</ul>
<ClickListener onClickOutside={handleClickOutside} ref={rootRef}>
<ul
className={classes}
onKeyDown={handleKeyDown}
data-level={level}
style={isRootMenu ? { left: `${x}px`, top: `${y}px` } : null}
role="menu">
{options}
</ul>
</ClickListener>
);
};

@@ -206,6 +215,11 @@ ContextMenu.propTypes = {
*/
level: PropTypes.number,

/**
* Function called when the menu is closed
*/
onClose: PropTypes.func,

/**
* Specify whether the ContextMenu is currently open
*/

0 comments on commit fdb5d39

Please sign in to comment.