@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
@@ -22,8 +22,8 @@ export var messages = defineMessages({
22
22
  },
23
23
  emojiPlaceholder: {
24
24
  id: 'fabric.emoji.placeholder',
25
- defaultMessage: 'Emoji name',
26
- description: 'Placeholder for emoji'
25
+ defaultMessage: 'e.g. hello',
26
+ description: 'Placeholder for emoji that provides an example for emoji name'
27
27
  },
28
28
  emojiNameAriaLabel: {
29
29
  id: 'fabric.emoji.name.ariaLabel',
@@ -42,9 +42,14 @@ export var messages = defineMessages({
42
42
  },
43
43
  emojiSelectSkinToneButtonAriaLabelText: {
44
44
  id: 'fabric.emoji.select.skin.tone.ariaLabel',
45
- defaultMessage: 'Select skin tone, {selectedTone}',
45
+ defaultMessage: 'Choose your skin tone, {selectedTone} selected',
46
46
  description: 'Message indicating the purpose of the skin tone selection button and the selected tone'
47
47
  },
48
+ emojiSelectSkinToneListAriaLabelText: {
49
+ id: 'fabric.emoji.select.skin.list.ariaLabel',
50
+ defaultMessage: 'Skin tone selector',
51
+ description: 'Message indicating the purpose of the skin tone list and make user to choose one tone'
52
+ },
48
53
  emojiImageRequirements: {
49
54
  id: 'fabric.emoji.image.requirements',
50
55
  defaultMessage: 'JPG, PNG or GIF. Max size 1 MB.',
@@ -82,9 +87,24 @@ export var messages = defineMessages({
82
87
  },
83
88
  searchLabel: {
84
89
  id: 'fabric.emoji.search.label',
85
- defaultMessage: 'Search emoji',
90
+ defaultMessage: 'Emoji name',
86
91
  description: 'verb - button label to search'
87
92
  },
93
+ searchResultsStatus: {
94
+ id: 'fabric.emoji.search.status',
95
+ defaultMessage: 'Found {count} emojis',
96
+ description: 'search results status for screenreader to read out'
97
+ },
98
+ searchResultsStatusSeeAll: {
99
+ id: 'fabric.emoji.search.status',
100
+ defaultMessage: 'Seeing all emojis',
101
+ description: 'search results status when no search query for screenreader to read out'
102
+ },
103
+ categoriesSelectorLabel: {
104
+ id: 'fabric.emoji.categories.label',
105
+ defaultMessage: 'Choose a emoji category',
106
+ description: 'Aria label for Emoji categories selector at the top of emoji picker'
107
+ },
88
108
  categoriesSearchResults: {
89
109
  id: 'fabric.emoji.categories.search.results',
90
110
  defaultMessage: 'Search results',
@@ -169,5 +189,25 @@ export var messages = defineMessages({
169
189
  id: 'fabric.emoji.error.image.too.big',
170
190
  defaultMessage: 'Selected image is more than 1 MB',
171
191
  description: 'Error message for image too big, beyond the size limit'
192
+ },
193
+ emojiPickerTitle: {
194
+ id: 'fabric.emoji.picker',
195
+ defaultMessage: 'Emoji picker',
196
+ description: 'Aria label for emoji picker'
197
+ },
198
+ emojiPickerListPanel: {
199
+ id: 'fabric.emoji.pickerlist.tabpanel',
200
+ defaultMessage: 'Emojis actions and list panel',
201
+ description: 'Aria lable for tabpanel of emoji picker, which shows emojis actions and list'
202
+ },
203
+ emojiPickerGrid: {
204
+ id: 'fabric.emoji.pickerlist.grid',
205
+ defaultMessage: '{showSearchResults, select, true{Search results} other{Emojis}}',
206
+ description: "Aria label for emoji picker grid, showSearchResults is a boolean variable, message will be \"Entering Emojis table\", or \"Leaving Emojis\""
207
+ },
208
+ emojiButtonRoleDescription: {
209
+ id: 'fabric.emoji.emojipicker.emoi.roledescription',
210
+ defaultMessage: 'emoji button',
211
+ description: "Aria roledescription for emoji button, used in emoji picker."
172
212
  }
173
213
  });
@@ -1,21 +1,15 @@
1
- import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
2
- import _createClass from "@babel/runtime/helpers/createClass";
3
- import _assertThisInitialized from "@babel/runtime/helpers/assertThisInitialized";
4
- import _inherits from "@babel/runtime/helpers/inherits";
5
- import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn";
6
- import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf";
7
- import _defineProperty from "@babel/runtime/helpers/defineProperty";
8
- 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); }; }
9
- 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; } }
1
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
10
2
  /** @jsx jsx */
11
- import React from 'react';
3
+ import React, { useCallback, useEffect, useRef, useState } from 'react';
12
4
  import { jsx } from '@emotion/react';
13
- import { PureComponent } from 'react';
14
- import { injectIntl } from 'react-intl-next';
15
- import { defaultCategories } from '../../util/constants';
5
+ import { useIntl } from 'react-intl-next';
6
+ import Tooltip from '@atlaskit/tooltip';
7
+ import { CATEGORYSELECTOR_KEYBOARD_KEYS_SUPPORTED, defaultCategories, KeyboardKeys } from '../../util/constants';
16
8
  import { messages } from '../i18n';
17
9
  import { CategoryDescriptionMap } from './categories';
18
- import { active, categorySelector, disable, categoryStyles } from './styles';
10
+ import { active, categorySelector, disable, categoryStyles, categorySelectorTablist } from './styles';
11
+ import { usePrevious } from '../../hooks/usePrevious';
12
+ import { RENDER_EMOJI_PICKER_LIST_TESTID } from './EmojiPickerList';
19
13
  export var sortCategories = function sortCategories(c1, c2) {
20
14
  return CategoryDescriptionMap[c1].order - CategoryDescriptionMap[c2].order;
21
15
  };
@@ -31,94 +25,119 @@ export var categorySelectorComponentTestId = 'category-selector-component';
31
25
  export var categorySelectorCategoryTestId = function categorySelectorCategoryTestId(categoryId) {
32
26
  return "category-selector-".concat(categoryId);
33
27
  };
34
- var CategorySelector = /*#__PURE__*/function (_PureComponent) {
35
- _inherits(CategorySelector, _PureComponent);
36
- var _super = _createSuper(CategorySelector);
37
- function CategorySelector(props) {
38
- var _this;
39
- _classCallCheck(this, CategorySelector);
40
- _this = _super.call(this, props);
41
- _defineProperty(_assertThisInitialized(_this), "onClick", function (event) {
42
- var _this$props = _this.props,
43
- onCategorySelected = _this$props.onCategorySelected,
44
- disableCategories = _this$props.disableCategories;
28
+ var CategorySelector = function CategorySelector(props) {
29
+ var disableCategories = props.disableCategories,
30
+ dynamicCategories = props.dynamicCategories,
31
+ activeCategoryId = props.activeCategoryId,
32
+ onCategorySelected = props.onCategorySelected;
33
+ var _useState = useState(addNewCategories(defaultCategories, dynamicCategories)),
34
+ _useState2 = _slicedToArray(_useState, 2),
35
+ categories = _useState2[0],
36
+ setCategories = _useState2[1];
37
+ var _useState3 = useState(0),
38
+ _useState4 = _slicedToArray(_useState3, 2),
39
+ currentFocus = _useState4[0],
40
+ setCurrentFocus = _useState4[1];
41
+ var categoryRef = useRef(null);
42
+ var prevDynamicCategories = usePrevious(dynamicCategories);
43
+ var _useIntl = useIntl(),
44
+ formatMessage = _useIntl.formatMessage;
45
+ var updateCategories = useCallback(function () {
46
+ var newCategories = addNewCategories(defaultCategories, dynamicCategories);
47
+ setCategories(newCategories);
48
+ }, [dynamicCategories]);
49
+ useEffect(function () {
50
+ if (prevDynamicCategories !== dynamicCategories) {
51
+ updateCategories();
52
+ }
53
+ }, [prevDynamicCategories, dynamicCategories, updateCategories]);
54
+ var focusCategory = useCallback(function (index) {
55
+ var _categoryRef$current;
56
+ var categoryToFocus = (_categoryRef$current = categoryRef.current) === null || _categoryRef$current === void 0 ? void 0 : _categoryRef$current.querySelector("[data-focus-index=\"".concat(index, "\"]"));
57
+ categoryToFocus && categoryToFocus.focus();
58
+ setCurrentFocus(index);
59
+ }, [categoryRef, setCurrentFocus]);
60
+ var handleKeyDown = function handleKeyDown(e) {
61
+ if (!CATEGORYSELECTOR_KEYBOARD_KEYS_SUPPORTED.includes(e.key)) {
62
+ return;
63
+ }
64
+ e.preventDefault();
65
+ var lastCategoryIndex = categories.length - 1;
66
+ switch (e.key) {
67
+ // navigate to the right category
68
+ case KeyboardKeys.ArrowRight:
69
+ focusCategory(currentFocus === lastCategoryIndex ? 0 : currentFocus + 1);
70
+ break;
71
+ // navigate to the left category
72
+ case KeyboardKeys.ArrowLeft:
73
+ focusCategory(currentFocus === 0 ? lastCategoryIndex : currentFocus - 1);
74
+ break;
75
+ // navigate to the first category
76
+ case KeyboardKeys.Home:
77
+ focusCategory(0);
78
+ break;
79
+ // navigate to the last category
80
+ case KeyboardKeys.End:
81
+ focusCategory(lastCategoryIndex);
82
+ break;
83
+ }
84
+ };
85
+ var handleClick = function handleClick(categoryId, index) {
86
+ return function (event) {
45
87
  if (disableCategories) {
46
88
  event.preventDefault();
47
89
  return;
48
90
  }
49
- var categoryId = event.currentTarget.getAttribute('data-category-id');
50
91
  if (onCategorySelected) {
51
92
  onCategorySelected(categoryId);
52
93
  }
53
- });
54
- var dynamicCategories = props.dynamicCategories;
55
- var categories = defaultCategories;
56
- if (dynamicCategories) {
57
- categories = addNewCategories(categories, dynamicCategories);
58
- }
59
- _this.state = {
60
- categories: categories
94
+ setCurrentFocus(index);
61
95
  };
62
- return _this;
63
- }
64
- _createClass(CategorySelector, [{
65
- key: "UNSAFE_componentWillUpdate",
66
- value: function UNSAFE_componentWillUpdate(nextProps) {
67
- if (this.props.dynamicCategories !== nextProps.dynamicCategories) {
68
- this.setState({
69
- categories: addNewCategories(defaultCategories, nextProps.dynamicCategories)
70
- });
96
+ };
97
+ var categoriesSection;
98
+ if (categories) {
99
+ categoriesSection = jsx("div", {
100
+ role: "tablist",
101
+ "aria-label": formatMessage(messages.categoriesSelectorLabel),
102
+ "data-testid": categorySelectorComponentTestId,
103
+ ref: categoryRef,
104
+ css: categorySelectorTablist
105
+ }, categories.map(function (categoryId, index) {
106
+ var category = CategoryDescriptionMap[categoryId];
107
+ var categoryClasses = [categoryStyles];
108
+ if (categoryId === activeCategoryId) {
109
+ categoryClasses.push(active);
71
110
  }
72
- }
73
- }, {
74
- key: "render",
75
- value: function render() {
76
- var _this2 = this;
77
- var _this$props2 = this.props,
78
- disableCategories = _this$props2.disableCategories,
79
- intl = _this$props2.intl;
80
- var categories = this.state.categories;
81
- var categoriesSection;
82
- if (categories) {
83
- var formatMessage = intl.formatMessage;
84
- categoriesSection = jsx("ul", {
85
- "data-testid": categorySelectorComponentTestId
86
- }, categories.map(function (categoryId) {
87
- var category = CategoryDescriptionMap[categoryId];
88
- var categoryClasses = [categoryStyles];
89
- if (categoryId === _this2.props.activeCategoryId) {
90
- categoryClasses.push(active);
91
- }
92
- if (disableCategories) {
93
- categoryClasses.push(disable);
94
- }
95
- var Icon = category.icon;
96
- var categoryName = formatMessage(messages[category.name]);
97
- return jsx("li", {
98
- key: category.id
99
- }, jsx("button", {
100
- "aria-label": categoryName,
101
- "data-category-id": category.id,
102
- disabled: disableCategories,
103
- css: categoryClasses,
104
- onClick: _this2.onClick,
105
- title: categoryName,
106
- type: "button",
107
- "data-testid": categorySelectorCategoryTestId(categoryId)
108
- }, jsx(Icon, {
109
- label: categoryName
110
- })));
111
- }));
111
+ if (disableCategories) {
112
+ categoryClasses.push(disable);
112
113
  }
113
- return jsx("div", {
114
- css: categorySelector
115
- }, categoriesSection);
116
- }
117
- }]);
118
- return CategorySelector;
119
- }(PureComponent);
120
- _defineProperty(CategorySelector, "defaultProps", {
121
- onCategorySelected: function onCategorySelected() {},
122
- dynamicCategories: []
123
- });
124
- export default injectIntl(CategorySelector);
114
+ var Icon = category.icon;
115
+ var categoryName = formatMessage(messages[category.name]);
116
+ return jsx(Tooltip, {
117
+ content: categoryName,
118
+ position: "bottom",
119
+ key: category.id
120
+ }, jsx("button", {
121
+ type: "button",
122
+ id: "category-selector-".concat(category.id),
123
+ "data-focus-index": index,
124
+ "aria-label": categoryName,
125
+ "aria-controls": currentFocus === index ? RENDER_EMOJI_PICKER_LIST_TESTID : undefined,
126
+ "aria-selected": currentFocus === index ? true : false,
127
+ css: categoryClasses,
128
+ disabled: disableCategories,
129
+ onClick: handleClick(categoryId, index),
130
+ "data-testid": categorySelectorCategoryTestId(categoryId),
131
+ tabIndex: currentFocus === index ? 0 : -1,
132
+ onKeyDown: handleKeyDown,
133
+ role: "tab"
134
+ }, jsx(Icon, {
135
+ label: categoryName
136
+ })));
137
+ }));
138
+ }
139
+ return jsx("div", {
140
+ css: categorySelector
141
+ }, categoriesSection);
142
+ };
143
+ export default CategorySelector;
@@ -30,34 +30,6 @@ var CategoryTracker = /*#__PURE__*/function () {
30
30
  value: function getRow(category) {
31
31
  return this.categoryToRow.get(category);
32
32
  }
33
- }, {
34
- key: "findNearestCategoryAbove",
35
- value: function findNearestCategoryAbove(startIndex, list) {
36
- var rows = Array.from(this.rowToCategory.keys()).sort(function (a, b) {
37
- return a - b;
38
- });
39
- if (rows.length === 0) {
40
- return;
41
- }
42
-
43
- // Return first category if list not yet rendered
44
- // or the top row is above the first category
45
- if (!list || rows[0] > startIndex) {
46
- return this.rowToCategory.get(rows[0]);
47
- }
48
- var bounds = [0, rows.length - 1];
49
- var index = Math.floor(rows.length / 2);
50
- while (rows[index] !== startIndex && bounds[0] < bounds[1]) {
51
- if (rows[index] > startIndex) {
52
- bounds[1] = Math.max(index - 1, 0);
53
- } else {
54
- bounds[0] = index + 1;
55
- }
56
- index = Math.floor((bounds[0] + bounds[1]) / 2);
57
- }
58
- var headerRow = rows[rows[index] > startIndex ? Math.max(index - 1, 0) : index];
59
- return this.rowToCategory.get(headerRow);
60
- }
61
33
  }]);
62
34
  return CategoryTracker;
63
35
  }();
@@ -5,7 +5,6 @@ import { FormattedMessage } from 'react-intl-next';
5
5
  import { isMessagesKey } from '../../util/type-helpers';
6
6
  import { messages } from '../i18n';
7
7
  import { emojiCategoryTitle } from './styles';
8
-
9
8
  /**
10
9
  * Test id for wrapper Emoji Picker List div
11
10
  */
@@ -18,7 +17,8 @@ var EmojiPickerCategoryHeading = function EmojiPickerCategoryHeading(_ref) {
18
17
  id: id,
19
18
  "data-category-id": id,
20
19
  className: className,
21
- "data-testid": RENDER_EMOJI_PICKER_CATEGORY_HEADING_TESTID
20
+ "data-testid": RENDER_EMOJI_PICKER_CATEGORY_HEADING_TESTID,
21
+ role: "rowheader"
22
22
  }, jsx("div", {
23
23
  css: emojiCategoryTitle
24
24
  }, isMessagesKey(title) ? jsx(FormattedMessage, messages[title]) : title));
@@ -7,12 +7,12 @@ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { va
7
7
  import { useCallback, useEffect, useMemo, useRef, useState, createRef, memo } from 'react';
8
8
  import { jsx } from '@emotion/react';
9
9
  import { unstable_batchedUpdates as batchedUpdates } from 'react-dom';
10
- import { FormattedMessage } from 'react-intl-next';
10
+ import { FormattedMessage, useIntl } from 'react-intl-next';
11
11
  import { getEmojiVariation } from '../../api/EmojiRepository';
12
12
  import { supportsUploadFeature } from '../../api/EmojiResource';
13
13
  import { customCategory, defaultEmojiPickerSize, frequentCategory } from '../../util/constants';
14
14
  import { containsEmojiId, isPromise /*, isEmojiIdEqual, isEmojiLoaded*/, isEmojiDescription } from '../../util/type-helpers';
15
- import { SearchSort } from '../../types';
15
+ import { SearchSort, SearchSourceTypes } from '../../types';
16
16
  import { getToneEmoji } from '../../util/filters';
17
17
  import { uploadEmoji } from '../common/UploadEmoji';
18
18
  import { createRecordSelectionDefault } from '../common/RecordSelectionDefault';
@@ -21,8 +21,9 @@ import EmojiPickerFooter from './EmojiPickerFooter';
21
21
  import EmojiPickerList from './EmojiPickerList';
22
22
  import { createAndFireEventInElementsChannel, categoryClickedEvent, closedPickerEvent, deleteBeginEvent, deleteCancelEvent, deleteConfirmEvent, openedPickerEvent, pickerClickedEvent, pickerSearchedEvent, selectedFileEvent, uploadBeginButton, uploadCancelButton, uploadConfirmButton, toneSelectorClosedEvent, ufoExperiences } from '../../util/analytics';
23
23
  import { emojiPicker } from './styles';
24
- import { useDidMount } from '../hooks';
25
24
  import { useEmoji } from '../../hooks/useEmoji';
25
+ import { useIsMounted } from '../../hooks/useIsMounted';
26
+ import { messages } from '../i18n';
26
27
  var FREQUENTLY_USED_MAX = 16;
27
28
  var EmojiPickerComponent = function EmojiPickerComponent(_ref) {
28
29
  var onSelection = _ref.onSelection,
@@ -31,6 +32,8 @@ var EmojiPickerComponent = function EmojiPickerComponent(_ref) {
31
32
  createAnalyticsEvent = _ref.createAnalyticsEvent,
32
33
  _ref$size = _ref.size,
33
34
  size = _ref$size === void 0 ? defaultEmojiPickerSize : _ref$size;
35
+ var _useIntl = useIntl(),
36
+ formatMessage = _useIntl.formatMessage;
34
37
  var _useEmoji = useEmoji(),
35
38
  emojiProvider = _useEmoji.emojiProvider,
36
39
  isUploadSupported = _useEmoji.isUploadSupported;
@@ -95,12 +98,11 @@ var EmojiPickerComponent = function EmojiPickerComponent(_ref) {
95
98
  }, []);
96
99
  var openTime = useRef(0);
97
100
  var isMounting = useRef(true);
98
- var didMount = useDidMount();
99
- var updateAfterDidMount = useRef(true);
100
101
  var previousEmojiProvider = useRef(emojiProvider);
101
102
  var currentUser = useMemo(function () {
102
103
  return emojiProvider.getCurrentUser();
103
104
  }, [emojiProvider]);
105
+ var isMounted = useIsMounted();
104
106
  var fireAnalytics = useCallback(function (analyticsEvent) {
105
107
  if (createAnalyticsEvent) {
106
108
  createAndFireEventInElementsChannel(analyticsEvent)(createAnalyticsEvent);
@@ -204,7 +206,11 @@ var EmojiPickerComponent = function EmojiPickerComponent(_ref) {
204
206
  emojiToRender: emojiToRender,
205
207
  searchEmoji: searchResults.emojis
206
208
  });
207
- }, [frequentlyUsedEmojis, query, setStateAfterEmojiChange]);
209
+ fireAnalytics(pickerSearchedEvent({
210
+ queryLength: searchQuery.length,
211
+ numMatches: searchResults.emojis.length
212
+ }));
213
+ }, [frequentlyUsedEmojis, query, setStateAfterEmojiChange, fireAnalytics]);
208
214
  var onProviderChange = useMemo(function () {
209
215
  return {
210
216
  result: onSearchResult
@@ -294,22 +300,22 @@ var EmojiPickerComponent = function EmojiPickerComponent(_ref) {
294
300
  var onFileChooserClicked = useCallback(function () {
295
301
  fireAnalytics(selectedFileEvent());
296
302
  }, [fireAnalytics]);
303
+ var scrollToTopOfList = useCallback(function () {
304
+ var _emojiPickerList$curr;
305
+ (_emojiPickerList$curr = emojiPickerList.current) === null || _emojiPickerList$curr === void 0 ? void 0 : _emojiPickerList$curr.scrollToTop();
306
+ }, [emojiPickerList]);
297
307
  var onSearch = useCallback(function (searchQuery) {
298
308
  var options = {
299
- skinTone: selectedTone
309
+ skinTone: selectedTone,
310
+ source: SearchSourceTypes.PICKER
300
311
  };
301
- if (query) {
302
- ufoExperiences['emoji-searched'].start();
303
- ufoExperiences['emoji-searched'].addMetadata({
304
- queryLength: query.length,
305
- source: 'EmojiPickerComponent'
306
- });
307
- }
308
312
  if (searchQuery !== query) {
309
313
  setQuery(searchQuery);
314
+ // scroll to top when search, which is search results section
315
+ scrollToTopOfList();
310
316
  }
311
- updateEmojis(query, options);
312
- }, [query, selectedTone, updateEmojis]);
317
+ updateEmojis(searchQuery, options);
318
+ }, [query, selectedTone, updateEmojis, scrollToTopOfList]);
313
319
  var onOpenUpload = useCallback(function () {
314
320
  // Prime upload token so it's ready when the user adds
315
321
  if (supportsUploadFeature(emojiProvider)) {
@@ -321,12 +327,12 @@ var EmojiPickerComponent = function EmojiPickerComponent(_ref) {
321
327
  });
322
328
  fireAnalytics(uploadBeginButton());
323
329
  }, [emojiProvider, fireAnalytics]);
324
- var scrollToEndOfList = useCallback(function () {
330
+ var scrollToUploadedEmoji = useCallback(function () {
325
331
  if (emojiPickerList.current) {
326
332
  // Wait a tick to ensure repaint and updated height for picker list
327
333
  window.setTimeout(function () {
328
- var _emojiPickerList$curr;
329
- (_emojiPickerList$curr = emojiPickerList.current) === null || _emojiPickerList$curr === void 0 ? void 0 : _emojiPickerList$curr.scrollToBottom();
334
+ var _emojiPickerList$curr2;
335
+ (_emojiPickerList$curr2 = emojiPickerList.current) === null || _emojiPickerList$curr2 === void 0 ? void 0 : _emojiPickerList$curr2.scrollToRecentlyUploaded();
330
336
  }, 0);
331
337
  }
332
338
  }, [emojiPickerList]);
@@ -343,10 +349,10 @@ var EmojiPickerComponent = function EmojiPickerComponent(_ref) {
343
349
  setSelectedEmoji(emojiDescription);
344
350
  setUploading(false);
345
351
  });
346
- scrollToEndOfList();
352
+ scrollToUploadedEmoji();
347
353
  };
348
354
  uploadEmoji(upload, emojiProvider, errorSetter, onSuccess, fireAnalytics, retry);
349
- }, [emojiProvider, fireAnalytics, scrollToEndOfList]);
355
+ }, [emojiProvider, fireAnalytics, scrollToUploadedEmoji]);
350
356
  var onTriggerDelete = useCallback(function (_emojiId, emoji) {
351
357
  if (_emojiId) {
352
358
  fireAnalytics(deleteBeginEvent({
@@ -397,11 +403,10 @@ var EmojiPickerComponent = function EmojiPickerComponent(_ref) {
397
403
  }
398
404
  useEffect(function () {
399
405
  // componentDidMount logic
400
- if (didMount && updateAfterDidMount.current) {
406
+ if (!isMounted) {
401
407
  onComponentDidMount();
402
- updateAfterDidMount.current = false;
403
408
  }
404
- }, [didMount, onComponentDidMount]);
409
+ }, [onComponentDidMount, isMounted]);
405
410
  useEffect(function () {
406
411
  previousEmojiProvider.current.unsubscribe(onProviderChange);
407
412
  previousEmojiProvider.current = emojiProvider;
@@ -410,26 +415,6 @@ var EmojiPickerComponent = function EmojiPickerComponent(_ref) {
410
415
  emojiProvider.unsubscribe(onProviderChange);
411
416
  };
412
417
  }, [emojiProvider, onProviderChange]);
413
- useEffect(function () {
414
- // We changed provider which means we subscribed to filter results for a new subscriber.
415
- // So we refresh the emoji display with onSearch and we do it here, after the new props have
416
- // been set since onSearch leads to filter being called on the current emojiProvider.
417
- // (Calling onSearch in a '...Will...' lifecycle method would lead to filter being called on
418
- // an emojiProvider we have already unsubscribed from)
419
- onSearch(query);
420
- }, [emojiProvider, onSearch, query]);
421
- useEffect(function () {
422
- // Fire analytics event whenever query changes
423
- fireAnalytics(pickerSearchedEvent({
424
- queryLength: query.length,
425
- numMatches: filteredEmojis.length
426
- }));
427
- ufoExperiences['emoji-searched'].success({
428
- metadata: {
429
- emojisLength: filteredEmojis.length
430
- }
431
- });
432
- }, [filteredEmojis.length, fireAnalytics, query]);
433
418
  useEffect(function () {
434
419
  if (!frequentlyUsedEmojis.length || query) {
435
420
  setFilteredEmojis(searchEmojis);
@@ -467,7 +452,10 @@ var EmojiPickerComponent = function EmojiPickerComponent(_ref) {
467
452
  return jsx("div", {
468
453
  css: emojiPicker(showPreview, size),
469
454
  ref: onPickerRef,
470
- "data-emoji-picker-container": true
455
+ "data-emoji-picker-container": true,
456
+ role: "dialog",
457
+ "aria-label": formatMessage(messages.emojiPickerTitle),
458
+ "aria-modal": true
471
459
  }, jsx(CategorySelector, {
472
460
  activeCategoryId: activeCategory,
473
461
  dynamicCategories: dynamicCategories,
@@ -499,7 +487,8 @@ var EmojiPickerComponent = function EmojiPickerComponent(_ref) {
499
487
  onCloseDelete: onCloseDelete,
500
488
  onFileChooserClicked: onFileChooserClicked,
501
489
  onOpenUpload: onOpenUpload,
502
- size: size
490
+ size: size,
491
+ activeCategoryId: activeCategory
503
492
  }), showPreview && jsx(EmojiPickerFooter, {
504
493
  selectedEmoji: selectedEmoji
505
494
  }));
@@ -1,32 +1,60 @@
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
  var EmojiPickerEmojiRow = function EmojiPickerEmojiRow(_ref) {
7
10
  var emojis = _ref.emojis,
8
11
  onSelected = _ref.onSelected,
9
12
  onMouseMove = _ref.onMouseMove,
13
+ onFocus = _ref.onFocus,
10
14
  title = _ref.title,
11
15
  showDelete = _ref.showDelete,
12
- onDelete = _ref.onDelete;
16
+ onDelete = _ref.onDelete,
17
+ virtualItemContext = _ref.virtualItemContext;
18
+ var _useEmojiPickerListCo = useEmojiPickerListContext(),
19
+ currentEmojisFocus = _useEmojiPickerListCo.currentEmojisFocus,
20
+ setEmojisFocus = _useEmojiPickerListCo.setEmojisFocus;
21
+ var rowIndex = (virtualItemContext === null || virtualItemContext === void 0 ? void 0 : virtualItemContext.index) || 0;
22
+ var _useIntl = useIntl(),
23
+ formatMessage = _useIntl.formatMessage;
24
+ var handleFocus = function handleFocus(index) {
25
+ return function (emojiId, emoji, event) {
26
+ setEmojisFocus({
27
+ rowIndex: rowIndex,
28
+ columnIndex: index
29
+ });
30
+ onFocus && onFocus(emojiId, emoji, event);
31
+ };
32
+ };
13
33
  return jsx("div", {
14
- css: emojiPickerRow
15
- }, emojis.map(function (emoji) {
34
+ css: emojiPickerRow,
35
+ role: "presentation"
36
+ }, emojis.map(function (emoji, index) {
16
37
  var shortName = emoji.shortName,
17
38
  id = emoji.id;
18
39
  var key = id ? "".concat(id, "-").concat(title) : "".concat(shortName, "-").concat(title);
40
+ var focus = currentEmojisFocus.rowIndex === rowIndex && currentEmojisFocus.columnIndex === index;
19
41
  return jsx("span", {
20
42
  css: emojiItem,
21
- key: key
43
+ key: key,
44
+ role: "gridcell",
45
+ "aria-colindex": index + 1 // aria-colindex is 1 based
22
46
  }, jsx(CachingEmoji, {
23
47
  emoji: emoji,
24
48
  selectOnHover: true,
25
49
  onSelected: onSelected,
26
50
  onMouseMove: onMouseMove,
51
+ onFocus: handleFocus(index),
27
52
  showDelete: showDelete,
28
53
  onDelete: onDelete,
29
54
  placeholderSize: 24,
55
+ "data-focus-index": "".concat(rowIndex, "-").concat(index),
56
+ tabIndex: focus ? 0 : -1,
57
+ "aria-roledescription": formatMessage(messages.emojiButtonRoleDescription),
30
58
  shouldBeInteractive: true
31
59
  }));
32
60
  }));