chore: disable invite link when in ABAC enabled room (#37349)

This commit is contained in:
Martin Schoeler 2025-11-03 18:59:43 -03:00 committed by GitHub
parent 2772d86682
commit 8e99ec6ce1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 541 additions and 5 deletions

View File

@ -0,0 +1,23 @@
import { composeStories } from '@storybook/react';
import { render } from '@testing-library/react';
import { axe } from 'jest-axe';
import * as stories from './RoomMembers.stories';
jest.mock('../../hooks/useUserInfoActions', () => ({
useUserInfoActions: jest.fn(),
}));
const testCases = Object.values(composeStories(stories)).map((Story) => [Story.storyName || 'Story', Story]);
test.each(testCases)(`renders %s without crashing`, async (_storyname, Story) => {
const { baseElement } = render(<Story />);
expect(baseElement).toMatchSnapshot();
});
test.each(testCases)('%s should have no a11y violations', async (_storyname, Story) => {
const { container } = render(<Story />);
// Disable 'nested-interactive' rule because our `Select` component is still not a11y compliant
const results = await axe(container, { rules: { 'nested-interactive': { enabled: false } } });
expect(results).toHaveNoViolations();
});

View File

@ -47,3 +47,27 @@ Loading.args = {
loadMoreItems: action('loadMoreItems'),
reload: action('reload'),
};
export const WithABACRoom = Template.bind({});
WithABACRoom.args = {
loading: false,
members: [
{
_id: 'rocket.cat',
username: 'rocket.cat',
status: UserStatus.ONLINE,
name: 'Rocket.Cat',
},
],
text: 'filter',
type: 'online',
setText: action('Lorem Ipsum'),
setType: action('online'),
total: 123,
loadMoreItems: action('loadMoreItems'),
rid: '!roomId',
isTeam: false,
isDirect: false,
reload: action('reload'),
isABACRoom: true,
};

View File

@ -44,13 +44,14 @@ type RoomMembersProps = {
loadMoreItems: () => void;
renderRow?: ElementType<ComponentProps<typeof RoomMembersRow>>;
reload: () => void;
isABACRoom?: boolean;
};
const RoomMembers = ({
loading,
members = [],
text,
type,
type = 'online',
setText,
setType,
onClickClose,
@ -65,6 +66,7 @@ const RoomMembers = ({
isTeam,
isDirect,
reload,
isABACRoom = false,
}: RoomMembersProps): ReactElement => {
const t = useTranslation();
const inputRef = useAutoFocus<HTMLInputElement>(true);
@ -199,7 +201,14 @@ const RoomMembers = ({
<ContextualbarFooter>
<ButtonGroup stretch>
{onClickInvite && (
<Button icon='link' onClick={onClickInvite} width='50%'>
<Button
icon='link'
onClick={onClickInvite}
width='50%'
disabled={isABACRoom}
title={isABACRoom ? t('Not_available_for_ABAC_enabled_rooms') : undefined}
aria-label={t('Invite_Link')}
>
{t('Invite_Link')}
</Button>
)}

View File

@ -39,7 +39,6 @@ const RoomMembersItem = ({
useRealName,
}: RoomMembersItemProps): ReactElement => {
const [showButton, setShowButton] = useState();
const isReduceMotionEnabled = usePrefersReducedMotion();
const handleMenuEvent = {
[isReduceMotionEnabled ? 'onMouseEnter' : 'onTransitionEnd']: setShowButton,
@ -62,7 +61,7 @@ const RoomMembersItem = ({
{showButton ? (
<UserActions username={username} name={name} rid={rid} _id={_id} freeSwitchExtension={freeSwitchExtension} reload={reload} />
) : (
<IconButton tiny icon='kebab' />
<IconButton tiny icon='kebab' aria-hidden tabIndex={-1} />
)}
</OptionMenu>
</Option>

View File

@ -34,7 +34,6 @@ const RoomMembersWithData = ({ rid }: { rid: IRoom['_id'] }): ReactElement => {
const isDirect = room && isDirectMessageRoom(room);
const hasPermissionToCreateInviteLinks = usePermission('create-invite-links', rid);
const isFederated = room && isRoomFederated(room);
// we are dropping the non native federation for now
const isFederationBlocked = room && !isRoomNativeFederated(room);
@ -118,6 +117,8 @@ const RoomMembersWithData = ({ rid }: { rid: IRoom['_id'] }): ReactElement => {
reload={refetch}
onClickInvite={canCreateInviteLinks && canAddUsers ? openInvite : undefined}
onClickAdd={canAddUsers ? openAddUser : undefined}
// @ts-expect-error to be implemented in ABAC Feature branch
isABACRoom={Boolean(room?.abacAttributes)}
/>
);
};

View File

@ -0,0 +1,480 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`renders Default without crashing 1`] = `
<body>
<div>
<div
class="rcx-box rcx-box--full rcx-vertical-bar rcx-css-ucmcg1"
>
<span
data-focus-scope-start="true"
hidden=""
/>
<div
aria-labelledby="contextualbarTitle"
class="rcx-box rcx-box--full rcx-vertical-bar rcx-css-1n0hsd8"
role="dialog"
tabindex="-1"
>
<div
class="rcx-box rcx-box--full rcx-css-ftwpdg"
>
<div
class="rcx-box rcx-box--full rcx-css-1sl6k6j"
>
<i
aria-hidden="true"
class="rcx-box rcx-box--full rcx-icon--name-members rcx-icon rcx-css-x7bl3q rcx-css-g86psg"
>
</i>
<div
class="rcx-box rcx-box--full rcx-css-x7bl3q rcx-css-1to6ka7"
id="contextualbarTitle"
>
Members
</div>
</div>
</div>
<div
class="rcx-box rcx-box--full rcx-vertical-bar__section rcx-css-s9fquj"
>
<label
class="rcx-box rcx-box--full rcx-label rcx-box rcx-box--full rcx-box--animated rcx-input-box__wrapper"
>
<input
class="rcx-box rcx-box--full rcx-box--animated rcx-input-box--undecorated rcx-input-box--type-text rcx-input-box"
placeholder="Search_by_username"
size="1"
type="text"
value="filter"
/>
<span
class="rcx-box rcx-box--full rcx-input-box__addon"
>
<i
aria-hidden="true"
class="rcx-box rcx-box--full rcx-icon--name-magnifier rcx-icon rcx-css-4pvxx3"
>
</i>
</span>
</label>
<div
class="rcx-box rcx-box--full rcx-css-l1qvi5"
>
<button
aria-expanded="false"
aria-haspopup="listbox"
aria-labelledby="react-aria-:r8:"
class="rcx-box rcx-box--full rcx-select rcx-css-1vw6rc6"
type="button"
>
<div
aria-hidden="true"
data-a11y-ignore="aria-hidden-focus"
data-react-aria-prevent-focus="true"
data-testid="hidden-select-container"
style="border: 0px; clip-path: inset(50%); height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: absolute; width: 1px; white-space: nowrap;"
>
<label>
<select
tabindex="-1"
>
<option />
<option
value="online"
>
online
</option>
<option
value="all"
>
all
</option>
</select>
</label>
</div>
<span
class="rcx-box rcx-box--full rcx-css-4w7o7u"
id="react-aria-:r8:"
>
Online
</span>
<i
aria-hidden="true"
class="rcx-box rcx-box--full rcx-icon--name-chevron-down rcx-icon rcx-css-6vi44e"
>
</i>
</button>
</div>
</div>
<div
class="rcx-box rcx-box--full rcx-vertical-bar__content rcx-css-1w66n2o"
>
<div
class="rcx-box rcx-box--full rcx-css-nyqak3"
>
<span
class="rcx-box rcx-box--full rcx-css-1c00ay2"
>
Showing_current_of_total
</span>
</div>
<div
class="rcx-box rcx-box--full rcx-css-4k230l"
>
<div
class="rcx-box rcx-box--full rcx-css-pln26h rcx-css-1cb6i7s"
>
<div
data-testid="virtuoso-scroller"
data-virtuoso-scroller="true"
style="height: 100%; outline: none; overflow-y: auto; position: relative; width: 100%;"
tabindex="-1"
>
<div
style="width: 100%; position: -webkit-sticky; top: 0px; z-index: 1; margin-top: 0px;"
>
<div
data-testid="virtuoso-top-item-list"
/>
</div>
<div
data-viewport-type="element"
style="width: 100%; height: 100%; position: absolute; top: 0px;"
>
<div
data-testid="virtuoso-item-list"
style="box-sizing: border-box; margin-top: 0px; padding-top: 0px; padding-bottom: 0px;"
/>
<div>
<div
class="rcx-box rcx-box--full rcx-css-jsq7k5"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<span
data-focus-scope-end="true"
hidden=""
/>
</div>
</div>
</body>
`;
exports[`renders Loading without crashing 1`] = `
<body>
<div>
<div
class="rcx-box rcx-box--full rcx-vertical-bar rcx-css-ucmcg1"
>
<span
data-focus-scope-start="true"
hidden=""
/>
<div
aria-labelledby="contextualbarTitle"
class="rcx-box rcx-box--full rcx-vertical-bar rcx-css-1n0hsd8"
role="dialog"
tabindex="-1"
>
<div
class="rcx-box rcx-box--full rcx-css-ftwpdg"
>
<div
class="rcx-box rcx-box--full rcx-css-1sl6k6j"
>
<i
aria-hidden="true"
class="rcx-box rcx-box--full rcx-icon--name-members rcx-icon rcx-css-x7bl3q rcx-css-g86psg"
>
</i>
<div
class="rcx-box rcx-box--full rcx-css-x7bl3q rcx-css-1to6ka7"
id="contextualbarTitle"
>
Members
</div>
</div>
</div>
<div
class="rcx-box rcx-box--full rcx-vertical-bar__section rcx-css-s9fquj"
>
<label
class="rcx-box rcx-box--full rcx-label rcx-box rcx-box--full rcx-box--animated rcx-input-box__wrapper"
>
<input
class="rcx-box rcx-box--full rcx-box--animated rcx-input-box--undecorated rcx-input-box--type-text rcx-input-box"
placeholder="Search_by_username"
size="1"
type="text"
value=""
/>
<span
class="rcx-box rcx-box--full rcx-input-box__addon"
>
<i
aria-hidden="true"
class="rcx-box rcx-box--full rcx-icon--name-magnifier rcx-icon rcx-css-4pvxx3"
>
</i>
</span>
</label>
<div
class="rcx-box rcx-box--full rcx-css-l1qvi5"
>
<button
aria-expanded="false"
aria-haspopup="listbox"
aria-labelledby="react-aria-:rh:"
class="rcx-box rcx-box--full rcx-select rcx-css-1vw6rc6"
type="button"
>
<div
aria-hidden="true"
data-a11y-ignore="aria-hidden-focus"
data-react-aria-prevent-focus="true"
data-testid="hidden-select-container"
style="border: 0px; clip-path: inset(50%); height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: absolute; width: 1px; white-space: nowrap;"
>
<label>
<select
tabindex="-1"
>
<option />
<option
value="online"
>
online
</option>
<option
value="all"
>
all
</option>
</select>
</label>
</div>
<span
class="rcx-box rcx-box--full rcx-css-4w7o7u"
id="react-aria-:rh:"
>
Online
</span>
<i
aria-hidden="true"
class="rcx-box rcx-box--full rcx-icon--name-chevron-down rcx-icon rcx-css-6vi44e"
>
</i>
</button>
</div>
</div>
<div
class="rcx-box rcx-box--full rcx-vertical-bar__content rcx-css-1w66n2o"
>
<div
class="rcx-box rcx-box--full rcx-css-nyqak3"
>
<div
class="rcx-box rcx-box--full rcx-throbber"
>
<span
class="rcx-box rcx-box--full rcx-throbber__circle rcx-css-1xtde58 rcx-css-10wc9to"
/>
<span
class="rcx-box rcx-box--full rcx-throbber__circle rcx-css-1mkyloh rcx-css-10wc9to"
/>
<span
class="rcx-box rcx-box--full rcx-throbber__circle rcx-css-1r9rmv6 rcx-css-10wc9to"
/>
</div>
</div>
</div>
</div>
<span
data-focus-scope-end="true"
hidden=""
/>
</div>
</div>
</body>
`;
exports[`renders WithABACRoom without crashing 1`] = `
<body>
<div>
<div
class="rcx-box rcx-box--full rcx-vertical-bar rcx-css-ucmcg1"
>
<span
data-focus-scope-start="true"
hidden=""
/>
<div
aria-labelledby="contextualbarTitle"
class="rcx-box rcx-box--full rcx-vertical-bar rcx-css-1n0hsd8"
role="dialog"
tabindex="-1"
>
<div
class="rcx-box rcx-box--full rcx-css-ftwpdg"
>
<div
class="rcx-box rcx-box--full rcx-css-1sl6k6j"
>
<i
aria-hidden="true"
class="rcx-box rcx-box--full rcx-icon--name-members rcx-icon rcx-css-x7bl3q rcx-css-g86psg"
>
</i>
<div
class="rcx-box rcx-box--full rcx-css-x7bl3q rcx-css-1to6ka7"
id="contextualbarTitle"
>
Members
</div>
</div>
</div>
<div
class="rcx-box rcx-box--full rcx-vertical-bar__section rcx-css-s9fquj"
>
<label
class="rcx-box rcx-box--full rcx-label rcx-box rcx-box--full rcx-box--animated rcx-input-box__wrapper"
>
<input
class="rcx-box rcx-box--full rcx-box--animated rcx-input-box--undecorated rcx-input-box--type-text rcx-input-box"
placeholder="Search_by_username"
size="1"
type="text"
value="filter"
/>
<span
class="rcx-box rcx-box--full rcx-input-box__addon"
>
<i
aria-hidden="true"
class="rcx-box rcx-box--full rcx-icon--name-magnifier rcx-icon rcx-css-4pvxx3"
>
</i>
</span>
</label>
<div
class="rcx-box rcx-box--full rcx-css-l1qvi5"
>
<button
aria-expanded="false"
aria-haspopup="listbox"
aria-labelledby="react-aria-:rq:"
class="rcx-box rcx-box--full rcx-select rcx-css-1vw6rc6"
type="button"
>
<div
aria-hidden="true"
data-a11y-ignore="aria-hidden-focus"
data-react-aria-prevent-focus="true"
data-testid="hidden-select-container"
style="border: 0px; clip-path: inset(50%); height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: absolute; width: 1px; white-space: nowrap;"
>
<label>
<select
tabindex="-1"
>
<option />
<option
value="online"
>
online
</option>
<option
value="all"
>
all
</option>
</select>
</label>
</div>
<span
class="rcx-box rcx-box--full rcx-css-4w7o7u"
id="react-aria-:rq:"
>
Online
</span>
<i
aria-hidden="true"
class="rcx-box rcx-box--full rcx-icon--name-chevron-down rcx-icon rcx-css-6vi44e"
>
</i>
</button>
</div>
</div>
<div
class="rcx-box rcx-box--full rcx-vertical-bar__content rcx-css-1w66n2o"
>
<div
class="rcx-box rcx-box--full rcx-css-nyqak3"
>
<span
class="rcx-box rcx-box--full rcx-css-1c00ay2"
>
Showing_current_of_total
</span>
</div>
<div
class="rcx-box rcx-box--full rcx-css-4k230l"
>
<div
class="rcx-box rcx-box--full rcx-css-pln26h rcx-css-1cb6i7s"
>
<div
data-testid="virtuoso-scroller"
data-virtuoso-scroller="true"
style="height: 100%; outline: none; overflow-y: auto; position: relative; width: 100%;"
tabindex="-1"
>
<div
style="width: 100%; position: -webkit-sticky; top: 0px; z-index: 1; margin-top: 0px;"
>
<div
data-testid="virtuoso-top-item-list"
/>
</div>
<div
data-viewport-type="element"
style="width: 100%; height: 100%; position: absolute; top: 0px;"
>
<div
data-testid="virtuoso-item-list"
style="box-sizing: border-box; margin-top: 0px; padding-top: 0px; padding-bottom: 0px;"
/>
<div>
<div
class="rcx-box rcx-box--full rcx-css-jsq7k5"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<span
data-focus-scope-end="true"
hidden=""
/>
</div>
</div>
</body>
`;