Skip to content

Commit 6c4c288

Browse files
authored
Merge pull request #3320 from tespin/refactor/menubar-a11y-keyboard-mouse
[Menubar] Adds keyboard behaviors to the menubar, improves accessibility
2 parents 1b09307 + a7c2487 commit 6c4c288

File tree

13 files changed

+1320
-488
lines changed

13 files changed

+1320
-488
lines changed

client/common/ButtonOrLink.jsx

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,60 @@ import PropTypes from 'prop-types';
55
/**
66
* Helper for switching between <button>, <a>, and <Link>
77
*/
8-
const ButtonOrLink = ({ href, children, ...props }) => {
9-
if (href) {
10-
if (href.startsWith('http')) {
8+
9+
const ButtonOrLink = React.forwardRef(
10+
({ href, children, isDisabled, onClick, ...props }, ref) => {
11+
const handleClick = (e) => {
12+
if (isDisabled) {
13+
e.preventDefault();
14+
e.stopPropagation();
15+
return;
16+
}
17+
if (onClick) {
18+
onClick(e);
19+
}
20+
};
21+
22+
if (href) {
23+
if (href.startsWith('http')) {
24+
return (
25+
<a
26+
ref={ref}
27+
href={href}
28+
target="_blank"
29+
rel="noopener noreferrer"
30+
aria-disabled={isDisabled}
31+
{...props}
32+
onClick={handleClick}
33+
>
34+
{children}
35+
</a>
36+
);
37+
}
1138
return (
12-
<a href={href} target="_blank" rel="noopener noreferrer" {...props}>
39+
<Link
40+
ref={ref}
41+
to={href}
42+
aria-disabled={isDisabled}
43+
{...props}
44+
onClick={handleClick}
45+
>
1346
{children}
14-
</a>
47+
</Link>
1548
);
1649
}
1750
return (
18-
<Link to={href} {...props}>
51+
<button
52+
ref={ref}
53+
aria-disabled={isDisabled}
54+
{...props}
55+
onClick={handleClick}
56+
>
1957
{children}
20-
</Link>
58+
</button>
2159
);
2260
}
23-
return <button {...props}>{children}</button>;
24-
};
61+
);
2562

2663
/**
2764
* Accepts all the props of an HTML <a> or <button> tag.
@@ -34,15 +71,19 @@ ButtonOrLink.propTypes = {
3471
* External links should start with 'http' or 'https' and will open in a new window.
3572
*/
3673
href: PropTypes.string,
74+
isDisabled: PropTypes.bool,
3775
/**
3876
* Content of the button/link.
3977
* Can be either a string or a complex element.
4078
*/
41-
children: PropTypes.node.isRequired
79+
children: PropTypes.node.isRequired,
80+
onClick: PropTypes.func
4281
};
4382

4483
ButtonOrLink.defaultProps = {
45-
href: null
84+
href: null,
85+
isDisabled: false,
86+
onClick: null
4687
};
4788

4889
export default ButtonOrLink;

client/common/ButtonOrLink.test.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ describe('ButtonOrLink', () => {
1313
render(<ButtonOrLink onClick={clickHandler}>Text</ButtonOrLink>);
1414
const button = screen.getByRole('button');
1515
expect(button).toBeInstanceOf(HTMLButtonElement);
16-
expect(button).toContainHTML('<button>Text</button>');
16+
expect(button).toContainHTML('<button aria-disabled="false">Text</button>');
1717
fireEvent.click(button);
1818
expect(clickHandler).toHaveBeenCalled();
1919
});

client/common/usePrevious.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { useEffect, useRef } from 'react';
2+
3+
export default function usePrevious(value) {
4+
const ref = useRef();
5+
6+
useEffect(() => {
7+
ref.current = value;
8+
}, [value]);
9+
10+
return ref.current;
11+
}

0 commit comments

Comments
 (0)