Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ Legends:
- 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)
- Fixed timestamps always using the 12-hour clock, such as "4:41 PM" instead of "16:41" in German, in PR [#5920](https://github.com/microsoft/BotFramework-WebChat/pull/5920), by [@OEvgeny](https://github.com/OEvgeny)
- Fixed screen readers not saying "Click to interact." for messages with links, in PR [#5927](https://github.com/microsoft/BotFramework-WebChat/pull/5927), by [@OEvgeny](https://github.com/OEvgeny)

## [4.19.2] - 2026-10-08

Expand Down
68 changes: 68 additions & 0 deletions __tests__/html2/accessibility/accessibleName.link.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<!doctype html>
<html lang="en-US">
<head>
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
<script crossorigin="anonymous" src="/test-harness.js"></script>
<script crossorigin="anonymous" src="/test-page-object.js"></script>
<script crossorigin="anonymous" src="/__dist__/webchat-es5.js"></script>
</head>
<body>
<main id="webchat"></main>
<script type="importmap">
{
"imports": {
"@testduet/wait-for": "https://esm.sh/@testduet/wait-for"
}
}
</script>
<script type="module">
import { waitFor } from '@testduet/wait-for';

run(async function () {
const { directLine, store } = testHelpers.createDirectLineEmulator();

WebChat.renderWebChat(
{
directLine,
store,
styleOptions: { hideSendBox: true }
},
document.getElementById('webchat')
);

await pageConditions.uiConnected();

const getActivityAccessibleName = activityElement =>
testHelpers.getAccessibleName(
activityElement.querySelector('.webchat__basic-transcript__activity-active-descendant')
);

// GIVEN: a message without links.
await directLine.emulateIncomingActivity({ text: 'Hello, World!', type: 'message' });

await pageConditions.numActivitiesShown(1);

// THEN: the accessible name has no interactive footnote.
await waitFor(async () =>
expect(await getActivityAccessibleName(pageElements.activities()[0])).toBe('Bot said: Hello, World!')
);

// GIVEN: a message with a link.
await directLine.emulateIncomingActivity({
text: 'Read the [documentation](https://example.com/docs).',
type: 'message'
});

await pageConditions.numActivitiesShown(2);

// THEN: the accessible name tells the message has links and can be interacted with.
// Zero-width spaces wrap every link for line breaking, screen readers do not pause on them.
await waitFor(async () =>
expect((await getActivityAccessibleName(pageElements.activities()[1])).replace(/\u200b/gu, '')).toBe(
'Bot said: Read the documentation. One or more links in the message. Click to interact.'
)
);
});
</script>
</body>
</html>
12 changes: 4 additions & 8 deletions packages/component/src/Transcript/useActivityAccessibleName.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@ import tabbableElements from '../Utils/tabbableElements';
import useRenderMarkdownAsHTML from '../hooks/useRenderMarkdownAsHTML';
import useValueRef from '../hooks/internal/useValueRef';

enum InteractiveType {
LINK,
WIDGET
}
type InteractiveType = 'link' | 'widget';

const { useAvatarForBot, useGetKeyByActivity, useLocalizer, useSendStatusByActivityKey } = hooks;

Expand Down Expand Up @@ -84,9 +81,9 @@ export default function useActivityAccessibleName(activity: WebChatActivity, bod
messageTextAlt,
numAttachmentsAlt,
isSendFailed ? activityInteractiveReasonSendFailedAlt : '',
interactiveType === InteractiveType.LINK
interactiveType === 'link'
? activityInteractiveReasonLinkAlt
: interactiveType === InteractiveType.WIDGET
: interactiveType === 'widget'
? activityInteractiveReasonInteractiveContentAlt
: '',
interactiveType || isSendFailed ? activityInteractiveFootNoteAlt : ''
Expand All @@ -110,8 +107,7 @@ export default function useActivityAccessibleName(activity: WebChatActivity, bod
const hasLinks = !!bodyRef.current?.querySelector('a');
const hasWidgets = !!tabbableElements(bodyRef.current).length;

interactiveTypeRef.current !== hasWidgets &&
setInteractiveType(hasLinks ? InteractiveType.LINK : hasWidgets ? InteractiveType.WIDGET : false);
interactiveTypeRef.current !== hasWidgets && setInteractiveType(hasLinks ? 'link' : hasWidgets ? 'widget' : false);
}, [bodyRef, interactiveTypeRef, setInteractiveType]);

return [accessibleName];
Expand Down
Loading