@atlaskit/emoji 67.2.1 → 67.3.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.
- 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/RetryableButton.js +9 -3
- package/dist/cjs/components/common/ToolTipContentWithKeymap.js +25 -0
- package/dist/cjs/components/common/styles.js +50 -20
- 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/RetryableButton.js +9 -3
- package/dist/es2019/components/common/ToolTipContentWithKeymap.js +15 -0
- package/dist/es2019/components/common/styles.js +44 -16
- 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/RetryableButton.js +9 -3
- package/dist/esm/components/common/ToolTipContentWithKeymap.js +14 -0
- package/dist/esm/components/common/styles.js +43 -17
- 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,
|
|
@@ -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%',
|
|
@@ -253,7 +258,9 @@ export const emojiUploadTop = css({
|
|
|
253
258
|
fontSize: '12px'
|
|
254
259
|
});
|
|
255
260
|
export const uploadChooseFileMessage = css({
|
|
256
|
-
|
|
261
|
+
'&&': {
|
|
262
|
+
color: `var(--ds-text-subtle, ${N300})`
|
|
263
|
+
}
|
|
257
264
|
});
|
|
258
265
|
export const closeEmojiUploadButton = css({
|
|
259
266
|
display: 'flex'
|
|
@@ -346,6 +353,19 @@ export const deleteText = css({
|
|
|
346
353
|
lineHeight: '16px'
|
|
347
354
|
}
|
|
348
355
|
});
|
|
356
|
+
export const headingH5 = css({
|
|
357
|
+
'&&': {
|
|
358
|
+
fontSize: "var(--ds-font-size-075, 12px)",
|
|
359
|
+
fontWeight: "var(--ds-font-weight-semibold, 600)",
|
|
360
|
+
letterSpacing: '-0.003em',
|
|
361
|
+
lineHeight: "var(--ds-font-lineHeight-100, 16px)"
|
|
362
|
+
}
|
|
363
|
+
});
|
|
364
|
+
export const requiredSymbol = css({
|
|
365
|
+
paddingLeft: `${getGridSize() / 4}px`,
|
|
366
|
+
color: `var(--ds-text-danger, ${R400})`,
|
|
367
|
+
fontFamily: getFontFamily()
|
|
368
|
+
});
|
|
349
369
|
export const previewButtonGroup = css({
|
|
350
370
|
display: 'flex'
|
|
351
371
|
});
|
|
@@ -416,4 +436,12 @@ export const emojiActionsWrapper = css({
|
|
|
416
436
|
display: 'flex',
|
|
417
437
|
justifyContent: 'flex-end',
|
|
418
438
|
alignItems: 'center'
|
|
439
|
+
});
|
|
440
|
+
export const tooltipShortcutStyle = css({
|
|
441
|
+
borderRadius: '3px',
|
|
442
|
+
backgroundColor: `var(--ds-background-inverse-subtle, ${N400})`,
|
|
443
|
+
padding: '0 2px',
|
|
444
|
+
/* TODO: fix in develop: https://atlassian.slack.com/archives/CFG3PSQ9E/p1647395052443259?thread_ts=1647394572.556029&cid=CFG3PSQ9E */
|
|
445
|
+
/* stylelint-disable-next-line */
|
|
446
|
+
label: 'tooltip-shortcut'
|
|
419
447
|
});
|
|
@@ -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',
|
|
@@ -37,7 +47,7 @@ export const messages = defineMessages({
|
|
|
37
47
|
},
|
|
38
48
|
emojiChooseFileScreenReaderDescription: {
|
|
39
49
|
id: 'fabric.emoji.choose.file.screenReaderDescription',
|
|
40
|
-
defaultMessage: 'Choose a file for the emoji. JPG, PNG or GIF. Max size 1 MB
|
|
50
|
+
defaultMessage: 'Choose a file for the emoji. JPG, PNG or GIF. Max size 1 MB',
|
|
41
51
|
description: 'Message indicating the purpose of choosing the file and requirements for the file'
|
|
42
52
|
},
|
|
43
53
|
emojiSelectSkinToneButtonAriaLabelText: {
|
|
@@ -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: {
|
|
@@ -209,5 +219,10 @@ export const messages = defineMessages({
|
|
|
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),
|
|
@@ -111,8 +111,6 @@ const EmojiPickerComponent = ({
|
|
|
111
111
|
if (emojiToRender && !containsEmojiId(emojiToRender, selectedEmoji)) {
|
|
112
112
|
batchedUpdates(() => {
|
|
113
113
|
setSelectedEmoji(undefined);
|
|
114
|
-
// Only enable categories for full emoji list (non-search)
|
|
115
|
-
setActiveCategory(null);
|
|
116
114
|
});
|
|
117
115
|
}
|
|
118
116
|
batchedUpdates(() => {
|
|
@@ -276,12 +274,12 @@ const EmojiPickerComponent = ({
|
|
|
276
274
|
});
|
|
277
275
|
fireAnalytics(uploadBeginButton());
|
|
278
276
|
}, [emojiProvider, fireAnalytics]);
|
|
279
|
-
const scrollToUploadedEmoji = useCallback(
|
|
277
|
+
const scrollToUploadedEmoji = useCallback(emojiDescription => {
|
|
280
278
|
if (emojiPickerList.current) {
|
|
281
279
|
// Wait a tick to ensure repaint and updated height for picker list
|
|
282
280
|
window.setTimeout(() => {
|
|
283
281
|
var _emojiPickerList$curr2;
|
|
284
|
-
(_emojiPickerList$curr2 = emojiPickerList.current) === null || _emojiPickerList$curr2 === void 0 ? void 0 : _emojiPickerList$curr2.scrollToRecentlyUploaded();
|
|
282
|
+
(_emojiPickerList$curr2 = emojiPickerList.current) === null || _emojiPickerList$curr2 === void 0 ? void 0 : _emojiPickerList$curr2.scrollToRecentlyUploaded(emojiDescription);
|
|
285
283
|
}, 0);
|
|
286
284
|
}
|
|
287
285
|
}, [emojiPickerList]);
|
|
@@ -298,7 +296,7 @@ const EmojiPickerComponent = ({
|
|
|
298
296
|
setSelectedEmoji(emojiDescription);
|
|
299
297
|
setUploading(false);
|
|
300
298
|
});
|
|
301
|
-
scrollToUploadedEmoji();
|
|
299
|
+
scrollToUploadedEmoji(emojiDescription);
|
|
302
300
|
};
|
|
303
301
|
uploadEmoji(upload, emojiProvider, errorSetter, onSuccess, fireAnalytics, retry);
|
|
304
302
|
}, [emojiProvider, fireAnalytics, scrollToUploadedEmoji]);
|
|
@@ -348,6 +346,11 @@ const EmojiPickerComponent = ({
|
|
|
348
346
|
fireAnalytics(openedPickerEvent());
|
|
349
347
|
isMounting.current = false;
|
|
350
348
|
}
|
|
349
|
+
|
|
350
|
+
// stop all key propagation to other event listeners
|
|
351
|
+
const suppressKeyPress = e => {
|
|
352
|
+
e.stopPropagation();
|
|
353
|
+
};
|
|
351
354
|
useEffect(() => {
|
|
352
355
|
// componentDidMount logic
|
|
353
356
|
if (!isMounted) {
|
|
@@ -402,7 +405,10 @@ const EmojiPickerComponent = ({
|
|
|
402
405
|
"data-emoji-picker-container": true,
|
|
403
406
|
role: "dialog",
|
|
404
407
|
"aria-label": formatMessage(messages.emojiPickerTitle),
|
|
405
|
-
"aria-modal": true
|
|
408
|
+
"aria-modal": true,
|
|
409
|
+
onKeyPress: suppressKeyPress,
|
|
410
|
+
onKeyUp: suppressKeyPress,
|
|
411
|
+
onKeyDown: suppressKeyPress
|
|
406
412
|
}, jsx(CategorySelector, {
|
|
407
413
|
activeCategoryId: activeCategory,
|
|
408
414
|
dynamicCategories: dynamicCategories,
|