@microsoft/omnichannel-chat-widget 1.8.4-main.2e8be66 → 1.8.4-main.319d589

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (18) hide show
  1. package/lib/cjs/common/Constants.js +0 -1
  2. package/lib/cjs/components/webchatcontainerstateful/WebChatContainerStateful.js +47 -17
  3. package/lib/cjs/components/webchatcontainerstateful/common/DesignerChatAdapter.js +10 -0
  4. package/lib/cjs/components/webchatcontainerstateful/common/defaultProps/defaultWebChatContainerStatefulProps.js +2 -1
  5. package/lib/cjs/components/webchatcontainerstateful/common/utils/chatAdapterUtils.js +3 -1
  6. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/attachments/AdaptiveCardAccessibilityWrapper.js +15 -0
  7. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/attachmentUploadValidatorMiddleware.js +4 -8
  8. package/lib/esm/common/Constants.js +0 -1
  9. package/lib/esm/components/webchatcontainerstateful/WebChatContainerStateful.js +48 -18
  10. package/lib/esm/components/webchatcontainerstateful/common/DesignerChatAdapter.js +10 -0
  11. package/lib/esm/components/webchatcontainerstateful/common/defaultProps/defaultWebChatContainerStatefulProps.js +2 -1
  12. package/lib/esm/components/webchatcontainerstateful/common/utils/chatAdapterUtils.js +3 -1
  13. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/attachments/AdaptiveCardAccessibilityWrapper.js +15 -0
  14. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/attachmentUploadValidatorMiddleware.js +5 -9
  15. package/lib/types/common/Constants.d.ts +0 -1
  16. package/lib/types/components/webchatcontainerstateful/common/utils/chatAdapterUtils.d.ts +1 -1
  17. package/lib/types/components/webchatcontainerstateful/interfaces/IWebChatContainerStatefulProps.d.ts +7 -0
  18. package/package.json +1 -1
@@ -158,7 +158,6 @@ let HtmlIdNames = /*#__PURE__*/_createClass(function HtmlIdNames() {
158
158
  });
159
159
  exports.HtmlIdNames = HtmlIdNames;
160
160
  _defineProperty(HtmlIdNames, "MSLiveChatWidget", "MSLiveChatWidget");
161
- _defineProperty(HtmlIdNames, "fileSentAnnouncementRegionId", "ms_lcw_file_sent_announcement");
162
161
  let HtmlClassNames = /*#__PURE__*/_createClass(function HtmlClassNames() {
163
162
  _classCallCheck(this, HtmlClassNames);
164
163
  });
@@ -232,6 +232,48 @@ const WebChatContainerStateful = props => {
232
232
  chatHistoryElement.setAttribute(_Constants.HtmlAttributeNames.ariaLabel, webChatContainerProps.webChatHistoryMobileAccessibilityLabel);
233
233
  }
234
234
  }
235
+
236
+ // internal tracking: WebChats BasicToaster renders a `role="log"` container
237
+ // with no accessible name. When the toaster is empty (typical idle
238
+ // state) screen readers traversing the page announce it as "blank".
239
+ // Inject an aria-label so the region has a meaningful name; the
240
+ // consumer can override via the new
241
+ // `webChatNotificationRegionAccessibilityLabel` prop. The toaster
242
+ // may not be rendered yet when this effect first runs, so observe
243
+ // for it via a MutationObserver scoped to the widget root (or, if
244
+ // not yet mounted, the widget container's parent) so we never fire
245
+ // on host-page mutations outside the widget subtree.
246
+ const toasterLabel = (webChatContainerProps === null || webChatContainerProps === void 0 ? void 0 : webChatContainerProps.webChatNotificationRegionAccessibilityLabel) ?? _defaultWebChatContainerStatefulProps.defaultWebChatContainerStatefulProps.webChatNotificationRegionAccessibilityLabel ?? "Chat notifications";
247
+ const labelToaster = root => {
248
+ const toaster = root.querySelector(".webchat__toaster[role='log']");
249
+ if (toaster && !toaster.getAttribute(_Constants.HtmlAttributeNames.ariaLabel)) {
250
+ toaster.setAttribute(_Constants.HtmlAttributeNames.ariaLabel, toasterLabel);
251
+ return true;
252
+ }
253
+ return false;
254
+ };
255
+ // Prefer the widget root; if it is not yet in the DOM, fall back to
256
+ // `document` for the *initial* lookup only, then observe the most
257
+ // specific available scope (widget root if present, otherwise the
258
+ // widget's parent / document.body as a last resort). The observer
259
+ // re-resolves the widget root on every callback so we tighten scope
260
+ // as soon as it mounts.
261
+ let toasterObserver;
262
+ const resolveScope = () => document.getElementById("ms_lcw_webchat_root") ?? document.body;
263
+ if (!labelToaster(resolveScope())) {
264
+ toasterObserver = new MutationObserver(() => {
265
+ const scope = resolveScope();
266
+ if (labelToaster(scope)) {
267
+ var _toasterObserver;
268
+ (_toasterObserver = toasterObserver) === null || _toasterObserver === void 0 ? void 0 : _toasterObserver.disconnect();
269
+ toasterObserver = undefined;
270
+ }
271
+ });
272
+ toasterObserver.observe(resolveScope(), {
273
+ childList: true,
274
+ subtree: true
275
+ });
276
+ }
235
277
  dispatch({
236
278
  type: _LiveChatWidgetActionType.LiveChatWidgetActionType.SET_RENDERING_MIDDLEWARE_PROPS,
237
279
  payload: webChatContainerProps === null || webChatContainerProps === void 0 ? void 0 : webChatContainerProps.renderingMiddlewareProps
@@ -256,6 +298,10 @@ const WebChatContainerStateful = props => {
256
298
  }
257
299
  }
258
300
  }
301
+ return () => {
302
+ var _toasterObserver2;
303
+ (_toasterObserver2 = toasterObserver) === null || _toasterObserver2 === void 0 ? void 0 : _toasterObserver2.disconnect();
304
+ };
259
305
  }, []);
260
306
  (0, _react2.useEffect)(() => {
261
307
  var _props$webChatContain6, _props$webChatContain7;
@@ -496,23 +542,7 @@ const WebChatContainerStateful = props => {
496
542
  height: "100%",
497
543
  width: "100%"
498
544
  }
499
- }, shouldLoadPersistentHistoryMessages && /*#__PURE__*/_react2.default.createElement(_WebChatEventSubscribers.default, null), /*#__PURE__*/_react2.default.createElement(BasicWebChat, null))), /*#__PURE__*/_react2.default.createElement("div", {
500
- id: _Constants.HtmlIdNames.fileSentAnnouncementRegionId,
501
- role: "alert",
502
- "aria-live": "assertive",
503
- "aria-atomic": "true",
504
- style: {
505
- position: "absolute",
506
- width: "1px",
507
- height: "1px",
508
- padding: "0",
509
- margin: "-1px",
510
- overflow: "hidden",
511
- clip: "rect(0, 0, 0, 0)",
512
- whiteSpace: "nowrap",
513
- border: "0"
514
- }
515
- }), citationPaneOpen && /*#__PURE__*/_react2.default.createElement(_CitationPaneStateful.default, {
545
+ }, shouldLoadPersistentHistoryMessages && /*#__PURE__*/_react2.default.createElement(_WebChatEventSubscribers.default, null), /*#__PURE__*/_react2.default.createElement(BasicWebChat, null))), citationPaneOpen && /*#__PURE__*/_react2.default.createElement(_CitationPaneStateful.default, {
516
546
  id: ((_props$citationPanePr = props.citationPaneProps) === null || _props$citationPanePr === void 0 ? void 0 : _props$citationPanePr.id) || _Constants.HtmlAttributeNames.ocwCitationPaneClassName,
517
547
  title: ((_props$citationPanePr2 = props.citationPaneProps) === null || _props$citationPanePr2 === void 0 ? void 0 : _props$citationPanePr2.title) || _Constants.HtmlAttributeNames.ocwCitationPaneTitle,
518
548
  contentHtml: citationPaneText,
@@ -59,6 +59,16 @@ let DesignerChatAdapter = /*#__PURE__*/function (_MockAdapter) {
59
59
  if (msg.text) {
60
60
  if (msg.suggestedActions) {
61
61
  (0, _chatAdapterUtils.postAgentSuggestedActionsActivity)(this.activityObserver, msg.text, msg.suggestedActions, index * 100);
62
+ } else if (msg.from && msg.from.role) {
63
+ // Route caller-supplied `from` through the shared
64
+ // postBotMessageActivity helper so the activity shape
65
+ // (id, channelData.tags, timestamp, type) matches every
66
+ // other designer-mode path and downstream middleware
67
+ // (localizedStringsBotInitialsMiddleware, telemetry,
68
+ // attachment plumbing) behaves identically. The override
69
+ // narrows only the fields the caller cares about (e.g.
70
+ // from.name / from.role) — base botUser fields remain.
71
+ (0, _chatAdapterUtils.postBotMessageActivity)(this.activityObserver, msg.text, undefined, index * 100, msg.from);
62
72
  } else {
63
73
  (0, _chatAdapterUtils.postBotMessageActivity)(this.activityObserver, msg.text, undefined, index * 100);
64
74
  }
@@ -20,6 +20,7 @@ const defaultWebChatContainerStatefulProps = {
20
20
  honorsTargetInHTMLLinks: false,
21
21
  hyperlinkTextOverride: false,
22
22
  directLine: new _mockadapter.default(),
23
- adaptiveCardStyles: _defaultAdaptiveCardStyles.defaultAdaptiveCardStyles
23
+ adaptiveCardStyles: _defaultAdaptiveCardStyles.defaultAdaptiveCardStyles,
24
+ webChatNotificationRegionAccessibilityLabel: "Chat notifications"
24
25
  };
25
26
  exports.defaultWebChatContainerStatefulProps = defaultWebChatContainerStatefulProps;
@@ -44,11 +44,13 @@ exports.postEchoActivity = postEchoActivity;
44
44
  const postBotMessageActivity = function (activityObserver, text) {
45
45
  let tags = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "";
46
46
  let delay = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 1000;
47
+ let fromOverride = arguments.length > 4 ? arguments[4] : undefined;
47
48
  setTimeout(() => {
48
49
  activityObserver === null || activityObserver === void 0 ? void 0 : activityObserver.next({
49
50
  id: (0, _omnichannelChatSdk.uuidv4)(),
50
51
  from: {
51
- ...botUser
52
+ ...botUser,
53
+ ...(fromOverride ?? {})
52
54
  },
53
55
  text,
54
56
  type: "message",
@@ -32,6 +32,21 @@ const AdaptiveCardAccessibilityWrapper = _ref => {
32
32
  const container = containerRef.current;
33
33
  if (!container) return;
34
34
 
35
+ // dropdown-double-label: compact Input.ChoiceSet renders as a native
36
+ // <select> with both aria-labelledby and a sibling visible <label for>.
37
+ // Only remove aria-labelledby when it points solely at that same visible
38
+ // label; preserve composite labels that include required/error/context text.
39
+ const compactSelects = container.querySelectorAll(".ac-input-container select.ac-input.ac-multichoiceInput[aria-labelledby]");
40
+ compactSelects.forEach(select => {
41
+ const id = select.id;
42
+ if (!id) return;
43
+ const visibleLabel = container.querySelector(`label[for="${CSS.escape(id)}"]:not([aria-hidden='true'])`);
44
+ const labelledBy = (select.getAttribute("aria-labelledby") || "").trim().split(/\s+/);
45
+ if (visibleLabel !== null && visibleLabel !== void 0 && visibleLabel.id && labelledBy.length === 1 && labelledBy[0] === visibleLabel.id) {
46
+ select.removeAttribute("aria-labelledby");
47
+ }
48
+ });
49
+
35
50
  // action-button-toggle / login-button-toggle: submit/login action buttons can be
36
51
  // rendered with toggle-only ARIA that causes screen readers to announce them as
37
52
  // switches instead of plain push buttons. Preserve Action.ToggleVisibility buttons,
@@ -18,14 +18,10 @@ var _WebChatActionType = require("../../enums/WebChatActionType");
18
18
 
19
19
  const MBtoBRatio = 1000000;
20
20
  const announceFileSent = message => {
21
- // TalkBack on Android WebView reliably announces newly *appended* role="alert"
22
- // nodes but often misses text-content updates on existing nodes.
23
- // Strategy: clear the static region, wait 500ms for the send-box focus shift
24
- // to settle, then inject a fresh alert element and remove it after 3s.
25
- const region = document.getElementById(_Constants.HtmlIdNames.fileSentAnnouncementRegionId);
26
- if (region) {
27
- region.textContent = "";
28
- }
21
+ // TalkBack on Android WebView reliably announces newly *appended*
22
+ // role="alert" nodes but often misses text-content updates on existing
23
+ // nodes. Wait 500ms for the send-box focus shift to settle, then inject
24
+ // a fresh alert element appended to document.body and remove it after 3s.
29
25
  setTimeout(() => {
30
26
  const el = document.createElement("div");
31
27
  el.setAttribute("role", "alert");
@@ -149,7 +149,6 @@ export let HtmlIdNames = /*#__PURE__*/_createClass(function HtmlIdNames() {
149
149
  _classCallCheck(this, HtmlIdNames);
150
150
  });
151
151
  _defineProperty(HtmlIdNames, "MSLiveChatWidget", "MSLiveChatWidget");
152
- _defineProperty(HtmlIdNames, "fileSentAnnouncementRegionId", "ms_lcw_file_sent_announcement");
153
152
  export let HtmlClassNames = /*#__PURE__*/_createClass(function HtmlClassNames() {
154
153
  _classCallCheck(this, HtmlClassNames);
155
154
  });
@@ -1,4 +1,4 @@
1
- import { Constants, HtmlAttributeNames, HtmlClassNames, HtmlIdNames } from "../../common/Constants";
1
+ import { Constants, HtmlAttributeNames, HtmlClassNames } from "../../common/Constants";
2
2
  import { Stack } from "@fluentui/react";
3
3
  import { LogLevel, TelemetryEvent } from "../../common/telemetry/TelemetryConstants";
4
4
  import React, { useEffect, useRef, useState } from "react";
@@ -223,6 +223,48 @@ export const WebChatContainerStateful = props => {
223
223
  chatHistoryElement.setAttribute(HtmlAttributeNames.ariaLabel, webChatContainerProps.webChatHistoryMobileAccessibilityLabel);
224
224
  }
225
225
  }
226
+
227
+ // internal tracking: WebChats BasicToaster renders a `role="log"` container
228
+ // with no accessible name. When the toaster is empty (typical idle
229
+ // state) screen readers traversing the page announce it as "blank".
230
+ // Inject an aria-label so the region has a meaningful name; the
231
+ // consumer can override via the new
232
+ // `webChatNotificationRegionAccessibilityLabel` prop. The toaster
233
+ // may not be rendered yet when this effect first runs, so observe
234
+ // for it via a MutationObserver scoped to the widget root (or, if
235
+ // not yet mounted, the widget container's parent) so we never fire
236
+ // on host-page mutations outside the widget subtree.
237
+ const toasterLabel = (webChatContainerProps === null || webChatContainerProps === void 0 ? void 0 : webChatContainerProps.webChatNotificationRegionAccessibilityLabel) ?? defaultWebChatContainerStatefulProps.webChatNotificationRegionAccessibilityLabel ?? "Chat notifications";
238
+ const labelToaster = root => {
239
+ const toaster = root.querySelector(".webchat__toaster[role='log']");
240
+ if (toaster && !toaster.getAttribute(HtmlAttributeNames.ariaLabel)) {
241
+ toaster.setAttribute(HtmlAttributeNames.ariaLabel, toasterLabel);
242
+ return true;
243
+ }
244
+ return false;
245
+ };
246
+ // Prefer the widget root; if it is not yet in the DOM, fall back to
247
+ // `document` for the *initial* lookup only, then observe the most
248
+ // specific available scope (widget root if present, otherwise the
249
+ // widget's parent / document.body as a last resort). The observer
250
+ // re-resolves the widget root on every callback so we tighten scope
251
+ // as soon as it mounts.
252
+ let toasterObserver;
253
+ const resolveScope = () => document.getElementById("ms_lcw_webchat_root") ?? document.body;
254
+ if (!labelToaster(resolveScope())) {
255
+ toasterObserver = new MutationObserver(() => {
256
+ const scope = resolveScope();
257
+ if (labelToaster(scope)) {
258
+ var _toasterObserver;
259
+ (_toasterObserver = toasterObserver) === null || _toasterObserver === void 0 ? void 0 : _toasterObserver.disconnect();
260
+ toasterObserver = undefined;
261
+ }
262
+ });
263
+ toasterObserver.observe(resolveScope(), {
264
+ childList: true,
265
+ subtree: true
266
+ });
267
+ }
226
268
  dispatch({
227
269
  type: LiveChatWidgetActionType.SET_RENDERING_MIDDLEWARE_PROPS,
228
270
  payload: webChatContainerProps === null || webChatContainerProps === void 0 ? void 0 : webChatContainerProps.renderingMiddlewareProps
@@ -247,6 +289,10 @@ export const WebChatContainerStateful = props => {
247
289
  }
248
290
  }
249
291
  }
292
+ return () => {
293
+ var _toasterObserver2;
294
+ (_toasterObserver2 = toasterObserver) === null || _toasterObserver2 === void 0 ? void 0 : _toasterObserver2.disconnect();
295
+ };
250
296
  }, []);
251
297
  useEffect(() => {
252
298
  var _props$webChatContain6, _props$webChatContain7;
@@ -487,23 +533,7 @@ export const WebChatContainerStateful = props => {
487
533
  height: "100%",
488
534
  width: "100%"
489
535
  }
490
- }, shouldLoadPersistentHistoryMessages && /*#__PURE__*/React.createElement(WebChatEventSubscribers, null), /*#__PURE__*/React.createElement(BasicWebChat, null))), /*#__PURE__*/React.createElement("div", {
491
- id: HtmlIdNames.fileSentAnnouncementRegionId,
492
- role: "alert",
493
- "aria-live": "assertive",
494
- "aria-atomic": "true",
495
- style: {
496
- position: "absolute",
497
- width: "1px",
498
- height: "1px",
499
- padding: "0",
500
- margin: "-1px",
501
- overflow: "hidden",
502
- clip: "rect(0, 0, 0, 0)",
503
- whiteSpace: "nowrap",
504
- border: "0"
505
- }
506
- }), citationPaneOpen && /*#__PURE__*/React.createElement(CitationPaneStateful, {
536
+ }, shouldLoadPersistentHistoryMessages && /*#__PURE__*/React.createElement(WebChatEventSubscribers, null), /*#__PURE__*/React.createElement(BasicWebChat, null))), citationPaneOpen && /*#__PURE__*/React.createElement(CitationPaneStateful, {
507
537
  id: ((_props$citationPanePr = props.citationPaneProps) === null || _props$citationPanePr === void 0 ? void 0 : _props$citationPanePr.id) || HtmlAttributeNames.ocwCitationPaneClassName,
508
538
  title: ((_props$citationPanePr2 = props.citationPaneProps) === null || _props$citationPanePr2 === void 0 ? void 0 : _props$citationPanePr2.title) || HtmlAttributeNames.ocwCitationPaneTitle,
509
539
  contentHtml: citationPaneText,
@@ -52,6 +52,16 @@ export let DesignerChatAdapter = /*#__PURE__*/function (_MockAdapter) {
52
52
  if (msg.text) {
53
53
  if (msg.suggestedActions) {
54
54
  postAgentSuggestedActionsActivity(this.activityObserver, msg.text, msg.suggestedActions, index * 100);
55
+ } else if (msg.from && msg.from.role) {
56
+ // Route caller-supplied `from` through the shared
57
+ // postBotMessageActivity helper so the activity shape
58
+ // (id, channelData.tags, timestamp, type) matches every
59
+ // other designer-mode path and downstream middleware
60
+ // (localizedStringsBotInitialsMiddleware, telemetry,
61
+ // attachment plumbing) behaves identically. The override
62
+ // narrows only the fields the caller cares about (e.g.
63
+ // from.name / from.role) — base botUser fields remain.
64
+ postBotMessageActivity(this.activityObserver, msg.text, undefined, index * 100, msg.from);
55
65
  } else {
56
66
  postBotMessageActivity(this.activityObserver, msg.text, undefined, index * 100);
57
67
  }
@@ -13,5 +13,6 @@ export const defaultWebChatContainerStatefulProps = {
13
13
  honorsTargetInHTMLLinks: false,
14
14
  hyperlinkTextOverride: false,
15
15
  directLine: new MockAdapter(),
16
- adaptiveCardStyles: defaultAdaptiveCardStyles
16
+ adaptiveCardStyles: defaultAdaptiveCardStyles,
17
+ webChatNotificationRegionAccessibilityLabel: "Chat notifications"
17
18
  };
@@ -34,11 +34,13 @@ export const postEchoActivity = function (activityObserver, activity, user) {
34
34
  export const postBotMessageActivity = function (activityObserver, text) {
35
35
  let tags = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "";
36
36
  let delay = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 1000;
37
+ let fromOverride = arguments.length > 4 ? arguments[4] : undefined;
37
38
  setTimeout(() => {
38
39
  activityObserver === null || activityObserver === void 0 ? void 0 : activityObserver.next({
39
40
  id: uuidv4(),
40
41
  from: {
41
- ...botUser
42
+ ...botUser,
43
+ ...(fromOverride ?? {})
42
44
  },
43
45
  text,
44
46
  type: "message",
@@ -25,6 +25,21 @@ const AdaptiveCardAccessibilityWrapper = _ref => {
25
25
  const container = containerRef.current;
26
26
  if (!container) return;
27
27
 
28
+ // dropdown-double-label: compact Input.ChoiceSet renders as a native
29
+ // <select> with both aria-labelledby and a sibling visible <label for>.
30
+ // Only remove aria-labelledby when it points solely at that same visible
31
+ // label; preserve composite labels that include required/error/context text.
32
+ const compactSelects = container.querySelectorAll(".ac-input-container select.ac-input.ac-multichoiceInput[aria-labelledby]");
33
+ compactSelects.forEach(select => {
34
+ const id = select.id;
35
+ if (!id) return;
36
+ const visibleLabel = container.querySelector(`label[for="${CSS.escape(id)}"]:not([aria-hidden='true'])`);
37
+ const labelledBy = (select.getAttribute("aria-labelledby") || "").trim().split(/\s+/);
38
+ if (visibleLabel !== null && visibleLabel !== void 0 && visibleLabel.id && labelledBy.length === 1 && labelledBy[0] === visibleLabel.id) {
39
+ select.removeAttribute("aria-labelledby");
40
+ }
41
+ });
42
+
28
43
  // action-button-toggle / login-button-toggle: submit/login action buttons can be
29
44
  // rendered with toggle-only ARIA that causes screen readers to announce them as
30
45
  // switches instead of plain push buttons. Preserve Action.ToggleVisibility buttons,
@@ -5,21 +5,17 @@
5
5
  ******/
6
6
 
7
7
  import { LogLevel, TelemetryEvent } from "../../../../../common/telemetry/TelemetryConstants";
8
- import { AMSConstants, HtmlIdNames } from "../../../../../common/Constants";
8
+ import { AMSConstants } from "../../../../../common/Constants";
9
9
  import { NotificationHandler } from "../../notification/NotificationHandler";
10
10
  import { NotificationScenarios } from "../../enums/NotificationScenarios";
11
11
  import { TelemetryHelper } from "../../../../../common/telemetry/TelemetryHelper";
12
12
  import { WebChatActionType } from "../../enums/WebChatActionType";
13
13
  const MBtoBRatio = 1000000;
14
14
  const announceFileSent = message => {
15
- // TalkBack on Android WebView reliably announces newly *appended* role="alert"
16
- // nodes but often misses text-content updates on existing nodes.
17
- // Strategy: clear the static region, wait 500ms for the send-box focus shift
18
- // to settle, then inject a fresh alert element and remove it after 3s.
19
- const region = document.getElementById(HtmlIdNames.fileSentAnnouncementRegionId);
20
- if (region) {
21
- region.textContent = "";
22
- }
15
+ // TalkBack on Android WebView reliably announces newly *appended*
16
+ // role="alert" nodes but often misses text-content updates on existing
17
+ // nodes. Wait 500ms for the send-box focus shift to settle, then inject
18
+ // a fresh alert element appended to document.body and remove it after 3s.
23
19
  setTimeout(() => {
24
20
  const el = document.createElement("div");
25
21
  el.setAttribute("role", "alert");
@@ -119,7 +119,6 @@ export declare const Regex: {
119
119
  };
120
120
  export declare class HtmlIdNames {
121
121
  static readonly MSLiveChatWidget = "MSLiveChatWidget";
122
- static readonly fileSentAnnouncementRegionId = "ms_lcw_file_sent_announcement";
123
122
  }
124
123
  export declare class HtmlClassNames {
125
124
  static readonly webChatBannerCloseButton = "webchat__toast__dismissButton";
@@ -4,7 +4,7 @@ export declare const customerUser: User;
4
4
  export declare const botUser: User;
5
5
  export declare const agentUser: User;
6
6
  export declare const postEchoActivity: (activityObserver: Subscriber<Activity> | undefined, activity: Message, user: User, delay?: number) => void;
7
- export declare const postBotMessageActivity: (activityObserver: Subscriber<Activity> | undefined, text: string, tags?: string, delay?: number) => void;
7
+ export declare const postBotMessageActivity: (activityObserver: Subscriber<Activity> | undefined, text: string, tags?: string, delay?: number, fromOverride?: Partial<User>) => void;
8
8
  export declare const postAgentMessageActivity: (activityObserver: Subscriber<Activity> | undefined, text: string, tags?: string, delay?: number) => void;
9
9
  export declare const postSystemMessageActivity: (activityObserver: Subscriber<Activity> | undefined, text: string, delay?: number) => void;
10
10
  export declare const postBotTypingActivity: (activityObserver: Subscriber<Activity> | undefined, delay?: number) => void;
@@ -25,4 +25,11 @@ export interface IWebChatContainerStatefulProps {
25
25
  adaptiveCardStyles?: IAdaptiveCardStyles;
26
26
  sendBoxTextBox?: ISendBox;
27
27
  webChatHistoryMobileAccessibilityLabel?: string;
28
+ /**
29
+ * Accessible name applied to WebChats notification toaster region
30
+ * (`role="log"`). When omitted, defaults to "Chat notifications" so
31
+ * screen readers don't announce the empty live region as "blank"
32
+ * (internal tracking).
33
+ */
34
+ webChatNotificationRegionAccessibilityLabel?: string;
28
35
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@microsoft/omnichannel-chat-widget",
3
- "version": "1.8.4-main.2e8be66",
3
+ "version": "1.8.4-main.319d589",
4
4
  "description": "Microsoft Omnichannel Chat Widget",
5
5
  "main": "lib/cjs/index.js",
6
6
  "types": "lib/types/index.d.ts",