Skip to content

Commit

Permalink
refactor(headermenu): added typescript types to headermenu (#16116)
Browse files Browse the repository at this point in the history
* refactor(headermenu): added typescript types to headermenu

* Update packages/react/src/components/UIShell/HeaderMenu.tsx

taylor's suggestion

Co-authored-by: Taylor Jones <tay1orjones@users.noreply.github.com>

* refactor(headermenu): changed customclassname to string literal

* refactor(headermenu): added proptype comments to interface

---------

Co-authored-by: Taylor Jones <tay1orjones@users.noreply.github.com>
  • Loading branch information
Gururajj77 and tay1orjones committed Apr 10, 2024
1 parent 0376df7 commit 719b5f4
Showing 1 changed file with 93 additions and 13 deletions.
Expand Up @@ -21,7 +21,80 @@ import { composeEventHandlers } from '../../tools/events';
* with managing focus. It also passes along refs to each child so that it can
* help manage focus state of its children.
*/
class HeaderMenu extends React.Component {

interface HeaderMenuProps {
/**
* Required props for the accessibility label of the menu
*/
'aria-label'?: string;
'aria-labelledby'?: string;

/**
* Optionally provide a custom class to apply to the underlying `<li>` node
*/
className?: string;

/**
* Provide a custom ref handler for the menu button
*/
focusRef?: React.Ref<any>;

/**
* Applies selected styles to the item if a user sets this to true and `aria-current !== 'page'`.
*/
isActive?: boolean;

/**
* Applies selected styles to the item if a user sets this to true and `aria-current !== 'page'`.
* @deprecated Please use `isActive` instead. This will be removed in the next major release.
*/
isCurrentPage?: boolean;

/**
* Provide a label for the link text
*/
menuLinkName: string;

/**
* Optionally provide an onBlur handler that is called when the underlying
* button fires it's onblur event
*/
onBlur?: (event: React.FocusEvent<HTMLLIElement>) => void;

/**
* Optionally provide an onClick handler that is called when the underlying
* button fires it's onclick event
*/
onClick?: (event: React.MouseEvent<HTMLLIElement, MouseEvent>) => void;

/**
* Optionally provide an onKeyDown handler that is called when the underlying
* button fires it's onkeydown event
*/
onKeyDown?: (event: React.KeyboardEvent<HTMLLIElement>) => void;

/**
* Optional component to render instead of string
*/
renderMenuContent?: () => JSX.Element;

/**
* Optionally provide a tabIndex for the underlying menu button
*/
tabIndex?: number;

/**
* The children should be a series of `HeaderMenuItem` components.
*/
children?: React.ReactNode;
}

interface HeaderMenuState {
expanded: boolean;
selectedIndex: number | null;
}

class HeaderMenu extends React.Component<HeaderMenuProps, HeaderMenuState> {
static propTypes = {
/**
* Required props for the accessibility label of the menu
Expand Down Expand Up @@ -89,7 +162,9 @@ class HeaderMenu extends React.Component {

static contextType = PrefixContext;

_subMenus = React.createRef();
_subMenus: React.RefObject<HTMLUListElement> = React.createRef();
private items: Array<HTMLElement | null> = [];
private menuButtonRef: HTMLElement | null = null;

constructor(props) {
super(props);
Expand Down Expand Up @@ -163,8 +238,10 @@ class HeaderMenu extends React.Component {
* button node when that child should receive focus.
*/
handleMenuButtonRef = (node) => {
if (this.props.focusRef) {
this.props.focusRef(node);
const { focusRef } = this.props;
// Check if focusRef is a function before calling it
if (typeof focusRef === 'function') {
focusRef(node);
}
this.menuButtonRef = node;
};
Expand All @@ -189,7 +266,9 @@ class HeaderMenu extends React.Component {
}));

// Return focus to menu button when the user hits ESC.
this.menuButtonRef.focus();
if (this.menuButtonRef !== null) {
this.menuButtonRef.focus();
}
return;
}
};
Expand All @@ -205,7 +284,8 @@ class HeaderMenu extends React.Component {
children,
renderMenuContent: MenuContent,
menuLinkName,
focusRef, // eslint-disable-line no-unused-vars
// eslint-disable-next-line @typescript-eslint/no-unused-vars
focusRef,
onBlur,
onClick,
onKeyDown,
Expand All @@ -215,10 +295,10 @@ class HeaderMenu extends React.Component {
const hasActiveDescendant = (childrenArg) =>
React.Children.toArray(childrenArg).some(
(child) =>
child.props &&
React.isValidElement(child) && // This is the type guard
(child.props.isActive ||
child.props.isCurrentPage ||
(child.props.children instanceof Array &&
(Array.isArray(child.props.children) &&
hasActiveDescendant(child.props.children)))
);

Expand All @@ -228,9 +308,9 @@ class HeaderMenu extends React.Component {
};
const itemClassName = cx({
[`${prefix}--header__submenu`]: true,
[customClassName]: !!customClassName,
[`${customClassName}`]: !!customClassName,
});
let isActivePage = isActive ? isActive : isCurrentPage;
const isActivePage = isActive ? isActive : isCurrentPage;
const linkClassName = cx({
[`${prefix}--header__menu-item`]: true,
[`${prefix}--header__menu-title`]: true,
Expand Down Expand Up @@ -286,17 +366,17 @@ class HeaderMenu extends React.Component {
* `tabIndex: -1` so the user won't hit a large number of items in their tab
* sequence when they might not want to go through all the items.
*/
_renderMenuItem = (item, index) => {
_renderMenuItem = (item: React.ReactNode, index: number) => {
if (React.isValidElement(item)) {
return React.cloneElement(item, {
return React.cloneElement(item as React.ReactElement<any>, {
ref: this.handleItemRef(index),
});
}
};
}

const HeaderMenuForwardRef = React.forwardRef((props, ref) => {
return <HeaderMenu {...props} focusRef={ref} />;
return <HeaderMenu menuLinkName="link" {...props} focusRef={ref} />;
});

HeaderMenuForwardRef.displayName = 'HeaderMenu';
Expand Down

0 comments on commit 719b5f4

Please sign in to comment.