Skip to content

Commit 9a52f9c

Browse files
[Chat] Fix for "The messsage is deleted" accessibility announcement (#4169)
1 parent c048e27 commit 9a52f9c

6 files changed

Lines changed: 299 additions & 145 deletions
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"type": "patch",
3+
"area": "fix",
4+
"workstream": "Accessibility",
5+
"comment": "Fix for a deleted message accessibility announcement",
6+
"packageName": "@azure/communication-react",
7+
"email": "98852890+vhuseinova-msft@users.noreply.github.com",
8+
"dependentChangeType": "patch"
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"type": "patch",
3+
"area": "fix",
4+
"workstream": "Accessibility",
5+
"comment": "Fix for a deleted message accessibility announcement",
6+
"packageName": "@azure/communication-react",
7+
"email": "98852890+vhuseinova-msft@users.noreply.github.com",
8+
"dependentChangeType": "patch"
9+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
import { Icon } from '@fluentui/react';
5+
import React from 'react';
6+
import LiveMessage from './Announcer/LiveMessage';
7+
8+
/**
9+
* Props for {@link MessageStatusIndicatorInternal}.
10+
*
11+
* @internal
12+
*/
13+
export interface MessageStatusIconProps {
14+
shouldAnnounce: boolean;
15+
iconName: string;
16+
iconClassName: string;
17+
ariaLabel?: string;
18+
}
19+
20+
/**
21+
* Component to display message status icon
22+
*
23+
* @internal
24+
*/
25+
export const MessageStatusIcon = (props: MessageStatusIconProps): JSX.Element => {
26+
const { shouldAnnounce, iconName, iconClassName, ariaLabel } = props;
27+
28+
return (
29+
<>
30+
{/* live message is used here so that aria labels are announced on mobile */}
31+
{ariaLabel && <LiveMessage message={ariaLabel} ariaLive="polite" />}
32+
<div
33+
// make icon accessible
34+
tabIndex={0}
35+
>
36+
<Icon
37+
role={'status'}
38+
// Role `status` is one of the live region roles and is used to notify screen readers of changes to the status of the message
39+
// it has aria-live prop set to `polite` by default.
40+
// By setting it to `off` value, we disable live updates when they aren't needed
41+
// and keep it available in the accessibility tree
42+
aria-live={shouldAnnounce ? 'polite' : 'off'}
43+
data-ui-id={'chat-composite-message-status-icon'}
44+
aria-label={ariaLabel}
45+
iconName={iconName}
46+
className={iconClassName}
47+
/>
48+
</div>
49+
</>
50+
);
51+
};
Lines changed: 6 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,10 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4-
import { ICalloutContentStyles, Icon, ITooltipHostStyles, mergeStyles, TooltipHost } from '@fluentui/react';
54
import { MessageStatus, _formatString } from '@internal/acs-ui-common';
6-
import React, { useState } from 'react';
7-
import { useLocale } from '../localization';
8-
import { useTheme } from '../theming';
9-
import { isDarkThemed } from '../theming/themeUtils';
5+
import React from 'react';
106
import { BaseCustomStyles } from '../types';
11-
import {
12-
MessageStatusIndicatorErrorIconStyle,
13-
MessageStatusIndicatorIconStyle
14-
} from './styles/MessageStatusIndicator.styles';
15-
import LiveMessage from './Announcer/LiveMessage';
7+
import { MessageStatusIndicatorInternal, MessageStatusIndicatorInternalProps } from './MessageStatusIndicatorInternal';
168

179
/**
1810
* Strings of {@link MessageStatusIndicator} that can be overridden.
@@ -74,137 +66,10 @@ export interface MessageStatusIndicatorProps {
7466
* @public
7567
*/
7668
export const MessageStatusIndicator = (props: MessageStatusIndicatorProps): JSX.Element => {
77-
const { status, styles, remoteParticipantsCount, onToggleToolTip, readCount } = props;
78-
const localeStrings = useLocale().strings.messageStatusIndicator;
79-
const [isTooltipToggled, setIsTooltipToggled] = useState<boolean>(false);
80-
const strings = { ...localeStrings, ...props.strings };
81-
const theme = useTheme();
82-
83-
const calloutStyle: Partial<ICalloutContentStyles> = { root: { padding: 0 }, calloutMain: { padding: '0.5rem' } };
84-
85-
// Place callout with no gap between it and the button.
86-
const calloutProps = {
87-
gapSpace: 0,
88-
styles: calloutStyle,
89-
backgroundColor: isDarkThemed(theme) ? theme.palette.neutralLighter : ''
69+
const internalProps: MessageStatusIndicatorInternalProps = {
70+
...props,
71+
shouldAnnounce: true
9072
};
9173

92-
switch (status) {
93-
case 'failed':
94-
return (
95-
<TooltipHost
96-
content={strings.failedToSendTooltipText}
97-
data-ui-id="chat-composite-message-tooltip"
98-
calloutProps={{ ...calloutProps }}
99-
styles={hostStyles}
100-
>
101-
{strings.failedToSendAriaLabel && (
102-
// live message is used here and in the following tooltips so that aria labels are announced on mobile
103-
<LiveMessage message={strings.failedToSendAriaLabel} ariaLive="polite" />
104-
)}
105-
<Icon
106-
role="status"
107-
data-ui-id="chat-composite-message-status-icon"
108-
aria-label={strings.failedToSendAriaLabel}
109-
iconName="MessageFailed"
110-
className={mergeStyles(
111-
MessageStatusIndicatorErrorIconStyle,
112-
{ color: theme.palette.redDark },
113-
styles?.root
114-
)}
115-
/>
116-
</TooltipHost>
117-
);
118-
case 'sending':
119-
return (
120-
<TooltipHost
121-
content={strings.sendingTooltipText}
122-
data-ui-id="chat-composite-message-tooltip"
123-
calloutProps={{ ...calloutProps }}
124-
styles={hostStyles}
125-
>
126-
{strings.sendingAriaLabel && <LiveMessage message={strings.sendingAriaLabel} ariaLive="polite" />}
127-
128-
<Icon
129-
role="status"
130-
data-ui-id="chat-composite-message-status-icon"
131-
aria-label={strings.sendingAriaLabel}
132-
iconName="MessageSending"
133-
className={mergeStyles(
134-
MessageStatusIndicatorIconStyle,
135-
{ color: theme.palette.themePrimary },
136-
styles?.root
137-
)}
138-
/>
139-
</TooltipHost>
140-
);
141-
case 'seen':
142-
return (
143-
<TooltipHost
144-
calloutProps={{ ...calloutProps }}
145-
data-ui-id="chat-composite-message-tooltip"
146-
styles={hostStyles}
147-
content={
148-
// when it's just 1 to 1 texting, we don't need to know who has read the message, just show message as 'seen'
149-
// when readcount is 0, we have a bug, show 'seen' to cover up as a fall back
150-
// when participant count is 0, we have a bug, show 'seen' to cover up as a fall back
151-
readCount === 0 ||
152-
(remoteParticipantsCount && remoteParticipantsCount <= 1) ||
153-
!readCount ||
154-
!remoteParticipantsCount ||
155-
strings.readByTooltipText === undefined
156-
? strings.seenTooltipText
157-
: _formatString(strings.readByTooltipText, {
158-
messageThreadReadCount: `${readCount}`,
159-
remoteParticipantsCount: `${remoteParticipantsCount}`
160-
})
161-
}
162-
onTooltipToggle={() => {
163-
if (onToggleToolTip) {
164-
onToggleToolTip(!isTooltipToggled);
165-
setIsTooltipToggled(!isTooltipToggled);
166-
}
167-
}}
168-
>
169-
{strings.seenAriaLabel && <LiveMessage message={strings.seenAriaLabel} ariaLive="polite" />}
170-
171-
<Icon
172-
data-ui-id="chat-composite-message-status-icon"
173-
role="status"
174-
aria-label={strings.seenAriaLabel}
175-
iconName="MessageSeen"
176-
className={mergeStyles({ color: theme.palette.themePrimary }, styles?.root)}
177-
/>
178-
</TooltipHost>
179-
);
180-
case 'delivered':
181-
return (
182-
<TooltipHost
183-
calloutProps={{ ...calloutProps }}
184-
content={strings.deliveredTooltipText}
185-
data-ui-id="chat-composite-message-tooltip"
186-
styles={hostStyles}
187-
>
188-
{strings.deliveredAriaLabel && <LiveMessage message={strings.deliveredAriaLabel} ariaLive="polite" />}
189-
<Icon
190-
role="status"
191-
data-ui-id="chat-composite-message-status-icon"
192-
aria-label={strings.deliveredAriaLabel}
193-
iconName="MessageDelivered"
194-
className={mergeStyles(
195-
MessageStatusIndicatorIconStyle,
196-
{ color: theme.palette.themePrimary },
197-
styles?.root
198-
)}
199-
/>
200-
</TooltipHost>
201-
);
202-
default:
203-
return <></>;
204-
}
74+
return <MessageStatusIndicatorInternal {...internalProps}></MessageStatusIndicatorInternal>;
20575
};
206-
207-
// The TooltipHost root uses display: inline by default.
208-
// To prevent sizing issues or tooltip positioning issues, we override to inline-block.
209-
// For more details see "Icon Button with Tooltip" on https://developer.microsoft.com/en-us/fluentui#/controls/web/button
210-
const hostStyles: Partial<ITooltipHostStyles> = { root: { display: 'inline-block' } };

0 commit comments

Comments
 (0)