@atlaskit/emoji 67.0.7 → 67.2.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 (238) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/cjs/api/EmojiResource.js +41 -25
  3. package/dist/cjs/api/media/TokenManager.js +4 -4
  4. package/dist/cjs/components/common/CachingEmoji.js +14 -6
  5. package/dist/cjs/components/common/Emoji.js +48 -12
  6. package/dist/cjs/components/common/EmojiActions.js +62 -26
  7. package/dist/cjs/components/common/EmojiErrorMessage.js +12 -7
  8. package/dist/cjs/components/common/EmojiPlaceholder.js +1 -0
  9. package/dist/cjs/components/common/{EmojiButton.js → EmojiRadioButton.js} +28 -19
  10. package/dist/cjs/components/common/EmojiUploadPicker.js +80 -37
  11. package/dist/cjs/components/common/EmojiUploadPreview.js +11 -2
  12. package/dist/cjs/components/common/FileChooser.js +2 -2
  13. package/dist/cjs/components/common/ResourcedEmojiComponent.js +4 -0
  14. package/dist/cjs/components/common/RetryableButton.js +7 -3
  15. package/dist/cjs/components/common/TonePreviewButton.js +44 -0
  16. package/dist/cjs/components/common/ToneSelector.js +53 -25
  17. package/dist/cjs/components/common/styles.js +45 -16
  18. package/dist/cjs/components/i18n.js +44 -4
  19. package/dist/cjs/components/picker/CategorySelector.js +112 -90
  20. package/dist/cjs/components/picker/CategoryTracker.js +0 -28
  21. package/dist/cjs/components/picker/EmojiPickerCategoryHeading.js +2 -1
  22. package/dist/cjs/components/picker/EmojiPickerComponent.js +33 -44
  23. package/dist/cjs/components/picker/EmojiPickerEmojiRow.js +32 -4
  24. package/dist/cjs/components/picker/EmojiPickerList.js +154 -88
  25. package/dist/cjs/components/picker/EmojiPickerListSearch.js +66 -117
  26. package/dist/cjs/components/picker/EmojiPickerVirtualItems.js +5 -2
  27. package/dist/cjs/components/picker/VirtualList.js +273 -171
  28. package/dist/cjs/components/picker/styles.js +43 -51
  29. package/dist/cjs/components/typeahead/EmojiTypeAheadComponent.js +0 -10
  30. package/dist/cjs/context/EmojiPickerListContext.js +33 -0
  31. package/dist/cjs/hooks/useEmojiPickerListContext.js +12 -0
  32. package/dist/cjs/hooks/useIsMounted.js +17 -0
  33. package/dist/cjs/i18n/cs.js +35 -34
  34. package/dist/cjs/i18n/da.js +35 -34
  35. package/dist/cjs/i18n/de.js +35 -34
  36. package/dist/cjs/i18n/en.js +35 -34
  37. package/dist/cjs/i18n/en_GB.js +35 -34
  38. package/dist/cjs/i18n/es.js +35 -34
  39. package/dist/cjs/i18n/fi.js +35 -34
  40. package/dist/cjs/i18n/fr.js +35 -34
  41. package/dist/cjs/i18n/hu.js +35 -34
  42. package/dist/cjs/i18n/it.js +35 -34
  43. package/dist/cjs/i18n/ja.js +35 -34
  44. package/dist/cjs/i18n/ko.js +35 -34
  45. package/dist/cjs/i18n/nb.js +35 -34
  46. package/dist/cjs/i18n/nl.js +35 -34
  47. package/dist/cjs/i18n/pl.js +35 -34
  48. package/dist/cjs/i18n/pt_BR.js +35 -34
  49. package/dist/cjs/i18n/ru.js +35 -34
  50. package/dist/cjs/i18n/sv.js +35 -34
  51. package/dist/cjs/i18n/th.js +35 -34
  52. package/dist/cjs/i18n/tr.js +35 -34
  53. package/dist/cjs/i18n/uk.js +35 -34
  54. package/dist/cjs/i18n/vi.js +35 -34
  55. package/dist/cjs/i18n/zh.js +35 -34
  56. package/dist/cjs/i18n/zh_TW.js +35 -34
  57. package/dist/cjs/types.js +7 -1
  58. package/dist/cjs/util/constants.js +43 -2
  59. package/dist/cjs/util/shared-styles.js +3 -4
  60. package/dist/cjs/version.json +1 -1
  61. package/dist/es2019/api/EmojiResource.js +42 -26
  62. package/dist/es2019/api/media/TokenManager.js +4 -4
  63. package/dist/es2019/components/common/CachingEmoji.js +10 -3
  64. package/dist/es2019/components/common/Emoji.js +44 -11
  65. package/dist/es2019/components/common/EmojiActions.js +55 -24
  66. package/dist/es2019/components/common/EmojiErrorMessage.js +7 -3
  67. package/dist/es2019/components/common/EmojiPlaceholder.js +1 -0
  68. package/dist/es2019/components/common/EmojiRadioButton.js +54 -0
  69. package/dist/es2019/components/common/EmojiUploadPicker.js +75 -36
  70. package/dist/es2019/components/common/EmojiUploadPreview.js +11 -2
  71. package/dist/es2019/components/common/FileChooser.js +1 -1
  72. package/dist/es2019/components/common/ResourcedEmojiComponent.js +4 -0
  73. package/dist/es2019/components/common/RetryableButton.js +7 -3
  74. package/dist/es2019/components/common/TonePreviewButton.js +34 -0
  75. package/dist/es2019/components/common/ToneSelector.js +55 -21
  76. package/dist/es2019/components/common/styles.js +41 -10
  77. package/dist/es2019/components/i18n.js +44 -4
  78. package/dist/es2019/components/picker/CategorySelector.js +114 -89
  79. package/dist/es2019/components/picker/CategoryTracker.js +0 -24
  80. package/dist/es2019/components/picker/EmojiPickerCategoryHeading.js +2 -2
  81. package/dist/es2019/components/picker/EmojiPickerComponent.js +36 -46
  82. package/dist/es2019/components/picker/EmojiPickerEmojiRow.js +51 -21
  83. package/dist/es2019/components/picker/EmojiPickerList.js +113 -55
  84. package/dist/es2019/components/picker/EmojiPickerListSearch.js +61 -98
  85. package/dist/es2019/components/picker/EmojiPickerVirtualItems.js +4 -1
  86. package/dist/es2019/components/picker/VirtualList.js +247 -108
  87. package/dist/es2019/components/picker/styles.js +20 -28
  88. package/dist/es2019/components/typeahead/EmojiTypeAheadComponent.js +0 -10
  89. package/dist/es2019/context/EmojiPickerListContext.js +17 -0
  90. package/dist/es2019/hooks/useEmojiPickerListContext.js +3 -0
  91. package/dist/es2019/hooks/useIsMounted.js +11 -0
  92. package/dist/es2019/i18n/cs.js +35 -34
  93. package/dist/es2019/i18n/da.js +35 -34
  94. package/dist/es2019/i18n/de.js +35 -34
  95. package/dist/es2019/i18n/en.js +35 -34
  96. package/dist/es2019/i18n/en_GB.js +35 -34
  97. package/dist/es2019/i18n/es.js +35 -34
  98. package/dist/es2019/i18n/fi.js +35 -34
  99. package/dist/es2019/i18n/fr.js +35 -34
  100. package/dist/es2019/i18n/hu.js +35 -34
  101. package/dist/es2019/i18n/it.js +35 -34
  102. package/dist/es2019/i18n/ja.js +35 -34
  103. package/dist/es2019/i18n/ko.js +35 -34
  104. package/dist/es2019/i18n/nb.js +35 -34
  105. package/dist/es2019/i18n/nl.js +35 -34
  106. package/dist/es2019/i18n/pl.js +35 -34
  107. package/dist/es2019/i18n/pt_BR.js +35 -34
  108. package/dist/es2019/i18n/ru.js +35 -34
  109. package/dist/es2019/i18n/sv.js +35 -34
  110. package/dist/es2019/i18n/th.js +35 -34
  111. package/dist/es2019/i18n/tr.js +35 -34
  112. package/dist/es2019/i18n/uk.js +35 -34
  113. package/dist/es2019/i18n/vi.js +35 -34
  114. package/dist/es2019/i18n/zh.js +35 -34
  115. package/dist/es2019/i18n/zh_TW.js +35 -34
  116. package/dist/es2019/types.js +5 -0
  117. package/dist/es2019/util/constants.js +32 -0
  118. package/dist/es2019/util/shared-styles.js +1 -2
  119. package/dist/es2019/version.json +1 -1
  120. package/dist/esm/api/EmojiResource.js +42 -26
  121. package/dist/esm/api/media/TokenManager.js +4 -4
  122. package/dist/esm/components/common/CachingEmoji.js +14 -6
  123. package/dist/esm/components/common/Emoji.js +48 -12
  124. package/dist/esm/components/common/EmojiActions.js +62 -26
  125. package/dist/esm/components/common/EmojiErrorMessage.js +7 -3
  126. package/dist/esm/components/common/EmojiPlaceholder.js +1 -0
  127. package/dist/esm/components/common/EmojiRadioButton.js +52 -0
  128. package/dist/esm/components/common/EmojiUploadPicker.js +77 -36
  129. package/dist/esm/components/common/EmojiUploadPreview.js +11 -2
  130. package/dist/esm/components/common/FileChooser.js +1 -1
  131. package/dist/esm/components/common/ResourcedEmojiComponent.js +4 -0
  132. package/dist/esm/components/common/RetryableButton.js +7 -3
  133. package/dist/esm/components/common/TonePreviewButton.js +33 -0
  134. package/dist/esm/components/common/ToneSelector.js +49 -18
  135. package/dist/esm/components/common/styles.js +40 -12
  136. package/dist/esm/components/i18n.js +44 -4
  137. package/dist/esm/components/picker/CategorySelector.js +114 -95
  138. package/dist/esm/components/picker/CategoryTracker.js +0 -28
  139. package/dist/esm/components/picker/EmojiPickerCategoryHeading.js +2 -2
  140. package/dist/esm/components/picker/EmojiPickerComponent.js +35 -46
  141. package/dist/esm/components/picker/EmojiPickerEmojiRow.js +32 -4
  142. package/dist/esm/components/picker/EmojiPickerList.js +156 -86
  143. package/dist/esm/components/picker/EmojiPickerListSearch.js +64 -117
  144. package/dist/esm/components/picker/EmojiPickerVirtualItems.js +5 -2
  145. package/dist/esm/components/picker/VirtualList.js +274 -172
  146. package/dist/esm/components/picker/styles.js +37 -45
  147. package/dist/esm/components/typeahead/EmojiTypeAheadComponent.js +0 -10
  148. package/dist/esm/context/EmojiPickerListContext.js +21 -0
  149. package/dist/esm/hooks/useEmojiPickerListContext.js +5 -0
  150. package/dist/esm/hooks/useIsMounted.js +11 -0
  151. package/dist/esm/i18n/cs.js +35 -34
  152. package/dist/esm/i18n/da.js +35 -34
  153. package/dist/esm/i18n/de.js +35 -34
  154. package/dist/esm/i18n/en.js +35 -34
  155. package/dist/esm/i18n/en_GB.js +35 -34
  156. package/dist/esm/i18n/es.js +35 -34
  157. package/dist/esm/i18n/fi.js +35 -34
  158. package/dist/esm/i18n/fr.js +35 -34
  159. package/dist/esm/i18n/hu.js +35 -34
  160. package/dist/esm/i18n/it.js +35 -34
  161. package/dist/esm/i18n/ja.js +35 -34
  162. package/dist/esm/i18n/ko.js +35 -34
  163. package/dist/esm/i18n/nb.js +35 -34
  164. package/dist/esm/i18n/nl.js +35 -34
  165. package/dist/esm/i18n/pl.js +35 -34
  166. package/dist/esm/i18n/pt_BR.js +35 -34
  167. package/dist/esm/i18n/ru.js +35 -34
  168. package/dist/esm/i18n/sv.js +35 -34
  169. package/dist/esm/i18n/th.js +35 -34
  170. package/dist/esm/i18n/tr.js +35 -34
  171. package/dist/esm/i18n/uk.js +35 -34
  172. package/dist/esm/i18n/vi.js +35 -34
  173. package/dist/esm/i18n/zh.js +35 -34
  174. package/dist/esm/i18n/zh_TW.js +35 -34
  175. package/dist/esm/types.js +5 -0
  176. package/dist/esm/util/constants.js +32 -0
  177. package/dist/esm/util/shared-styles.js +1 -2
  178. package/dist/esm/version.json +1 -1
  179. package/dist/types/api/EmojiResource.d.ts +2 -0
  180. package/dist/types/components/common/Emoji.d.ts +7 -1
  181. package/dist/types/components/common/EmojiActions.d.ts +4 -3
  182. package/dist/types/components/common/{EmojiButton.d.ts → EmojiRadioButton.d.ts} +3 -4
  183. package/dist/types/components/common/EmojiUploadPicker.d.ts +6 -4
  184. package/dist/types/components/common/RetryableButton.d.ts +1 -0
  185. package/dist/types/components/common/TonePreviewButton.d.ts +14 -0
  186. package/dist/types/components/common/ToneSelector.d.ts +8 -5
  187. package/dist/types/components/common/internal-types.d.ts +9 -0
  188. package/dist/types/components/common/styles.d.ts +2 -1
  189. package/dist/types/components/i18n.d.ts +40 -0
  190. package/dist/types/components/picker/CategorySelector.d.ts +3 -10
  191. package/dist/types/components/picker/CategoryTracker.d.ts +0 -2
  192. package/dist/types/components/picker/EmojiPickerCategoryHeading.d.ts +2 -1
  193. package/dist/types/components/picker/EmojiPickerEmojiRow.d.ts +5 -0
  194. package/dist/types/components/picker/EmojiPickerList.d.ts +14 -7
  195. package/dist/types/components/picker/EmojiPickerListSearch.d.ts +4 -8
  196. package/dist/types/components/picker/EmojiPickerVirtualItems.d.ts +1 -1
  197. package/dist/types/components/picker/VirtualList.d.ts +7 -24
  198. package/dist/types/components/picker/styles.d.ts +1 -1
  199. package/dist/types/context/EmojiPickerListContext.d.ts +10 -0
  200. package/dist/types/hooks/useEmojiPickerListContext.d.ts +1 -0
  201. package/dist/types/hooks/useIsMounted.d.ts +1 -0
  202. package/dist/types/i18n/cs.d.ts +34 -34
  203. package/dist/types/i18n/da.d.ts +34 -34
  204. package/dist/types/i18n/de.d.ts +34 -34
  205. package/dist/types/i18n/en.d.ts +34 -34
  206. package/dist/types/i18n/en_GB.d.ts +34 -34
  207. package/dist/types/i18n/es.d.ts +34 -34
  208. package/dist/types/i18n/fi.d.ts +34 -34
  209. package/dist/types/i18n/fr.d.ts +34 -34
  210. package/dist/types/i18n/hu.d.ts +34 -34
  211. package/dist/types/i18n/it.d.ts +34 -34
  212. package/dist/types/i18n/ja.d.ts +34 -34
  213. package/dist/types/i18n/ko.d.ts +34 -34
  214. package/dist/types/i18n/nb.d.ts +34 -34
  215. package/dist/types/i18n/nl.d.ts +34 -34
  216. package/dist/types/i18n/pl.d.ts +34 -34
  217. package/dist/types/i18n/pt_BR.d.ts +34 -34
  218. package/dist/types/i18n/ru.d.ts +34 -34
  219. package/dist/types/i18n/sv.d.ts +34 -34
  220. package/dist/types/i18n/th.d.ts +34 -34
  221. package/dist/types/i18n/tr.d.ts +34 -34
  222. package/dist/types/i18n/uk.d.ts +34 -34
  223. package/dist/types/i18n/vi.d.ts +34 -34
  224. package/dist/types/i18n/zh.d.ts +34 -34
  225. package/dist/types/i18n/zh_TW.d.ts +34 -34
  226. package/dist/types/types.d.ts +5 -0
  227. package/dist/types/util/constants.d.ts +28 -0
  228. package/dist/types/util/shared-styles.d.ts +1 -1
  229. package/dist/types/util/type-helpers.d.ts +1 -1
  230. package/package.json +12 -8
  231. package/report.api.md +62 -1
  232. package/README.md +0 -3
  233. package/dist/cjs/components/hooks.js +0 -14
  234. package/dist/es2019/components/common/EmojiButton.js +0 -49
  235. package/dist/es2019/components/hooks.js +0 -8
  236. package/dist/esm/components/common/EmojiButton.js +0 -43
  237. package/dist/esm/components/hooks.js +0 -8
  238. package/dist/types/components/hooks.d.ts +0 -1
@@ -2,12 +2,12 @@
2
2
  import { useCallback, useEffect, useMemo, useRef, useState, createRef, memo } from 'react';
3
3
  import { jsx } from '@emotion/react';
4
4
  import { unstable_batchedUpdates as batchedUpdates } from 'react-dom';
5
- import { FormattedMessage } from 'react-intl-next';
5
+ import { FormattedMessage, useIntl } from 'react-intl-next';
6
6
  import { getEmojiVariation } from '../../api/EmojiRepository';
7
7
  import { supportsUploadFeature } from '../../api/EmojiResource';
8
8
  import { customCategory, defaultEmojiPickerSize, frequentCategory } from '../../util/constants';
9
9
  import { containsEmojiId, isPromise /*, isEmojiIdEqual, isEmojiLoaded*/, isEmojiDescription } from '../../util/type-helpers';
10
- import { SearchSort } from '../../types';
10
+ import { SearchSort, SearchSourceTypes } from '../../types';
11
11
  import { getToneEmoji } from '../../util/filters';
12
12
  import { uploadEmoji } from '../common/UploadEmoji';
13
13
  import { createRecordSelectionDefault } from '../common/RecordSelectionDefault';
@@ -16,8 +16,9 @@ import EmojiPickerFooter from './EmojiPickerFooter';
16
16
  import EmojiPickerList from './EmojiPickerList';
17
17
  import { createAndFireEventInElementsChannel, categoryClickedEvent, closedPickerEvent, deleteBeginEvent, deleteCancelEvent, deleteConfirmEvent, openedPickerEvent, pickerClickedEvent, pickerSearchedEvent, selectedFileEvent, uploadBeginButton, uploadCancelButton, uploadConfirmButton, toneSelectorClosedEvent, ufoExperiences } from '../../util/analytics';
18
18
  import { emojiPicker } from './styles';
19
- import { useDidMount } from '../hooks';
20
19
  import { useEmoji } from '../../hooks/useEmoji';
20
+ import { useIsMounted } from '../../hooks/useIsMounted';
21
+ import { messages } from '../i18n';
21
22
  const FREQUENTLY_USED_MAX = 16;
22
23
  const EmojiPickerComponent = ({
23
24
  onSelection,
@@ -26,6 +27,9 @@ const EmojiPickerComponent = ({
26
27
  createAnalyticsEvent,
27
28
  size = defaultEmojiPickerSize
28
29
  }) => {
30
+ const {
31
+ formatMessage
32
+ } = useIntl();
29
33
  const {
30
34
  emojiProvider,
31
35
  isUploadSupported
@@ -47,12 +51,11 @@ const EmojiPickerComponent = ({
47
51
  const emojiPickerList = useMemo(() => /*#__PURE__*/createRef(), []);
48
52
  const openTime = useRef(0);
49
53
  const isMounting = useRef(true);
50
- const didMount = useDidMount();
51
- const updateAfterDidMount = useRef(true);
52
54
  const previousEmojiProvider = useRef(emojiProvider);
53
55
  const currentUser = useMemo(() => {
54
56
  return emojiProvider.getCurrentUser();
55
57
  }, [emojiProvider]);
58
+ const isMounted = useIsMounted();
56
59
  const fireAnalytics = useCallback(analyticsEvent => {
57
60
  if (createAnalyticsEvent) {
58
61
  createAndFireEventInElementsChannel(analyticsEvent)(createAnalyticsEvent);
@@ -157,7 +160,11 @@ const EmojiPickerComponent = ({
157
160
  emojiToRender,
158
161
  searchEmoji: searchResults.emojis
159
162
  });
160
- }, [frequentlyUsedEmojis, query, setStateAfterEmojiChange]);
163
+ fireAnalytics(pickerSearchedEvent({
164
+ queryLength: searchQuery.length,
165
+ numMatches: searchResults.emojis.length
166
+ }));
167
+ }, [frequentlyUsedEmojis, query, setStateAfterEmojiChange, fireAnalytics]);
161
168
  const onProviderChange = useMemo(() => {
162
169
  return {
163
170
  result: onSearchResult
@@ -242,22 +249,22 @@ const EmojiPickerComponent = ({
242
249
  const onFileChooserClicked = useCallback(() => {
243
250
  fireAnalytics(selectedFileEvent());
244
251
  }, [fireAnalytics]);
252
+ const scrollToTopOfList = useCallback(() => {
253
+ var _emojiPickerList$curr;
254
+ (_emojiPickerList$curr = emojiPickerList.current) === null || _emojiPickerList$curr === void 0 ? void 0 : _emojiPickerList$curr.scrollToTop();
255
+ }, [emojiPickerList]);
245
256
  const onSearch = useCallback(searchQuery => {
246
257
  const options = {
247
- skinTone: selectedTone
258
+ skinTone: selectedTone,
259
+ source: SearchSourceTypes.PICKER
248
260
  };
249
- if (query) {
250
- ufoExperiences['emoji-searched'].start();
251
- ufoExperiences['emoji-searched'].addMetadata({
252
- queryLength: query.length,
253
- source: 'EmojiPickerComponent'
254
- });
255
- }
256
261
  if (searchQuery !== query) {
257
262
  setQuery(searchQuery);
263
+ // scroll to top when search, which is search results section
264
+ scrollToTopOfList();
258
265
  }
259
- updateEmojis(query, options);
260
- }, [query, selectedTone, updateEmojis]);
266
+ updateEmojis(searchQuery, options);
267
+ }, [query, selectedTone, updateEmojis, scrollToTopOfList]);
261
268
  const onOpenUpload = useCallback(() => {
262
269
  // Prime upload token so it's ready when the user adds
263
270
  if (supportsUploadFeature(emojiProvider)) {
@@ -269,12 +276,12 @@ const EmojiPickerComponent = ({
269
276
  });
270
277
  fireAnalytics(uploadBeginButton());
271
278
  }, [emojiProvider, fireAnalytics]);
272
- const scrollToEndOfList = useCallback(() => {
279
+ const scrollToUploadedEmoji = useCallback(() => {
273
280
  if (emojiPickerList.current) {
274
281
  // Wait a tick to ensure repaint and updated height for picker list
275
282
  window.setTimeout(() => {
276
- var _emojiPickerList$curr;
277
- (_emojiPickerList$curr = emojiPickerList.current) === null || _emojiPickerList$curr === void 0 ? void 0 : _emojiPickerList$curr.scrollToBottom();
283
+ var _emojiPickerList$curr2;
284
+ (_emojiPickerList$curr2 = emojiPickerList.current) === null || _emojiPickerList$curr2 === void 0 ? void 0 : _emojiPickerList$curr2.scrollToRecentlyUploaded();
278
285
  }, 0);
279
286
  }
280
287
  }, [emojiPickerList]);
@@ -291,10 +298,10 @@ const EmojiPickerComponent = ({
291
298
  setSelectedEmoji(emojiDescription);
292
299
  setUploading(false);
293
300
  });
294
- scrollToEndOfList();
301
+ scrollToUploadedEmoji();
295
302
  };
296
303
  uploadEmoji(upload, emojiProvider, errorSetter, onSuccess, fireAnalytics, retry);
297
- }, [emojiProvider, fireAnalytics, scrollToEndOfList]);
304
+ }, [emojiProvider, fireAnalytics, scrollToUploadedEmoji]);
298
305
  const onTriggerDelete = useCallback((_emojiId, emoji) => {
299
306
  if (_emojiId) {
300
307
  fireAnalytics(deleteBeginEvent({
@@ -343,11 +350,10 @@ const EmojiPickerComponent = ({
343
350
  }
344
351
  useEffect(() => {
345
352
  // componentDidMount logic
346
- if (didMount && updateAfterDidMount.current) {
353
+ if (!isMounted) {
347
354
  onComponentDidMount();
348
- updateAfterDidMount.current = false;
349
355
  }
350
- }, [didMount, onComponentDidMount]);
356
+ }, [onComponentDidMount, isMounted]);
351
357
  useEffect(() => {
352
358
  previousEmojiProvider.current.unsubscribe(onProviderChange);
353
359
  previousEmojiProvider.current = emojiProvider;
@@ -356,26 +362,6 @@ const EmojiPickerComponent = ({
356
362
  emojiProvider.unsubscribe(onProviderChange);
357
363
  };
358
364
  }, [emojiProvider, onProviderChange]);
359
- useEffect(() => {
360
- // We changed provider which means we subscribed to filter results for a new subscriber.
361
- // So we refresh the emoji display with onSearch and we do it here, after the new props have
362
- // been set since onSearch leads to filter being called on the current emojiProvider.
363
- // (Calling onSearch in a '...Will...' lifecycle method would lead to filter being called on
364
- // an emojiProvider we have already unsubscribed from)
365
- onSearch(query);
366
- }, [emojiProvider, onSearch, query]);
367
- useEffect(() => {
368
- // Fire analytics event whenever query changes
369
- fireAnalytics(pickerSearchedEvent({
370
- queryLength: query.length,
371
- numMatches: filteredEmojis.length
372
- }));
373
- ufoExperiences['emoji-searched'].success({
374
- metadata: {
375
- emojisLength: filteredEmojis.length
376
- }
377
- });
378
- }, [filteredEmojis.length, fireAnalytics, query]);
379
365
  useEffect(() => {
380
366
  if (!frequentlyUsedEmojis.length || query) {
381
367
  setFilteredEmojis(searchEmojis);
@@ -413,7 +399,10 @@ const EmojiPickerComponent = ({
413
399
  return jsx("div", {
414
400
  css: emojiPicker(showPreview, size),
415
401
  ref: onPickerRef,
416
- "data-emoji-picker-container": true
402
+ "data-emoji-picker-container": true,
403
+ role: "dialog",
404
+ "aria-label": formatMessage(messages.emojiPickerTitle),
405
+ "aria-modal": true
417
406
  }, jsx(CategorySelector, {
418
407
  activeCategoryId: activeCategory,
419
408
  dynamicCategories: dynamicCategories,
@@ -445,7 +434,8 @@ const EmojiPickerComponent = ({
445
434
  onCloseDelete: onCloseDelete,
446
435
  onFileChooserClicked: onFileChooserClicked,
447
436
  onOpenUpload: onOpenUpload,
448
- size: size
437
+ size: size,
438
+ activeCategoryId: activeCategory
449
439
  }), showPreview && jsx(EmojiPickerFooter, {
450
440
  selectedEmoji: selectedEmoji
451
441
  }));
@@ -1,35 +1,65 @@
1
1
  /** @jsx jsx */
2
2
  import { memo } from 'react';
3
3
  import { jsx } from '@emotion/react';
4
+ import { useIntl } from 'react-intl-next';
4
5
  import CachingEmoji from '../common/CachingEmoji';
5
6
  import { emojiItem, emojiPickerRow } from './styles';
7
+ import { useEmojiPickerListContext } from '../../hooks/useEmojiPickerListContext';
8
+ import { messages } from '../i18n';
6
9
  const EmojiPickerEmojiRow = ({
7
10
  emojis,
8
11
  onSelected,
9
12
  onMouseMove,
13
+ onFocus,
10
14
  title,
11
15
  showDelete,
12
- onDelete
13
- }) => jsx("div", {
14
- css: emojiPickerRow
15
- }, emojis.map(emoji => {
16
+ onDelete,
17
+ virtualItemContext
18
+ }) => {
16
19
  const {
17
- shortName,
18
- id
19
- } = emoji;
20
- const key = id ? `${id}-${title}` : `${shortName}-${title}`;
21
- return jsx("span", {
22
- css: emojiItem,
23
- key: key
24
- }, jsx(CachingEmoji, {
25
- emoji: emoji,
26
- selectOnHover: true,
27
- onSelected: onSelected,
28
- onMouseMove: onMouseMove,
29
- showDelete: showDelete,
30
- onDelete: onDelete,
31
- placeholderSize: 24,
32
- shouldBeInteractive: true
20
+ currentEmojisFocus,
21
+ setEmojisFocus
22
+ } = useEmojiPickerListContext();
23
+ const rowIndex = (virtualItemContext === null || virtualItemContext === void 0 ? void 0 : virtualItemContext.index) || 0;
24
+ const {
25
+ formatMessage
26
+ } = useIntl();
27
+ const handleFocus = index => (emojiId, emoji, event) => {
28
+ setEmojisFocus({
29
+ rowIndex,
30
+ columnIndex: index
31
+ });
32
+ onFocus && onFocus(emojiId, emoji, event);
33
+ };
34
+ return jsx("div", {
35
+ css: emojiPickerRow,
36
+ role: "presentation"
37
+ }, emojis.map((emoji, index) => {
38
+ const {
39
+ shortName,
40
+ id
41
+ } = emoji;
42
+ const key = id ? `${id}-${title}` : `${shortName}-${title}`;
43
+ const focus = currentEmojisFocus.rowIndex === rowIndex && currentEmojisFocus.columnIndex === index;
44
+ return jsx("span", {
45
+ css: emojiItem,
46
+ key: key,
47
+ role: "gridcell",
48
+ "aria-colindex": index + 1 // aria-colindex is 1 based
49
+ }, jsx(CachingEmoji, {
50
+ emoji: emoji,
51
+ selectOnHover: true,
52
+ onSelected: onSelected,
53
+ onMouseMove: onMouseMove,
54
+ onFocus: handleFocus(index),
55
+ showDelete: showDelete,
56
+ onDelete: onDelete,
57
+ placeholderSize: 24,
58
+ "data-focus-index": `${rowIndex}-${index}`,
59
+ tabIndex: focus ? 0 : -1,
60
+ "aria-roledescription": formatMessage(messages.emojiButtonRoleDescription),
61
+ shouldBeInteractive: true
62
+ }));
33
63
  }));
34
- }));
64
+ };
35
65
  export default /*#__PURE__*/memo(EmojiPickerEmojiRow);
@@ -1,8 +1,9 @@
1
1
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
2
  /** @jsx jsx */
3
- import React, { PureComponent } from 'react';
3
+ import { createRef, PureComponent } from 'react';
4
4
  import { jsx } from '@emotion/react';
5
- import { customCategory, defaultEmojiPickerSize, userCustomTitle } from '../../util/constants';
5
+ import VisuallyHidden from '@atlaskit/visually-hidden';
6
+ import { customCategory, defaultEmojiPickerSize, frequentCategory, searchCategory, userCustomTitle, yourUploadsCategory } from '../../util/constants';
6
7
  import { CategoryDescriptionMap } from './categories';
7
8
  import CategoryTracker from './CategoryTracker';
8
9
  import { sizes } from './EmojiPickerSizes';
@@ -11,25 +12,60 @@ import EmojiActions from '../common/EmojiActions';
11
12
  import { emojiPickerList } from './styles';
12
13
  import { emojiPickerHeightOffset } from './utils';
13
14
  import { VirtualList } from './VirtualList';
15
+ import { injectIntl } from 'react-intl-next';
16
+ import { messages } from '../i18n';
17
+ import { EmojiPickerListContextProvider } from '../../context/EmojiPickerListContext';
18
+
14
19
  /**
15
20
  * Test id for wrapper Emoji Picker List div
16
21
  */
17
22
  export const RENDER_EMOJI_PICKER_LIST_TESTID = 'render-emoji-picker-list';
18
23
  const categoryClassname = 'emoji-category';
19
24
  const byOrder = (orderableA, orderableB) => (orderableA.order || 0) - (orderableB.order || 0);
20
- export default class EmojiPickerVirtualList extends PureComponent {
25
+
26
+ /**
27
+ * TODO: have to use class component here as unit test is relying on ref.root. Will refactor this whole file to functional component in future
28
+ * ticket: COLLAB-2317
29
+ */
30
+
31
+ class EmojiPickerTabPanelInternal extends PureComponent {
32
+ render() {
33
+ const {
34
+ intl: {
35
+ formatMessage
36
+ },
37
+ children,
38
+ showSearchResults
39
+ } = this.props;
40
+ return jsx("div", {
41
+ ref: "root",
42
+ css: emojiPickerList,
43
+ "data-testid": RENDER_EMOJI_PICKER_LIST_TESTID,
44
+ id: RENDER_EMOJI_PICKER_LIST_TESTID,
45
+ role: "tabpanel",
46
+ "aria-label": formatMessage(messages.emojiPickerListPanel)
47
+ }, jsx(VisuallyHidden, {
48
+ id: "emoji-picker-table-description"
49
+ }, formatMessage(messages.emojiPickerGrid, {
50
+ showSearchResults
51
+ })), children);
52
+ }
53
+ }
54
+ const EmojiPickerTabPanel = injectIntl(EmojiPickerTabPanelInternal);
55
+ export default class EmojiPickerVirtualListInternal extends PureComponent {
21
56
  constructor(_props) {
22
57
  super(_props);
23
58
  _defineProperty(this, "virtualItems", []);
24
59
  _defineProperty(this, "categoryTracker", new CategoryTracker());
25
- _defineProperty(this, "onEmojiMouseEnter", (emojiId, emoji) => {
60
+ _defineProperty(this, "listRef", /*#__PURE__*/createRef());
61
+ _defineProperty(this, "onEmojiActive", (emojiId, emoji) => {
26
62
  if (this.props.onEmojiActive) {
27
63
  this.props.onEmojiActive(emojiId, emoji);
28
64
  }
29
65
  });
30
- _defineProperty(this, "onSearch", e => {
66
+ _defineProperty(this, "onSearch", value => {
31
67
  if (this.props.onSearch) {
32
- this.props.onSearch(e.target.value);
68
+ this.props.onSearch(value);
33
69
  }
34
70
  });
35
71
  _defineProperty(this, "buildVirtualItemFromGroup", group => {
@@ -48,12 +84,14 @@ export default class EmojiPickerVirtualList extends PureComponent {
48
84
  const rowEmojis = remainingEmojis.slice(0, sizes.emojiPerRow);
49
85
  remainingEmojis = remainingEmojis.slice(sizes.emojiPerRow);
50
86
  items.push(new EmojisRowItem({
87
+ category: group.category,
51
88
  emojis: rowEmojis,
52
89
  title: group.title,
53
90
  showDelete: group.title === userCustomTitle,
54
91
  onSelected: onEmojiSelected,
55
92
  onDelete: onEmojiDelete,
56
- onMouseMove: this.onEmojiMouseEnter
93
+ onMouseMove: this.onEmojiActive,
94
+ onFocus: this.onEmojiActive
57
95
  }));
58
96
  }
59
97
  return items;
@@ -73,7 +111,7 @@ export default class EmojiPickerVirtualList extends PureComponent {
73
111
  const search = CategoryDescriptionMap.SEARCH;
74
112
  // Only a single "result" category
75
113
  items = [...items, ...this.buildVirtualItemFromGroup({
76
- category: 'SEARCH',
114
+ category: searchCategory,
77
115
  title: search.name,
78
116
  emojis,
79
117
  order: search.order
@@ -87,22 +125,16 @@ export default class EmojiPickerVirtualList extends PureComponent {
87
125
  // by not passing it to irrelevant groups
88
126
  this.categoryTracker.add(group.emojis[0].category, items.length);
89
127
  items = [...items, ...this.buildVirtualItemFromGroup(group)];
128
+ if (group.category === yourUploadsCategory) {
129
+ this.lastYourUploadsRow = items.length - 1;
130
+ }
131
+ });
132
+ this.onRowsRendered({
133
+ startIndex: 0
90
134
  });
91
135
  }
92
136
  }
93
- const rowCountChanged = this.virtualItems.length !== items.length;
94
137
  this.virtualItems = items;
95
- const list = this.refs.list;
96
- if (!rowCountChanged && list) {
97
- // Row count has not changed, so need to tell list to rerender.
98
- list.forceUpdateGrid();
99
- }
100
- if (!query && list) {
101
- // VirtualList can apply stale heights since it performs a shallow
102
- // compare to check if the list has changed. Should manually recompute
103
- // row heights for the case when frequent category come in later
104
- list.recomputeRowHeights();
105
- }
106
138
  });
107
139
  _defineProperty(this, "addToCategoryMap", (categoryToGroupMap, emoji, category) => {
108
140
  if (!categoryToGroupMap[category]) {
@@ -121,46 +153,54 @@ export default class EmojiPickerVirtualList extends PureComponent {
121
153
  this.addToCategoryMap(categoryToGroupMap, emoji, emoji.category);
122
154
  // separate user emojis
123
155
  if (emoji.category === customCategory && currentUser && emoji.creatorUserId === currentUser.id) {
124
- this.addToCategoryMap(categoryToGroupMap, emoji, 'USER_CUSTOM');
156
+ this.addToCategoryMap(categoryToGroupMap, emoji, yourUploadsCategory);
125
157
  }
126
158
  return categoryToGroupMap;
127
159
  });
128
160
  _defineProperty(this, "buildEmojiGroupedByCategory", (emojis, currentUser) => {
129
161
  const categoryToGroupMap = emojis.reduce(this.groupByCategory(currentUser), {});
130
162
  this.allEmojiGroups = Object.keys(categoryToGroupMap).map(key => categoryToGroupMap[key]).map(group => {
131
- if (group.category !== 'FREQUENT') {
163
+ if (group.category !== frequentCategory) {
132
164
  group.emojis.sort(byOrder);
133
165
  }
134
166
  return group;
135
167
  }).sort(byOrder);
136
168
  });
137
- _defineProperty(this, "repaintList", () => {
138
- if (this.refs.root) {
139
- const root = this.refs.root;
140
- const display = root.style.display;
141
- root.style.display = 'none';
142
-
143
- // we need to access offset to force repaint
144
- // eslint-disable-next-line no-unused-expressions
145
- root.offsetHeight;
146
- root.style.display = display;
169
+ _defineProperty(this, "findCategoryToActivate", row => {
170
+ let category = null;
171
+ if (row instanceof CategoryHeadingItem) {
172
+ category = row.props.id;
173
+ } else if (row instanceof EmojisRowItem) {
174
+ category = row.props.category;
147
175
  }
176
+ // your uploads is rendered, take it as upload category, so could be highlighted in category selector
177
+ if (category === yourUploadsCategory) {
178
+ return customCategory;
179
+ // search results is rendered, return null so won't be highlighted for category selector
180
+ } else if (category === searchCategory) {
181
+ return null;
182
+ }
183
+ return category;
148
184
  });
149
- _defineProperty(this, "checkCategoryIdChange", indexes => {
185
+ _defineProperty(this, "onRowsRendered", indexes => {
150
186
  const {
151
187
  startIndex
152
188
  } = indexes;
189
+ const rowItem = this.virtualItems[startIndex];
190
+ const list = this.listRef.current;
153
191
 
154
- // FS-1844 Fix a rendering problem when scrolling to the top
155
- if (startIndex === 0) {
156
- this.repaintList();
192
+ // update tabIndex manually, startIndex is not 0 based here
193
+ if (rowItem instanceof CategoryHeadingItem) {
194
+ // if top of row rendered is category heading, update tabIndex for the next emoji row
195
+ list === null || list === void 0 ? void 0 : list.updateFocusIndex(startIndex + 1);
196
+ } else if (rowItem instanceof EmojisRowItem) {
197
+ // if top of row rendered is emoji row, update it's tabIndex.
198
+ list === null || list === void 0 ? void 0 : list.updateFocusIndex(startIndex);
157
199
  }
158
200
  if (!this.props.query) {
159
201
  // Calculate category in view - only relevant if categories shown, i.e. no query
160
- const list = this.refs.list;
161
- const currentCategory = this.categoryTracker.findNearestCategoryAbove(startIndex, list);
162
- if (currentCategory && this.activeCategoryId !== currentCategory) {
163
- this.activeCategoryId = currentCategory;
202
+ const currentCategory = this.findCategoryToActivate(rowItem);
203
+ if (currentCategory !== null && this.props.activeCategoryId !== currentCategory) {
164
204
  if (this.props.onCategoryActivated) {
165
205
  this.props.onCategoryActivated(currentCategory);
166
206
  }
@@ -174,6 +214,7 @@ export default class EmojiPickerVirtualList extends PureComponent {
174
214
  _defineProperty(this, "renderRow", context => {
175
215
  return virtualItemRenderer(this.virtualItems, context);
176
216
  });
217
+ this.lastYourUploadsRow = 0;
177
218
  this.buildEmojiGroupedByCategory(_props.emojis, _props.currentUser);
178
219
  this.buildVirtualItems(_props, this.state);
179
220
  }
@@ -191,12 +232,24 @@ export default class EmojiPickerVirtualList extends PureComponent {
191
232
  */
192
233
  reveal(category) {
193
234
  const row = this.categoryTracker.getRow(category);
194
- const list = this.refs.list;
195
- list.scrollToRow(row);
235
+ this.scrollToRow(row);
196
236
  }
197
237
  scrollToBottom() {
198
- const list = this.refs.list;
199
- list.scrollToRow(this.virtualItems.length);
238
+ this.scrollToRow(this.virtualItems.length);
239
+ }
240
+ scrollToTop() {
241
+ this.scrollToRow(0);
242
+ }
243
+ scrollToRow(index) {
244
+ var _this$listRef$current;
245
+ (_this$listRef$current = this.listRef.current) === null || _this$listRef$current === void 0 ? void 0 : _this$listRef$current.scrollToRow(index);
246
+ }
247
+ scrollToRecentlyUploaded() {
248
+ const row = this.lastYourUploadsRow;
249
+ if (row > 0) {
250
+ var _this$listRef$current2;
251
+ (_this$listRef$current2 = this.listRef.current) === null || _this$listRef$current2 === void 0 ? void 0 : _this$listRef$current2.scrollToRowAndFocusLastEmoji(this.lastYourUploadsRow);
252
+ }
200
253
  }
201
254
  render() {
202
255
  const {
@@ -216,13 +269,12 @@ export default class EmojiPickerVirtualList extends PureComponent {
216
269
  onDeleteEmoji,
217
270
  onFileChooserClicked,
218
271
  onOpenUpload,
219
- size = defaultEmojiPickerSize
272
+ size = defaultEmojiPickerSize,
273
+ emojis
220
274
  } = this.props;
221
275
  const virtualListHeight = sizes.listHeight + emojiPickerHeightOffset(size);
222
- return jsx("div", {
223
- ref: "root",
224
- css: emojiPickerList,
225
- "data-testid": RENDER_EMOJI_PICKER_LIST_TESTID
276
+ return jsx(EmojiPickerTabPanel, {
277
+ showSearchResults: !!query
226
278
  }, jsx(EmojiActions, {
227
279
  selectedTone: selectedTone,
228
280
  onToneSelected: onToneSelected,
@@ -240,9 +292,15 @@ export default class EmojiPickerVirtualList extends PureComponent {
240
292
  onFileChooserClicked: onFileChooserClicked,
241
293
  onOpenUpload: onOpenUpload,
242
294
  query: query,
243
- onChange: this.onSearch
244
- }), jsx(VirtualList, {
245
- ref: "list",
295
+ onChange: this.onSearch,
296
+ resultsCount: emojis.length
297
+ }), jsx(EmojiPickerListContextProvider, {
298
+ initialEmojisFocus: {
299
+ rowIndex: 1,
300
+ columnIndex: 0
301
+ }
302
+ }, jsx(VirtualList, {
303
+ ref: this.listRef,
246
304
  height: virtualListHeight,
247
305
  overscanRowCount: 10,
248
306
  rowCount: this.virtualItems.length,
@@ -250,11 +308,11 @@ export default class EmojiPickerVirtualList extends PureComponent {
250
308
  rowRenderer: this.renderRow,
251
309
  scrollToAlignment: "start",
252
310
  width: sizes.listWidth,
253
- onRowsRendered: this.checkCategoryIdChange
254
- }));
311
+ onRowsRendered: this.onRowsRendered
312
+ })));
255
313
  }
256
314
  }
257
- _defineProperty(EmojiPickerVirtualList, "defaultProps", {
315
+ _defineProperty(EmojiPickerVirtualListInternal, "defaultProps", {
258
316
  onEmojiSelected: () => {},
259
317
  onEmojiActive: () => {},
260
318
  onEmojiDelete: () => {},