Fix the interactivity of buttons while reconnecting or in earpiece mode (#3486)
* Fix the interactivity of buttons while reconnecting or in earpiece mode When we're in one of these modes, we need to ensure that everything above the overlay (the header and footer buttons) is interactive, while everything obscured by the overlay (the media tiles) is non-interactive and removed from the accessibility tree. It's not a very easy task to trap focus *outside* an element, so the best solution I could come up with is to set tabindex="-1" manually on all interactive elements belonging to the media tiles. * Write a Playwright test for reconnecting * fix lints Signed-off-by: Timo K <toger5@hotmail.de> * fix test Signed-off-by: Timo K <toger5@hotmail.de> * enable http2 for matrx-rtc host to allow the jwt service to talk to the SFU * remove rate limit for delayed events * more time to connect to livekit SFU * Due to a Firefox issue we set the start anchor for the tab test to the Mute microphone button * adapt to most recent Element Web version * Use the "End call" button as proofe for a started call * Currrenty disabled due to recent Element Web - not indicating the number of participants - bypassing Lobby * linting * disable 'can only interact with header and footer while reconnecting' for firefox --------- Signed-off-by: Timo K <toger5@hotmail.de> Co-authored-by: Timo <16718859+toger5@users.noreply.github.com> Co-authored-by: Timo K <toger5@hotmail.de> Co-authored-by: fkwp <github-fkwp@w4ve.de>
This commit is contained in:
@@ -64,6 +64,7 @@ test("GridTile is accessible", async () => {
|
||||
targetWidth={300}
|
||||
targetHeight={200}
|
||||
showSpeakingIndicators
|
||||
focusable={true}
|
||||
/>
|
||||
</ReactionsSenderProvider>,
|
||||
);
|
||||
|
||||
@@ -60,6 +60,7 @@ interface TileProps {
|
||||
targetHeight: number;
|
||||
displayName: string;
|
||||
showSpeakingIndicators: boolean;
|
||||
focusable: boolean;
|
||||
}
|
||||
|
||||
interface UserMediaTileProps extends TileProps {
|
||||
@@ -81,6 +82,7 @@ const UserMediaTile: FC<UserMediaTileProps> = ({
|
||||
menuEnd,
|
||||
className,
|
||||
displayName,
|
||||
focusable,
|
||||
...props
|
||||
}) => {
|
||||
const { toggleRaisedHand } = useReactionsSender();
|
||||
@@ -162,6 +164,7 @@ const UserMediaTile: FC<UserMediaTileProps> = ({
|
||||
/>
|
||||
}
|
||||
displayName={displayName}
|
||||
focusable={focusable}
|
||||
primaryButton={
|
||||
primaryButton ?? (
|
||||
<Menu
|
||||
@@ -169,7 +172,10 @@ const UserMediaTile: FC<UserMediaTileProps> = ({
|
||||
onOpenChange={setMenuOpen}
|
||||
title={displayName}
|
||||
trigger={
|
||||
<button aria-label={t("common.options")}>
|
||||
<button
|
||||
aria-label={t("common.options")}
|
||||
tabIndex={focusable ? undefined : -1}
|
||||
>
|
||||
<OverflowHorizontalIcon aria-hidden width={20} height={20} />
|
||||
</button>
|
||||
}
|
||||
@@ -208,6 +214,7 @@ const LocalUserMediaTile: FC<LocalUserMediaTileProps> = ({
|
||||
ref,
|
||||
vm,
|
||||
onOpenProfile,
|
||||
focusable,
|
||||
...props
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
@@ -236,6 +243,7 @@ const LocalUserMediaTile: FC<LocalUserMediaTileProps> = ({
|
||||
className={styles.switchCamera}
|
||||
aria-label={t("switch_camera")}
|
||||
onClick={switchCamera}
|
||||
tabIndex={focusable ? undefined : -1}
|
||||
>
|
||||
<SwitchCameraSolidIcon aria-hidden width={20} height={20} />
|
||||
</button>
|
||||
@@ -258,6 +266,7 @@ const LocalUserMediaTile: FC<LocalUserMediaTileProps> = ({
|
||||
/>
|
||||
)
|
||||
}
|
||||
focusable={focusable}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -337,6 +346,7 @@ interface GridTileProps {
|
||||
className?: string;
|
||||
style?: ComponentProps<typeof animated.div>["style"];
|
||||
showSpeakingIndicators: boolean;
|
||||
focusable: boolean;
|
||||
}
|
||||
|
||||
export const GridTile: FC<GridTileProps> = ({
|
||||
|
||||
@@ -47,6 +47,7 @@ describe("MediaView", () => {
|
||||
video: trackReference,
|
||||
member: undefined,
|
||||
localParticipant: false,
|
||||
focusable: true,
|
||||
};
|
||||
|
||||
test("is accessible", async () => {
|
||||
|
||||
@@ -38,6 +38,7 @@ interface Props extends ComponentProps<typeof animated.div> {
|
||||
encryptionStatus: EncryptionStatus;
|
||||
nameTagLeadingIcon?: ReactNode;
|
||||
displayName: string;
|
||||
focusable: boolean;
|
||||
primaryButton?: ReactNode;
|
||||
raisedHandTime?: Date;
|
||||
currentReaction?: ReactionOption;
|
||||
@@ -61,6 +62,7 @@ export const MediaView: FC<Props> = ({
|
||||
unencryptedWarning,
|
||||
nameTagLeadingIcon,
|
||||
displayName,
|
||||
focusable,
|
||||
primaryButton,
|
||||
encryptionStatus,
|
||||
raisedHandTime,
|
||||
@@ -114,6 +116,7 @@ export const MediaView: FC<Props> = ({
|
||||
miniature={avatarSize < 96}
|
||||
showTimer={handRaiseTimerVisible}
|
||||
onClick={raisedHandOnClick}
|
||||
tabIndex={focusable ? undefined : -1}
|
||||
/>
|
||||
{currentReaction && (
|
||||
<ReactionIndicator
|
||||
@@ -164,6 +167,7 @@ export const MediaView: FC<Props> = ({
|
||||
label={t("common.unencrypted")}
|
||||
placement="bottom"
|
||||
isTriggerInteractive={false}
|
||||
nonInteractiveTriggerTabIndex={focusable ? undefined : -1}
|
||||
>
|
||||
<ErrorSolidIcon
|
||||
width={20}
|
||||
|
||||
@@ -59,6 +59,7 @@ test("SpotlightTile is accessible", async () => {
|
||||
expanded={false}
|
||||
onToggleExpanded={toggleExpanded}
|
||||
showIndicators
|
||||
focusable={true}
|
||||
/>,
|
||||
);
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ interface SpotlightItemBaseProps {
|
||||
unencryptedWarning: boolean;
|
||||
encryptionStatus: EncryptionStatus;
|
||||
displayName: string;
|
||||
focusable: boolean;
|
||||
"aria-hidden"?: boolean;
|
||||
localParticipant: boolean;
|
||||
}
|
||||
@@ -112,6 +113,7 @@ interface SpotlightItemProps {
|
||||
vm: MediaViewModel;
|
||||
targetWidth: number;
|
||||
targetHeight: number;
|
||||
focusable: boolean;
|
||||
intersectionObserver$: Observable<IntersectionObserver>;
|
||||
/**
|
||||
* Whether this item should act as a scroll snapping point.
|
||||
@@ -125,6 +127,7 @@ const SpotlightItem: FC<SpotlightItemProps> = ({
|
||||
vm,
|
||||
targetWidth,
|
||||
targetHeight,
|
||||
focusable,
|
||||
intersectionObserver$,
|
||||
snap,
|
||||
"aria-hidden": ariaHidden,
|
||||
@@ -163,6 +166,7 @@ const SpotlightItem: FC<SpotlightItemProps> = ({
|
||||
member: vm.member,
|
||||
unencryptedWarning,
|
||||
displayName,
|
||||
focusable,
|
||||
encryptionStatus,
|
||||
"aria-hidden": ariaHidden,
|
||||
localParticipant: vm.local,
|
||||
@@ -185,6 +189,7 @@ interface Props {
|
||||
targetWidth: number;
|
||||
targetHeight: number;
|
||||
showIndicators: boolean;
|
||||
focusable: boolean;
|
||||
className?: string;
|
||||
style?: ComponentProps<typeof animated.div>["style"];
|
||||
}
|
||||
@@ -197,6 +202,7 @@ export const SpotlightTile: FC<Props> = ({
|
||||
targetWidth,
|
||||
targetHeight,
|
||||
showIndicators,
|
||||
focusable = true,
|
||||
className,
|
||||
style,
|
||||
}) => {
|
||||
@@ -293,6 +299,7 @@ export const SpotlightTile: FC<Props> = ({
|
||||
className={classNames(styles.advance, styles.back)}
|
||||
aria-label={t("common.back")}
|
||||
onClick={onBackClick}
|
||||
tabIndex={focusable ? undefined : -1}
|
||||
>
|
||||
<ChevronLeftIcon aria-hidden width={24} height={24} />
|
||||
</button>
|
||||
@@ -304,6 +311,7 @@ export const SpotlightTile: FC<Props> = ({
|
||||
vm={vm}
|
||||
targetWidth={targetWidth}
|
||||
targetHeight={targetHeight}
|
||||
focusable={focusable}
|
||||
intersectionObserver$={intersectionObserver$}
|
||||
// This is how we get the container to scroll to the right media
|
||||
// when the previous/next buttons are clicked: we temporarily
|
||||
@@ -319,6 +327,7 @@ export const SpotlightTile: FC<Props> = ({
|
||||
className={classNames(styles.expand)}
|
||||
aria-label={"maximise"}
|
||||
onClick={onToggleFullscreen}
|
||||
tabIndex={focusable ? undefined : -1}
|
||||
>
|
||||
<FullScreenIcon aria-hidden width={20} height={20} />
|
||||
</button>
|
||||
@@ -330,6 +339,7 @@ export const SpotlightTile: FC<Props> = ({
|
||||
expanded ? t("video_tile.collapse") : t("video_tile.expand")
|
||||
}
|
||||
onClick={onToggleExpanded}
|
||||
tabIndex={focusable ? undefined : -1}
|
||||
>
|
||||
<ToggleExpandIcon aria-hidden width={20} height={20} />
|
||||
</button>
|
||||
@@ -341,6 +351,7 @@ export const SpotlightTile: FC<Props> = ({
|
||||
className={classNames(styles.advance, styles.next)}
|
||||
aria-label={t("common.next")}
|
||||
onClick={onNextClick}
|
||||
tabIndex={focusable ? undefined : -1}
|
||||
>
|
||||
<ChevronRightIcon aria-hidden width={24} height={24} />
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user