-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Add functions to ContextualMenuItem to open and close menus on command #4741
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
aafc5d3
90aa6a6
a844d06
da3bef7
4fda842
cad5754
8d73bf7
be9da3d
863b40b
6fa83bb
8699f31
e06449a
782e08f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| { | ||
| "changes": [ | ||
| { | ||
| "packageName": "office-ui-fabric-react", | ||
| "comment": "Add ContextualMenuItem functions to open and close menus", | ||
| "type": "minor" | ||
| } | ||
| ], | ||
| "packageName": "office-ui-fabric-react", | ||
| "email": "[email protected]" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,7 +24,8 @@ import { | |
| getFirstFocusable, | ||
| getLastFocusable, | ||
| css, | ||
| shouldWrapFocus | ||
| shouldWrapFocus, | ||
| createRef | ||
| } from '../../Utilities'; | ||
| import { hasSubmenu, getIsChecked, isItemDisabled } from '../../utilities/contextualMenu/index'; | ||
| import { withResponsiveMode, ResponsiveMode } from '../../utilities/decorators/withResponsiveMode'; | ||
|
|
@@ -499,6 +500,8 @@ export class ContextualMenu extends BaseComponent<IContextualMenuProps, IContext | |
| const itemHasSubmenu = hasSubmenu(item); | ||
| const nativeProps = getNativeProps(item, anchorProperties); | ||
| const disabled = isItemDisabled(item); | ||
| const anchorRef = createRef<HTMLAnchorElement>(); | ||
| const dismissMenu = this.dismiss.bind(this, undefined); | ||
|
|
||
| return ( | ||
| <div> | ||
|
|
@@ -511,6 +514,7 @@ export class ContextualMenu extends BaseComponent<IContextualMenuProps, IContext | |
| <a | ||
| { ...nativeProps } | ||
| { ...keytipAttributes } | ||
| ref={ anchorRef } | ||
| href={ item.href } | ||
| target={ item.target } | ||
| rel={ anchorRel } | ||
|
|
@@ -534,6 +538,11 @@ export class ContextualMenu extends BaseComponent<IContextualMenuProps, IContext | |
| index={ index } | ||
| onCheckmarkClick={ hasCheckmarks ? this._onItemClick : undefined } | ||
| hasIcons={ hasIcons } | ||
| componentRef={ item.renderItemRef } | ||
| openSubMenu={ this._onItemSubMenuExpand } | ||
| dismissSubMenu={ this._onSubMenuDismiss } | ||
| dismissMenu={ dismissMenu } | ||
| subMenuTargetRef={ anchorRef } | ||
| /> | ||
| </a> | ||
| ) } | ||
|
|
@@ -599,6 +608,8 @@ export class ContextualMenu extends BaseComponent<IContextualMenuProps, IContext | |
| hasMenu: true | ||
| }; | ||
| } | ||
| const btnRef = createRef<HTMLButtonElement>(); | ||
|
||
| const dismissMenu = this.dismiss.bind(this, undefined); | ||
|
|
||
| return ( | ||
| <KeytipData | ||
|
|
@@ -608,6 +619,7 @@ export class ContextualMenu extends BaseComponent<IContextualMenuProps, IContext | |
| > | ||
| { (keytipAttributes: any): JSX.Element => ( | ||
| <button | ||
| ref={ btnRef } | ||
| { ...buttonNativeProperties as React.ButtonHTMLAttributes<HTMLButtonElement> } | ||
| { ...itemButtonProperties as React.ButtonHTMLAttributes<HTMLButtonElement> } | ||
| { ...keytipAttributes } | ||
|
|
@@ -618,6 +630,11 @@ export class ContextualMenu extends BaseComponent<IContextualMenuProps, IContext | |
| index={ index } | ||
| onCheckmarkClick={ hasCheckmarks ? this._onItemClick : undefined } | ||
| hasIcons={ hasIcons } | ||
| componentRef={ item.renderItemRef } | ||
| openSubMenu={ this._onItemSubMenuExpand } | ||
| dismissSubMenu={ this._onSubMenuDismiss } | ||
| dismissMenu={ dismissMenu } | ||
| subMenuTargetRef={ btnRef } | ||
| /> | ||
| </button> | ||
| ) } | ||
|
|
@@ -635,6 +652,10 @@ export class ContextualMenu extends BaseComponent<IContextualMenuProps, IContext | |
| hasIcons?: boolean): JSX.Element { | ||
| const { contextualMenuItemAs } = this.props; | ||
|
|
||
| const openSubMenu = this._onItemSubMenuExpand.bind(this); | ||
| const dismissSubMenu = this._onSubMenuDismiss.bind(this); | ||
|
||
| const dismissMenu = this.dismiss.bind(this, undefined); | ||
|
|
||
| return ( | ||
| <ContextualMenuSplitButton | ||
| item={ item } | ||
|
|
@@ -653,6 +674,10 @@ export class ContextualMenu extends BaseComponent<IContextualMenuProps, IContext | |
| onItemClick={ this._onItemClick } | ||
| onItemClickBase={ this._onItemClickBase } | ||
| onItemKeyDown={ this._onItemKeyDown } | ||
| openSubMenu={ openSubMenu } | ||
| dismissSubMenu={ dismissSubMenu } | ||
| dismissMenu={ dismissMenu } | ||
| componentRef={ item.renderItemRef } | ||
| /> | ||
| ); | ||
| } | ||
|
|
@@ -889,7 +914,7 @@ export class ContextualMenu extends BaseComponent<IContextualMenuProps, IContext | |
| } | ||
| } | ||
|
|
||
| private _onItemSubMenuExpand(item: IContextualMenuItem, target: HTMLElement) { | ||
| private _onItemSubMenuExpand = (item: IContextualMenuItem, target: HTMLElement): void => { | ||
| if (this.state.expandedMenuItemKey !== item.key) { | ||
|
|
||
| if (this.state.expandedMenuItemKey) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| import * as React from 'react'; | ||
| import { hasSubmenu, getIsChecked } from '../../utilities/contextualMenu/index'; | ||
| import { getRTL } from '../../Utilities'; | ||
| import { BaseComponent, getRTL } from '../../Utilities'; | ||
| import { Icon } from '../../Icon'; | ||
| import { IContextualMenuItemProps } from './ContextualMenuItem.types'; | ||
|
|
||
|
|
@@ -67,19 +67,42 @@ const renderSubMenuIcon = ({ item, classNames }: IContextualMenuItemProps) => { | |
| return null; | ||
| }; | ||
|
|
||
| export const ContextualMenuItem: React.StatelessComponent<IContextualMenuItemProps> = (props) => { | ||
| const { item, classNames } = props; | ||
| export class ContextualMenuItem extends BaseComponent<IContextualMenuItemProps, {}> { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why did the extends change from React.StatelessComponent to BaseComponent?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To take advantage of componentRef. You can't use a ref on a functional component
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another option would be to just extend PureComponent and assign ref instead of componentRef. But then naming the prop 'componentRef' is a little misleading
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any decision here? |
||
| public render() { | ||
| const { item, classNames } = this.props; | ||
|
|
||
| return ( | ||
| <div | ||
| className={ | ||
| item.split ? classNames.linkContentMenu : classNames.linkContent | ||
| } | ||
| > | ||
| { renderCheckMarkIcon(props) } | ||
| { renderItemIcon(props) } | ||
| { renderItemName(props) } | ||
| { renderSubMenuIcon(props) } | ||
| </div> | ||
| ); | ||
| }; | ||
| return ( | ||
| <div | ||
| className={ | ||
| item.split ? classNames.linkContentMenu : classNames.linkContent | ||
| } | ||
| > | ||
| { renderCheckMarkIcon(this.props) } | ||
| { renderItemIcon(this.props) } | ||
| { renderItemName(this.props) } | ||
| { renderSubMenuIcon(this.props) } | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| public openSubMenu = (): void => { | ||
| const { item, openSubMenu, subMenuTargetRef } = this.props; | ||
| if (hasSubmenu(item) && openSubMenu && subMenuTargetRef && subMenuTargetRef.current) { | ||
| openSubMenu(item, subMenuTargetRef.current); | ||
| } | ||
| } | ||
|
|
||
| public dismissSubMenu = (): void => { | ||
| const { item, dismissSubMenu } = this.props; | ||
| if (hasSubmenu(item) && dismissSubMenu) { | ||
| dismissSubMenu(); | ||
| } | ||
| } | ||
|
|
||
| public dismissMenu = (dismissAll?: boolean): void => { | ||
| const { dismissMenu } = this.props; | ||
| if (dismissMenu) { | ||
| dismissMenu(dismissAll); | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you need to bind these sine the functions are defined a variables the
thisshould be correct. And for dismissMenu you shound't need to pass in undefined since the parameters are optionalThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm passing undefined because this.dismiss takes in the event (optional) as the first param but dismissMenu takes in one param which is dismissAll, which is the second param in this.dismiss. So I'm padding the params basically
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will look into not binding them. I was just following the pattern throughout the file where there are binds