From 99e28c40f4ab76c2491c1711dec761d9e862bb25 Mon Sep 17 00:00:00 2001 From: yash-rajpal Date: Fri, 16 May 2025 20:40:46 +0530 Subject: [PATCH 01/12] wip: files contextual bar accessibility --- .../components/ImageGallery/ImageGallery.tsx | 2 +- .../contextualBar/RoomFiles/RoomFiles.tsx | 8 +++- .../RoomFiles/components/FileItem.tsx | 39 ++++++++++++++-- .../RoomFiles/components/FileItemMenu.tsx | 46 +++++++++---------- .../RoomFiles/components/ImageItem.tsx | 10 ++-- 5 files changed, 72 insertions(+), 33 deletions(-) diff --git a/apps/meteor/client/components/ImageGallery/ImageGallery.tsx b/apps/meteor/client/components/ImageGallery/ImageGallery.tsx index 9884c463e8986..f871a60a3af2d 100644 --- a/apps/meteor/client/components/ImageGallery/ImageGallery.tsx +++ b/apps/meteor/client/components/ImageGallery/ImageGallery.tsx @@ -122,7 +122,7 @@ export const ImageGallery = ({ images, onClose, loadMore }: { images: IUpload[]; return createPortal( <> - +
diff --git a/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx b/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx index fb72544006545..6e0860496d8d5 100644 --- a/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx +++ b/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx @@ -2,7 +2,7 @@ import type { IUpload, IUploadWithUser } from '@rocket.chat/core-typings'; import type { SelectOption } from '@rocket.chat/fuselage'; import { Box, Icon, TextInput, Select, Throbber, ContextualbarSection } from '@rocket.chat/fuselage'; import type { ChangeEvent } from 'react'; -import { useMemo } from 'react'; +import { forwardRef, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { Virtuoso } from 'react-virtuoso'; @@ -95,6 +95,12 @@ const RoomFiles = ({ overscan={50} data={filesItems} itemContent={(_, data) => } + components={{ + // eslint-disable-next-line react/no-multi-comp + List: forwardRef(function List(props, ref) { + return ; + }), + }} /> diff --git a/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItem.tsx b/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItem.tsx index 1f5109a9c10ee..f1595b60ecbad 100644 --- a/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItem.tsx +++ b/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItem.tsx @@ -1,6 +1,7 @@ import type { IUpload, IUploadWithUser } from '@rocket.chat/core-typings'; import { css } from '@rocket.chat/css-in-js'; import { Box, Palette } from '@rocket.chat/fuselage'; +import { useRef } from 'react'; import FileItemIcon from './FileItemIcon'; import FileItemMenu from './FileItemMenu'; @@ -8,11 +9,18 @@ import ImageItem from './ImageItem'; import { useDownloadFromServiceWorker } from '../../../../../hooks/useDownloadFromServiceWorker'; import { useFormatDateAndTime } from '../../../../../hooks/useFormatDateAndTime'; -const hoverClass = css` +const customClass = css` &:hover { cursor: pointer; background: ${Palette.surface['surface-hover']}; } + + &:focus.focus-visible { + outline: 0; + margin: 2px; + box-shadow: 0 0 0 2px ${Palette.stroke['stroke-extra-light-highlight']}; + border-color: ${Palette.stroke['stroke-highlight']}; + } `; type FileItemProps = { @@ -25,11 +33,34 @@ const FileItem = ({ fileData, onClickDelete }: FileItemProps) => { const { _id, path, name, uploadedAt, type, typeGroup, user } = fileData; const encryptedAnchorProps = useDownloadFromServiceWorker(path || '', name); + const ref = useRef(null); + + const handleKeyDown = (event: React.KeyboardEvent) => { + if (!ref.current) { + return; + } + + if (event.code === 'Enter' || event.code === 'Space') { + event.preventDefault(); + console.log('click'); + ref.current.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window })); + } + }; return ( - + {typeGroup === 'image' ? ( - + ) : ( { flexShrink={1} href={path} textDecorationLine='none' + ref={ref} + tabIndex={-1} {...(path?.includes('/file-decrypt/') ? encryptedAnchorProps : {})} > diff --git a/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItemMenu.tsx b/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItemMenu.tsx index 73690be488b03..0de6cd1867d25 100644 --- a/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItemMenu.tsx +++ b/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItemMenu.tsx @@ -1,6 +1,8 @@ import type { IUpload } from '@rocket.chat/core-typings'; import { Emitter } from '@rocket.chat/emitter'; -import { Box, Menu, Icon } from '@rocket.chat/fuselage'; +import { Box } from '@rocket.chat/fuselage'; +import type { GenericMenuItemProps } from '@rocket.chat/ui-client'; +import { GenericMenu } from '@rocket.chat/ui-client'; import { useTranslation, useUserId } from '@rocket.chat/ui-contexts'; import { memo, useEffect, useId } from 'react'; @@ -45,15 +47,12 @@ const FileItemMenu = ({ fileData, onClickDelete }: FileItemMenuProps) => { [fileData, t, uid], ); - const menuOptions = { - downLoad: { - label: ( - - - {t('Download')} - - ), - action: () => { + const menuOptions = [ + { + id: 'download', + content: t('Download'), + icon: 'download', + onClick: () => { if (fileData.path?.includes('/file-decrypt/')) { if (!controller) { return; @@ -76,21 +75,20 @@ const FileItemMenu = ({ fileData, onClickDelete }: FileItemMenuProps) => { }, disabled: !canDownloadFile, }, - ...(isDeletionAllowed && - onClickDelete && { - delete: { - label: ( - - - {t('Delete')} - - ), - action: () => onClickDelete(fileData._id), - }, - }), - }; + ...(isDeletionAllowed && onClickDelete + ? [ + { + id: 'delete', + content: {t('Delete')}, + onClick: () => onClickDelete(fileData._id), + icon: 'trash', + iconColor: 'status-font-on-danger', + }, + ] + : []), + ] as GenericMenuItemProps[]; - return ; + return ; }; export default memo(FileItemMenu); diff --git a/apps/meteor/client/views/room/contextualBar/RoomFiles/components/ImageItem.tsx b/apps/meteor/client/views/room/contextualBar/RoomFiles/components/ImageItem.tsx index d9ae0d30cfd67..b8f518b923e2f 100644 --- a/apps/meteor/client/views/room/contextualBar/RoomFiles/components/ImageItem.tsx +++ b/apps/meteor/client/views/room/contextualBar/RoomFiles/components/ImageItem.tsx @@ -1,4 +1,6 @@ import { Avatar, Box } from '@rocket.chat/fuselage'; +import type { ComponentProps } from 'react'; +import { forwardRef } from 'react'; type ImageItemProps = { id: string; @@ -6,11 +8,11 @@ type ImageItemProps = { name: string | undefined; timestamp: string; username?: string; -}; +} & ComponentProps; -const ImageItem = ({ id, url, name, timestamp, username }: ImageItemProps) => { +const ImageItem = forwardRef(function ImageItem({ id, url, name, timestamp, username }: ImageItemProps, ref) { return ( - + {url && ( @@ -33,6 +35,6 @@ const ImageItem = ({ id, url, name, timestamp, username }: ImageItemProps) => { ); -}; +}); export default ImageItem; From 9fc641b9773c8b1b3d5f335dd3bf3a54a32169d3 Mon Sep 17 00:00:00 2001 From: yash-rajpal Date: Mon, 19 May 2025 22:21:49 +0530 Subject: [PATCH 02/12] add keyboard navigation --- .../VirtualizedScrollbars.tsx | 6 ++- .../contextualBar/RoomFiles/RoomFiles.tsx | 6 ++- .../hooks/useRoomFilesListNavigation.ts | 42 +++++++++++++++++++ 3 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useRoomFilesListNavigation.ts diff --git a/apps/meteor/client/components/CustomScrollbars/VirtualizedScrollbars.tsx b/apps/meteor/client/components/CustomScrollbars/VirtualizedScrollbars.tsx index 26298ed208571..82c117f25fca3 100644 --- a/apps/meteor/client/components/CustomScrollbars/VirtualizedScrollbars.tsx +++ b/apps/meteor/client/components/CustomScrollbars/VirtualizedScrollbars.tsx @@ -1,3 +1,4 @@ +import { useMergedRefs } from '@rocket.chat/fuselage-hooks'; import { useOverlayScrollbars } from 'overlayscrollbars-react'; import type { HTMLAttributes, ReactElement } from 'react'; import { useEffect, useState, useRef, cloneElement, forwardRef, memo } from 'react'; @@ -13,13 +14,14 @@ const VirtualizedScrollbars = forwardRef(null); const [scroller, setScroller] = useState(null); const scrollbarsOptions = getScrollbarsOptions(overflowX); const [initialize, osInstance] = useOverlayScrollbars({ options: scrollbarsOptions, defer: true, }); + const mergedRef = useMergedRefs(rootRef, ref); useEffect(() => { const { current: root } = rootRef; @@ -36,7 +38,7 @@ const VirtualizedScrollbars = forwardRef osInstance()?.destroy(); }, [initialize, osInstance, ref, scroller]); - return {cloneElement(props.children, { scrollerRef: setScroller })}; + return {cloneElement(props.children, { scrollerRef: setScroller })}; }); export default memo(VirtualizedScrollbars); diff --git a/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx b/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx index 6e0860496d8d5..caab4e721ae1a 100644 --- a/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx +++ b/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx @@ -7,6 +7,7 @@ import { useTranslation } from 'react-i18next'; import { Virtuoso } from 'react-virtuoso'; import FileItem from './components/FileItem'; +import { useRoomFilesNavigation } from './hooks/useRoomFilesListNavigation'; import { ContextualbarHeader, ContextualbarIcon, @@ -56,6 +57,8 @@ const RoomFiles = ({ [t], ); + const roomFilesRef = useRoomFilesNavigation(); + return ( <> @@ -84,7 +87,7 @@ const RoomFiles = ({ {!loading && filesItems.length === 0 && } {!loading && filesItems.length > 0 && ( - + ; }), }} + tabIndex={-1} /> diff --git a/apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useRoomFilesListNavigation.ts b/apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useRoomFilesListNavigation.ts new file mode 100644 index 0000000000000..473df3328c644 --- /dev/null +++ b/apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useRoomFilesListNavigation.ts @@ -0,0 +1,42 @@ +import { useCallback } from 'react'; +import { useFocusManager } from 'react-aria'; + +const isListItem = (node: EventTarget) => (node as HTMLElement).getAttribute('role') === 'listitem'; + +export const useRoomFilesNavigation = () => { + const roomFilesFocusManager = useFocusManager(); + + const roomFilesRef = useCallback( + (node: HTMLDivElement) => { + if (!node) { + return; + } + + node.addEventListener('keydown', (e) => { + if (!e.target) { + return; + } + + if (!isListItem(e.target)) { + return; + } + + if (e.key === 'ArrowUp' || e.key === 'ArrowDown') { + e.preventDefault(); + e.stopPropagation(); + + if (e.key === 'ArrowUp') { + roomFilesFocusManager?.focusPrevious({ accept: (node) => isListItem(node) }); + } + + if (e.key === 'ArrowDown') { + roomFilesFocusManager?.focusNext({ accept: (node) => isListItem(node) }); + } + } + }); + }, + [roomFilesFocusManager], + ); + + return roomFilesRef; +}; From c27c31b55a8b80fa822836d7f01ee84a862bfca9 Mon Sep 17 00:00:00 2001 From: yash-rajpal Date: Tue, 20 May 2025 01:44:59 +0530 Subject: [PATCH 03/12] escape key functionality --- .../contextualBar/RoomFiles/hooks/useRoomFilesListNavigation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useRoomFilesListNavigation.ts b/apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useRoomFilesListNavigation.ts index 473df3328c644..d4cbc41e2646c 100644 --- a/apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useRoomFilesListNavigation.ts +++ b/apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useRoomFilesListNavigation.ts @@ -7,7 +7,7 @@ export const useRoomFilesNavigation = () => { const roomFilesFocusManager = useFocusManager(); const roomFilesRef = useCallback( - (node: HTMLDivElement) => { + (node: HTMLDivElement | null) => { if (!node) { return; } From 0826009a248dcda67fd7156a287579599e6ee78b Mon Sep 17 00:00:00 2001 From: yash-rajpal Date: Tue, 20 May 2025 01:59:58 +0530 Subject: [PATCH 04/12] maybe this will fix virtuoso list re-renders on menu close --- .../views/room/contextualBar/RoomFiles/RoomFiles.tsx | 8 +++++++- .../room/contextualBar/RoomFiles/components/FileItem.tsx | 5 +++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx b/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx index caab4e721ae1a..714f0e692100a 100644 --- a/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx +++ b/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx @@ -2,7 +2,7 @@ import type { IUpload, IUploadWithUser } from '@rocket.chat/core-typings'; import type { SelectOption } from '@rocket.chat/fuselage'; import { Box, Icon, TextInput, Select, Throbber, ContextualbarSection } from '@rocket.chat/fuselage'; import type { ChangeEvent } from 'react'; -import { forwardRef, useMemo } from 'react'; +import { forwardRef, useEffect, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { Virtuoso } from 'react-virtuoso'; @@ -17,6 +17,7 @@ import { ContextualbarEmptyContent, } from '../../../../components/Contextualbar'; import { VirtualizedScrollbars } from '../../../../components/CustomScrollbars'; +import { useMessageListNavigation } from '../../hooks/useMessageListNavigation'; type RoomFilesProps = { loading: boolean; @@ -57,6 +58,11 @@ const RoomFiles = ({ [t], ); + useEffect(() => { + console.log('filesItems', filesItems); + }, [filesItems]); + + // const { messageListRef: roomFilesRef } = useMessageListNavigation(); const roomFilesRef = useRoomFilesNavigation(); return ( diff --git a/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItem.tsx b/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItem.tsx index f1595b60ecbad..b5adf15f04957 100644 --- a/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItem.tsx +++ b/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItem.tsx @@ -1,7 +1,7 @@ import type { IUpload, IUploadWithUser } from '@rocket.chat/core-typings'; import { css } from '@rocket.chat/css-in-js'; import { Box, Palette } from '@rocket.chat/fuselage'; -import { useRef } from 'react'; +import { memo, useRef } from 'react'; import FileItemIcon from './FileItemIcon'; import FileItemMenu from './FileItemMenu'; @@ -58,6 +58,7 @@ const FileItem = ({ fileData, onClickDelete }: FileItemProps) => { tabIndex={0} onKeyDown={handleKeyDown} role='listitem' + key={`file-${_id}`} > {typeGroup === 'image' ? ( @@ -99,4 +100,4 @@ const FileItem = ({ fileData, onClickDelete }: FileItemProps) => { ); }; -export default FileItem; +export default memo(FileItem); From f075e3f73863dfe8c1b9758e448dd2173ba343b1 Mon Sep 17 00:00:00 2001 From: yash-rajpal Date: Sat, 24 May 2025 01:16:19 +0530 Subject: [PATCH 05/12] POC: keyboard navigation --- .../contextualBar/RoomFiles/RoomFiles.tsx | 12 ++--- .../RoomFiles/components/FileItem.tsx | 16 +++++-- .../hooks/useRoomFilesListNavigation.ts | 47 +++++++++++++++++-- 3 files changed, 60 insertions(+), 15 deletions(-) diff --git a/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx b/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx index 714f0e692100a..3ec6454ce36d0 100644 --- a/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx +++ b/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx @@ -2,7 +2,7 @@ import type { IUpload, IUploadWithUser } from '@rocket.chat/core-typings'; import type { SelectOption } from '@rocket.chat/fuselage'; import { Box, Icon, TextInput, Select, Throbber, ContextualbarSection } from '@rocket.chat/fuselage'; import type { ChangeEvent } from 'react'; -import { forwardRef, useEffect, useMemo } from 'react'; +import { forwardRef, memo, useEffect, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { Virtuoso } from 'react-virtuoso'; @@ -17,7 +17,6 @@ import { ContextualbarEmptyContent, } from '../../../../components/Contextualbar'; import { VirtualizedScrollbars } from '../../../../components/CustomScrollbars'; -import { useMessageListNavigation } from '../../hooks/useMessageListNavigation'; type RoomFilesProps = { loading: boolean; @@ -62,8 +61,7 @@ const RoomFiles = ({ console.log('filesItems', filesItems); }, [filesItems]); - // const { messageListRef: roomFilesRef } = useMessageListNavigation(); - const roomFilesRef = useRoomFilesNavigation(); + const {roomFilesRef, setFocusedItem, focusedItem} = useRoomFilesNavigation(); return ( <> @@ -101,9 +99,9 @@ const RoomFiles = ({ }} totalCount={total} endReached={(start) => loadMoreItems(start, Math.min(50, total - start))} - overscan={50} + overscan={100} data={filesItems} - itemContent={(_, data) => } + itemContent={(index, data) => } components={{ // eslint-disable-next-line react/no-multi-comp List: forwardRef(function List(props, ref) { @@ -120,4 +118,4 @@ const RoomFiles = ({ ); }; -export default RoomFiles; +export default memo(RoomFiles); diff --git a/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItem.tsx b/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItem.tsx index b5adf15f04957..112905434f52d 100644 --- a/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItem.tsx +++ b/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItem.tsx @@ -1,7 +1,7 @@ import type { IUpload, IUploadWithUser } from '@rocket.chat/core-typings'; import { css } from '@rocket.chat/css-in-js'; import { Box, Palette } from '@rocket.chat/fuselage'; -import { memo, useRef } from 'react'; +import { memo, useEffect, useRef } from 'react'; import FileItemIcon from './FileItemIcon'; import FileItemMenu from './FileItemMenu'; @@ -26,14 +26,23 @@ const customClass = css` type FileItemProps = { fileData: IUploadWithUser; onClickDelete: (id: IUpload['_id']) => void; + focused: boolean; }; -const FileItem = ({ fileData, onClickDelete }: FileItemProps) => { +const FileItem = ({ fileData, onClickDelete, focused }: FileItemProps) => { const format = useFormatDateAndTime(); const { _id, path, name, uploadedAt, type, typeGroup, user } = fileData; const encryptedAnchorProps = useDownloadFromServiceWorker(path || '', name); const ref = useRef(null); + const containerRef = useRef(null); + + useEffect(() => { + if (focused && containerRef.current) { + containerRef.current.focus(); + } + } + , [focused]); const handleKeyDown = (event: React.KeyboardEvent) => { if (!ref.current) { @@ -58,7 +67,8 @@ const FileItem = ({ fileData, onClickDelete }: FileItemProps) => { tabIndex={0} onKeyDown={handleKeyDown} role='listitem' - key={`file-${_id}`} + ref={containerRef} + // key={`file-${_id}`} > {typeGroup === 'image' ? ( diff --git a/apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useRoomFilesListNavigation.ts b/apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useRoomFilesListNavigation.ts index d4cbc41e2646c..2f08489ff74ec 100644 --- a/apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useRoomFilesListNavigation.ts +++ b/apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useRoomFilesListNavigation.ts @@ -1,4 +1,4 @@ -import { useCallback } from 'react'; +import { useCallback, useState } from 'react'; import { useFocusManager } from 'react-aria'; const isListItem = (node: EventTarget) => (node as HTMLElement).getAttribute('role') === 'listitem'; @@ -6,6 +6,40 @@ const isListItem = (node: EventTarget) => (node as HTMLElement).getAttribute('ro export const useRoomFilesNavigation = () => { const roomFilesFocusManager = useFocusManager(); + // const roomFilesRef = useCallback( + // (node: HTMLDivElement | null) => { + // if (!node) { + // return; + // } + + // node.addEventListener('keydown', (e) => { + // if (!e.target) { + // return; + // } + + // if (!isListItem(e.target)) { + // return; + // } + + // if (e.key === 'ArrowUp' || e.key === 'ArrowDown') { + // e.preventDefault(); + // e.stopPropagation(); + + // if (e.key === 'ArrowUp') { + // roomFilesFocusManager?.focusPrevious({ accept: (node) => isListItem(node) }); + // } + + // if (e.key === 'ArrowDown') { + // roomFilesFocusManager?.focusNext({ accept: (node) => isListItem(node) }); + // } + // } + // }); + // }, + // [roomFilesFocusManager], + // ); + + const [focusedItem, setFocusedItem] = useState(-1); + const roomFilesRef = useCallback( (node: HTMLDivElement | null) => { if (!node) { @@ -22,21 +56,24 @@ export const useRoomFilesNavigation = () => { } if (e.key === 'ArrowUp' || e.key === 'ArrowDown') { + console.log('arrow up or down'); e.preventDefault(); e.stopPropagation(); if (e.key === 'ArrowUp') { - roomFilesFocusManager?.focusPrevious({ accept: (node) => isListItem(node) }); + // roomFilesFocusManager?.focusPrevious({ accept: (node) => isListItem(node) }); + setFocusedItem((prev) => (prev > 0 ? prev - 1 : prev)); } if (e.key === 'ArrowDown') { - roomFilesFocusManager?.focusNext({ accept: (node) => isListItem(node) }); + // roomFilesFocusManager?.focusNext({ accept: (node) => isListItem(node) }); + setFocusedItem((prev) => prev + 1); } } }); }, - [roomFilesFocusManager], + [roomFilesFocusManager, setFocusedItem], ); - return roomFilesRef; + return {roomFilesRef, focusedItem, setFocusedItem}; }; From 4eedd4fd877bdd9d85cf61087e780ec565118ede Mon Sep 17 00:00:00 2001 From: yash-rajpal Date: Wed, 4 Jun 2025 23:10:30 +0530 Subject: [PATCH 06/12] keyboard navigation --- .../contextualBar/RoomFiles/RoomFiles.tsx | 13 ++++- .../RoomFiles/components/FileItem.tsx | 34 +++++++++++-- .../hooks/useRoomFilesListNavigation.ts | 49 ++----------------- 3 files changed, 46 insertions(+), 50 deletions(-) diff --git a/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx b/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx index 6299a4cd60725..8144c57a2c20b 100644 --- a/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx +++ b/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx @@ -62,7 +62,7 @@ const RoomFiles = ({ console.log('filesItems', filesItems); }, [filesItems]); - const {roomFilesRef, setFocusedItem, focusedItem} = useRoomFilesNavigation(); + const { roomFilesRef, focusedItem, setFocusedItem } = useRoomFilesNavigation(total); return ( @@ -102,7 +102,16 @@ const RoomFiles = ({ endReached={(start) => loadMoreItems(start, Math.min(50, total - start))} overscan={100} data={filesItems} - itemContent={(index, data) => } + itemContent={(index, data) => ( + + )} components={{ // eslint-disable-next-line react/no-multi-comp List: forwardRef(function List(props, ref) { diff --git a/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItem.tsx b/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItem.tsx index 112905434f52d..f9f4e97ab3783 100644 --- a/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItem.tsx +++ b/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItem.tsx @@ -27,9 +27,12 @@ type FileItemProps = { fileData: IUploadWithUser; onClickDelete: (id: IUpload['_id']) => void; focused: boolean; + focusedItem: number; + setFocusedItem: (index: number) => void; + index: number; }; -const FileItem = ({ fileData, onClickDelete, focused }: FileItemProps) => { +const FileItem = ({ fileData, onClickDelete, focusedItem, setFocusedItem, index }: FileItemProps) => { const format = useFormatDateAndTime(); const { _id, path, name, uploadedAt, type, typeGroup, user } = fileData; @@ -38,11 +41,34 @@ const FileItem = ({ fileData, onClickDelete, focused }: FileItemProps) => { const containerRef = useRef(null); useEffect(() => { - if (focused && containerRef.current) { + if (!containerRef.current) { + return; + } + + if (index === focusedItem) { containerRef.current.focus(); } - } - , [focused]); + }, [index, focusedItem]); + + useEffect(() => { + if (!containerRef.current) { + return; + } + + const handleFocus = () => { + if (focusedItem === index) { + return; + } + + setFocusedItem(index); + }; + + containerRef.current.addEventListener('focus', handleFocus); + + return () => { + containerRef.current?.removeEventListener('focus', handleFocus); + }; + }, [focusedItem, index, setFocusedItem]); const handleKeyDown = (event: React.KeyboardEvent) => { if (!ref.current) { diff --git a/apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useRoomFilesListNavigation.ts b/apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useRoomFilesListNavigation.ts index 2f08489ff74ec..0d1854dc7cfee 100644 --- a/apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useRoomFilesListNavigation.ts +++ b/apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useRoomFilesListNavigation.ts @@ -1,43 +1,8 @@ import { useCallback, useState } from 'react'; -import { useFocusManager } from 'react-aria'; const isListItem = (node: EventTarget) => (node as HTMLElement).getAttribute('role') === 'listitem'; -export const useRoomFilesNavigation = () => { - const roomFilesFocusManager = useFocusManager(); - - // const roomFilesRef = useCallback( - // (node: HTMLDivElement | null) => { - // if (!node) { - // return; - // } - - // node.addEventListener('keydown', (e) => { - // if (!e.target) { - // return; - // } - - // if (!isListItem(e.target)) { - // return; - // } - - // if (e.key === 'ArrowUp' || e.key === 'ArrowDown') { - // e.preventDefault(); - // e.stopPropagation(); - - // if (e.key === 'ArrowUp') { - // roomFilesFocusManager?.focusPrevious({ accept: (node) => isListItem(node) }); - // } - - // if (e.key === 'ArrowDown') { - // roomFilesFocusManager?.focusNext({ accept: (node) => isListItem(node) }); - // } - // } - // }); - // }, - // [roomFilesFocusManager], - // ); - +export const useRoomFilesNavigation = (maxItems: number) => { const [focusedItem, setFocusedItem] = useState(-1); const roomFilesRef = useCallback( @@ -56,24 +21,20 @@ export const useRoomFilesNavigation = () => { } if (e.key === 'ArrowUp' || e.key === 'ArrowDown') { - console.log('arrow up or down'); e.preventDefault(); e.stopPropagation(); - if (e.key === 'ArrowUp') { - // roomFilesFocusManager?.focusPrevious({ accept: (node) => isListItem(node) }); - setFocusedItem((prev) => (prev > 0 ? prev - 1 : prev)); + setFocusedItem((prev) => Math.max(0, prev - 1)); } if (e.key === 'ArrowDown') { - // roomFilesFocusManager?.focusNext({ accept: (node) => isListItem(node) }); - setFocusedItem((prev) => prev + 1); + setFocusedItem((prev) => Math.min(maxItems - 1, prev + 1)); } } }); }, - [roomFilesFocusManager, setFocusedItem], + [setFocusedItem, maxItems], ); - return {roomFilesRef, focusedItem, setFocusedItem}; + return { roomFilesRef, focusedItem, setFocusedItem }; }; From f8c65160cf806143a9b885179d7bfeade7582656 Mon Sep 17 00:00:00 2001 From: yash-rajpal Date: Thu, 5 Jun 2025 00:10:57 +0530 Subject: [PATCH 07/12] remove logs --- .../client/views/room/contextualBar/RoomFiles/RoomFiles.tsx | 6 +----- .../room/contextualBar/RoomFiles/components/FileItem.tsx | 1 - 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx b/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx index 8144c57a2c20b..3203708810e22 100644 --- a/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx +++ b/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx @@ -2,7 +2,7 @@ import type { IUpload, IUploadWithUser } from '@rocket.chat/core-typings'; import type { SelectOption } from '@rocket.chat/fuselage'; import { Box, Icon, TextInput, Select, Throbber, ContextualbarSection } from '@rocket.chat/fuselage'; import type { ChangeEvent } from 'react'; -import { forwardRef, memo, useEffect, useMemo } from 'react'; +import { forwardRef, memo, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { Virtuoso } from 'react-virtuoso'; @@ -58,10 +58,6 @@ const RoomFiles = ({ [t], ); - useEffect(() => { - console.log('filesItems', filesItems); - }, [filesItems]); - const { roomFilesRef, focusedItem, setFocusedItem } = useRoomFilesNavigation(total); return ( diff --git a/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItem.tsx b/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItem.tsx index f9f4e97ab3783..8e6ec75f3074d 100644 --- a/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItem.tsx +++ b/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItem.tsx @@ -77,7 +77,6 @@ const FileItem = ({ fileData, onClickDelete, focusedItem, setFocusedItem, index if (event.code === 'Enter' || event.code === 'Space') { event.preventDefault(); - console.log('click'); ref.current.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window })); } }; From f78f27fe65a214a943fb3f87a1305907b2601290 Mon Sep 17 00:00:00 2001 From: dougfabris Date: Mon, 9 Jun 2025 17:34:08 -0300 Subject: [PATCH 08/12] fix: review --- .../VirtualizedScrollbars.tsx | 2 +- .../RoomFiles/RoomFileItemWrapper.tsx | 35 ++++++++ .../contextualBar/RoomFiles/RoomFiles.tsx | 27 ++---- .../RoomFiles/RoomFilesListWrapper.tsx | 10 +++ .../RoomFiles/components/FileItem.tsx | 90 ++----------------- .../hooks/useRoomFilesListNavigation.ts | 40 --------- packages/i18n/src/locales/en.i18n.json | 1 + 7 files changed, 62 insertions(+), 143 deletions(-) create mode 100644 apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFileItemWrapper.tsx create mode 100644 apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFilesListWrapper.tsx delete mode 100644 apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useRoomFilesListNavigation.ts diff --git a/apps/meteor/client/components/CustomScrollbars/VirtualizedScrollbars.tsx b/apps/meteor/client/components/CustomScrollbars/VirtualizedScrollbars.tsx index 82c117f25fca3..7095e3971f6cc 100644 --- a/apps/meteor/client/components/CustomScrollbars/VirtualizedScrollbars.tsx +++ b/apps/meteor/client/components/CustomScrollbars/VirtualizedScrollbars.tsx @@ -38,7 +38,7 @@ const VirtualizedScrollbars = forwardRef osInstance()?.destroy(); }, [initialize, osInstance, ref, scroller]); - return {cloneElement(props.children, { scrollerRef: setScroller })}; + return {cloneElement(props.children, { tabIndex: -1, scrollerRef: setScroller })}; }); export default memo(VirtualizedScrollbars); diff --git a/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFileItemWrapper.tsx b/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFileItemWrapper.tsx new file mode 100644 index 0000000000000..d9c310667d584 --- /dev/null +++ b/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFileItemWrapper.tsx @@ -0,0 +1,35 @@ +import type { IUploadWithUser } from '@rocket.chat/core-typings'; +import { css } from '@rocket.chat/css-in-js'; +import { Box, Palette } from '@rocket.chat/fuselage'; +import type { ComponentProps, Ref } from 'react'; +import { forwardRef } from 'react'; + +const customClass = css` + &:hover { + cursor: pointer; + background: ${Palette.surface['surface-hover']}; + } +`; + +type RoomFileItemWrapperProps = ComponentProps & { item: IUploadWithUser }; + +const RoomFileItemWrapper = forwardRef(function RoomFileItemWrapper( + { item, ...props }: RoomFileItemWrapperProps, + ref: Ref, +) { + return ( + + ); +}); + +export default RoomFileItemWrapper; diff --git a/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx b/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx index 3203708810e22..48f0d698edcae 100644 --- a/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx +++ b/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx @@ -2,12 +2,13 @@ import type { IUpload, IUploadWithUser } from '@rocket.chat/core-typings'; import type { SelectOption } from '@rocket.chat/fuselage'; import { Box, Icon, TextInput, Select, Throbber, ContextualbarSection } from '@rocket.chat/fuselage'; import type { ChangeEvent } from 'react'; -import { forwardRef, memo, useMemo } from 'react'; +import { memo, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { Virtuoso } from 'react-virtuoso'; +import RoomFileItemWrapper from './RoomFileItemWrapper'; +import RoomFilesListWrapper from './RoomFilesListWrapper'; import FileItem from './components/FileItem'; -import { useRoomFilesNavigation } from './hooks/useRoomFilesListNavigation'; import { ContextualbarHeader, ContextualbarIcon, @@ -58,8 +59,6 @@ const RoomFiles = ({ [t], ); - const { roomFilesRef, focusedItem, setFocusedItem } = useRoomFilesNavigation(total); - return ( @@ -88,7 +87,7 @@ const RoomFiles = ({ {!loading && filesItems.length === 0 && } {!loading && filesItems.length > 0 && ( - + loadMoreItems(start, Math.min(50, total - start))} overscan={100} data={filesItems} - itemContent={(index, data) => ( - - )} + itemContent={(_, data) => } components={{ - // eslint-disable-next-line react/no-multi-comp - List: forwardRef(function List(props, ref) { - return ; - }), + List: RoomFilesListWrapper, + Item: RoomFileItemWrapper, }} - tabIndex={-1} /> diff --git a/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFilesListWrapper.tsx b/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFilesListWrapper.tsx new file mode 100644 index 0000000000000..2bddc51b5f25a --- /dev/null +++ b/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFilesListWrapper.tsx @@ -0,0 +1,10 @@ +import type { HTMLAttributes, Ref } from 'react'; +import { forwardRef } from 'react'; +import { useTranslation } from 'react-i18next'; + +const RoomFilesListWrapper = forwardRef(function RoomFilesListWrapper(props: HTMLAttributes, ref: Ref) { + const { t } = useTranslation(); + return
; +}); + +export default RoomFilesListWrapper; diff --git a/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItem.tsx b/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItem.tsx index 8e6ec75f3074d..0fb8eed68aaed 100644 --- a/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItem.tsx +++ b/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItem.tsx @@ -1,7 +1,6 @@ import type { IUpload, IUploadWithUser } from '@rocket.chat/core-typings'; -import { css } from '@rocket.chat/css-in-js'; -import { Box, Palette } from '@rocket.chat/fuselage'; -import { memo, useEffect, useRef } from 'react'; +import { Box } from '@rocket.chat/fuselage'; +import { memo } from 'react'; import FileItemIcon from './FileItemIcon'; import FileItemMenu from './FileItemMenu'; @@ -9,98 +8,26 @@ import ImageItem from './ImageItem'; import { useDownloadFromServiceWorker } from '../../../../../hooks/useDownloadFromServiceWorker'; import { useFormatDateAndTime } from '../../../../../hooks/useFormatDateAndTime'; -const customClass = css` - &:hover { - cursor: pointer; - background: ${Palette.surface['surface-hover']}; - } - - &:focus.focus-visible { - outline: 0; - margin: 2px; - box-shadow: 0 0 0 2px ${Palette.stroke['stroke-extra-light-highlight']}; - border-color: ${Palette.stroke['stroke-highlight']}; - } -`; - type FileItemProps = { fileData: IUploadWithUser; onClickDelete: (id: IUpload['_id']) => void; - focused: boolean; - focusedItem: number; - setFocusedItem: (index: number) => void; - index: number; }; -const FileItem = ({ fileData, onClickDelete, focusedItem, setFocusedItem, index }: FileItemProps) => { +const FileItem = ({ fileData, onClickDelete }: FileItemProps) => { const format = useFormatDateAndTime(); const { _id, path, name, uploadedAt, type, typeGroup, user } = fileData; const encryptedAnchorProps = useDownloadFromServiceWorker(path || '', name); - const ref = useRef(null); - const containerRef = useRef(null); - - useEffect(() => { - if (!containerRef.current) { - return; - } - - if (index === focusedItem) { - containerRef.current.focus(); - } - }, [index, focusedItem]); - - useEffect(() => { - if (!containerRef.current) { - return; - } - - const handleFocus = () => { - if (focusedItem === index) { - return; - } - - setFocusedItem(index); - }; - - containerRef.current.addEventListener('focus', handleFocus); - - return () => { - containerRef.current?.removeEventListener('focus', handleFocus); - }; - }, [focusedItem, index, setFocusedItem]); - - const handleKeyDown = (event: React.KeyboardEvent) => { - if (!ref.current) { - return; - } - - if (event.code === 'Enter' || event.code === 'Space') { - event.preventDefault(); - ref.current.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window })); - } - }; return ( - + <> {typeGroup === 'image' ? ( - + ) : ( @@ -131,7 +57,7 @@ const FileItem = ({ fileData, onClickDelete, focusedItem, setFocusedItem, index )} - + ); }; diff --git a/apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useRoomFilesListNavigation.ts b/apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useRoomFilesListNavigation.ts deleted file mode 100644 index 0d1854dc7cfee..0000000000000 --- a/apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useRoomFilesListNavigation.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { useCallback, useState } from 'react'; - -const isListItem = (node: EventTarget) => (node as HTMLElement).getAttribute('role') === 'listitem'; - -export const useRoomFilesNavigation = (maxItems: number) => { - const [focusedItem, setFocusedItem] = useState(-1); - - const roomFilesRef = useCallback( - (node: HTMLDivElement | null) => { - if (!node) { - return; - } - - node.addEventListener('keydown', (e) => { - if (!e.target) { - return; - } - - if (!isListItem(e.target)) { - return; - } - - if (e.key === 'ArrowUp' || e.key === 'ArrowDown') { - e.preventDefault(); - e.stopPropagation(); - if (e.key === 'ArrowUp') { - setFocusedItem((prev) => Math.max(0, prev - 1)); - } - - if (e.key === 'ArrowDown') { - setFocusedItem((prev) => Math.min(maxItems - 1, prev + 1)); - } - } - }); - }, - [setFocusedItem, maxItems], - ); - - return { roomFilesRef, focusedItem, setFocusedItem }; -}; diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json index 1037d901b078c..4d1133ee4647f 100644 --- a/packages/i18n/src/locales/en.i18n.json +++ b/packages/i18n/src/locales/en.i18n.json @@ -2214,6 +2214,7 @@ "File_uploaded": "File uploaded", "File_uploaded_successfully": "File uploaded successfully", "Files": "Files", + "Files_list": "Files list", "Files_only": "Only remove the attached files, keep messages", "Filter": "Filter", "Filter_By_Price": "Filter by price", From 3633e15a26b4414b4ab7b24da1e5ba418e960b5b Mon Sep 17 00:00:00 2001 From: yash-rajpal Date: Tue, 10 Jun 2025 02:31:51 +0530 Subject: [PATCH 09/12] cleanupg --- .../CustomScrollbars/VirtualizedScrollbars.tsx | 6 ++---- .../client/components/ImageGallery/ImageGallery.tsx | 2 +- .../views/room/contextualBar/RoomFiles/RoomFiles.tsx | 4 ++-- .../contextualBar/RoomFiles/components/FileItem.tsx | 3 +-- .../contextualBar/RoomFiles/components/ImageItem.tsx | 10 ++++------ 5 files changed, 10 insertions(+), 15 deletions(-) diff --git a/apps/meteor/client/components/CustomScrollbars/VirtualizedScrollbars.tsx b/apps/meteor/client/components/CustomScrollbars/VirtualizedScrollbars.tsx index 7095e3971f6cc..2ddde3b87c13c 100644 --- a/apps/meteor/client/components/CustomScrollbars/VirtualizedScrollbars.tsx +++ b/apps/meteor/client/components/CustomScrollbars/VirtualizedScrollbars.tsx @@ -1,4 +1,3 @@ -import { useMergedRefs } from '@rocket.chat/fuselage-hooks'; import { useOverlayScrollbars } from 'overlayscrollbars-react'; import type { HTMLAttributes, ReactElement } from 'react'; import { useEffect, useState, useRef, cloneElement, forwardRef, memo } from 'react'; @@ -14,14 +13,13 @@ const VirtualizedScrollbars = forwardRef(null); + const rootRef = useRef(null); const [scroller, setScroller] = useState(null); const scrollbarsOptions = getScrollbarsOptions(overflowX); const [initialize, osInstance] = useOverlayScrollbars({ options: scrollbarsOptions, defer: true, }); - const mergedRef = useMergedRefs(rootRef, ref); useEffect(() => { const { current: root } = rootRef; @@ -38,7 +36,7 @@ const VirtualizedScrollbars = forwardRef osInstance()?.destroy(); }, [initialize, osInstance, ref, scroller]); - return {cloneElement(props.children, { tabIndex: -1, scrollerRef: setScroller })}; + return {cloneElement(props.children, { tabIndex: -1, scrollerRef: setScroller })}; }); export default memo(VirtualizedScrollbars); diff --git a/apps/meteor/client/components/ImageGallery/ImageGallery.tsx b/apps/meteor/client/components/ImageGallery/ImageGallery.tsx index f871a60a3af2d..9884c463e8986 100644 --- a/apps/meteor/client/components/ImageGallery/ImageGallery.tsx +++ b/apps/meteor/client/components/ImageGallery/ImageGallery.tsx @@ -122,7 +122,7 @@ export const ImageGallery = ({ images, onClose, loadMore }: { images: IUpload[]; return createPortal( <> - +
diff --git a/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx b/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx index 48f0d698edcae..97aba1cebb4d7 100644 --- a/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx +++ b/apps/meteor/client/views/room/contextualBar/RoomFiles/RoomFiles.tsx @@ -2,7 +2,7 @@ import type { IUpload, IUploadWithUser } from '@rocket.chat/core-typings'; import type { SelectOption } from '@rocket.chat/fuselage'; import { Box, Icon, TextInput, Select, Throbber, ContextualbarSection } from '@rocket.chat/fuselage'; import type { ChangeEvent } from 'react'; -import { memo, useMemo } from 'react'; +import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { Virtuoso } from 'react-virtuoso'; @@ -111,4 +111,4 @@ const RoomFiles = ({ ); }; -export default memo(RoomFiles); +export default RoomFiles; diff --git a/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItem.tsx b/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItem.tsx index 0fb8eed68aaed..44b3bd18636b8 100644 --- a/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItem.tsx +++ b/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItem.tsx @@ -1,6 +1,5 @@ import type { IUpload, IUploadWithUser } from '@rocket.chat/core-typings'; import { Box } from '@rocket.chat/fuselage'; -import { memo } from 'react'; import FileItemIcon from './FileItemIcon'; import FileItemMenu from './FileItemMenu'; @@ -61,4 +60,4 @@ const FileItem = ({ fileData, onClickDelete }: FileItemProps) => { ); }; -export default memo(FileItem); +export default FileItem; diff --git a/apps/meteor/client/views/room/contextualBar/RoomFiles/components/ImageItem.tsx b/apps/meteor/client/views/room/contextualBar/RoomFiles/components/ImageItem.tsx index b8f518b923e2f..d9ae0d30cfd67 100644 --- a/apps/meteor/client/views/room/contextualBar/RoomFiles/components/ImageItem.tsx +++ b/apps/meteor/client/views/room/contextualBar/RoomFiles/components/ImageItem.tsx @@ -1,6 +1,4 @@ import { Avatar, Box } from '@rocket.chat/fuselage'; -import type { ComponentProps } from 'react'; -import { forwardRef } from 'react'; type ImageItemProps = { id: string; @@ -8,11 +6,11 @@ type ImageItemProps = { name: string | undefined; timestamp: string; username?: string; -} & ComponentProps; +}; -const ImageItem = forwardRef(function ImageItem({ id, url, name, timestamp, username }: ImageItemProps, ref) { +const ImageItem = ({ id, url, name, timestamp, username }: ImageItemProps) => { return ( - + {url && ( @@ -35,6 +33,6 @@ const ImageItem = forwardRef(function ImageItem({ i ); -}); +}; export default ImageItem; From dd7e835294f54dcf8cc798f7826ce3ac58ec0121 Mon Sep 17 00:00:00 2001 From: yash-rajpal Date: Tue, 10 Jun 2025 02:40:36 +0530 Subject: [PATCH 10/12] menu props --- .../room/contextualBar/RoomFiles/components/FileItemMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItemMenu.tsx b/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItemMenu.tsx index 0de6cd1867d25..edde3dd71c98b 100644 --- a/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItemMenu.tsx +++ b/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItemMenu.tsx @@ -88,7 +88,7 @@ const FileItemMenu = ({ fileData, onClickDelete }: FileItemMenuProps) => { : []), ] as GenericMenuItemProps[]; - return ; + return ; }; export default memo(FileItemMenu); From 9fc6e37ee5f3a8039b0c35154de518e8931aeda2 Mon Sep 17 00:00:00 2001 From: yash-rajpal Date: Tue, 10 Jun 2025 18:43:42 +0530 Subject: [PATCH 11/12] add changeset --- .changeset/silver-rice-hug.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/silver-rice-hug.md diff --git a/.changeset/silver-rice-hug.md b/.changeset/silver-rice-hug.md new file mode 100644 index 0000000000000..b5471b50d9c86 --- /dev/null +++ b/.changeset/silver-rice-hug.md @@ -0,0 +1,6 @@ +--- +'@rocket.chat/i18n': minor +'@rocket.chat/meteor': minor +--- + +Improves room files contextualbar web accessibility. From ff29387cabb79e8b8a0350199644d73db2fee9dc Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Tue, 10 Jun 2025 12:57:39 -0300 Subject: [PATCH 12/12] chore: update changeset --- .changeset/silver-rice-hug.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/silver-rice-hug.md b/.changeset/silver-rice-hug.md index b5471b50d9c86..5b193e4749b6f 100644 --- a/.changeset/silver-rice-hug.md +++ b/.changeset/silver-rice-hug.md @@ -3,4 +3,4 @@ '@rocket.chat/meteor': minor --- -Improves room files contextualbar web accessibility. +Resolves some accessibility violations in the rooms file list context bar, including issues with ARIA attributes and focus management.