@microsoft/omnichannel-chat-widget 1.8.4-main.f6a1732 → 1.8.4-main.fba10aa
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/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/webchatcontroller/middlewares/renderingmiddlewares/attachments/AdaptiveCardAccessibilityWrapper.js +15 -0
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/localizedStringsBotInitialsMiddleware.js +43 -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/webchatcontroller/middlewares/renderingmiddlewares/attachments/AdaptiveCardAccessibilityWrapper.js +15 -0
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/localizedStringsBotInitialsMiddleware.js +43 -5
- package/package.json +4 -4
|
@@ -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;
|
|
@@ -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,
|
|
@@ -16,6 +16,32 @@ let currentAgentName = _defaultWebChatStyles.defaultWebChatStyles.botAvatarIniti
|
|
|
16
16
|
|
|
17
17
|
// Optional external updater (React context dispatch wrapper) set at runtime
|
|
18
18
|
let externalInitialsUpdater;
|
|
19
|
+
const hasTransferTag = activity => {
|
|
20
|
+
var _activity$channelData;
|
|
21
|
+
const tags = [...(Array.isArray(activity === null || activity === void 0 ? void 0 : (_activity$channelData = activity.channelData) === null || _activity$channelData === void 0 ? void 0 : _activity$channelData.tags) ? activity.channelData.tags : []), ...(Array.isArray(activity === null || activity === void 0 ? void 0 : activity.tags) ? activity.tags : [])];
|
|
22
|
+
return tags.some(tag => typeof tag === "string" && tag.toLowerCase().includes("transfer"));
|
|
23
|
+
};
|
|
24
|
+
const isTransferSystemMessage = activity => {
|
|
25
|
+
const text = ((activity === null || activity === void 0 ? void 0 : activity.text) || "").toString();
|
|
26
|
+
return hasTransferTag(activity) || /\btransfer(?:red|ring)?\b/i.test(text);
|
|
27
|
+
};
|
|
28
|
+
const resetCachedAgentName = dispatch => {
|
|
29
|
+
if (currentAgentName !== _defaultWebChatStyles.defaultWebChatStyles.botAvatarInitials || currentAgentInitials !== _defaultWebChatStyles.defaultWebChatStyles.botAvatarInitials) {
|
|
30
|
+
var _externalInitialsUpda;
|
|
31
|
+
currentAgentName = _defaultWebChatStyles.defaultWebChatStyles.botAvatarInitials;
|
|
32
|
+
currentAgentInitials = _defaultWebChatStyles.defaultWebChatStyles.botAvatarInitials;
|
|
33
|
+
(_externalInitialsUpda = externalInitialsUpdater) === null || _externalInitialsUpda === void 0 ? void 0 : _externalInitialsUpda(currentAgentInitials || "");
|
|
34
|
+
_omnichannelChatComponents.BroadcastService.postMessage({
|
|
35
|
+
eventName: "BotAvatarInitialsUpdated",
|
|
36
|
+
payload: {
|
|
37
|
+
initials: currentAgentInitials
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
dispatch({
|
|
41
|
+
type: "__BOT_INITIALS_UPDATED__"
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
};
|
|
19
45
|
const localizedStringsBotInitialsMiddleware = onInitialsChange => _ref => {
|
|
20
46
|
let {
|
|
21
47
|
dispatch
|
|
@@ -29,19 +55,31 @@ const localizedStringsBotInitialsMiddleware = onInitialsChange => _ref => {
|
|
|
29
55
|
var _action$payload, _activity$from;
|
|
30
56
|
const activity = (_action$payload = action.payload) === null || _action$payload === void 0 ? void 0 : _action$payload.activity;
|
|
31
57
|
if (activity !== null && activity !== void 0 && (_activity$from = activity.from) !== null && _activity$from !== void 0 && _activity$from.name && activity.from.role !== _Constants.Constants.userMessageTag && activity.from.name !== _Constants.Constants.userMessageTag) {
|
|
32
|
-
var _activity$
|
|
58
|
+
var _activity$channelData2, _activity$channelData3, _activity$tags;
|
|
33
59
|
const agentName = activity.from.name.trim();
|
|
34
|
-
const isSystemMessage = agentName === "__agent__" || agentName.startsWith("__") || ((_activity$
|
|
35
|
-
if (
|
|
60
|
+
const isSystemMessage = agentName === "__agent__" || agentName.startsWith("__") || ((_activity$channelData2 = activity.channelData) === null || _activity$channelData2 === void 0 ? void 0 : (_activity$channelData3 = _activity$channelData2.tags) === null || _activity$channelData3 === void 0 ? void 0 : _activity$channelData3.includes(_Constants.Constants.systemMessageTag)) || ((_activity$tags = activity.tags) === null || _activity$tags === void 0 ? void 0 : _activity$tags.includes(_Constants.Constants.systemMessageTag));
|
|
61
|
+
if (isSystemMessage) {
|
|
62
|
+
// transfer-stale-bot-name: when a transfer system message indicates the
|
|
63
|
+
// conversation has been routed to a different agent, the
|
|
64
|
+
// previously-cached agent name must NOT linger. Otherwise the
|
|
65
|
+
// next non-system activity (e.g. typing indicator, welcome
|
|
66
|
+
// message, or the new agent's first turn before their name
|
|
67
|
+
// is observed) is announced to screen readers as the OLD
|
|
68
|
+
// agent. Reset to defaults so the next observed name takes
|
|
69
|
+
// effect cleanly.
|
|
70
|
+
if (isTransferSystemMessage(activity)) {
|
|
71
|
+
resetCachedAgentName(dispatch);
|
|
72
|
+
}
|
|
73
|
+
} else if (agentName) {
|
|
36
74
|
const newInitials = (0, _utils.getIconText)(agentName) || currentAgentInitials;
|
|
37
75
|
const hasInitialsChanged = newInitials !== currentAgentInitials;
|
|
38
76
|
const hasNameChanged = agentName !== currentAgentName;
|
|
39
77
|
currentAgentName = agentName;
|
|
40
78
|
if (hasInitialsChanged) {
|
|
41
|
-
var
|
|
79
|
+
var _externalInitialsUpda2;
|
|
42
80
|
currentAgentInitials = newInitials;
|
|
43
81
|
// Notify external React context if provided
|
|
44
|
-
(
|
|
82
|
+
(_externalInitialsUpda2 = externalInitialsUpdater) === null || _externalInitialsUpda2 === void 0 ? void 0 : _externalInitialsUpda2(currentAgentInitials || "");
|
|
45
83
|
// Broadcast (optional) for multi-tab sync without forcing consumers
|
|
46
84
|
_omnichannelChatComponents.BroadcastService.postMessage({
|
|
47
85
|
eventName: "BotAvatarInitialsUpdated",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { LogLevel, TelemetryEvent } from "../../common/telemetry/TelemetryConstants";
|
|
2
|
-
import React, { useEffect } from "react";
|
|
2
|
+
import React, { useEffect, useState } from "react";
|
|
3
3
|
import { createTimer, findAllFocusableElement } from "../../common/utils";
|
|
4
4
|
import { LoadingPane } from "@microsoft/omnichannel-chat-components";
|
|
5
5
|
import { TelemetryHelper } from "../../common/telemetry/TelemetryHelper";
|
|
@@ -7,13 +7,22 @@ import { defaultGeneralPostChatLoadingPaneStyleProps } from "./common/defaultgen
|
|
|
7
7
|
import useChatContextStore from "../../hooks/useChatContextStore";
|
|
8
8
|
let uiTimer;
|
|
9
9
|
export const PostChatLoadingPaneStateful = props => {
|
|
10
|
-
var _props$styleProps;
|
|
10
|
+
var _props$controlProps, _props$styleProps;
|
|
11
|
+
const defaultSubtitleText = "Please take a moment to give us feedback about your chat experience. We are loading the survey for you now.";
|
|
12
|
+
const subtitleText = ((_props$controlProps = props.controlProps) === null || _props$controlProps === void 0 ? void 0 : _props$controlProps.subtitleText) ?? defaultSubtitleText;
|
|
13
|
+
const [announcedSubtitleText, setAnnouncedSubtitleText] = useState("");
|
|
11
14
|
useEffect(() => {
|
|
12
15
|
uiTimer = createTimer();
|
|
13
16
|
TelemetryHelper.logLoadingEvent(LogLevel.INFO, {
|
|
14
17
|
Event: TelemetryEvent.UXPostChatLoadingPaneStart
|
|
15
18
|
});
|
|
16
19
|
}, []);
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
const timeout = window.setTimeout(() => {
|
|
22
|
+
setAnnouncedSubtitleText(subtitleText);
|
|
23
|
+
}, 0);
|
|
24
|
+
return () => window.clearTimeout(timeout);
|
|
25
|
+
}, [subtitleText]);
|
|
17
26
|
const [state] = useChatContextStore();
|
|
18
27
|
const generalStyleProps = Object.assign({}, defaultGeneralPostChatLoadingPaneStyleProps, (_props$styleProps = props.styleProps) === null || _props$styleProps === void 0 ? void 0 : _props$styleProps.generalStyleProps);
|
|
19
28
|
const styleProps = {
|
|
@@ -27,8 +36,11 @@ export const PostChatLoadingPaneStateful = props => {
|
|
|
27
36
|
hideTitle: true,
|
|
28
37
|
hideSpinner: true,
|
|
29
38
|
hideSpinnerText: true,
|
|
30
|
-
|
|
31
|
-
|
|
39
|
+
...props.controlProps,
|
|
40
|
+
role: "status",
|
|
41
|
+
"aria-live": "polite",
|
|
42
|
+
"aria-atomic": "true",
|
|
43
|
+
subtitleText: announcedSubtitleText
|
|
32
44
|
};
|
|
33
45
|
|
|
34
46
|
// Move focus to the first button
|
|
@@ -58,6 +58,7 @@ export const PostChatSurveyPaneStateful = props => {
|
|
|
58
58
|
};
|
|
59
59
|
const controlProps = {
|
|
60
60
|
id: "oc-lcw-postchatsurvey-pane",
|
|
61
|
+
title: "Post-chat survey",
|
|
61
62
|
surveyURL: ((_props$controlProps = props.controlProps) === null || _props$controlProps === void 0 ? void 0 : _props$controlProps.surveyURL) ?? surveyInviteLink,
|
|
62
63
|
...props.controlProps
|
|
63
64
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { HtmlAttributeNames, Regex } from "../../common/Constants";
|
|
2
2
|
import { ConversationStage, LogLevel, TelemetryEvent } from "../../common/telemetry/TelemetryConstants";
|
|
3
|
-
import React, { useEffect } from "react";
|
|
3
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
4
4
|
import { createTimer, extractPreChatSurveyResponseValues, findAllFocusableElement, getStateFromCache, getWidgetCacheId, isUndefinedOrEmpty, parseAdaptiveCardPayload } from "../../common/utils";
|
|
5
5
|
import { ConversationState } from "../../contexts/common/ConversationState";
|
|
6
6
|
import { LiveChatWidgetActionType } from "../../contexts/common/LiveChatWidgetActionType";
|
|
@@ -11,6 +11,34 @@ import { defaultGeneralPreChatSurveyPaneStyleProps } from "./common/defaultStyle
|
|
|
11
11
|
import { defaultPreChatSurveyLocalizedTexts } from "./common/defaultProps/defaultPreChatSurveyLocalizedTexts";
|
|
12
12
|
import useChatContextStore from "../../hooks/useChatContextStore";
|
|
13
13
|
let uiTimer;
|
|
14
|
+
const getFocusedElementAnnouncement = element => {
|
|
15
|
+
var _element$textContent;
|
|
16
|
+
const ariaLabel = element.getAttribute(HtmlAttributeNames.ariaLabel);
|
|
17
|
+
if (ariaLabel) {
|
|
18
|
+
return ariaLabel.trim();
|
|
19
|
+
}
|
|
20
|
+
const ariaLabelledBy = element.getAttribute(HtmlAttributeNames.ariaLabelledby);
|
|
21
|
+
if (ariaLabelledBy) {
|
|
22
|
+
const labelledByText = ariaLabelledBy.split(/\s+/).map(id => {
|
|
23
|
+
var _document$getElementB, _document$getElementB2;
|
|
24
|
+
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();
|
|
25
|
+
}).filter(Boolean).join(" ");
|
|
26
|
+
if (labelledByText) {
|
|
27
|
+
return labelledByText;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (element.id) {
|
|
31
|
+
const label = Array.from(document.querySelectorAll("label")).find(candidate => candidate.htmlFor === element.id || candidate.getAttribute("for") === element.id);
|
|
32
|
+
if (label !== null && label !== void 0 && label.textContent) {
|
|
33
|
+
return label.textContent.trim();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const parentLabel = element.closest("label");
|
|
37
|
+
if (parentLabel !== null && parentLabel !== void 0 && parentLabel.textContent) {
|
|
38
|
+
return parentLabel.textContent.trim();
|
|
39
|
+
}
|
|
40
|
+
return ((_element$textContent = element.textContent) === null || _element$textContent === void 0 ? void 0 : _element$textContent.trim()) ?? "";
|
|
41
|
+
};
|
|
14
42
|
|
|
15
43
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
16
44
|
export const PreChatSurveyPaneStateful = props => {
|
|
@@ -33,6 +61,8 @@ export const PreChatSurveyPaneStateful = props => {
|
|
|
33
61
|
surveyProps,
|
|
34
62
|
initStartChat
|
|
35
63
|
} = props;
|
|
64
|
+
const [preChatFocusAnnouncement, setPreChatFocusAnnouncement] = useState("");
|
|
65
|
+
const liveRegionUpdateTimeout = useRef();
|
|
36
66
|
const generalStyleProps = Object.assign({}, defaultGeneralPreChatSurveyPaneStyleProps, surveyProps === null || surveyProps === void 0 ? void 0 : (_surveyProps$stylePro = surveyProps.styleProps) === null || _surveyProps$stylePro === void 0 ? void 0 : _surveyProps$stylePro.generalStyleProps, {
|
|
37
67
|
display: state.appStates.isMinimized ? "none" : ""
|
|
38
68
|
});
|
|
@@ -119,6 +149,16 @@ export const PreChatSurveyPaneStateful = props => {
|
|
|
119
149
|
...(surveyProps === null || surveyProps === void 0 ? void 0 : surveyProps.styleProps),
|
|
120
150
|
generalStyleProps: generalStyleProps
|
|
121
151
|
};
|
|
152
|
+
const announceFocusedElement = event => {
|
|
153
|
+
if (liveRegionUpdateTimeout.current) {
|
|
154
|
+
window.clearTimeout(liveRegionUpdateTimeout.current);
|
|
155
|
+
}
|
|
156
|
+
const announcement = getFocusedElementAnnouncement(event.target);
|
|
157
|
+
setPreChatFocusAnnouncement("");
|
|
158
|
+
liveRegionUpdateTimeout.current = window.setTimeout(() => {
|
|
159
|
+
setPreChatFocusAnnouncement(announcement);
|
|
160
|
+
}, 0);
|
|
161
|
+
};
|
|
122
162
|
useEffect(() => {
|
|
123
163
|
// Set Aria-Label Attribute for Inputs
|
|
124
164
|
const adaptiveCardElements = document.getElementsByClassName(HtmlAttributeNames.adaptiveCardClassName);
|
|
@@ -166,9 +206,31 @@ export const PreChatSurveyPaneStateful = props => {
|
|
|
166
206
|
}
|
|
167
207
|
}
|
|
168
208
|
}, [state.appStates.isMinimized]);
|
|
169
|
-
|
|
209
|
+
useEffect(() => () => {
|
|
210
|
+
if (liveRegionUpdateTimeout.current) {
|
|
211
|
+
window.clearTimeout(liveRegionUpdateTimeout.current);
|
|
212
|
+
}
|
|
213
|
+
}, []);
|
|
214
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
215
|
+
onFocusCapture: announceFocusedElement
|
|
216
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
217
|
+
role: "status",
|
|
218
|
+
"aria-live": "polite",
|
|
219
|
+
"aria-atomic": "true",
|
|
220
|
+
style: {
|
|
221
|
+
position: "absolute",
|
|
222
|
+
width: "1px",
|
|
223
|
+
height: "1px",
|
|
224
|
+
margin: "-1px",
|
|
225
|
+
padding: 0,
|
|
226
|
+
border: 0,
|
|
227
|
+
clip: "rect(0, 0, 0, 0)",
|
|
228
|
+
overflow: "hidden",
|
|
229
|
+
whiteSpace: "nowrap"
|
|
230
|
+
}
|
|
231
|
+
}, preChatFocusAnnouncement), /*#__PURE__*/React.createElement(PreChatSurveyPane, {
|
|
170
232
|
controlProps: controlProps,
|
|
171
233
|
styleProps: styleProps
|
|
172
|
-
});
|
|
234
|
+
}));
|
|
173
235
|
};
|
|
174
236
|
export default PreChatSurveyPaneStateful;
|
|
@@ -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,
|
|
@@ -10,6 +10,32 @@ let currentAgentName = defaultWebChatStyles.botAvatarInitials;
|
|
|
10
10
|
|
|
11
11
|
// Optional external updater (React context dispatch wrapper) set at runtime
|
|
12
12
|
let externalInitialsUpdater;
|
|
13
|
+
const hasTransferTag = activity => {
|
|
14
|
+
var _activity$channelData;
|
|
15
|
+
const tags = [...(Array.isArray(activity === null || activity === void 0 ? void 0 : (_activity$channelData = activity.channelData) === null || _activity$channelData === void 0 ? void 0 : _activity$channelData.tags) ? activity.channelData.tags : []), ...(Array.isArray(activity === null || activity === void 0 ? void 0 : activity.tags) ? activity.tags : [])];
|
|
16
|
+
return tags.some(tag => typeof tag === "string" && tag.toLowerCase().includes("transfer"));
|
|
17
|
+
};
|
|
18
|
+
const isTransferSystemMessage = activity => {
|
|
19
|
+
const text = ((activity === null || activity === void 0 ? void 0 : activity.text) || "").toString();
|
|
20
|
+
return hasTransferTag(activity) || /\btransfer(?:red|ring)?\b/i.test(text);
|
|
21
|
+
};
|
|
22
|
+
const resetCachedAgentName = dispatch => {
|
|
23
|
+
if (currentAgentName !== defaultWebChatStyles.botAvatarInitials || currentAgentInitials !== defaultWebChatStyles.botAvatarInitials) {
|
|
24
|
+
var _externalInitialsUpda;
|
|
25
|
+
currentAgentName = defaultWebChatStyles.botAvatarInitials;
|
|
26
|
+
currentAgentInitials = defaultWebChatStyles.botAvatarInitials;
|
|
27
|
+
(_externalInitialsUpda = externalInitialsUpdater) === null || _externalInitialsUpda === void 0 ? void 0 : _externalInitialsUpda(currentAgentInitials || "");
|
|
28
|
+
BroadcastService.postMessage({
|
|
29
|
+
eventName: "BotAvatarInitialsUpdated",
|
|
30
|
+
payload: {
|
|
31
|
+
initials: currentAgentInitials
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
dispatch({
|
|
35
|
+
type: "__BOT_INITIALS_UPDATED__"
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
};
|
|
13
39
|
export const localizedStringsBotInitialsMiddleware = onInitialsChange => _ref => {
|
|
14
40
|
let {
|
|
15
41
|
dispatch
|
|
@@ -23,19 +49,31 @@ export const localizedStringsBotInitialsMiddleware = onInitialsChange => _ref =>
|
|
|
23
49
|
var _action$payload, _activity$from;
|
|
24
50
|
const activity = (_action$payload = action.payload) === null || _action$payload === void 0 ? void 0 : _action$payload.activity;
|
|
25
51
|
if (activity !== null && activity !== void 0 && (_activity$from = activity.from) !== null && _activity$from !== void 0 && _activity$from.name && activity.from.role !== Constants.userMessageTag && activity.from.name !== Constants.userMessageTag) {
|
|
26
|
-
var _activity$
|
|
52
|
+
var _activity$channelData2, _activity$channelData3, _activity$tags;
|
|
27
53
|
const agentName = activity.from.name.trim();
|
|
28
|
-
const isSystemMessage = agentName === "__agent__" || agentName.startsWith("__") || ((_activity$
|
|
29
|
-
if (
|
|
54
|
+
const isSystemMessage = agentName === "__agent__" || agentName.startsWith("__") || ((_activity$channelData2 = activity.channelData) === null || _activity$channelData2 === void 0 ? void 0 : (_activity$channelData3 = _activity$channelData2.tags) === null || _activity$channelData3 === void 0 ? void 0 : _activity$channelData3.includes(Constants.systemMessageTag)) || ((_activity$tags = activity.tags) === null || _activity$tags === void 0 ? void 0 : _activity$tags.includes(Constants.systemMessageTag));
|
|
55
|
+
if (isSystemMessage) {
|
|
56
|
+
// transfer-stale-bot-name: when a transfer system message indicates the
|
|
57
|
+
// conversation has been routed to a different agent, the
|
|
58
|
+
// previously-cached agent name must NOT linger. Otherwise the
|
|
59
|
+
// next non-system activity (e.g. typing indicator, welcome
|
|
60
|
+
// message, or the new agent's first turn before their name
|
|
61
|
+
// is observed) is announced to screen readers as the OLD
|
|
62
|
+
// agent. Reset to defaults so the next observed name takes
|
|
63
|
+
// effect cleanly.
|
|
64
|
+
if (isTransferSystemMessage(activity)) {
|
|
65
|
+
resetCachedAgentName(dispatch);
|
|
66
|
+
}
|
|
67
|
+
} else if (agentName) {
|
|
30
68
|
const newInitials = getIconText(agentName) || currentAgentInitials;
|
|
31
69
|
const hasInitialsChanged = newInitials !== currentAgentInitials;
|
|
32
70
|
const hasNameChanged = agentName !== currentAgentName;
|
|
33
71
|
currentAgentName = agentName;
|
|
34
72
|
if (hasInitialsChanged) {
|
|
35
|
-
var
|
|
73
|
+
var _externalInitialsUpda2;
|
|
36
74
|
currentAgentInitials = newInitials;
|
|
37
75
|
// Notify external React context if provided
|
|
38
|
-
(
|
|
76
|
+
(_externalInitialsUpda2 = externalInitialsUpdater) === null || _externalInitialsUpda2 === void 0 ? void 0 : _externalInitialsUpda2(currentAgentInitials || "");
|
|
39
77
|
// Broadcast (optional) for multi-tab sync without forcing consumers
|
|
40
78
|
BroadcastService.postMessage({
|
|
41
79
|
eventName: "BotAvatarInitialsUpdated",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@microsoft/omnichannel-chat-widget",
|
|
3
|
-
"version": "1.8.4-main.
|
|
3
|
+
"version": "1.8.4-main.fba10aa",
|
|
4
4
|
"description": "Microsoft Omnichannel Chat Widget",
|
|
5
5
|
"main": "lib/cjs/index.js",
|
|
6
6
|
"types": "lib/types/index.d.ts",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"react-test-renderer": "^18.3.1",
|
|
85
85
|
"rimraf": "^6.0.1",
|
|
86
86
|
"storybook-addon-playwright": "^4.9.2",
|
|
87
|
-
"swiper": "^
|
|
87
|
+
"swiper": "^12.1.2",
|
|
88
88
|
"terser-webpack-plugin": "^4.2.3",
|
|
89
89
|
"thread-loader": "^2.1.3",
|
|
90
90
|
"ts-loader": "^9.2.6",
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
"dependencies": {
|
|
96
96
|
"@azure/core-tracing": "^1.2.0",
|
|
97
97
|
"@microsoft/applicationinsights-web": "^3.3.6",
|
|
98
|
-
"@microsoft/omnichannel-chat-components": "1.1.17-main.
|
|
98
|
+
"@microsoft/omnichannel-chat-components": "1.1.17-main.f21df63",
|
|
99
99
|
"@microsoft/omnichannel-chat-sdk": "1.11.9-main.da8219b",
|
|
100
100
|
"@opentelemetry/api": "^1.9.0",
|
|
101
101
|
"abort-controller": "^3",
|
|
@@ -172,7 +172,7 @@
|
|
|
172
172
|
"**/lodash": "4.17.23",
|
|
173
173
|
"**/@babel/runtime-corejs3": "^7.29.0",
|
|
174
174
|
"**/brace-expansion": "2.0.3",
|
|
175
|
-
"**/swiper": "
|
|
175
|
+
"**/swiper": "12.1.2"
|
|
176
176
|
},
|
|
177
177
|
"jest": {
|
|
178
178
|
"verbose": true,
|