@microsoft/omnichannel-chat-widget 1.8.4-main.e0c2625 → 1.8.4-main.f45c44b
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.
- package/README.md +1 -0
- package/lib/cjs/common/Constants.js +0 -1
- package/lib/cjs/common/utils.js +36 -1
- package/lib/cjs/components/citationpanestateful/CitationPaneStateful.js +7 -1
- package/lib/cjs/components/confirmationpanestateful/ConfirmationPaneStateful.js +11 -1
- package/lib/cjs/components/emailtranscriptpanestateful/EmailTranscriptPaneStateful.js +35 -13
- package/lib/cjs/components/livechatwidget/common/createMarkdown.js +96 -5
- package/lib/cjs/components/postchatloadingpanestateful/PostChatLoadingPaneStateful.js +15 -3
- package/lib/cjs/components/postchatsurveypanestateful/PostChatSurveyPaneStateful.js +1 -0
- package/lib/cjs/components/prechatsurveypanestateful/PreChatSurveyPaneStateful.js +64 -2
- package/lib/cjs/components/webchatcontainerstateful/WebChatContainerStateful.js +98 -17
- package/lib/cjs/components/webchatcontainerstateful/common/DesignerChatAdapter.js +10 -0
- package/lib/cjs/components/webchatcontainerstateful/common/defaultProps/defaultMiddlewareLocalizedTexts.js +2 -0
- package/lib/cjs/components/webchatcontainerstateful/common/defaultProps/defaultWebChatContainerStatefulProps.js +2 -1
- package/lib/cjs/components/webchatcontainerstateful/common/utils/chatAdapterUtils.js +3 -1
- package/lib/cjs/components/webchatcontainerstateful/common/utils/citationA11y.js +195 -0
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/attachments/AdaptiveCardAccessibilityWrapper.js +65 -0
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/attachmentUploadValidatorMiddleware.js +4 -8
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/localizedStringsBotInitialsMiddleware.js +43 -5
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/notification/NotificationHandler.js +55 -1
- package/lib/cjs/contexts/common/LiveChatWidgetContextInitialState.js +11 -0
- package/lib/esm/common/Constants.js +0 -1
- package/lib/esm/common/utils.js +34 -0
- package/lib/esm/components/citationpanestateful/CitationPaneStateful.js +7 -1
- package/lib/esm/components/confirmationpanestateful/ConfirmationPaneStateful.js +11 -1
- package/lib/esm/components/emailtranscriptpanestateful/EmailTranscriptPaneStateful.js +36 -14
- package/lib/esm/components/livechatwidget/common/createMarkdown.js +96 -5
- package/lib/esm/components/postchatloadingpanestateful/PostChatLoadingPaneStateful.js +16 -4
- package/lib/esm/components/postchatsurveypanestateful/PostChatSurveyPaneStateful.js +1 -0
- package/lib/esm/components/prechatsurveypanestateful/PreChatSurveyPaneStateful.js +65 -3
- package/lib/esm/components/webchatcontainerstateful/WebChatContainerStateful.js +99 -18
- package/lib/esm/components/webchatcontainerstateful/common/DesignerChatAdapter.js +10 -0
- package/lib/esm/components/webchatcontainerstateful/common/defaultProps/defaultMiddlewareLocalizedTexts.js +2 -0
- package/lib/esm/components/webchatcontainerstateful/common/defaultProps/defaultWebChatContainerStatefulProps.js +2 -1
- package/lib/esm/components/webchatcontainerstateful/common/utils/chatAdapterUtils.js +3 -1
- package/lib/esm/components/webchatcontainerstateful/common/utils/citationA11y.js +188 -0
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/attachments/AdaptiveCardAccessibilityWrapper.js +65 -0
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/attachmentUploadValidatorMiddleware.js +5 -9
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/localizedStringsBotInitialsMiddleware.js +43 -5
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/notification/NotificationHandler.js +55 -1
- package/lib/esm/contexts/common/LiveChatWidgetContextInitialState.js +11 -0
- package/lib/types/common/Constants.d.ts +0 -1
- package/lib/types/common/utils.d.ts +1 -0
- package/lib/types/components/webchatcontainerstateful/common/utils/chatAdapterUtils.d.ts +1 -1
- package/lib/types/components/webchatcontainerstateful/common/utils/citationA11y.d.ts +1 -0
- package/lib/types/components/webchatcontainerstateful/interfaces/IWebChatContainerStatefulProps.d.ts +7 -0
- package/lib/types/components/webchatcontainerstateful/webchatcontroller/notification/NotificationHandler.d.ts +7 -0
- package/lib/types/contexts/common/ILiveChatWidgetLocalizedTexts.d.ts +12 -0
- package/package.json +24 -6
package/README.md
CHANGED
|
@@ -311,6 +311,7 @@ BroadcastService.getMessageByEventName("CloseChat").subscribe(async (msg) => {
|
|
|
311
311
|
[Create LiveChatWidget with Webpack5 and TypeScript](https://github.com/microsoft/omnichannel-chat-widget/blob/main/docs/BuildingUsingWebpack5.md)\
|
|
312
312
|
[Omnichannel Features](https://github.com/microsoft/omnichannel-chat-widget/blob/main/docs/Features.md)\
|
|
313
313
|
[How to Add Visual Regression Tests](https://github.com/microsoft/omnichannel-chat-widget/blob/main/docs/VisualRegressionTestingGuide.md)\
|
|
314
|
+
[Accessibility Tooling](https://github.com/microsoft/omnichannel-chat-widget/blob/main/docs/accessibility/README.md)\
|
|
314
315
|
[Security](https://github.com/microsoft/omnichannel-chat-widget/blob/main/SECURITY.md)\
|
|
315
316
|
[Third Party Cookie Support](https://github.com/microsoft/omnichannel-chat-widget/blob/main/docs/Tpc.md)\
|
|
316
317
|
[Storybook](https://microsoft.github.io/omnichannel-chat-widget/docs/storybook/)
|
|
@@ -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
|
});
|
package/lib/cjs/common/utils.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.getCustomEventValue = exports.getConversationDetailsCall = exports.getBroadcastChannelName = exports.formatTemplateString = exports.findParentFocusableElementsWithoutChildContainer = exports.findAllFocusableElement = exports.extractPreChatSurveyResponseValues = exports.escapeHtml = exports.debounceLeading = exports.createTimer = exports.createFileAndDownload = exports.checkContactIdError = exports.changeLanguageCodeFormatForWebChat = exports.addDelayInMs = void 0;
|
|
6
|
+
exports.getCustomEventValue = exports.getConversationDetailsCall = exports.getBroadcastChannelName = exports.formatTemplateString = exports.findParentFocusableElementsWithoutChildContainer = exports.findAllFocusableElement = exports.extractPreChatSurveyResponseValues = exports.escapeHtml = exports.debounceLeading = exports.createTimer = exports.createFileAndDownload = exports.checkContactIdError = exports.changeLanguageCodeFormatForWebChat = exports.announceMessageImmediately = exports.addDelayInMs = void 0;
|
|
7
7
|
exports.getDeviceType = getDeviceType;
|
|
8
8
|
exports.getWidgetEndChatEventName = exports.getWidgetCacheIdfromProps = exports.getWidgetCacheId = exports.getTimestampHourMinute = exports.getStateFromCache = exports.getLocaleDirection = exports.getIconText = exports.getDomain = void 0;
|
|
9
9
|
exports.isEndConversationDueToOverflowActivity = isEndConversationDueToOverflowActivity;
|
|
@@ -158,6 +158,41 @@ const setFocusOnElement = selector => {
|
|
|
158
158
|
element === null || element === void 0 ? void 0 : element.focus();
|
|
159
159
|
};
|
|
160
160
|
exports.setFocusOnElement = setFocusOnElement;
|
|
161
|
+
const IMMEDIATE_ANNOUNCEMENT_REGION_ID = "oc-lcw-immediate-announcement";
|
|
162
|
+
|
|
163
|
+
// Announces a message to screen readers via an aria-live="assertive" region
|
|
164
|
+
// attached to document.body — outside the chat widget's DOM subtree — so the
|
|
165
|
+
// screen reader does not have to traverse chat content to reach it.
|
|
166
|
+
const announceMessageImmediately = message => {
|
|
167
|
+
if (!message || typeof document === "undefined") {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
let region = document.getElementById(IMMEDIATE_ANNOUNCEMENT_REGION_ID);
|
|
171
|
+
if (!region) {
|
|
172
|
+
region = document.createElement("div");
|
|
173
|
+
region.id = IMMEDIATE_ANNOUNCEMENT_REGION_ID;
|
|
174
|
+
region.setAttribute("aria-live", "assertive");
|
|
175
|
+
region.setAttribute("role", "alert");
|
|
176
|
+
region.setAttribute("aria-atomic", "true");
|
|
177
|
+
region.style.position = "absolute";
|
|
178
|
+
region.style.width = "1px";
|
|
179
|
+
region.style.height = "1px";
|
|
180
|
+
region.style.overflow = "hidden";
|
|
181
|
+
region.style.clip = "rect(0 0 0 0)";
|
|
182
|
+
region.style.clipPath = "inset(50%)";
|
|
183
|
+
region.style.whiteSpace = "nowrap";
|
|
184
|
+
document.body.appendChild(region);
|
|
185
|
+
}
|
|
186
|
+
region.textContent = "";
|
|
187
|
+
// Re-set on the next tick so screen readers detect the change even when
|
|
188
|
+
// the same message is announced twice in a row.
|
|
189
|
+
setTimeout(() => {
|
|
190
|
+
if (region) {
|
|
191
|
+
region.textContent = message;
|
|
192
|
+
}
|
|
193
|
+
}, 50);
|
|
194
|
+
};
|
|
195
|
+
exports.announceMessageImmediately = announceMessageImmediately;
|
|
161
196
|
const escapeHtml = inputString => {
|
|
162
197
|
const entityMap = {
|
|
163
198
|
"<": "<",
|
|
@@ -62,7 +62,13 @@ const CitationPaneStateful = props => {
|
|
|
62
62
|
Event: _TelemetryConstants.TelemetryEvent.UXCitationPaneCompleted,
|
|
63
63
|
ElapsedTimeInMilliseconds: uiTimer.milliSecondsElapsed
|
|
64
64
|
});
|
|
65
|
-
return
|
|
65
|
+
return () => {
|
|
66
|
+
// internal tracking: restore parent tab indices on unmount so focus
|
|
67
|
+
// can escape the widget even if the pane is dismissed by an
|
|
68
|
+
// external state change rather than a user button click.
|
|
69
|
+
(0, _utils.setTabIndices)(elements, initialTabIndexMap, true);
|
|
70
|
+
cleanup();
|
|
71
|
+
};
|
|
66
72
|
}, []);
|
|
67
73
|
|
|
68
74
|
// Retry focus once pane is actually visible (isReady) in case initial attempt occurred while wrapper was visibility:hidden
|
|
@@ -100,7 +100,17 @@ const ConfirmationPaneStateful = props => {
|
|
|
100
100
|
Event: _TelemetryConstants.TelemetryEvent.UXConfirmationPaneCompleted,
|
|
101
101
|
ElapsedTimeInMilliseconds: uiTimer.milliSecondsElapsed
|
|
102
102
|
});
|
|
103
|
-
return
|
|
103
|
+
return () => {
|
|
104
|
+
// internal tracking: when the confirmation pane unmounts (page reload,
|
|
105
|
+
// parent state change, or any path that bypasses onConfirm /
|
|
106
|
+
// onCancel) the tab indices that were forced to -1 on sibling
|
|
107
|
+
// focusable elements must be restored, otherwise Tab order
|
|
108
|
+
// remains broken and focus can be trapped inside the widget.
|
|
109
|
+
// setTabIndices is a no-op when initialTabIndexMap is already
|
|
110
|
+
// empty (onConfirm / onCancel already restored).
|
|
111
|
+
(0, _utils.setTabIndices)(elements, initialTabIndexMap, true);
|
|
112
|
+
cleanup();
|
|
113
|
+
};
|
|
104
114
|
}, []);
|
|
105
115
|
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_DimLayer.DimLayer, {
|
|
106
116
|
brightness: (controlProps === null || controlProps === void 0 ? void 0 : controlProps.brightnessValueOnDim) ?? "0.2"
|
|
@@ -35,16 +35,23 @@ const EmailTranscriptPaneStateful = props => {
|
|
|
35
35
|
const [state, dispatch] = (0, _useChatContextStore.default)();
|
|
36
36
|
const [facadeChatSDK] = (0, _useFacadeChatSDKStore.default)();
|
|
37
37
|
const [initialEmail, setInitialEmail] = (0, _react.useState)("");
|
|
38
|
-
|
|
38
|
+
// restoreFocus=false is used on the submit path: the notification banner
|
|
39
|
+
// (success or error) takes focus via NotificationHandler.setFocusOnNotificationCloseButton,
|
|
40
|
+
// so an intermediate restore to the chat-widget shell would otherwise cause SRs to
|
|
41
|
+
// announce "Enter <widget>" in between the dialog and the banner.
|
|
42
|
+
const closeEmailTranscriptPane = function () {
|
|
43
|
+
let restoreFocus = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
|
|
39
44
|
dispatch({
|
|
40
45
|
type: _LiveChatWidgetActionType.LiveChatWidgetActionType.SET_SHOW_EMAIL_TRANSCRIPT_PANE,
|
|
41
46
|
payload: false
|
|
42
47
|
});
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
if (restoreFocus) {
|
|
49
|
+
const previousFocusedElementId = state.appStates.previousElementIdOnFocusBeforeModalOpen;
|
|
50
|
+
if (previousFocusedElementId) {
|
|
51
|
+
(0, _utils.setFocusOnElement)("#" + previousFocusedElementId);
|
|
52
|
+
} else {
|
|
53
|
+
(0, _utils.setFocusOnSendBox)();
|
|
54
|
+
}
|
|
48
55
|
}
|
|
49
56
|
dispatch({
|
|
50
57
|
type: _LiveChatWidgetActionType.LiveChatWidgetActionType.SET_PREVIOUS_FOCUSED_ELEMENT_ID,
|
|
@@ -55,23 +62,31 @@ const EmailTranscriptPaneStateful = props => {
|
|
|
55
62
|
const onSend = (0, _react.useCallback)(async email => {
|
|
56
63
|
var _state$domainStates;
|
|
57
64
|
const liveChatContext = state === null || state === void 0 ? void 0 : (_state$domainStates = state.domainStates) === null || _state$domainStates === void 0 ? void 0 : _state$domainStates.liveChatContext;
|
|
58
|
-
closeEmailTranscriptPane();
|
|
65
|
+
closeEmailTranscriptPane(false);
|
|
59
66
|
const chatTranscriptBody = {
|
|
60
67
|
emailAddress: email,
|
|
61
68
|
attachmentMessage: (props === null || props === void 0 ? void 0 : props.attachmentMessage) ?? "The following attachment was uploaded during the conversation:"
|
|
62
69
|
};
|
|
63
70
|
try {
|
|
64
|
-
var _state$domainStates2, _state$domainStates2$;
|
|
71
|
+
var _state$domainStates2, _state$domainStates2$, _state$domainStates3, _state$domainStates3$;
|
|
65
72
|
await (facadeChatSDK === null || facadeChatSDK === void 0 ? void 0 : facadeChatSDK.emailLiveChatTranscript(chatTranscriptBody, {
|
|
66
73
|
liveChatContext
|
|
67
74
|
}));
|
|
68
|
-
|
|
75
|
+
const successMessage = (state === null || state === void 0 ? void 0 : (_state$domainStates2 = state.domainStates) === null || _state$domainStates2 === void 0 ? void 0 : (_state$domainStates2$ = _state$domainStates2.middlewareLocalizedTexts) === null || _state$domainStates2$ === void 0 ? void 0 : _state$domainStates2$.MIDDLEWARE_BANNER_FILE_EMAIL_ADDRESS_RECORDED_SUCCESS) ?? (_defaultMiddlewareLocalizedTexts.defaultMiddlewareLocalizedTexts === null || _defaultMiddlewareLocalizedTexts.defaultMiddlewareLocalizedTexts === void 0 ? void 0 : _defaultMiddlewareLocalizedTexts.defaultMiddlewareLocalizedTexts.MIDDLEWARE_BANNER_FILE_EMAIL_ADDRESS_RECORDED_SUCCESS);
|
|
76
|
+
// Announce from a live region at document.body level so the
|
|
77
|
+
// screen reader speaks the confirmation immediately, without
|
|
78
|
+
// traversing the chat transcript on the way to the banner.
|
|
79
|
+
// Prefix with the explicit state word so SR users hear the outcome
|
|
80
|
+
// (visual users already see a success icon on the banner).
|
|
81
|
+
const successSrPrefix = (state === null || state === void 0 ? void 0 : (_state$domainStates3 = state.domainStates) === null || _state$domainStates3 === void 0 ? void 0 : (_state$domainStates3$ = _state$domainStates3.middlewareLocalizedTexts) === null || _state$domainStates3$ === void 0 ? void 0 : _state$domainStates3$.MIDDLEWARE_SR_PREFIX_SUCCESS) ?? _defaultMiddlewareLocalizedTexts.defaultMiddlewareLocalizedTexts.MIDDLEWARE_SR_PREFIX_SUCCESS;
|
|
82
|
+
(0, _utils.announceMessageImmediately)(`${successSrPrefix}${successMessage}`);
|
|
83
|
+
_NotificationHandler.NotificationHandler.notifySuccess(_NotificationScenarios.NotificationScenarios.EmailAddressSaved, successMessage);
|
|
69
84
|
_TelemetryHelper.TelemetryHelper.logActionEventToAllTelemetry(_TelemetryConstants.LogLevel.INFO, {
|
|
70
85
|
Event: _TelemetryConstants.TelemetryEvent.EmailTranscriptSent,
|
|
71
86
|
Description: "Transcript sent to email successfully."
|
|
72
87
|
});
|
|
73
88
|
} catch (ex) {
|
|
74
|
-
var _state$
|
|
89
|
+
var _state$domainStates4, _state$domainStates4$, _state$domainStates5, _state$domainStates5$;
|
|
75
90
|
_TelemetryHelper.TelemetryHelper.logActionEventToAllTelemetry(_TelemetryConstants.LogLevel.ERROR, {
|
|
76
91
|
Event: _TelemetryConstants.TelemetryEvent.EmailTranscriptFailed,
|
|
77
92
|
Description: "Email transcript failed.",
|
|
@@ -79,8 +94,11 @@ const EmailTranscriptPaneStateful = props => {
|
|
|
79
94
|
exception: ex
|
|
80
95
|
}
|
|
81
96
|
});
|
|
82
|
-
const message = (0, _utils.formatTemplateString)((state === null || state === void 0 ? void 0 : (_state$
|
|
83
|
-
|
|
97
|
+
const message = (0, _utils.formatTemplateString)((state === null || state === void 0 ? void 0 : (_state$domainStates4 = state.domainStates) === null || _state$domainStates4 === void 0 ? void 0 : (_state$domainStates4$ = _state$domainStates4.middlewareLocalizedTexts) === null || _state$domainStates4$ === void 0 ? void 0 : _state$domainStates4$.MIDDLEWARE_BANNER_FILE_EMAIL_ADDRESS_RECORDED_ERROR) ?? _defaultMiddlewareLocalizedTexts.defaultMiddlewareLocalizedTexts.MIDDLEWARE_BANNER_FILE_EMAIL_ADDRESS_RECORDED_ERROR, [email]);
|
|
98
|
+
const bannerMessage = (props === null || props === void 0 ? void 0 : props.bannerMessageOnError) ?? message;
|
|
99
|
+
const errorSrPrefix = (state === null || state === void 0 ? void 0 : (_state$domainStates5 = state.domainStates) === null || _state$domainStates5 === void 0 ? void 0 : (_state$domainStates5$ = _state$domainStates5.middlewareLocalizedTexts) === null || _state$domainStates5$ === void 0 ? void 0 : _state$domainStates5$.MIDDLEWARE_SR_PREFIX_ERROR) ?? _defaultMiddlewareLocalizedTexts.defaultMiddlewareLocalizedTexts.MIDDLEWARE_SR_PREFIX_ERROR;
|
|
100
|
+
(0, _utils.announceMessageImmediately)(`${errorSrPrefix}${bannerMessage}`);
|
|
101
|
+
_NotificationHandler.NotificationHandler.notifyError(_NotificationScenarios.NotificationScenarios.EmailTranscriptError, bannerMessage);
|
|
84
102
|
}
|
|
85
103
|
}, [props.attachmentMessage, props.bannerMessageOnError, facadeChatSDK, state.domainStates.liveChatContext]);
|
|
86
104
|
const controlProps = {
|
|
@@ -118,7 +136,11 @@ const EmailTranscriptPaneStateful = props => {
|
|
|
118
136
|
Event: _TelemetryConstants.TelemetryEvent.UXEmailTranscriptPaneCompleted,
|
|
119
137
|
ElapsedTimeInMilliseconds: uiTimer.milliSecondsElapsed
|
|
120
138
|
});
|
|
121
|
-
return
|
|
139
|
+
return () => {
|
|
140
|
+
// internal tracking: restore parent tab indices on unmount.
|
|
141
|
+
(0, _utils.setTabIndices)(elements, initialTabIndexMap, true);
|
|
142
|
+
cleanup();
|
|
143
|
+
};
|
|
122
144
|
}, [initialEmail]);
|
|
123
145
|
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_DimLayer.DimLayer, {
|
|
124
146
|
brightness: (controlProps === null || controlProps === void 0 ? void 0 : controlProps.brightnessValueOnDim) ?? "0.2"
|
|
@@ -42,10 +42,8 @@ const createMarkdown = (disableMarkdownMessageFormatting, disableNewLineMarkdown
|
|
|
42
42
|
|
|
43
43
|
markdown.disable(["strikethrough"]);
|
|
44
44
|
|
|
45
|
-
// Custom plugin to fix numbered list continuity
|
|
45
|
+
// Custom plugin to fix numbered list continuity and merge adjacent markdown links with the same href.
|
|
46
46
|
markdown.use(function (md) {
|
|
47
|
-
const originalRender = md.render.bind(md);
|
|
48
|
-
const originalRenderInline = md.renderInline.bind(md);
|
|
49
47
|
function preprocessText(text) {
|
|
50
48
|
// Handle numbered lists that come with double line breaks (knowledge article format)
|
|
51
49
|
// This ensures proper continuous numbering instead of separate lists
|
|
@@ -80,16 +78,109 @@ const createMarkdown = (disableMarkdownMessageFormatting, disableNewLineMarkdown
|
|
|
80
78
|
return result;
|
|
81
79
|
}
|
|
82
80
|
|
|
81
|
+
// Accessibility fix: when a bot emits content like "[1.](url) [View details](url)",
|
|
82
|
+
// markdown-it renders two sibling links that screen readers announce as two separate
|
|
83
|
+
// focusable links. Merge consecutive markdown link tokens with identical attributes so
|
|
84
|
+
// the number and label form one combined focusable link without discarding metadata.
|
|
85
|
+
md.core.ruler.after("inline", "merge_adjacent_same_href_links", function (state) {
|
|
86
|
+
const sortedAttrs = token => (token.attrs || []).map(attr => `${attr[0]}=${attr[1]}`).sort();
|
|
87
|
+
const hasSameAttributes = (first, second) => {
|
|
88
|
+
const firstAttrs = sortedAttrs(first);
|
|
89
|
+
const secondAttrs = sortedAttrs(second);
|
|
90
|
+
return firstAttrs.length === secondAttrs.length && firstAttrs.every((attr, index) => attr === secondAttrs[index]);
|
|
91
|
+
};
|
|
92
|
+
const collectLink = (children, index) => {
|
|
93
|
+
var _open$attrGet;
|
|
94
|
+
const open = children[index];
|
|
95
|
+
if (!open || open.type !== "link_open") {
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
const href = (_open$attrGet = open.attrGet) === null || _open$attrGet === void 0 ? void 0 : _open$attrGet.call(open, "href");
|
|
99
|
+
if (!href) {
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
let depth = 0;
|
|
103
|
+
for (let currentIndex = index; currentIndex < children.length; currentIndex++) {
|
|
104
|
+
const token = children[currentIndex];
|
|
105
|
+
if (token.type === "link_open") {
|
|
106
|
+
depth++;
|
|
107
|
+
} else if (token.type === "link_close") {
|
|
108
|
+
depth--;
|
|
109
|
+
if (depth === 0) {
|
|
110
|
+
return {
|
|
111
|
+
closeIndex: currentIndex,
|
|
112
|
+
href,
|
|
113
|
+
open
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return undefined;
|
|
119
|
+
};
|
|
120
|
+
const getNextAdjacentLink = (children, startIndex) => {
|
|
121
|
+
const separatorTokens = [];
|
|
122
|
+
let index = startIndex;
|
|
123
|
+
while (index < children.length && children[index].type === "text" && /^\s*$/.test(children[index].content || "")) {
|
|
124
|
+
separatorTokens.push(children[index]);
|
|
125
|
+
index++;
|
|
126
|
+
}
|
|
127
|
+
const link = collectLink(children, index);
|
|
128
|
+
if (!link) {
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
...link,
|
|
133
|
+
openIndex: index,
|
|
134
|
+
separatorTokens
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
state.tokens.forEach(blockToken => {
|
|
138
|
+
if (blockToken.type !== "inline" || !blockToken.children) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
const mergedChildren = [];
|
|
142
|
+
const children = blockToken.children;
|
|
143
|
+
let index = 0;
|
|
144
|
+
while (index < children.length) {
|
|
145
|
+
const firstLink = collectLink(children, index);
|
|
146
|
+
if (!firstLink) {
|
|
147
|
+
mergedChildren.push(children[index]);
|
|
148
|
+
index++;
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
const linkTokens = [firstLink.open, ...children.slice(index + 1, firstLink.closeIndex)];
|
|
152
|
+
let nextIndex = firstLink.closeIndex + 1;
|
|
153
|
+
let mergedAnyLink = false;
|
|
154
|
+
let nextLink = getNextAdjacentLink(children, nextIndex);
|
|
155
|
+
while (nextLink && nextLink.href === firstLink.href && hasSameAttributes(firstLink.open, nextLink.open)) {
|
|
156
|
+
linkTokens.push(...nextLink.separatorTokens);
|
|
157
|
+
linkTokens.push(...children.slice(nextLink.openIndex + 1, nextLink.closeIndex));
|
|
158
|
+
nextIndex = nextLink.closeIndex + 1;
|
|
159
|
+
mergedAnyLink = true;
|
|
160
|
+
nextLink = getNextAdjacentLink(children, nextIndex);
|
|
161
|
+
}
|
|
162
|
+
if (mergedAnyLink) {
|
|
163
|
+
mergedChildren.push(...linkTokens, children[firstLink.closeIndex]);
|
|
164
|
+
index = nextIndex;
|
|
165
|
+
} else {
|
|
166
|
+
mergedChildren.push(children[index]);
|
|
167
|
+
index++;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
blockToken.children = mergedChildren;
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
83
174
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
84
175
|
md.render = function (text, env) {
|
|
85
176
|
const processedText = preprocessText(text);
|
|
86
|
-
return
|
|
177
|
+
return md.renderer.render(md.parse(processedText, env), md.options, env);
|
|
87
178
|
};
|
|
88
179
|
|
|
89
180
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
90
181
|
md.renderInline = function (text, env) {
|
|
91
182
|
const processedText = preprocessText(text);
|
|
92
|
-
return
|
|
183
|
+
return md.renderer.render(md.parseInline(processedText, env), md.options, env);
|
|
93
184
|
};
|
|
94
185
|
});
|
|
95
186
|
|
|
@@ -16,13 +16,22 @@ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "functio
|
|
|
16
16
|
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
17
17
|
let uiTimer;
|
|
18
18
|
const PostChatLoadingPaneStateful = props => {
|
|
19
|
-
var _props$styleProps;
|
|
19
|
+
var _props$controlProps, _props$styleProps;
|
|
20
|
+
const defaultSubtitleText = "Please take a moment to give us feedback about your chat experience. We are loading the survey for you now.";
|
|
21
|
+
const subtitleText = ((_props$controlProps = props.controlProps) === null || _props$controlProps === void 0 ? void 0 : _props$controlProps.subtitleText) ?? defaultSubtitleText;
|
|
22
|
+
const [announcedSubtitleText, setAnnouncedSubtitleText] = (0, _react.useState)("");
|
|
20
23
|
(0, _react.useEffect)(() => {
|
|
21
24
|
uiTimer = (0, _utils.createTimer)();
|
|
22
25
|
_TelemetryHelper.TelemetryHelper.logLoadingEvent(_TelemetryConstants.LogLevel.INFO, {
|
|
23
26
|
Event: _TelemetryConstants.TelemetryEvent.UXPostChatLoadingPaneStart
|
|
24
27
|
});
|
|
25
28
|
}, []);
|
|
29
|
+
(0, _react.useEffect)(() => {
|
|
30
|
+
const timeout = window.setTimeout(() => {
|
|
31
|
+
setAnnouncedSubtitleText(subtitleText);
|
|
32
|
+
}, 0);
|
|
33
|
+
return () => window.clearTimeout(timeout);
|
|
34
|
+
}, [subtitleText]);
|
|
26
35
|
const [state] = (0, _useChatContextStore.default)();
|
|
27
36
|
const generalStyleProps = Object.assign({}, _defaultgeneralPostChatLoadingPaneStyleProps.defaultGeneralPostChatLoadingPaneStyleProps, (_props$styleProps = props.styleProps) === null || _props$styleProps === void 0 ? void 0 : _props$styleProps.generalStyleProps);
|
|
28
37
|
const styleProps = {
|
|
@@ -36,8 +45,11 @@ const PostChatLoadingPaneStateful = props => {
|
|
|
36
45
|
hideTitle: true,
|
|
37
46
|
hideSpinner: true,
|
|
38
47
|
hideSpinnerText: true,
|
|
39
|
-
|
|
40
|
-
|
|
48
|
+
...props.controlProps,
|
|
49
|
+
role: "status",
|
|
50
|
+
"aria-live": "polite",
|
|
51
|
+
"aria-atomic": "true",
|
|
52
|
+
subtitleText: announcedSubtitleText
|
|
41
53
|
};
|
|
42
54
|
|
|
43
55
|
// Move focus to the first button
|
|
@@ -67,6 +67,7 @@ const PostChatSurveyPaneStateful = props => {
|
|
|
67
67
|
};
|
|
68
68
|
const controlProps = {
|
|
69
69
|
id: "oc-lcw-postchatsurvey-pane",
|
|
70
|
+
title: "Post-chat survey",
|
|
70
71
|
surveyURL: ((_props$controlProps = props.controlProps) === null || _props$controlProps === void 0 ? void 0 : _props$controlProps.surveyURL) ?? surveyInviteLink,
|
|
71
72
|
...props.controlProps
|
|
72
73
|
};
|
|
@@ -20,6 +20,34 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
20
20
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
21
21
|
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
22
22
|
let uiTimer;
|
|
23
|
+
const getFocusedElementAnnouncement = element => {
|
|
24
|
+
var _element$textContent;
|
|
25
|
+
const ariaLabel = element.getAttribute(_Constants.HtmlAttributeNames.ariaLabel);
|
|
26
|
+
if (ariaLabel) {
|
|
27
|
+
return ariaLabel.trim();
|
|
28
|
+
}
|
|
29
|
+
const ariaLabelledBy = element.getAttribute(_Constants.HtmlAttributeNames.ariaLabelledby);
|
|
30
|
+
if (ariaLabelledBy) {
|
|
31
|
+
const labelledByText = ariaLabelledBy.split(/\s+/).map(id => {
|
|
32
|
+
var _document$getElementB, _document$getElementB2;
|
|
33
|
+
return (_document$getElementB = document.getElementById(id)) === null || _document$getElementB === void 0 ? void 0 : (_document$getElementB2 = _document$getElementB.textContent) === null || _document$getElementB2 === void 0 ? void 0 : _document$getElementB2.trim();
|
|
34
|
+
}).filter(Boolean).join(" ");
|
|
35
|
+
if (labelledByText) {
|
|
36
|
+
return labelledByText;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (element.id) {
|
|
40
|
+
const label = Array.from(document.querySelectorAll("label")).find(candidate => candidate.htmlFor === element.id || candidate.getAttribute("for") === element.id);
|
|
41
|
+
if (label !== null && label !== void 0 && label.textContent) {
|
|
42
|
+
return label.textContent.trim();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const parentLabel = element.closest("label");
|
|
46
|
+
if (parentLabel !== null && parentLabel !== void 0 && parentLabel.textContent) {
|
|
47
|
+
return parentLabel.textContent.trim();
|
|
48
|
+
}
|
|
49
|
+
return ((_element$textContent = element.textContent) === null || _element$textContent === void 0 ? void 0 : _element$textContent.trim()) ?? "";
|
|
50
|
+
};
|
|
23
51
|
|
|
24
52
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
25
53
|
const PreChatSurveyPaneStateful = props => {
|
|
@@ -42,6 +70,8 @@ const PreChatSurveyPaneStateful = props => {
|
|
|
42
70
|
surveyProps,
|
|
43
71
|
initStartChat
|
|
44
72
|
} = props;
|
|
73
|
+
const [preChatFocusAnnouncement, setPreChatFocusAnnouncement] = (0, _react.useState)("");
|
|
74
|
+
const liveRegionUpdateTimeout = (0, _react.useRef)();
|
|
45
75
|
const generalStyleProps = Object.assign({}, _defaultGeneralPreChatSurveyPaneStyleProps.defaultGeneralPreChatSurveyPaneStyleProps, surveyProps === null || surveyProps === void 0 ? void 0 : (_surveyProps$stylePro = surveyProps.styleProps) === null || _surveyProps$stylePro === void 0 ? void 0 : _surveyProps$stylePro.generalStyleProps, {
|
|
46
76
|
display: state.appStates.isMinimized ? "none" : ""
|
|
47
77
|
});
|
|
@@ -128,6 +158,16 @@ const PreChatSurveyPaneStateful = props => {
|
|
|
128
158
|
...(surveyProps === null || surveyProps === void 0 ? void 0 : surveyProps.styleProps),
|
|
129
159
|
generalStyleProps: generalStyleProps
|
|
130
160
|
};
|
|
161
|
+
const announceFocusedElement = event => {
|
|
162
|
+
if (liveRegionUpdateTimeout.current) {
|
|
163
|
+
window.clearTimeout(liveRegionUpdateTimeout.current);
|
|
164
|
+
}
|
|
165
|
+
const announcement = getFocusedElementAnnouncement(event.target);
|
|
166
|
+
setPreChatFocusAnnouncement("");
|
|
167
|
+
liveRegionUpdateTimeout.current = window.setTimeout(() => {
|
|
168
|
+
setPreChatFocusAnnouncement(announcement);
|
|
169
|
+
}, 0);
|
|
170
|
+
};
|
|
131
171
|
(0, _react.useEffect)(() => {
|
|
132
172
|
// Set Aria-Label Attribute for Inputs
|
|
133
173
|
const adaptiveCardElements = document.getElementsByClassName(_Constants.HtmlAttributeNames.adaptiveCardClassName);
|
|
@@ -175,10 +215,32 @@ const PreChatSurveyPaneStateful = props => {
|
|
|
175
215
|
}
|
|
176
216
|
}
|
|
177
217
|
}, [state.appStates.isMinimized]);
|
|
178
|
-
|
|
218
|
+
(0, _react.useEffect)(() => () => {
|
|
219
|
+
if (liveRegionUpdateTimeout.current) {
|
|
220
|
+
window.clearTimeout(liveRegionUpdateTimeout.current);
|
|
221
|
+
}
|
|
222
|
+
}, []);
|
|
223
|
+
return /*#__PURE__*/_react.default.createElement("div", {
|
|
224
|
+
onFocusCapture: announceFocusedElement
|
|
225
|
+
}, /*#__PURE__*/_react.default.createElement("div", {
|
|
226
|
+
role: "status",
|
|
227
|
+
"aria-live": "polite",
|
|
228
|
+
"aria-atomic": "true",
|
|
229
|
+
style: {
|
|
230
|
+
position: "absolute",
|
|
231
|
+
width: "1px",
|
|
232
|
+
height: "1px",
|
|
233
|
+
margin: "-1px",
|
|
234
|
+
padding: 0,
|
|
235
|
+
border: 0,
|
|
236
|
+
clip: "rect(0, 0, 0, 0)",
|
|
237
|
+
overflow: "hidden",
|
|
238
|
+
whiteSpace: "nowrap"
|
|
239
|
+
}
|
|
240
|
+
}, preChatFocusAnnouncement), /*#__PURE__*/_react.default.createElement(_omnichannelChatComponents.PreChatSurveyPane, {
|
|
179
241
|
controlProps: controlProps,
|
|
180
242
|
styleProps: styleProps
|
|
181
|
-
});
|
|
243
|
+
}));
|
|
182
244
|
};
|
|
183
245
|
exports.PreChatSurveyPaneStateful = PreChatSurveyPaneStateful;
|
|
184
246
|
var _default = PreChatSurveyPaneStateful;
|
|
@@ -31,6 +31,7 @@ var _liveChatConfigUtils = require("../livechatwidget/common/liveChatConfigUtils
|
|
|
31
31
|
var _ = require("../..");
|
|
32
32
|
var _useFacadeChatSDKStore = _interopRequireDefault(require("../../hooks/useFacadeChatSDKStore"));
|
|
33
33
|
var _usePersistentChatHistory = _interopRequireDefault(require("./hooks/usePersistentChatHistory"));
|
|
34
|
+
var _citationA11y = require("./common/utils/citationA11y");
|
|
34
35
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
35
36
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
36
37
|
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
@@ -155,6 +156,56 @@ const WebChatContainerStateful = props => {
|
|
|
155
156
|
document.addEventListener("click", clickHandler);
|
|
156
157
|
return () => document.removeEventListener("click", clickHandler);
|
|
157
158
|
}, [state]);
|
|
159
|
+
|
|
160
|
+
// Accessibility: Merge citation card's number badge and link text into a single
|
|
161
|
+
// focusable element. WebChat's LinkDefinitionItem renders an <a> containing a
|
|
162
|
+
// badge <div> (the number, e.g. "1") and a text <div> (the title). On iOS
|
|
163
|
+
// VoiceOver and Android TalkBack, those block-level descendants are otherwise
|
|
164
|
+
// announced as two separate focusable links. The patch sets a combined
|
|
165
|
+
// aria-label on the anchor and hides every descendant from the a11y tree so
|
|
166
|
+
// the whole card is announced as one link.
|
|
167
|
+
(0, _react2.useEffect)(() => {
|
|
168
|
+
const webChatRoot = document.getElementById("ms_lcw_webchat_root");
|
|
169
|
+
if (!webChatRoot) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
(0, _citationA11y.patchCitationAnchorsForA11y)(webChatRoot);
|
|
173
|
+
const observer = new MutationObserver(mutations => {
|
|
174
|
+
for (const m of mutations) {
|
|
175
|
+
if (m.type === "childList") {
|
|
176
|
+
m.addedNodes.forEach(node => {
|
|
177
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
178
|
+
var _parentElement, _parentElement$closes;
|
|
179
|
+
(0, _citationA11y.patchCitationAnchorsForA11y)(node);
|
|
180
|
+
// A child added inside an existing anchor (e.g. React
|
|
181
|
+
// mounting OpenInNewWindowIcon after the anchor) means
|
|
182
|
+
// the parent anchor needs to re-walk its descendants.
|
|
183
|
+
const ancestor = (_parentElement = node.parentElement) === null || _parentElement === void 0 ? void 0 : (_parentElement$closes = _parentElement.closest) === null || _parentElement$closes === void 0 ? void 0 : _parentElement$closes.call(_parentElement, "a.webchat__link-definitions__list-item-box");
|
|
184
|
+
if (ancestor) {
|
|
185
|
+
(0, _citationA11y.patchCitationAnchorsForA11y)(ancestor);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
} else if (m.type === "attributes" && m.target.nodeType === Node.ELEMENT_NODE) {
|
|
190
|
+
var _target$closest;
|
|
191
|
+
// React may strip our aria-hidden during a re-render; reapply
|
|
192
|
+
// by re-patching the closest matching anchor ancestor.
|
|
193
|
+
const target = m.target;
|
|
194
|
+
const ancestor = (_target$closest = target.closest) === null || _target$closest === void 0 ? void 0 : _target$closest.call(target, "a.webchat__link-definitions__list-item-box");
|
|
195
|
+
if (ancestor) {
|
|
196
|
+
(0, _citationA11y.patchCitationAnchorsForA11y)(ancestor);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
observer.observe(webChatRoot, {
|
|
202
|
+
childList: true,
|
|
203
|
+
subtree: true,
|
|
204
|
+
attributes: true,
|
|
205
|
+
attributeFilter: ["aria-hidden", "role", "tabindex", "inert"]
|
|
206
|
+
});
|
|
207
|
+
return () => observer.disconnect();
|
|
208
|
+
}, []);
|
|
158
209
|
const minimizedStyles = state.appStates.isMinimized ? shouldLoadPersistentHistoryMessages ? {
|
|
159
210
|
visibility: "hidden",
|
|
160
211
|
position: "absolute",
|
|
@@ -181,6 +232,48 @@ const WebChatContainerStateful = props => {
|
|
|
181
232
|
chatHistoryElement.setAttribute(_Constants.HtmlAttributeNames.ariaLabel, webChatContainerProps.webChatHistoryMobileAccessibilityLabel);
|
|
182
233
|
}
|
|
183
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
|
+
}
|
|
184
277
|
dispatch({
|
|
185
278
|
type: _LiveChatWidgetActionType.LiveChatWidgetActionType.SET_RENDERING_MIDDLEWARE_PROPS,
|
|
186
279
|
payload: webChatContainerProps === null || webChatContainerProps === void 0 ? void 0 : webChatContainerProps.renderingMiddlewareProps
|
|
@@ -205,6 +298,10 @@ const WebChatContainerStateful = props => {
|
|
|
205
298
|
}
|
|
206
299
|
}
|
|
207
300
|
}
|
|
301
|
+
return () => {
|
|
302
|
+
var _toasterObserver2;
|
|
303
|
+
(_toasterObserver2 = toasterObserver) === null || _toasterObserver2 === void 0 ? void 0 : _toasterObserver2.disconnect();
|
|
304
|
+
};
|
|
208
305
|
}, []);
|
|
209
306
|
(0, _react2.useEffect)(() => {
|
|
210
307
|
var _props$webChatContain6, _props$webChatContain7;
|
|
@@ -445,23 +542,7 @@ const WebChatContainerStateful = props => {
|
|
|
445
542
|
height: "100%",
|
|
446
543
|
width: "100%"
|
|
447
544
|
}
|
|
448
|
-
}, shouldLoadPersistentHistoryMessages && /*#__PURE__*/_react2.default.createElement(_WebChatEventSubscribers.default, null), /*#__PURE__*/_react2.default.createElement(BasicWebChat, null))), /*#__PURE__*/_react2.default.createElement(
|
|
449
|
-
id: _Constants.HtmlIdNames.fileSentAnnouncementRegionId,
|
|
450
|
-
role: "alert",
|
|
451
|
-
"aria-live": "assertive",
|
|
452
|
-
"aria-atomic": "true",
|
|
453
|
-
style: {
|
|
454
|
-
position: "absolute",
|
|
455
|
-
width: "1px",
|
|
456
|
-
height: "1px",
|
|
457
|
-
padding: "0",
|
|
458
|
-
margin: "-1px",
|
|
459
|
-
overflow: "hidden",
|
|
460
|
-
clip: "rect(0, 0, 0, 0)",
|
|
461
|
-
whiteSpace: "nowrap",
|
|
462
|
-
border: "0"
|
|
463
|
-
}
|
|
464
|
-
}), 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, {
|
|
465
546
|
id: ((_props$citationPanePr = props.citationPaneProps) === null || _props$citationPanePr === void 0 ? void 0 : _props$citationPanePr.id) || _Constants.HtmlAttributeNames.ocwCitationPaneClassName,
|
|
466
547
|
title: ((_props$citationPanePr2 = props.citationPaneProps) === null || _props$citationPanePr2 === void 0 ? void 0 : _props$citationPanePr2.title) || _Constants.HtmlAttributeNames.ocwCitationPaneTitle,
|
|
467
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
|
}
|