@atlaskit/emoji 64.6.1 → 64.7.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 (61) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/cjs/api/EmojiRepository.js +4 -0
  3. package/dist/cjs/api/EmojiResource.js +8 -0
  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 +13 -10
  7. package/dist/cjs/components/common/DeleteButton.js +2 -1
  8. package/dist/cjs/components/common/Emoji.js +76 -20
  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/ResourcedEmojiComponent.js +12 -1
  12. package/dist/cjs/components/picker/EmojiPickerComponent.js +4 -2
  13. package/dist/cjs/components/picker/EmojiPickerListSearch.js +5 -0
  14. package/dist/cjs/components/picker/styles.js +2 -0
  15. package/dist/cjs/types.js +25 -2
  16. package/dist/cjs/util/analytics/ufoExperiences.js +26 -1
  17. package/dist/cjs/util/browser-support.js +8 -0
  18. package/dist/cjs/util/storage-available.js +4 -0
  19. package/dist/cjs/version.json +1 -1
  20. package/dist/es2019/api/EmojiRepository.js +4 -0
  21. package/dist/es2019/api/EmojiResource.js +8 -0
  22. package/dist/es2019/api/EmojiUtils.js +4 -0
  23. package/dist/es2019/api/internal/UsageFrequencyTracker.js +4 -1
  24. package/dist/es2019/components/common/CachingEmoji.js +9 -7
  25. package/dist/es2019/components/common/DeleteButton.js +2 -1
  26. package/dist/es2019/components/common/Emoji.js +54 -21
  27. package/dist/es2019/components/common/EmojiPlaceholder.js +1 -0
  28. package/dist/es2019/components/common/Popup.js +21 -1
  29. package/dist/es2019/components/common/ResourcedEmojiComponent.js +9 -1
  30. package/dist/es2019/components/picker/EmojiPickerComponent.js +4 -1
  31. package/dist/es2019/components/picker/EmojiPickerListSearch.js +5 -0
  32. package/dist/es2019/components/picker/styles.js +3 -0
  33. package/dist/es2019/types.js +22 -1
  34. package/dist/es2019/util/analytics/ufoExperiences.js +27 -2
  35. package/dist/es2019/util/browser-support.js +1 -0
  36. package/dist/es2019/util/storage-available.js +4 -0
  37. package/dist/es2019/version.json +1 -1
  38. package/dist/esm/api/EmojiRepository.js +4 -0
  39. package/dist/esm/api/EmojiResource.js +8 -0
  40. package/dist/esm/api/EmojiUtils.js +4 -0
  41. package/dist/esm/api/internal/UsageFrequencyTracker.js +4 -1
  42. package/dist/esm/components/common/CachingEmoji.js +13 -11
  43. package/dist/esm/components/common/DeleteButton.js +2 -1
  44. package/dist/esm/components/common/Emoji.js +60 -21
  45. package/dist/esm/components/common/EmojiPlaceholder.js +1 -0
  46. package/dist/esm/components/common/Popup.js +21 -1
  47. package/dist/esm/components/common/ResourcedEmojiComponent.js +11 -1
  48. package/dist/esm/components/picker/EmojiPickerComponent.js +4 -1
  49. package/dist/esm/components/picker/EmojiPickerListSearch.js +5 -0
  50. package/dist/esm/components/picker/styles.js +2 -0
  51. package/dist/esm/types.js +22 -1
  52. package/dist/esm/util/analytics/ufoExperiences.js +27 -2
  53. package/dist/esm/util/browser-support.js +1 -0
  54. package/dist/esm/util/storage-available.js +4 -0
  55. package/dist/esm/version.json +1 -1
  56. package/dist/types/components/common/CachingEmoji.d.ts +0 -1
  57. package/dist/types/components/common/Emoji.d.ts +2 -0
  58. package/dist/types/components/common/ResourcedEmojiComponent.d.ts +1 -0
  59. package/dist/types/types.d.ts +15 -0
  60. package/dist/types/util/browser-support.d.ts +1 -0
  61. package/package.json +4 -3
@@ -1,8 +1,9 @@
1
1
  import _extends from "@babel/runtime/helpers/extends";
2
2
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
3
- import React from 'react';
3
+ import React, { useEffect } from 'react';
4
4
  import { PureComponent } from 'react';
5
5
  import { isMediaEmoji } from '../../util/type-helpers';
6
+ import { UfoEmojiTimings } from '../../types';
6
7
  import debug from '../../util/logger';
7
8
  import Emoji from './Emoji';
8
9
  import EmojiPlaceholder from './EmojiPlaceholder';
@@ -23,8 +24,12 @@ export const CachingEmoji = props => {
23
24
  } = props; // start emoji rendered experience, it may have already started earlier in ResourcedEmoji
24
25
 
25
26
  useSampledUFOComponentExperience(ufoExperiences['emoji-rendered'].getInstance(emojiProps.emoji.id || emojiProps.emoji.shortName), SAMPLING_RATE_EMOJI_RENDERED_EXP, {
26
- source: 'caching-emoji'
27
+ source: 'caching-emoji',
28
+ emoji: emojiProps.emoji.shortName
27
29
  });
30
+ useEffect(() => {
31
+ sampledUfoRenderedEmoji(emojiProps.emoji).mark(UfoEmojiTimings.MOUNTED_END); // eslint-disable-next-line react-hooks/exhaustive-deps
32
+ }, []);
28
33
 
29
34
  const emojiNode = () => {
30
35
  if (isMediaEmoji(props.emoji)) {
@@ -64,10 +69,6 @@ export class CachingMediaEmoji extends PureComponent {
64
69
  this.loadEmoji(props.emoji, context);
65
70
  }
66
71
 
67
- componentDidMount() {
68
- sampledUfoRenderedEmoji(this.props.emoji).markFMP();
69
- }
70
-
71
72
  componentDidUpdate() {
72
73
  var _this$state$cachedEmo;
73
74
 
@@ -94,11 +95,13 @@ export class CachingMediaEmoji extends PureComponent {
94
95
  }
95
96
 
96
97
  debug('Loading image via media cache', emoji.shortName);
98
+ sampledUfoRenderedEmoji(emoji).mark(UfoEmojiTimings.MEDIA_START);
97
99
  emojiProvider.getMediaEmojiDescriptionURLWithInlineToken(emoji).then(cachedEmoji => {
98
100
  this.setState({
99
101
  cachedEmoji,
100
102
  invalidImage: false
101
103
  });
104
+ sampledUfoRenderedEmoji(emoji).mark(UfoEmojiTimings.MEDIA_END);
102
105
  }).catch(() => {
103
106
  this.setState({
104
107
  cachedEmoji: undefined,
@@ -119,7 +122,6 @@ export class CachingMediaEmoji extends PureComponent {
119
122
  } = this.state;
120
123
  const {
121
124
  children,
122
- placeholderSize,
123
125
  ...otherProps
124
126
  } = this.props;
125
127
  let emojiComponent;
@@ -9,7 +9,8 @@ import { emojiDeleteButton, deleteButton } from './styles';
9
9
 
10
10
  const DeleteButton = props => jsx("span", {
11
11
  css: deleteButton,
12
- className: emojiDeleteButton
12
+ className: emojiDeleteButton,
13
+ "data-testid": "emoji-delete-button"
13
14
  }, jsx(Button, {
14
15
  iconBefore: jsx(CrossCircleIcon, {
15
16
  label: deleteEmojiLabel,
@@ -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
7
  import { deleteEmojiLabel } 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
13
  import { sampledUfoRenderedEmoji } from '../../util/analytics';
14
+ import { EmojiPlaceholder } from '../..';
15
+ import { useInView } from 'react-intersection-observer';
16
+ import { isIntersectionObserverSupported } from '../../util/browser-support'; // import { isIntersectionObserverSupported } from '../../util/browser-support';
13
17
 
14
18
  const handleMouseDown = (props, event) => {
15
19
  // Clicked emoji delete button
@@ -85,7 +89,7 @@ const handleImageError = (props, event) => {
85
89
  // When rendering 1500+ emoji using class based components had a significant impact.
86
90
 
87
91
 
88
- const renderAsSprite = props => {
92
+ export const SpriteEmoji = props => {
89
93
  const {
90
94
  emoji,
91
95
  fitToHeight,
@@ -117,11 +121,9 @@ const renderAsSprite = props => {
117
121
  backgroundSize: `${sprite.column * 100}% ${sprite.row * 100}%`,
118
122
  ...sizing
119
123
  };
120
- const emojiNode = jsx("span", {
121
- className: emojiSprite,
122
- style: style
123
- }, "\xA0");
124
124
  return jsx("span", {
125
+ "data-testid": `sprite-emoji-${emoji.shortName}`,
126
+ "data-emoji-type": "sprite",
125
127
  tabIndex: shouldBeInteractive ? 0 : undefined,
126
128
  role: shouldBeInteractive ? 'button' : undefined,
127
129
  css: emojiContainer,
@@ -135,11 +137,13 @@ const renderAsSprite = props => {
135
137
  },
136
138
  "aria-label": emoji.shortName,
137
139
  title: showTooltip ? emoji.shortName : ''
138
- }, emojiNode);
140
+ }, jsx("span", {
141
+ className: emojiSprite,
142
+ style: style
143
+ }, "\xA0"));
139
144
  }; // Keep as pure functional component, see renderAsSprite.
140
145
 
141
-
142
- const renderAsImage = props => {
146
+ export const ImageEmoji = props => {
143
147
  const {
144
148
  emoji,
145
149
  fitToHeight,
@@ -189,16 +193,23 @@ const renderAsImage = props => {
189
193
  };
190
194
 
191
195
  const onLoad = () => {
196
+ sampledUfoRenderedEmoji(emoji).mark(UfoEmojiTimings.ONLOAD_END);
192
197
  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.
198
-
198
+ };
199
199
 
200
+ const onBeforeLoad = useCallback(() => {
201
+ sampledUfoRenderedEmoji(emoji).mark(UfoEmojiTimings.ONLOAD_START);
202
+ }, [emoji]);
203
+ const [ref, inView] = useInView({
204
+ triggerOnce: true
205
+ });
206
+ useEffect(() => {
207
+ if (inView) {
208
+ onBeforeLoad();
209
+ }
210
+ }, [inView, onBeforeLoad]);
200
211
  const emojiNode = jsx("img", _extends({
201
- // @ts-ignore
212
+ //@ts-ignore
202
213
  loading: "lazy",
203
214
  src: src,
204
215
  key: src,
@@ -213,7 +224,29 @@ const renderAsImage = props => {
213
224
  onError: onError,
214
225
  onLoad: onLoad
215
226
  }, sizing));
227
+ const placeholder = jsx(EmojiPlaceholder, {
228
+ shortName: emoji.shortName,
229
+ size: fitToHeight,
230
+ showTooltip: showTooltip,
231
+ representation: emoji.representation
232
+ });
233
+
234
+ const renderLazyLoadedEmoji = () => {
235
+ // if browser not supported, render emoji node directly
236
+ if (!isIntersectionObserverSupported) {
237
+ return emojiNode;
238
+ }
239
+
240
+ if (inView) {
241
+ return emojiNode;
242
+ }
243
+
244
+ return placeholder;
245
+ };
246
+
216
247
  return jsx("span", {
248
+ "data-testid": `image-emoji-${emoji.shortName}`,
249
+ "data-emoji-type": "image",
217
250
  css: emojiStyles,
218
251
  tabIndex: shouldBeInteractive ? 0 : undefined,
219
252
  role: shouldBeInteractive ? 'button' : undefined,
@@ -226,19 +259,19 @@ const renderAsImage = props => {
226
259
  handleMouseMove(props, event);
227
260
  },
228
261
  "aria-label": emoji.shortName,
229
- title: showTooltip ? emoji.shortName : ''
230
- }, deleteButton, emojiNode);
262
+ title: showTooltip ? emoji.shortName : '',
263
+ ref: ref
264
+ }, deleteButton, renderLazyLoadedEmoji());
231
265
  };
232
-
233
266
  export const Emoji = props => {
234
267
  const {
235
268
  emoji
236
269
  } = props; // TODO: We always prefer render as image as having accessibility issues with sprite representation
237
270
 
238
271
  if (isSpriteRepresentation(emoji.representation)) {
239
- return renderAsSprite(props);
272
+ return jsx(SpriteEmoji, props);
240
273
  }
241
274
 
242
- return renderAsImage(props);
275
+ return jsx(ImageEmoji, props);
243
276
  };
244
277
  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') {
@@ -3,6 +3,7 @@ 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';
@@ -21,6 +22,7 @@ export default class ResourcedEmojiComponent extends Component {
21
22
 
22
23
  refreshEmoji(emojiProvider, emojiId) {
23
24
  const foundEmoji = emojiProvider.findByEmojiId(emojiId);
25
+ sampledUfoRenderedEmoji(emojiId).mark(UfoEmojiTimings.METADATA_START);
24
26
 
25
27
  if (isPromise(foundEmoji)) {
26
28
  this.setState({
@@ -33,6 +35,7 @@ export default class ResourcedEmojiComponent extends Component {
33
35
  emoji,
34
36
  loaded: true
35
37
  });
38
+ sampledUfoRenderedEmoji(emojiId).mark(UfoEmojiTimings.METADATA_END);
36
39
 
37
40
  if (!emoji) {
38
41
  // emoji is undefined
@@ -51,7 +54,8 @@ export default class ResourcedEmojiComponent extends Component {
51
54
  });
52
55
  });
53
56
  } else {
54
- // loaded
57
+ sampledUfoRenderedEmoji(emojiId).mark(UfoEmojiTimings.METADATA_END); // loaded
58
+
55
59
  this.setState({
56
60
  emoji: foundEmoji,
57
61
  loaded: true
@@ -73,6 +77,10 @@ export default class ResourcedEmojiComponent extends Component {
73
77
  this.ready = false;
74
78
  }
75
79
 
80
+ componentDidMount() {
81
+ sampledUfoRenderedEmoji(this.props.emojiId).mark(UfoEmojiTimings.MOUNTED_END);
82
+ }
83
+
76
84
  UNSAFE_componentWillReceiveProps(nextProps) {
77
85
  if (nextProps.emojiProvider !== this.props.emojiProvider || nextProps.emojiId !== this.props.emojiId) {
78
86
  this.refreshEmoji(nextProps.emojiProvider, nextProps.emojiId);
@@ -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(() => {
@@ -397,7 +401,6 @@ export default class EmojiPickerComponent extends PureComponent {
397
401
  }));
398
402
  ufoExperiences['emoji-picker-opened'].abort();
399
403
  ufoExperiences['emoji-searched'].abort();
400
- ufoExperiences['emoji-selection-recorded'].abort();
401
404
  }
402
405
 
403
406
  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',
@@ -61,4 +61,25 @@ 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["MOUNTED"] = "emoji-mount";
70
+ UfoEmojiTimingsKeys["METADATA"] = "emoji-metadata";
71
+ UfoEmojiTimingsKeys["MEDIADATA"] = "emoji-media";
72
+ UfoEmojiTimingsKeys["ONLOAD"] = "emoji-onload";
73
+ })(UfoEmojiTimingsKeys || (UfoEmojiTimingsKeys = {}));
74
+
75
+ export let UfoEmojiTimings;
76
+
77
+ (function (UfoEmojiTimings) {
78
+ UfoEmojiTimings["MOUNTED_END"] = "emoji-mount_end";
79
+ UfoEmojiTimings["METADATA_START"] = "emoji-metadata_start";
80
+ UfoEmojiTimings["METADATA_END"] = "emoji-metadata_end";
81
+ UfoEmojiTimings["MEDIA_START"] = "emoji-media_start";
82
+ UfoEmojiTimings["MEDIA_END"] = "emoji-media_end";
83
+ UfoEmojiTimings["ONLOAD_START"] = "emoji-onload_start";
84
+ UfoEmojiTimings["ONLOAD_END"] = "emoji-onload_end";
85
+ })(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,33 @@ const createInlineExperience = componentName => {
22
22
  };
23
23
  };
24
24
 
25
+ const customEmojiTimings = [{
26
+ key: UfoEmojiTimingsKeys.MOUNTED,
27
+ endMark: UfoEmojiTimings.MOUNTED_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.ONLOAD,
40
+ startMark: UfoEmojiTimings.ONLOAD_START,
41
+ endMark: UfoEmojiTimings.ONLOAD_END
42
+ }];
25
43
  export const ufoExperiences = {
26
- 'emoji-rendered': new ConcurrentExperience(UfoExperienceName.EMOJI_RENDERED, createRenderExperience(UfoComponentName.EMOJI)),
44
+ 'emoji-rendered': new ConcurrentExperience(UfoExperienceName.EMOJI_RENDERED, {
45
+ platform: {
46
+ component: UfoComponentName.EMOJI
47
+ },
48
+ type: ExperienceTypes.Operation,
49
+ performanceType: ExperiencePerformanceTypes.Custom,
50
+ timings: customEmojiTimings
51
+ }),
27
52
  'emoji-resource-fetched': new ConcurrentExperience(UfoExperienceName.EMOJI_RESOURCE_FETCHED, createRenderExperience(UfoComponentName.EMOJI_PROVIDER)),
28
53
  'emoji-picker-opened': new UFOExperience(UfoExperienceName.EMOJI_PICKER_OPENED, createRenderExperience(UfoComponentName.EMOJI_PICKER)),
29
54
  'emoji-selection-recorded': new UFOExperience(UfoExperienceName.EMOJI_SELECTION_RECORDED, createInlineExperience(UfoComponentName.EMOJI_PROVIDER)),
@@ -0,0 +1 @@
1
+ export const isIntersectionObserverSupported = typeof window === 'undefined' ? false : !!window.IntersectionObserver;
@@ -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 {
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/emoji",
3
- "version": "64.6.1",
3
+ "version": "64.7.0",
4
4
  "sideEffects": false
5
5
  }
@@ -288,6 +288,10 @@ var EmojiRepository = /*#__PURE__*/function () {
288
288
  // frequent category being shown in the picker).
289
289
 
290
290
  if (this.dynamicCategoryList.indexOf(frequentCategory) === -1) {
291
+ if (typeof window === 'undefined') {
292
+ return;
293
+ }
294
+
291
295
  window.setTimeout(function () {
292
296
  _this2.dynamicCategoryList.push(frequentCategory);
293
297
  });
@@ -185,6 +185,10 @@ export var EmojiResource = /*#__PURE__*/function (_AbstractResource) {
185
185
  }, {
186
186
  key: "loadStoredTone",
187
187
  value: function loadStoredTone() {
188
+ if (typeof window === 'undefined') {
189
+ return undefined;
190
+ }
191
+
188
192
  var storedToneString = window.localStorage.getItem(selectedToneStorageKey);
189
193
 
190
194
  if (storedToneString) {
@@ -498,6 +502,10 @@ export var EmojiResource = /*#__PURE__*/function (_AbstractResource) {
498
502
  value: function setSelectedTone(tone) {
499
503
  this.selectedTone = tone;
500
504
 
505
+ if (typeof window === 'undefined') {
506
+ return;
507
+ }
508
+
501
509
  if (storageAvailable('localStorage')) {
502
510
  try {
503
511
  window.localStorage.setItem(selectedToneStorageKey, tone ? tone.toString() : '');
@@ -49,6 +49,10 @@ var calculateScale = function calculateScale(getRatio) {
49
49
  };
50
50
 
51
51
  export var getPixelRatio = function getPixelRatio() {
52
+ if (typeof window === 'undefined') {
53
+ return 0;
54
+ }
55
+
52
56
  return window.devicePixelRatio;
53
57
  };
54
58
  export var getAltRepresentation = function getAltRepresentation(reps) {
@@ -137,7 +137,10 @@ export var Gateway = /*#__PURE__*/function () {
137
137
  }
138
138
  };
139
139
 
140
- window.setTimeout(wrappedFunc);
140
+ if (typeof window !== 'undefined') {
141
+ window.setTimeout(wrappedFunc);
142
+ }
143
+
141
144
  return true;
142
145
  }
143
146
  }, {
@@ -8,15 +8,16 @@ import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf";
8
8
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
9
9
  import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
10
10
  var _excluded = ["placeholderSize"],
11
- _excluded2 = ["children", "placeholderSize"];
11
+ _excluded2 = ["children"];
12
12
 
13
13
  function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
14
14
 
15
15
  function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
16
16
 
17
- import React from 'react';
17
+ import React, { useEffect } from 'react';
18
18
  import { PureComponent } from 'react';
19
19
  import { isMediaEmoji } from '../../util/type-helpers';
20
+ import { UfoEmojiTimings } from '../../types';
20
21
  import debug from '../../util/logger';
21
22
  import Emoji from './Emoji';
22
23
  import EmojiPlaceholder from './EmojiPlaceholder';
@@ -36,8 +37,12 @@ export var CachingEmoji = function CachingEmoji(props) {
36
37
 
37
38
 
38
39
  useSampledUFOComponentExperience(ufoExperiences['emoji-rendered'].getInstance(emojiProps.emoji.id || emojiProps.emoji.shortName), SAMPLING_RATE_EMOJI_RENDERED_EXP, {
39
- source: 'caching-emoji'
40
+ source: 'caching-emoji',
41
+ emoji: emojiProps.emoji.shortName
40
42
  });
43
+ useEffect(function () {
44
+ sampledUfoRenderedEmoji(emojiProps.emoji).mark(UfoEmojiTimings.MOUNTED_END); // eslint-disable-next-line react-hooks/exhaustive-deps
45
+ }, []);
41
46
 
42
47
  var emojiNode = function emojiNode() {
43
48
  if (isMediaEmoji(props.emoji)) {
@@ -90,11 +95,6 @@ export var CachingMediaEmoji = /*#__PURE__*/function (_PureComponent) {
90
95
  }
91
96
 
92
97
  _createClass(CachingMediaEmoji, [{
93
- key: "componentDidMount",
94
- value: function componentDidMount() {
95
- sampledUfoRenderedEmoji(this.props.emoji).markFMP();
96
- }
97
- }, {
98
98
  key: "componentDidUpdate",
99
99
  value: function componentDidUpdate() {
100
100
  var _this$state$cachedEmo;
@@ -123,11 +123,14 @@ export var CachingMediaEmoji = /*#__PURE__*/function (_PureComponent) {
123
123
  }
124
124
 
125
125
  debug('Loading image via media cache', emoji.shortName);
126
+ sampledUfoRenderedEmoji(emoji).mark(UfoEmojiTimings.MEDIA_START);
126
127
  emojiProvider.getMediaEmojiDescriptionURLWithInlineToken(emoji).then(function (cachedEmoji) {
127
128
  _this2.setState({
128
129
  cachedEmoji: cachedEmoji,
129
130
  invalidImage: false
130
131
  });
132
+
133
+ sampledUfoRenderedEmoji(emoji).mark(UfoEmojiTimings.MEDIA_END);
131
134
  }).catch(function () {
132
135
  _this2.setState({
133
136
  cachedEmoji: undefined,
@@ -150,7 +153,6 @@ export var CachingMediaEmoji = /*#__PURE__*/function (_PureComponent) {
150
153
 
151
154
  var _this$props = this.props,
152
155
  children = _this$props.children,
153
- placeholderSize = _this$props.placeholderSize,
154
156
  otherProps = _objectWithoutProperties(_this$props, _excluded2);
155
157
 
156
158
  var emojiComponent;
@@ -163,13 +165,13 @@ export var CachingMediaEmoji = /*#__PURE__*/function (_PureComponent) {
163
165
  } else {
164
166
  var _this$props2 = this.props,
165
167
  emoji = _this$props2.emoji,
166
- _placeholderSize = _this$props2.placeholderSize,
168
+ placeholderSize = _this$props2.placeholderSize,
167
169
  showTooltip = _this$props2.showTooltip,
168
170
  fitToHeight = _this$props2.fitToHeight;
169
171
  var shortName = emoji.shortName,
170
172
  representation = emoji.representation;
171
173
  emojiComponent = /*#__PURE__*/React.createElement(EmojiPlaceholder, {
172
- size: fitToHeight || _placeholderSize,
174
+ size: fitToHeight || placeholderSize,
173
175
  shortName: shortName,
174
176
  showTooltip: showTooltip,
175
177
  representation: representation
@@ -10,7 +10,8 @@ import { emojiDeleteButton, deleteButton } from './styles';
10
10
  var DeleteButton = function DeleteButton(props) {
11
11
  return jsx("span", {
12
12
  css: deleteButton,
13
- className: emojiDeleteButton
13
+ className: emojiDeleteButton,
14
+ "data-testid": "emoji-delete-button"
14
15
  }, jsx(Button, {
15
16
  iconBefore: jsx(CrossCircleIcon, {
16
17
  label: deleteEmojiLabel,