@atlaskit/emoji 64.6.0 → 64.7.1

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 (93) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/cjs/api/EmojiRepository.js +4 -0
  3. package/dist/cjs/api/EmojiResource.js +13 -1
  4. package/dist/cjs/api/EmojiUtils.js +4 -0
  5. package/dist/cjs/api/internal/UsageFrequencyTracker.js +4 -1
  6. package/dist/cjs/components/common/CachingEmoji.js +33 -12
  7. package/dist/cjs/components/common/DeleteButton.js +2 -1
  8. package/dist/cjs/components/common/Emoji.js +98 -21
  9. package/dist/cjs/components/common/EmojiPlaceholder.js +1 -0
  10. package/dist/cjs/components/common/Popup.js +21 -1
  11. package/dist/cjs/components/common/RecordSelectionDefault.js +13 -1
  12. package/dist/cjs/components/common/ResourcedEmoji.js +13 -3
  13. package/dist/cjs/components/common/ResourcedEmojiComponent.js +32 -3
  14. package/dist/cjs/components/common/UfoErrorBoundary.js +30 -4
  15. package/dist/cjs/components/common/UploadEmoji.js +17 -1
  16. package/dist/cjs/components/picker/EmojiPickerComponent.js +16 -4
  17. package/dist/cjs/components/picker/EmojiPickerListSearch.js +5 -0
  18. package/dist/cjs/components/picker/styles.js +2 -0
  19. package/dist/cjs/components/typeahead/EmojiTypeAheadComponent.js +18 -2
  20. package/dist/cjs/components/uploader/EmojiUploadComponent.js +6 -1
  21. package/dist/cjs/types.js +27 -2
  22. package/dist/cjs/util/analytics/ufoExperiences.js +46 -3
  23. package/dist/cjs/util/analytics/useSampledUFOComponentExperience.js +2 -1
  24. package/dist/cjs/util/browser-support.js +8 -0
  25. package/dist/cjs/util/constants.js +4 -5
  26. package/dist/cjs/util/storage-available.js +4 -0
  27. package/dist/cjs/util/useInView.js +23 -0
  28. package/dist/cjs/version.json +1 -1
  29. package/dist/es2019/api/EmojiRepository.js +4 -0
  30. package/dist/es2019/api/EmojiResource.js +13 -1
  31. package/dist/es2019/api/EmojiUtils.js +4 -0
  32. package/dist/es2019/api/internal/UsageFrequencyTracker.js +4 -1
  33. package/dist/es2019/components/common/CachingEmoji.js +28 -9
  34. package/dist/es2019/components/common/DeleteButton.js +2 -1
  35. package/dist/es2019/components/common/Emoji.js +76 -24
  36. package/dist/es2019/components/common/EmojiPlaceholder.js +1 -0
  37. package/dist/es2019/components/common/Popup.js +21 -1
  38. package/dist/es2019/components/common/RecordSelectionDefault.js +13 -1
  39. package/dist/es2019/components/common/ResourcedEmoji.js +14 -4
  40. package/dist/es2019/components/common/ResourcedEmojiComponent.js +28 -3
  41. package/dist/es2019/components/common/UfoErrorBoundary.js +14 -2
  42. package/dist/es2019/components/common/UploadEmoji.js +10 -1
  43. package/dist/es2019/components/picker/EmojiPickerComponent.js +16 -3
  44. package/dist/es2019/components/picker/EmojiPickerListSearch.js +5 -0
  45. package/dist/es2019/components/picker/styles.js +3 -0
  46. package/dist/es2019/components/typeahead/EmojiTypeAheadComponent.js +18 -2
  47. package/dist/es2019/components/uploader/EmojiUploadComponent.js +6 -1
  48. package/dist/es2019/types.js +24 -1
  49. package/dist/es2019/util/analytics/ufoExperiences.js +37 -2
  50. package/dist/es2019/util/analytics/useSampledUFOComponentExperience.js +2 -1
  51. package/dist/es2019/util/browser-support.js +1 -0
  52. package/dist/es2019/util/constants.js +2 -2
  53. package/dist/es2019/util/storage-available.js +4 -0
  54. package/dist/es2019/util/useInView.js +12 -0
  55. package/dist/es2019/version.json +1 -1
  56. package/dist/esm/api/EmojiRepository.js +4 -0
  57. package/dist/esm/api/EmojiResource.js +13 -1
  58. package/dist/esm/api/EmojiUtils.js +4 -0
  59. package/dist/esm/api/internal/UsageFrequencyTracker.js +4 -1
  60. package/dist/esm/components/common/CachingEmoji.js +32 -13
  61. package/dist/esm/components/common/DeleteButton.js +2 -1
  62. package/dist/esm/components/common/Emoji.js +84 -24
  63. package/dist/esm/components/common/EmojiPlaceholder.js +1 -0
  64. package/dist/esm/components/common/Popup.js +21 -1
  65. package/dist/esm/components/common/RecordSelectionDefault.js +13 -1
  66. package/dist/esm/components/common/ResourcedEmoji.js +14 -4
  67. package/dist/esm/components/common/ResourcedEmojiComponent.js +30 -3
  68. package/dist/esm/components/common/UfoErrorBoundary.js +30 -4
  69. package/dist/esm/components/common/UploadEmoji.js +15 -1
  70. package/dist/esm/components/picker/EmojiPickerComponent.js +16 -3
  71. package/dist/esm/components/picker/EmojiPickerListSearch.js +5 -0
  72. package/dist/esm/components/picker/styles.js +2 -0
  73. package/dist/esm/components/typeahead/EmojiTypeAheadComponent.js +18 -2
  74. package/dist/esm/components/uploader/EmojiUploadComponent.js +6 -1
  75. package/dist/esm/types.js +24 -1
  76. package/dist/esm/util/analytics/ufoExperiences.js +39 -2
  77. package/dist/esm/util/analytics/useSampledUFOComponentExperience.js +2 -1
  78. package/dist/esm/util/browser-support.js +1 -0
  79. package/dist/esm/util/constants.js +2 -2
  80. package/dist/esm/util/storage-available.js +4 -0
  81. package/dist/esm/util/useInView.js +12 -0
  82. package/dist/esm/version.json +1 -1
  83. package/dist/types/components/common/CachingEmoji.d.ts +0 -1
  84. package/dist/types/components/common/Emoji.d.ts +2 -0
  85. package/dist/types/components/common/ResourcedEmojiComponent.d.ts +1 -0
  86. package/dist/types/components/common/UfoErrorBoundary.d.ts +2 -2
  87. package/dist/types/types.d.ts +17 -0
  88. package/dist/types/util/analytics/samplingUfo.d.ts +7 -0
  89. package/dist/types/util/analytics/ufoExperiences.d.ts +4 -1
  90. package/dist/types/util/browser-support.d.ts +1 -0
  91. package/dist/types/util/constants.d.ts +1 -1
  92. package/dist/types/util/useInView.d.ts +4 -0
  93. package/package.json +7 -6
@@ -2,14 +2,18 @@ import _extends from "@babel/runtime/helpers/extends";
2
2
 
3
3
  /** @jsx jsx */
4
4
  import { jsx } from '@emotion/core';
5
- import React from 'react';
5
+ import React, { useEffect, useCallback } from 'react';
6
6
  import { shouldUseAltRepresentation } from '../../api/EmojiUtils';
7
- import { deleteEmojiLabel } from '../../util/constants';
7
+ import { deleteEmojiLabel, SAMPLING_RATE_EMOJI_RENDERED_EXP } from '../../util/constants';
8
8
  import { isImageRepresentation, isMediaRepresentation, isSpriteRepresentation, toEmojiId } from '../../util/type-helpers';
9
+ import { UfoEmojiTimings } from '../../types';
9
10
  import { leftClick } from '../../util/mouse';
10
11
  import DeleteButton from './DeleteButton';
11
12
  import { emojiContainer, emojiNodeStyles, commonSelectedStyles, selectOnHoverStyles, emojiSprite, emojiMainStyle, emojiStyles, emojiImage } from './styles';
12
- import { sampledUfoRenderedEmoji } from '../../util/analytics';
13
+ import { sampledUfoRenderedEmoji, ufoExperiences, useSampledUFOComponentExperience } from '../../util/analytics';
14
+ import { isIntersectionObserverSupported } from '../../util/browser-support';
15
+ import { useInView } from '../../util/useInView';
16
+ import { hasUfoMarked } from '../../util/analytics/ufoExperiences';
13
17
 
14
18
  const handleMouseDown = (props, event) => {
15
19
  // Clicked emoji delete button
@@ -83,9 +87,10 @@ const handleImageError = (props, event) => {
83
87
  }
84
88
  }; // Pure functional components are used in favour of class based components, due to the performance!
85
89
  // When rendering 1500+ emoji using class based components had a significant impact.
90
+ // TODO: add UFO tracking for sprite emoji
86
91
 
87
92
 
88
- const renderAsSprite = props => {
93
+ export const SpriteEmoji = props => {
89
94
  const {
90
95
  emoji,
91
96
  fitToHeight,
@@ -117,11 +122,9 @@ const renderAsSprite = props => {
117
122
  backgroundSize: `${sprite.column * 100}% ${sprite.row * 100}%`,
118
123
  ...sizing
119
124
  };
120
- const emojiNode = jsx("span", {
121
- className: emojiSprite,
122
- style: style
123
- }, "\xA0");
124
125
  return jsx("span", {
126
+ "data-testid": `sprite-emoji-${emoji.shortName}`,
127
+ "data-emoji-type": "sprite",
125
128
  tabIndex: shouldBeInteractive ? 0 : undefined,
126
129
  role: shouldBeInteractive ? 'button' : undefined,
127
130
  css: emojiContainer,
@@ -135,11 +138,13 @@ const renderAsSprite = props => {
135
138
  },
136
139
  "aria-label": emoji.shortName,
137
140
  title: showTooltip ? emoji.shortName : ''
138
- }, emojiNode);
141
+ }, jsx("span", {
142
+ className: emojiSprite,
143
+ style: style
144
+ }, "\xA0"));
139
145
  }; // Keep as pure functional component, see renderAsSprite.
140
146
 
141
-
142
- const renderAsImage = props => {
147
+ export const ImageEmoji = props => {
143
148
  const {
144
149
  emoji,
145
150
  fitToHeight,
@@ -150,6 +155,10 @@ const renderAsImage = props => {
150
155
  showDelete,
151
156
  shouldBeInteractive
152
157
  } = props;
158
+ const [ref, inView] = useInView({
159
+ triggerOnce: true
160
+ });
161
+ const ufoExp = sampledUfoRenderedEmoji(emoji);
153
162
  const classes = `${emojiMainStyle} ${emojiNodeStyles} ${selected ? commonSelectedStyles : ''} ${selectOnHover ? selectOnHoverStyles : ''} ${emojiImage} ${className ? className : ''}`;
154
163
  let width;
155
164
  let height;
@@ -188,17 +197,41 @@ const renderAsImage = props => {
188
197
  handleImageError(props, event);
189
198
  };
190
199
 
191
- const onLoad = () => {
192
- sampledUfoRenderedEmoji(emoji).success();
193
- }; // Pass src attribute as key to force React to rerender img node since browser does not
194
- // change preview image until loaded
195
- // We currently have the following error: property 'loading' does not exist on type 'DetailedHTMLProps<ImgHTMLAttributes, HTMLImageElement>'
196
- // The fix for this was added as a part of @types/react@16.9.20 - https://github.com/facebook/react/issues/16942
197
- // TODO: remove @ts-ignore for the <img> below when the @types/react will be bumped from v16.8.0 to v16.9.20 or higher.
200
+ const markStartLoadFromMountedTime = useCallback(() => {
201
+ const mountedMark = ufoExp.metrics.marks.find(mark => mark.name === UfoEmojiTimings.MOUNTED_END);
202
+ ufoExp.mark(UfoEmojiTimings.ONLOAD_START, mountedMark === null || mountedMark === void 0 ? void 0 : mountedMark.time);
203
+ }, [ufoExp]);
198
204
 
205
+ const onLoad = () => {
206
+ // onload could trigger before onBeforeLoad when emojis in viewport at start, so we need to mark onload start manually.
207
+ if (!hasUfoMarked(ufoExp, UfoEmojiTimings.ONLOAD_START)) {
208
+ markStartLoadFromMountedTime();
209
+ }
210
+
211
+ if (!hasUfoMarked(ufoExp, UfoEmojiTimings.ONLOAD_END)) {
212
+ ufoExp.mark(UfoEmojiTimings.ONLOAD_END);
213
+ }
214
+
215
+ ufoExp.success({
216
+ metadata: {
217
+ IBSupported: isIntersectionObserverSupported
218
+ }
219
+ });
220
+ };
199
221
 
222
+ const onBeforeLoad = useCallback(() => {
223
+ if (!hasUfoMarked(ufoExp, UfoEmojiTimings.ONLOAD_START)) {
224
+ ufoExp.mark(UfoEmojiTimings.ONLOAD_START);
225
+ }
226
+ }, [ufoExp]); // because of the lack of browser support of on before load natively, used IntersectionObserver helper hook to mimic the before load time mark for UFO.
227
+
228
+ useEffect(() => {
229
+ if (inView) {
230
+ onBeforeLoad();
231
+ }
232
+ }, [inView, onBeforeLoad]);
200
233
  const emojiNode = jsx("img", _extends({
201
- // @ts-ignore
234
+ //@ts-ignore
202
235
  loading: "lazy",
203
236
  src: src,
204
237
  key: src,
@@ -214,6 +247,8 @@ const renderAsImage = props => {
214
247
  onLoad: onLoad
215
248
  }, sizing));
216
249
  return jsx("span", {
250
+ "data-testid": `image-emoji-${emoji.shortName}`,
251
+ "data-emoji-type": "image",
217
252
  css: emojiStyles,
218
253
  tabIndex: shouldBeInteractive ? 0 : undefined,
219
254
  role: shouldBeInteractive ? 'button' : undefined,
@@ -226,19 +261,36 @@ const renderAsImage = props => {
226
261
  handleMouseMove(props, event);
227
262
  },
228
263
  "aria-label": emoji.shortName,
229
- title: showTooltip ? emoji.shortName : ''
264
+ title: showTooltip ? emoji.shortName : '',
265
+ ref: ref
230
266
  }, deleteButton, emojiNode);
231
267
  };
232
-
233
268
  export const Emoji = props => {
234
269
  const {
235
270
  emoji
236
- } = props; // TODO: We always prefer render as image as having accessibility issues with sprite representation
271
+ } = props; // start emoji rendered experience, it may have already started earlier in ResourcedEmoji or CachingEmoji
272
+
273
+ useSampledUFOComponentExperience(ufoExperiences['emoji-rendered'].getInstance(emoji.id || emoji.shortName), SAMPLING_RATE_EMOJI_RENDERED_EXP, {
274
+ source: 'emoji',
275
+ emoji: emoji.shortName
276
+ });
277
+ useEffect(() => {
278
+ const ufoExp = sampledUfoRenderedEmoji(emoji);
279
+
280
+ if (!hasUfoMarked(ufoExp, 'fmp')) {
281
+ ufoExp.markFMP();
282
+ }
283
+
284
+ if (!hasUfoMarked(ufoExp, UfoEmojiTimings.MOUNTED_END)) {
285
+ ufoExp.mark(UfoEmojiTimings.MOUNTED_END);
286
+ } // eslint-disable-next-line react-hooks/exhaustive-deps
287
+
288
+ }, []); // TODO: We always prefer render as image as having accessibility issues with sprite representation
237
289
 
238
290
  if (isSpriteRepresentation(emoji.representation)) {
239
- return renderAsSprite(props);
291
+ return jsx(SpriteEmoji, props);
240
292
  }
241
293
 
242
- return renderAsImage(props);
294
+ return jsx(ImageEmoji, props);
243
295
  };
244
296
  export default Emoji;
@@ -32,6 +32,7 @@ const EmojiPlaceholder = props => {
32
32
  height: `${height}px`
33
33
  };
34
34
  return jsx("span", {
35
+ "data-testid": `emoji-placeholder-${shortName}`,
35
36
  "aria-label": shortName,
36
37
  className: placeholder,
37
38
  css: placeholderContainer,
@@ -25,6 +25,10 @@ export default class Popup extends PureComponent {
25
25
  if (this.debounced) {
26
26
  clearTimeout(this.debounced);
27
27
  this.debounced = null;
28
+ }
29
+
30
+ if (typeof window === 'undefined') {
31
+ return;
28
32
  } // Timeout set to 30ms as to not throttle IE11
29
33
 
30
34
 
@@ -39,7 +43,11 @@ export default class Popup extends PureComponent {
39
43
  this.popup = document.createElement('div');
40
44
  document.body.appendChild(this.popup);
41
45
  this.popup.style.position = 'absolute';
42
- window.addEventListener('resize', this.handleResize);
46
+
47
+ if (typeof window !== 'undefined') {
48
+ window.addEventListener('resize', this.handleResize);
49
+ }
50
+
43
51
  this.applyAbsolutePosition();
44
52
  this.renderContent();
45
53
  }
@@ -50,6 +58,10 @@ export default class Popup extends PureComponent {
50
58
  }
51
59
 
52
60
  componentWillUnmount() {
61
+ if (typeof window === 'undefined') {
62
+ return;
63
+ }
64
+
53
65
  window.removeEventListener('resize', this.handleResize);
54
66
 
55
67
  if (this.popup) {
@@ -73,6 +85,10 @@ export default class Popup extends PureComponent {
73
85
  }
74
86
 
75
87
  applyAbovePosition() {
88
+ if (typeof window === 'undefined') {
89
+ return;
90
+ }
91
+
76
92
  const targetNode = getTargetNode(this.props.target);
77
93
 
78
94
  if (targetNode && this.popup) {
@@ -86,6 +102,10 @@ export default class Popup extends PureComponent {
86
102
  }
87
103
 
88
104
  applyAbsolutePosition() {
105
+ if (typeof window === 'undefined') {
106
+ return;
107
+ }
108
+
89
109
  if (this.props.relativePosition === 'above') {
90
110
  this.applyAbovePosition();
91
111
  } else if (this.props.relativePosition === 'below') {
@@ -18,7 +18,19 @@ export const createRecordSelectionDefault = (provider, onSelect, fireAnalytics)
18
18
  ufoExperiences['emoji-selection-recorded'].success();
19
19
  }).catch(() => {
20
20
  fireAnalytics && fireAnalytics(recordFailed);
21
- ufoExperiences['emoji-selection-recorded'].failure();
21
+ ufoExperiences['emoji-selection-recorded'].failure({
22
+ metadata: {
23
+ reason: 'recordSelection error',
24
+ source: 'RecordSelectionDefault',
25
+ data: {
26
+ emoji: {
27
+ id: emoji.id,
28
+ name: emoji.name,
29
+ shortName: emoji.shortName
30
+ }
31
+ }
32
+ }
33
+ });
22
34
  });
23
35
  }
24
36
  } finally {
@@ -1,7 +1,7 @@
1
1
  import _extends from "@babel/runtime/helpers/extends";
2
2
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
3
3
  import React from 'react';
4
- import { defaultEmojiHeight, SAMPLING_RATE_EMOJI_RENDERED_EXP_RESOURCEEMOJI } from '../../util/constants';
4
+ import { defaultEmojiHeight, SAMPLING_RATE_EMOJI_RENDERED_EXP } from '../../util/constants';
5
5
  import EmojiPlaceholder from './EmojiPlaceholder';
6
6
  import LoadingEmojiComponent from './LoadingEmojiComponent';
7
7
  import { sampledUfoRenderedEmoji, ufoExperiences } from '../../util/analytics';
@@ -24,15 +24,25 @@ export default class ResourcedEmoji extends LoadingEmojiComponent {
24
24
  });
25
25
 
26
26
  sampledUfoRenderedEmoji(props.emojiId).start({
27
- samplingRate: SAMPLING_RATE_EMOJI_RENDERED_EXP_RESOURCEEMOJI
27
+ samplingRate: SAMPLING_RATE_EMOJI_RENDERED_EXP
28
28
  });
29
29
  ufoExperiences['emoji-rendered'].getInstance(props.emojiId.id || props.emojiId.shortName).addMetadata({
30
- source: 'resourced-emoji'
30
+ source: 'resourced-emoji',
31
+ emoji: props.emojiId.shortName
31
32
  });
32
33
  }
33
34
 
34
35
  componentWillUnmount() {
35
- sampledUfoRenderedEmoji(this.props.emojiId).abort();
36
+ sampledUfoRenderedEmoji(this.props.emojiId).abort({
37
+ metadata: {
38
+ source: 'ResourcedEmoji',
39
+ reason: 'unmount',
40
+ data: {
41
+ emojiId: this.props.emojiId.id,
42
+ shortName: this.props.emojiId.shortName
43
+ }
44
+ }
45
+ });
36
46
  }
37
47
 
38
48
  asyncLoadComponent() {
@@ -3,10 +3,12 @@ import React from 'react';
3
3
  import { Component } from 'react';
4
4
  import { defaultEmojiHeight } from '../../util/constants';
5
5
  import { isPromise } from '../../util/type-helpers';
6
+ import { UfoEmojiTimings } from '../../types';
6
7
  import CachingEmoji from './CachingEmoji';
7
8
  import EmojiPlaceholder from './EmojiPlaceholder';
8
9
  import { sampledUfoRenderedEmoji } from '../../util/analytics';
9
10
  import LegacyEmojiContextProvider from '../../context/LegacyEmojiContextProvider';
11
+ import { hasUfoMarked } from '../../util/analytics/ufoExperiences';
10
12
  export default class ResourcedEmojiComponent extends Component {
11
13
  constructor(props) {
12
14
  super(props);
@@ -21,6 +23,7 @@ export default class ResourcedEmojiComponent extends Component {
21
23
 
22
24
  refreshEmoji(emojiProvider, emojiId) {
23
25
  const foundEmoji = emojiProvider.findByEmojiId(emojiId);
26
+ sampledUfoRenderedEmoji(emojiId).mark(UfoEmojiTimings.METADATA_START);
24
27
 
25
28
  if (isPromise(foundEmoji)) {
26
29
  this.setState({
@@ -33,12 +36,20 @@ export default class ResourcedEmojiComponent extends Component {
33
36
  emoji,
34
37
  loaded: true
35
38
  });
39
+ sampledUfoRenderedEmoji(emojiId).mark(UfoEmojiTimings.METADATA_END);
36
40
 
37
41
  if (!emoji) {
38
42
  // emoji is undefined
39
43
  sampledUfoRenderedEmoji(emojiId).failure({
40
44
  metadata: {
41
- reason: 'failed to find'
45
+ reason: 'failed to find',
46
+ source: 'ResourcedEmojiComponent',
47
+ data: {
48
+ emoji: {
49
+ id: emojiId.id,
50
+ shortName: emojiId.shortName
51
+ }
52
+ }
42
53
  }
43
54
  });
44
55
  }
@@ -46,12 +57,20 @@ export default class ResourcedEmojiComponent extends Component {
46
57
  }).catch(() => {
47
58
  sampledUfoRenderedEmoji(emojiId).failure({
48
59
  metadata: {
49
- reason: 'failed to load'
60
+ reason: 'failed to load',
61
+ source: 'ResourcedEmojiComponent',
62
+ data: {
63
+ emoji: {
64
+ id: emojiId.id,
65
+ shortName: emojiId.shortName
66
+ }
67
+ }
50
68
  }
51
69
  });
52
70
  });
53
71
  } else {
54
- // loaded
72
+ sampledUfoRenderedEmoji(emojiId).mark(UfoEmojiTimings.METADATA_END); // loaded
73
+
55
74
  this.setState({
56
75
  emoji: foundEmoji,
57
76
  loaded: true
@@ -73,6 +92,12 @@ export default class ResourcedEmojiComponent extends Component {
73
92
  this.ready = false;
74
93
  }
75
94
 
95
+ componentDidMount() {
96
+ if (!hasUfoMarked(sampledUfoRenderedEmoji(this.props.emojiId), UfoEmojiTimings.FMP_END)) {
97
+ sampledUfoRenderedEmoji(this.props.emojiId).markFMP();
98
+ }
99
+ }
100
+
76
101
  UNSAFE_componentWillReceiveProps(nextProps) {
77
102
  if (nextProps.emojiProvider !== this.props.emojiProvider || nextProps.emojiId !== this.props.emojiId) {
78
103
  this.refreshEmoji(nextProps.emojiProvider, nextProps.emojiId);
@@ -1,7 +1,19 @@
1
1
  import React from 'react';
2
2
  export class UfoErrorBoundary extends React.Component {
3
- componentDidCatch() {
4
- this.props.experiences.forEach(e => e.failure());
3
+ componentDidCatch(error, errorInfo) {
4
+ for (const exp of this.props.experiences) {
5
+ exp.failure({
6
+ metadata: {
7
+ source: 'UfoErrorBoundary',
8
+ reason: 'error',
9
+ error: {
10
+ name: error.name,
11
+ message: error.message,
12
+ infoStack: errorInfo.componentStack
13
+ }
14
+ }
15
+ });
16
+ }
5
17
  }
6
18
 
7
19
  render() {
@@ -22,7 +22,16 @@ export const uploadEmoji = (upload, emojiProvider, errorSetter, onSuccess, fireA
22
22
  duration: Date.now() - startTime,
23
23
  reason: messages.emojiUploadFailed.defaultMessage
24
24
  }));
25
- ufoExperiences['emoji-uploaded'].failure();
25
+ ufoExperiences['emoji-uploaded'].failure({
26
+ metadata: {
27
+ source: 'UploadEmoji',
28
+ error: err,
29
+ data: {
30
+ upload: { ...upload
31
+ }
32
+ }
33
+ }
34
+ });
26
35
  });
27
36
  }
28
37
  };
@@ -294,6 +294,10 @@ export default class EmojiPickerComponent extends PureComponent {
294
294
  _defineProperty(this, "scrollToEndOfList", () => {
295
295
  const emojiPickerList = this.refs.emojiPickerList;
296
296
 
297
+ if (typeof window === 'undefined') {
298
+ return;
299
+ }
300
+
297
301
  if (emojiPickerList) {
298
302
  // Wait a tick to ensure repaint and updated height for picker list
299
303
  window.setTimeout(() => {
@@ -395,9 +399,18 @@ export default class EmojiPickerComponent extends PureComponent {
395
399
  this.fireAnalytics(closedPickerEvent({
396
400
  duration: this.calculateElapsedTime()
397
401
  }));
398
- ufoExperiences['emoji-picker-opened'].abort();
399
- ufoExperiences['emoji-searched'].abort();
400
- ufoExperiences['emoji-selection-recorded'].abort();
402
+ ufoExperiences['emoji-picker-opened'].abort({
403
+ metadata: {
404
+ source: 'EmojiPickerComponent',
405
+ reason: 'unmount'
406
+ }
407
+ });
408
+ ufoExperiences['emoji-searched'].abort({
409
+ metadata: {
410
+ source: 'EmojiPickerComponent',
411
+ reason: 'unmount'
412
+ }
413
+ });
401
414
  }
402
415
 
403
416
  UNSAFE_componentWillReceiveProps(nextProps) {
@@ -39,6 +39,11 @@ class EmojiPickerListSearch extends PureComponent {
39
39
  // setting the focus to search input.
40
40
  // see FS-2056
41
41
  this.inputRef = input;
42
+
43
+ if (typeof window === 'undefined') {
44
+ return;
45
+ }
46
+
42
47
  window.setTimeout(this.focusInput);
43
48
  }
44
49
  });
@@ -158,6 +158,9 @@ export const emojiItem = css({
158
158
  minWidth: '24px',
159
159
  maxWidth: '24px'
160
160
  },
161
+ [`& .${emojiNodeStyles} .${placeholder}`]: {
162
+ margin: '0'
163
+ },
161
164
  // Fit non-square emoji to square
162
165
  [`& .${emojiNodeStyles} > img`]: {
163
166
  position: 'relative',
@@ -172,8 +172,24 @@ export default class EmojiTypeAheadComponent extends PureComponent {
172
172
  this.fireAnalyticsEvent(typeaheadCancelledEvent(Date.now() - this.openTime, query, emojis));
173
173
  }
174
174
 
175
- ufoExperiences['emoji-searched'].abort();
176
- ufoExperiences['emoji-selection-recorded'].abort();
175
+ ufoExperiences['emoji-searched'].abort({
176
+ metadata: {
177
+ source: 'EmojiTypeAheadComponent',
178
+ reason: 'unmount',
179
+ data: {
180
+ query
181
+ }
182
+ }
183
+ });
184
+ ufoExperiences['emoji-selection-recorded'].abort({
185
+ metadata: {
186
+ source: 'EmojiTypeAheadComponent',
187
+ reason: 'unmount',
188
+ data: {
189
+ query
190
+ }
191
+ }
192
+ });
177
193
  this.sessionId = uuid();
178
194
  this.selected = false;
179
195
  }
@@ -84,7 +84,12 @@ export default class EmojiUploadComponent extends PureComponent {
84
84
  }
85
85
 
86
86
  componentWillUnmount() {
87
- ufoExperiences['emoji-uploaded'].abort();
87
+ ufoExperiences['emoji-uploaded'].abort({
88
+ metadata: {
89
+ source: 'EmojiUploadComponent',
90
+ reason: 'unmount'
91
+ }
92
+ });
88
93
  }
89
94
 
90
95
  render() {
@@ -61,4 +61,27 @@ export let UfoComponentName;
61
61
  UfoComponentName["EMOJI"] = "emoji";
62
62
  UfoComponentName["EMOJI_PICKER"] = "emoji-picker";
63
63
  UfoComponentName["EMOJI_PROVIDER"] = "emoji-provider";
64
- })(UfoComponentName || (UfoComponentName = {}));
64
+ })(UfoComponentName || (UfoComponentName = {}));
65
+
66
+ export let UfoEmojiTimingsKeys;
67
+
68
+ (function (UfoEmojiTimingsKeys) {
69
+ UfoEmojiTimingsKeys["FMP"] = "fmp";
70
+ UfoEmojiTimingsKeys["MOUNTED"] = "emoji-mount";
71
+ UfoEmojiTimingsKeys["METADATA"] = "emoji-metadata";
72
+ UfoEmojiTimingsKeys["MEDIADATA"] = "emoji-media";
73
+ UfoEmojiTimingsKeys["ONLOAD"] = "emoji-onload";
74
+ })(UfoEmojiTimingsKeys || (UfoEmojiTimingsKeys = {}));
75
+
76
+ export let UfoEmojiTimings;
77
+
78
+ (function (UfoEmojiTimings) {
79
+ UfoEmojiTimings["FMP_END"] = "fmp";
80
+ UfoEmojiTimings["MOUNTED_END"] = "emoji-mount_end";
81
+ UfoEmojiTimings["METADATA_START"] = "emoji-metadata_start";
82
+ UfoEmojiTimings["METADATA_END"] = "emoji-metadata_end";
83
+ UfoEmojiTimings["MEDIA_START"] = "emoji-media_start";
84
+ UfoEmojiTimings["MEDIA_END"] = "emoji-media_end";
85
+ UfoEmojiTimings["ONLOAD_START"] = "emoji-onload_start";
86
+ UfoEmojiTimings["ONLOAD_END"] = "emoji-onload_end";
87
+ })(UfoEmojiTimings || (UfoEmojiTimings = {}));
@@ -1,4 +1,4 @@
1
- import { UfoComponentName, UfoExperienceName } from '../../types';
1
+ import { UfoComponentName, UfoEmojiTimings, UfoEmojiTimingsKeys, UfoExperienceName } from '../../types';
2
2
  import { ExperiencePerformanceTypes, ExperienceTypes, ConcurrentExperience, UFOExperience } from '@atlaskit/ufo';
3
3
  import { withSampling } from './samplingUfo';
4
4
 
@@ -22,8 +22,37 @@ const createInlineExperience = componentName => {
22
22
  };
23
23
  };
24
24
 
25
+ const customEmojiTimings = [{
26
+ key: UfoEmojiTimingsKeys.FMP,
27
+ endMark: UfoEmojiTimings.FMP_END
28
+ }, {
29
+ key: UfoEmojiTimingsKeys.METADATA,
30
+ component: 'resourced-emoji',
31
+ startMark: UfoEmojiTimings.METADATA_START,
32
+ endMark: UfoEmojiTimings.METADATA_END
33
+ }, {
34
+ key: UfoEmojiTimingsKeys.MEDIADATA,
35
+ component: 'caching-emoji',
36
+ startMark: UfoEmojiTimings.MEDIA_START,
37
+ endMark: UfoEmojiTimings.MEDIA_END
38
+ }, {
39
+ key: UfoEmojiTimingsKeys.MOUNTED,
40
+ component: 'emoji',
41
+ endMark: UfoEmojiTimings.MOUNTED_END
42
+ }, {
43
+ key: UfoEmojiTimingsKeys.ONLOAD,
44
+ startMark: UfoEmojiTimings.ONLOAD_START,
45
+ endMark: UfoEmojiTimings.ONLOAD_END
46
+ }];
25
47
  export const ufoExperiences = {
26
- 'emoji-rendered': new ConcurrentExperience(UfoExperienceName.EMOJI_RENDERED, createRenderExperience(UfoComponentName.EMOJI)),
48
+ 'emoji-rendered': new ConcurrentExperience(UfoExperienceName.EMOJI_RENDERED, {
49
+ platform: {
50
+ component: UfoComponentName.EMOJI
51
+ },
52
+ type: ExperienceTypes.Operation,
53
+ performanceType: ExperiencePerformanceTypes.Custom,
54
+ timings: customEmojiTimings
55
+ }),
27
56
  'emoji-resource-fetched': new ConcurrentExperience(UfoExperienceName.EMOJI_RESOURCE_FETCHED, createRenderExperience(UfoComponentName.EMOJI_PROVIDER)),
28
57
  'emoji-picker-opened': new UFOExperience(UfoExperienceName.EMOJI_PICKER_OPENED, createRenderExperience(UfoComponentName.EMOJI_PICKER)),
29
58
  'emoji-selection-recorded': new UFOExperience(UfoExperienceName.EMOJI_SELECTION_RECORDED, createInlineExperience(UfoComponentName.EMOJI_PROVIDER)),
@@ -32,4 +61,10 @@ export const ufoExperiences = {
32
61
  };
33
62
  export const sampledUfoRenderedEmoji = emojiId => {
34
63
  return withSampling(ufoExperiences['emoji-rendered'].getInstance(emojiId.id || emojiId.shortName));
64
+ };
65
+ export const hasUfoMarked = (ufoExperience, name) => {
66
+ return ufoExperience.metrics.marks.some(mask => mask.name === name);
67
+ };
68
+ export const sampledUfoEmojiResourceFetched = providerType => {
69
+ return withSampling(ufoExperiences['emoji-resource-fetched'].getInstance(providerType));
35
70
  };
@@ -30,8 +30,9 @@ export const useSampledUFOComponentExperience = (experience, samplingRate, metad
30
30
  withSampling(experience).start({
31
31
  samplingRate
32
32
  });
33
+ const isMetadataEmpty = Object.keys(experience.metadata).length === 0;
33
34
 
34
- if (metadata) {
35
+ if (metadata && isMetadataEmpty) {
35
36
  experience.addMetadata(metadata);
36
37
  }
37
38
  });
@@ -0,0 +1 @@
1
+ export const isIntersectionObserverSupported = typeof window !== 'undefined' && 'IntersectionObserver' in window && 'IntersectionObserverEntry' in window && 'intersectionRatio' in window.IntersectionObserverEntry.prototype;
@@ -21,6 +21,6 @@ export const defaultListLimit = 50;
21
21
  export const migrationUserId = 'hipchat_migration_emoticons';
22
22
  export const analyticsEmojiPrefix = 'atlassian.fabric.emoji.picker'; // This is the base sampling rate in Emoji
23
23
 
24
- export const SAMPLING_RATE_EMOJI_RENDERED_EXP = 0.05; // This rate is used in ResourceEmoji which will be used in stead of base sampling rate above
24
+ export const SAMPLING_RATE_EMOJI_RENDERED_EXP = 0.05; // This rate is used in fetching emoji resource
25
25
 
26
- export const SAMPLING_RATE_EMOJI_RENDERED_EXP_RESOURCEEMOJI = 0.1; // add more sampling rate in parent components like EmojiTypeAheadItem.
26
+ export const SAMPLING_RATE_EMOJI_RESOURCE_FETCHED_EXP = 0.01;
@@ -1,6 +1,10 @@
1
1
  // Copied from https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API#feature-detecting_localstorage
2
2
  // a brief history of local storage - https://gist.github.com/paulirish/5558557
3
3
  export default function storageAvailable(type) {
4
+ if (typeof window === 'undefined') {
5
+ return false;
6
+ }
7
+
4
8
  var storage;
5
9
 
6
10
  try {
@@ -0,0 +1,12 @@
1
+ import { useInView as useInViewLib } from 'react-intersection-observer';
2
+ import { isIntersectionObserverSupported } from './browser-support';
3
+ export const useInView = options => {
4
+ const hookResult = useInViewLib(options);
5
+
6
+ if (!isIntersectionObserverSupported) {
7
+ // Unsupported, return no `ref` and default `inView` true value
8
+ return [undefined, true];
9
+ }
10
+
11
+ return hookResult;
12
+ };
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/emoji",
3
- "version": "64.6.0",
3
+ "version": "64.7.1",
4
4
  "sideEffects": false
5
5
  }