@atlaskit/reactions 21.8.1 → 22.0.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 (104) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/cjs/MockReactionsClient.js +24 -16
  3. package/dist/cjs/analytics/analytics.js +9 -1
  4. package/dist/cjs/analytics/ufo.js +24 -2
  5. package/dist/cjs/components/Counter/Counter.js +16 -20
  6. package/dist/cjs/components/Reaction/Reaction.js +52 -18
  7. package/dist/cjs/components/Reaction/styles.js +8 -1
  8. package/dist/cjs/components/ReactionDialog/ReactionView.js +121 -0
  9. package/dist/cjs/components/ReactionDialog/ReactionsDialog.js +187 -0
  10. package/dist/cjs/components/ReactionDialog/ReactionsList.js +104 -0
  11. package/dist/cjs/components/ReactionDialog/index.js +13 -0
  12. package/dist/cjs/components/ReactionDialog/styles.js +202 -0
  13. package/dist/cjs/components/ReactionPicker/ReactionPicker.js +15 -20
  14. package/dist/cjs/components/ReactionTooltip/ReactionTooltip.js +26 -12
  15. package/dist/cjs/components/ReactionTooltip/styles.js +11 -2
  16. package/dist/cjs/components/Reactions/Reactions.js +166 -21
  17. package/dist/cjs/components/Reactions/styles.js +11 -6
  18. package/dist/cjs/components/Trigger/Trigger.js +1 -2
  19. package/dist/cjs/components/index.js +9 -1
  20. package/dist/cjs/containers/ConnectedReactionPicker/ConnectedReactionPicker.js +6 -2
  21. package/dist/cjs/containers/ConnectedReactionsView/ConnectedReactionsView.js +9 -4
  22. package/dist/cjs/shared/constants.js +62 -10
  23. package/dist/cjs/shared/i18n.js +40 -0
  24. package/dist/cjs/shared/utils.js +60 -2
  25. package/dist/cjs/types/reaction.js +13 -1
  26. package/dist/cjs/version.json +1 -1
  27. package/dist/es2019/MockReactionsClient.js +22 -14
  28. package/dist/es2019/analytics/analytics.js +3 -0
  29. package/dist/es2019/analytics/ufo.js +19 -0
  30. package/dist/es2019/components/Counter/Counter.js +16 -15
  31. package/dist/es2019/components/Reaction/Reaction.js +43 -18
  32. package/dist/es2019/components/Reaction/styles.js +9 -2
  33. package/dist/es2019/components/ReactionDialog/ReactionView.js +69 -0
  34. package/dist/es2019/components/ReactionDialog/ReactionsDialog.js +145 -0
  35. package/dist/es2019/components/ReactionDialog/ReactionsList.js +69 -0
  36. package/dist/es2019/components/ReactionDialog/index.js +1 -0
  37. package/dist/es2019/components/ReactionDialog/styles.js +169 -0
  38. package/dist/es2019/components/ReactionPicker/ReactionPicker.js +12 -20
  39. package/dist/es2019/components/ReactionTooltip/ReactionTooltip.js +22 -12
  40. package/dist/es2019/components/ReactionTooltip/styles.js +9 -1
  41. package/dist/es2019/components/Reactions/Reactions.js +146 -22
  42. package/dist/es2019/components/Reactions/styles.js +9 -5
  43. package/dist/es2019/components/Trigger/Trigger.js +1 -2
  44. package/dist/es2019/components/index.js +2 -1
  45. package/dist/es2019/containers/ConnectedReactionPicker/ConnectedReactionPicker.js +8 -2
  46. package/dist/es2019/containers/ConnectedReactionsView/ConnectedReactionsView.js +5 -4
  47. package/dist/es2019/shared/constants.js +55 -6
  48. package/dist/es2019/shared/i18n.js +43 -0
  49. package/dist/es2019/shared/utils.js +51 -0
  50. package/dist/es2019/types/reaction.js +13 -1
  51. package/dist/es2019/version.json +1 -1
  52. package/dist/esm/MockReactionsClient.js +24 -13
  53. package/dist/esm/analytics/analytics.js +5 -0
  54. package/dist/esm/analytics/ufo.js +19 -0
  55. package/dist/esm/components/Counter/Counter.js +17 -17
  56. package/dist/esm/components/Reaction/Reaction.js +51 -19
  57. package/dist/esm/components/Reaction/styles.js +9 -2
  58. package/dist/esm/components/ReactionDialog/ReactionView.js +98 -0
  59. package/dist/esm/components/ReactionDialog/ReactionsDialog.js +161 -0
  60. package/dist/esm/components/ReactionDialog/ReactionsList.js +79 -0
  61. package/dist/esm/components/ReactionDialog/index.js +1 -0
  62. package/dist/esm/components/ReactionDialog/styles.js +177 -0
  63. package/dist/esm/components/ReactionPicker/ReactionPicker.js +15 -20
  64. package/dist/esm/components/ReactionTooltip/ReactionTooltip.js +26 -12
  65. package/dist/esm/components/ReactionTooltip/styles.js +9 -1
  66. package/dist/esm/components/Reactions/Reactions.js +158 -22
  67. package/dist/esm/components/Reactions/styles.js +9 -5
  68. package/dist/esm/components/Trigger/Trigger.js +1 -2
  69. package/dist/esm/components/index.js +2 -1
  70. package/dist/esm/containers/ConnectedReactionPicker/ConnectedReactionPicker.js +6 -2
  71. package/dist/esm/containers/ConnectedReactionsView/ConnectedReactionsView.js +8 -4
  72. package/dist/esm/shared/constants.js +57 -6
  73. package/dist/esm/shared/i18n.js +40 -0
  74. package/dist/esm/shared/utils.js +53 -0
  75. package/dist/esm/types/reaction.js +13 -1
  76. package/dist/esm/version.json +1 -1
  77. package/dist/types/MockReactionsClient.d.ts +7 -3
  78. package/dist/types/analytics/analytics.d.ts +10 -0
  79. package/dist/types/analytics/ufo.d.ts +18 -2
  80. package/dist/types/components/Counter/Counter.d.ts +0 -1
  81. package/dist/types/components/Reaction/Reaction.d.ts +11 -1
  82. package/dist/types/components/ReactionDialog/ReactionView.d.ts +19 -0
  83. package/dist/types/components/ReactionDialog/ReactionsDialog.d.ts +32 -0
  84. package/dist/types/components/ReactionDialog/ReactionsList.d.ts +23 -0
  85. package/dist/types/components/ReactionDialog/index.d.ts +1 -0
  86. package/dist/types/components/ReactionDialog/styles.d.ts +11 -0
  87. package/dist/types/components/ReactionPicker/ReactionPicker.d.ts +5 -0
  88. package/dist/types/components/ReactionTooltip/ReactionTooltip.d.ts +12 -0
  89. package/dist/types/components/ReactionTooltip/styles.d.ts +1 -0
  90. package/dist/types/components/Reactions/Reactions.d.ts +45 -6
  91. package/dist/types/components/Reactions/styles.d.ts +1 -0
  92. package/dist/types/components/index.d.ts +1 -0
  93. package/dist/types/containers/ConnectedReactionPicker/ConnectedReactionPicker.d.ts +0 -4
  94. package/dist/types/containers/ConnectedReactionsView/ConnectedReactionsView.d.ts +2 -11
  95. package/dist/types/index.d.ts +1 -1
  96. package/dist/types/shared/constants.d.ts +11 -5
  97. package/dist/types/shared/i18n.d.ts +40 -0
  98. package/dist/types/shared/utils.d.ts +7 -0
  99. package/dist/types/types/User.d.ts +10 -0
  100. package/dist/types/types/index.d.ts +1 -1
  101. package/dist/types/types/reaction.d.ts +15 -2
  102. package/docs/0-intro.tsx +3 -0
  103. package/docs/5-graphql-support.tsx +153 -0
  104. package/package.json +8 -6
@@ -0,0 +1,169 @@
1
+ /** @jsx jsx */
2
+ import { css } from '@emotion/react';
3
+ import { B400, N500, N800 } from '@atlaskit/theme/colors';
4
+ import { token } from '@atlaskit/tokens';
5
+ import { constants } from '../../shared';
6
+ const REACTIONS_CONTAINER_WIDTH = 48;
7
+ const REACTION_RIGHT_MARGIN = 8;
8
+ /* we want to display around 9 reactions and show 10th one as faded so removing 2px from REACTIONS_CONTAINER_WIDTH*/
9
+
10
+ const CONTAINER_WIDTH = constants.NUMBER_OF_REACTIONS_TO_DISPLAY * (REACTIONS_CONTAINER_WIDTH + REACTION_RIGHT_MARGIN) + REACTIONS_CONTAINER_WIDTH - 2;
11
+ const REACTION_CONTAINER_HEIGHT = 48;
12
+ /*Reactions Container. Using pseudo Element :after to set border since with onClick of Reaction to higlight the
13
+ border blue below the reaction. Setting Border Width based on number of reactions to make sure it shows up
14
+ in case the container overflows */
15
+
16
+ export const containerStyle = reactionsBorderWidth => css({
17
+ overflow: 'hidden',
18
+ maxWidth: `${CONTAINER_WIDTH}px`,
19
+ height: '100%',
20
+ display: 'flex',
21
+ justifyContent: 'start',
22
+ position: 'relative',
23
+ scrollBehavior: 'smooth',
24
+ '&:after': {
25
+ content: '""',
26
+ zIndex: 0,
27
+ display: 'block',
28
+ minWidth: `${reactionsBorderWidth}%`,
29
+ bottom: '0px',
30
+ position: 'absolute'
31
+ }
32
+ });
33
+ export const titleStyle = css({
34
+ '& > h1': {
35
+ fontSize: '24px!important',
36
+ color: `${token('color.text', N800)}`
37
+ }
38
+ });
39
+ const containerEdgeAngle = {
40
+ rightEdge: 270,
41
+ leftEdge: 90
42
+ };
43
+ export const counterStyle = isSelected => css({
44
+ display: 'flex',
45
+ alignSelf: 'center',
46
+ lineHeight: '14px',
47
+ fontSize: '11px',
48
+ fontWeight: isSelected ? 700 : 400,
49
+ paddingRight: '0px',
50
+ marginTop: '5px',
51
+ '> div': {
52
+ width: '100%',
53
+ padding: '0px!important',
54
+ //Counter component has its own styles overriding them to match designs
55
+ color: isSelected ? `${token('color.text', B400)}!important` : `2px solid ${token('color.text', N500)}!important`
56
+ }
57
+ });
58
+
59
+ const fadedCss = (edge, theme) => css({
60
+ content: '""',
61
+ position: 'absolute',
62
+ left: '0px',
63
+ top: '0px',
64
+ width: `${REACTIONS_CONTAINER_WIDTH}px`,
65
+ height: `${REACTION_CONTAINER_HEIGHT}px`,
66
+ zIndex: 0,
67
+ background: theme === 'dark' || theme === 'legacy-dark' ? `linear-gradient(${containerEdgeAngle[edge]}deg, rgba(34, 39, 43, 0.95) 40.23%, rgba(34, 39, 43, 0.55) 58.33%, rgba(34, 39, 43, 0) 77.49%)` : `linear-gradient(${containerEdgeAngle[edge]}deg, rgba(255, 255, 255, 0.95) 40.23%, rgba(255, 255, 255, 0.55) 58.33%, rgba(255, 255, 255, 0) 77.49%)`
68
+ });
69
+
70
+ export const customTabWrapper = (isSelected, selectedEmojiId, theme) => css({
71
+ flexShrink: 0,
72
+ display: 'flex',
73
+ flexDirection: 'column',
74
+ textAlign: 'center',
75
+ alignItems: 'center',
76
+ justifyContent: 'center',
77
+ minWidth: `${REACTIONS_CONTAINER_WIDTH}px`,
78
+ minHeight: `${REACTION_CONTAINER_HEIGHT}px`,
79
+ marginRight: `${REACTION_RIGHT_MARGIN}px`,
80
+ boxSizing: 'border-box',
81
+ position: 'relative',
82
+ '> div': {
83
+ minWidth: `${REACTIONS_CONTAINER_WIDTH}px`,
84
+ minHeight: `${REACTION_CONTAINER_HEIGHT}px`,
85
+ padding: '0px !important',
86
+ alignItems: 'center',
87
+ justifyContent: 'center'
88
+ },
89
+ '& > span': {
90
+ minHeight: '16px',
91
+ minWidth: '16px'
92
+ },
93
+ '&.disabled:after': fadedCss('rightEdge', theme),
94
+ '&.disabled + &.disabled:after': fadedCss('leftEdge', theme),
95
+ '&:after': isSelected ? {
96
+ content: '""',
97
+ borderBottom: `2px solid ${token('color.text', B400)}`,
98
+ width: `${REACTIONS_CONTAINER_WIDTH}px`,
99
+ bottom: '0px',
100
+ display: 'block',
101
+ position: 'absolute',
102
+ zIndex: 2
103
+ } : {
104
+ content: '""',
105
+ borderBottom: `2px solid transparent`,
106
+ width: `${REACTIONS_CONTAINER_WIDTH}px`,
107
+ bottom: '0px',
108
+ display: 'block',
109
+ position: 'absolute',
110
+ zIndex: 1
111
+ }
112
+ });
113
+ export const navigationContainerStyle = css({
114
+ '> button': {
115
+ cursor: 'pointer'
116
+ },
117
+ 'button:last-child': {
118
+ marginLeft: '16px'
119
+ }
120
+ });
121
+ export const reactionViewStyle = css({
122
+ marginTop: '24px',
123
+ display: 'flex',
124
+ flexDirection: 'column',
125
+ p: {
126
+ margin: 0,
127
+ color: `${token('color.text', N800)}`,
128
+ textTransform: 'capitalize',
129
+ fontWeight: 600,
130
+ fontSize: 16,
131
+ lineHeight: '20px',
132
+ '> span': {
133
+ marginRight: 8
134
+ }
135
+ }
136
+ });
137
+ export const userListStyle = css({
138
+ listStyle: 'none',
139
+ marginTop: 16,
140
+ padding: 0,
141
+ textAlign: 'left',
142
+ li: {
143
+ color: `${token('color.text', N500)}`,
144
+ fontSize: 14
145
+ }
146
+ });
147
+ export const userStyle = css({
148
+ display: 'flex',
149
+ alignItems: 'center',
150
+ padding: '8px 0px 8px 0px',
151
+ '> span': {
152
+ marginLeft: 16
153
+ }
154
+ });
155
+ export const customTabListStyles = css({
156
+ overflow: 'auto',
157
+ scrollBehavior: 'smooth',
158
+ display: 'flex',
159
+ paddingBottom: 4,
160
+ 'div[role=tablist]': {
161
+ flexGrow: 1
162
+ }
163
+ });
164
+ export const centerSpinner = css({
165
+ display: 'flex',
166
+ justifyContent: 'center',
167
+ alignItems: 'center',
168
+ height: '100%'
169
+ });
@@ -61,10 +61,11 @@ export const ReactionPicker = /*#__PURE__*/React.memo(props => {
61
61
  allowAllEmojis,
62
62
  disabled,
63
63
  pickerQuickReactionEmojiIds,
64
- onShowMore,
65
- onOpen,
66
- onCancel,
67
- tooltipContent = jsx(FormattedMessage, i18n.messages.addReaction)
64
+ onShowMore = () => {},
65
+ onOpen = () => {},
66
+ onCancel = () => {},
67
+ tooltipContent = jsx(FormattedMessage, i18n.messages.addReaction),
68
+ emojiPickerSize
68
69
  } = props;
69
70
  /**
70
71
  * Container <div /> reference (used by custom hook to detect click outside)
@@ -92,10 +93,7 @@ export const ReactionPicker = /*#__PURE__*/React.memo(props => {
92
93
  */
93
94
 
94
95
  useClickAway(wrapperRef, () => {
95
- if (onCancel) {
96
- onCancel();
97
- }
98
-
96
+ onCancel();
99
97
  close();
100
98
  }, 'click', true);
101
99
  /**
@@ -128,10 +126,7 @@ export const ReactionPicker = /*#__PURE__*/React.memo(props => {
128
126
  isOpen: true,
129
127
  showFullPicker: true
130
128
  });
131
-
132
- if (onShowMore) {
133
- onShowMore();
134
- }
129
+ onShowMore();
135
130
  }, [onShowMore]);
136
131
  /**
137
132
  * Event callback when an emoji icon is selected
@@ -158,11 +153,7 @@ export const ReactionPicker = /*#__PURE__*/React.memo(props => {
158
153
  isOpen: !settings.isOpen,
159
154
  showFullPicker: !!allowAllEmojis && Array.isArray(pickerQuickReactionEmojiIds) && pickerQuickReactionEmojiIds.length === 0
160
155
  });
161
-
162
- if (onOpen) {
163
- onOpen();
164
- } // ufo reactions picker opened success
165
-
156
+ onOpen(); // ufo reactions picker opened success
166
157
 
167
158
  UFO.PickerRender.success();
168
159
  };
@@ -182,7 +173,6 @@ export const ReactionPicker = /*#__PURE__*/React.memo(props => {
182
173
  return jsx("div", {
183
174
  className: wrapperClassName,
184
175
  css: styles.pickerStyle,
185
- ref: wrapperRef,
186
176
  "data-testid": RENDER_REACTIONPICKER_TESTID
187
177
  }, jsx(Manager, null, jsx(Reference, null, ({
188
178
  ref: popperRef
@@ -214,10 +204,12 @@ export const ReactionPicker = /*#__PURE__*/React.memo(props => {
214
204
  },
215
205
  ref: ref
216
206
  }, jsx("div", {
217
- css: styles.popupStyle
207
+ css: styles.popupStyle,
208
+ ref: wrapperRef
218
209
  }, settings.showFullPicker ? jsx(EmojiPicker, {
219
210
  emojiProvider: emojiProvider,
220
- onSelection: onEmojiSelected
211
+ onSelection: onEmojiSelected,
212
+ size: emojiPickerSize
221
213
  }) : jsx("div", {
222
214
  css: styles.contentStyle
223
215
  }, jsx(Selector, {
@@ -13,31 +13,41 @@ import * as styles from './styles';
13
13
 
14
14
  export const RENDER_REACTIONTOOLTIP_TESTID = 'render-reactionTooltip';
15
15
  export const ReactionTooltip = ({
16
- emojiName,
17
16
  children,
18
- maxReactions = constants.TOOLTIP_USERS_LIMIT,
17
+ emojiName,
19
18
  reaction: {
20
- users = []
21
- }
19
+ users = [],
20
+ emojiId = ''
21
+ },
22
+ maxReactions = constants.TOOLTIP_USERS_LIMIT,
23
+ handleUserListClick,
24
+ allowUserDialog = false,
25
+ isEnabled = true
22
26
  }) => {
23
27
  /**
24
28
  * Render list of users in the tooltip box
25
29
  */
26
- const content = !users || users.length === 0 ? null : jsx("div", {
27
- css: styles.tooltipStyle
30
+ const content = !users || users.length === 0 || !isEnabled ? null : jsx("div", {
31
+ css: styles.tooltipStyle,
32
+ tabIndex: 0
28
33
  }, jsx("ul", null, emojiName ? jsx("li", {
29
34
  css: styles.emojiNameStyle
30
- }, emojiName) : null, users.slice(0, maxReactions).map((user, index) => {
35
+ }, emojiName) : null, users.slice(0, maxReactions).map(user => {
31
36
  return jsx("li", {
32
- key: index
37
+ key: user.id
33
38
  }, user.displayName);
34
- }), users.length > maxReactions ? jsx("li", {
35
- css: styles.footerStyle
36
- }, jsx(FormattedMessage, _extends({}, i18n.messages.otherUsers, {
39
+ }), jsx("li", {
40
+ css: allowUserDialog ? [styles.footerStyle, styles.underlineStyle] : styles.footerStyle,
41
+ onClick: () => {
42
+ if (allowUserDialog && handleUserListClick) {
43
+ handleUserListClick(emojiId);
44
+ }
45
+ }
46
+ }, users.length > maxReactions ? jsx(FormattedMessage, _extends({}, i18n.messages.otherUsers, {
37
47
  values: {
38
48
  count: users.length - maxReactions
39
49
  }
40
- }))) : null));
50
+ })) : allowUserDialog && jsx(FormattedMessage, i18n.messages.moreInfo))));
41
51
  return jsx(Tooltip, {
42
52
  content: content,
43
53
  position: "bottom",
@@ -1,7 +1,7 @@
1
1
  /** @jsx jsx */
2
2
  import { css } from '@emotion/react';
3
3
  import { token } from '@atlaskit/tokens';
4
- import { N90 } from '@atlaskit/theme/colors';
4
+ import { N90, N800, N0 } from '@atlaskit/theme/colors';
5
5
  export const verticalMargin = 5;
6
6
  export const tooltipStyle = css({
7
7
  maxWidth: '150px',
@@ -29,4 +29,12 @@ export const emojiNameStyle = css({
29
29
  export const footerStyle = css({
30
30
  color: token('color.text.inverse', N90),
31
31
  fontWeight: 300
32
+ });
33
+ export const underlineStyle = css({
34
+ cursor: 'pointer',
35
+ textDecoration: 'underline',
36
+ ':hover': {
37
+ backgroundColor: token('color.background.neutral.bold', N800),
38
+ color: token('color.text.inverse', N0)
39
+ }
32
40
  });
@@ -1,19 +1,48 @@
1
1
  /** @jsx jsx */
2
- import React, { useCallback, useEffect, useMemo, useRef } from 'react';
2
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
3
  import { jsx } from '@emotion/react';
4
- import { useAnalyticsEvents } from '@atlaskit/analytics-next';
5
4
  import { FormattedMessage } from 'react-intl-next';
6
- import { Analytics } from '../../analytics';
5
+ import { useAnalyticsEvents } from '@atlaskit/analytics-next';
6
+ import { ModalTransition } from '@atlaskit/modal-dialog';
7
+ import Button from '@atlaskit/button';
8
+ import Tooltip from '@atlaskit/tooltip';
9
+ import { Analytics, UFO } from '../../analytics';
7
10
  import { ReactionStatus } from '../../types';
8
11
  import { i18n } from '../../shared';
9
12
  import { Reaction } from '../Reaction';
13
+ import { ReactionsDialog } from '../ReactionDialog';
10
14
  import { ReactionPicker } from '../ReactionPicker';
11
15
  import * as styles from './styles';
16
+ /**
17
+ * Set of all available UFO experiences relating to reactions dialog
18
+ */
19
+
20
+ export const ufoExperiences = {
21
+ /**
22
+ * Expeirence when a reaction dialog is opened
23
+ */
24
+ openDialog: UFO.ReactionDialogOpened,
25
+
26
+ /**
27
+ * Experience when a reaction dialog is closed
28
+ */
29
+ closeDialog: UFO.ReactionDialogClosed,
30
+
31
+ /**
32
+ * Experience when a reaction changed/fetched from inside the modal dialog
33
+ */
34
+ selectedReactionChangeInsideDialog: UFO.ReactionDialogSelectedReactionChanged
35
+ };
12
36
  /**
13
37
  * Test id for wrapper Reactions div
14
38
  */
15
39
 
16
40
  export const RENDER_REACTIONS_TESTID = 'render-reactions';
41
+ /**
42
+ * Test id for the view all reacted user to trigger the dialog
43
+ */
44
+
45
+ export const RENDER_VIEWALL_REACTED_USERS_DIALOG = 'viewall-reacted-users-dialog';
17
46
 
18
47
  /**
19
48
  * Get content of the tooltip
@@ -46,41 +75,48 @@ export const Reactions = /*#__PURE__*/React.memo(({
46
75
  loadReaction,
47
76
  quickReactionEmojis,
48
77
  pickerQuickReactionEmojiIds,
49
- onReactionHover,
78
+ getReactionDetails = () => {},
79
+ onReactionHover = () => {},
50
80
  onSelection,
51
81
  reactions = [],
52
82
  emojiProvider,
53
83
  allowAllEmojis,
54
- onReactionClick
84
+ onReactionClick,
85
+ allowUserDialog,
86
+ onDialogOpenCallback = () => {},
87
+ onDialogCloseCallback = () => {},
88
+ onDialogSelectReactionCallback = () => {},
89
+ emojiPickerSize = 'medium'
55
90
  }) => {
91
+ const [selectedEmojiId, setSelectedEmojiId] = useState();
56
92
  const {
57
93
  createAnalyticsEvent
58
94
  } = useAnalyticsEvents();
59
95
  let openTime = useRef();
60
96
  let renderTime = useRef();
61
-
62
- if (status !== ReactionStatus.ready) {
63
- renderTime.current = Date.now();
64
- }
65
-
66
97
  useEffect(() => {
67
98
  if (status === ReactionStatus.notLoaded) {
68
99
  loadReaction();
69
100
  }
70
101
  }, [status, loadReaction]);
71
102
  useEffect(() => {
72
- if (status === ReactionStatus.ready && renderTime.current) {
73
- Analytics.createAndFireSafe(createAnalyticsEvent, Analytics.createReactionsRenderedEvent, renderTime.current);
103
+ if (status !== ReactionStatus.ready) {
104
+ renderTime.current = Date.now();
105
+ } else {
106
+ var _renderTime$current;
107
+
108
+ Analytics.createAndFireSafe(createAnalyticsEvent, Analytics.createReactionsRenderedEvent, (_renderTime$current = renderTime.current) !== null && _renderTime$current !== void 0 ? _renderTime$current : Date.now() //renderTime.current can be null during unit test cases
109
+ );
74
110
  renderTime.current = undefined;
75
111
  }
76
112
  }, [createAnalyticsEvent, status]);
77
-
78
- const handleReactionMouseEnter = summary => {
79
- if (onReactionHover) {
80
- onReactionHover(summary.emojiId);
81
- }
82
- };
83
-
113
+ const handleReactionMouseEnter = useCallback(emojiId => {
114
+ getReactionDetails(emojiId);
115
+ onReactionHover(emojiId);
116
+ }, [getReactionDetails, onReactionHover]);
117
+ const handleReactionFocused = useCallback(emojiId => {
118
+ getReactionDetails(emojiId);
119
+ }, [getReactionDetails]);
84
120
  const handlePickerOpen = useCallback(() => {
85
121
  openTime.current = Date.now();
86
122
  Analytics.createAndFireSafe(createAnalyticsEvent, Analytics.createPickerButtonClickedEvent, reactions.length);
@@ -97,10 +133,80 @@ export const Reactions = /*#__PURE__*/React.memo(({
97
133
  openTime.current = undefined;
98
134
  onSelection(emojiId);
99
135
  }, [createAnalyticsEvent, onSelection, reactions]);
136
+ /**
137
+ * event handler to open selected reaction from tooltip
138
+ * @param emojiId selected emoji id
139
+ */
140
+
141
+ const handleOpenReactionsDialog = emojiId => {
142
+ // ufo start opening reaction dialog
143
+ ufoExperiences.openDialog.start();
144
+ setSelectedEmojiId(emojiId);
145
+ onDialogOpenCallback(emojiId, 'tooltip'); // ufo opening reaction dialog success
146
+
147
+ ufoExperiences.openDialog.success({
148
+ metadata: {
149
+ emojiId: emojiId,
150
+ source: 'Reactions',
151
+ reason: 'Opening dialog from emoji tooltip link successfully'
152
+ }
153
+ });
154
+ };
155
+ /**
156
+ * Event handler to oepn all reactions link button
157
+ */
158
+
159
+
160
+ const handleOpenAllReactionsDialog = () => {
161
+ // ufo start opening reaction dialog
162
+ ufoExperiences.openDialog.start();
163
+ const emojiId = reactions[0].emojiId;
164
+ getReactionDetails(emojiId);
165
+ setSelectedEmojiId(emojiId);
166
+ onDialogOpenCallback(emojiId, 'button'); // ufo opening reaction dialog success
167
+
168
+ ufoExperiences.openDialog.success({
169
+ metadata: {
170
+ emojiId: emojiId,
171
+ source: 'Reactions',
172
+ reason: 'Opening all reactions dialog link successfully'
173
+ }
174
+ });
175
+ };
176
+
177
+ const handleCloseReactionsDialog = (e, analyticsEvent) => {
178
+ // ufo closing opening reaction dialog
179
+ ufoExperiences.closeDialog.start();
180
+ setSelectedEmojiId('');
181
+ onDialogCloseCallback(e, analyticsEvent); // ufo closing reaction dialog success
182
+
183
+ ufoExperiences.closeDialog.success({
184
+ metadata: {
185
+ source: 'Reactions',
186
+ reason: 'Closing reactions dialog successfully'
187
+ }
188
+ });
189
+ };
190
+
191
+ const handleSelectReactionInDialog = (emojiId, analyticsEvent) => {
192
+ // ufo selected reaction inside the modal dialog
193
+ ufoExperiences.selectedReactionChangeInsideDialog.start();
194
+ handleReactionMouseEnter(emojiId);
195
+ onDialogSelectReactionCallback(emojiId, analyticsEvent); // ufo selected reaction inside the modal dialog success
196
+
197
+ ufoExperiences.selectedReactionChangeInsideDialog.success({
198
+ metadata: {
199
+ emojiId: emojiId,
200
+ source: 'Reactions',
201
+ reason: 'Selected Emoji changed'
202
+ }
203
+ });
204
+ };
100
205
  /**
101
206
  * Get the reactions that we want to render are any reactions with a count greater than zero as well as any default emoji not already shown
102
207
  */
103
208
 
209
+
104
210
  const memorizedReactions = useMemo(() => {
105
211
  //
106
212
 
@@ -138,7 +244,10 @@ export const Reactions = /*#__PURE__*/React.memo(({
138
244
  emojiProvider: emojiProvider,
139
245
  onClick: onReactionClick,
140
246
  onMouseEnter: handleReactionMouseEnter,
141
- flash: flash[reaction.emojiId]
247
+ onFocused: handleReactionFocused,
248
+ flash: flash[reaction.emojiId],
249
+ handleUserListClick: handleOpenReactionsDialog,
250
+ allowUserDialog: allowUserDialog
142
251
  })), jsx(ReactionPicker, {
143
252
  css: styles.reactionStyle,
144
253
  emojiProvider: emojiProvider,
@@ -150,6 +259,21 @@ export const Reactions = /*#__PURE__*/React.memo(({
150
259
  onOpen: handlePickerOpen,
151
260
  onCancel: handleOnCancel,
152
261
  onShowMore: handleOnMore,
153
- tooltipContent: getTooltip(status, errorMessage)
154
- }));
262
+ tooltipContent: getTooltip(status, errorMessage),
263
+ emojiPickerSize: emojiPickerSize
264
+ }), allowUserDialog && reactions.length > 0 && jsx(Tooltip, {
265
+ content: jsx(FormattedMessage, i18n.messages.seeWhoReactedTooltip),
266
+ hideTooltipOnClick: true
267
+ }, jsx(Button, {
268
+ appearance: "subtle-link",
269
+ onClick: handleOpenAllReactionsDialog,
270
+ css: styles.seeWhoReacted,
271
+ testId: RENDER_VIEWALL_REACTED_USERS_DIALOG
272
+ }, jsx(FormattedMessage, i18n.messages.seeWhoReacted))), jsx(ModalTransition, null, !!selectedEmojiId && jsx(ReactionsDialog, {
273
+ selectedEmojiId: selectedEmojiId,
274
+ reactions: memorizedReactions,
275
+ emojiProvider: emojiProvider,
276
+ handleCloseReactionsDialog: handleCloseReactionsDialog,
277
+ handleSelectReaction: handleSelectReactionInDialog
278
+ })));
155
279
  });
@@ -2,8 +2,14 @@
2
2
  import { css } from '@emotion/react';
3
3
  export const reactionStyle = css({
4
4
  display: 'inline-block',
5
- // top margin of 2px to allow spacing between rows when wrapped (paired with top margin in reactionsStyle)
6
- margin: '2px 4px 0 4px'
5
+ margin: '4px'
6
+ });
7
+ export const seeWhoReacted = css({
8
+ height: '24px',
9
+ lineHeight: '24px',
10
+ paddingLeft: '4px',
11
+ paddingRight: '4px',
12
+ margin: '4px'
7
13
  });
8
14
  export const wrapperStyle = css({
9
15
  display: 'flex',
@@ -11,9 +17,7 @@ export const wrapperStyle = css({
11
17
  position: 'relative',
12
18
  alignItems: 'center',
13
19
  borderRadius: '15px',
14
- // To allow to row spacing of 2px on wrap, and 0px on first row
15
- marginTop: '-2px',
16
- '> :first-of-type > :first-child': {
20
+ '> :first-of-type > :first-of-type': {
17
21
  marginLeft: 0
18
22
  }
19
23
  });
@@ -33,8 +33,7 @@ export const Trigger = /*#__PURE__*/React.forwardRef((props, ref) => {
33
33
 
34
34
  return jsx(Tooltip, {
35
35
  testId: RENDER_TOOLTIP_TRIGGER_TESTID,
36
- content: tooltipContent,
37
- position: "top"
36
+ content: tooltipContent
38
37
  }, jsx(Button, _extends({
39
38
  css: styles.triggerStyle({
40
39
  miniMode,
@@ -1,4 +1,5 @@
1
1
  export { Reaction } from './Reaction';
2
2
  export { ReactionPicker } from './ReactionPicker';
3
3
  export { Reactions } from './Reactions';
4
- export { UfoErrorBoundary } from './UfoErrorBoundary';
4
+ export { UfoErrorBoundary } from './UfoErrorBoundary';
5
+ export { ReactionsDialog } from './ReactionDialog/ReactionsDialog';
@@ -7,15 +7,21 @@ import { UFO } from '../../analytics';
7
7
  * Reaction Picker component
8
8
  */
9
9
  export const ConnectedReactionPicker = props => {
10
+ const {
11
+ store,
12
+ containerAri,
13
+ ari
14
+ } = props;
10
15
  /**
11
16
  * callback event when an emoji item is selected
12
17
  * @param emojiId unique id for the reaction emoji
13
18
  */
19
+
14
20
  const onSelection = emojiId => {
15
21
  (async () => {
16
- const _store = await Promise.resolve(props.store);
22
+ const _store = await Promise.resolve(store);
17
23
 
18
- _store.addReaction(props.containerAri, props.ari, emojiId);
24
+ _store.addReaction(containerAri, ari, emojiId);
19
25
  })();
20
26
  };
21
27
 
@@ -52,7 +52,7 @@ export const mapDispatchToPropsHelper = (actions, containerAri, ari) => {
52
52
  onReactionClick: emojiId => {
53
53
  actions.toggleReaction(containerAri, ari, emojiId);
54
54
  },
55
- onReactionHover: emojiId => {
55
+ getReactionDetails: emojiId => {
56
56
  actions.getDetailedReaction(containerAri, ari, emojiId);
57
57
  },
58
58
  onSelection: emojiId => {
@@ -64,7 +64,8 @@ export const ConnectedReactionsView = props => {
64
64
  const {
65
65
  ari,
66
66
  containerAri,
67
- store
67
+ store,
68
+ ...rest
68
69
  } = props;
69
70
  /**
70
71
  * Reference to the <Reactions /> component instance mandatory props
@@ -159,6 +160,6 @@ export const ConnectedReactionsView = props => {
159
160
  }, /*#__PURE__*/React.createElement(UfoErrorBoundary, {
160
161
  experiences: experienceInstance.current ? [experienceInstance.current] : []
161
162
  }, stateData && dispatchData ? /*#__PURE__*/React.createElement(Reactions, _extends({
162
- key: `${props.containerAri}|${props.ari}`
163
- }, props, dispatchData, stateData)) : null));
163
+ key: `${containerAri}|${ari}`
164
+ }, rest, dispatchData, stateData)) : null));
164
165
  };