@atlaskit/emoji 67.2.0 → 67.3.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.
- package/CHANGELOG.md +20 -0
- package/dist/cjs/api/EmojiResource.js +6 -0
- package/dist/cjs/components/common/DeletableEmojiTooltipContent.js +34 -0
- package/dist/cjs/components/common/DeleteButton.js +2 -1
- package/dist/cjs/components/common/Emoji.js +94 -81
- package/dist/cjs/components/common/EmojiActions.js +8 -6
- package/dist/cjs/components/common/EmojiDeletePreview.js +19 -6
- package/dist/cjs/components/common/EmojiErrorMessage.js +13 -5
- package/dist/cjs/components/common/EmojiRadioButton.js +1 -0
- package/dist/cjs/components/common/EmojiUploadPicker.js +30 -29
- package/dist/cjs/components/common/EmojiUploadPreview.js +12 -5
- package/dist/cjs/components/common/FileChooser.js +7 -1
- package/dist/cjs/components/common/ResourcedEmoji.js +5 -3
- package/dist/cjs/components/common/RetryableButton.js +9 -3
- package/dist/cjs/components/common/ToolTipContentWithKeymap.js +25 -0
- package/dist/cjs/components/common/styles.js +45 -19
- package/dist/cjs/components/i18n.js +17 -2
- package/dist/cjs/components/picker/CategorySelector.js +2 -1
- package/dist/cjs/components/picker/CategoryTracker.js +5 -0
- package/dist/cjs/components/picker/EmojiPickerComponent.js +12 -6
- package/dist/cjs/components/picker/EmojiPickerList.js +49 -8
- package/dist/cjs/components/picker/EmojiPickerListSearch.js +4 -1
- package/dist/cjs/components/picker/VirtualList.js +8 -3
- package/dist/cjs/{util → hooks}/useInView.js +3 -2
- package/dist/cjs/util/browser-support.js +11 -3
- package/dist/cjs/util/constants.js +4 -1
- package/dist/cjs/util/keymaps.js +55 -0
- package/dist/cjs/version.json +1 -1
- package/dist/es2019/api/EmojiResource.js +6 -0
- package/dist/es2019/components/common/DeletableEmojiTooltipContent.js +27 -0
- package/dist/es2019/components/common/DeleteButton.js +2 -1
- package/dist/es2019/components/common/Emoji.js +90 -72
- package/dist/es2019/components/common/EmojiActions.js +9 -7
- package/dist/es2019/components/common/EmojiDeletePreview.js +17 -7
- package/dist/es2019/components/common/EmojiErrorMessage.js +10 -4
- package/dist/es2019/components/common/EmojiRadioButton.js +1 -0
- package/dist/es2019/components/common/EmojiUploadPicker.js +32 -27
- package/dist/es2019/components/common/EmojiUploadPreview.js +9 -5
- package/dist/es2019/components/common/FileChooser.js +7 -1
- package/dist/es2019/components/common/ResourcedEmoji.js +5 -3
- package/dist/es2019/components/common/RetryableButton.js +9 -3
- package/dist/es2019/components/common/ToolTipContentWithKeymap.js +15 -0
- package/dist/es2019/components/common/styles.js +39 -15
- package/dist/es2019/components/i18n.js +17 -2
- package/dist/es2019/components/picker/CategorySelector.js +2 -1
- package/dist/es2019/components/picker/CategoryTracker.js +3 -0
- package/dist/es2019/components/picker/EmojiPickerComponent.js +12 -6
- package/dist/es2019/components/picker/EmojiPickerList.js +48 -8
- package/dist/es2019/components/picker/EmojiPickerListSearch.js +3 -1
- package/dist/es2019/components/picker/VirtualList.js +7 -3
- package/dist/es2019/{util → hooks}/useInView.js +2 -2
- package/dist/es2019/util/browser-support.js +9 -1
- package/dist/es2019/util/constants.js +2 -0
- package/dist/es2019/util/keymaps.js +43 -0
- package/dist/es2019/version.json +1 -1
- package/dist/esm/api/EmojiResource.js +6 -0
- package/dist/esm/components/common/DeletableEmojiTooltipContent.js +25 -0
- package/dist/esm/components/common/DeleteButton.js +2 -1
- package/dist/esm/components/common/Emoji.js +94 -82
- package/dist/esm/components/common/EmojiActions.js +9 -7
- package/dist/esm/components/common/EmojiDeletePreview.js +17 -7
- package/dist/esm/components/common/EmojiErrorMessage.js +11 -4
- package/dist/esm/components/common/EmojiRadioButton.js +1 -0
- package/dist/esm/components/common/EmojiUploadPicker.js +32 -31
- package/dist/esm/components/common/EmojiUploadPreview.js +9 -5
- package/dist/esm/components/common/FileChooser.js +7 -1
- package/dist/esm/components/common/ResourcedEmoji.js +5 -3
- package/dist/esm/components/common/RetryableButton.js +9 -3
- package/dist/esm/components/common/ToolTipContentWithKeymap.js +14 -0
- package/dist/esm/components/common/styles.js +38 -16
- package/dist/esm/components/i18n.js +17 -2
- package/dist/esm/components/picker/CategorySelector.js +2 -1
- package/dist/esm/components/picker/CategoryTracker.js +5 -0
- package/dist/esm/components/picker/EmojiPickerComponent.js +12 -6
- package/dist/esm/components/picker/EmojiPickerList.js +49 -8
- package/dist/esm/components/picker/EmojiPickerListSearch.js +4 -1
- package/dist/esm/components/picker/VirtualList.js +8 -3
- package/dist/esm/{util → hooks}/useInView.js +2 -2
- package/dist/esm/util/browser-support.js +9 -1
- package/dist/esm/util/constants.js +2 -0
- package/dist/esm/util/keymaps.js +43 -0
- package/dist/esm/version.json +1 -1
- package/dist/types/components/common/DeletableEmojiTooltipContent.d.ts +6 -0
- package/dist/types/components/common/Emoji.d.ts +6 -0
- package/dist/types/components/common/EmojiErrorMessage.d.ts +1 -0
- package/dist/types/components/common/FileChooser.d.ts +1 -0
- package/dist/types/components/common/RetryableButton.d.ts +2 -1
- package/dist/types/components/common/ToolTipContentWithKeymap.d.ts +9 -0
- package/dist/types/components/common/styles.d.ts +6 -2
- package/dist/types/components/i18n.d.ts +15 -0
- package/dist/types/components/picker/CategoryTracker.d.ts +1 -0
- package/dist/types/components/picker/EmojiPickerList.d.ts +8 -1
- package/dist/types/components/picker/EmojiPickerListSearch.d.ts +1 -0
- package/dist/types/components/picker/VirtualList.d.ts +21 -1
- package/dist/types/types.d.ts +1 -1
- package/dist/types/util/browser-support.d.ts +6 -1
- package/dist/types/util/constants.d.ts +3 -1
- package/dist/types/util/keymaps.d.ts +14 -0
- package/dist/types/util/type-helpers.d.ts +1 -1
- package/package.json +2 -2
- package/report.api.md +16 -1
- /package/dist/types/{util → hooks}/useInView.d.ts +0 -0
|
@@ -5,12 +5,15 @@ import { Component } from 'react';
|
|
|
5
5
|
import { jsx } from '@emotion/react';
|
|
6
6
|
import { FormattedMessage, injectIntl } from 'react-intl-next';
|
|
7
7
|
import AkButton from '@atlaskit/button/custom-theme-button';
|
|
8
|
+
import FocusLock from 'react-focus-lock';
|
|
8
9
|
import { messages } from '../i18n';
|
|
9
10
|
import CachingEmoji from './CachingEmoji';
|
|
10
|
-
import EmojiErrorMessage from './EmojiErrorMessage';
|
|
11
|
+
import EmojiErrorMessage, { emojiErrorScreenreaderTestId } from './EmojiErrorMessage';
|
|
11
12
|
import RetryableButton from './RetryableButton';
|
|
12
|
-
import { cancelButton, deleteFooter, deletePreview, deleteText, emojiDeleteErrorMessage, previewButtonGroup } from './styles';
|
|
13
|
+
import { cancelButton, deleteFooter, deletePreview, deleteText, emojiDeleteErrorMessage, headingH5, previewButtonGroup } from './styles';
|
|
14
|
+
import VisuallyHidden from '@atlaskit/visually-hidden';
|
|
13
15
|
export const emojiDeletePreviewTestId = 'emoji-delete-preview';
|
|
16
|
+
const deleteEmojiLabelId = 'fabric.emoji.delete.label.id';
|
|
14
17
|
class EmojiDeletePreview extends Component {
|
|
15
18
|
constructor(props) {
|
|
16
19
|
super(props);
|
|
@@ -63,12 +66,16 @@ class EmojiDeletePreview extends Component {
|
|
|
63
66
|
const {
|
|
64
67
|
formatMessage
|
|
65
68
|
} = intl;
|
|
66
|
-
return jsx(
|
|
69
|
+
return jsx(FocusLock, {
|
|
70
|
+
noFocusGuards: true
|
|
71
|
+
}, jsx("div", {
|
|
67
72
|
css: deletePreview,
|
|
68
73
|
"data-testid": emojiDeletePreviewTestId
|
|
69
74
|
}, jsx("div", {
|
|
70
75
|
css: deleteText
|
|
71
|
-
}, jsx("
|
|
76
|
+
}, jsx("h2", {
|
|
77
|
+
css: headingH5
|
|
78
|
+
}, jsx(FormattedMessage, messages.deleteEmojiTitle)), jsx(FormattedMessage, _extends({}, messages.deleteEmojiDescription, {
|
|
72
79
|
values: {
|
|
73
80
|
emojiShortName: emoji.shortName
|
|
74
81
|
}
|
|
@@ -82,17 +89,20 @@ class EmojiDeletePreview extends Component {
|
|
|
82
89
|
message: formatMessage(messages.deleteEmojiFailed),
|
|
83
90
|
messageStyles: emojiDeleteErrorMessage,
|
|
84
91
|
tooltip: true
|
|
85
|
-
}) : null : null, jsx(
|
|
92
|
+
}) : null : null, jsx(VisuallyHidden, {
|
|
93
|
+
id: deleteEmojiLabelId
|
|
94
|
+
}, formatMessage(messages.deleteEmojiLabel)), jsx(RetryableButton, {
|
|
86
95
|
label: formatMessage(messages.deleteEmojiLabel),
|
|
87
96
|
onSubmit: this.onSubmit,
|
|
88
97
|
appearance: "danger",
|
|
89
98
|
loading: loading,
|
|
90
|
-
error: error
|
|
99
|
+
error: error,
|
|
100
|
+
ariaLabelledBy: `${emojiErrorScreenreaderTestId} ${deleteEmojiLabelId}`
|
|
91
101
|
}), jsx(AkButton, {
|
|
92
102
|
appearance: "subtle",
|
|
93
103
|
onClick: this.onCancel,
|
|
94
104
|
css: cancelButton
|
|
95
|
-
}, jsx(FormattedMessage, messages.cancelLabel)))));
|
|
105
|
+
}, jsx(FormattedMessage, messages.cancelLabel))))));
|
|
96
106
|
}
|
|
97
107
|
}
|
|
98
108
|
export default injectIntl(EmojiDeletePreview);
|
|
@@ -4,6 +4,9 @@ import { jsx } from '@emotion/react';
|
|
|
4
4
|
import Tooltip from '@atlaskit/tooltip';
|
|
5
5
|
import ErrorIcon from '@atlaskit/icon/glyph/error';
|
|
6
6
|
import VisuallyHidden from '@atlaskit/visually-hidden';
|
|
7
|
+
import { FormattedMessage, useIntl } from 'react-intl-next';
|
|
8
|
+
import { messages } from '../i18n';
|
|
9
|
+
export const emojiErrorScreenreaderTestId = 'emoji-error-screenreader-message';
|
|
7
10
|
export const emojiErrorMessageTestId = 'emoji-error-message';
|
|
8
11
|
export const emojiErrorMessageTooltipTestId = 'emoji-error-message-tooltip';
|
|
9
12
|
export const emojiErrorIconTestId = 'emoji-error-icon';
|
|
@@ -13,6 +16,9 @@ const EmojiErrorMessage = props => {
|
|
|
13
16
|
message,
|
|
14
17
|
tooltip
|
|
15
18
|
} = props;
|
|
19
|
+
const {
|
|
20
|
+
formatMessage
|
|
21
|
+
} = useIntl();
|
|
16
22
|
const visualContent = tooltip ? jsx("div", {
|
|
17
23
|
css: messageStyles,
|
|
18
24
|
"data-testid": emojiErrorMessageTestId
|
|
@@ -21,18 +27,18 @@ const EmojiErrorMessage = props => {
|
|
|
21
27
|
position: "top",
|
|
22
28
|
testId: emojiErrorMessageTooltipTestId
|
|
23
29
|
}, jsx(ErrorIcon, {
|
|
24
|
-
label:
|
|
30
|
+
label: formatMessage(messages.error),
|
|
25
31
|
size: "medium",
|
|
26
32
|
testId: emojiErrorIconTestId
|
|
27
33
|
}))) : jsx("div", {
|
|
28
34
|
css: messageStyles,
|
|
29
35
|
"data-testid": emojiErrorMessageTestId
|
|
30
36
|
}, jsx(ErrorIcon, {
|
|
31
|
-
label:
|
|
37
|
+
label: formatMessage(messages.error),
|
|
32
38
|
size: "small"
|
|
33
39
|
}), message);
|
|
34
40
|
return jsx(Fragment, null, jsx(VisuallyHidden, {
|
|
35
|
-
|
|
36
|
-
}, message), visualContent);
|
|
41
|
+
id: emojiErrorScreenreaderTestId
|
|
42
|
+
}, jsx(FormattedMessage, messages.error, errMsg => jsx("span", null, errMsg, " ", message, "."))), visualContent);
|
|
37
43
|
};
|
|
38
44
|
export default EmojiErrorMessage;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** @jsx jsx */
|
|
2
|
-
import { Fragment, useEffect, useState, useRef, memo, useCallback
|
|
2
|
+
import { Fragment, useEffect, useLayoutEffect, useState, useRef, memo, useCallback } from 'react';
|
|
3
3
|
import { jsx } from '@emotion/react';
|
|
4
4
|
import { FormattedMessage, injectIntl } from 'react-intl-next';
|
|
5
5
|
import TextField from '@atlaskit/textfield';
|
|
@@ -9,14 +9,15 @@ import FocusLock from 'react-focus-lock';
|
|
|
9
9
|
import * as ImageUtil from '../../util/image';
|
|
10
10
|
import debug from '../../util/logger';
|
|
11
11
|
import { messages } from '../i18n';
|
|
12
|
-
import EmojiErrorMessage from './EmojiErrorMessage';
|
|
12
|
+
import EmojiErrorMessage, { emojiErrorScreenreaderTestId } from './EmojiErrorMessage';
|
|
13
13
|
import EmojiUploadPreview from './EmojiUploadPreview';
|
|
14
14
|
import FileChooser from './FileChooser';
|
|
15
15
|
import { UploadStatus } from './internal-types';
|
|
16
|
-
import { closeEmojiUploadButton, emojiChooseFileErrorMessage, emojiUpload, emojiUploadBottom, emojiUploadTop, uploadChooseFileBrowse, uploadChooseFileEmojiName, uploadChooseFileMessage, uploadChooseFileRow } from './styles';
|
|
16
|
+
import { closeEmojiUploadButton, emojiChooseFileErrorMessage, emojiUpload, emojiUploadBottom, emojiUploadTop, headingH5, requiredSymbol, uploadChooseFileBrowse, uploadChooseFileEmojiName, uploadChooseFileMessage, uploadChooseFileRow } from './styles';
|
|
17
17
|
export const uploadEmojiNameInputTestId = 'upload-emoji-name-input';
|
|
18
18
|
export const uploadEmojiComponentTestId = 'upload-emoji-component';
|
|
19
19
|
export const cancelEmojiUploadPickerTestId = 'cancel-emoji-upload-picker';
|
|
20
|
+
const addCustomEmojiChooseFileScreenreaderId = 'fabric.emoji.choose.file.label.id';
|
|
20
21
|
const disallowedReplacementsMap = new Map([[':', ''], ['!', ''], ['@', ''], ['#', ''], ['%', ''], ['^', ''], ['&', ''], ['*', ''], ['(', ''], [')', ''], [' ', '_']]);
|
|
21
22
|
const sanitizeName = name => {
|
|
22
23
|
// prevent / replace certain characters, allow others
|
|
@@ -51,18 +52,12 @@ const ChooseEmojiFile = /*#__PURE__*/memo(props => {
|
|
|
51
52
|
onUploadCancelled();
|
|
52
53
|
}
|
|
53
54
|
}, [onUploadCancelled]);
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
55
|
+
useLayoutEffect(() => {
|
|
56
|
+
requestAnimationFrame(() => {
|
|
57
|
+
var _inputRef$current;
|
|
58
|
+
(_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 ? void 0 : _inputRef$current.focus();
|
|
59
|
+
});
|
|
60
60
|
}, []);
|
|
61
|
-
|
|
62
|
-
// make sure input has focus after update
|
|
63
|
-
useEffect(() => {
|
|
64
|
-
window.requestAnimationFrame(setInputFocus);
|
|
65
|
-
}, [setInputFocus]);
|
|
66
61
|
const cancelLabel = formatMessage(messages.cancelLabel);
|
|
67
62
|
const emojiPlaceholder = formatMessage(messages.emojiPlaceholder);
|
|
68
63
|
const emojiNameAriaLabel = formatMessage(messages.emojiNameAriaLabel);
|
|
@@ -72,9 +67,12 @@ const ChooseEmojiFile = /*#__PURE__*/memo(props => {
|
|
|
72
67
|
"data-testid": uploadEmojiComponentTestId
|
|
73
68
|
}, jsx("div", {
|
|
74
69
|
css: emojiUploadTop
|
|
75
|
-
}, jsx("
|
|
76
|
-
css: uploadChooseFileMessage
|
|
77
|
-
}, jsx(FormattedMessage, messages.addCustomEmojiLabel
|
|
70
|
+
}, jsx("h2", {
|
|
71
|
+
css: [uploadChooseFileMessage, headingH5]
|
|
72
|
+
}, jsx(FormattedMessage, messages.addCustomEmojiLabel), jsx("span", {
|
|
73
|
+
"aria-hidden": "true",
|
|
74
|
+
css: requiredSymbol
|
|
75
|
+
}, "*")), jsx("div", {
|
|
78
76
|
css: closeEmojiUploadButton
|
|
79
77
|
}, jsx(AkButton, {
|
|
80
78
|
onClick: onUploadCancelled,
|
|
@@ -101,18 +99,23 @@ const ChooseEmojiFile = /*#__PURE__*/memo(props => {
|
|
|
101
99
|
autoFocus: true,
|
|
102
100
|
testId: uploadEmojiNameInputTestId,
|
|
103
101
|
ref: inputRef,
|
|
104
|
-
id: "new-emoji-name-input"
|
|
102
|
+
id: "new-emoji-name-input",
|
|
103
|
+
"aria-required": true
|
|
105
104
|
})), jsx("span", {
|
|
106
105
|
css: uploadChooseFileBrowse
|
|
107
106
|
}, jsx(FormattedMessage, messages.emojiChooseFileScreenReaderDescription, screenReaderDescription => jsx(Fragment, null, jsx("span", {
|
|
108
107
|
hidden: true,
|
|
109
108
|
id: fileChooserButtonDescriptionId
|
|
110
|
-
}, screenReaderDescription), jsx(
|
|
109
|
+
}, screenReaderDescription), jsx("span", {
|
|
110
|
+
hidden: true,
|
|
111
|
+
id: addCustomEmojiChooseFileScreenreaderId
|
|
112
|
+
}, emojiChooseFileTitle), jsx(FileChooser, {
|
|
111
113
|
label: emojiChooseFileTitle,
|
|
112
114
|
onChange: onChooseFile,
|
|
113
115
|
onClick: onClick,
|
|
114
116
|
accept: "image/png,image/jpeg,image/gif",
|
|
115
117
|
ariaDescribedBy: fileChooserButtonDescriptionId,
|
|
118
|
+
ariaLabelledBy: `${emojiErrorScreenreaderTestId} ${addCustomEmojiChooseFileScreenreaderId}`,
|
|
116
119
|
isDisabled: disableChooser
|
|
117
120
|
}))))), jsx("div", {
|
|
118
121
|
css: emojiUploadBottom
|
|
@@ -122,7 +125,7 @@ const ChooseEmojiFile = /*#__PURE__*/memo(props => {
|
|
|
122
125
|
})));
|
|
123
126
|
});
|
|
124
127
|
const EmojiUploadPicker = props => {
|
|
125
|
-
var _document$
|
|
128
|
+
var _document$activeEleme;
|
|
126
129
|
const {
|
|
127
130
|
errorMessage,
|
|
128
131
|
initialUploadName,
|
|
@@ -137,7 +140,7 @@ const EmojiUploadPicker = props => {
|
|
|
137
140
|
const [filename, setFilename] = useState();
|
|
138
141
|
const [previewImage, setPreviewImage] = useState();
|
|
139
142
|
// document is undefined during ssr rendering and throws an error
|
|
140
|
-
const lastFocusedElementId = useRef(typeof document !== 'undefined' ? (_document$
|
|
143
|
+
const lastFocusedElementId = useRef(typeof document !== 'undefined' ? (_document$activeEleme = document.activeElement) === null || _document$activeEleme === void 0 ? void 0 : _document$activeEleme.id : '');
|
|
141
144
|
useEffect(() => {
|
|
142
145
|
if (errorMessage) {
|
|
143
146
|
setUploadStatus(UploadStatus.Error);
|
|
@@ -202,7 +205,7 @@ const EmojiUploadPicker = props => {
|
|
|
202
205
|
}, []);
|
|
203
206
|
const errorOnUpload = useCallback(event => {
|
|
204
207
|
debug('File load error: ', event);
|
|
205
|
-
setChooseEmojiErrorMessage(messages.emojiUploadFailed);
|
|
208
|
+
setChooseEmojiErrorMessage(jsx(FormattedMessage, messages.emojiUploadFailed));
|
|
206
209
|
cancelChooseFile();
|
|
207
210
|
}, [cancelChooseFile]);
|
|
208
211
|
const onFileLoad = useCallback(file => async f => {
|
|
@@ -211,7 +214,7 @@ const EmojiUploadPicker = props => {
|
|
|
211
214
|
await ImageUtil.parseImage(f.target.result);
|
|
212
215
|
setPreviewImage(f.target.result);
|
|
213
216
|
} catch {
|
|
214
|
-
setChooseEmojiErrorMessage(messages.emojiInvalidImage);
|
|
217
|
+
setChooseEmojiErrorMessage(jsx(FormattedMessage, messages.emojiInvalidImage));
|
|
215
218
|
cancelChooseFile();
|
|
216
219
|
}
|
|
217
220
|
}, [cancelChooseFile]);
|
|
@@ -221,7 +224,7 @@ const EmojiUploadPicker = props => {
|
|
|
221
224
|
const reader = new FileReader();
|
|
222
225
|
const file = files[0];
|
|
223
226
|
if (ImageUtil.hasFileExceededSize(file)) {
|
|
224
|
-
setChooseEmojiErrorMessage(messages.emojiImageTooBig);
|
|
227
|
+
setChooseEmojiErrorMessage(jsx(FormattedMessage, messages.emojiImageTooBig));
|
|
225
228
|
cancelChooseFile();
|
|
226
229
|
return;
|
|
227
230
|
}
|
|
@@ -245,7 +248,9 @@ const EmojiUploadPicker = props => {
|
|
|
245
248
|
}
|
|
246
249
|
}, 0, lastFocusedElementId.current);
|
|
247
250
|
}, [clearUploadPicker, onUploadCancelled]);
|
|
248
|
-
const
|
|
251
|
+
const onChooseFileClicked = () => {
|
|
252
|
+
onFileChooserClicked && onFileChooserClicked();
|
|
253
|
+
};
|
|
249
254
|
return jsx(FocusLock, {
|
|
250
255
|
noFocusGuards: true
|
|
251
256
|
}, name && previewImage ? jsx(EmojiUploadPreview, {
|
|
@@ -258,10 +263,10 @@ const EmojiUploadPicker = props => {
|
|
|
258
263
|
}) : jsx(ChooseEmojiFile, {
|
|
259
264
|
name: name,
|
|
260
265
|
onChooseFile: onChooseFile,
|
|
261
|
-
onClick:
|
|
266
|
+
onClick: onChooseFileClicked,
|
|
262
267
|
onNameChange: onNameChange,
|
|
263
268
|
onUploadCancelled: cancelUpload,
|
|
264
|
-
errorMessage:
|
|
269
|
+
errorMessage: chooseEmojiErrorMessage,
|
|
265
270
|
intl: intl
|
|
266
271
|
}));
|
|
267
272
|
};
|
|
@@ -7,14 +7,15 @@ import { FormattedMessage, injectIntl } from 'react-intl-next';
|
|
|
7
7
|
import { customCategory } from '../../util/constants';
|
|
8
8
|
import { messages } from '../i18n';
|
|
9
9
|
import Emoji from './Emoji';
|
|
10
|
-
import EmojiErrorMessage from './EmojiErrorMessage';
|
|
10
|
+
import EmojiErrorMessage, { emojiErrorScreenreaderTestId } from './EmojiErrorMessage';
|
|
11
11
|
import { UploadStatus } from './internal-types';
|
|
12
12
|
import RetryableButton from './RetryableButton';
|
|
13
13
|
import { bigEmojiPreview, cancelButton, emojiPreviewErrorMessage, uploadAddRow, uploadPreview, uploadPreviewFooter, uploadPreviewText } from './styles';
|
|
14
14
|
import VisuallyHidden from '@atlaskit/visually-hidden';
|
|
15
15
|
export const uploadPreviewTestId = 'upload-preview';
|
|
16
16
|
export const cancelUploadButtonTestId = 'cancel-upload-button';
|
|
17
|
-
const
|
|
17
|
+
const addEmojiPreviewDescriptionId = 'fabric.emoji.preview.description.id';
|
|
18
|
+
const addEmojiButtonLabelId = 'fabric.emoji.add.label.id';
|
|
18
19
|
class EmojiUploadPreview extends PureComponent {
|
|
19
20
|
render() {
|
|
20
21
|
const {
|
|
@@ -67,18 +68,21 @@ class EmojiUploadPreview extends PureComponent {
|
|
|
67
68
|
message: errorMessage,
|
|
68
69
|
tooltip: true
|
|
69
70
|
}) : null, !errorMessage && jsx(VisuallyHidden, {
|
|
70
|
-
id:
|
|
71
|
+
id: addEmojiPreviewDescriptionId
|
|
71
72
|
}, jsx(FormattedMessage, _extends({}, messages.emojiPreview, {
|
|
72
73
|
values: {
|
|
73
74
|
emoji: name
|
|
74
75
|
}
|
|
75
|
-
}))), jsx(
|
|
76
|
+
}))), jsx(VisuallyHidden, {
|
|
77
|
+
id: addEmojiButtonLabelId
|
|
78
|
+
}, errorMessage ? formatMessage(messages.retryLabel) : formatMessage(messages.addEmojiLabel)), jsx(RetryableButton, {
|
|
76
79
|
label: formatMessage(messages.addEmojiLabel),
|
|
77
80
|
onSubmit: onAddEmoji,
|
|
78
81
|
appearance: "primary",
|
|
79
82
|
loading: uploading,
|
|
80
83
|
error: !!errorMessage,
|
|
81
|
-
|
|
84
|
+
ariaDescribedBy: addEmojiPreviewDescriptionId,
|
|
85
|
+
ariaLabelledBy: `${emojiErrorScreenreaderTestId} ${addEmojiButtonLabelId}`
|
|
82
86
|
}), jsx(AkButton, {
|
|
83
87
|
onClick: onUploadCancelled,
|
|
84
88
|
appearance: "subtle",
|
|
@@ -6,13 +6,16 @@ const FileChooser = props => {
|
|
|
6
6
|
const {
|
|
7
7
|
accept,
|
|
8
8
|
ariaDescribedBy,
|
|
9
|
+
ariaLabelledBy,
|
|
9
10
|
isDisabled,
|
|
10
11
|
label,
|
|
11
12
|
onChange,
|
|
12
13
|
onClick
|
|
13
14
|
} = props;
|
|
14
15
|
const filePickerRef = useRef(null);
|
|
16
|
+
const fileButtonRef = useRef(null);
|
|
15
17
|
const handleOnChooseFile = () => {
|
|
18
|
+
var _fileButtonRef$curren;
|
|
16
19
|
if (!filePickerRef.current) {
|
|
17
20
|
return;
|
|
18
21
|
}
|
|
@@ -20,12 +23,15 @@ const FileChooser = props => {
|
|
|
20
23
|
onClick();
|
|
21
24
|
}
|
|
22
25
|
filePickerRef.current.click();
|
|
26
|
+
(_fileButtonRef$curren = fileButtonRef.current) === null || _fileButtonRef$curren === void 0 ? void 0 : _fileButtonRef$curren.focus();
|
|
23
27
|
};
|
|
24
28
|
return /*#__PURE__*/React.createElement("span", null, /*#__PURE__*/React.createElement(AkButton, {
|
|
25
29
|
onClick: handleOnChooseFile,
|
|
26
30
|
isDisabled: isDisabled,
|
|
27
31
|
"aria-describedby": ariaDescribedBy,
|
|
28
|
-
|
|
32
|
+
"aria-labelledby": ariaLabelledBy,
|
|
33
|
+
testId: chooseFileButtonTestId,
|
|
34
|
+
ref: fileButtonRef
|
|
29
35
|
}, label), /*#__PURE__*/React.createElement("input", {
|
|
30
36
|
className: "emojiUploadFileInput",
|
|
31
37
|
ref: filePickerRef,
|
|
@@ -9,7 +9,8 @@ const ResourcedEmojiComponent = Loadable({
|
|
|
9
9
|
});
|
|
10
10
|
const ResourcedEmoji = props => {
|
|
11
11
|
const {
|
|
12
|
-
emojiId
|
|
12
|
+
emojiId,
|
|
13
|
+
optimisticImageURL
|
|
13
14
|
} = props;
|
|
14
15
|
useEffect(() => {
|
|
15
16
|
if (!emojiId) {
|
|
@@ -20,7 +21,8 @@ const ResourcedEmoji = props => {
|
|
|
20
21
|
});
|
|
21
22
|
ufoExperiences['emoji-rendered'].getInstance(emojiId.id || emojiId.shortName).addMetadata({
|
|
22
23
|
source: 'ResourcedEmoji',
|
|
23
|
-
emojiId: emojiId.id
|
|
24
|
+
emojiId: emojiId.id,
|
|
25
|
+
isOptimisticImageURL: !!optimisticImageURL
|
|
24
26
|
});
|
|
25
27
|
return () => {
|
|
26
28
|
sampledUfoRenderedEmoji(emojiId).abort({
|
|
@@ -30,7 +32,7 @@ const ResourcedEmoji = props => {
|
|
|
30
32
|
}
|
|
31
33
|
});
|
|
32
34
|
};
|
|
33
|
-
}, [emojiId]);
|
|
35
|
+
}, [emojiId, optimisticImageURL]);
|
|
34
36
|
return /*#__PURE__*/React.createElement(UfoErrorBoundary, {
|
|
35
37
|
experiences: [ufoExperiences['emoji-rendered'].getInstance(props.emojiId.id || props.emojiId.shortName)]
|
|
36
38
|
}, /*#__PURE__*/React.createElement(ResourcedEmojiComponent, props));
|
|
@@ -15,13 +15,17 @@ const LoadingSpinner = () => {
|
|
|
15
15
|
};
|
|
16
16
|
const RetryButton = props => {
|
|
17
17
|
const {
|
|
18
|
-
onSubmit
|
|
18
|
+
onSubmit,
|
|
19
|
+
ariaLabelledBy,
|
|
20
|
+
ariaDescribedBy
|
|
19
21
|
} = props;
|
|
20
22
|
return jsx(FormattedMessage, messages.retryLabel, retryLabel => jsx(AkButton, {
|
|
21
23
|
css: uploadRetryButton,
|
|
22
24
|
appearance: "warning",
|
|
23
25
|
onClick: onSubmit,
|
|
24
26
|
testId: retryUploadButtonTestId,
|
|
27
|
+
"aria-describedby": ariaDescribedBy,
|
|
28
|
+
"aria-labelledby": ariaLabelledBy,
|
|
25
29
|
autoFocus: true
|
|
26
30
|
}, retryLabel));
|
|
27
31
|
};
|
|
@@ -30,14 +34,16 @@ const UploadButton = props => {
|
|
|
30
34
|
appearance,
|
|
31
35
|
onSubmit,
|
|
32
36
|
label,
|
|
33
|
-
|
|
37
|
+
ariaLabelledBy,
|
|
38
|
+
ariaDescribedBy
|
|
34
39
|
} = props;
|
|
35
40
|
return jsx(AkButton, {
|
|
36
41
|
css: uploadEmojiButton,
|
|
37
42
|
appearance: appearance,
|
|
38
43
|
onClick: onSubmit,
|
|
39
44
|
testId: uploadEmojiButtonTestId,
|
|
40
|
-
"aria-describedby":
|
|
45
|
+
"aria-describedby": ariaDescribedBy,
|
|
46
|
+
"aria-labelledby": ariaLabelledBy,
|
|
41
47
|
autoFocus: true
|
|
42
48
|
}, label);
|
|
43
49
|
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** @jsx jsx */
|
|
2
|
+
import React, { Fragment } from 'react';
|
|
3
|
+
import { jsx } from '@emotion/react';
|
|
4
|
+
import { formatShortcut } from '../../util/keymaps';
|
|
5
|
+
import { tooltipShortcutStyle } from './styles';
|
|
6
|
+
export const ToolTipContentWithKeymap = /*#__PURE__*/React.memo(({
|
|
7
|
+
description,
|
|
8
|
+
shortcutOverride,
|
|
9
|
+
keymap
|
|
10
|
+
}) => {
|
|
11
|
+
const shortcut = shortcutOverride || keymap && formatShortcut(keymap);
|
|
12
|
+
return shortcut || description ? jsx(Fragment, null, description, shortcut && description && '\u00A0', shortcut && jsx("span", {
|
|
13
|
+
css: tooltipShortcutStyle
|
|
14
|
+
}, shortcut)) : null;
|
|
15
|
+
});
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { css, keyframes } from '@emotion/react';
|
|
2
2
|
import { defaultEmojiHeight } from '../../util/constants';
|
|
3
3
|
import { akEmojiSelectedBackgroundColor } from '../../util/shared-styles';
|
|
4
|
-
import { B100, N20, N200, N20A, N300, N900, R300, R400 } from '@atlaskit/theme/colors';
|
|
4
|
+
import { B100, N20, N200, N20A, N300, N400, N900, R300, R400 } from '@atlaskit/theme/colors';
|
|
5
|
+
import { fontFamily as getFontFamily, gridSize as getGridSize } from '@atlaskit/theme/constants';
|
|
5
6
|
export const commonSelectedStyles = 'emoji-common-selected';
|
|
6
7
|
export const selectOnHoverStyles = 'emoji-common-select-on-hover';
|
|
7
8
|
export const emojiSprite = 'emoji-common-emoji-sprite';
|
|
@@ -9,18 +10,15 @@ export const emojiNodeStyles = 'emoji-common-node';
|
|
|
9
10
|
export const emojiImage = 'emoji-common-emoji-image';
|
|
10
11
|
export const emojiDeleteButton = 'emoji-common-deleteButton';
|
|
11
12
|
export const emojiMainStyle = 'emoji-common-main-styles';
|
|
13
|
+
export const deletableEmoji = 'emoji-common-deletable';
|
|
12
14
|
export const deleteButton = css({
|
|
13
15
|
// hide by default
|
|
14
16
|
visibility: 'hidden',
|
|
15
17
|
display: 'flex',
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
justifyContent: 'flex-end',
|
|
21
|
-
// vertically align button and prevent emoji offset
|
|
22
|
-
paddingTop: '4px',
|
|
23
|
-
marginBottom: '-4px'
|
|
18
|
+
position: 'absolute',
|
|
19
|
+
top: '-8px',
|
|
20
|
+
right: '-8px',
|
|
21
|
+
zIndex: 1
|
|
24
22
|
});
|
|
25
23
|
export const emojiToneSelectorContainer = css({
|
|
26
24
|
flex: 1,
|
|
@@ -28,7 +26,7 @@ export const emojiToneSelectorContainer = css({
|
|
|
28
26
|
justifyContent: 'flex-end',
|
|
29
27
|
padding: '10px 10px 11px 0'
|
|
30
28
|
});
|
|
31
|
-
export const
|
|
29
|
+
export const emojiImageContainer = css({
|
|
32
30
|
borderRadius: "var(--ds-radius-100, 3px)",
|
|
33
31
|
backgroundColor: 'transparent',
|
|
34
32
|
display: 'inline-block',
|
|
@@ -37,6 +35,9 @@ export const emojiStyles = css({
|
|
|
37
35
|
// headings. Smaller headings get a slight increase in height, cannot add more negative margin
|
|
38
36
|
// as a "selected" emoji (e.g. in the editor) will not look good.
|
|
39
37
|
margin: '-1px 0',
|
|
38
|
+
img: {
|
|
39
|
+
display: 'block'
|
|
40
|
+
},
|
|
40
41
|
[`&.${commonSelectedStyles},&.${selectOnHoverStyles}:hover`]: {
|
|
41
42
|
backgroundColor: akEmojiSelectedBackgroundColor
|
|
42
43
|
},
|
|
@@ -44,8 +45,12 @@ export const emojiStyles = css({
|
|
|
44
45
|
// show delete button on hover
|
|
45
46
|
visibility: 'visible'
|
|
46
47
|
},
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
[`&.${deletableEmoji}`]: {
|
|
49
|
+
position: 'relative'
|
|
50
|
+
},
|
|
51
|
+
// show delete button on focus
|
|
52
|
+
[`&.${deletableEmoji}:focus-within .${emojiDeleteButton}`]: {
|
|
53
|
+
visibility: 'visible'
|
|
49
54
|
},
|
|
50
55
|
'&:focus': {
|
|
51
56
|
boxShadow: `0 0 0 2px ${`var(--ds-border-focused, ${B100})`}`,
|
|
@@ -53,7 +58,7 @@ export const emojiStyles = css({
|
|
|
53
58
|
outline: 'none'
|
|
54
59
|
}
|
|
55
60
|
});
|
|
56
|
-
export const
|
|
61
|
+
export const emojiSpriteContainer = css({
|
|
57
62
|
display: 'inline-block',
|
|
58
63
|
// Ensure along with vertical align middle, we don't increase the line height for h1..h6, and p
|
|
59
64
|
margin: '-1px 0',
|
|
@@ -206,13 +211,13 @@ export const previewImg = css({
|
|
|
206
211
|
display: 'inline-block',
|
|
207
212
|
flex: 'initial',
|
|
208
213
|
width: '32px',
|
|
209
|
-
[`& .${emojiSprite},
|
|
214
|
+
[`& .${emojiSprite}, span[role="img"]`]: {
|
|
210
215
|
width: '32px',
|
|
211
216
|
height: '32px',
|
|
212
217
|
padding: 0,
|
|
213
218
|
maxHeight: 'inherit'
|
|
214
219
|
},
|
|
215
|
-
[`&
|
|
220
|
+
[`& span[role="img"] > img`]: {
|
|
216
221
|
position: 'relative',
|
|
217
222
|
left: '50%',
|
|
218
223
|
top: '50%',
|
|
@@ -346,6 +351,17 @@ export const deleteText = css({
|
|
|
346
351
|
lineHeight: '16px'
|
|
347
352
|
}
|
|
348
353
|
});
|
|
354
|
+
export const headingH5 = css({
|
|
355
|
+
fontSize: "var(--ds-font-size-075, 12px)",
|
|
356
|
+
fontWeight: "var(--ds-font-weight-semibold, 600)",
|
|
357
|
+
letterSpacing: '-0.003em',
|
|
358
|
+
lineHeight: "var(--ds-font-lineHeight-100, 16px)"
|
|
359
|
+
});
|
|
360
|
+
export const requiredSymbol = css({
|
|
361
|
+
paddingLeft: `${getGridSize() / 4}px`,
|
|
362
|
+
color: `var(--ds-text-danger, ${R400})`,
|
|
363
|
+
fontFamily: getFontFamily()
|
|
364
|
+
});
|
|
349
365
|
export const previewButtonGroup = css({
|
|
350
366
|
display: 'flex'
|
|
351
367
|
});
|
|
@@ -416,4 +432,12 @@ export const emojiActionsWrapper = css({
|
|
|
416
432
|
display: 'flex',
|
|
417
433
|
justifyContent: 'flex-end',
|
|
418
434
|
alignItems: 'center'
|
|
435
|
+
});
|
|
436
|
+
export const tooltipShortcutStyle = css({
|
|
437
|
+
borderRadius: '3px',
|
|
438
|
+
backgroundColor: `var(--ds-background-inverse-subtle, ${N400})`,
|
|
439
|
+
padding: '0 2px',
|
|
440
|
+
/* TODO: fix in develop: https://atlassian.slack.com/archives/CFG3PSQ9E/p1647395052443259?thread_ts=1647394572.556029&cid=CFG3PSQ9E */
|
|
441
|
+
/* stylelint-disable-next-line */
|
|
442
|
+
label: 'tooltip-shortcut'
|
|
419
443
|
});
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { defineMessages } from 'react-intl-next';
|
|
2
2
|
export const messages = defineMessages({
|
|
3
|
+
deleteEmojiTooltip: {
|
|
4
|
+
id: 'fabric.emoji.delete.tooltip',
|
|
5
|
+
defaultMessage: 'Delete',
|
|
6
|
+
description: 'Tooltip content for delete emoji when focus on deletable emoji'
|
|
7
|
+
},
|
|
8
|
+
deleteEmojiTooltipForScreenreader: {
|
|
9
|
+
id: 'fabric.emoji.delete.screenreader.tooltip',
|
|
10
|
+
defaultMessage: 'To delete {shortName} emoji, press Backspace',
|
|
11
|
+
description: 'Tooltip content for delete emoji when focus on deletable emoji'
|
|
12
|
+
},
|
|
3
13
|
deleteEmojiTitle: {
|
|
4
14
|
id: 'fabric.emoji.delete.title',
|
|
5
15
|
defaultMessage: 'Remove emoji',
|
|
@@ -102,7 +112,7 @@ export const messages = defineMessages({
|
|
|
102
112
|
},
|
|
103
113
|
categoriesSelectorLabel: {
|
|
104
114
|
id: 'fabric.emoji.categories.label',
|
|
105
|
-
defaultMessage: 'Choose
|
|
115
|
+
defaultMessage: 'Choose an emoji category',
|
|
106
116
|
description: 'Aria label for Emoji categories selector at the top of emoji picker'
|
|
107
117
|
},
|
|
108
118
|
categoriesSearchResults: {
|
|
@@ -202,12 +212,17 @@ export const messages = defineMessages({
|
|
|
202
212
|
},
|
|
203
213
|
emojiPickerGrid: {
|
|
204
214
|
id: 'fabric.emoji.pickerlist.grid',
|
|
205
|
-
defaultMessage: '{showSearchResults, select, true{Search results} other{Emojis}}',
|
|
215
|
+
defaultMessage: '{showSearchResults, select, true{Search results} other{Emojis}}.',
|
|
206
216
|
description: `Aria label for emoji picker grid, showSearchResults is a boolean variable, message will be "Entering Emojis table", or "Leaving Emojis"`
|
|
207
217
|
},
|
|
208
218
|
emojiButtonRoleDescription: {
|
|
209
219
|
id: 'fabric.emoji.emojipicker.emoi.roledescription',
|
|
210
220
|
defaultMessage: 'emoji button',
|
|
211
221
|
description: `Aria roledescription for emoji button, used in emoji picker.`
|
|
222
|
+
},
|
|
223
|
+
error: {
|
|
224
|
+
id: 'fabric.emoji.emojipicker.error',
|
|
225
|
+
defaultMessage: 'Error!',
|
|
226
|
+
description: `Aria label for error icon, apperaed in emoji uploader screens and delete emoji screens of emoji picker`
|
|
212
227
|
}
|
|
213
228
|
});
|
|
@@ -52,6 +52,7 @@ const CategorySelector = props => {
|
|
|
52
52
|
return;
|
|
53
53
|
}
|
|
54
54
|
e.preventDefault();
|
|
55
|
+
e.stopPropagation();
|
|
55
56
|
const lastCategoryIndex = categories.length - 1;
|
|
56
57
|
switch (e.key) {
|
|
57
58
|
// navigate to the right category
|
|
@@ -111,7 +112,7 @@ const CategorySelector = props => {
|
|
|
111
112
|
"data-focus-index": index,
|
|
112
113
|
"aria-label": categoryName,
|
|
113
114
|
"aria-controls": currentFocus === index ? RENDER_EMOJI_PICKER_LIST_TESTID : undefined,
|
|
114
|
-
"aria-selected":
|
|
115
|
+
"aria-selected": categoryId === activeCategoryId,
|
|
115
116
|
css: categoryClasses,
|
|
116
117
|
disabled: disableCategories,
|
|
117
118
|
onClick: handleClick(categoryId, index),
|