diff --git a/CHANGELOG.md b/CHANGELOG.md index d32d8a4300..88397e39bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,7 @@ Legends: - Fixed GIFs in animation cards looping with no way to stop them: they now have a pause button, and do not play when the user prefers reduced motion or the card has `autostart: false`; the icons of the Copy and View code buttons are now visible in forced colors, fixes [#5734](https://github.com/microsoft/BotFramework-WebChat/issues/5734), in PR [#5897](https://github.com/microsoft/BotFramework-WebChat/pull/5897), by [@OEvgeny](https://github.com/OEvgeny) - Fixed the play, mute, and "more options" icons of audio players being invisible in dark high contrast themes, such as "Aquatic" on Windows, fixes [#5710](https://github.com/microsoft/BotFramework-WebChat/issues/5710), in PR [#5906](https://github.com/microsoft/BotFramework-WebChat/pull/5906), by [@OEvgeny](https://github.com/OEvgeny) - Fixed the "Positive" card action button having less than 4.5:1 color contrast, fixes [#5713](https://github.com/microsoft/BotFramework-WebChat/issues/5713), in PR [#5905](https://github.com/microsoft/BotFramework-WebChat/pull/5905), by [@OEvgeny](https://github.com/OEvgeny) +- Fixed screen readers announcing Adaptive Cards without `speak` as just "A card", they now read the card text, in PR [#5922](https://github.com/microsoft/BotFramework-WebChat/pull/5922), by [@OEvgeny](https://github.com/OEvgeny) - Fixed screen readers announcing a single audio, video, or animation in a card as a list item, such as "1 of 1", fixes [#5722](https://github.com/microsoft/BotFramework-WebChat/issues/5722), in PR [#5903](https://github.com/microsoft/BotFramework-WebChat/pull/5903), by [@OEvgeny](https://github.com/OEvgeny) - Fixed video and animation cards disappearing in production builds when the video URL is relative or invalid, in PR [#5889](https://github.com/microsoft/BotFramework-WebChat/pull/5889), by [@OEvgeny](https://github.com/OEvgeny) diff --git a/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.hiddenText.html b/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.hiddenText.html new file mode 100644 index 0000000000..1ee37df0ce --- /dev/null +++ b/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.hiddenText.html @@ -0,0 +1,102 @@ + + +
+ + + + + + + + + + diff --git a/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.hiddenText.html.snap-1.png b/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.hiddenText.html.snap-1.png new file mode 100644 index 0000000000..401c45cd8a Binary files /dev/null and b/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.hiddenText.html.snap-1.png differ diff --git a/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.speakOverText.html b/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.speakOverText.html new file mode 100644 index 0000000000..a62bdc4b61 --- /dev/null +++ b/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.speakOverText.html @@ -0,0 +1,82 @@ + + + + + + + + + + + + + diff --git a/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.speakOverText.html.snap-1.png b/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.speakOverText.html.snap-1.png new file mode 100644 index 0000000000..57ad374651 Binary files /dev/null and b/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.speakOverText.html.snap-1.png differ diff --git a/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.text.html b/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.text.html new file mode 100644 index 0000000000..715fef4edf --- /dev/null +++ b/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.text.html @@ -0,0 +1,100 @@ + + + + + + + + + + + + + diff --git a/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.text.html.snap-1.png b/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.text.html.snap-1.png new file mode 100644 index 0000000000..a478db8d8c Binary files /dev/null and b/__tests__/html2/accessibility/liveRegion/attachment/adaptiveCard.text.html.snap-1.png differ diff --git a/packages/bundle/src/adaptiveCards/AttachmentForScreenReader/AdaptiveCardAttachment.js b/packages/bundle/src/adaptiveCards/AttachmentForScreenReader/AdaptiveCardAttachment.js index 8b38b0f1f0..d52b17eabf 100644 --- a/packages/bundle/src/adaptiveCards/AttachmentForScreenReader/AdaptiveCardAttachment.js +++ b/packages/bundle/src/adaptiveCards/AttachmentForScreenReader/AdaptiveCardAttachment.js @@ -10,19 +10,22 @@ import useUniqueId from '../hooks/internal/useUniqueId'; const { useLocalizer } = hooks; -// Perform a depth-first search of the Adaptive Card tree. +// Perform a depth-first search of the visible elements of the Adaptive Card tree, skipping hidden subtrees. +// Actions are visited but not walked into, so the card of Action.ShowCard, hidden until opened, is not visited. function walkAllItems(node, fn) { - fn(node); + if (node.isVisible) { + fn(node); - if (node.getItemAt && node.getItemCount) { - for (let count = node.getItemCount(), index = 0; index < count; index++) { - walkAllItems(node.getItemAt(index), fn); + if (node.getItemAt && node.getItemCount) { + for (let count = node.getItemCount(), index = 0; index < count; index++) { + walkAllItems(node.getItemAt(index), fn); + } } - } - if (node.getActionAt && node.getActionCount) { - for (let count = node.getActionCount(), index = 0; index < count; index++) { - fn(node.getActionAt(index)); + if (node.getActionAt && node.getActionCount) { + for (let count = node.getActionCount(), index = 0; index < count; index++) { + fn(node.getActionAt(index)); + } } } } @@ -70,10 +73,14 @@ const AdaptiveCardAttachment = ({ content }) => { { ChoiceSetInput, DateInput, + FactSet, + Image, NumberInput, OpenUrlAction, + RichTextBlock, ShowCardAction, SubmitAction, + TextBlock, TextInput, TimeInput, ToggleInput @@ -114,7 +121,27 @@ const AdaptiveCardAttachment = ({ content }) => { ToggleInput ]); - const cardLabel = localize('ATTACHMENT_CARD', card.speak || '', '', ''); + // Without `speak`, the card is described by its visible text, in document order. Inputs and actions are listed separately below. + const text = useMemo(() => { + const nodes = []; + + walkAllItems(card, node => nodes.push(node)); + + return nodes + .flatMap(node => + node instanceof FactSet + ? node.facts.map(({ name, value }) => [name, value].filter(Boolean).join(': ')) + : node instanceof Image + ? [node.altText] + : node instanceof RichTextBlock || node instanceof TextBlock + ? [node.asString()] + : [] + ) + .filter(Boolean) + .join(' '); + }, [card, FactSet, Image, RichTextBlock, TextBlock]); + + const cardLabel = localize('ATTACHMENT_CARD', card.speak || text, '', ''); return (