@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
package/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # @atlaskit/emoji
2
2
 
3
+ ## 64.7.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [`6e01776f0b7`](https://bitbucket.org/atlassian/atlassian-frontend/commits/6e01776f0b7) - remove placeholder when lazy load emoji
8
+ - [`6e01776f0b7`](https://bitbucket.org/atlassian/atlassian-frontend/commits/6e01776f0b7) - fix placeholder issue in lazy loaded emoji by removing it and re-write the anlytics tracking around it
9
+ - [`c3f9e9bce1c`](https://bitbucket.org/atlassian/atlassian-frontend/commits/c3f9e9bce1c) - Add custom information to failure and abort events for UFO experiences
10
+ - Updated dependencies
11
+
12
+ ## 64.7.0
13
+
14
+ ### Minor Changes
15
+
16
+ - [`2b241314079`](https://bitbucket.org/atlassian/atlassian-frontend/commits/2b241314079) - improve lazy load emoji with placeholder and improved analtyics tracking
17
+
18
+ ## 64.6.1
19
+
20
+ ### Patch Changes
21
+
22
+ - Updated dependencies
23
+
3
24
  ## 64.6.0
4
25
 
5
26
  ### Minor Changes
@@ -307,6 +307,10 @@ var EmojiRepository = /*#__PURE__*/function () {
307
307
  // frequent category being shown in the picker).
308
308
 
309
309
  if (this.dynamicCategoryList.indexOf(_constants.frequentCategory) === -1) {
310
+ if (typeof window === 'undefined') {
311
+ return;
312
+ }
313
+
310
314
  window.setTimeout(function () {
311
315
  _this2.dynamicCategoryList.push(_constants.frequentCategory);
312
316
  });
@@ -123,7 +123,11 @@ var EmojiResource = /*#__PURE__*/function (_AbstractResource) {
123
123
 
124
124
  _analytics.ufoExperiences['emoji-resource-fetched'].getInstance(providerType).failure({
125
125
  metadata: {
126
- reason: reason
126
+ reason: reason,
127
+ source: 'EmojiProvider',
128
+ data: {
129
+ providerUrl: provider.url
130
+ }
127
131
  }
128
132
  });
129
133
  });
@@ -212,6 +216,10 @@ var EmojiResource = /*#__PURE__*/function (_AbstractResource) {
212
216
  }, {
213
217
  key: "loadStoredTone",
214
218
  value: function loadStoredTone() {
219
+ if (typeof window === 'undefined') {
220
+ return undefined;
221
+ }
222
+
215
223
  var storedToneString = window.localStorage.getItem(_constants.selectedToneStorageKey);
216
224
 
217
225
  if (storedToneString) {
@@ -525,6 +533,10 @@ var EmojiResource = /*#__PURE__*/function (_AbstractResource) {
525
533
  value: function setSelectedTone(tone) {
526
534
  this.selectedTone = tone;
527
535
 
536
+ if (typeof window === 'undefined') {
537
+ return;
538
+ }
539
+
528
540
  if ((0, _storageAvailable.default)('localStorage')) {
529
541
  try {
530
542
  window.localStorage.setItem(_constants.selectedToneStorageKey, tone ? tone.toString() : '');
@@ -64,6 +64,10 @@ var calculateScale = function calculateScale(getRatio) {
64
64
  };
65
65
 
66
66
  var getPixelRatio = function getPixelRatio() {
67
+ if (typeof window === 'undefined') {
68
+ return 0;
69
+ }
70
+
67
71
  return window.devicePixelRatio;
68
72
  };
69
73
 
@@ -150,7 +150,10 @@ var Gateway = /*#__PURE__*/function () {
150
150
  }
151
151
  };
152
152
 
153
- window.setTimeout(wrappedFunc);
153
+ if (typeof window !== 'undefined') {
154
+ window.setTimeout(wrappedFunc);
155
+ }
156
+
154
157
  return true;
155
158
  }
156
159
  }, {
@@ -31,6 +31,8 @@ var _react = _interopRequireWildcard(require("react"));
31
31
 
32
32
  var _typeHelpers = require("../../util/type-helpers");
33
33
 
34
+ var _types = require("../../types");
35
+
34
36
  var _logger = _interopRequireDefault(require("../../util/logger"));
35
37
 
36
38
  var _Emoji = _interopRequireDefault(require("./Emoji"));
@@ -45,8 +47,10 @@ var _constants = require("../../util/constants");
45
47
 
46
48
  var _EmojiContext = require("../../context/EmojiContext");
47
49
 
50
+ var _ufoExperiences = require("../../util/analytics/ufoExperiences");
51
+
48
52
  var _excluded = ["placeholderSize"],
49
- _excluded2 = ["children", "placeholderSize"];
53
+ _excluded2 = ["children"];
50
54
 
51
55
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
52
56
 
@@ -66,8 +70,15 @@ var CachingEmoji = function CachingEmoji(props) {
66
70
  emojiProps = (0, _objectWithoutProperties2.default)(props, _excluded); // start emoji rendered experience, it may have already started earlier in ResourcedEmoji
67
71
 
68
72
  (0, _analytics.useSampledUFOComponentExperience)(_analytics.ufoExperiences['emoji-rendered'].getInstance(emojiProps.emoji.id || emojiProps.emoji.shortName), _constants.SAMPLING_RATE_EMOJI_RENDERED_EXP, {
69
- source: 'caching-emoji'
73
+ source: 'caching-emoji',
74
+ emoji: emojiProps.emoji.shortName
70
75
  });
76
+ (0, _react.useEffect)(function () {
77
+ if (!(0, _ufoExperiences.hasUfoMarked)((0, _analytics.sampledUfoRenderedEmoji)(emojiProps.emoji), 'fmp')) {
78
+ (0, _analytics.sampledUfoRenderedEmoji)(emojiProps.emoji).markFMP();
79
+ } // eslint-disable-next-line react-hooks/exhaustive-deps
80
+
81
+ }, []);
71
82
 
72
83
  var emojiNode = function emojiNode() {
73
84
  if ((0, _typeHelpers.isMediaEmoji)(props.emoji)) {
@@ -102,7 +113,12 @@ var CachingMediaEmoji = /*#__PURE__*/function (_PureComponent) {
102
113
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "handleLoadError", function (_emojiId) {
103
114
  (0, _analytics.sampledUfoRenderedEmoji)(_emojiId).failure({
104
115
  metadata: {
105
- reason: 'load error'
116
+ reason: 'load error',
117
+ source: 'CachingMediaEmoji',
118
+ emoji: {
119
+ id: _emojiId.id,
120
+ shortName: _emojiId.shortName
121
+ }
106
122
  }
107
123
  });
108
124
 
@@ -120,11 +136,6 @@ var CachingMediaEmoji = /*#__PURE__*/function (_PureComponent) {
120
136
  }
121
137
 
122
138
  (0, _createClass2.default)(CachingMediaEmoji, [{
123
- key: "componentDidMount",
124
- value: function componentDidMount() {
125
- (0, _analytics.sampledUfoRenderedEmoji)(this.props.emoji).markFMP();
126
- }
127
- }, {
128
139
  key: "componentDidUpdate",
129
140
  value: function componentDidUpdate() {
130
141
  var _this$state$cachedEmo;
@@ -153,11 +164,14 @@ var CachingMediaEmoji = /*#__PURE__*/function (_PureComponent) {
153
164
  }
154
165
 
155
166
  (0, _logger.default)('Loading image via media cache', emoji.shortName);
167
+ (0, _analytics.sampledUfoRenderedEmoji)(emoji).mark(_types.UfoEmojiTimings.MEDIA_START);
156
168
  emojiProvider.getMediaEmojiDescriptionURLWithInlineToken(emoji).then(function (cachedEmoji) {
157
169
  _this2.setState({
158
170
  cachedEmoji: cachedEmoji,
159
171
  invalidImage: false
160
172
  });
173
+
174
+ (0, _analytics.sampledUfoRenderedEmoji)(emoji).mark(_types.UfoEmojiTimings.MEDIA_END);
161
175
  }).catch(function () {
162
176
  _this2.setState({
163
177
  cachedEmoji: undefined,
@@ -166,7 +180,15 @@ var CachingMediaEmoji = /*#__PURE__*/function (_PureComponent) {
166
180
 
167
181
  (0, _analytics.sampledUfoRenderedEmoji)(emoji).failure({
168
182
  metadata: {
169
- reason: 'failed to load media emoji'
183
+ reason: 'failed to load media emoji',
184
+ source: 'CachingMediaEmoji',
185
+ data: {
186
+ emoji: {
187
+ id: emoji.id,
188
+ shortName: emoji.shortName,
189
+ name: emoji.name
190
+ }
191
+ }
170
192
  }
171
193
  });
172
194
  });
@@ -179,7 +201,6 @@ var CachingMediaEmoji = /*#__PURE__*/function (_PureComponent) {
179
201
  invalidImage = _this$state.invalidImage;
180
202
  var _this$props = this.props,
181
203
  children = _this$props.children,
182
- placeholderSize = _this$props.placeholderSize,
183
204
  otherProps = (0, _objectWithoutProperties2.default)(_this$props, _excluded2);
184
205
  var emojiComponent;
185
206
 
@@ -191,13 +212,13 @@ var CachingMediaEmoji = /*#__PURE__*/function (_PureComponent) {
191
212
  } else {
192
213
  var _this$props2 = this.props,
193
214
  emoji = _this$props2.emoji,
194
- _placeholderSize = _this$props2.placeholderSize,
215
+ placeholderSize = _this$props2.placeholderSize,
195
216
  showTooltip = _this$props2.showTooltip,
196
217
  fitToHeight = _this$props2.fitToHeight;
197
218
  var shortName = emoji.shortName,
198
219
  representation = emoji.representation;
199
220
  emojiComponent = /*#__PURE__*/_react.default.createElement(_EmojiPlaceholder.default, {
200
- size: fitToHeight || _placeholderSize,
221
+ size: fitToHeight || placeholderSize,
201
222
  shortName: shortName,
202
223
  showTooltip: showTooltip,
203
224
  representation: representation
@@ -25,7 +25,8 @@ var _styles = require("./styles");
25
25
  var DeleteButton = function DeleteButton(props) {
26
26
  return (0, _core.jsx)("span", {
27
27
  css: _styles.deleteButton,
28
- className: _styles.emojiDeleteButton
28
+ className: _styles.emojiDeleteButton,
29
+ "data-testid": "emoji-delete-button"
29
30
  }, (0, _core.jsx)(_customThemeButton.default, {
30
31
  iconBefore: (0, _core.jsx)(_crossCircle.default, {
31
32
  label: _constants.deleteEmojiLabel,
@@ -2,18 +2,22 @@
2
2
 
3
3
  var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
4
 
5
+ var _typeof = require("@babel/runtime/helpers/typeof");
6
+
5
7
  Object.defineProperty(exports, "__esModule", {
6
8
  value: true
7
9
  });
8
- exports.default = exports.Emoji = void 0;
10
+ exports.default = exports.SpriteEmoji = exports.ImageEmoji = exports.Emoji = void 0;
9
11
 
10
12
  var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
11
13
 
14
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
15
+
12
16
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
13
17
 
14
18
  var _core = require("@emotion/core");
15
19
 
16
- var _react = _interopRequireDefault(require("react"));
20
+ var _react = _interopRequireWildcard(require("react"));
17
21
 
18
22
  var _EmojiUtils = require("../../api/EmojiUtils");
19
23
 
@@ -21,6 +25,8 @@ var _constants = require("../../util/constants");
21
25
 
22
26
  var _typeHelpers = require("../../util/type-helpers");
23
27
 
28
+ var _types = require("../../types");
29
+
24
30
  var _mouse = require("../../util/mouse");
25
31
 
26
32
  var _DeleteButton = _interopRequireDefault(require("./DeleteButton"));
@@ -29,6 +35,16 @@ var _styles = require("./styles");
29
35
 
30
36
  var _analytics = require("../../util/analytics");
31
37
 
38
+ var _browserSupport = require("../../util/browser-support");
39
+
40
+ var _useInView3 = require("../../util/useInView");
41
+
42
+ var _ufoExperiences = require("../../util/analytics/ufoExperiences");
43
+
44
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
45
+
46
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
47
+
32
48
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
33
49
 
34
50
  function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
@@ -95,9 +111,10 @@ var handleImageError = function handleImageError(props, event) {
95
111
  }
96
112
  }; // Pure functional components are used in favour of class based components, due to the performance!
97
113
  // When rendering 1500+ emoji using class based components had a significant impact.
114
+ // TODO: add UFO tracking for sprite emoji
98
115
 
99
116
 
100
- var renderAsSprite = function renderAsSprite(props) {
117
+ var SpriteEmoji = function SpriteEmoji(props) {
101
118
  var emoji = props.emoji,
102
119
  fitToHeight = props.fitToHeight,
103
120
  selected = props.selected,
@@ -128,11 +145,9 @@ var renderAsSprite = function renderAsSprite(props) {
128
145
  backgroundSize: "".concat(sprite.column * 100, "% ").concat(sprite.row * 100, "%")
129
146
  }, sizing);
130
147
 
131
- var emojiNode = (0, _core.jsx)("span", {
132
- className: _styles.emojiSprite,
133
- style: style
134
- }, "\xA0");
135
148
  return (0, _core.jsx)("span", {
149
+ "data-testid": "sprite-emoji-".concat(emoji.shortName),
150
+ "data-emoji-type": "sprite",
136
151
  tabIndex: shouldBeInteractive ? 0 : undefined,
137
152
  role: shouldBeInteractive ? 'button' : undefined,
138
153
  css: _styles.emojiContainer,
@@ -148,11 +163,16 @@ var renderAsSprite = function renderAsSprite(props) {
148
163
  },
149
164
  "aria-label": emoji.shortName,
150
165
  title: showTooltip ? emoji.shortName : ''
151
- }, emojiNode);
166
+ }, (0, _core.jsx)("span", {
167
+ className: _styles.emojiSprite,
168
+ style: style
169
+ }, "\xA0"));
152
170
  }; // Keep as pure functional component, see renderAsSprite.
153
171
 
154
172
 
155
- var renderAsImage = function renderAsImage(props) {
173
+ exports.SpriteEmoji = SpriteEmoji;
174
+
175
+ var ImageEmoji = function ImageEmoji(props) {
156
176
  var emoji = props.emoji,
157
177
  fitToHeight = props.fitToHeight,
158
178
  selected = props.selected,
@@ -161,6 +181,15 @@ var renderAsImage = function renderAsImage(props) {
161
181
  showTooltip = props.showTooltip,
162
182
  showDelete = props.showDelete,
163
183
  shouldBeInteractive = props.shouldBeInteractive;
184
+
185
+ var _useInView = (0, _useInView3.useInView)({
186
+ triggerOnce: true
187
+ }),
188
+ _useInView2 = (0, _slicedToArray2.default)(_useInView, 2),
189
+ ref = _useInView2[0],
190
+ inView = _useInView2[1];
191
+
192
+ var ufoExp = (0, _analytics.sampledUfoRenderedEmoji)(emoji);
164
193
  var classes = "".concat(_styles.emojiMainStyle, " ").concat(_styles.emojiNodeStyles, " ").concat(selected ? _styles.commonSelectedStyles : '', " ").concat(selectOnHover ? _styles.selectOnHoverStyles : '', " ").concat(_styles.emojiImage, " ").concat(className ? className : '');
165
194
  var width;
166
195
  var height;
@@ -201,17 +230,43 @@ var renderAsImage = function renderAsImage(props) {
201
230
  handleImageError(props, event);
202
231
  };
203
232
 
204
- var onLoad = function onLoad() {
205
- (0, _analytics.sampledUfoRenderedEmoji)(emoji).success();
206
- }; // Pass src attribute as key to force React to rerender img node since browser does not
207
- // change preview image until loaded
208
- // We currently have the following error: property 'loading' does not exist on type 'DetailedHTMLProps<ImgHTMLAttributes, HTMLImageElement>'
209
- // The fix for this was added as a part of @types/react@16.9.20 - https://github.com/facebook/react/issues/16942
210
- // 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.
233
+ var markStartLoadFromMountedTime = (0, _react.useCallback)(function () {
234
+ var mountedMark = ufoExp.metrics.marks.find(function (mark) {
235
+ return mark.name === _types.UfoEmojiTimings.MOUNTED_END;
236
+ });
237
+ ufoExp.mark(_types.UfoEmojiTimings.ONLOAD_START, mountedMark === null || mountedMark === void 0 ? void 0 : mountedMark.time);
238
+ }, [ufoExp]);
211
239
 
240
+ var onLoad = function onLoad() {
241
+ // onload could trigger before onBeforeLoad when emojis in viewport at start, so we need to mark onload start manually.
242
+ if (!(0, _ufoExperiences.hasUfoMarked)(ufoExp, _types.UfoEmojiTimings.ONLOAD_START)) {
243
+ markStartLoadFromMountedTime();
244
+ }
245
+
246
+ if (!(0, _ufoExperiences.hasUfoMarked)(ufoExp, _types.UfoEmojiTimings.ONLOAD_END)) {
247
+ ufoExp.mark(_types.UfoEmojiTimings.ONLOAD_END);
248
+ }
249
+
250
+ ufoExp.success({
251
+ metadata: {
252
+ IBSupported: _browserSupport.isIntersectionObserverSupported
253
+ }
254
+ });
255
+ };
212
256
 
257
+ var onBeforeLoad = (0, _react.useCallback)(function () {
258
+ if (!(0, _ufoExperiences.hasUfoMarked)(ufoExp, _types.UfoEmojiTimings.ONLOAD_START)) {
259
+ ufoExp.mark(_types.UfoEmojiTimings.ONLOAD_START);
260
+ }
261
+ }, [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.
262
+
263
+ (0, _react.useEffect)(function () {
264
+ if (inView) {
265
+ onBeforeLoad();
266
+ }
267
+ }, [inView, onBeforeLoad]);
213
268
  var emojiNode = (0, _core.jsx)("img", (0, _extends2.default)({
214
- // @ts-ignore
269
+ //@ts-ignore
215
270
  loading: "lazy",
216
271
  src: src,
217
272
  key: src,
@@ -227,6 +282,8 @@ var renderAsImage = function renderAsImage(props) {
227
282
  onLoad: onLoad
228
283
  }, sizing));
229
284
  return (0, _core.jsx)("span", {
285
+ "data-testid": "image-emoji-".concat(emoji.shortName),
286
+ "data-emoji-type": "image",
230
287
  css: _styles.emojiStyles,
231
288
  tabIndex: shouldBeInteractive ? 0 : undefined,
232
289
  role: shouldBeInteractive ? 'button' : undefined,
@@ -241,18 +298,38 @@ var renderAsImage = function renderAsImage(props) {
241
298
  handleMouseMove(props, event);
242
299
  },
243
300
  "aria-label": emoji.shortName,
244
- title: showTooltip ? emoji.shortName : ''
301
+ title: showTooltip ? emoji.shortName : '',
302
+ ref: ref
245
303
  }, deleteButton, emojiNode);
246
304
  };
247
305
 
306
+ exports.ImageEmoji = ImageEmoji;
307
+
248
308
  var Emoji = function Emoji(props) {
249
- var emoji = props.emoji; // TODO: We always prefer render as image as having accessibility issues with sprite representation
309
+ var emoji = props.emoji; // start emoji rendered experience, it may have already started earlier in ResourcedEmoji or CachingEmoji
310
+
311
+ (0, _analytics.useSampledUFOComponentExperience)(_analytics.ufoExperiences['emoji-rendered'].getInstance(emoji.id || emoji.shortName), _constants.SAMPLING_RATE_EMOJI_RENDERED_EXP, {
312
+ source: 'emoji',
313
+ emoji: emoji.shortName
314
+ });
315
+ (0, _react.useEffect)(function () {
316
+ var ufoExp = (0, _analytics.sampledUfoRenderedEmoji)(emoji);
317
+
318
+ if (!(0, _ufoExperiences.hasUfoMarked)(ufoExp, 'fmp')) {
319
+ ufoExp.markFMP();
320
+ }
321
+
322
+ if (!(0, _ufoExperiences.hasUfoMarked)(ufoExp, _types.UfoEmojiTimings.MOUNTED_END)) {
323
+ ufoExp.mark(_types.UfoEmojiTimings.MOUNTED_END);
324
+ } // eslint-disable-next-line react-hooks/exhaustive-deps
325
+
326
+ }, []); // TODO: We always prefer render as image as having accessibility issues with sprite representation
250
327
 
251
328
  if ((0, _typeHelpers.isSpriteRepresentation)(emoji.representation)) {
252
- return renderAsSprite(props);
329
+ return (0, _core.jsx)(SpriteEmoji, props);
253
330
  }
254
331
 
255
- return renderAsImage(props);
332
+ return (0, _core.jsx)(ImageEmoji, props);
256
333
  };
257
334
 
258
335
  exports.Emoji = Emoji;
@@ -41,6 +41,7 @@ var EmojiPlaceholder = function EmojiPlaceholder(props) {
41
41
  height: "".concat(height, "px")
42
42
  };
43
43
  return (0, _core.jsx)("span", {
44
+ "data-testid": "emoji-placeholder-".concat(shortName),
44
45
  "aria-label": shortName,
45
46
  className: _styles.placeholder,
46
47
  css: _styles.placeholderContainer,
@@ -67,6 +67,10 @@ var Popup = /*#__PURE__*/function (_PureComponent) {
67
67
  if (_this.debounced) {
68
68
  clearTimeout(_this.debounced);
69
69
  _this.debounced = null;
70
+ }
71
+
72
+ if (typeof window === 'undefined') {
73
+ return;
70
74
  } // Timeout set to 30ms as to not throttle IE11
71
75
 
72
76
 
@@ -85,7 +89,11 @@ var Popup = /*#__PURE__*/function (_PureComponent) {
85
89
  this.popup = document.createElement('div');
86
90
  document.body.appendChild(this.popup);
87
91
  this.popup.style.position = 'absolute';
88
- window.addEventListener('resize', this.handleResize);
92
+
93
+ if (typeof window !== 'undefined') {
94
+ window.addEventListener('resize', this.handleResize);
95
+ }
96
+
89
97
  this.applyAbsolutePosition();
90
98
  this.renderContent();
91
99
  }
@@ -98,6 +106,10 @@ var Popup = /*#__PURE__*/function (_PureComponent) {
98
106
  }, {
99
107
  key: "componentWillUnmount",
100
108
  value: function componentWillUnmount() {
109
+ if (typeof window === 'undefined') {
110
+ return;
111
+ }
112
+
101
113
  window.removeEventListener('resize', this.handleResize);
102
114
 
103
115
  if (this.popup) {
@@ -124,6 +136,10 @@ var Popup = /*#__PURE__*/function (_PureComponent) {
124
136
  }, {
125
137
  key: "applyAbovePosition",
126
138
  value: function applyAbovePosition() {
139
+ if (typeof window === 'undefined') {
140
+ return;
141
+ }
142
+
127
143
  var targetNode = getTargetNode(this.props.target);
128
144
 
129
145
  if (targetNode && this.popup) {
@@ -138,6 +154,10 @@ var Popup = /*#__PURE__*/function (_PureComponent) {
138
154
  }, {
139
155
  key: "applyAbsolutePosition",
140
156
  value: function applyAbsolutePosition() {
157
+ if (typeof window === 'undefined') {
158
+ return;
159
+ }
160
+
141
161
  if (this.props.relativePosition === 'above') {
142
162
  this.applyAbovePosition();
143
163
  } else if (this.props.relativePosition === 'below') {
@@ -28,7 +28,19 @@ var createRecordSelectionDefault = function createRecordSelectionDefault(provide
28
28
  }).catch(function () {
29
29
  fireAnalytics && fireAnalytics(_analytics.recordFailed);
30
30
 
31
- _analytics.ufoExperiences['emoji-selection-recorded'].failure();
31
+ _analytics.ufoExperiences['emoji-selection-recorded'].failure({
32
+ metadata: {
33
+ reason: 'recordSelection error',
34
+ source: 'RecordSelectionDefault',
35
+ data: {
36
+ emoji: {
37
+ id: emoji.id,
38
+ name: emoji.name,
39
+ shortName: emoji.shortName
40
+ }
41
+ }
42
+ }
43
+ });
32
44
  });
33
45
  }
34
46
  } finally {
@@ -77,11 +77,12 @@ var ResourcedEmoji = /*#__PURE__*/function (_LoadingEmojiComponen) {
77
77
  asyncLoadedComponent: ResourcedEmoji.AsyncLoadedComponent
78
78
  });
79
79
  (0, _analytics.sampledUfoRenderedEmoji)(props.emojiId).start({
80
- samplingRate: _constants.SAMPLING_RATE_EMOJI_RENDERED_EXP_RESOURCEEMOJI
80
+ samplingRate: _constants.SAMPLING_RATE_EMOJI_RENDERED_EXP
81
81
  });
82
82
 
83
83
  _analytics.ufoExperiences['emoji-rendered'].getInstance(props.emojiId.id || props.emojiId.shortName).addMetadata({
84
- source: 'resourced-emoji'
84
+ source: 'resourced-emoji',
85
+ emoji: props.emojiId.shortName
85
86
  });
86
87
 
87
88
  return _this;
@@ -90,7 +91,16 @@ var ResourcedEmoji = /*#__PURE__*/function (_LoadingEmojiComponen) {
90
91
  (0, _createClass2.default)(ResourcedEmoji, [{
91
92
  key: "componentWillUnmount",
92
93
  value: function componentWillUnmount() {
93
- (0, _analytics.sampledUfoRenderedEmoji)(this.props.emojiId).abort();
94
+ (0, _analytics.sampledUfoRenderedEmoji)(this.props.emojiId).abort({
95
+ metadata: {
96
+ source: 'ResourcedEmoji',
97
+ reason: 'unmount',
98
+ data: {
99
+ emojiId: this.props.emojiId.id,
100
+ shortName: this.props.emojiId.shortName
101
+ }
102
+ }
103
+ });
94
104
  }
95
105
  }, {
96
106
  key: "asyncLoadComponent",
@@ -29,6 +29,8 @@ var _constants = require("../../util/constants");
29
29
 
30
30
  var _typeHelpers = require("../../util/type-helpers");
31
31
 
32
+ var _types = require("../../types");
33
+
32
34
  var _CachingEmoji = _interopRequireDefault(require("./CachingEmoji"));
33
35
 
34
36
  var _EmojiPlaceholder = _interopRequireDefault(require("./EmojiPlaceholder"));
@@ -37,6 +39,8 @@ var _analytics = require("../../util/analytics");
37
39
 
38
40
  var _LegacyEmojiContextProvider = _interopRequireDefault(require("../../context/LegacyEmojiContextProvider"));
39
41
 
42
+ var _ufoExperiences = require("../../util/analytics/ufoExperiences");
43
+
40
44
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
41
45
 
42
46
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
@@ -69,6 +73,7 @@ var ResourcedEmojiComponent = /*#__PURE__*/function (_Component) {
69
73
  var _this2 = this;
70
74
 
71
75
  var foundEmoji = emojiProvider.findByEmojiId(emojiId);
76
+ (0, _analytics.sampledUfoRenderedEmoji)(emojiId).mark(_types.UfoEmojiTimings.METADATA_START);
72
77
 
73
78
  if ((0, _typeHelpers.isPromise)(foundEmoji)) {
74
79
  this.setState({
@@ -82,11 +87,20 @@ var ResourcedEmojiComponent = /*#__PURE__*/function (_Component) {
82
87
  loaded: true
83
88
  });
84
89
 
90
+ (0, _analytics.sampledUfoRenderedEmoji)(emojiId).mark(_types.UfoEmojiTimings.METADATA_END);
91
+
85
92
  if (!emoji) {
86
93
  // emoji is undefined
87
94
  (0, _analytics.sampledUfoRenderedEmoji)(emojiId).failure({
88
95
  metadata: {
89
- reason: 'failed to find'
96
+ reason: 'failed to find',
97
+ source: 'ResourcedEmojiComponent',
98
+ data: {
99
+ emoji: {
100
+ id: emojiId.id,
101
+ shortName: emojiId.shortName
102
+ }
103
+ }
90
104
  }
91
105
  });
92
106
  }
@@ -94,12 +108,20 @@ var ResourcedEmojiComponent = /*#__PURE__*/function (_Component) {
94
108
  }).catch(function () {
95
109
  (0, _analytics.sampledUfoRenderedEmoji)(emojiId).failure({
96
110
  metadata: {
97
- reason: 'failed to load'
111
+ reason: 'failed to load',
112
+ source: 'ResourcedEmojiComponent',
113
+ data: {
114
+ emoji: {
115
+ id: emojiId.id,
116
+ shortName: emojiId.shortName
117
+ }
118
+ }
98
119
  }
99
120
  });
100
121
  });
101
122
  } else {
102
- // loaded
123
+ (0, _analytics.sampledUfoRenderedEmoji)(emojiId).mark(_types.UfoEmojiTimings.METADATA_END); // loaded
124
+
103
125
  this.setState({
104
126
  emoji: foundEmoji,
105
127
  loaded: true
@@ -122,6 +144,13 @@ var ResourcedEmojiComponent = /*#__PURE__*/function (_Component) {
122
144
  value: function componentWillUnmount() {
123
145
  this.ready = false;
124
146
  }
147
+ }, {
148
+ key: "componentDidMount",
149
+ value: function componentDidMount() {
150
+ if (!(0, _ufoExperiences.hasUfoMarked)((0, _analytics.sampledUfoRenderedEmoji)(this.props.emojiId), _types.UfoEmojiTimings.FMP_END)) {
151
+ (0, _analytics.sampledUfoRenderedEmoji)(this.props.emojiId).markFMP();
152
+ }
153
+ }
125
154
  }, {
126
155
  key: "UNSAFE_componentWillReceiveProps",
127
156
  value: function UNSAFE_componentWillReceiveProps(nextProps) {