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

Commit

Permalink
Add action button (#306)
Browse files Browse the repository at this point in the history
  • Loading branch information
saket2403 authored Aug 29, 2022
1 parent 749f5ce commit 676a9a9
Show file tree
Hide file tree
Showing 41 changed files with 331 additions and 21 deletions.
3 changes: 3 additions & 0 deletions packages/terra-application/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

* Added
* Added user action utility button.

## 1.53.1 - (June 22, 2022)

* Changed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import PropTypes from 'prop-types';
import uuidv4 from 'uuid/v4';
import TerraApplicationNavigation from './private/ApplicationNavigation';
import {
titleConfigPropType, navigationItemsPropType, extensionItemsPropType, utilityItemsPropType, userConfigPropType,
titleConfigPropType, navigationItemsPropType, extensionItemsPropType, utilityItemsPropType, userConfigPropType, userActionConfigPropType,
} from './private/utils/propTypes';

import ApplicationErrorBoundary from '../application-error-boundary';
Expand Down Expand Up @@ -113,6 +113,10 @@ const propTypes = {
* A configuration object that defines the strings rendered within the ApplicationNavigation header.
*/
titleConfig: titleConfigPropType,
/**
* A configuration object to render an action button for user Config.
*/
userActionConfig: userActionConfigPropType,
/**
* A configuration object with information pertaining to the application's user.
*/
Expand Down Expand Up @@ -150,6 +154,7 @@ const ApplicationNavigation = ({
userConfig,
utilityItems,
workspace,
userActionConfig,
}) => {
const applicationIntl = React.useContext(ApplicationIntlContext);
const navigationPromptCheckpointRef = useRef();
Expand Down Expand Up @@ -187,6 +192,7 @@ const ApplicationNavigation = ({
onSelectNavigationItem={propOnSelectNavigationItem && onSelectNavigationItem}
activeNavigationItemKey={activeNavigationItemKey}
userConfig={userConfig}
userActionConfig={userActionConfig}
extensionItems={extensionItems}
onSelectExtensionItem={onSelectExtensionItem}
utilityItems={utilityItems}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import DrawerMenu from './drawer-menu/_DrawerMenu';
import UtilityMenu from './utility-menu/_UtilityMenu';
import { shouldRenderCompactNavigation } from './utils/helpers';
import {
titleConfigPropType, userConfigPropType, navigationItemsPropType, extensionItemsPropType, utilityItemsPropType,
titleConfigPropType, userConfigPropType, navigationItemsPropType, extensionItemsPropType, utilityItemsPropType, userActionConfigPropType,
} from './utils/propTypes';
import WorkspaceLayout from './workspace-layout/WorkspaceLayout';

Expand Down Expand Up @@ -95,6 +95,10 @@ const propTypes = {
* Ex: `onSelectLogout()`
*/
onSelectLogout: PropTypes.func,
/**
* A configuration object to render an action button for user Config.
*/
userActionConfig: userActionConfigPropType,
/**
* An array of configuration objects with information specifying the creation of additional utility menu items.
* These items are rendered within the popup utility menu at larger breakpoints and within the drawer menu at smaller breakpoints.
Expand Down Expand Up @@ -140,6 +144,7 @@ const ApplicationNavigation = ({
notifications,
children,
workspace,
userActionConfig,
}) => {
const drawerMenuRef = useRef();
const contentLayoutRef = useRef();
Expand Down Expand Up @@ -235,6 +240,7 @@ const ApplicationNavigation = ({
<DrawerMenu
titleConfig={titleConfig}
userConfig={userConfig}
userActionConfig={userActionConfig}
hero={hero}
navigationItems={navigationItems}
activeNavigationItemKey={activeNavigationItemKey}
Expand Down Expand Up @@ -270,6 +276,7 @@ const ApplicationNavigation = ({
<UtilityMenu
hero={hero}
userConfig={userConfig}
userActionConfig={userActionConfig}
onSelectSettings={onSelectSettings ? generateMenuClosingCallback(onSelectSettings) : undefined}
onSelectHelp={onSelectHelp ? generateMenuClosingCallback(onSelectHelp) : undefined}
onSelectLogout={onSelectLogout ? generateMenuClosingCallback(onSelectLogout) : undefined}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@
--terra-application-navigation-drawer-count-background-color: #ffda6c;
--terra-application-navigation-drawer-count-border: 1px solid #ffb20b;
--terra-application-navigation-drawer-count-color: #000;

--terra-application-navigation-drawer-menu-action-button-background-color: #222a2e;
--terra-application-navigation-drawer-menu-action-button-border: 1px solid #868a8c;
--terra-application-navigation-drawer-menu-action-button-border-radius: 3px;
--terra-application-navigation-drawer-menu-action-button-color: #b2b5b6;
--terra-application-navigation-drawer-menu-action-button-active-background-color: #000;
--terra-application-navigation-drawer-menu-action-button-active-color: #b2b5b6;
--terra-application-navigation-drawer-menu-action-button-focus-outline: 2px dashed #dae2e7;
--terra-application-navigation-drawer-menu-action-button-focus-outline-offset: 1px;
--terra-application-navigation-drawer-menu-action-button-hover-background-color: #181b1d;
--terra-application-navigation-drawer-menu-action-button-hover-color: #b2b5b6;

--terra-application-navigation-drawer-menu-user-large-avatar-inner-font-size: 1.6521rem;
--terra-application-navigation-drawer-menu-avatar-outline-border: 0.1429rem solid #181b1d;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
}

.info-container {
align-items: flex-start;
display: flex;
flex: 1 1 auto;
flex-direction: column;
Expand All @@ -41,6 +42,10 @@
white-space: normal;
word-break: break-word;
word-wrap: break-word;
}
}

.action-button {
margin-top: 0.2142857143rem;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, {
useRef,
useEffect,
} from 'react';
import PropTypes from 'prop-types';
import classNamesBind from 'classnames/bind';
Expand All @@ -16,8 +17,8 @@ import {
} from 'keycode-js';

import PopupMenuListItem from './_PopupMenuListItem';
import { userConfigPropType } from '../utils/propTypes';
import { logoutUtilityItemId } from '../utils/helpers';
import { userConfigPropType, userActionConfigPropType } from '../utils/propTypes';
import { logoutUtilityItemId, userActionItemId } from '../utils/helpers';
import PopupMenuUser from './_PopupMenuUser';

import styles from './PopupMenu.module.scss';
Expand Down Expand Up @@ -102,6 +103,10 @@ const propTypes = {
* Role of the parent ul that child items should match.
*/
role: PropTypes.oneOf(['list', 'menu', 'listbox']),
/**
* A configuration object to render an action button for user Config.
*/
userActionConfig: userActionConfigPropType,
};

const defaultProps = {
Expand All @@ -112,14 +117,17 @@ const defaultProps = {
};

const PopupMenu = ({
title, footerText, id, onSelectFooterItem, onSelectMenuItem, customContent, userConfig, menuItems, isHeightBounded, showSelections, role,
title, footerText, id, onSelectFooterItem, onSelectMenuItem, customContent, userConfig, menuItems, isHeightBounded, showSelections, role, userActionConfig,
}) => {
const listRef = useRef();
const buttonRef = useRef();

function setButtonRef(node) {
buttonRef.current = node;
}
useEffect(() => {
listRef.current.focus();
}, []);

function handleArrowDown(event) {
if (listRef.current.hasChildNodes()) {
Expand Down Expand Up @@ -201,7 +209,7 @@ const PopupMenu = ({
{customContent}
</div>
) : undefined}
{userConfig ? <PopupMenuUser userConfig={userConfig} /> : null}
{userConfig ? <PopupMenuUser id={id && userActionItemId(id)} 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
@@ -1,22 +1,32 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames/bind';
import Avatar, { Generic } from 'terra-avatar';
import ThemeContext from 'terra-theme-context';
import Button from 'terra-button';

import { userConfigPropType } from '../utils/propTypes';
import { userConfigPropType, userActionConfigPropType } from '../utils/propTypes';

import styles from './PopupMenuUser.module.scss';

const cx = classNames.bind(styles);

const propTypes = {
/**
* An id for the user action button
*/
id: PropTypes.string,
/**
* A configuration object to render an action button for user Config.
*/
userActionConfig: userActionConfigPropType,
/**
* A configuration object with information pertaining to the application's user.
*/
userConfig: userConfigPropType.isRequired,
};

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

return (
Expand All @@ -32,6 +42,17 @@ const PopupMenuUser = ({ userConfig }) => {
<div className={cx('info-container')}>
<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
/>
)}
</div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,40 @@
.avatar-inner {
z-index: 0;
}

.drawer-menu-action-button {
background-color: var(--terra-application-navigation-drawer-menu-action-button-background-color, #004c76);
border: var(--terra-application-navigation-drawer-menu-action-button-border, 1px solid #a7aaab);
border-radius: var(--terra-application-navigation-drawer-menu-action-button-border-radius, 3px);
color: var(--terra-application-navigation-drawer-menu-action-button-color, #dedfe0);
font-size: 1rem;
outline: none;
overflow-wrap: break-word; /* Modern browsers */
padding: 3px 0.7142857143rem;
text-align: center;
text-decoration: none;
text-transform: none;
touch-action: manipulation; // Enable fast tap interaction in webkit browsers; see https://webkit.org/blog/5610/more-responsive-tapping-on-ios/
user-select: none; // Prevent text selection on buttons on mobile devices
white-space: normal;
width: 100%;
word-wrap: break-word; /* For IE 10 and IE 11 */

&[data-focus-styles-enabled='true']:focus {
outline: var(--terra-application-navigation-drawer-menu-action-button-focus-outline, 2px dashed #fff);
outline-offset: var(--terra-application-navigation-drawer-menu-action-button-focus-outline-offset, 1px);
}

&:hover {
background-color: var(--terra-application-navigation-drawer-menu-action-button-hover-background-color, #005685);
color: var(--terra-application-navigation-drawer-menu-action-button-hover-color, #dedfe0);
}

&:active {
background-color: var(--terra-application-navigation-drawer-menu-action-button-active-background-color, #0065a3);
color: var(--terra-application-navigation-drawer-menu-action-button-active-color, #dedfe0);
}
}
}

.large-user-layout {
Expand Down Expand Up @@ -68,7 +102,11 @@
white-space: normal;
word-break: break-word;
word-wrap: break-word;
}
}

.drawer-menu-action-button {
margin-top: 12px;
}
}

.small-user-layout {
Expand Down Expand Up @@ -121,7 +159,11 @@
white-space: normal;
word-break: break-word;
word-wrap: break-word;
}
}

.drawer-menu-action-button {
margin-top: 6px;
}
}
}
/* stylelint-enable selector-max-compound-selectors */
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ import DrawerMenuListItem from './_DrawerMenuListItem';
import DrawerMenuUser from './_DrawerMenuUser';
import DrawerMenuFooterButton from './_DrawerMenuFooterButton';
import {
titleConfigPropType, userConfigPropType, navigationItemsPropType, utilityItemsPropType,
titleConfigPropType, userConfigPropType, navigationItemsPropType, utilityItemsPropType, userActionConfigPropType,
} from '../utils/propTypes';
import {
navigationItemId, utilityItemId, settingsUtilityItemId, helpUtilityItemId, logoutUtilityItemId,
navigationItemId, utilityItemId, settingsUtilityItemId, helpUtilityItemId, logoutUtilityItemId, userActionItemId,
} from '../utils/helpers';

import styles from './DrawerMenu.module.scss';
Expand Down Expand Up @@ -89,6 +89,10 @@ const propTypes = {
* Object containing intl APIs
*/
intl: PropTypes.shape({ formatMessage: PropTypes.func }),
/**
* A configuration object to render an action button for user Config.
*/
userActionConfig: userActionConfigPropType,
};

const defaultProps = {
Expand All @@ -111,9 +115,10 @@ const DrawerMenu = ({
onSelectUtilityItem,
notifications,
intl,
userActionConfig,
}) => {
const titleComponent = titleConfig && !(titleConfig.element || titleConfig.hideTitleWithinDrawerMenu) ? <DrawerMenuTitle titleConfig={titleConfig} /> : undefined;
const userComponent = userConfig ? <DrawerMenuUser userConfig={userConfig} variant={hero ? 'small' : 'large'} /> : undefined;
const userComponent = userConfig ? <DrawerMenuUser id={id && userActionItemId(id)} 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 @@ -3,13 +3,22 @@ import PropTypes from 'prop-types';
import classNames from 'classnames/bind';
import Avatar, { Generic } from 'terra-avatar';
import ThemeContext from 'terra-theme-context';
import { userConfigPropType } from '../utils/propTypes';
import { userConfigPropType, userActionConfigPropType } from '../utils/propTypes';
import { enableFocusStyles, disableFocusStyles } from '../utils/helpers';

import styles from './DrawerMenuUser.module.scss';

const cx = classNames.bind(styles);

const propTypes = {
/**
* An id for the user action button
*/
id: PropTypes.string,
/**
* A configuration object to render an action button for user Config.
*/
userActionConfig: userActionConfigPropType,
/**
* A configuration object with information pertaining to the application's user.
*/
Expand All @@ -24,7 +33,9 @@ const defaultProps = {
variant: 'small',
};

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

return (
Expand All @@ -42,6 +53,19 @@ const DrawerMenuUser = ({ userConfig, variant }) => {
<div className={cx('info-container')}>
<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>
)}
</div>
</div>
);
Expand Down
Loading

0 comments on commit 676a9a9

Please sign in to comment.