Skip to content

Commit 8fd0fe2

Browse files
compulimcorinagum
andcommitted
Fix usability on accessibility (#3076)
* Experiment * Clean up * Clean up * Add entry * Fix ESLint * Sorting Co-authored-by: Corina <[email protected]>
1 parent f152bcb commit 8fd0fe2

File tree

5 files changed

+29
-9
lines changed

5 files changed

+29
-9
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
2222

2323
## [Unreleased]
2424

25+
### Fixed
26+
27+
- Fixes [#3075](https://github.com/microsoft/BotFramework-WebChat/issues/3075). Fix usability issues around accessibility, by [@compulim](https://github.com/compulim) in PR [#3076](https://github.com/microsoft/BotFramework-WebChat/issue/3076)
28+
- Fix timestamp should not be narrated more than once.
29+
- Associate the activity text with its attachments, by adding a `role="region"` to the activity DOM element.
30+
2531
## [4.8.0] - 2020-03-05
2632

2733
### Breaking changes

packages/component/src/Activity/CarouselFilmStrip.js

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import ScreenReaderText from '../ScreenReaderText';
1313
import textFormatToContentType from '../Utils/textFormatToContentType';
1414
import useAvatarForBot from '../hooks/useAvatarForBot';
1515
import useAvatarForUser from '../hooks/useAvatarForUser';
16+
import useDateFormatter from '../hooks/useDateFormatter';
1617
import useDirection from '../hooks/useDirection';
1718
import useLocalizer from '../hooks/useLocalizer';
1819
import useRenderActivityStatus from '../hooks/useRenderActivityStatus';
@@ -96,6 +97,7 @@ const WebChatCarouselFilmStrip = ({
9697
const [{ initials: botInitials }] = useAvatarForBot();
9798
const [{ initials: userInitials }] = useAvatarForUser();
9899
const [direction] = useDirection();
100+
const formatDate = useDateFormatter();
99101
const localize = useLocalizer();
100102
const renderActivityStatus = useRenderActivityStatus({ activity, nextVisibleActivity });
101103
const renderAvatar = useRenderAvatar({ activity });
@@ -105,18 +107,28 @@ const WebChatCarouselFilmStrip = ({
105107
channelData: { messageBack: { displayText: messageBackDisplayText } = {} } = {},
106108
from: { role } = {},
107109
text,
108-
textFormat
110+
textFormat,
111+
timestamp
109112
} = activity;
110113

111-
const fromUser = role === 'user';
112114
const activityDisplayText = messageBackDisplayText || text;
113-
const strippedActivityDisplayText = remarkStripMarkdown(activityDisplayText);
115+
const fromUser = role === 'user';
116+
114117
const indented = fromUser ? bubbleFromUserNubSize : bubbleNubSize;
115118
const initials = fromUser ? userInitials : botInitials;
119+
const plainText = remarkStripMarkdown(activityDisplayText);
116120
const roleLabel = localize(fromUser ? 'CAROUSEL_ATTACHMENTS_USER_ALT' : 'CAROUSEL_ATTACHMENTS_BOT_ALT');
117121

122+
const ariaLabel = localize(
123+
fromUser ? 'ACTIVITY_USER_SAID' : 'ACTIVITY_BOT_SAID',
124+
initials,
125+
plainText,
126+
formatDate(timestamp)
127+
).trim();
128+
118129
return (
119130
<div
131+
aria-label={ariaLabel}
120132
className={classNames(
121133
ROOT_CSS + '',
122134
carouselFilmStripStyleSet + '',
@@ -128,12 +140,12 @@ const WebChatCarouselFilmStrip = ({
128140
direction === 'rtl' ? 'webchat__carousel--rtl' : ''
129141
)}
130142
ref={scrollableRef}
143+
role="region"
131144
>
132145
{renderAvatar && <div className="webchat__carouselFilmStrip__avatar">{renderAvatar()}</div>}
133146
<div className="content">
134147
{!!activityDisplayText && (
135148
<div className="message">
136-
<ScreenReaderText text={roleLabel + ' ' + strippedActivityDisplayText} />
137149
<Bubble aria-hidden={true} className="bubble" fromUser={fromUser} nub={true}>
138150
{children({
139151
activity,

packages/component/src/Activity/StackedLayout.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,12 @@ const StackedLayout = ({ activity, children, nextVisibleActivity }) => {
103103

104104
const activityDisplayText = messageBackDisplayText || text;
105105
const fromUser = role === 'user';
106+
107+
const indented = fromUser ? bubbleFromUserNubSize : bubbleNubSize;
106108
const initials = fromUser ? userInitials : botInitials;
107109
const plainText = remarkStripMarkdown(text);
108-
const indented = fromUser ? bubbleFromUserNubSize : bubbleNubSize;
109-
110110
const roleLabel = localize(fromUser ? 'CAROUSEL_ATTACHMENTS_USER_ALT' : 'CAROUSEL_ATTACHMENTS_BOT_ALT');
111+
111112
const ariaLabel = localize(
112113
fromUser ? 'ACTIVITY_USER_SAID' : 'ACTIVITY_BOT_SAID',
113114
initials,
@@ -117,6 +118,7 @@ const StackedLayout = ({ activity, children, nextVisibleActivity }) => {
117118

118119
return (
119120
<div
121+
aria-label={ariaLabel}
120122
className={classNames(
121123
ROOT_CSS + '',
122124
stackedLayoutStyleSet + '',
@@ -133,12 +135,12 @@ const StackedLayout = ({ activity, children, nextVisibleActivity }) => {
133135
'webchat__stackedLayout--hasAvatar': renderAvatar && !!(fromUser ? bubbleFromUserNubSize : bubbleNubSize)
134136
}
135137
)}
138+
role="region"
136139
>
137140
{renderAvatar && <div className="webchat__stackedLayout__avatar">{renderAvatar()}</div>}
138141
<div className="webchat__stackedLayout__content">
139142
{!!activityDisplayText && (
140143
<div className="webchat__row message">
141-
<ScreenReaderText text={ariaLabel} />
142144
<Bubble aria-hidden={true} className="bubble" fromUser={fromUser} nub={!!indented}>
143145
{children({
144146
activity,

packages/component/src/BasicTranscript.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ const BasicTranscript = ({ className }) => {
117117
<ul
118118
aria-atomic="false"
119119
aria-live="polite"
120-
aria-relevant="additions text"
120+
aria-relevant="additions"
121121
className={classNames(LIST_CSS + '', activitiesStyleSet + '')}
122122
role="list"
123123
>

packages/component/src/Middleware/ActivityStatus/createTimestampMiddleware.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import Timestamp from './Timestamp';
55
export default function createTimestampMiddleware() {
66
return () => next => ({ activity, sameTimestampGroup, ...args }) => {
77
if (!sameTimestampGroup) {
8-
return <Timestamp activity={activity} />;
8+
return <Timestamp activity={activity} aria-hidden={true} />;
99
}
1010

1111
return next({ activity, sameTimestampGroup, ...args });

0 commit comments

Comments
 (0)