Skip to content
This repository has been archived by the owner on May 24, 2024. It is now read-only.

Fix Utility Menu auto closing issue #315

Merged
merged 6 commits into from
Nov 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/terra-application/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Changelog

## Unreleased
* Fixed
* Fixed utility menu to close on userAction button click.

## 1.54.0 - (November 2, 2022)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ const ApplicationNavigation = ({
className={cx('drawer-menu-focus-trap-container')}
>
<DrawerMenu
menuClosingCallback={generateMenuClosingCallback}
titleConfig={titleConfig}
userConfig={userConfig}
userActionConfig={userActionConfig}
Expand Down Expand Up @@ -274,6 +275,7 @@ const ApplicationNavigation = ({
isContentFocusDisabled
>
<UtilityMenu
menuClosingCallback={generateMenuClosingCallback}
hero={hero}
userConfig={userConfig}
userActionConfig={userActionConfig}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,13 @@ const propTypes = {
* A configuration object to render an action button for user Config.
*/
userActionConfig: userActionConfigPropType,
/**
* @private
* Given a callback function, menuClosingCallback will return a new function
* that will ensure that the various menu states are reset before the callback function
* is executed.
*/
menuClosingCallback: PropTypes.func,
};

const defaultProps = {
Expand All @@ -117,7 +124,19 @@ const defaultProps = {
};

const PopupMenu = ({
title, footerText, id, onSelectFooterItem, onSelectMenuItem, customContent, userConfig, menuItems, isHeightBounded, showSelections, role, userActionConfig,
title,
footerText,
id,
onSelectFooterItem,
menuClosingCallback,
onSelectMenuItem,
customContent,
userConfig,
menuItems,
isHeightBounded,
showSelections,
role,
userActionConfig,
}) => {
const listRef = useRef();
const buttonRef = useRef();
Expand Down Expand Up @@ -209,7 +228,7 @@ const PopupMenu = ({
{customContent}
</div>
) : undefined}
{userConfig ? <PopupMenuUser id={id && userActionItemId(id)} userActionConfig={userActionConfig} userConfig={userConfig} /> : null}
{userConfig ? <PopupMenuUser id={id && userActionItemId(id)} menuClosingCallback={menuClosingCallback} userActionConfig={userActionConfig} userConfig={userConfig} /> : null}
<ul className={cx('utility-list')} aria-label={title} ref={listRef} role={role} tabIndex="0" onKeyDown={handleKeyDown}>
{menuItems.map(item => (
<PopupMenuListItem
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,18 @@ const propTypes = {
* A configuration object with information pertaining to the application's user.
*/
userConfig: userConfigPropType.isRequired,
/**
* @private
* Given a callback function, menuClosingCallback will return a new function
* that will ensure that the various menu states are reset before the callback function
* is executed.
*/
menuClosingCallback: PropTypes.func,
};

const PopupMenuUser = ({ userConfig, userActionConfig, id }) => {
const PopupMenuUser = ({
userConfig, userActionConfig, id, menuClosingCallback,
}) => {
const theme = React.useContext(ThemeContext);

return (
Expand All @@ -43,15 +52,15 @@ const PopupMenuUser = ({ userConfig, userActionConfig, id }) => {
<div aria-hidden className={cx('name')}>{userConfig.name}</div>
{userConfig.detail ? <div className={cx('detail')}>{userConfig.detail}</div> : null}
{ userActionConfig && (
<Button
id={id || undefined}
text={userActionConfig.text}
onClick={userActionConfig.userActionCallback}
data-navigation-utility-item-logout
className={cx('action-button')}
variant="ghost"
isCompact
/>
<Button
id={id || undefined}
text={userActionConfig.text}
onClick={menuClosingCallback(userActionConfig.userActionCallback)}
data-navigation-popupmenu-item-user-action
className={cx('action-button')}
variant="ghost"
isCompact
/>
)}
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ const propTypes = {
* A configuration object to render an action button for user Config.
*/
userActionConfig: userActionConfigPropType,
/**
* @private
* Given a callback function, menuClosingCallback will return a new function
* that will ensure that the various menu states are reset before the callback function
* is executed.
*/
menuClosingCallback: PropTypes.func,
};

const defaultProps = {
Expand All @@ -116,9 +123,18 @@ const DrawerMenu = ({
notifications,
intl,
userActionConfig,
menuClosingCallback,
}) => {
const titleComponent = titleConfig && !(titleConfig.element || titleConfig.hideTitleWithinDrawerMenu) ? <DrawerMenuTitle titleConfig={titleConfig} /> : undefined;
const userComponent = userConfig ? <DrawerMenuUser id={id && userActionItemId(id)} userActionConfig={userActionConfig} userConfig={userConfig} variant={hero ? 'small' : 'large'} /> : undefined;
const userComponent = userConfig ? (
<DrawerMenuUser
id={id && userActionItemId(id)}
menuClosingCallback={menuClosingCallback}
userActionConfig={userActionConfig}
userConfig={userConfig}
variant={hero ? 'small' : 'large'}
/>
) : undefined;
const logoutButton = onSelectLogout ? (
<div className={cx('footer')}>
<DrawerMenuFooterButton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,21 @@ const propTypes = {
* Size variant of the user. One of values [`small`, `large`].
*/
variant: PropTypes.oneOf(['small', 'large']),
/**
* @private
* Given a callback function, menuClosingCallback will return a new function
* that will ensure that the various menu states are reset before the callback function
* is executed.
*/
menuClosingCallback: PropTypes.func,
};

const defaultProps = {
variant: 'small',
};

const DrawerMenuUser = ({
userConfig, variant, userActionConfig, id,
userConfig, variant, userActionConfig, id, menuClosingCallback,
}) => {
const theme = React.useContext(ThemeContext);

Expand All @@ -54,17 +61,18 @@ const DrawerMenuUser = ({
<div aria-hidden className={cx('name')}>{userConfig.name}</div>
{userConfig.detail ? <div className={cx('detail')}>{userConfig.detail}</div> : null}
{ userActionConfig && (
<button
id={id || undefined}
className={cx('drawer-menu-action-button', theme.className)}
type="button"
onClick={userActionConfig.userActionCallback}
onBlur={enableFocusStyles}
onMouseDown={disableFocusStyles}
data-focus-styles-enabled
>
{userActionConfig.text}
</button>
<button
id={id || undefined}
className={cx('drawer-menu-action-button', theme.className)}
type="button"
onClick={menuClosingCallback(userActionConfig.userActionCallback)}
onBlur={enableFocusStyles}
onMouseDown={disableFocusStyles}
data-focus-styles-enabled
data-navigation-drawermenu-item-user-action
>
{userActionConfig.text}
</button>
)}
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ const propTypes = {
* A configuration object to render an action button for user Config.
*/
userActionConfig: userActionConfigPropType,
/**
* @private
* Given a callback function, menuClosingCallback will return a new function
* that will ensure that the various menu states are reset before the callback function
* is executed.
*/
menuClosingCallback: PropTypes.func,
};

const defaultProps = {
Expand All @@ -71,7 +78,7 @@ const utilityMenuSettingsKey = 'terra-application-navigation.utility-menu.settin
const utilityMenuHelpKey = 'terra-application-navigation.utility-menu.help';

const UtilityMenu = ({
userConfig, hero, onSelectSettings, onSelectHelp, onSelectLogout, utilityItems, id, onSelectUtilityItem, isHeightBounded, intl, userActionConfig,
userConfig, hero, onSelectSettings, onSelectHelp, menuClosingCallback, onSelectLogout, utilityItems, id, onSelectUtilityItem, isHeightBounded, intl, userActionConfig,
}) => {
let menuItems = [];
menuItems = utilityItems.map(item => ({
Expand Down Expand Up @@ -109,6 +116,7 @@ const UtilityMenu = ({
title={intl.formatMessage({ id: 'terraApplication.navigation.utilityMenu.headerTitle' })}
footerText={intl.formatMessage({ id: 'terraApplication.navigation.utilityMenu.logout' })}
onSelectFooterItem={onSelectLogout}
menuClosingCallback={menuClosingCallback}
userConfig={userConfig}
userActionConfig={userActionConfig}
customContent={hero}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,6 @@ const userConfig = {
initials: 'TN',
};

const userActionConfig = {
text: 'Edit Photo',
// eslint-disable-next-line no-console
userActionCallback: () => console.log('Button Clicked'),
};

const extensionItems = [
{
icon: <IconSearch />,
Expand Down Expand Up @@ -141,6 +135,12 @@ function ApplicationNavigationTest() {
const [isDrawerOpen, setDrawerOpen] = useState(false);
const [functionType, setFunctionType] = useState('');

const userActionConfig = {
text: 'Edit Photo',
// eslint-disable-next-line no-alert
userActionCallback: () => setFunctionType('userActionButton was clicked'),
};

/* eslint-disable no-alert */
const handleItemSelection = (key, metaData) => {
alert(`itemKey: ${key}, metaData: ${metaData.test}`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ exports[`ApplicationNavigation correctly applies the theme context className 1`]
className="drawer-menu-focus-trap-container"
>
<InjectIntl(DrawerMenu)
menuClosingCallback={[Function]}
notifications={Object {}}
utilityItems={Array []}
>
Expand Down Expand Up @@ -105,6 +106,7 @@ exports[`ApplicationNavigation correctly applies the theme context className 1`]
"timeZone": null,
}
}
menuClosingCallback={[Function]}
navigationItems={Array []}
notifications={Object {}}
utilityItems={Array []}
Expand Down Expand Up @@ -355,6 +357,7 @@ exports[`ApplicationNavigation should render default element 1`] = `
targetRef={[Function]}
>
<InjectIntl(UtilityMenu)
menuClosingCallback={[Function]}
utilityItems={Array []}
/>
</Popup>
Expand Down Expand Up @@ -501,6 +504,7 @@ exports[`ApplicationNavigation should render with prop data at large breakpoint
my test hero
</div>
}
menuClosingCallback={[Function]}
onSelectHelp={[Function]}
onSelectLogout={[Function]}
onSelectSettings={[Function]}
Expand Down Expand Up @@ -565,6 +569,7 @@ exports[`ApplicationNavigation should render with prop data at medium breakpoint
my test hero
</div>
}
menuClosingCallback={[Function]}
navigationItems={
Array [
Object {
Expand Down Expand Up @@ -734,6 +739,7 @@ exports[`ApplicationNavigation should render with prop data at medium breakpoint
my test hero
</div>
}
menuClosingCallback={[Function]}
onSelectHelp={[Function]}
onSelectLogout={[Function]}
onSelectSettings={[Function]}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ describe('DrawerMenuUser', () => {
it('should render default element', () => {
const shallowComponent = shallow(
<DrawerMenuUser
menuClosingCallback={() => jest.fn()}
userConfig={{
name: 'user-name',
detail: 'user-detail',
Expand All @@ -26,6 +27,7 @@ describe('DrawerMenuUser', () => {
it('should render with small variant', () => {
const shallowComponent = shallow(
<DrawerMenuUser
menuClosingCallback={() => jest.fn()}
userConfig={{
name: 'user-name',
detail: 'user-detail',
Expand All @@ -43,6 +45,7 @@ describe('DrawerMenuUser', () => {
it('should render with large variant', () => {
const shallowComponent = shallow(
<DrawerMenuUser
menuClosingCallback={() => jest.fn()}
userConfig={{
name: 'user-name',
detail: 'user-detail',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ exports[`DrawerMenuUser should render default element 1`] = `
<button
className="drawer-menu-action-button"
data-focus-styles-enabled={true}
data-navigation-drawermenu-item-user-action={true}
onBlur={[Function]}
onClick={[Function]}
onClick={[MockFunction]}
onMouseDown={[Function]}
type="button"
>
Expand Down Expand Up @@ -91,8 +92,9 @@ exports[`DrawerMenuUser should render with large variant 1`] = `
<button
className="drawer-menu-action-button"
data-focus-styles-enabled={true}
data-navigation-drawermenu-item-user-action={true}
onBlur={[Function]}
onClick={[Function]}
onClick={[MockFunction]}
onMouseDown={[Function]}
type="button"
>
Expand Down Expand Up @@ -142,8 +144,9 @@ exports[`DrawerMenuUser should render with small variant 1`] = `
<button
className="drawer-menu-action-button"
data-focus-styles-enabled={true}
data-navigation-drawermenu-item-user-action={true}
onBlur={[Function]}
onClick={[Function]}
onClick={[MockFunction]}
onMouseDown={[Function]}
type="button"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ describe('UtilityMenu', () => {
it('should render with function callbacks', () => {
const mountComponent = mountWithIntl(
<UtilityMenu.WrappedComponent
menuClosingCallback={() => jest.fn()}
hero={<div>my test hero</div>}
userConfig={{
name: 'user-name',
Expand Down
Loading