box-ui-elements-test
Version:
Box UI Elements
231 lines (212 loc) • 8.73 kB
JavaScript
import React, { Component } from 'react';
import classNames from 'classnames';
import PropTypes from 'prop-types';
import { FormattedMessage, injectIntl } from 'react-intl';
import Button from '../../components/button';
import messages from './messages';
import PresenceAvatarList from './PresenceAvatarList';
import PresenceCollaboratorsList from './PresenceCollaboratorsList';
import { ARROW_DOWN, ENTER, SPACE } from '../../common/keyboard-events';
import { collaboratorsPropType, flyoutPositionPropType } from './propTypes';
import { Flyout, Overlay } from '../../components/flyout';
import {
GROWTH_382_EXPERIMENT_BUCKET,
GROWTH_382_AUTOFLY_CLASS,
GROWTH_382_AUTOFLY_CLASS_FIRST_LOAD,
} from './constants';
import './Presence.scss';
class Presence extends Component {
/* eslint-disable no-underscore-dangle */
static propTypes = {
/** Addtional attributes for avatar container */
avatarAttributes: PropTypes.object,
className: PropTypes.string,
collaborators: PropTypes.arrayOf(collaboratorsPropType).isRequired,
/** Addtional attributes for presence container */
containerAttributes: PropTypes.object,
/** Get Link callback */
getLinkCallback: PropTypes.func,
/** Invite button callback */
inviteCallback: PropTypes.func,
/** Maximum number of avatars to display before showing a +{n} avatar */
maxDisplayedAvatars: PropTypes.number,
/** Maximum number of collaborators before displaying a {maxAdditionalCollaboratorsNum}+ avatar */
maxAdditionalCollaboratorsNum: PropTypes.number,
/** Callback funtion for avatar mouseEnter, argument: id of user */
onAvatarMouseEnter: PropTypes.func,
/** Callback function for avatar mouseLeave */
onAvatarMouseLeave: PropTypes.func,
/** Callback funtion for Flyout events, argument: SyntheticEvent */
onFlyoutClose: PropTypes.func,
onFlyoutOpen: PropTypes.func,
onFlyoutScroll: PropTypes.func,
/** GROWTH-382 bucketing */
experimentBucket: PropTypes.string,
/** GROWTH-382 broadcast that the user wants to view stats from the flyout */
onAccessStatsRequested: PropTypes.func,
/** GROWTH-382 log that the user wants to view collaborators from the flyout */
onClickViewCollaborators: PropTypes.func,
/** Option to change the orientation of the dropdown. MUST be: bottom-right, bottom-left, bottom-center etc. or in this specific format */
flyoutPosition: flyoutPositionPropType,
/** Sets the tether constraint to scrollParent for the flyout */
constrainToScrollParent: PropTypes.bool,
/** Sets the tether constraint to window for the flyout */
constrainToWindow: PropTypes.bool,
/** Closes the flyout when window loses focus */
closeOnWindowBlur: PropTypes.bool,
intl: PropTypes.any,
};
static defaultProps = {
className: '',
maxDisplayedAvatars: 3,
maxAdditionalCollaboratorsNum: 99,
experimentBucket: null,
flyoutPosition: 'bottom-left',
constrainToScrollParent: true,
constrainToWindow: false,
closeOnWindowBlur: false,
};
state = {
isDropdownActive: false,
showActivityPrompt: Boolean(
this.props.collaborators.length &&
this.props.onClickViewCollaborators &&
this.props.experimentBucket === GROWTH_382_EXPERIMENT_BUCKET,
),
};
saveRefToContainer = el => {
this.presenceContainerEl = el;
};
_handleOverlayOpen = event => {
const { onFlyoutOpen } = this.props;
this.setState({
isDropdownActive: true,
});
if (onFlyoutOpen) {
onFlyoutOpen(event);
}
};
_handleOverlayClose = event => {
const { onFlyoutClose } = this.props;
this.setState({
isDropdownActive: false,
});
if (onFlyoutClose) {
onFlyoutClose(event);
}
};
stopPropagationAndPreventDefault = event => {
event.stopPropagation();
event.preventDefault();
};
openDropDown = () => {
if (this.presenceContainerEl) {
this.presenceContainerEl.click();
}
};
handleKeyDown = event => {
switch (event.key) {
case ARROW_DOWN:
case ENTER:
case SPACE:
this.openDropDown();
this.stopPropagationAndPreventDefault(event);
break;
default:
break;
}
};
// GROWTH-382 click through the first CTA, spawn the normal Presence dropdown
_showRecentsFlyout = event => {
const { onClickViewCollaborators } = this.props;
onClickViewCollaborators();
this.stopPropagationAndPreventDefault(event);
this.setState({ showActivityPrompt: false });
};
render() {
const {
avatarAttributes,
className,
closeOnWindowBlur,
collaborators,
constrainToScrollParent,
constrainToWindow,
containerAttributes,
flyoutPosition,
getLinkCallback,
intl,
inviteCallback,
maxAdditionalCollaboratorsNum,
maxDisplayedAvatars,
onAvatarMouseEnter,
onAvatarMouseLeave,
onFlyoutScroll,
} = this.props;
const { isDropdownActive } = this.state;
// GROWTH-382
const { experimentBucket, onAccessStatsRequested } = this.props;
const { showActivityPrompt } = this.state;
let requestAccessStats = null;
if (!showActivityPrompt && experimentBucket === GROWTH_382_EXPERIMENT_BUCKET) {
requestAccessStats = (
// eslint-disable-next-line jsx-a11y/anchor-is-valid
<a className="presence-overlay-request-stats" href="#" onClick={onAccessStatsRequested}>
<FormattedMessage {...messages.previewPresenceFlyoutAccessStatsLink} />
</a>
);
}
const overlayClassNames = classNames('presence-overlay', {
[GROWTH_382_AUTOFLY_CLASS]: experimentBucket && !showActivityPrompt,
[GROWTH_382_AUTOFLY_CLASS_FIRST_LOAD]: experimentBucket && showActivityPrompt,
});
const overlayContent = showActivityPrompt ? (
<>
<FormattedMessage {...messages.previewPresenceFlyoutCopy} />
<Button className="btn-primary" onClick={this._showRecentsFlyout}>
<FormattedMessage {...messages.previewPresenceFlyoutActivityCTA} />
</Button>
</>
) : (
<PresenceCollaboratorsList
collaborators={collaborators}
experimentBucket={experimentBucket}
getLinkCallback={getLinkCallback}
inviteCallback={inviteCallback}
onScroll={onFlyoutScroll}
/>
);
return (
<Flyout
className={`presence ${className}`}
closeOnWindowBlur={closeOnWindowBlur}
constrainToScrollParent={constrainToScrollParent}
constrainToWindow={constrainToWindow}
isVisibleByDefault={showActivityPrompt}
onClose={this._handleOverlayClose}
onOpen={this._handleOverlayOpen}
position={flyoutPosition}
>
<PresenceAvatarList
ref={this.saveRefToContainer}
aria-label={intl.formatMessage(messages.toggleButtonLabel)}
avatarAttributes={avatarAttributes}
className={classNames('presence-avatar-container', { 'dropdown-active': isDropdownActive })}
collaborators={collaborators}
hideTooltips={isDropdownActive}
maxAdditionalCollaborators={maxAdditionalCollaboratorsNum}
maxDisplayedAvatars={maxDisplayedAvatars}
onAvatarMouseEnter={onAvatarMouseEnter}
onAvatarMouseLeave={onAvatarMouseLeave}
onKeyDown={this.handleKeyDown}
{...containerAttributes}
/>
<Overlay className={overlayClassNames} shouldDefaultFocus={false}>
{overlayContent}
{requestAccessStats}
</Overlay>
</Flyout>
);
}
}
export { Presence as PresenceComponent };
export default injectIntl(Presence);