Skip to content

Commit

Permalink
Make Suggested Actions accessible again! (#2613)
Browse files Browse the repository at this point in the history
* packages update

* Attachment aria-label fixes

* Fix arialabel for connectivity status & ea. bubble

* #1780 make Suggested Actions accessible

* Code cleanup

* Upload file string correction

* Update CHANGELOG.md

* Linting fixes

* Update packages/component/src/Utils/AbsoluteTime.js

Co-Authored-By: William Wong <compulim@users.noreply.github.com>

* Apply suggestions from code review

Co-Authored-By: William Wong <compulim@users.noreply.github.com>

* Apply PR comments
  • Loading branch information
corinagum authored Nov 20, 2019
1 parent 39ab54e commit 62e0e88
Show file tree
Hide file tree
Showing 25 changed files with 228 additions and 103 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Resolves [#2337](https://github.com/microsoft/BotFramework-WebChat/issues/2337). Remove Cognitive Services Preview warning, by [@corinagum](https://github.com/corinagum) in PR [#2578](https://github.com/microsoft/BotFramework-WebChat/pull/2578)
- Fixes [#2559](https://github.com/microsoft/BotFramework-WebChat/issues/2559). De-bump remark and strip-markdown, by [@corinagum](https://github.com/corinagum) in PR [#2576](https://github.com/microsoft/BotFramework-WebChat/pull/2576)
- Fixes [#2512](https://github.com/microsoft/BotFramework-WebChat/issues/2512). Adds check to ensure Adaptive Card's content is an Object, by [@tdurnford](https://github.com/tdurnford) in PR [#2590](https://github.com/microsoft/BotFramework-WebChat/pull/2590)
- Fixes [#1780](https://github.com/microsoft/BotFramework-WebChat/issues/1780), [#2277](https://github.com/microsoft/BotFramework-WebChat/issues/2277), and [#2285](https://github.com/microsoft/BotFramework-WebChat/issues/2285). Make Suggested Actions accessible, Fix Markdown card in carousel being read multiple times, and label widgets of Connectivity Status and Suggested Actions containers, by [@corinagum](https://github.com/corinagum) in PR [#2613](https://github.com/microsoft/BotFramework-WebChat/pull/2613)

### Added

Expand Down
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ const AnimationCardAttachment = ({
<div className={animationCardAttachmentStyleSet}>
<ul className="media-list">
{media.map(({ profile = '', url }, index) => (
<li key={index}>
// Because of differences in browser implementations, aria-label=" " is used to make the screen reader not repeat the same text multiple times in Chrome v75 and Edge 44
<li aria-label=" " key={index}>
{/\.gif$/iu.test(url) ? <ImageContent alt={profile} src={url} /> : <VideoContent alt={profile} src={url} />}
</li>
))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ const AudioCardAttachment = ({
<div className={audioCardAttachmentStyleSet}>
<ul className="media-list">
{media.map(({ url }, index) => (
<li key={index}>
// Because of differences in browser implementations, aria-label=" " is used to make the screen reader not repeat the same text multiple times in Chrome v75 and Edge 44
<li aria-label=" " key={index}>
<AudioContent autoPlay={autostart} loop={autoloop} poster={imageURL} src={url} />
</li>
))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ const VideoCardAttachment = ({
<div className={audioCardAttachmentStyleSet}>
<ul className="media-list">
{media.map(({ url }, index) => (
<li key={index}>
// Because of differences in browser implementations, aria-label=" " is used to make the screen reader not repeat the same text multiple times in Chrome v75 and Edge 44
<li aria-label=" " key={index}>
<VideoContent autoPlay={autostart} loop={autoloop} poster={imageURL} src={url} />
</li>
))}
Expand Down
10 changes: 6 additions & 4 deletions packages/component/src/Activity/CarouselFilmStrip.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Context as FilmContext } from 'react-film';
import classNames from 'classnames';
import PropTypes from 'prop-types';
import React from 'react';
import remarkStripMarkdown from '../Utils/remarkStripMarkdown';

import { Constants } from 'botframework-webchat-core';

Expand Down Expand Up @@ -114,10 +115,10 @@ const WebChatCarouselFilmStrip = ({

const fromUser = role === 'user';
const activityDisplayText = messageBackDisplayText || text;
const strippedActivityDisplayText = remarkStripMarkdown(activityDisplayText);
const indented = fromUser ? bubbleFromUserNubSize : bubbleNubSize;
const initials = fromUser ? userInitials : botInitials;
const roleLabel = fromUser ? userRoleLabel : botRoleLabel;

return (
<div
className={classNames(ROOT_CSS + '', carouselFilmStripStyleSet + '', className + '', {
Expand All @@ -129,8 +130,8 @@ const WebChatCarouselFilmStrip = ({
<div className="content">
{!!activityDisplayText && (
<div className="message">
<ScreenReaderText text={roleLabel} />
<Bubble className="bubble" fromUser={fromUser} nub={true}>
<ScreenReaderText text={roleLabel + ' ' + strippedActivityDisplayText} />
<Bubble aria-hidden="true" className="bubble" fromUser={fromUser} nub={true}>
{children({
activity,
attachment: {
Expand All @@ -144,7 +145,8 @@ const WebChatCarouselFilmStrip = ({
)}
<ul className={classNames({ webchat__carousel__item_indented: indented })} ref={itemContainerRef}>
{attachments.map((attachment, index) => (
<li key={index}>
// Because of differences in browser implementations, aria-label=" " is used to make the screen reader not repeat the same text multiple times in Chrome v75 and Edge 44
<li aria-label=" " key={index}>
<ScreenReaderText text={roleLabel} />
<Bubble fromUser={fromUser} key={index} nub={false}>
{children({ attachment })}
Expand Down
21 changes: 11 additions & 10 deletions packages/component/src/Activity/StackedLayout.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
/* eslint react/no-array-index-key: "off" */
/* eslint-disable no-sync */

import { Constants } from 'botframework-webchat-core';
import { css } from 'glamor';
import classNames from 'classnames';
import PropTypes from 'prop-types';
import React from 'react';
import remark from 'remark';
import stripMarkdown from 'strip-markdown';
import remarkStripMarkdown from '../Utils/remarkStripMarkdown';

import Avatar from './Avatar';
import Bubble from './Bubble';
Expand All @@ -19,6 +17,7 @@ import Timestamp from './Timestamp';
import useAvatarForBot from '../hooks/useAvatarForBot';
import useAvatarForUser from '../hooks/useAvatarForUser';
import useLocalize from '../hooks/useLocalize';
import useLocalizeDate from '../hooks/useLocalizeDate';
import useStyleOptions from '../hooks/useStyleOptions';
import useStyleSet from '../hooks/useStyleSet';

Expand Down Expand Up @@ -97,16 +96,15 @@ const StackedLayout = ({ activity, children, timestampClassName }) => {
channelData: { messageBack: { displayText: messageBackDisplayText } = {}, state } = {},
from: { role } = {},
text,
textFormat
textFormat,
timestamp
} = activity;

const activityDisplayText = messageBackDisplayText || text;
const fromUser = role === 'user';
const initials = fromUser ? userInitials : botInitials;
const showSendStatus = state === SENDING || state === SEND_FAILED;
const plainText = remark()
.use(stripMarkdown)
.processSync(text);
const plainText = remarkStripMarkdown(text);
const indented = fromUser ? bubbleFromUserNubSize : bubbleNubSize;

const botRoleLabel = useLocalize('BotSent');
Expand All @@ -116,8 +114,11 @@ const StackedLayout = ({ activity, children, timestampClassName }) => {

const botAriaLabel = useLocalize('Bot said something', initials, plainText);
const userAriaLabel = useLocalize('User said something', initials, plainText);
const sentAtTimestamp = useLocalize('SentAt') + useLocalizeDate(timestamp);

const ariaLabel = fromUser ? userAriaLabel : botAriaLabel;
const someoneSaidString = (fromUser ? userAriaLabel : botAriaLabel).trim();

const ariaLabel = someoneSaidString + (someoneSaidString.endsWith('.') ? '' : '.') + ' ' + sentAtTimestamp;

return (
<div
Expand Down Expand Up @@ -146,8 +147,8 @@ const StackedLayout = ({ activity, children, timestampClassName }) => {
<div className="filler" />
</div>
)}
{/* Because of differences in browser implementations, aria-label=" " is used to make the screen reader not repeat the same text multiple times in Chrome v75 */}
{attachments.map((attachment, index) => (
// Because of differences in browser implementations, aria-label=" " is used to make the screen reader not repeat the same text multiple times in Chrome v75 and Edge 44
<div
aria-label=" "
className={classNames('webchat__row attachment', { webchat__stacked_item_indented: indented })}
Expand All @@ -163,7 +164,7 @@ const StackedLayout = ({ activity, children, timestampClassName }) => {
{showSendStatus ? (
<SendStatus activity={activity} className="timestamp" />
) : (
<Timestamp activity={activity} className={classNames('timestamp', timestampClassName)} />
<Timestamp activity={activity} aria-hidden={true} className={classNames('timestamp', timestampClassName)} />
)}
<div className="filler" />
</div>
Expand Down
6 changes: 4 additions & 2 deletions packages/component/src/Activity/Timestamp.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import RelativeTime from '../Utils/RelativeTime';
import useStyleOptions from '../hooks/useStyleOptions';
import useStyleSet from '../hooks/useStyleSet';

const Timestamp = ({ activity: { timestamp }, className }) => {
const Timestamp = ({ activity: { timestamp }, 'aria-hidden': ariaHidden, className }) => {
const [{ timestampFormat }] = useStyleOptions();
const [{ timestamp: timestampStyleSet }] = useStyleSet();

Expand All @@ -16,20 +16,22 @@ const Timestamp = ({ activity: { timestamp }, className }) => {
}

return (
<span className={classNames(timestampStyleSet + '', (className || '') + '')}>
<span aria-hidden={ariaHidden} className={classNames(timestampStyleSet + '', (className || '') + '')}>
{timestampFormat === 'relative' ? <RelativeTime value={timestamp} /> : <AbsoluteTime value={timestamp} />}
</span>
);
};

Timestamp.defaultProps = {
'aria-hidden': false,
className: ''
};

Timestamp.propTypes = {
activity: PropTypes.shape({
timestamp: PropTypes.string.isRequired
}).isRequired,
'aria-hidden': PropTypes.bool,
className: PropTypes.string
};

Expand Down
9 changes: 6 additions & 3 deletions packages/component/src/Attachment/TextContent.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// The content rendered here is sanitized.
/* eslint react/no-danger: "off" */
/* eslint react/no-array-index-key: "off" */

// The content rendered here is sanitized.

import classNames from 'classnames';
import PropTypes from 'prop-types';
import React from 'react';
import remarkStripMarkdown from '../Utils/remarkStripMarkdown';

import ScreenReaderText from '../ScreenReaderText';
import useRenderMarkdownAsHTML from '../hooks/useRenderMarkdownAsHTML';
Expand All @@ -13,10 +15,11 @@ import useStyleSet from '../hooks/useStyleSet';
const TextContent = ({ contentType, text }) => {
const renderMarkdownAsHTML = useRenderMarkdownAsHTML();
const [{ textContent: textContentStyleSet }] = useStyleSet();
const strippedText = remarkStripMarkdown(text).contents;

return contentType === 'text/markdown' && renderMarkdownAsHTML ? (
<React.Fragment>
<ScreenReaderText text={text} />
<ScreenReaderText text={strippedText} />
<div
aria-hidden={true}
className={classNames('markdown', textContentStyleSet + '')}
Expand All @@ -26,7 +29,7 @@ const TextContent = ({ contentType, text }) => {
) : (
(text || '').split('\n').map((line, index) => (
<React.Fragment key={index}>
<ScreenReaderText text={text} />
<ScreenReaderText text={remarkStripMarkdown(line.trim()).contents} />
<p aria-hidden={true} className={classNames('plain', textContentStyleSet + '')}>
{line.trim()}
</p>
Expand Down
8 changes: 7 additions & 1 deletion packages/component/src/Attachment/UploadAttachment.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,15 @@ const UploadAttachment = ({
const [{ uploadAttachment: uploadAttachmentStyleSet }] = useStyleSet();

const attachmentIndex = attachments.indexOf(attachment);
const uploadLabel = useLocalize('Upload file');
const size = attachmentSizes[attachmentIndex];
const formattedSize = typeof size === 'number' && format(size);
const uploadFileWithFileSizeLabel = useLocalize('UploadFileWithFileSize', attachment.name, formattedSize);
const uploadFileWithFileSizeLabel = useLocalize(
'UploadFileWithFileSize',
uploadLabel,
attachment.name,
formattedSize
);

return (
<React.Fragment>
Expand Down
2 changes: 1 addition & 1 deletion packages/component/src/BasicTranscript.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ const BasicTranscript = ({ activityRenderer, attachmentRenderer, className, grou
>
{activityElements.map(({ activity, element }, index) => (
<li
// Because of differences in browser implementations, aria-label=" " is used to make the screen reader not repeat the same text multiple times in Chrome v75
// Because of differences in browser implementations, aria-label=" " is used to make the screen reader not repeat the same text multiple times in Chrome v75 and Edge 44
aria-label=" "
className={classNames(activityStyleSet + '', {
// Hide timestamp if same timestamp group with the next activity
Expand Down
2 changes: 1 addition & 1 deletion packages/component/src/Localization/bg-BG.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ function downloadFileWithFileSize(downloadFileText, fileName, size) {
return `${downloadFileText} ${fileName} с размер ${size}`;
}

function uploadFileWithFileSize(fileName, size) {
function uploadFileWithFileSize(uploadFileText, fileName, size) {
return `${fileName} с рамер ${size}`;
}

Expand Down
10 changes: 7 additions & 3 deletions packages/component/src/Localization/en-US.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ function botSaidSomething(avatarInitials, text) {

function downloadFileWithFileSize(downloadFileText, fileName, size) {
// Full text should read: "Download file <filename> of size <filesize>"
return `${downloadFileText} ${fileName} of size ${size}`;
return `${downloadFileText} '${fileName}' of size ${size}`;
}

function uploadFileWithFileSize(fileName, size) {
return `${fileName} of size ${size}`;
function uploadFileWithFileSize(uploadFileText, fileName, size) {
return `${uploadFileText} '${fileName}' of size ${size}`;
}

function userSaidSomething(avatarInitials, text) {
Expand All @@ -69,6 +69,7 @@ export default {
BotSent: 'Bot sent: ',
Chat: 'Chat',
'Download file': 'Download file',
ConnectivityStatus: 'Connectivity status: ',
DownloadFileWithFileSize: downloadFileWithFileSize,
ErrorMessage: 'Error message',
'Microphone off': 'Microphone off',
Expand All @@ -84,6 +85,9 @@ export default {
SendStatus: 'Send status: ',
SentAt: 'Sent at: ',
Speak: 'Speak',
SuggestedActionsContainer: 'Suggested Actions container: ',
SuggestedActionsContent: 'has content',
SuggestedActionsEmpty: 'is empty',
'Starting…': 'Starting…',
Tax: 'Tax',
Total: 'Total',
Expand Down
2 changes: 1 addition & 1 deletion packages/component/src/Localization/fi-FI.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ function downloadFileWithFileSize(downloadFileText, fileName, size) {
return `Lataa tiedosto ${fileName}, koko: ${size}`;
}

function uploadFileWithFileSize(fileName, size) {
function uploadFileWithFileSize(uploadFileText, fileName, size) {
return `${fileName}, koko: ${size}`;
}

Expand Down
2 changes: 1 addition & 1 deletion packages/component/src/Localization/it-IT.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ function downloadFileWithFileSize(downloadFileText, fileName, size) {
return `${downloadFileText} ${fileName} di dimensione ${size}`;
}

function uploadFileWithFileSize(fileName, size) {
function uploadFileWithFileSize(uploadFileText, fileName, size) {
return `${fileName} di dimensione ${size}`;
}

Expand Down
26 changes: 23 additions & 3 deletions packages/component/src/Localization/ja-JP.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,24 @@ function botSaidSomething(avatarInitials, text) {
return `ボット${avatarInitials}${text}と言いました`;
}

function downloadFileWithFileSize(downloadFileText, fileName, size) {
// Full text should read: "Download file <filename> of size <filesize>"
return `サイズ${size}のファイル${fileName}${downloadFileText}する`;
}

function uploadFileWithFileSize(uploadFileText, fileName, size) {
return `サイズ${size} のファイル${fileName}${uploadFileText}する`;
}

function userSaidSomething(avatarInitials, text) {
return `ユーザー ${avatarInitials}${text}と言いました`;
}

export default {
CONNECTED_NOTIFICATION: '接続しました。',
FAILED_CONNECTION_NOTIFICATION: '接続できませんでした。',
INITIAL_CONNECTION_NOTIFICATION: '接続中...',
INTERRUPTED_CONNECTION_NOTIFICATION: 'ネットワーク中断しました。 再接続中...',
INITIAL_CONNECTION_NOTIFICATION: '接続中',
INTERRUPTED_CONNECTION_NOTIFICATION: 'ネットワーク中断しました。 再接続中',
RENDER_ERROR_NOTIFICATION:
'レンダリングエラーが発生しました。コンソールを確認するか、ボットの開発者に連絡してください。',
// Do not localize {Retry}; it is a placeholder for "Retry". English translation should be, "Send failed. Retry."
Expand All @@ -59,8 +69,12 @@ export default {
'X minutes ago': xMinutesAgo,
'Adaptive Card parse error': 'Adaptive Cardの解析エラー',
'Adaptive Card render error': 'Adaptive Cardのレンダリングエラー',
BotSent: 'ボットが送った:',
Chat: 'チャット',
'Download file': 'ダウンロード',
ConnectivityStatus: '接続状態:',
DownloadFileWithFileSize: downloadFileWithFileSize,
ErrorMessage: 'Error message',
'Microphone off': 'マイクオン',
'Microphone on': 'マイクオフ',
Left: '左',
Expand All @@ -71,10 +85,16 @@ export default {
SendBox: 'テキストボックス',
Sending: '送信中',
Speak: '話してください',
SuggestedActionsContainer: 'Suggested Actions コンテンツ:',
SuggestedActionsContent: '有り',
SuggestedActionsEmpty: '無し',
'Starting…': 'スタート…',
Tax: '税',
Total: '合計',
'Type your message': 'メッセージを入力してください',
'Upload file': 'ファイルをアップロード',
TypingIndicator: 'タイピングインジケーターの表示',
'Upload file': 'アップロード',
UploadFileWithFileSize: uploadFileWithFileSize,
UserSent: 'ユーザーが送った:',
VAT: '消費税'
};
Loading

0 comments on commit 62e0e88

Please sign in to comment.