diff --git a/apps/meteor/client/components/message/MessageHeader.tsx b/apps/meteor/client/components/message/MessageHeader.tsx index 8eb55a964f34a..ddd74f238b2aa 100644 --- a/apps/meteor/client/components/message/MessageHeader.tsx +++ b/apps/meteor/client/components/message/MessageHeader.tsx @@ -7,9 +7,10 @@ import { MessageStatusPrivateIndicator, MessageNameContainer, } from '@rocket.chat/fuselage'; +import { useButtonPattern } from '@rocket.chat/fuselage-hooks'; import { useUserDisplayName } from '@rocket.chat/ui-client'; import { useUserPresence } from '@rocket.chat/ui-contexts'; -import type { KeyboardEvent, ReactElement } from 'react'; +import type { ReactElement } from 'react'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -35,6 +36,7 @@ const MessageHeader = ({ message }: MessageHeaderProps): ReactElement => { const formatTime = useMessageListFormatTime(); const formatDateAndTime = useMessageListFormatDateAndTime(); const { triggerProps, openUserCard } = useUserCard(); + const buttonProps = useButtonPattern((e) => openUserCard(e, message.u.username)); const showRealName = useMessageListShowRealName(); const user = { ...message.u, roles: [], ...useUserPresence(message.u._id) }; @@ -49,15 +51,10 @@ const MessageHeader = ({ message }: MessageHeaderProps): ReactElement => { return ( openUserCard(e, message.u.username)} - onKeyDown={(e: KeyboardEvent) => { - (e.code === 'Enter' || e.code === 'Space') && openUserCard(e, message.u.username); - }} style={{ cursor: 'pointer' }} + {...buttonProps} {...triggerProps} > { const { username } = useContext(MessageListContext); const toggleReactionMutation = useToggleReactionMutation(); const { toolbarProps } = useToolbar(props, ref); + const buttonProps = useButtonPattern(openEmojiPicker); return ( @@ -33,17 +35,10 @@ const Reactions = ({ message, ...props }: ReactionsProps): ReactElement => { name={name} names={reactions.usernames.filter((user) => user !== username).map((username) => `@${username}`)} messageId={message._id} - onKeyDown={(e: KeyboardEvent) => - (e.code === 'Space' || e.code === 'Enter') && toggleReactionMutation.mutate({ mid: message._id, reaction: name }) - } onClick={() => toggleReactionMutation.mutate({ mid: message._id, reaction: name })} /> ))} - (e.code === 'Space' || e.code === 'Enter') && openEmojiPicker(e)} - onClick={openEmojiPicker} - /> + ); }; diff --git a/apps/meteor/client/components/message/content/reactions/Reaction.tsx b/apps/meteor/client/components/message/content/reactions/Reaction.tsx index a1694740a00db..e0f954e8f978c 100644 --- a/apps/meteor/client/components/message/content/reactions/Reaction.tsx +++ b/apps/meteor/client/components/message/content/reactions/Reaction.tsx @@ -1,4 +1,5 @@ import { MessageReaction as MessageReactionTemplate, MessageReactionEmoji, MessageReactionCounter } from '@rocket.chat/fuselage'; +import { useButtonPattern } from '@rocket.chat/fuselage-hooks'; import { useTooltipClose, useTooltipOpen } from '@rocket.chat/ui-contexts'; import type { ComponentProps, ReactElement } from 'react'; import { useRef, useContext } from 'react'; @@ -15,9 +16,10 @@ type ReactionProps = { name: string; names: string[]; messageId: string; + onClick: () => void; } & ComponentProps; -const Reaction = ({ hasReacted, counter, name, names, messageId, ...props }: ReactionProps): ReactElement => { +const Reaction = ({ hasReacted, counter, name, names, messageId, onClick, ...props }: ReactionProps): ReactElement => { const { t } = useTranslation(); const ref = useRef(null); const openTooltip = useTooltipOpen(); @@ -27,6 +29,7 @@ const Reaction = ({ hasReacted, counter, name, names, messageId, ...props }: Rea const mine = hasReacted(name); const emojiProps = getEmojiClassNameAndDataTitle(name); + const buttonProps = useButtonPattern(onClick); return ( { closeTooltip(); }} + {...buttonProps} {...props} > diff --git a/apps/meteor/client/components/message/variants/SystemMessage.tsx b/apps/meteor/client/components/message/variants/SystemMessage.tsx index 9f3b8d2aa1b83..32b01ed3a36a4 100644 --- a/apps/meteor/client/components/message/variants/SystemMessage.tsx +++ b/apps/meteor/client/components/message/variants/SystemMessage.tsx @@ -11,11 +11,12 @@ import { MessageUsername, MessageNameContainer, } from '@rocket.chat/fuselage'; +import { useButtonPattern } from '@rocket.chat/fuselage-hooks'; import { UserAvatar } from '@rocket.chat/ui-avatar'; import { useUserDisplayName } from '@rocket.chat/ui-client'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { useUserPresence } from '@rocket.chat/ui-contexts'; -import type { ComponentProps, ReactElement, KeyboardEvent } from 'react'; +import type { ComponentProps, ReactElement } from 'react'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -59,6 +60,7 @@ const SystemMessage = ({ message, showUserAvatar, ...props }: SystemMessageProps const toggleSelected = useToggleSelect(message._id); const isSelected = useIsSelectedMessage(message._id); useCountSelected(); + const buttonProps = useButtonPattern((e) => openUserCard(e, user.username)); return ( - user.username && openUserCard(e, user.username)} - onKeyDown={(e: KeyboardEvent) => { - (e.code === 'Enter' || e.code === 'Space') && openUserCard(e, message.u.username); - }} - style={{ cursor: 'pointer' }} - {...triggerProps} - > + {displayName} {showUsername && ( <> diff --git a/apps/meteor/client/views/room/Header/ParentRoom.tsx b/apps/meteor/client/views/room/Header/ParentRoom.tsx index eea7e45a839bf..f97462282d8ce 100644 --- a/apps/meteor/client/views/room/Header/ParentRoom.tsx +++ b/apps/meteor/client/views/room/Header/ParentRoom.tsx @@ -1,4 +1,5 @@ import type { IRoom } from '@rocket.chat/core-typings'; +import { useButtonPattern } from '@rocket.chat/fuselage-hooks'; import type { ReactElement } from 'react'; import { HeaderTag, HeaderTagIcon } from '../../../components/Header'; @@ -12,15 +13,11 @@ type ParentRoomProps = { const ParentRoom = ({ room }: ParentRoomProps): ReactElement => { const icon = useRoomIcon(room); - const handleRedirect = (): void => roomCoordinator.openRouteLink(room.t, { rid: room._id, ...room }); + const handleRedirect = () => roomCoordinator.openRouteLink(room.t, { rid: room._id, ...room }); + const buttonProps = useButtonPattern(handleRedirect); return ( - (e.code === 'Space' || e.code === 'Enter') && handleRedirect()} - onClick={handleRedirect} - > + {roomCoordinator.getRoomName(room.t, room)} diff --git a/apps/meteor/client/views/room/Header/ParentTeam.tsx b/apps/meteor/client/views/room/Header/ParentTeam.tsx index 4084cc12a1909..726657c23e9ad 100644 --- a/apps/meteor/client/views/room/Header/ParentTeam.tsx +++ b/apps/meteor/client/views/room/Header/ParentTeam.tsx @@ -1,5 +1,6 @@ import type { IRoom } from '@rocket.chat/core-typings'; import { TEAM_TYPE } from '@rocket.chat/core-typings'; +import { useButtonPattern } from '@rocket.chat/fuselage-hooks'; import { useUserId, useEndpoint } from '@rocket.chat/ui-contexts'; import { keepPreviousData, useQuery } from '@tanstack/react-query'; import type { ReactElement } from 'react'; @@ -56,6 +57,8 @@ const ParentTeam = ({ room }: { room: IRoom }): ReactElement | null => { goToRoomById(rid); }; + const buttonProps = useButtonPattern(redirectToMainRoom); + if (teamInfoLoading || userTeamsLoading) { return ; } @@ -65,12 +68,7 @@ const ParentTeam = ({ room }: { room: IRoom }): ReactElement | null => { } return ( - (e.code === 'Space' || e.code === 'Enter') && redirectToMainRoom()} - onClick={redirectToMainRoom} - > + {teamInfoData?.teamInfo.name} diff --git a/apps/meteor/client/views/room/Header/RoomTitle.tsx b/apps/meteor/client/views/room/Header/RoomTitle.tsx index 2ff1a21650156..7251ab55fed22 100644 --- a/apps/meteor/client/views/room/Header/RoomTitle.tsx +++ b/apps/meteor/client/views/room/Header/RoomTitle.tsx @@ -1,7 +1,7 @@ import { isTeamRoom, type IRoom } from '@rocket.chat/core-typings'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useButtonPattern, useEffectEvent } from '@rocket.chat/fuselage-hooks'; import { useDocumentTitle } from '@rocket.chat/ui-client'; -import type { KeyboardEvent, ReactElement } from 'react'; +import type { ReactElement } from 'react'; import HeaderIconWithRoom from './HeaderIconWithRoom'; import { HeaderTitle, HeaderTitleButton } from '../../../components/Header'; @@ -35,14 +35,10 @@ const RoomTitle = ({ room }: { room: IRoom }): ReactElement => { } }); + const buttonProps = useButtonPattern(handleOpenRoomInfo); + return ( - (e.code === 'Enter' || e.code === 'Space') && handleOpenRoomInfo()} - onClick={() => handleOpenRoomInfo()} - tabIndex={0} - role='button' - mie={4} - > + {room.name} diff --git a/apps/meteor/client/views/room/HeaderV2/RoomTitle.tsx b/apps/meteor/client/views/room/HeaderV2/RoomTitle.tsx index 77b9ecafbd3e8..86f5c4fe52327 100644 --- a/apps/meteor/client/views/room/HeaderV2/RoomTitle.tsx +++ b/apps/meteor/client/views/room/HeaderV2/RoomTitle.tsx @@ -1,7 +1,6 @@ import { isTeamRoom, type IRoom } from '@rocket.chat/core-typings'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useButtonPattern, useEffectEvent } from '@rocket.chat/fuselage-hooks'; import { useDocumentTitle } from '@rocket.chat/ui-client'; -import type { KeyboardEvent } from 'react'; import HeaderIconWithRoom from './HeaderIconWithRoom'; import { HeaderTitle, HeaderTitleButton } from '../../../components/Header'; @@ -37,14 +36,10 @@ const RoomTitle = ({ room }: RoomTitleProps) => { } }); + const buttonProps = useButtonPattern(handleOpenRoomInfo); + return ( - (e.code === 'Enter' || e.code === 'Space') && handleOpenRoomInfo()} - onClick={() => handleOpenRoomInfo()} - tabIndex={0} - role='button' - mie={4} - > + {room.name} diff --git a/packages/gazzodown/package.json b/packages/gazzodown/package.json index b6b8d2c832d89..e16c7ad76aa4e 100644 --- a/packages/gazzodown/package.json +++ b/packages/gazzodown/package.json @@ -31,6 +31,7 @@ "@rocket.chat/core-typings": "workspace:^", "@rocket.chat/css-in-js": "~0.31.25", "@rocket.chat/fuselage": "^0.64.0", + "@rocket.chat/fuselage-hooks": "^0.36.0", "@rocket.chat/fuselage-tokens": "~0.33.2", "@rocket.chat/jest-presets": "workspace:~", "@rocket.chat/message-parser": "workspace:^", diff --git a/packages/gazzodown/src/mentions/ChannelMentionElement.tsx b/packages/gazzodown/src/mentions/ChannelMentionElement.tsx index 54f22ccca7d87..147acc478b2aa 100644 --- a/packages/gazzodown/src/mentions/ChannelMentionElement.tsx +++ b/packages/gazzodown/src/mentions/ChannelMentionElement.tsx @@ -1,5 +1,6 @@ import { Message } from '@rocket.chat/fuselage'; -import { memo, ReactElement, useContext, useMemo, KeyboardEvent } from 'react'; +import { useButtonPattern } from '@rocket.chat/fuselage-hooks'; +import { memo, ReactElement, useContext, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { MarkupInteractionContext } from '../MarkupInteractionContext'; @@ -16,23 +17,14 @@ const ChannelMentionElement = ({ mention }: ChannelMentionElementProps): ReactEl const resolved = useMemo(() => resolveChannelMention?.(mention), [mention, resolveChannelMention]); const handleClick = useMemo(() => (resolved ? onChannelMentionClick?.(resolved) : undefined), [resolved, onChannelMentionClick]); + const buttonProps = useButtonPattern((e) => handleClick?.(e)); if (!resolved) { return <>#{mention}; } return ( - ): void => { - (e.code === 'Enter' || e.code === 'Space') && handleClick?.(e); - }} - > + {handleChannelMention(resolved.fname ?? mention, showMentionSymbol)} ); diff --git a/packages/gazzodown/src/mentions/UserMentionElement.tsx b/packages/gazzodown/src/mentions/UserMentionElement.tsx index 6f52a16a5482a..49fc8b5866d6d 100644 --- a/packages/gazzodown/src/mentions/UserMentionElement.tsx +++ b/packages/gazzodown/src/mentions/UserMentionElement.tsx @@ -1,5 +1,6 @@ import { Message } from '@rocket.chat/fuselage'; -import { memo, ReactElement, useContext, useMemo, KeyboardEvent } from 'react'; +import { useButtonPattern } from '@rocket.chat/fuselage-hooks'; +import { memo, ReactElement, useContext, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { MarkupInteractionContext } from '../MarkupInteractionContext'; @@ -18,6 +19,7 @@ const UserMentionElement = ({ mention }: UserMentionElementProps): ReactElement const resolved = useMemo(() => resolveUserMention?.(mention), [mention, resolveUserMention]); const handleClick = useMemo(() => (resolved ? onUserMentionClick?.(resolved) : undefined), [resolved, onUserMentionClick]); + const buttonProps = useButtonPattern((e) => handleClick?.(e)); if (mention === 'all') { return ( @@ -44,12 +46,7 @@ const UserMentionElement = ({ mention }: UserMentionElementProps): ReactElement variant={resolved._id === ownUserId ? 'critical' : 'other'} title={resolved._id === ownUserId ? t('Mentions_you') : t('Mentions_user')} clickable - tabIndex={0} - role='button' - onClick={handleClick} - onKeyDown={(e: KeyboardEvent): void => { - (e.code === 'Enter' || e.code === 'Space') && handleClick?.(e); - }} + {...buttonProps} {...triggerProps} data-uid={resolved._id} > diff --git a/packages/ui-client/src/components/MultiSelectCustom/MultiSelectCustom.tsx b/packages/ui-client/src/components/MultiSelectCustom/MultiSelectCustom.tsx index d758de46d21f7..a6e348ef82e27 100644 --- a/packages/ui-client/src/components/MultiSelectCustom/MultiSelectCustom.tsx +++ b/packages/ui-client/src/components/MultiSelectCustom/MultiSelectCustom.tsx @@ -1,5 +1,5 @@ import { Box, Button } from '@rocket.chat/fuselage'; -import { useOutsideClick, useToggle } from '@rocket.chat/fuselage-hooks'; +import { useButtonPattern, useOutsideClick, useToggle } from '@rocket.chat/fuselage-hooks'; import type { ComponentProps, FormEvent, ReactElement, RefObject } from 'react'; import { useCallback, useRef } from 'react'; @@ -96,18 +96,18 @@ export const MultiSelectCustom = ({ ); const selectedOptionsCount = dropdownOptions.filter((option) => option.hasOwnProperty('checked') && option.checked).length; + const buttonProps = useButtonPattern(() => toggleCollapsed(!collapsed)); return ( toggleCollapsed(!collapsed)} - onKeyDown={(e) => (e.code === 'Enter' || e.code === 'Space') && toggleCollapsed(!collapsed)} defaultTitle={defaultTitle} selectedOptionsTitle={selectedOptionsTitle} selectedOptionsCount={selectedOptionsCount} maxCount={dropdownOptions.length} + {...buttonProps} {...props} /> {collapsed && ( diff --git a/yarn.lock b/yarn.lock index 5acf3415ce2bd..c980740f35a9d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8970,6 +8970,7 @@ __metadata: "@rocket.chat/core-typings": "workspace:^" "@rocket.chat/css-in-js": "npm:~0.31.25" "@rocket.chat/fuselage": "npm:^0.64.0" + "@rocket.chat/fuselage-hooks": "npm:^0.36.0" "@rocket.chat/fuselage-tokens": "npm:~0.33.2" "@rocket.chat/jest-presets": "workspace:~" "@rocket.chat/message-parser": "workspace:^"