Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/silver-rice-hug.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@rocket.chat/i18n': minor
'@rocket.chat/meteor': minor
---

Resolves some accessibility violations in the rooms file list context bar, including issues with ARIA attributes and focus management.
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const VirtualizedScrollbars = forwardRef<HTMLElement, VirtualizedScrollbarsProps
return () => osInstance()?.destroy();
}, [initialize, osInstance, ref, scroller]);

return <BaseScrollbars ref={rootRef}>{cloneElement(props.children, { scrollerRef: setScroller })}</BaseScrollbars>;
return <BaseScrollbars ref={rootRef}>{cloneElement(props.children, { tabIndex: -1, scrollerRef: setScroller })}</BaseScrollbars>;
});

export default memo(VirtualizedScrollbars);
Original file line number Diff line number Diff line change
@@ -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<typeof Box> & { item: IUploadWithUser };

const RoomFileItemWrapper = forwardRef(function RoomFileItemWrapper(
{ item, ...props }: RoomFileItemWrapperProps,
ref: Ref<HTMLDivElement>,
) {
return (
<Box
ref={ref}
role='listitem'
aria-label={item.name}
display='flex'
pb={12}
pi={24}
borderRadius={4}
className={customClass}
{...props}
/>
);
});

export default RoomFileItemWrapper;
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { 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 {
ContextualbarHeader,
Expand Down Expand Up @@ -93,9 +95,13 @@ const RoomFiles = ({
}}
totalCount={total}
endReached={(start) => loadMoreItems(start, Math.min(50, total - start))}
overscan={50}
overscan={100}
data={filesItems}
itemContent={(_, data) => <FileItem fileData={data} onClickDelete={onClickDelete} />}
components={{
List: RoomFilesListWrapper,
Item: RoomFileItemWrapper,
}}
/>
</VirtualizedScrollbars>
</Box>
Expand Down
Original file line number Diff line number Diff line change
@@ -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<HTMLDivElement>, ref: Ref<HTMLDivElement>) {
const { t } = useTranslation();
return <div role='list' aria-label={t('Files_list')} ref={ref} {...props} />;
});

export default RoomFilesListWrapper;
Original file line number Diff line number Diff line change
@@ -1,20 +1,12 @@
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 { Box } from '@rocket.chat/fuselage';

import FileItemIcon from './FileItemIcon';
import FileItemMenu from './FileItemMenu';
import ImageItem from './ImageItem';
import { useDownloadFromServiceWorker } from '../../../../../hooks/useDownloadFromServiceWorker';
import { useFormatDateAndTime } from '../../../../../hooks/useFormatDateAndTime';

const hoverClass = css`
&:hover {
cursor: pointer;
background: ${Palette.surface['surface-hover']};
}
`;

type FileItemProps = {
fileData: IUploadWithUser;
onClickDelete: (id: IUpload['_id']) => void;
Expand All @@ -27,13 +19,14 @@ const FileItem = ({ fileData, onClickDelete }: FileItemProps) => {
const encryptedAnchorProps = useDownloadFromServiceWorker(path || '', name);

return (
<Box display='flex' pb={12} pi={24} borderRadius={4} className={hoverClass}>
<>
{typeGroup === 'image' ? (
<ImageItem id={_id} url={path} name={name} username={user?.username} timestamp={format(uploadedAt)} />
) : (
<Box
is='a'
minWidth={0}
aria-label={name}
download
rel='noopener noreferrer'
target='_blank'
Expand All @@ -42,6 +35,7 @@ const FileItem = ({ fileData, onClickDelete }: FileItemProps) => {
flexGrow={1}
flexShrink={1}
href={path}
tabIndex={-1}
textDecorationLine='none'
{...(path?.includes('/file-decrypt/') ? encryptedAnchorProps : {})}
>
Expand All @@ -62,7 +56,7 @@ const FileItem = ({ fileData, onClickDelete }: FileItemProps) => {
</Box>
)}
<FileItemMenu fileData={fileData} onClickDelete={onClickDelete} />
</Box>
</>
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -45,15 +47,12 @@ const FileItemMenu = ({ fileData, onClickDelete }: FileItemMenuProps) => {
[fileData, t, uid],
);

const menuOptions = {
downLoad: {
label: (
<Box display='flex' alignItems='center'>
<Icon mie={4} name='download' size='x16' />
{t('Download')}
</Box>
),
action: () => {
const menuOptions = [
{
id: 'download',
content: t('Download'),
icon: 'download',
onClick: () => {
if (fileData.path?.includes('/file-decrypt/')) {
if (!controller) {
return;
Expand All @@ -76,21 +75,20 @@ const FileItemMenu = ({ fileData, onClickDelete }: FileItemMenuProps) => {
},
disabled: !canDownloadFile,
},
...(isDeletionAllowed &&
onClickDelete && {
delete: {
label: (
<Box display='flex' alignItems='center' color='status-font-on-danger'>
<Icon mie={4} name='trash' size='x16' />
{t('Delete')}
</Box>
),
action: () => onClickDelete(fileData._id),
},
}),
};
...(isDeletionAllowed && onClickDelete
? [
{
id: 'delete',
content: <Box color='status-font-on-danger'>{t('Delete')}</Box>,
onClick: () => onClickDelete(fileData._id),
icon: 'trash',
iconColor: 'status-font-on-danger',
},
]
: []),
] as GenericMenuItemProps[];

return <Menu options={menuOptions} />;
return <GenericMenu title={t('More')} aria-label={t('More')} items={menuOptions} placement='bottom-end' />;
};

export default memo(FileItemMenu);
1 change: 1 addition & 0 deletions packages/i18n/src/locales/en.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Loading