[MM-61577]: Ensure all interactive functionality is operable with the keyboard in emoji picker (#29629)
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
316cde2569
Коммит
e8e40c79f6
@@ -134,7 +134,9 @@ exports[`components/emoji_picker/EmojiPicker should match snapshot 1`] = `
|
||||
style="height: 290px;"
|
||||
>
|
||||
<div
|
||||
aria-labelledby="emojiPickerSearch"
|
||||
class="emoji-picker__container"
|
||||
role="grid"
|
||||
>
|
||||
<div
|
||||
style="overflow: visible; height: 0px; width: 0px;"
|
||||
|
||||
@@ -35,6 +35,7 @@ function EmojiPickerCategoryOrEmojiRow({index, style, data, cursorRowIndex, curs
|
||||
<div
|
||||
style={style}
|
||||
className='emoji-picker__row'
|
||||
role='row'
|
||||
>
|
||||
{row.items.map((emojiColumn) => {
|
||||
const emoji = emojiColumn.item;
|
||||
|
||||
@@ -17,6 +17,7 @@ function EmojiPickerCategoryRow({categoryName, style}: Props) {
|
||||
<div
|
||||
className='emoji-picker-items__container'
|
||||
style={style}
|
||||
role='row'
|
||||
>
|
||||
<div
|
||||
className='emoji-picker__category-header'
|
||||
|
||||
@@ -91,7 +91,11 @@ const EmojiPickerCurrentResults = forwardRef<InfiniteLoader, Props>(({categoryOr
|
||||
className='emoji-picker__items'
|
||||
style={{height: EMOJI_CONTAINER_HEIGHT}}
|
||||
>
|
||||
<div className='emoji-picker__container'>
|
||||
<div
|
||||
className='emoji-picker__container'
|
||||
role='grid'
|
||||
aria-labelledby='emojiPickerSearch'
|
||||
>
|
||||
<AutoSizer>
|
||||
{({height, width}) => (
|
||||
<InfiniteLoader
|
||||
|
||||
@@ -55,32 +55,21 @@ function EmojiPickerItem({emoji, rowIndex, isSelected, onClick, onMouseOver}: Pr
|
||||
let content;
|
||||
|
||||
if (isSystemEmoji(emoji)) {
|
||||
const emojiName = emoji.short_name ? emoji.short_name : emoji.name;
|
||||
const emojiUnified = emoji.unified ? emoji.unified.toLowerCase() : emoji.name.toLowerCase();
|
||||
|
||||
content = (
|
||||
<img
|
||||
alt={'emoji image'}
|
||||
alt={`${emoji.name.toLocaleLowerCase()} emoji`}
|
||||
data-testid={emoji.short_names}
|
||||
src={imgTrans}
|
||||
className={`emojisprite emoji-category-${emoji.category} emoji-${emojiUnified}`}
|
||||
id={`emoji-${emojiUnified}`}
|
||||
aria-label={formatMessage(
|
||||
{
|
||||
id: 'emoji_picker_item.emoji_aria_label',
|
||||
defaultMessage: '{emojiName} emoji',
|
||||
},
|
||||
{
|
||||
emojiName: (emojiName).replace(/_/g, ' '),
|
||||
},
|
||||
)}
|
||||
role='button'
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
content = (
|
||||
<img
|
||||
alt={'custom emoji image'}
|
||||
alt={'custom emoji'}
|
||||
data-testid={emoji.name}
|
||||
src={getEmojiImageUrl(emoji)}
|
||||
className={'emoji-category--custom'}
|
||||
@@ -89,15 +78,26 @@ function EmojiPickerItem({emoji, rowIndex, isSelected, onClick, onMouseOver}: Pr
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
<button
|
||||
className={itemClassName}
|
||||
onClick={handleClick}
|
||||
onMouseOver={throttledMouseOver}
|
||||
data-testid='emojiItem'
|
||||
tabIndex={-1}
|
||||
type='button'
|
||||
id={emoji.name.toLocaleLowerCase().replaceAll(' ', '_')}
|
||||
aria-label={formatMessage(
|
||||
{
|
||||
id: 'emoji_picker_item.emoji_aria_label',
|
||||
defaultMessage: '{emojiName} emoji',
|
||||
},
|
||||
{
|
||||
emojiName: (isSystemEmoji(emoji) ? emoji.short_name : emoji.name).replace(/_/g, ' '),
|
||||
},
|
||||
)}
|
||||
>
|
||||
<div data-testid='emojiItem'>
|
||||
{content}
|
||||
</div>
|
||||
</div>
|
||||
{content}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ import {useIntl} from 'react-intl';
|
||||
import {EMOJI_PER_ROW} from 'components/emoji_picker/constants';
|
||||
import {NavigationDirection} from 'components/emoji_picker/types';
|
||||
|
||||
import Constants from 'utils/constants';
|
||||
|
||||
interface Props {
|
||||
value: string;
|
||||
cursorCategoryIndex: number;
|
||||
@@ -24,6 +26,8 @@ interface Props {
|
||||
resetCursorPosition: () => void;
|
||||
}
|
||||
|
||||
const KeyCodes = Constants.KeyCodes;
|
||||
|
||||
const EmojiPickerSearch = forwardRef<HTMLInputElement, Props>(({value, cursorCategoryIndex, cursorEmojiIndex, onChange, resetCursorPosition, onKeyDown, focus, onEnter}: Props, ref) => {
|
||||
const {formatMessage} = useIntl();
|
||||
|
||||
@@ -39,7 +43,7 @@ const EmojiPickerSearch = forwardRef<HTMLInputElement, Props>(({value, cursorCat
|
||||
|
||||
const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
|
||||
switch (event.key) {
|
||||
case 'ArrowRight':
|
||||
case KeyCodes.RIGHT[0]:
|
||||
// If the cursor is at the end of the textbox and an emoji is currently selected, move it to the next emoji
|
||||
if ((event.currentTarget?.selectionStart ?? 0) + 1 > value.length || (cursorCategoryIndex !== -1 || cursorEmojiIndex !== -1)) {
|
||||
event.stopPropagation();
|
||||
@@ -48,7 +52,7 @@ const EmojiPickerSearch = forwardRef<HTMLInputElement, Props>(({value, cursorCat
|
||||
onKeyDown(NavigationDirection.NextEmoji);
|
||||
}
|
||||
break;
|
||||
case 'ArrowLeft':
|
||||
case KeyCodes.LEFT[0]:
|
||||
if (cursorCategoryIndex > 0 || cursorEmojiIndex > 0) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
@@ -65,7 +69,7 @@ const EmojiPickerSearch = forwardRef<HTMLInputElement, Props>(({value, cursorCat
|
||||
focus();
|
||||
}
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
case KeyCodes.UP[0]:
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
@@ -88,7 +92,7 @@ const EmojiPickerSearch = forwardRef<HTMLInputElement, Props>(({value, cursorCat
|
||||
onKeyDown(NavigationDirection.PreviousEmojiRow);
|
||||
}
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
case KeyCodes.DOWN[0]:
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
@@ -104,7 +108,14 @@ const EmojiPickerSearch = forwardRef<HTMLInputElement, Props>(({value, cursorCat
|
||||
onKeyDown(NavigationDirection.NextEmojiRow);
|
||||
}
|
||||
break;
|
||||
case 'Enter': {
|
||||
case KeyCodes.SPACE[0]: {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
onEnter();
|
||||
break;
|
||||
}
|
||||
case KeyCodes.ENTER[0]: {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
|
||||
@@ -340,6 +340,7 @@ const EmojiPicker = ({
|
||||
return;
|
||||
}
|
||||
|
||||
searchInputRef.current?.setAttribute('aria-activedescendant', newCursorEmoji.name.toLocaleLowerCase().replaceAll(' ', '_'));
|
||||
setCursor({
|
||||
rowIndex: newCursor.rowIndex,
|
||||
emojiId: newCursor.emojiId,
|
||||
|
||||
@@ -29,6 +29,7 @@ const PostEmoji = ({children, name, imageUrl}: Props) => {
|
||||
className='emoticon'
|
||||
data-testid={`postEmoji.${emojiText}`}
|
||||
style={{backgroundImage: backgroundImageUrl}}
|
||||
aria-label={emojiText}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
|
||||
@@ -538,7 +538,9 @@ $emoji-footer-height: $emoji-footer-border-width + $emoji-half-height + $emoji-
|
||||
height: 36px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
background-color: transparent;
|
||||
cursor: pointer;
|
||||
vertical-align: middle;
|
||||
|
||||
|
||||
Ссылка в новой задаче
Block a user