@atlaskit/emoji 70.11.0 → 70.11.2

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 (42) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/afm-cc/tsconfig.json +3 -0
  3. package/afm-products/tsconfig.json +3 -0
  4. package/dist/cjs/components/common/EmojiUploadPicker.js +19 -0
  5. package/dist/cjs/components/i18n.js +15 -0
  6. package/dist/cjs/components/picker/EmojiPickerComponent.compiled.css +3 -0
  7. package/dist/cjs/components/picker/EmojiPickerComponent.js +20 -6
  8. package/dist/cjs/components/picker/EmojiPickerList.js +31 -10
  9. package/dist/cjs/components/picker/EmojiPickerNoResults.compiled.css +12 -0
  10. package/dist/cjs/components/picker/EmojiPickerNoResults.js +50 -0
  11. package/dist/cjs/components/picker/EmojiPickerSizes.js +2 -0
  12. package/dist/cjs/components/picker/EmojiPickerVirtualItems.js +15 -1
  13. package/dist/cjs/util/analytics/analytics.js +1 -1
  14. package/dist/es2019/components/common/EmojiUploadPicker.js +17 -0
  15. package/dist/es2019/components/i18n.js +15 -0
  16. package/dist/es2019/components/picker/EmojiPickerComponent.compiled.css +3 -0
  17. package/dist/es2019/components/picker/EmojiPickerComponent.js +20 -6
  18. package/dist/es2019/components/picker/EmojiPickerList.js +33 -11
  19. package/dist/es2019/components/picker/EmojiPickerNoResults.compiled.css +12 -0
  20. package/dist/es2019/components/picker/EmojiPickerNoResults.js +40 -0
  21. package/dist/es2019/components/picker/EmojiPickerSizes.js +2 -0
  22. package/dist/es2019/components/picker/EmojiPickerVirtualItems.js +7 -0
  23. package/dist/es2019/util/analytics/analytics.js +1 -1
  24. package/dist/esm/components/common/EmojiUploadPicker.js +19 -0
  25. package/dist/esm/components/i18n.js +15 -0
  26. package/dist/esm/components/picker/EmojiPickerComponent.compiled.css +3 -0
  27. package/dist/esm/components/picker/EmojiPickerComponent.js +20 -6
  28. package/dist/esm/components/picker/EmojiPickerList.js +32 -11
  29. package/dist/esm/components/picker/EmojiPickerNoResults.compiled.css +12 -0
  30. package/dist/esm/components/picker/EmojiPickerNoResults.js +41 -0
  31. package/dist/esm/components/picker/EmojiPickerSizes.js +2 -0
  32. package/dist/esm/components/picker/EmojiPickerVirtualItems.js +14 -0
  33. package/dist/esm/util/analytics/analytics.js +1 -1
  34. package/dist/types/components/i18n.d.ts +15 -0
  35. package/dist/types/components/picker/EmojiPickerNoResults.d.ts +7 -0
  36. package/dist/types/components/picker/EmojiPickerSizes.d.ts +1 -0
  37. package/dist/types/components/picker/EmojiPickerVirtualItems.d.ts +6 -1
  38. package/dist/types-ts4.5/components/i18n.d.ts +15 -0
  39. package/dist/types-ts4.5/components/picker/EmojiPickerNoResults.d.ts +7 -0
  40. package/dist/types-ts4.5/components/picker/EmojiPickerSizes.d.ts +1 -0
  41. package/dist/types-ts4.5/components/picker/EmojiPickerVirtualItems.d.ts +6 -1
  42. package/package.json +4 -3
@@ -1,15 +1,17 @@
1
+ import { useIntl } from 'react-intl';
1
2
  import React, { useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
2
3
  import { fg } from '@atlaskit/platform-feature-flags';
3
4
  import { customCategory, defaultEmojiPickerSize, frequentCategory, searchCategory, userCustomTitle, yourUploadsCategory } from '../../util/constants';
4
5
  import { CategoryDescriptionMap, CategoryDescriptionMapNew } from './categories';
5
6
  import CategoryTracker from './CategoryTracker';
6
7
  import { sizes } from './EmojiPickerSizes';
7
- import { CategoryHeadingItem, EmojisRowItem, LoadingItem, virtualItemRenderer } from './EmojiPickerVirtualItems';
8
+ import { CategoryHeadingItem, EmojisRowItem, LoadingItem, NoResultsItem, virtualItemRenderer } from './EmojiPickerVirtualItems';
8
9
  import EmojiActions from '../common/EmojiActions';
9
10
  import { emojiPickerHeightOffset, scrollToRow } from './utils';
10
11
  import { VirtualList } from './VirtualList';
11
12
  import { EmojiPickerListContextProvider } from '../../context/EmojiPickerListContext';
12
13
  import EmojiPickerTabPanel from './EmojiPickerTabPanel';
14
+ import { messages } from '../i18n';
13
15
 
14
16
  /**
15
17
  * Test id for wrapper Emoji Picker List div
@@ -52,6 +54,9 @@ export const EmojiPickerVirtualListInternal = /*#__PURE__*/React.forwardRef((pro
52
54
  onOpenUpload,
53
55
  activeCategoryId
54
56
  } = props;
57
+ const {
58
+ formatMessage
59
+ } = useIntl();
55
60
  const listRef = useRef(null);
56
61
  const [allEmojiGroups, setAllEmojiGroups] = useState([]);
57
62
  const [virtualItems, setVirtualItems] = useState([]);
@@ -153,14 +158,27 @@ export const EmojiPickerVirtualListInternal = /*#__PURE__*/React.forwardRef((pro
153
158
  items.push(new LoadingItem());
154
159
  } else {
155
160
  if (query) {
156
- const search = CategoryDescriptionMap.SEARCH;
157
- // Only a single "result" category
158
- items = [...items, ...buildVirtualItemFromGroup({
159
- category: searchCategory,
160
- title: search.name,
161
- emojis,
162
- order: search.order
163
- })];
161
+ const search = fg('platform_emoji_picker_refresh') ? CategoryDescriptionMapNew.SEARCH : CategoryDescriptionMap.SEARCH;
162
+ if (emojis.length === 0 && fg('platform_emoji_picker_refresh')) {
163
+ // Show a "No results" category heading, then a no-results illustration below it
164
+ items.push(new CategoryHeadingItem({
165
+ id: searchCategory,
166
+ title: formatMessage(messages.emojiPickerNoResults),
167
+ className: categoryClassname
168
+ }));
169
+ items.push(new NoResultsItem({
170
+ onOpenUpload,
171
+ uploadEnabled
172
+ }));
173
+ } else {
174
+ // Only a single "result" category
175
+ items = [...items, ...buildVirtualItemFromGroup({
176
+ category: searchCategory,
177
+ title: search.name,
178
+ emojis,
179
+ order: search.order
180
+ })];
181
+ }
164
182
  } else {
165
183
  // Group by category
166
184
 
@@ -185,7 +203,7 @@ export const EmojiPickerVirtualListInternal = /*#__PURE__*/React.forwardRef((pro
185
203
  }
186
204
  }
187
205
  // eslint-disable-next-line react-hooks/exhaustive-deps
188
- }, [allEmojiGroups, loading, query, emojis]);
206
+ }, [allEmojiGroups, loading, query, emojis, onOpenUpload, uploadEnabled, formatMessage]);
189
207
  const findCategoryToActivate = row => {
190
208
  let category = null;
191
209
  if (row instanceof CategoryHeadingItem) {
@@ -294,8 +312,12 @@ export const EmojiPickerVirtualListInternal = /*#__PURE__*/React.forwardRef((pro
294
312
  // eslint-disable-next-line react-hooks/exhaustive-deps
295
313
  }, [virtualItems, categoriesChanged]);
296
314
  const virtualListHeight = useMemo(() => {
315
+ if (query && emojis.length === 0 && fg('platform_emoji_picker_refresh')) {
316
+ // No-results state: expand the list height to fit heading + illustration without scrolling
317
+ return sizes.categoryHeadingHeight + sizes.noResultsHeight + emojiPickerHeightOffset(size);
318
+ }
297
319
  return fg('platform_emoji_picker_refresh') ? sizes.listHeightNew + emojiPickerHeightOffset(size) : sizes.listHeight + emojiPickerHeightOffset(size);
298
- }, [size]);
320
+ }, [size, query, emojis.length]);
299
321
  return /*#__PURE__*/React.createElement(EmojiPickerTabPanel, {
300
322
  showSearchResults: !!query
301
323
  }, /*#__PURE__*/React.createElement(EmojiActions, {
@@ -0,0 +1,12 @@
1
+
2
+ ._zulppxbi{gap:var(--ds-space-200,1pc)}._19bvpxbi{padding-left:var(--ds-space-200,1pc)}
3
+ ._1bah1h6o{justify-content:center}
4
+ ._1bsb1osq{width:100%}
5
+ ._1e0c1txw{display:flex}
6
+ ._2lx21bp4{flex-direction:column}
7
+ ._4cvr1h6o{align-items:center}
8
+ ._ca0q1ejb{padding-top:var(--ds-space-300,24px)}
9
+ ._n3td1ejb{padding-bottom:var(--ds-space-300,24px)}
10
+ ._u5f3pxbi{padding-right:var(--ds-space-200,1pc)}
11
+ ._vchhusvi{box-sizing:border-box}
12
+ ._y3gn1h6o{text-align:center}
@@ -0,0 +1,40 @@
1
+ /* EmojiPickerNoResults.tsx generated by @compiled/babel-plugin v0.39.1 */
2
+ import "./EmojiPickerNoResults.compiled.css";
3
+ import * as React from 'react';
4
+ import { ax, ix } from "@compiled/react/runtime";
5
+ import { useCallback } from 'react';
6
+ import { fg } from '@atlaskit/platform-feature-flags';
7
+ import { FormattedMessage } from 'react-intl';
8
+ import AkButton from '@atlaskit/button/standard-button';
9
+ import Image from '@atlaskit/image';
10
+ import { messages } from '../i18n';
11
+ import SearchNoResultDark from './assets/spot/search-no-result/dark.svg';
12
+ import SearchNoResultLight from './assets/spot/search-no-result/light.svg';
13
+ const noResultsContainer = null;
14
+ export const RENDER_EMOJI_PICKER_NO_RESULTS_TESTID = 'render-emoji-picker-no-results';
15
+ const EmojiPickerNoResults = ({
16
+ onOpenUpload,
17
+ uploadEnabled
18
+ }) => {
19
+ const handleOpenUpload = useCallback(event => {
20
+ if (fg('platform_emoji_keep_picker_open_on_upload')) {
21
+ event.preventDefault();
22
+ event.stopPropagation();
23
+ }
24
+ onOpenUpload();
25
+ }, [onOpenUpload]);
26
+ return /*#__PURE__*/React.createElement("div", {
27
+ "data-testid": RENDER_EMOJI_PICKER_NO_RESULTS_TESTID,
28
+ className: ax(["_zulppxbi _1e0c1txw _2lx21bp4 _4cvr1h6o _1bah1h6o _ca0q1ejb _u5f3pxbi _n3td1ejb _19bvpxbi _y3gn1h6o _1bsb1osq _vchhusvi"])
29
+ }, /*#__PURE__*/React.createElement(Image, {
30
+ src: SearchNoResultLight,
31
+ srcDark: SearchNoResultDark,
32
+ alt: "",
33
+ width: 200
34
+ }), uploadEnabled && /*#__PURE__*/React.createElement(FormattedMessage, messages.emojiPickerAddCustomEmoji, label => /*#__PURE__*/React.createElement(AkButton, {
35
+ onClick: handleOpenUpload,
36
+ appearance: "default",
37
+ tabIndex: 0
38
+ }, label)));
39
+ };
40
+ export default EmojiPickerNoResults;
@@ -13,6 +13,8 @@ export const sizes = {
13
13
  // 32px height
14
14
  loadingRowHeight: 150,
15
15
  // Fills remaining space without scrolling when loading.
16
+ noResultsHeight: 300,
17
+ // illustration (200px) + button + padding + gap
16
18
  uploadActionHeight: 40,
17
19
  // 40px height
18
20
  emojiPerRow: 8
@@ -7,6 +7,7 @@ import { ax, ix } from "@compiled/react/runtime";
7
7
  import Spinner from '@atlaskit/spinner';
8
8
  import EmojiPickerCategoryHeading from './EmojiPickerCategoryHeading';
9
9
  import EmojiPickerEmojiRow from './EmojiPickerEmojiRow';
10
+ import EmojiPickerNoResults from './EmojiPickerNoResults';
10
11
  import { sizes } from './EmojiPickerSizes';
11
12
  const emojiPickerSpinner = null;
12
13
  export class AbstractItem {
@@ -39,6 +40,12 @@ export class CategoryHeadingItem extends AbstractItem {
39
40
  _defineProperty(this, "renderItem", () => /*#__PURE__*/React.createElement(EmojiPickerCategoryHeading, this.props));
40
41
  }
41
42
  }
43
+ export class NoResultsItem extends AbstractItem {
44
+ constructor(props) {
45
+ super(props, sizes.noResultsHeight);
46
+ _defineProperty(this, "renderItem", () => /*#__PURE__*/React.createElement(EmojiPickerNoResults, this.props));
47
+ }
48
+ }
42
49
  export const virtualItemRenderer = (rows, context) => {
43
50
  const {
44
51
  index,
@@ -9,7 +9,7 @@ const createEvent = (eventType, action, actionSubject, actionSubjectId, attribut
9
9
  actionSubjectId,
10
10
  attributes: {
11
11
  packageName: "@atlaskit/emoji",
12
- packageVersion: "70.10.14",
12
+ packageVersion: "70.11.1",
13
13
  ...attributes
14
14
  }
15
15
  });
@@ -35,6 +35,8 @@ var uploadChooseFileEmojiName = null;
35
35
  var uploadChooseFileMessage = null;
36
36
  var uploadChooseFileRow = null;
37
37
  var uploadChooseFileRowNew = null;
38
+ var supportedEmojiUploadMimeTypes = new Set(['image/png', 'image/jpeg', 'image/gif']);
39
+ var supportedEmojiUploadExtensions = ['.png', '.jpg', '.jpeg', '.gif'];
38
40
  export var uploadEmojiNameInputTestId = 'upload-emoji-name-input';
39
41
  export var uploadEmojiComponentTestId = 'upload-emoji-component';
40
42
  export var cancelEmojiUploadPickerTestId = 'cancel-emoji-upload-picker';
@@ -51,6 +53,18 @@ var toEmojiName = function toEmojiName(uploadName) {
51
53
  var name = uploadName.split('_').join(' ');
52
54
  return "".concat(name.substr(0, 1).toLocaleUpperCase()).concat(name.substr(1));
53
55
  };
56
+ var isSupportedEmojiUploadFileType = function isSupportedEmojiUploadFileType(file) {
57
+ if (!fg('platform_emoji_picker_refresh')) {
58
+ return true;
59
+ }
60
+ if (supportedEmojiUploadMimeTypes.has(file.type)) {
61
+ return true;
62
+ }
63
+ var lowerCaseFileName = file.name.toLowerCase();
64
+ return supportedEmojiUploadExtensions.some(function (extension) {
65
+ return lowerCaseFileName.endsWith(extension);
66
+ });
67
+ };
54
68
  var ChooseEmojiFile = /*#__PURE__*/memo(function (props) {
55
69
  var _props$name = props.name,
56
70
  name = _props$name === void 0 ? '' : _props$name,
@@ -334,6 +348,11 @@ var EmojiUploadPicker = function EmojiUploadPicker(props) {
334
348
  if (files.length) {
335
349
  var reader = new FileReader();
336
350
  var file = files[0];
351
+ if (!isSupportedEmojiUploadFileType(file)) {
352
+ setChooseEmojiErrorMessage( /*#__PURE__*/React.createElement(FormattedMessage, messages.emojiUnsupportedFileType));
353
+ cancelChooseFile();
354
+ return;
355
+ }
337
356
  if (ImageUtil.hasFileExceededSize(file)) {
338
357
  setChooseEmojiErrorMessage( /*#__PURE__*/React.createElement(FormattedMessage, messages.emojiImageTooBig));
339
358
  cancelChooseFile();
@@ -215,11 +215,26 @@ export var messages = defineMessages({
215
215
  defaultMessage: 'Selected image is more than 1 MB',
216
216
  description: 'Error message for image too big, beyond the size limit'
217
217
  },
218
+ emojiUnsupportedFileType: {
219
+ id: 'fabric.emoji.error.unsupported.file.type',
220
+ defaultMessage: "This file type isn't supported. Select a PNG, JPEG, or GIF to create your emoji.",
221
+ description: 'Error message shown when the selected emoji upload file type is not supported'
222
+ },
218
223
  emojiDuplicateName: {
219
224
  id: 'fabric.emoji.error.duplicate.name',
220
225
  defaultMessage: 'An emoji with this name exists already',
221
226
  description: 'Error message shown when the user tries to upload an emoji with a name that already exists in the custom emoji set'
222
227
  },
228
+ emojiPickerNoResults: {
229
+ id: 'fabric.emoji.picker.no.results',
230
+ defaultMessage: 'No results',
231
+ description: 'Heading shown in the emoji picker when a search query returns no matching emojis.'
232
+ },
233
+ emojiPickerAddCustomEmoji: {
234
+ id: 'fabric.emoji.picker.add.custom.emoji',
235
+ defaultMessage: 'Add custom emoji',
236
+ description: 'Label for the button shown in the emoji picker no-results screen that opens the custom emoji upload panel.'
237
+ },
223
238
  emojiPickerTitle: {
224
239
  id: 'fabric.emoji.picker',
225
240
  defaultMessage: 'Emoji picker',
@@ -21,13 +21,16 @@
21
21
  ._1xi2idpf{right:0}
22
22
  ._2lx21bp4{flex-direction:column}
23
23
  ._4t3i1784{height:470px}
24
+ ._4t3i19lm{height:514px}
24
25
  ._4t3i1ckg{height:455px}
25
26
  ._4t3i1dsu{height:419px}
27
+ ._4t3i1j8x{height:354px}
26
28
  ._4t3i1sa4{height:390px}
27
29
  ._4t3i2300{height:429px}
28
30
  ._4t3i50k7{height:499px}
29
31
  ._4t3iaq3k{height:295px}
30
32
  ._4t3ibqjm{height:310px}
33
+ ._4t3iihnn{height:434px}
31
34
  ._4t3iixjv{height:375px}
32
35
  ._4t3iqbeb{height:339px}
33
36
  ._4t3iuxo9{height:var(--_19dn98e)}
@@ -67,6 +67,12 @@ var withoutPreviewHeight = {
67
67
  medium: "_4t3iixjv _1tke5x59",
68
68
  large: "_4t3i1ckg _1tke1pna"
69
69
  };
70
+ var emojiPickerHeightNoResults = 354;
71
+ var withNoResultsRefreshHeight = {
72
+ small: "_4t3i1j8x _1tkegx0z",
73
+ medium: "_4t3iihnn _1tke5x59",
74
+ large: "_4t3i19lm _1tke1pna"
75
+ };
70
76
  var FREQUENTLY_USED_MAX = 16;
71
77
  var EmojiPickerComponent = function EmojiPickerComponent(_ref) {
72
78
  var onSelection = _ref.onSelection,
@@ -141,6 +147,7 @@ var EmojiPickerComponent = function EmojiPickerComponent(_ref) {
141
147
  }, []);
142
148
  var openTime = useRef(0);
143
149
  var isMounting = useRef(true);
150
+ var lastNonSearchCategory = useRef(activeCategory);
144
151
  var previousEmojiProvider = useRef(emojiProvider);
145
152
  var isProgrammaticScroll = useRef(false);
146
153
  var pickerRef = useRef(null);
@@ -182,8 +189,11 @@ var EmojiPickerComponent = function EmojiPickerComponent(_ref) {
182
189
  }
183
190
  if (activeCategory !== category) {
184
191
  setActiveCategory(category);
192
+ if (!query && fg('platform_emoji_picker_refresh')) {
193
+ lastNonSearchCategory.current = category;
194
+ }
185
195
  }
186
- }, [activeCategory, uploading, emojiToDelete]);
196
+ }, [activeCategory, uploading, emojiToDelete, query]);
187
197
  var calculateElapsedTime = function calculateElapsedTime() {
188
198
  return Date.now() - openTime.current;
189
199
  };
@@ -400,6 +410,10 @@ var EmojiPickerComponent = function EmojiPickerComponent(_ref) {
400
410
  source: SearchSourceTypes.PICKER
401
411
  };
402
412
  if (searchQuery !== query) {
413
+ // Capture the active category before entering search so we can keep it highlighted
414
+ if (!query && searchQuery && fg('platform_emoji_picker_refresh')) {
415
+ lastNonSearchCategory.current = activeCategory;
416
+ }
403
417
  setQuery(searchQuery);
404
418
  }
405
419
  updateEmojis(searchQuery, options);
@@ -407,7 +421,7 @@ var EmojiPickerComponent = function EmojiPickerComponent(_ref) {
407
421
  // scroll to top when search, which is search results section
408
422
  scrollToTopOfList();
409
423
  }
410
- }, [query, filteredEmojis, selectedTone, updateEmojis, scrollToTopOfList]);
424
+ }, [activeCategory, query, filteredEmojis, selectedTone, updateEmojis, scrollToTopOfList]);
411
425
 
412
426
  // When the upload screen is open, intercept any file drag at the window level so it
413
427
  // cannot reach underlying page drop handlers (e.g. the Confluence editor).
@@ -628,7 +642,7 @@ var EmojiPickerComponent = function EmojiPickerComponent(_ref) {
628
642
  role: "dialog",
629
643
  "aria-label": formatMessage(messages.emojiPickerTitle),
630
644
  "aria-modal": true,
631
- className: ax([fg('platform_emoji_picker_refresh') ? "_19itahnd _2rko1mok _1e0c1txw _2lx21bp4 _1bah1yb4 _bfhk1bhr _16qs130s _4t3iuxo9 _1bsb10mj _1ul910mj _c71l1y6z _kqswh2mm" : "_19itahnd _2rkofajl _1e0c1txw _2lx21bp4 _1bah1yb4 _bfhk1bhr _16qs130s _4t3iuxo9 _1bsb10mj _1ul910mj _c71l1y6z _kqswh2mm", !!emojiToDelete && fg('platform_emoji_picker_refresh') ? withDeleteRefreshHeight[size] : uploading && fg('platform_emoji_picker_refresh') ? withUploadRefreshHeight[size] : showPreview ? withPreviewHeight[size] : withoutPreviewHeight[size]]),
645
+ className: ax([fg('platform_emoji_picker_refresh') ? "_19itahnd _2rko1mok _1e0c1txw _2lx21bp4 _1bah1yb4 _bfhk1bhr _16qs130s _4t3iuxo9 _1bsb10mj _1ul910mj _c71l1y6z _kqswh2mm" : "_19itahnd _2rkofajl _1e0c1txw _2lx21bp4 _1bah1yb4 _bfhk1bhr _16qs130s _4t3iuxo9 _1bsb10mj _1ul910mj _c71l1y6z _kqswh2mm", !!emojiToDelete && fg('platform_emoji_picker_refresh') ? withDeleteRefreshHeight[size] : uploading && fg('platform_emoji_picker_refresh') ? withUploadRefreshHeight[size] : query && filteredEmojis.length === 0 && fg('platform_emoji_picker_refresh') ? withNoResultsRefreshHeight[size] : showPreview ? withPreviewHeight[size] : withoutPreviewHeight[size]]),
632
646
  style: {
633
647
  "--_19dn98e": ix("".concat(emojiPickerHeight, "px")),
634
648
  "--_gsvyy7": ix("".concat(emojiPickerWidth, "px"))
@@ -673,7 +687,7 @@ var EmojiPickerComponent = function EmojiPickerComponent(_ref) {
673
687
  onOpenUpload: onOpenUpload,
674
688
  size: size,
675
689
  activeCategoryId: activeCategory
676
- }), showPreview && !(emojiToDelete && fg('platform_emoji_picker_refresh')) && /*#__PURE__*/React.createElement(EmojiPickerFooter, {
690
+ }), showPreview && !(emojiToDelete && fg('platform_emoji_picker_refresh')) && !(query && filteredEmojis.length === 0 && fg('platform_emoji_picker_refresh')) && /*#__PURE__*/React.createElement(EmojiPickerFooter, {
677
691
  selectedEmoji: selectedEmoji,
678
692
  uploadEnabled: isUploadSupported && !uploading,
679
693
  onOpenUpload: onOpenUpload
@@ -688,7 +702,7 @@ var EmojiPickerComponent = function EmojiPickerComponent(_ref) {
688
702
  onKeyPress: suppressKeyPress,
689
703
  onKeyUp: suppressKeyPress,
690
704
  onKeyDown: suppressKeyPress,
691
- className: ax([fg('platform_emoji_picker_refresh') ? "_19itahnd _2rko1mok _1e0c1txw _2lx21bp4 _1bah1yb4 _bfhk1bhr _16qs130s _4t3iuxo9 _1bsb10mj _1ul910mj _c71l1y6z _kqswh2mm" : "_19itahnd _2rkofajl _1e0c1txw _2lx21bp4 _1bah1yb4 _bfhk1bhr _16qs130s _4t3iuxo9 _1bsb10mj _1ul910mj _c71l1y6z _kqswh2mm", !!emojiToDelete && fg('platform_emoji_picker_refresh') ? withDeleteRefreshHeight[size] : uploading && fg('platform_emoji_picker_refresh') ? withUploadRefreshHeight[size] : showPreview ? withPreviewHeight[size] : withoutPreviewHeight[size]]),
705
+ className: ax([fg('platform_emoji_picker_refresh') ? "_19itahnd _2rko1mok _1e0c1txw _2lx21bp4 _1bah1yb4 _bfhk1bhr _16qs130s _4t3iuxo9 _1bsb10mj _1ul910mj _c71l1y6z _kqswh2mm" : "_19itahnd _2rkofajl _1e0c1txw _2lx21bp4 _1bah1yb4 _bfhk1bhr _16qs130s _4t3iuxo9 _1bsb10mj _1ul910mj _c71l1y6z _kqswh2mm", !!emojiToDelete && fg('platform_emoji_picker_refresh') ? withDeleteRefreshHeight[size] : uploading && fg('platform_emoji_picker_refresh') ? withUploadRefreshHeight[size] : query && filteredEmojis.length === 0 && fg('platform_emoji_picker_refresh') ? withNoResultsRefreshHeight[size] : showPreview ? withPreviewHeight[size] : withoutPreviewHeight[size]]),
692
706
  style: {
693
707
  "--_19dn98e": ix("".concat(emojiPickerHeight, "px")),
694
708
  "--_gsvyy7": ix("".concat(emojiPickerWidth, "px"))
@@ -727,7 +741,7 @@ var EmojiPickerComponent = function EmojiPickerComponent(_ref) {
727
741
  onOpenUpload: onOpenUpload,
728
742
  size: size,
729
743
  activeCategoryId: activeCategory
730
- }), showPreview && !(emojiToDelete && fg('platform_emoji_picker_refresh')) && /*#__PURE__*/React.createElement(EmojiPickerFooter, {
744
+ }), showPreview && !(emojiToDelete && fg('platform_emoji_picker_refresh')) && !(query && filteredEmojis.length === 0 && fg('platform_emoji_picker_refresh')) && /*#__PURE__*/React.createElement(EmojiPickerFooter, {
731
745
  selectedEmoji: selectedEmoji,
732
746
  uploadEnabled: isUploadSupported && !uploading,
733
747
  onOpenUpload: onOpenUpload
@@ -1,17 +1,19 @@
1
1
  import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
2
  import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
3
+ import { useIntl } from 'react-intl';
3
4
  import React, { useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
4
5
  import { fg } from '@atlaskit/platform-feature-flags';
5
6
  import { customCategory, defaultEmojiPickerSize, frequentCategory, searchCategory, userCustomTitle, yourUploadsCategory } from '../../util/constants';
6
7
  import { CategoryDescriptionMap, CategoryDescriptionMapNew } from './categories';
7
8
  import CategoryTracker from './CategoryTracker';
8
9
  import { sizes } from './EmojiPickerSizes';
9
- import { CategoryHeadingItem, EmojisRowItem, LoadingItem, virtualItemRenderer } from './EmojiPickerVirtualItems';
10
+ import { CategoryHeadingItem, EmojisRowItem, LoadingItem, NoResultsItem, virtualItemRenderer } from './EmojiPickerVirtualItems';
10
11
  import EmojiActions from '../common/EmojiActions';
11
12
  import { emojiPickerHeightOffset, scrollToRow as _scrollToRow } from './utils';
12
13
  import { VirtualList } from './VirtualList';
13
14
  import { EmojiPickerListContextProvider } from '../../context/EmojiPickerListContext';
14
15
  import EmojiPickerTabPanel from './EmojiPickerTabPanel';
16
+ import { messages } from '../i18n';
15
17
 
16
18
  /**
17
19
  * Test id for wrapper Emoji Picker List div
@@ -60,6 +62,8 @@ export var EmojiPickerVirtualListInternal = /*#__PURE__*/React.forwardRef(functi
60
62
  onFileChooserClicked = props.onFileChooserClicked,
61
63
  onOpenUpload = props.onOpenUpload,
62
64
  activeCategoryId = props.activeCategoryId;
65
+ var _useIntl = useIntl(),
66
+ formatMessage = _useIntl.formatMessage;
63
67
  var listRef = useRef(null);
64
68
  var _useState = useState([]),
65
69
  _useState2 = _slicedToArray(_useState, 2),
@@ -177,14 +181,27 @@ export var EmojiPickerVirtualListInternal = /*#__PURE__*/React.forwardRef(functi
177
181
  items.push(new LoadingItem());
178
182
  } else {
179
183
  if (query) {
180
- var search = CategoryDescriptionMap.SEARCH;
181
- // Only a single "result" category
182
- items = [].concat(_toConsumableArray(items), _toConsumableArray(buildVirtualItemFromGroup({
183
- category: searchCategory,
184
- title: search.name,
185
- emojis: emojis,
186
- order: search.order
187
- })));
184
+ var search = fg('platform_emoji_picker_refresh') ? CategoryDescriptionMapNew.SEARCH : CategoryDescriptionMap.SEARCH;
185
+ if (emojis.length === 0 && fg('platform_emoji_picker_refresh')) {
186
+ // Show a "No results" category heading, then a no-results illustration below it
187
+ items.push(new CategoryHeadingItem({
188
+ id: searchCategory,
189
+ title: formatMessage(messages.emojiPickerNoResults),
190
+ className: categoryClassname
191
+ }));
192
+ items.push(new NoResultsItem({
193
+ onOpenUpload: onOpenUpload,
194
+ uploadEnabled: uploadEnabled
195
+ }));
196
+ } else {
197
+ // Only a single "result" category
198
+ items = [].concat(_toConsumableArray(items), _toConsumableArray(buildVirtualItemFromGroup({
199
+ category: searchCategory,
200
+ title: search.name,
201
+ emojis: emojis,
202
+ order: search.order
203
+ })));
204
+ }
188
205
  } else {
189
206
  // Group by category
190
207
 
@@ -209,7 +226,7 @@ export var EmojiPickerVirtualListInternal = /*#__PURE__*/React.forwardRef(functi
209
226
  }
210
227
  }
211
228
  // eslint-disable-next-line react-hooks/exhaustive-deps
212
- }, [allEmojiGroups, loading, query, emojis]);
229
+ }, [allEmojiGroups, loading, query, emojis, onOpenUpload, uploadEnabled, formatMessage]);
213
230
  var findCategoryToActivate = function findCategoryToActivate(row) {
214
231
  var category = null;
215
232
  if (row instanceof CategoryHeadingItem) {
@@ -319,8 +336,12 @@ export var EmojiPickerVirtualListInternal = /*#__PURE__*/React.forwardRef(functi
319
336
  // eslint-disable-next-line react-hooks/exhaustive-deps
320
337
  }, [virtualItems, categoriesChanged]);
321
338
  var virtualListHeight = useMemo(function () {
339
+ if (query && emojis.length === 0 && fg('platform_emoji_picker_refresh')) {
340
+ // No-results state: expand the list height to fit heading + illustration without scrolling
341
+ return sizes.categoryHeadingHeight + sizes.noResultsHeight + emojiPickerHeightOffset(size);
342
+ }
322
343
  return fg('platform_emoji_picker_refresh') ? sizes.listHeightNew + emojiPickerHeightOffset(size) : sizes.listHeight + emojiPickerHeightOffset(size);
323
- }, [size]);
344
+ }, [size, query, emojis.length]);
324
345
  return /*#__PURE__*/React.createElement(EmojiPickerTabPanel, {
325
346
  showSearchResults: !!query
326
347
  }, /*#__PURE__*/React.createElement(EmojiActions, {
@@ -0,0 +1,12 @@
1
+
2
+ ._zulppxbi{gap:var(--ds-space-200,1pc)}._19bvpxbi{padding-left:var(--ds-space-200,1pc)}
3
+ ._1bah1h6o{justify-content:center}
4
+ ._1bsb1osq{width:100%}
5
+ ._1e0c1txw{display:flex}
6
+ ._2lx21bp4{flex-direction:column}
7
+ ._4cvr1h6o{align-items:center}
8
+ ._ca0q1ejb{padding-top:var(--ds-space-300,24px)}
9
+ ._n3td1ejb{padding-bottom:var(--ds-space-300,24px)}
10
+ ._u5f3pxbi{padding-right:var(--ds-space-200,1pc)}
11
+ ._vchhusvi{box-sizing:border-box}
12
+ ._y3gn1h6o{text-align:center}
@@ -0,0 +1,41 @@
1
+ /* EmojiPickerNoResults.tsx generated by @compiled/babel-plugin v0.39.1 */
2
+ import "./EmojiPickerNoResults.compiled.css";
3
+ import * as React from 'react';
4
+ import { ax, ix } from "@compiled/react/runtime";
5
+ import { useCallback } from 'react';
6
+ import { fg } from '@atlaskit/platform-feature-flags';
7
+ import { FormattedMessage } from 'react-intl';
8
+ import AkButton from '@atlaskit/button/standard-button';
9
+ import Image from '@atlaskit/image';
10
+ import { messages } from '../i18n';
11
+ import SearchNoResultDark from './assets/spot/search-no-result/dark.svg';
12
+ import SearchNoResultLight from './assets/spot/search-no-result/light.svg';
13
+ var noResultsContainer = null;
14
+ export var RENDER_EMOJI_PICKER_NO_RESULTS_TESTID = 'render-emoji-picker-no-results';
15
+ var EmojiPickerNoResults = function EmojiPickerNoResults(_ref) {
16
+ var onOpenUpload = _ref.onOpenUpload,
17
+ uploadEnabled = _ref.uploadEnabled;
18
+ var handleOpenUpload = useCallback(function (event) {
19
+ if (fg('platform_emoji_keep_picker_open_on_upload')) {
20
+ event.preventDefault();
21
+ event.stopPropagation();
22
+ }
23
+ onOpenUpload();
24
+ }, [onOpenUpload]);
25
+ return /*#__PURE__*/React.createElement("div", {
26
+ "data-testid": RENDER_EMOJI_PICKER_NO_RESULTS_TESTID,
27
+ className: ax(["_zulppxbi _1e0c1txw _2lx21bp4 _4cvr1h6o _1bah1h6o _ca0q1ejb _u5f3pxbi _n3td1ejb _19bvpxbi _y3gn1h6o _1bsb1osq _vchhusvi"])
28
+ }, /*#__PURE__*/React.createElement(Image, {
29
+ src: SearchNoResultLight,
30
+ srcDark: SearchNoResultDark,
31
+ alt: "",
32
+ width: 200
33
+ }), uploadEnabled && /*#__PURE__*/React.createElement(FormattedMessage, messages.emojiPickerAddCustomEmoji, function (label) {
34
+ return /*#__PURE__*/React.createElement(AkButton, {
35
+ onClick: handleOpenUpload,
36
+ appearance: "default",
37
+ tabIndex: 0
38
+ }, label);
39
+ }));
40
+ };
41
+ export default EmojiPickerNoResults;
@@ -13,6 +13,8 @@ export var sizes = {
13
13
  // 32px height
14
14
  loadingRowHeight: 150,
15
15
  // Fills remaining space without scrolling when loading.
16
+ noResultsHeight: 300,
17
+ // illustration (200px) + button + padding + gap
16
18
  uploadActionHeight: 40,
17
19
  // 40px height
18
20
  emojiPerRow: 8
@@ -14,6 +14,7 @@ function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.
14
14
  import Spinner from '@atlaskit/spinner';
15
15
  import EmojiPickerCategoryHeading from './EmojiPickerCategoryHeading';
16
16
  import EmojiPickerEmojiRow from './EmojiPickerEmojiRow';
17
+ import EmojiPickerNoResults from './EmojiPickerNoResults';
17
18
  import { sizes } from './EmojiPickerSizes';
18
19
  var emojiPickerSpinner = null;
19
20
  export var AbstractItem = /*#__PURE__*/_createClass(function AbstractItem(props, height) {
@@ -66,6 +67,19 @@ export var CategoryHeadingItem = /*#__PURE__*/function (_AbstractItem3) {
66
67
  _inherits(CategoryHeadingItem, _AbstractItem3);
67
68
  return _createClass(CategoryHeadingItem);
68
69
  }(AbstractItem);
70
+ export var NoResultsItem = /*#__PURE__*/function (_AbstractItem4) {
71
+ function NoResultsItem(props) {
72
+ var _this4;
73
+ _classCallCheck(this, NoResultsItem);
74
+ _this4 = _callSuper(this, NoResultsItem, [props, sizes.noResultsHeight]);
75
+ _defineProperty(_this4, "renderItem", function () {
76
+ return /*#__PURE__*/React.createElement(EmojiPickerNoResults, _this4.props);
77
+ });
78
+ return _this4;
79
+ }
80
+ _inherits(NoResultsItem, _AbstractItem4);
81
+ return _createClass(NoResultsItem);
82
+ }(AbstractItem);
69
83
  export var virtualItemRenderer = function virtualItemRenderer(rows, context) {
70
84
  var index = context.index,
71
85
  key = context.key;
@@ -14,7 +14,7 @@ var createEvent = function createEvent(eventType, action, actionSubject, actionS
14
14
  actionSubjectId: actionSubjectId,
15
15
  attributes: _objectSpread({
16
16
  packageName: "@atlaskit/emoji",
17
- packageVersion: "70.10.14"
17
+ packageVersion: "70.11.1"
18
18
  }, attributes)
19
19
  };
20
20
  };
@@ -119,11 +119,21 @@ export declare const messages: {
119
119
  description: string;
120
120
  id: string;
121
121
  };
122
+ emojiPickerAddCustomEmoji: {
123
+ defaultMessage: string;
124
+ description: string;
125
+ id: string;
126
+ };
122
127
  emojiPickerGrid: {
123
128
  defaultMessage: string;
124
129
  description: string;
125
130
  id: string;
126
131
  };
132
+ emojiPickerNoResults: {
133
+ defaultMessage: string;
134
+ description: string;
135
+ id: string;
136
+ };
127
137
  emojiPickerListPanel: {
128
138
  defaultMessage: string;
129
139
  description: string;
@@ -159,6 +169,11 @@ export declare const messages: {
159
169
  description: string;
160
170
  id: string;
161
171
  };
172
+ emojiUnsupportedFileType: {
173
+ defaultMessage: string;
174
+ description: string;
175
+ id: string;
176
+ };
162
177
  emojiUploadFailed: {
163
178
  defaultMessage: string;
164
179
  description: string;
@@ -0,0 +1,7 @@
1
+ export declare const RENDER_EMOJI_PICKER_NO_RESULTS_TESTID = "render-emoji-picker-no-results";
2
+ export interface Props {
3
+ onOpenUpload: () => void;
4
+ uploadEnabled: boolean;
5
+ }
6
+ declare const EmojiPickerNoResults: ({ onOpenUpload, uploadEnabled }: Props) => JSX.Element;
7
+ export default EmojiPickerNoResults;
@@ -7,6 +7,7 @@ export declare const sizes: {
7
7
  listHeightNew: number;
8
8
  listWidth: number;
9
9
  loadingRowHeight: number;
10
+ noResultsHeight: number;
10
11
  searchHeight: number;
11
12
  uploadActionHeight: number;
12
13
  };
@@ -5,6 +5,7 @@
5
5
  import type { ReactNode } from 'react';
6
6
  import { type Props as CategoryHeadingProps } from './EmojiPickerCategoryHeading';
7
7
  import { type Props as EmojiRowProps } from './EmojiPickerEmojiRow';
8
+ import { type Props as NoResultsProps } from './EmojiPickerNoResults';
8
9
  import type { VirtualItem as VirtualItemContext } from '@tanstack/react-virtual';
9
10
  export interface RenderItem {
10
11
  (context?: VirtualItemContext): ReactNode;
@@ -32,4 +33,8 @@ export declare class CategoryHeadingItem extends AbstractItem<CategoryHeadingPro
32
33
  constructor(props: CategoryHeadingProps);
33
34
  renderItem: () => JSX.Element;
34
35
  }
35
- export declare const virtualItemRenderer: (rows: VirtualItem<CategoryHeadingProps | EmojiRowProps | {}>[], context: VirtualItemContext) => JSX.Element;
36
+ export declare class NoResultsItem extends AbstractItem<NoResultsProps> {
37
+ constructor(props: NoResultsProps);
38
+ renderItem: () => JSX.Element;
39
+ }
40
+ export declare const virtualItemRenderer: (rows: VirtualItem<CategoryHeadingProps | EmojiRowProps | NoResultsProps | {}>[], context: VirtualItemContext) => JSX.Element;