Skip to content

Commit 2db6ad6

Browse files
committed
accessibility fix
1 parent 478c8ae commit 2db6ad6

File tree

1 file changed

+44
-40
lines changed

1 file changed

+44
-40
lines changed

packages/react-components/src/components/TogetherModeOverlay.tsx

Lines changed: 44 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Licensed under the MIT License.
33

44
/* @conditional-compile-remove(together-mode) */
5-
import React, { useMemo, useState, memo } from 'react';
5+
import React, { useMemo, useState, memo, useEffect } from 'react';
66
/* @conditional-compile-remove(together-mode) */
77
import {
88
Reaction,
@@ -84,13 +84,22 @@ export const TogetherModeOverlay = memo(
8484
[key: string]: TogetherModeParticipantStatus;
8585
}>({});
8686
const [hoveredParticipantID, setHoveredParticipantID] = useState('');
87+
const [tabbedParticipantID, setTabbedParticipantID] = useState('');
88+
// const [tabFocused, setTabFocused] = useState(false);
89+
90+
// Reset the Tab key tracking on any other key press
91+
const handleKeyUp = (e, participantId: string) => {
92+
if (e.key === 'Tab') {
93+
setTabbedParticipantID(participantId);
94+
}
95+
};
8796

8897
/*
8998
* The useMemo hook is used to calculate the participant status for the Together Mode overlay.
9099
* It updates the togetherModeParticipantStatus state when there's a change in the remoteParticipants, localParticipant,
91100
* raisedHand, spotlight, isMuted, displayName, or hoveredParticipantID.
92101
*/
93-
useMemo(() => {
102+
const updatedParticipantStatus = useMemo(() => {
94103
const allParticipants = [...remoteParticipants, localParticipant];
95104

96105
const participantsWithVideoAvailable = allParticipants.filter(
@@ -109,7 +118,12 @@ export const TogetherModeOverlay = memo(
109118
isSpotlighted: !!spotlight,
110119
isMuted,
111120
displayName: displayName || locale.strings.videoGallery.displayNamePlaceholder,
112-
showDisplayName: !!(spotlight || raisedHand || hoveredParticipantID === userId),
121+
showDisplayName: !!(
122+
spotlight ||
123+
raisedHand ||
124+
hoveredParticipantID === userId ||
125+
tabbedParticipantID === userId
126+
),
113127
scaledSize: calculateScaledSize(seatingPosition.width, seatingPosition.height),
114128
seatPositionStyle: setTogetherModeSeatPositionStyle(seatingPosition)
115129
};
@@ -121,68 +135,58 @@ export const TogetherModeOverlay = memo(
121135
(id) => !updatedSignals[id]
122136
);
123137

124-
setTogetherModeParticipantStatus((prevSignals) => {
125-
const newSignals = { ...prevSignals, ...updatedSignals };
138+
const newSignals = { ...togetherModeParticipantStatus, ...updatedSignals };
126139

127-
participantsNotInTogetherModeStream.forEach((id) => {
128-
delete newSignals[id];
129-
});
140+
participantsNotInTogetherModeStream.forEach((id) => {
141+
delete newSignals[id];
142+
});
130143

131-
const hasSignalingChange = Object.keys(newSignals).some(
132-
(key) => JSON.stringify(newSignals[key]) !== JSON.stringify(prevSignals[key])
133-
);
144+
const hasSignalingChange = Object.keys(newSignals).some(
145+
(key) => JSON.stringify(newSignals[key]) !== JSON.stringify(togetherModeParticipantStatus[key])
146+
);
134147

135-
const updateTogetherModeParticipantStatusState =
136-
hasSignalingChange || Object.keys(newSignals).length !== Object.keys(prevSignals).length;
137-
return updateTogetherModeParticipantStatusState ? newSignals : prevSignals;
138-
});
148+
const updateTogetherModeParticipantStatusState =
149+
hasSignalingChange || Object.keys(newSignals).length !== Object.keys(togetherModeParticipantStatus).length;
150+
return updateTogetherModeParticipantStatusState ? newSignals : togetherModeParticipantStatus;
139151
}, [
140152
remoteParticipants,
141153
localParticipant,
142154
togetherModeParticipantStatus,
143155
togetherModeSeatPositions,
144156
reactionResources,
145157
locale.strings.videoGallery.displayNamePlaceholder,
146-
hoveredParticipantID
158+
hoveredParticipantID,
159+
tabbedParticipantID
147160
]);
148161

149-
/*
150-
* When a larger participant scene switches to a smaller group in Together Mode,
151-
* participant video streams remain available because their video is still active,
152-
* even though they are not visible in the Together Mode stream.
153-
* Therefore, we rely on the updated seating position values to identify who is included in the Together Mode stream.
154-
* The Together mode seat position will only contain seat coordinates of participants who are visible in the Together Mode stream.
155-
*/
156-
useMemo(() => {
157-
const removedVisibleParticipants = Object.keys(togetherModeParticipantStatus).filter(
158-
(participantId) => !togetherModeSeatPositions[participantId]
159-
);
162+
useEffect(() => {
163+
if (hoveredParticipantID && !updatedParticipantStatus[hoveredParticipantID]) {
164+
setHoveredParticipantID('');
165+
}
160166

161-
setTogetherModeParticipantStatus((prevSignals) => {
162-
const newSignals = { ...prevSignals };
163-
removedVisibleParticipants.forEach((participantId) => {
164-
delete newSignals[participantId];
165-
});
167+
if (tabbedParticipantID && !updatedParticipantStatus[tabbedParticipantID]) {
168+
setTabbedParticipantID('');
169+
}
166170

167-
// Trigger a re-render only if changes occurred
168-
const updateTogetherModeParticipantStatusState =
169-
Object.keys(newSignals).length !== Object.keys(prevSignals).length;
170-
return updateTogetherModeParticipantStatusState ? newSignals : prevSignals;
171-
});
172-
}, [togetherModeParticipantStatus, togetherModeSeatPositions]);
171+
setTogetherModeParticipantStatus(updatedParticipantStatus);
172+
}, [hoveredParticipantID, tabbedParticipantID, updatedParticipantStatus]);
173173

174174
return (
175175
<div style={{ position: 'absolute', width: '100%', height: '100%' }}>
176176
{Object.values(togetherModeParticipantStatus).map(
177-
(participantStatus) =>
177+
(participantStatus, index) =>
178178
participantStatus.id && (
179179
<div
180180
key={participantStatus.id}
181181
style={{
182182
...getTogetherModeParticipantOverlayStyle(participantStatus.seatPositionStyle)
183+
// border: '1px solid yellow'
183184
}}
184185
onMouseEnter={() => setHoveredParticipantID(participantStatus.id)}
185186
onMouseLeave={() => setHoveredParticipantID('')}
187+
onKeyUp={(e) => handleKeyUp(e, participantStatus.id)}
188+
onBlur={() => setTabbedParticipantID('')}
189+
tabIndex={index}
186190
>
187191
<div>
188192
{participantStatus.reaction?.reactionType && (
@@ -219,7 +223,7 @@ export const TogetherModeOverlay = memo(
219223
)}
220224

221225
{participantStatus.showDisplayName && (
222-
<div style={{ ...participantStatusTransitionStyle }}>
226+
<div style={{ ...participantStatusTransitionStyle }} tabIndex={index}>
223227
<div
224228
style={{
225229
...togetherModeParticipantStatusContainer(

0 commit comments

Comments
 (0)