@atlaskit/profilecard 16.12.1 → 17.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/cjs/client/ProfileCardClient.js +4 -4
  3. package/dist/cjs/client/TeamCentralCardClient.js +106 -59
  4. package/dist/cjs/client/TeamProfileCardClient.js +13 -12
  5. package/dist/cjs/client/UserProfileCardClient.js +25 -2
  6. package/dist/cjs/client/errorUtils.js +25 -0
  7. package/dist/cjs/components/Error/ErrorMessage.js +40 -77
  8. package/dist/cjs/components/Team/TeamLoadingState.js +1 -1
  9. package/dist/cjs/components/Team/TeamProfileCard.js +4 -4
  10. package/dist/cjs/components/Team/TeamProfileCardTrigger.js +3 -3
  11. package/dist/cjs/components/User/OverflowProfileCardButtons.js +44 -10
  12. package/dist/cjs/components/User/ProfileCard.js +195 -362
  13. package/dist/cjs/components/User/ProfileCardDetails.js +142 -0
  14. package/dist/cjs/components/User/ProfileCardResourced.js +25 -20
  15. package/dist/cjs/components/User/ProfileCardTrigger.js +35 -7
  16. package/dist/cjs/components/User/ReportingLinesDetails.js +13 -13
  17. package/dist/cjs/components/User/UserLoadingState.js +14 -2
  18. package/dist/cjs/util/analytics.js +31 -16
  19. package/dist/cjs/version.json +1 -1
  20. package/dist/es2019/client/ProfileCardClient.js +4 -4
  21. package/dist/es2019/client/TeamCentralCardClient.js +101 -59
  22. package/dist/es2019/client/TeamProfileCardClient.js +3 -9
  23. package/dist/es2019/client/UserProfileCardClient.js +23 -2
  24. package/dist/es2019/client/errorUtils.js +17 -0
  25. package/dist/es2019/components/Error/ErrorMessage.js +38 -42
  26. package/dist/es2019/components/Team/TeamLoadingState.js +2 -2
  27. package/dist/es2019/components/Team/TeamProfileCard.js +5 -5
  28. package/dist/es2019/components/Team/TeamProfileCardTrigger.js +4 -4
  29. package/dist/es2019/components/User/OverflowProfileCardButtons.js +30 -4
  30. package/dist/es2019/components/User/ProfileCard.js +161 -292
  31. package/dist/es2019/components/User/ProfileCardDetails.js +118 -0
  32. package/dist/es2019/components/User/ProfileCardResourced.js +21 -21
  33. package/dist/es2019/components/User/ProfileCardTrigger.js +32 -6
  34. package/dist/es2019/components/User/ReportingLinesDetails.js +10 -11
  35. package/dist/es2019/components/User/UserLoadingState.js +10 -2
  36. package/dist/es2019/util/analytics.js +13 -8
  37. package/dist/es2019/version.json +1 -1
  38. package/dist/esm/client/ProfileCardClient.js +4 -4
  39. package/dist/esm/client/TeamCentralCardClient.js +106 -59
  40. package/dist/esm/client/TeamProfileCardClient.js +11 -12
  41. package/dist/esm/client/UserProfileCardClient.js +22 -2
  42. package/dist/esm/client/errorUtils.js +17 -0
  43. package/dist/esm/components/Error/ErrorMessage.js +35 -80
  44. package/dist/esm/components/Team/TeamLoadingState.js +2 -2
  45. package/dist/esm/components/Team/TeamProfileCard.js +5 -5
  46. package/dist/esm/components/Team/TeamProfileCardTrigger.js +4 -4
  47. package/dist/esm/components/User/OverflowProfileCardButtons.js +39 -9
  48. package/dist/esm/components/User/ProfileCard.js +180 -362
  49. package/dist/esm/components/User/ProfileCardDetails.js +120 -0
  50. package/dist/esm/components/User/ProfileCardResourced.js +17 -17
  51. package/dist/esm/components/User/ProfileCardTrigger.js +33 -7
  52. package/dist/esm/components/User/ReportingLinesDetails.js +12 -12
  53. package/dist/esm/components/User/UserLoadingState.js +7 -2
  54. package/dist/esm/util/analytics.js +21 -12
  55. package/dist/esm/version.json +1 -1
  56. package/dist/types/client/ProfileCardClient.d.ts +3 -2
  57. package/dist/types/client/TeamCentralCardClient.d.ts +2 -0
  58. package/dist/types/client/TeamProfileCardClient.d.ts +2 -1
  59. package/dist/types/client/UserProfileCardClient.d.ts +2 -1
  60. package/dist/types/client/errorUtils.d.ts +6 -0
  61. package/dist/types/components/Error/ErrorMessage.d.ts +6 -15
  62. package/dist/types/components/Team/TeamProfileCardTrigger.d.ts +5 -11
  63. package/dist/types/components/User/OverflowProfileCardButtons.d.ts +4 -3
  64. package/dist/types/components/User/ProfileCard.d.ts +5 -29
  65. package/dist/types/components/User/ProfileCardDetails.d.ts +3 -0
  66. package/dist/types/components/User/ProfileCardResourced.d.ts +7 -3
  67. package/dist/types/components/User/ProfileCardTrigger.d.ts +3 -40
  68. package/dist/types/components/User/ReportingLinesDetails.d.ts +2 -4
  69. package/dist/types/components/User/UserLoadingState.d.ts +5 -1
  70. package/dist/types/components/User/lazyProfileCard.d.ts +1 -1
  71. package/dist/types/types.d.ts +12 -10
  72. package/dist/types/util/analytics.d.ts +22 -13
  73. package/package.json +9 -9
  74. package/report.api.md +74 -124
  75. package/dist/cjs/internal/analytics.js +0 -15
  76. package/dist/es2019/internal/analytics.js +0 -8
  77. package/dist/esm/internal/analytics.js +0 -8
  78. package/dist/types/internal/analytics.d.ts +0 -8
@@ -46,6 +46,14 @@ const buildCheckFeatureFlagQuery = (featureKey, context) => ({
46
46
  }
47
47
  });
48
48
 
49
+ function hasTCWorkspace(config) {
50
+ return config.cloudId ? fetch(`/gateway/api/watermelon/organization/containsAnyWorkspace?cloudId=${config.cloudId}`).then(res => {
51
+ return !res || res && res.ok;
52
+ }) : Promise.resolve(false);
53
+ }
54
+
55
+ let isTCReadyPromiseMap = new Map();
56
+
49
57
  class TeamCentralCardClient extends CachingClient {
50
58
  /**
51
59
  * Simple circuit breaker to avoid making unnecessary calls to Team Central on auth failures
@@ -60,6 +68,22 @@ class TeamCentralCardClient extends CachingClient {
60
68
  this.options = options;
61
69
  this.bypassOnFailure = false;
62
70
  this.featureFlagKeys = new Map();
71
+ this.isTCReadyPromise = this.createTcReadyPromise(options);
72
+ }
73
+
74
+ createTcReadyPromise(config) {
75
+ if (config.cloudId) {
76
+ let promise = isTCReadyPromiseMap.get(config.cloudId);
77
+
78
+ if (!promise) {
79
+ promise = hasTCWorkspace(config);
80
+ isTCReadyPromiseMap.set(config.cloudId, promise);
81
+ }
82
+
83
+ return promise;
84
+ }
85
+
86
+ return Promise.resolve(true);
63
87
  }
64
88
 
65
89
  async makeFeatureFlagCheckRequest(featureKey, context) {
@@ -68,7 +92,7 @@ class TeamCentralCardClient extends CachingClient {
68
92
  }
69
93
 
70
94
  const query = buildCheckFeatureFlagQuery(featureKey, context);
71
- const response = await graphqlQuery(this.options.teamCentralUrl, query);
95
+ const response = await graphqlQuery(`${this.options.teamCentralUrl}?operationName=isFeatureKeyEnabled`, query);
72
96
  return response.isFeatureEnabled.enabled;
73
97
  }
74
98
 
@@ -78,83 +102,101 @@ class TeamCentralCardClient extends CachingClient {
78
102
  }
79
103
 
80
104
  const query = buildReportingLinesQuery(userId);
81
- const response = await graphqlQuery(this.options.teamCentralUrl, query);
105
+ const response = await graphqlQuery(`${this.options.teamCentralUrl}?operationName=ReportingLines`, query);
82
106
  return response.reportingLines;
83
107
  }
84
108
 
85
109
  getReportingLines(userId) {
86
- if (!userId) {
87
- return Promise.reject(new Error('userId missing'));
88
- }
89
-
90
- const cache = this.getCachedProfile(userId);
91
-
92
- if (cache) {
93
- return Promise.resolve(cache);
94
- }
95
-
96
- if (this.bypassOnFailure) {
97
- return Promise.resolve({});
98
- }
110
+ return this.isTCReadyPromise.then(workSpaceExists => {
111
+ if (workSpaceExists) {
112
+ if (!userId) {
113
+ return Promise.reject(new Error('userId missing'));
114
+ }
99
115
 
100
- return new Promise(resolve => {
101
- this.makeRequest(userId).then(data => {
102
- const enhancedData = {
103
- managers: this.filterReportingLinesUser(data === null || data === void 0 ? void 0 : data.managers),
104
- reports: this.filterReportingLinesUser(data === null || data === void 0 ? void 0 : data.reports)
105
- };
116
+ const cache = this.getCachedProfile(userId);
106
117
 
107
- if (this.cache) {
108
- this.setCachedProfile(userId, enhancedData);
118
+ if (cache) {
119
+ return Promise.resolve(cache);
109
120
  }
110
121
 
111
- resolve(enhancedData);
112
- }).catch(error => {
113
- if ((error === null || error === void 0 ? void 0 : error.status) === 401 || (error === null || error === void 0 ? void 0 : error.status) === 403) {
114
- // Trigger circuit breaker
115
- this.bypassOnFailure = true;
122
+ if (this.bypassOnFailure) {
123
+ return Promise.resolve({});
116
124
  }
117
- /**
118
- * Reporting lines aren't part of the critical path of profile card.
119
- * Just resolve with empty values instead of bubbling up the error.
120
- */
121
125
 
126
+ return new Promise(resolve => {
127
+ this.makeRequest(userId).then(data => {
128
+ const enhancedData = {
129
+ managers: this.filterReportingLinesUser(data === null || data === void 0 ? void 0 : data.managers),
130
+ reports: this.filterReportingLinesUser(data === null || data === void 0 ? void 0 : data.reports)
131
+ };
132
+
133
+ if (this.cache) {
134
+ this.setCachedProfile(userId, enhancedData);
135
+ }
136
+
137
+ resolve(enhancedData);
138
+ }).catch(error => {
139
+ if ((error === null || error === void 0 ? void 0 : error.status) === 401 || (error === null || error === void 0 ? void 0 : error.status) === 403) {
140
+ // Trigger circuit breaker
141
+ this.bypassOnFailure = true;
142
+ }
143
+ /**
144
+ * Reporting lines aren't part of the critical path of profile card.
145
+ * Just resolve with empty values instead of bubbling up the error.
146
+ */
147
+
148
+
149
+ resolve({});
150
+ });
151
+ });
152
+ }
122
153
 
123
- resolve({});
154
+ return Promise.resolve({
155
+ managers: [],
156
+ reports: []
124
157
  });
125
- });
158
+ }, () => Promise.resolve({
159
+ managers: [],
160
+ reports: []
161
+ }));
126
162
  }
127
163
 
128
164
  getFlagEnabled(featureKey, productIdentifier) {
129
- if (!featureKey) {
130
- return Promise.reject(new Error('featureKey missing'));
131
- }
132
-
133
- if (this.featureFlagKeys.has(featureKey)) {
134
- return Promise.resolve(this.featureFlagKeys.get(featureKey));
135
- }
165
+ return this.isTCReadyPromise.then(workSpaceExists => {
166
+ if (workSpaceExists) {
167
+ if (!featureKey) {
168
+ return Promise.reject(new Error('featureKey missing'));
169
+ }
136
170
 
137
- if (this.bypassOnFailure) {
138
- return Promise.resolve(false);
139
- }
171
+ if (this.featureFlagKeys.has(featureKey)) {
172
+ return Promise.resolve(this.featureFlagKeys.get(featureKey));
173
+ }
140
174
 
141
- const context = [{
142
- key: 'productIdentifier',
143
- value: productIdentifier || 'unset'
144
- }];
145
- return new Promise(resolve => {
146
- this.makeFeatureFlagCheckRequest(featureKey, context).then(enabled => {
147
- this.featureFlagKeys.set(featureKey, enabled);
148
- resolve(enabled);
149
- }).catch(error => {
150
- if ((error === null || error === void 0 ? void 0 : error.status) === 401 || (error === null || error === void 0 ? void 0 : error.status) === 403) {
151
- // Trigger circuit breaker
152
- this.bypassOnFailure = true;
175
+ if (this.bypassOnFailure) {
176
+ return Promise.resolve(false);
153
177
  }
154
178
 
155
- resolve(false);
156
- });
157
- });
179
+ const context = [{
180
+ key: 'productIdentifier',
181
+ value: productIdentifier || 'unset'
182
+ }];
183
+ return new Promise(resolve => {
184
+ this.makeFeatureFlagCheckRequest(featureKey, context).then(enabled => {
185
+ this.featureFlagKeys.set(featureKey, enabled);
186
+ resolve(enabled);
187
+ }).catch(error => {
188
+ if ((error === null || error === void 0 ? void 0 : error.status) === 401 || (error === null || error === void 0 ? void 0 : error.status) === 403) {
189
+ // Trigger circuit breaker
190
+ this.bypassOnFailure = true;
191
+ }
192
+
193
+ resolve(false);
194
+ });
195
+ });
196
+ }
197
+
198
+ return Promise.resolve(false);
199
+ }, () => Promise.resolve(false));
158
200
  }
159
201
 
160
202
  filterReportingLinesUser(users = []) {
@@ -1,6 +1,7 @@
1
1
  import { teamRequestAnalytics } from '../util/analytics';
2
2
  import { getPageTime } from '../util/performance';
3
3
  import CachingClient from './CachingClient';
4
+ import { getErrorAttributes } from './errorUtils';
4
5
  import { getTeamFromAGG } from './getTeamFromAGG';
5
6
  import { graphqlQuery } from './graphqlUtils';
6
7
  const QUERY = `query Team($teamId: String!, $organizationId: String) {
@@ -28,12 +29,6 @@ const buildTeamQuery = (teamId, orgId) => ({
28
29
  }
29
30
  });
30
31
 
31
- const IGNORED_ERRORS = ['NotPermitted', 'Gone'];
32
-
33
- function isRealError(error) {
34
- return !IGNORED_ERRORS.includes(error.reason);
35
- }
36
-
37
32
  export default class TeamProfileCardClient extends CachingClient {
38
33
  constructor(options) {
39
34
  super(options);
@@ -91,11 +86,10 @@ export default class TeamProfileCardClient extends CachingClient {
91
86
 
92
87
  resolve(data);
93
88
  }).catch(error => {
94
- if (analytics && isRealError(error)) {
89
+ if (analytics) {
95
90
  analytics(teamRequestAnalytics('failed', {
96
91
  duration: getPageTime() - startTime,
97
- errorStatus: error.code,
98
- errorReason: error.reason,
92
+ ...getErrorAttributes(error),
99
93
  gateway: shouldUseGateway
100
94
  }));
101
95
  }
@@ -1,4 +1,7 @@
1
+ import { userRequestAnalytics } from '../util/analytics';
2
+ import { getPageTime } from '../util/performance';
1
3
  import CachingClient from './CachingClient';
4
+ import { getErrorAttributes } from './errorUtils';
2
5
  import { graphqlQuery } from './graphqlUtils';
3
6
  /**
4
7
  * Transform response from GraphQL
@@ -21,7 +24,6 @@ export const modifyResponse = response => {
21
24
  return {
22
25
  isBot: data.isBot,
23
26
  isCurrentUser: data.isCurrentUser,
24
- isNotMentionable: data.isNotMentionable,
25
27
  status: data.status,
26
28
  statusModifiedDate: data.statusModifiedDate || undefined,
27
29
  avatarUrl: data.avatarUrl || undefined,
@@ -83,7 +85,7 @@ export default class UserProfileCardClient extends CachingClient {
83
85
  return modifyResponse(response);
84
86
  }
85
87
 
86
- getProfile(cloudId, userId) {
88
+ getProfile(cloudId, userId, analytics) {
87
89
  if (!userId) {
88
90
  return Promise.reject(new Error('userId missing'));
89
91
  }
@@ -96,13 +98,32 @@ export default class UserProfileCardClient extends CachingClient {
96
98
  }
97
99
 
98
100
  return new Promise((resolve, reject) => {
101
+ const startTime = getPageTime();
102
+
103
+ if (analytics) {
104
+ analytics(userRequestAnalytics('triggered'));
105
+ }
106
+
99
107
  this.makeRequest(cloudId, userId).then(data => {
100
108
  if (this.cache) {
101
109
  this.setCachedProfile(cacheIdentifier, data);
102
110
  }
103
111
 
112
+ if (analytics) {
113
+ analytics(userRequestAnalytics('succeeded', {
114
+ duration: getPageTime() - startTime
115
+ }));
116
+ }
117
+
104
118
  resolve(data);
105
119
  }).catch(error => {
120
+ if (analytics) {
121
+ analytics(userRequestAnalytics('failed', {
122
+ duration: getPageTime() - startTime,
123
+ ...getErrorAttributes(error)
124
+ }));
125
+ }
126
+
106
127
  reject(error);
107
128
  });
108
129
  });
@@ -0,0 +1,17 @@
1
+ const IGNORED_ERRORS = ['NotPermitted', 'Gone'];
2
+
3
+ function isIgnoredError(error) {
4
+ return !!error && IGNORED_ERRORS.includes(error.reason);
5
+ }
6
+
7
+ export const getErrorAttributes = error => {
8
+ var _error$response, _error$response$heade;
9
+
10
+ const traceId = !!error ? (_error$response = error.response) === null || _error$response === void 0 ? void 0 : (_error$response$heade = _error$response.headers) === null || _error$response$heade === void 0 ? void 0 : _error$response$heade.get('atl-traceid') : undefined;
11
+ return {
12
+ errorStatus: error === null || error === void 0 ? void 0 : error.code,
13
+ errorReason: error === null || error === void 0 ? void 0 : error.reason,
14
+ isSLOFailure: !isIgnoredError(error),
15
+ traceId: traceId !== null && traceId !== void 0 ? traceId : undefined
16
+ };
17
+ };
@@ -1,47 +1,43 @@
1
- import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
- import React from 'react';
3
- import AkButton from '@atlaskit/button/custom-theme-button';
1
+ import React, { useEffect } from 'react';
2
+ import Button from '@atlaskit/button/standard-button';
4
3
  import IconError from '@atlaskit/icon/glyph/cross-circle';
5
4
  import { ErrorText, ErrorTitle, ErrorWrapper } from '../../styled/Error';
6
- export default class ErrorMessage extends React.PureComponent {
7
- constructor(...args) {
8
- super(...args);
5
+ import { profileCardRendered } from '../../util/analytics';
9
6
 
10
- _defineProperty(this, "renderNotFound", () => /*#__PURE__*/React.createElement(ErrorTitle, null, "The user is no longer available for the site"));
11
-
12
- _defineProperty(this, "renderDefault", () => /*#__PURE__*/React.createElement(ErrorTitle, null, "Oops, looks like we\u2019re having issues", /*#__PURE__*/React.createElement("br", null), this.props.reload ? /*#__PURE__*/React.createElement(ErrorText, null, "Try again and we\u2019ll give it another shot") : null));
13
-
14
- _defineProperty(this, "renderRetryButton", () => this.props.reload ? /*#__PURE__*/React.createElement(AkButton, {
15
- appearance: "link",
16
- onClick: this.props.reload
17
- }, "Try again") : null);
18
- }
19
-
20
- renderErrorContent() {
21
- const errorType = this.props.errorType || {
22
- reason: 'default'
23
- };
24
-
25
- switch (errorType.reason) {
26
- case 'NotFound':
27
- return this.renderNotFound();
28
-
29
- default:
30
- return this.renderDefault();
7
+ const ErrorMessage = props => {
8
+ const errorType = props.errorType || {
9
+ reason: 'default'
10
+ };
11
+ const errorReason = errorType.reason;
12
+ const {
13
+ fireAnalytics,
14
+ reload
15
+ } = props;
16
+ const hasRetry = !!reload;
17
+ useEffect(() => {
18
+ fireAnalytics(profileCardRendered('user', 'error', {
19
+ hasRetry,
20
+ errorType: errorReason
21
+ }));
22
+ }, [errorReason, fireAnalytics, hasRetry]);
23
+
24
+ const errorContent = () => {
25
+ if (errorReason === 'NotFound') {
26
+ return /*#__PURE__*/React.createElement(ErrorTitle, null, "The user is no longer available for the site");
31
27
  }
32
- }
33
-
34
- render() {
35
- return /*#__PURE__*/React.createElement(ErrorWrapper, null, /*#__PURE__*/React.createElement(IconError, {
36
- label: "icon error",
37
- size: "xlarge"
38
- }), this.renderErrorContent(), this.renderRetryButton());
39
- }
40
28
 
41
- }
42
-
43
- _defineProperty(ErrorMessage, "defaultProps", {
44
- errorType: {
45
- reason: 'default'
46
- }
47
- });
29
+ return /*#__PURE__*/React.createElement(ErrorTitle, null, "Oops, looks like we\u2019re having issues", /*#__PURE__*/React.createElement("br", null), reload && /*#__PURE__*/React.createElement(ErrorText, null, "Try again and we\u2019ll give it another shot"));
30
+ };
31
+
32
+ return /*#__PURE__*/React.createElement(ErrorWrapper, {
33
+ "data-testid": "profilecard-error"
34
+ }, /*#__PURE__*/React.createElement(IconError, {
35
+ label: "icon error",
36
+ size: "xlarge"
37
+ }), errorContent(), reload && /*#__PURE__*/React.createElement(Button, {
38
+ appearance: "link",
39
+ onClick: reload
40
+ }, "Try again"));
41
+ };
42
+
43
+ export default ErrorMessage;
@@ -1,13 +1,13 @@
1
1
  import React, { useEffect } from 'react';
2
2
  import Spinner from '@atlaskit/spinner';
3
3
  import { CardContent, CardHeader, CardWrapper, LoadingWrapper } from '../../styled/TeamCard';
4
- import { teamProfileCardRendered } from '../../util/analytics';
4
+ import { profileCardRendered } from '../../util/analytics';
5
5
  export default (props => {
6
6
  const {
7
7
  analytics
8
8
  } = props;
9
9
  useEffect(() => {
10
- analytics(duration => teamProfileCardRendered('spinner', {
10
+ analytics(duration => profileCardRendered('team', 'spinner', {
11
11
  duration
12
12
  }));
13
13
  }, [analytics]);
@@ -13,7 +13,7 @@ import messages from '../../messages';
13
13
  import { AnimatedKudosButton, KudosBlobAnimation } from '../../styled/Card';
14
14
  import { ErrorWrapper, TeamErrorText, TeamErrorTitle } from '../../styled/Error';
15
15
  import { ActionButtons, AvatarSection, CardContent, CardHeader, CardWrapper, Description, DescriptionWrapper, MemberCount, MoreButton, TeamName, WrappedButton } from '../../styled/TeamCard';
16
- import { errorRetryClicked, moreActionsClicked, moreMembersClicked, teamActionClicked, teamAvatarClicked, teamProfileCardRendered } from '../../util/analytics';
16
+ import { actionClicked, errorRetryClicked, moreActionsClicked, moreMembersClicked, profileCardRendered, teamAvatarClicked } from '../../util/analytics';
17
17
  import { isBasicClick } from '../../util/click';
18
18
  import { ErrorIllustration } from '../Error';
19
19
  import TeamLoadingState from './TeamLoadingState';
@@ -88,7 +88,7 @@ const TeamMembers = ({
88
88
 
89
89
  function onActionClick(action, analytics, index) {
90
90
  return (event, ...args) => {
91
- analytics(duration => teamActionClicked({
91
+ analytics(duration => actionClicked('team', {
92
92
  duration,
93
93
  hasHref: !!action.link,
94
94
  hasOnClick: !!action.callback,
@@ -132,7 +132,7 @@ const ExtraActions = ({
132
132
  const onMoreClick = useCallback(shouldBeOpen => {
133
133
  if (shouldBeOpen) {
134
134
  // Only fire this event when OPENING the dropdown
135
- analytics(duration => moreActionsClicked({
135
+ analytics(duration => moreActionsClicked('team', {
136
136
  duration,
137
137
  numActions: count + 2
138
138
  }));
@@ -209,7 +209,7 @@ const TeamProfilecardContent = ({
209
209
  analytics(duration => {
210
210
  var _team$members;
211
211
 
212
- return teamProfileCardRendered('content', {
212
+ return profileCardRendered('team', 'content', {
213
213
  duration,
214
214
  numActions: allActions.length,
215
215
  memberCount: (_team$members = team.members) === null || _team$members === void 0 ? void 0 : _team$members.length,
@@ -242,7 +242,7 @@ const ErrorMessage = ({
242
242
  }) => {
243
243
  const hasRetry = !!clientFetchProfile;
244
244
  useEffect(() => {
245
- analytics(duration => teamProfileCardRendered('error', {
245
+ analytics(duration => profileCardRendered('team', 'error', {
246
246
  duration,
247
247
  hasRetry
248
248
  }));
@@ -8,7 +8,7 @@ import Popup from '@atlaskit/popup';
8
8
  import { layers } from '@atlaskit/theme/constants';
9
9
  import filterActions from '../../internal/filterActions';
10
10
  import messages from '../../messages';
11
- import { fireEvent, teamCardTriggered, teamProfileCardRendered } from '../../util/analytics';
11
+ import { cardTriggered, fireEvent, profileCardRendered } from '../../util/analytics';
12
12
  import { isBasicClick } from '../../util/click';
13
13
  import { DELAY_MS_HIDE, DELAY_MS_SHOW } from '../../util/config';
14
14
  import { getPageTime } from '../../util/performance';
@@ -89,7 +89,7 @@ export class TeamProfileCardTriggerInternal extends React.PureComponent {
89
89
  this.showProfilecard(0);
90
90
 
91
91
  if (!this.state.visible) {
92
- this.fireAnalytics(teamCardTriggered('click'));
92
+ this.fireAnalytics(cardTriggered('team', 'click'));
93
93
  }
94
94
  }
95
95
  });
@@ -101,7 +101,7 @@ export class TeamProfileCardTriggerInternal extends React.PureComponent {
101
101
 
102
102
  if (!this.state.visible) {
103
103
  this.openedByHover = true;
104
- this.fireAnalytics(teamCardTriggered('hover'));
104
+ this.fireAnalytics(cardTriggered('team', 'hover'));
105
105
  }
106
106
 
107
107
  this.showProfilecard(DELAY_MS_SHOW);
@@ -197,7 +197,7 @@ export class TeamProfileCardTriggerInternal extends React.PureComponent {
197
197
  });
198
198
 
199
199
  _defineProperty(this, "onErrorBoundary", () => {
200
- this.fireAnalytics(teamProfileCardRendered('errorBoundary', {
200
+ this.fireAnalytics(profileCardRendered('team', 'errorBoundary', {
201
201
  duration: 0
202
202
  }));
203
203
  this.setState({
@@ -1,14 +1,40 @@
1
1
  import _extends from "@babel/runtime/helpers/extends";
2
- import React from 'react';
2
+ import React, { useCallback, useState } from 'react';
3
3
  import { useIntl } from 'react-intl-next';
4
4
  import Button from '@atlaskit/button/custom-theme-button';
5
5
  import DropdownMenu, { DropdownItem, DropdownItemGroup } from '@atlaskit/dropdown-menu';
6
6
  import MoreIcon from '@atlaskit/icon/glyph/more';
7
7
  import messages from '../../messages';
8
8
  import { OverflowActionButtonsWrapper } from '../../styled/Card';
9
+ import { moreActionsClicked } from '../../util/analytics';
10
+ export const ACTION_OVERFLOW_THRESHOLD = 2;
9
11
  export const OverflowProfileCardButtons = props => {
10
12
  const intl = useIntl();
11
- return /*#__PURE__*/React.createElement(OverflowActionButtonsWrapper, null, /*#__PURE__*/React.createElement(DropdownMenu, {
13
+ const [, setOpen] = useState(false);
14
+ const {
15
+ actions,
16
+ onItemClick,
17
+ fireAnalyticsWithDuration
18
+ } = props;
19
+ const numActions = actions.length + ACTION_OVERFLOW_THRESHOLD;
20
+ const onOpenChange = useCallback(({
21
+ isOpen: nextOpen
22
+ }) => {
23
+ setOpen(prevOpen => {
24
+ if (nextOpen && !prevOpen) {
25
+ fireAnalyticsWithDuration(duration => moreActionsClicked('user', {
26
+ duration,
27
+ numActions
28
+ }));
29
+ }
30
+
31
+ return nextOpen;
32
+ });
33
+ }, [numActions, fireAnalyticsWithDuration]);
34
+ return /*#__PURE__*/React.createElement(OverflowActionButtonsWrapper, {
35
+ "data-testid": "profilecard-actions-overflow"
36
+ }, /*#__PURE__*/React.createElement(DropdownMenu, {
37
+ onOpenChange: onOpenChange,
12
38
  placement: 'bottom-end',
13
39
  trigger: ({
14
40
  triggerRef,
@@ -23,10 +49,10 @@ export const OverflowProfileCardButtons = props => {
23
49
  label: intl.formatMessage(messages.profileCardMoreIconLabel)
24
50
  })
25
51
  }))
26
- }, /*#__PURE__*/React.createElement(DropdownItemGroup, null, props.actions.map(action => /*#__PURE__*/React.createElement(DropdownItem, {
52
+ }, /*#__PURE__*/React.createElement(DropdownItemGroup, null, actions.map((action, index) => /*#__PURE__*/React.createElement(DropdownItem, {
27
53
  key: action.id,
28
54
  onClick: (event, ...args) => {
29
- props.onItemClick(action, args, event);
55
+ onItemClick(action, args, event, index);
30
56
  },
31
57
  href: action.link
32
58
  }, action.label)))));