diff --git a/.changelog/5929-fix-liveregion-card-text-format.md b/.changelog/5929-fix-liveregion-card-text-format.md new file mode 100644 index 0000000000..76444827c6 --- /dev/null +++ b/.changelog/5929-fix-liveregion-card-text-format.md @@ -0,0 +1,3 @@ +### Fixed + +- Fixed screen readers reading Adaptive Card text with its Markdown and `{{DATE()}}` syntax, in PR [#5929](https://github.com/microsoft/BotFramework-WebChat/pull/5929), by [@OEvgeny](https://github.com/OEvgeny) diff --git a/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.dateTime.html b/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.dateTime.html new file mode 100644 index 0000000000..39af46ff3c --- /dev/null +++ b/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.dateTime.html @@ -0,0 +1,91 @@ + + +
+ + + + + + + + + + diff --git a/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.dateTime.html.snap-1.png b/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.dateTime.html.snap-1.png new file mode 100644 index 0000000000..d02e7faa07 Binary files /dev/null and b/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.dateTime.html.snap-1.png differ diff --git a/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.imageAndBlocks.html b/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.imageAndBlocks.html new file mode 100644 index 0000000000..2dad3ea518 --- /dev/null +++ b/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.imageAndBlocks.html @@ -0,0 +1,92 @@ + + + + + + + + + + + + + diff --git a/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.imageAndBlocks.html.snap-1.png b/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.imageAndBlocks.html.snap-1.png new file mode 100644 index 0000000000..f2a916ac38 Binary files /dev/null and b/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.imageAndBlocks.html.snap-1.png differ diff --git a/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.markdown.html b/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.markdown.html new file mode 100644 index 0000000000..c469d239d7 --- /dev/null +++ b/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.markdown.html @@ -0,0 +1,96 @@ + + + + + + + + + + + + + diff --git a/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.markdown.html.snap-1.png b/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.markdown.html.snap-1.png new file mode 100644 index 0000000000..f330909696 Binary files /dev/null and b/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.markdown.html.snap-1.png differ diff --git a/packages/bundle/src/adaptiveCards/Attachment/private/renderAdaptiveCard.ts b/packages/bundle/src/adaptiveCards/Attachment/private/renderAdaptiveCard.ts index a41cc32e71..8ea39fadc4 100644 --- a/packages/bundle/src/adaptiveCards/Attachment/private/renderAdaptiveCard.ts +++ b/packages/bundle/src/adaptiveCards/Attachment/private/renderAdaptiveCard.ts @@ -1,9 +1,6 @@ -import type { - AdaptiveCard, - GlobalSettings as GlobalSettingsType, - HostConfig as HostConfigType, - IMarkdownProcessingResult -} from 'adaptivecards'; +import type { AdaptiveCard, GlobalSettings as GlobalSettingsType, HostConfig as HostConfigType } from 'adaptivecards'; + +import setMarkdownEngine from './setMarkdownEngine'; /** * Returns `true`, if the object is a plain object and not a class, otherwise, `false`. @@ -29,19 +26,7 @@ export default function renderAdaptiveCard( setTabIndexAtCardRoot: boolean; } ): { element: HTMLElement } | { errors: Error[] } { - // Currently, the only way to set the Markdown engine is to set it thru static member of AdaptiveCard class - - // TODO: [P3] Checks if we could make the "renderMarkdownAsHTML" per card - // This could be limitations from Adaptive Cards package (not supported as of 1.2.5) - // Because there could be timing difference between .parse and .render, we could be using wrong Markdown engine - - // "onProcessMarkdown" is a static function but we are trying to scope it to the current object instead. - adaptiveCard.constructor['onProcessMarkdown'] = (text: string, result: IMarkdownProcessingResult) => { - if (renderMarkdownAsHTML) { - result.outputHtml = renderMarkdownAsHTML(text); - result.didProcess = true; - } - }; + setMarkdownEngine(adaptiveCard, renderMarkdownAsHTML); if (adaptiveCardsHostConfig) { adaptiveCard.hostConfig = isPlainObject(adaptiveCardsHostConfig) diff --git a/packages/bundle/src/adaptiveCards/Attachment/private/setMarkdownEngine.ts b/packages/bundle/src/adaptiveCards/Attachment/private/setMarkdownEngine.ts new file mode 100644 index 0000000000..2f4d2e325b --- /dev/null +++ b/packages/bundle/src/adaptiveCards/Attachment/private/setMarkdownEngine.ts @@ -0,0 +1,20 @@ +import type { AdaptiveCard, IMarkdownProcessingResult } from 'adaptivecards'; + +export default function setMarkdownEngine( + adaptiveCard: AdaptiveCard, + renderMarkdownAsHTML: ((markdown: string) => string) | undefined +): void { + // Currently, the only way to set the Markdown engine is to set it thru static member of AdaptiveCard class + + // TODO: [P3] Checks if we could make the "renderMarkdownAsHTML" per card + // This could be limitations from Adaptive Cards package (not supported as of 1.2.5) + // Because there could be timing difference between .parse and .render, we could be using wrong Markdown engine + + // "onProcessMarkdown" is a static function but we are trying to scope it to the current object instead. + adaptiveCard.constructor['onProcessMarkdown'] = (text: string, result: IMarkdownProcessingResult) => { + if (renderMarkdownAsHTML) { + result.outputHtml = renderMarkdownAsHTML(text); + result.didProcess = true; + } + }; +} diff --git a/packages/bundle/src/adaptiveCards/AttachmentForScreenReader/AdaptiveCardAttachment.js b/packages/bundle/src/adaptiveCards/AttachmentForScreenReader/AdaptiveCardAttachment.js index d52b17eabf..f58abbaa7e 100644 --- a/packages/bundle/src/adaptiveCards/AttachmentForScreenReader/AdaptiveCardAttachment.js +++ b/packages/bundle/src/adaptiveCards/AttachmentForScreenReader/AdaptiveCardAttachment.js @@ -1,14 +1,44 @@ /* eslint-disable react/no-array-index-key */ /* eslint-disable react/forbid-dom-props */ import { hooks } from 'botframework-webchat-component'; +import { htmlTextAlternatives } from 'botframework-webchat-component/internal.js'; +import { onErrorResumeNext } from 'botframework-webchat-core'; import PropTypes from 'prop-types'; import React, { useMemo } from 'react'; +import setMarkdownEngine from '../Attachment/private/setMarkdownEngine'; import useAdaptiveCardsPackage from '../hooks/useAdaptiveCardsPackage'; import useParseAdaptiveCardJSON from '../hooks/internal/useParseAdaptiveCardJSON'; import useUniqueId from '../hooks/internal/useUniqueId'; -const { useLocalizer } = hooks; +const { useLocalizer, useRenderMarkdownAsHTML } = hooks; + +// Returns the text as the visible card shows it: Adaptive Cards formats {{DATE()}} and {{TIME()}}, then renders the Markdown. +// Images are read by their alt text and blocks are separated, as in the live region of message text. +// If the Markdown engine or Adaptive Cards throws, the visible card shows an error, and the text is read as written. +function getRenderedText(element) { + return ( + onErrorResumeNext(() => { + const renderedElement = element.render(); + + // Plain text, such as a TextRun, is set with innerText, which renders line breaks as