@atlaskit/emoji 67.1.0 → 67.2.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 (134) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/cjs/api/EmojiResource.js +29 -24
  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 +60 -24
  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/ResourcedEmoji.js +5 -3
  14. package/dist/cjs/components/common/ResourcedEmojiComponent.js +4 -0
  15. package/dist/cjs/components/common/RetryableButton.js +7 -3
  16. package/dist/cjs/components/common/TonePreviewButton.js +44 -0
  17. package/dist/cjs/components/common/ToneSelector.js +53 -25
  18. package/dist/cjs/components/common/styles.js +45 -16
  19. package/dist/cjs/components/i18n.js +44 -4
  20. package/dist/cjs/components/picker/CategorySelector.js +112 -90
  21. package/dist/cjs/components/picker/CategoryTracker.js +0 -28
  22. package/dist/cjs/components/picker/EmojiPickerCategoryHeading.js +2 -1
  23. package/dist/cjs/components/picker/EmojiPickerComponent.js +13 -7
  24. package/dist/cjs/components/picker/EmojiPickerEmojiRow.js +32 -4
  25. package/dist/cjs/components/picker/EmojiPickerList.js +140 -51
  26. package/dist/cjs/components/picker/EmojiPickerListSearch.js +16 -3
  27. package/dist/cjs/components/picker/EmojiPickerVirtualItems.js +5 -2
  28. package/dist/cjs/components/picker/VirtualList.js +196 -14
  29. package/dist/cjs/components/picker/styles.js +43 -51
  30. package/dist/cjs/context/EmojiPickerListContext.js +33 -0
  31. package/dist/cjs/hooks/useEmojiPickerListContext.js +12 -0
  32. package/dist/cjs/util/constants.js +40 -1
  33. package/dist/cjs/util/shared-styles.js +3 -4
  34. package/dist/cjs/version.json +1 -1
  35. package/dist/es2019/api/EmojiResource.js +29 -24
  36. package/dist/es2019/api/media/TokenManager.js +4 -4
  37. package/dist/es2019/components/common/CachingEmoji.js +10 -3
  38. package/dist/es2019/components/common/Emoji.js +44 -11
  39. package/dist/es2019/components/common/EmojiActions.js +54 -23
  40. package/dist/es2019/components/common/EmojiErrorMessage.js +7 -3
  41. package/dist/es2019/components/common/EmojiPlaceholder.js +1 -0
  42. package/dist/es2019/components/common/EmojiRadioButton.js +54 -0
  43. package/dist/es2019/components/common/EmojiUploadPicker.js +75 -36
  44. package/dist/es2019/components/common/EmojiUploadPreview.js +11 -2
  45. package/dist/es2019/components/common/FileChooser.js +1 -1
  46. package/dist/es2019/components/common/ResourcedEmoji.js +5 -3
  47. package/dist/es2019/components/common/ResourcedEmojiComponent.js +4 -0
  48. package/dist/es2019/components/common/RetryableButton.js +7 -3
  49. package/dist/es2019/components/common/TonePreviewButton.js +34 -0
  50. package/dist/es2019/components/common/ToneSelector.js +55 -21
  51. package/dist/es2019/components/common/styles.js +41 -10
  52. package/dist/es2019/components/i18n.js +44 -4
  53. package/dist/es2019/components/picker/CategorySelector.js +114 -89
  54. package/dist/es2019/components/picker/CategoryTracker.js +0 -24
  55. package/dist/es2019/components/picker/EmojiPickerCategoryHeading.js +2 -2
  56. package/dist/es2019/components/picker/EmojiPickerComponent.js +14 -7
  57. package/dist/es2019/components/picker/EmojiPickerEmojiRow.js +51 -21
  58. package/dist/es2019/components/picker/EmojiPickerList.js +102 -21
  59. package/dist/es2019/components/picker/EmojiPickerListSearch.js +14 -4
  60. package/dist/es2019/components/picker/EmojiPickerVirtualItems.js +4 -1
  61. package/dist/es2019/components/picker/VirtualList.js +193 -12
  62. package/dist/es2019/components/picker/styles.js +20 -28
  63. package/dist/es2019/context/EmojiPickerListContext.js +17 -0
  64. package/dist/es2019/hooks/useEmojiPickerListContext.js +3 -0
  65. package/dist/es2019/util/constants.js +31 -0
  66. package/dist/es2019/util/shared-styles.js +1 -2
  67. package/dist/es2019/version.json +1 -1
  68. package/dist/esm/api/EmojiResource.js +29 -24
  69. package/dist/esm/api/media/TokenManager.js +4 -4
  70. package/dist/esm/components/common/CachingEmoji.js +14 -6
  71. package/dist/esm/components/common/Emoji.js +48 -12
  72. package/dist/esm/components/common/EmojiActions.js +61 -25
  73. package/dist/esm/components/common/EmojiErrorMessage.js +7 -3
  74. package/dist/esm/components/common/EmojiPlaceholder.js +1 -0
  75. package/dist/esm/components/common/EmojiRadioButton.js +52 -0
  76. package/dist/esm/components/common/EmojiUploadPicker.js +77 -36
  77. package/dist/esm/components/common/EmojiUploadPreview.js +11 -2
  78. package/dist/esm/components/common/FileChooser.js +1 -1
  79. package/dist/esm/components/common/ResourcedEmoji.js +5 -3
  80. package/dist/esm/components/common/ResourcedEmojiComponent.js +4 -0
  81. package/dist/esm/components/common/RetryableButton.js +7 -3
  82. package/dist/esm/components/common/TonePreviewButton.js +33 -0
  83. package/dist/esm/components/common/ToneSelector.js +49 -18
  84. package/dist/esm/components/common/styles.js +40 -12
  85. package/dist/esm/components/i18n.js +44 -4
  86. package/dist/esm/components/picker/CategorySelector.js +114 -95
  87. package/dist/esm/components/picker/CategoryTracker.js +0 -28
  88. package/dist/esm/components/picker/EmojiPickerCategoryHeading.js +2 -2
  89. package/dist/esm/components/picker/EmojiPickerComponent.js +13 -7
  90. package/dist/esm/components/picker/EmojiPickerEmojiRow.js +32 -4
  91. package/dist/esm/components/picker/EmojiPickerList.js +141 -52
  92. package/dist/esm/components/picker/EmojiPickerListSearch.js +17 -4
  93. package/dist/esm/components/picker/EmojiPickerVirtualItems.js +5 -2
  94. package/dist/esm/components/picker/VirtualList.js +195 -12
  95. package/dist/esm/components/picker/styles.js +37 -45
  96. package/dist/esm/context/EmojiPickerListContext.js +21 -0
  97. package/dist/esm/hooks/useEmojiPickerListContext.js +5 -0
  98. package/dist/esm/util/constants.js +31 -0
  99. package/dist/esm/util/shared-styles.js +1 -2
  100. package/dist/esm/version.json +1 -1
  101. package/dist/types/api/EmojiResource.d.ts +2 -0
  102. package/dist/types/components/common/Emoji.d.ts +7 -1
  103. package/dist/types/components/common/EmojiActions.d.ts +3 -2
  104. package/dist/types/components/common/{EmojiButton.d.ts → EmojiRadioButton.d.ts} +3 -4
  105. package/dist/types/components/common/EmojiUploadPicker.d.ts +6 -4
  106. package/dist/types/components/common/RetryableButton.d.ts +1 -0
  107. package/dist/types/components/common/TonePreviewButton.d.ts +14 -0
  108. package/dist/types/components/common/ToneSelector.d.ts +8 -5
  109. package/dist/types/components/common/internal-types.d.ts +9 -0
  110. package/dist/types/components/common/styles.d.ts +2 -1
  111. package/dist/types/components/i18n.d.ts +40 -0
  112. package/dist/types/components/picker/CategorySelector.d.ts +3 -10
  113. package/dist/types/components/picker/CategoryTracker.d.ts +0 -2
  114. package/dist/types/components/picker/EmojiPickerCategoryHeading.d.ts +2 -1
  115. package/dist/types/components/picker/EmojiPickerEmojiRow.d.ts +5 -0
  116. package/dist/types/components/picker/EmojiPickerList.d.ts +10 -5
  117. package/dist/types/components/picker/EmojiPickerListSearch.d.ts +1 -0
  118. package/dist/types/components/picker/EmojiPickerVirtualItems.d.ts +1 -1
  119. package/dist/types/components/picker/VirtualList.d.ts +2 -0
  120. package/dist/types/components/picker/styles.d.ts +1 -1
  121. package/dist/types/context/EmojiPickerListContext.d.ts +10 -0
  122. package/dist/types/hooks/useEmojiPickerListContext.d.ts +1 -0
  123. package/dist/types/util/constants.d.ts +27 -0
  124. package/dist/types/util/shared-styles.d.ts +1 -1
  125. package/dist/types/util/type-helpers.d.ts +1 -1
  126. package/package.json +9 -6
  127. package/report.api.md +52 -1
  128. package/README.md +0 -3
  129. package/dist/es2019/components/common/EmojiButton.js +0 -49
  130. package/dist/esm/components/common/EmojiButton.js +0 -43
  131. /package/dist/cjs/{components/hooks.js → hooks/useIsMounted.js} +0 -0
  132. /package/dist/es2019/{components/hooks.js → hooks/useIsMounted.js} +0 -0
  133. /package/dist/esm/{components/hooks.js → hooks/useIsMounted.js} +0 -0
  134. /package/dist/types/{components/hooks.d.ts → hooks/useIsMounted.d.ts} +0 -0
@@ -2,11 +2,17 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
2
  import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
3
3
  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; }
4
4
  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) { _defineProperty(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; }
5
- import React, { useEffect, useMemo, useRef } from 'react';
6
- import EmojiButton from './EmojiButton';
5
+ /** @jsx jsx */
6
+ import { jsx } from '@emotion/react';
7
+ import { memo, useEffect, useMemo, useRef } from 'react';
7
8
  import { withAnalyticsEvents } from '@atlaskit/analytics-next';
8
9
  import { createAndFireEventInElementsChannel, toneSelectedEvent, toneSelectorOpenedEvent } from '../../util/analytics';
9
10
  import { setSkinToneAriaLabelText } from './setSkinToneAriaLabelText';
11
+ import EmojiRadioButton from './EmojiRadioButton';
12
+ import { useIntl } from 'react-intl-next';
13
+ import { messages } from '../i18n';
14
+ import { hidden } from './styles';
15
+ export var toneSelectorTestId = 'tone-selector';
10
16
  var extractAllTones = function extractAllTones(emoji) {
11
17
  if (emoji.skinVariations) {
12
18
  return [emoji].concat(_toConsumableArray(emoji.skinVariations));
@@ -16,24 +22,39 @@ var extractAllTones = function extractAllTones(emoji) {
16
22
  export var ToneSelectorInternal = function ToneSelectorInternal(props) {
17
23
  var createAnalyticsEvent = props.createAnalyticsEvent,
18
24
  emoji = props.emoji,
19
- previewEmojiId = props.previewEmojiId,
20
- onToneSelected = props.onToneSelected;
25
+ onToneSelected = props.onToneSelected,
26
+ onToneClose = props.onToneClose,
27
+ selectedTone = props.selectedTone,
28
+ isVisible = props.isVisible;
21
29
  var isMounted = useRef(false);
22
- var firstToneButtonRef = useRef(null);
30
+ var selectedToneRadioRef = useRef(null);
31
+ var _useIntl = useIntl(),
32
+ formatMessage = _useIntl.formatMessage;
23
33
  var emojiToneCollection = useMemo(function () {
24
- return extractAllTones(emoji).map(function (tone, index) {
34
+ var selectedToneIndex = -1;
35
+ var toneColletion = extractAllTones(emoji).map(function (tone, index) {
36
+ var isSelected = index === selectedTone;
37
+ if (isSelected) {
38
+ selectedToneIndex = index;
39
+ }
25
40
  return _objectSpread(_objectSpread({}, tone), {}, {
26
- focused: tone.id !== previewEmojiId,
41
+ isSelected: isSelected,
27
42
  label: setSkinToneAriaLabelText(tone.name),
28
- toneId: index
43
+ toneIndex: index
29
44
  });
30
45
  });
31
- }, [emoji, previewEmojiId]);
46
+
47
+ // push description of selected tone to the end of the array
48
+ // so that it gets rendered last/rightmost
49
+ toneColletion.push(toneColletion.splice(selectedToneIndex, 1)[0]);
50
+ return toneColletion;
51
+ }, [emoji, selectedTone]);
32
52
  useEffect(function () {
33
- if (firstToneButtonRef.current) {
34
- firstToneButtonRef.current.focus();
53
+ if (isVisible) {
54
+ var _selectedToneRadioRef;
55
+ (_selectedToneRadioRef = selectedToneRadioRef.current) === null || _selectedToneRadioRef === void 0 ? void 0 : _selectedToneRadioRef.focus();
35
56
  }
36
- }, [firstToneButtonRef]);
57
+ }, [isVisible, selectedToneRadioRef]);
37
58
  var fireAnalyticsEvent = function fireAnalyticsEvent(event) {
38
59
  if (createAnalyticsEvent) {
39
60
  createAndFireEventInElementsChannel(event)(createAnalyticsEvent);
@@ -41,6 +62,10 @@ export var ToneSelectorInternal = function ToneSelectorInternal(props) {
41
62
  };
42
63
  var onToneSelectedHandler = function onToneSelectedHandler(toneValue) {
43
64
  return function () {
65
+ if (selectedTone === toneValue && onToneClose) {
66
+ onToneClose();
67
+ return;
68
+ }
44
69
  onToneSelected(toneValue);
45
70
  var toneList = ['default', 'light', 'mediumLight', 'medium', 'mediumDark', 'dark'];
46
71
  fireAnalyticsEvent(toneSelectedEvent({
@@ -52,17 +77,23 @@ export var ToneSelectorInternal = function ToneSelectorInternal(props) {
52
77
  fireAnalyticsEvent(toneSelectorOpenedEvent({}));
53
78
  }
54
79
  isMounted.current = true;
55
- return /*#__PURE__*/React.createElement("div", null, emojiToneCollection.map(function (tone) {
56
- return /*#__PURE__*/React.createElement(EmojiButton, {
57
- ref: tone.focused ? firstToneButtonRef : null,
58
- shouldHideButton: tone.id === previewEmojiId,
80
+ return jsx("div", {
81
+ role: "radiogroup",
82
+ "data-testid": toneSelectorTestId,
83
+ id: "emoji-picker-tone-selector",
84
+ "aria-label": formatMessage(messages.emojiSelectSkinToneListAriaLabelText),
85
+ css: !isVisible && hidden
86
+ }, emojiToneCollection.map(function (tone) {
87
+ return jsx(EmojiRadioButton, {
88
+ ref: tone.isSelected ? selectedToneRadioRef : null,
89
+ defaultChecked: tone.isSelected,
59
90
  ariaLabelText: tone.label,
60
91
  key: "".concat(tone.id),
61
- onSelected: onToneSelectedHandler(tone.toneId),
62
92
  emoji: tone,
93
+ onSelected: onToneSelectedHandler(tone.toneIndex),
63
94
  selectOnHover: true
64
95
  });
65
96
  }));
66
97
  };
67
98
  var ToneSelector = withAnalyticsEvents()(ToneSelectorInternal);
68
- export default ToneSelector;
99
+ export default /*#__PURE__*/memo(ToneSelector);
@@ -2,10 +2,9 @@ import _taggedTemplateLiteral from "@babel/runtime/helpers/taggedTemplateLiteral
2
2
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
3
3
  var _css, _css2, _templateObject, _span, _css3, _css6, _input;
4
4
  import { css, keyframes } from '@emotion/react';
5
- import { borderRadius } from '@atlaskit/theme/constants';
6
5
  import { defaultEmojiHeight } from '../../util/constants';
7
6
  import { akEmojiSelectedBackgroundColor } from '../../util/shared-styles';
8
- import { N20, N200, N20A, N300, N900, R300, R400 } from '@atlaskit/theme/colors';
7
+ import { B100, N20, N200, N20A, N300, N900, R300, R400 } from '@atlaskit/theme/colors';
9
8
  export var commonSelectedStyles = 'emoji-common-selected';
10
9
  export var selectOnHoverStyles = 'emoji-common-select-on-hover';
11
10
  export var emojiSprite = 'emoji-common-emoji-sprite';
@@ -33,7 +32,7 @@ export var emojiToneSelectorContainer = css({
33
32
  padding: '10px 10px 11px 0'
34
33
  });
35
34
  export var emojiStyles = css((_css = {
36
- borderRadius: "".concat(borderRadius(), "px"),
35
+ borderRadius: "var(--ds-radius-100, 3px)",
37
36
  backgroundColor: 'transparent',
38
37
  display: 'inline-block',
39
38
  verticalAlign: 'middle',
@@ -48,6 +47,10 @@ export var emojiStyles = css((_css = {
48
47
  visibility: 'visible'
49
48
  }), _defineProperty(_css, "img", {
50
49
  display: 'block'
50
+ }), _defineProperty(_css, '&:focus', {
51
+ boxShadow: "0 0 0 2px ".concat("var(--ds-border-focused, ".concat(B100, ")")),
52
+ transitionDuration: '0s, 0.2s',
53
+ outline: 'none'
51
54
  }), _css));
52
55
  export var emojiContainer = css((_css2 = {
53
56
  display: 'inline-block',
@@ -61,6 +64,10 @@ export var emojiContainer = css((_css2 = {
61
64
  minHeight: "".concat(defaultEmojiHeight, "px"),
62
65
  minWidth: "".concat(defaultEmojiHeight, "px"),
63
66
  verticalAlign: 'middle'
67
+ }), _defineProperty(_css2, '&:focus', {
68
+ boxShadow: "0 0 0 2px ".concat("var(--ds-border-focused, ".concat(B100, ")")),
69
+ transitionDuration: '0s, 0.2s',
70
+ outline: 'none'
64
71
  }), _css2));
65
72
  export var placeholder = 'emoji-common-placeholder';
66
73
  export var placeholderContainer = css({
@@ -68,7 +75,7 @@ export var placeholderContainer = css({
68
75
  margin: '-1px 0',
69
76
  display: 'inline-block',
70
77
  background: "var(--ds-border, #f7f7f7)",
71
- borderRadius: "".concat(borderRadius(), "px"),
78
+ borderRadius: "var(--ds-radius-100, 3px)",
72
79
  overflow: 'hidden',
73
80
  verticalAlign: 'middle',
74
81
  whiteSpace: 'nowrap',
@@ -86,16 +93,23 @@ export var placeholderContainerAnimated = css({
86
93
  animation: "".concat(easeSweep, " 1s cubic-bezier(0.4, 0.0, 0.2, 1) infinite")
87
94
  }
88
95
  });
96
+ export var hidden = css({
97
+ opacity: 0,
98
+ visibility: 'hidden',
99
+ display: 'none'
100
+ });
89
101
  export var emojiButton = css((_css3 = {
90
102
  backgroundColor: 'transparent',
91
103
  border: '0',
104
+ borderRadius: "var(--ds-radius-100, 3px)",
92
105
  cursor: 'pointer',
93
- padding: 0
106
+ padding: 0,
107
+ position: 'relative',
108
+ display: 'inline-block'
94
109
  }, _defineProperty(_css3, '&::-moz-focus-inner', {
95
110
  border: '0 none',
96
111
  padding: 0
97
112
  }), _defineProperty(_css3, '&>span', (_span = {
98
- borderRadius: "".concat(borderRadius(), "px"),
99
113
  padding: '6px'
100
114
  }, _defineProperty(_span, "&>.".concat(emojiSprite), {
101
115
  height: '24px',
@@ -103,10 +117,24 @@ export var emojiButton = css((_css3 = {
103
117
  }), _defineProperty(_span, "&>img", {
104
118
  height: '24px',
105
119
  width: '24px'
106
- }), _span)), _css3));
107
- export var hiddenToneButton = css({
108
- // Hide currently selected tone that rendered in the ToneSelector to avoid duplication
109
- display: 'none'
120
+ }), _span)), _defineProperty(_css3, '&:focus', {
121
+ boxShadow: "0 0 0 2px ".concat("var(--ds-border-focused, ".concat(B100, ")")),
122
+ transitionDuration: '0s, 0.2s',
123
+ outline: 'none'
124
+ }), _css3));
125
+ export var emojiRadio = css({
126
+ opacity: 0,
127
+ position: 'absolute',
128
+ top: '-10px',
129
+ left: '-10px',
130
+ '+span': {
131
+ borderRadius: "var(--ds-radius-100, 3px)"
132
+ },
133
+ '&:focus + span': {
134
+ boxShadow: "0 0 0 2px ".concat("var(--ds-border-focused, ".concat(B100, ")")),
135
+ transitionDuration: '0s, 0.2s',
136
+ outline: 'none'
137
+ }
110
138
  });
111
139
 
112
140
  // Emoji Preview
@@ -177,7 +205,7 @@ export var previewImg = css((_css6 = {
177
205
 
178
206
  export var emojiScrollable = css({
179
207
  border: "1px solid ".concat("var(--ds-border, #fff)"),
180
- borderRadius: "".concat(borderRadius(), "px"),
208
+ borderRadius: "var(--ds-radius-100, 3px)",
181
209
  display: 'block',
182
210
  margin: '0',
183
211
  overflowX: 'hidden',
@@ -242,7 +270,7 @@ export var uploadPreview = css({
242
270
  justifyContent: 'space-between',
243
271
  alignItems: 'center',
244
272
  background: "var(--ds-background-neutral, ".concat(N20, ")"),
245
- borderRadius: "".concat(borderRadius(), "px"),
273
+ borderRadius: "var(--ds-radius-100, 3px)",
246
274
  padding: '10px'
247
275
  });
248
276
  export var uploadPreviewText = css({
@@ -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,7 +7,7 @@ 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';
@@ -22,7 +22,8 @@ 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
24
  import { useEmoji } from '../../hooks/useEmoji';
25
- import { useIsMounted } from '../hooks';
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;
@@ -324,12 +327,12 @@ var EmojiPickerComponent = function EmojiPickerComponent(_ref) {
324
327
  });
325
328
  fireAnalytics(uploadBeginButton());
326
329
  }, [emojiProvider, fireAnalytics]);
327
- var scrollToEndOfList = useCallback(function () {
330
+ var scrollToUploadedEmoji = useCallback(function () {
328
331
  if (emojiPickerList.current) {
329
332
  // Wait a tick to ensure repaint and updated height for picker list
330
333
  window.setTimeout(function () {
331
334
  var _emojiPickerList$curr2;
332
- (_emojiPickerList$curr2 = emojiPickerList.current) === null || _emojiPickerList$curr2 === void 0 ? void 0 : _emojiPickerList$curr2.scrollToBottom();
335
+ (_emojiPickerList$curr2 = emojiPickerList.current) === null || _emojiPickerList$curr2 === void 0 ? void 0 : _emojiPickerList$curr2.scrollToRecentlyUploaded();
333
336
  }, 0);
334
337
  }
335
338
  }, [emojiPickerList]);
@@ -346,10 +349,10 @@ var EmojiPickerComponent = function EmojiPickerComponent(_ref) {
346
349
  setSelectedEmoji(emojiDescription);
347
350
  setUploading(false);
348
351
  });
349
- scrollToEndOfList();
352
+ scrollToUploadedEmoji();
350
353
  };
351
354
  uploadEmoji(upload, emojiProvider, errorSetter, onSuccess, fireAnalytics, retry);
352
- }, [emojiProvider, fireAnalytics, scrollToEndOfList]);
355
+ }, [emojiProvider, fireAnalytics, scrollToUploadedEmoji]);
353
356
  var onTriggerDelete = useCallback(function (_emojiId, emoji) {
354
357
  if (_emojiId) {
355
358
  fireAnalytics(deleteBeginEvent({
@@ -449,7 +452,10 @@ var EmojiPickerComponent = function EmojiPickerComponent(_ref) {
449
452
  return jsx("div", {
450
453
  css: emojiPicker(showPreview, size),
451
454
  ref: onPickerRef,
452
- "data-emoji-picker-container": true
455
+ "data-emoji-picker-container": true,
456
+ role: "dialog",
457
+ "aria-label": formatMessage(messages.emojiPickerTitle),
458
+ "aria-modal": true
453
459
  }, jsx(CategorySelector, {
454
460
  activeCategoryId: activeCategory,
455
461
  dynamicCategories: dynamicCategories,