@atlaskit/profilecard
Version:
A React component to display a card with user information.
376 lines (375 loc) • 12.9 kB
JavaScript
import _extends from "@babel/runtime/helpers/extends";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import React, { Suspense } from 'react';
import { FormattedMessage, injectIntl } from 'react-intl-next';
import { withAnalyticsEvents } from '@atlaskit/analytics-next';
import { GiveKudosLauncherLazy, KudosType } from '@atlaskit/give-kudos';
import { fg } from '@atlaskit/platform-feature-flags';
import Popup from '@atlaskit/popup';
import { Box } from '@atlaskit/primitives';
import { layers } from '@atlaskit/theme/constants';
import filterActions from '../../internal/filterActions';
import messages from '../../messages';
import { cardTriggered, fireEvent, profileCardRendered } from '../../util/analytics';
import { isBasicClick } from '../../util/click';
import { DELAY_MS_HIDE, DELAY_MS_SHOW } from '../../util/config';
import { getPageTime } from '../../util/performance';
import { ErrorBoundary } from '../Error';
import { TeamProfileCardLazy } from './lazyTeamProfileCard';
import TeamLoadingState from './TeamLoadingState';
export class TeamProfileCardTriggerInternal extends React.PureComponent {
constructor(...args) {
super(...args);
_defineProperty(this, "_isMounted", false);
_defineProperty(this, "showTimer", 0);
_defineProperty(this, "hideTimer", 0);
_defineProperty(this, "openedByHover", false);
_defineProperty(this, "openTime", 0);
_defineProperty(this, "fireAnalytics", payload => {
// Don't fire any analytics if the component is unmounted
if (!this._isMounted) {
return;
}
if (this.props.createAnalyticsEvent) {
fireEvent(this.props.createAnalyticsEvent, payload);
}
});
_defineProperty(this, "fireAnalyticsWithDuration", generator => {
const event = generator(getPageTime() - this.openTime);
this.fireAnalytics(event);
});
_defineProperty(this, "hideProfilecard", (delay = 0) => {
clearTimeout(this.showTimer);
clearTimeout(this.hideTimer);
this.hideTimer = window.setTimeout(() => {
this.setState({
visible: false
});
}, delay);
});
_defineProperty(this, "showProfilecard", (delay = 0) => {
clearTimeout(this.hideTimer);
clearTimeout(this.showTimer);
this.showTimer = window.setTimeout(() => {
if (!this.state.visible) {
this.clientFetchProfile();
this.openTime = getPageTime();
this.setState({
visible: true
});
}
}, delay);
});
_defineProperty(this, "onClick", event => {
if (this.props.triggerLinkType === 'link') {
// We want to prevent navigation occurring on basic click, but it's important that
// cmd+click, ctrl+click, etc. still work as expected.
if (isBasicClick(event)) {
event.preventDefault();
}
}
if (this.props.triggerLinkType === 'clickable-link') {
if (this.props.viewProfileOnClick) {
this.props.viewProfileOnClick(event);
}
}
if (this.props.trigger !== 'hover') {
this.openedByHover = false;
this.showProfilecard(0);
if (!this.state.visible) {
this.fireAnalytics(cardTriggered('team', 'click', this.props.teamId));
}
}
});
_defineProperty(this, "onMouseEnter", () => {
if (this.props.trigger === 'click') {
return;
}
if (!this.state.visible) {
this.openedByHover = true;
this.fireAnalytics(cardTriggered('team', 'hover', this.props.teamId));
}
this.showProfilecard(DELAY_MS_SHOW);
});
_defineProperty(this, "onMouseLeave", () => {
if (this.props.trigger === 'click') {
return;
}
if (this.openedByHover) {
this.hideProfilecard(DELAY_MS_HIDE);
}
});
_defineProperty(this, "onKeyPress", event => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
this.setState({
isTriggeredByKeyboard: true
});
this.showProfilecard(0);
if (!this.state.visible) {
this.fireAnalytics(cardTriggered('team', 'click', this.props.teamId));
}
}
});
_defineProperty(this, "onClose", () => {
this.hideProfilecard();
this.setState({
isTriggeredByKeyboard: false
});
});
_defineProperty(this, "openKudosDrawer", () => {
this.hideProfilecard(DELAY_MS_HIDE);
this.setState({
kudosDrawerOpen: true
});
});
_defineProperty(this, "closeKudosDrawer", () => {
this.setState({
kudosDrawerOpen: false
});
});
_defineProperty(this, "kudosUrl", () => {
const recipientId = this.props.teamId && `&recipientId=${this.props.teamId}` || '';
const cloudId = this.props.cloudId && `&cloudId=${this.props.cloudId}` || '';
return `${this.state.teamCentralBaseUrl}/kudos/give?type=team${recipientId}${cloudId}`;
});
_defineProperty(this, "stopPropagation", event => {
// We need to stop propagation when users click on the card, so that it
// doesn't trigger any special effects that occur when clicking the trigger.
event.stopPropagation();
});
_defineProperty(this, "triggerListeners", {
onClick: this.onClick,
onMouseEnter: this.onMouseEnter,
onMouseLeave: this.onMouseLeave
});
_defineProperty(this, "cardListeners", {
onClick: this.stopPropagation,
onMouseEnter: this.onMouseEnter,
onMouseLeave: this.onMouseLeave
});
_defineProperty(this, "state", {
visible: false,
isLoading: undefined,
hasError: false,
error: null,
data: null,
shouldShowGiveKudos: false,
teamCentralBaseUrl: undefined,
kudosDrawerOpen: false,
isTriggeredByKeyboard: false
});
_defineProperty(this, "clientFetchProfile", () => {
const {
orgId,
teamId
} = this.props;
const {
isLoading
} = this.state;
if (isLoading === true) {
// don't fetch data when fetching is in process
return;
}
this.setState({
isLoading: true,
data: null
}, () => {
const fireEvent = event => {
this.fireAnalytics(event);
};
const requests = Promise.all([this.props.resourceClient.getTeamProfile(teamId, orgId, fireEvent), this.props.resourceClient.shouldShowGiveKudos(), this.props.resourceClient.getTeamCentralBaseUrl({
withOrgContext: true,
withSiteContext: true
})]);
requests.then(res => this.handleClientSuccess(...res), err => this.handleClientError(err)).catch(err => this.handleClientError(err));
});
});
_defineProperty(this, "onErrorBoundary", () => {
this.fireAnalytics(profileCardRendered('team', 'errorBoundary', {
duration: 0
}));
this.setState({
renderError: true
});
});
_defineProperty(this, "renderProfileCard", () => {
const {
generateUserLink,
onUserClick,
viewingUserId,
viewProfileLink,
viewProfileOnClick
} = this.props;
const {
data,
error,
hasError,
isLoading
} = this.state;
const newProps = {
clientFetchProfile: this.clientFetchProfile,
actions: this.filterActions(),
analytics: this.fireAnalyticsWithDuration,
team: data || undefined,
generateUserLink,
onUserClick,
viewingUserId,
viewProfileLink,
viewProfileOnClick
};
return /*#__PURE__*/React.createElement("div", this.cardListeners, this.state.visible && /*#__PURE__*/React.createElement(Suspense, {
fallback: /*#__PURE__*/React.createElement(TeamLoadingState, {
analytics: this.fireAnalyticsWithDuration
})
}, /*#__PURE__*/React.createElement(TeamProfileCardLazy, _extends({}, newProps, {
isLoading: isLoading,
hasError: hasError,
errorType: error,
isTriggeredByKeyboard: this.state.isTriggeredByKeyboard
}))));
});
_defineProperty(this, "renderKudosLauncher", () => {
return this.state.shouldShowGiveKudos && /*#__PURE__*/React.createElement(Suspense, {
fallback: null
}, /*#__PURE__*/React.createElement(GiveKudosLauncherLazy, {
isOpen: this.state.kudosDrawerOpen,
recipient: {
type: KudosType.TEAM,
recipientId: this.props.teamId
},
analyticsSource: "team-profile-card",
teamCentralBaseUrl: this.state.teamCentralBaseUrl,
cloudId: this.props.cloudId || '',
addFlag: this.props.addFlag,
onClose: this.closeKudosDrawer
}));
});
_defineProperty(this, "renderTrigger", triggerProps => {
const {
children,
intl,
triggerLinkType,
viewProfileLink
} = this.props;
if (triggerLinkType === 'none') {
return /*#__PURE__*/React.createElement(React.Fragment, null, this.renderKudosLauncher(), fg('enable_team_profilecard_toggletip_a11y_fix') ? /*#__PURE__*/React.createElement(Box, _extends({
as: "span",
role: "button",
testId: "team-profilecard-trigger-wrapper",
tabIndex: 0,
"aria-label": intl.formatMessage(messages.teamProfileCardAriaLabel),
onKeyUp: this.onKeyPress
}, triggerProps, this.triggerListeners), children) : /*#__PURE__*/React.createElement("span", _extends({
"data-testid": "team-profilecard-trigger-wrapper"
}, triggerProps, this.triggerListeners), children));
}
return /*#__PURE__*/React.createElement(React.Fragment, null, this.renderKudosLauncher(), /*#__PURE__*/React.createElement("a", _extends({
"data-testid": "team-profilecard-trigger-wrapper"
// eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766
,
style: {
color: 'initial',
textDecoration: 'none'
},
href: viewProfileLink
}, triggerProps, {
ref: triggerProps.ref
}, this.triggerListeners), children));
});
}
componentDidMount() {
this._isMounted = true;
}
componentDidUpdate(prevProps) {
const {
orgId,
teamId,
resourceClient
} = this.props;
const {
visible
} = this.state;
// just re-fetching data when the card opens
if (visible && (teamId !== prevProps.teamId || orgId !== prevProps.orgId || resourceClient !== prevProps.resourceClient)) {
this.setState({
isLoading: undefined
}, this.clientFetchProfile);
}
}
componentWillUnmount() {
this._isMounted = false;
clearTimeout(this.showTimer);
clearTimeout(this.hideTimer);
}
handleClientSuccess(team, shouldShowGiveKudos, teamCentralBaseUrl) {
if (!this._isMounted) {
return;
}
this.setState({
isLoading: false,
hasError: false,
data: team,
shouldShowGiveKudos,
teamCentralBaseUrl
});
}
handleClientError(err) {
if (!this._isMounted) {
return;
}
this.setState({
isLoading: false,
hasError: true,
error: err
});
}
filterActions() {
const actions = filterActions(this.props.actions, this.state.data);
if (this.state.shouldShowGiveKudos) {
const kudosAction = {
label: /*#__PURE__*/React.createElement(FormattedMessage, messages.giveKudosButton),
id: 'give-kudos',
callback: () => {
this.openKudosDrawer();
},
link: this.kudosUrl()
};
return actions.concat([kudosAction]);
}
return actions;
}
renderPopup() {
if (this.state.renderError) {
return this.props.children;
}
return /*#__PURE__*/React.createElement(ErrorBoundary, {
onError: this.onErrorBoundary
}, /*#__PURE__*/React.createElement(Popup, {
isOpen: !!this.state.visible,
onClose: this.onClose,
placement: this.props.position,
content: this.renderProfileCard,
trigger: triggerProps => this.renderTrigger(triggerProps),
zIndex: layers.modal(),
shouldFlip: true,
autoFocus: this.props.trigger !== 'hover' && !this.openedByHover,
shouldRenderToParent: fg('enable_appropriate_reading_order_in_profile_card') && this.props.shouldRenderToParent
}));
}
render() {
if (this.props.children) {
return this.renderPopup();
} else {
throw new Error('Component "TeamProfileCardTrigger" must have "children" property');
}
}
}
_defineProperty(TeamProfileCardTriggerInternal, "defaultProps", {
actions: [],
trigger: 'hover',
position: 'bottom-start',
triggerLinkType: 'link',
shouldRenderToParent: true
});
export default withAnalyticsEvents()(injectIntl(TeamProfileCardTriggerInternal));