diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3304cea8fe..3bf922394b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -50,6 +50,7 @@ Legends:
### Fixed
+- Fixed screen reader primary-action activation to move focus into interactive transcript activities, in PR [#5867](https://github.com/microsoft/BotFramework-WebChat/pull/5867), by [@ahonamatata](https://github.com/ahonamatata)
- Fixed an error when a failed activity is present when Web Chat mounts, resolving [#5812](https://github.com/microsoft/BotFramework-WebChat/issues/5812), in PR [#5848](https://github.com/microsoft/BotFramework-WebChat/pull/5848), by [@OEvgeny](https://github.com/OEvgeny)
## [4.19.1] - 2026-06-09
diff --git a/__tests__/html2/part-grouping/primaryAction.html b/__tests__/html2/part-grouping/primaryAction.html
new file mode 100644
index 0000000000..b039ffa8a7
--- /dev/null
+++ b/__tests__/html2/part-grouping/primaryAction.html
@@ -0,0 +1,97 @@
+
+
+
+ Part grouping: screen reader primary action
+
+
+
+
+
+
+
+
+
+
diff --git a/__tests__/html2/transcript/navigation/focusAttachment.screenReaderPrimaryAction.html b/__tests__/html2/transcript/navigation/focusAttachment.screenReaderPrimaryAction.html
new file mode 100644
index 0000000000..a11cd7542d
--- /dev/null
+++ b/__tests__/html2/transcript/navigation/focusAttachment.screenReaderPrimaryAction.html
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/component/src/Middleware/ActivityGrouping/ui/PartGrouping/private/PartGroupingActivity.tsx b/packages/component/src/Middleware/ActivityGrouping/ui/PartGrouping/private/PartGroupingActivity.tsx
index 4b221a11c4..cae5539991 100644
--- a/packages/component/src/Middleware/ActivityGrouping/ui/PartGrouping/private/PartGroupingActivity.tsx
+++ b/packages/component/src/Middleware/ActivityGrouping/ui/PartGrouping/private/PartGroupingActivity.tsx
@@ -2,7 +2,7 @@ import { reactNode, validateProps } from '@msinternal/botframework-webchat-react
import { useStyles } from '@msinternal/botframework-webchat-styles/react';
import { hooks } from 'botframework-webchat-api';
import { getOrgSchemaMessage, type WebChatActivity } from 'botframework-webchat-core';
-import React, { Fragment, memo, useCallback, useMemo, useState, type MouseEventHandler } from 'react';
+import React, { Fragment, memo, useCallback, useMemo, useRef, useState, type MouseEventHandler } from 'react';
import cx from 'classnames';
import { array, custom, is, minLength, object, optional, pipe, readonly, string, type InferOutput } from 'valibot';
@@ -67,6 +67,7 @@ const FocusablePartGroupingActivity = memo(function FocusablePartGroupingActivit
const { activity, children, className, groupKey } = validateProps(partGroupingFocusableActivityPropsSchema, props);
const [activeDescendantId] = useActiveDescendantId();
+ const focusTrapTargetRef = useRef();
const getGroupDescendantIdByActivityKey = useGetGroupDescendantIdByActivityKey();
const focusByGroupKey = useFocusByGroupKey();
@@ -85,6 +86,9 @@ const FocusablePartGroupingActivity = memo(function FocusablePartGroupingActivit
// When receive Escape key from descendant, focus back to the group.
const handleLeaveFocusTrap = useCallback(() => focusSelf(), [focusSelf]);
+ // Screen readers dispatch a click to the active descendant when users invoke its primary action.
+ const handleScreenReaderPrimaryAction = useCallback(() => focusTrapTargetRef.current?.focus(), [focusTrapTargetRef]);
+
// When the user press UP/DOWN arrow keys, we put a visual focus indicator around the focused group.
// We should do the same for mouse, when the user click on the activity, we should also put a visual focus indicator around the activity.
// We are doing it in event capture phase to prevent descendants from stopping event propagation to us.
@@ -115,11 +119,17 @@ const FocusablePartGroupingActivity = memo(function FocusablePartGroupingActivit
onFocus={handleDescendantFocus}
onLeave={handleLeaveFocusTrap}
targetClassName="webchat__basic-transcript__group-focus-target"
+ targetRef={focusTrapTargetRef}
>
{children}
- {!android && }
+ {!android && (
+
+ )}
diff --git a/packages/component/src/Transcript/ActivityRow.tsx b/packages/component/src/Transcript/ActivityRow.tsx
index 697f6d3e5f..04ebb5585c 100644
--- a/packages/component/src/Transcript/ActivityRow.tsx
+++ b/packages/component/src/Transcript/ActivityRow.tsx
@@ -32,6 +32,7 @@ const ActivityRow = forwardRef(({ activity, child
const [readActivityKeys] = useActivityKeysByRead();
const activityRef = useRefFrom(activity);
const bodyRef = useRef();
+ const focusTrapTargetRef = useRef();
const focusByActivityKey = useFocusByActivityKey();
const getKeyByActivity = useGetKeyByActivity();
// TODO: [P2] #2858 We should use core/definitions/speakingActivity for this predicate instead
@@ -59,6 +60,9 @@ const ActivityRow = forwardRef(({ activity, child
// When receive Escape key from descendant, focus back to the activity.
const handleLeaveFocusTrap = useCallback(() => focusSelf(), [focusSelf]);
+ // Screen readers dispatch a click to the active descendant when users invoke its primary action.
+ const handleScreenReaderPrimaryAction = useCallback(() => focusTrapTargetRef.current?.focus(), [focusTrapTargetRef]);
+
// When the user press UP/DOWN arrow keys, we put a visual focus indicator around the focused activity.
// We should do the same for mouse, when the user click on the activity, we should also put a visual focus indicator around the activity.
// We are doing it in event capture phase to prevent descendants from stopping event propagation to us.
@@ -146,6 +150,7 @@ const ActivityRow = forwardRef(({ activity, child
onFocus={handleDescendantFocus}
onLeave={handleLeaveFocusTrap}
targetClassName="webchat__basic-transcript__activity-focus-target"
+ targetRef={focusTrapTargetRef}
>
{focusTrapChildren}
@@ -160,6 +165,7 @@ const ActivityRow = forwardRef(({ activity, child
className="webchat__basic-transcript__activity-active-descendant"
// "id" is required for "aria-labelledby"
id={descendantId}
+ onClick={handleScreenReaderPrimaryAction}
role="article"
>
diff --git a/packages/component/src/Transcript/FocusTrap.tsx b/packages/component/src/Transcript/FocusTrap.tsx
index 73b7ac7688..1d29e81ef7 100644
--- a/packages/component/src/Transcript/FocusTrap.tsx
+++ b/packages/component/src/Transcript/FocusTrap.tsx
@@ -5,6 +5,7 @@ import React, {
type FocusEvent,
type KeyboardEventHandler,
type ReactNode,
+ type Ref,
useCallback,
useRef
} from 'react';
@@ -16,12 +17,14 @@ const FocusTrap = ({
children,
onFocus,
onLeave,
- targetClassName
+ targetClassName,
+ targetRef
}: Readonly<{
children: ReactNode;
onFocus: () => void;
onLeave: () => void;
targetClassName?: string | undefined;
+ targetRef?: Ref | undefined;
}>) => {
const bodyRef = useRef();
const lastFocused = useRef();
@@ -122,7 +125,7 @@ const FocusTrap = ({
>
{children}
-
+
);
};