diff --git a/.github/workflows/pull-request-validation.yml b/.github/workflows/pull-request-validation.yml
index 6d631f9663..7bc02a7f0a 100644
--- a/.github/workflows/pull-request-validation.yml
+++ b/.github/workflows/pull-request-validation.yml
@@ -281,7 +281,9 @@ jobs:
with:
compression-level: 0
name: test-snapshot-diff-html-${{ matrix.shard-index }}
- path: ./__tests__/__image_snapshots__/*/__diff_output__/*
+ path: |
+ ./__tests__/__image_snapshots__/*/__diff_output__/*
+ ./__tests__/html2/**/*.snap-*-diff.png
merge-test-results:
if: always()
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dbeb618c52..23b76cd819 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -85,6 +85,7 @@ Notes: web developers are advised to use [`~` (tilde range)](https://github.com/
- Added support of [contentless activity in livestream](https://github.com/microsoft/BotFramework-WebChat/blob/main/docs/LIVESTREAMING.md#scenario-3-interim-activities-with-no-content), in PR [#5430](https://github.com/microsoft/BotFramework-WebChat/pull/5430), by [@compulim](https://github.com/compulim)
- Added sliding dots typing indicator in Fluent theme, in PR [#5447](https://github.com/microsoft/BotFramework-WebChat/pull/5447) and PR [#5448](https://github.com/microsoft/BotFramework-WebChat/pull/5448), by [@compulim](https://github.com/compulim)
- (Experimental) Add an ability to pass `completion` prop into Fluent send box and expose the component, in PR [#5466](https://github.com/microsoft/BotFramework-WebChat/pull/5466), by [@OEvgeny](https://github.com/OEvgeny)
+- Added feedback form for like/dislike button when `feedbackActionsPlacement` is `"activity-actions"`, in PR [#5460](https://github.com/microsoft/BotFramework-WebChat/pull/5460), by [@lexi-taylor](https://github.com/lexi-taylor) and [@OEvgeny](https://github.com/OEvgeny)
### Changed
diff --git a/__tests__/html2/activity/feedback.form.activity.html b/__tests__/html2/activity/feedback.form.activity.html
new file mode 100644
index 0000000000..b722ac3b5d
--- /dev/null
+++ b/__tests__/html2/activity/feedback.form.activity.html
@@ -0,0 +1,133 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/__tests__/html2/activity/feedback.form.activity.html.snap-1.png b/__tests__/html2/activity/feedback.form.activity.html.snap-1.png
new file mode 100644
index 0000000000..f6e7ec73b9
Binary files /dev/null and b/__tests__/html2/activity/feedback.form.activity.html.snap-1.png differ
diff --git a/__tests__/html2/activity/feedback.form.activity.html.snap-2.png b/__tests__/html2/activity/feedback.form.activity.html.snap-2.png
new file mode 100644
index 0000000000..d31a8f1581
Binary files /dev/null and b/__tests__/html2/activity/feedback.form.activity.html.snap-2.png differ
diff --git a/__tests__/html2/activity/feedback.form.activity.html.snap-3.png b/__tests__/html2/activity/feedback.form.activity.html.snap-3.png
new file mode 100644
index 0000000000..a20059bfe8
Binary files /dev/null and b/__tests__/html2/activity/feedback.form.activity.html.snap-3.png differ
diff --git a/__tests__/html2/activity/feedback.form.activity.html.snap-4.png b/__tests__/html2/activity/feedback.form.activity.html.snap-4.png
new file mode 100644
index 0000000000..d7bf2be2fc
Binary files /dev/null and b/__tests__/html2/activity/feedback.form.activity.html.snap-4.png differ
diff --git a/__tests__/html2/activity/feedback.form.activity.html.snap-5.png b/__tests__/html2/activity/feedback.form.activity.html.snap-5.png
new file mode 100644
index 0000000000..a20059bfe8
Binary files /dev/null and b/__tests__/html2/activity/feedback.form.activity.html.snap-5.png differ
diff --git a/__tests__/html2/fluentTheme/defaultFeedback.activity.dark.html b/__tests__/html2/fluentTheme/defaultFeedback.activity.dark.html
new file mode 100644
index 0000000000..0798671f60
--- /dev/null
+++ b/__tests__/html2/fluentTheme/defaultFeedback.activity.dark.html
@@ -0,0 +1,169 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/__tests__/html2/fluentTheme/defaultFeedback.activity.dark.html.snap-1.png b/__tests__/html2/fluentTheme/defaultFeedback.activity.dark.html.snap-1.png
new file mode 100644
index 0000000000..8d9b22f8f7
Binary files /dev/null and b/__tests__/html2/fluentTheme/defaultFeedback.activity.dark.html.snap-1.png differ
diff --git a/__tests__/html2/fluentTheme/defaultFeedback.activity.dark.html.snap-2.png b/__tests__/html2/fluentTheme/defaultFeedback.activity.dark.html.snap-2.png
new file mode 100644
index 0000000000..1f3bd6fa46
Binary files /dev/null and b/__tests__/html2/fluentTheme/defaultFeedback.activity.dark.html.snap-2.png differ
diff --git a/__tests__/html2/fluentTheme/defaultFeedback.activity.dark.html.snap-3.png b/__tests__/html2/fluentTheme/defaultFeedback.activity.dark.html.snap-3.png
new file mode 100644
index 0000000000..e4ef8d8c8e
Binary files /dev/null and b/__tests__/html2/fluentTheme/defaultFeedback.activity.dark.html.snap-3.png differ
diff --git a/__tests__/html2/fluentTheme/defaultFeedback.activity.dark.html.snap-4.png b/__tests__/html2/fluentTheme/defaultFeedback.activity.dark.html.snap-4.png
new file mode 100644
index 0000000000..4408c2006d
Binary files /dev/null and b/__tests__/html2/fluentTheme/defaultFeedback.activity.dark.html.snap-4.png differ
diff --git a/__tests__/html2/fluentTheme/defaultFeedback.activity.html b/__tests__/html2/fluentTheme/defaultFeedback.activity.html
new file mode 100644
index 0000000000..0b60c262d4
--- /dev/null
+++ b/__tests__/html2/fluentTheme/defaultFeedback.activity.html
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/__tests__/html2/fluentTheme/defaultFeedback.activity.html.snap-1.png b/__tests__/html2/fluentTheme/defaultFeedback.activity.html.snap-1.png
new file mode 100644
index 0000000000..b4d90bd648
Binary files /dev/null and b/__tests__/html2/fluentTheme/defaultFeedback.activity.html.snap-1.png differ
diff --git a/__tests__/html2/fluentTheme/defaultFeedback.activity.html.snap-2.png b/__tests__/html2/fluentTheme/defaultFeedback.activity.html.snap-2.png
new file mode 100644
index 0000000000..53f8665347
Binary files /dev/null and b/__tests__/html2/fluentTheme/defaultFeedback.activity.html.snap-2.png differ
diff --git a/__tests__/html2/fluentTheme/defaultFeedback.activity.html.snap-3.png b/__tests__/html2/fluentTheme/defaultFeedback.activity.html.snap-3.png
new file mode 100644
index 0000000000..66072faa38
Binary files /dev/null and b/__tests__/html2/fluentTheme/defaultFeedback.activity.html.snap-3.png differ
diff --git a/__tests__/html2/fluentTheme/defaultFeedback.activity.html.snap-4.png b/__tests__/html2/fluentTheme/defaultFeedback.activity.html.snap-4.png
new file mode 100644
index 0000000000..309151bdd4
Binary files /dev/null and b/__tests__/html2/fluentTheme/defaultFeedback.activity.html.snap-4.png differ
diff --git a/__tests__/html2/fluentTheme/feedback.form.multiple.html b/__tests__/html2/fluentTheme/feedback.form.multiple.html
new file mode 100644
index 0000000000..9eb690a5a9
--- /dev/null
+++ b/__tests__/html2/fluentTheme/feedback.form.multiple.html
@@ -0,0 +1,119 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/__tests__/html2/fluentTheme/feedback.form.multiple.html.snap-1.png b/__tests__/html2/fluentTheme/feedback.form.multiple.html.snap-1.png
new file mode 100644
index 0000000000..1ab0c215b2
Binary files /dev/null and b/__tests__/html2/fluentTheme/feedback.form.multiple.html.snap-1.png differ
diff --git a/packages/api/src/StyleOptions.ts b/packages/api/src/StyleOptions.ts
index f229984adc..b55d1f91ce 100644
--- a/packages/api/src/StyleOptions.ts
+++ b/packages/api/src/StyleOptions.ts
@@ -932,7 +932,7 @@ type StyleOptions = {
/**
* (EXPERIMENTAL) Feedback buttons placement
*
- * - `'activity-actions'` - place feedback buttons inside activity actions
+ * - `'activity-actions'` - place feedback buttons inside activity actions - will show feedback form
* - `'activity-status'` - place feedback buttons inside activity status
*
* @default 'activity-status'
diff --git a/packages/api/src/defaultStyleOptions.ts b/packages/api/src/defaultStyleOptions.ts
index 8fa1ef4e6b..cda952a509 100644
--- a/packages/api/src/defaultStyleOptions.ts
+++ b/packages/api/src/defaultStyleOptions.ts
@@ -244,7 +244,6 @@ const DEFAULT_OPTIONS: Required = {
transcriptOverlayButtonColorOnHover: undefined,
// Toast UI
-
notificationDebounceTimeout: 400,
hideToaster: false,
diff --git a/packages/api/src/localization/en-US.json b/packages/api/src/localization/en-US.json
index 396e9a6f3d..a675514234 100644
--- a/packages/api/src/localization/en-US.json
+++ b/packages/api/src/localization/en-US.json
@@ -76,6 +76,10 @@
"COPY_BUTTON_TEXT": "Copy",
"COPY_BUTTON_COPIED_TEXT": "Copied",
"_COPY_BUTTON_COPIED.comment": "After clicking on the copy button, this text will show briefly",
+ "FEEDBACK_FORM_SUBMIT_BUTTON_LABEL": "Submit",
+ "FEEDBACK_FORM_CANCEL_BUTTON_LABEL": "Cancel",
+ "FEEDBACK_FORM_PLACEHOLDER": "Give as much detail as you can, but do not include any private or sensitive information.",
+ "FEEDBACK_FORM_TITLE": "Tell us about your experience",
"FILE_CONTENT_ALT": "'$1'",
"FILE_CONTENT_DOWNLOADABLE_ALT": "Download file '$1'",
"FILE_CONTENT_DOWNLOADABLE_WITH_SIZE_ALT": "Download file '$1' of size $2",
diff --git a/packages/component/src/Activity/ActivityFeedback.tsx b/packages/component/src/Activity/ActivityFeedback.tsx
index 2cb7ad93a0..644dddc669 100644
--- a/packages/component/src/Activity/ActivityFeedback.tsx
+++ b/packages/component/src/Activity/ActivityFeedback.tsx
@@ -1,10 +1,13 @@
import { hooks } from 'botframework-webchat-api';
import { getOrgSchemaMessage, OrgSchemaAction, parseAction, WebChatActivity } from 'botframework-webchat-core';
-import cx from 'classnames';
-import React, { memo, useMemo } from 'react';
-
-import Feedback from './private/Feedback';
+import classNames from 'classnames';
+import React, { memo, useCallback, useMemo, useState } from 'react';
+import useStyleSet from '../hooks/useStyleSet';
import dereferenceBlankNodes from '../Utils/JSONLinkedData/dereferenceBlankNodes';
+import Feedback from './private/Feedback';
+import getDisclaimer from './private/getDisclaimer';
+import hasFeedbackLoop from './private/hasFeedbackLoop';
+import FeedbackForm from './private/FeedbackForm';
const { useStyleOptions } = hooks;
@@ -12,12 +15,47 @@ type ActivityFeedbackProps = Readonly<{
activity: WebChatActivity;
}>;
+const parseActivity = (entities?: WebChatActivity['entities']) => {
+ const graph = dereferenceBlankNodes(entities || []);
+ const messageThing = getOrgSchemaMessage(graph);
+
+ return { graph, messageThing };
+};
+
+const defaultFeedbackEntities = {
+ '@context': 'https://schema.org',
+ '@id': '',
+ '@type': 'Message',
+ type: 'https://schema.org/Message',
+
+ keywords: [],
+ potentialAction: [
+ {
+ '@type': 'LikeAction',
+ actionStatus: 'PotentialActionStatus'
+ },
+ {
+ '@type': 'DislikeAction',
+ actionStatus: 'PotentialActionStatus'
+ }
+ ]
+};
+
function ActivityFeedback({ activity }: ActivityFeedbackProps) {
const [{ feedbackActionsPlacement }] = useStyleOptions();
+ const [{ feedbackForm }] = useStyleSet();
+
+ const [selectedAction, setSelectedAction] = useState();
- const graph = useMemo(() => dereferenceBlankNodes(activity.entities || []), [activity.entities]);
+ const isFeedbackLoopSupported = hasFeedbackLoop(activity);
- const messageThing = useMemo(() => getOrgSchemaMessage(graph), [graph]);
+ const { graph, messageThing } = useMemo(() => {
+ if (isFeedbackLoopSupported) {
+ return parseActivity([defaultFeedbackEntities]);
+ }
+
+ return parseActivity(activity.entities);
+ }, [activity.entities, isFeedbackLoopSupported]);
const feedbackActions = useMemo>(() => {
try {
@@ -40,14 +78,53 @@ function ActivityFeedback({ activity }: ActivityFeedbackProps) {
return Object.freeze(new Set([] as OrgSchemaAction[]));
}, [graph, messageThing?.potentialAction]);
- return (
-
+ const handleFeedbackActionClick = useCallback(
+ (action: OrgSchemaAction) => setSelectedAction(action === selectedAction ? undefined : action),
+ [selectedAction, setSelectedAction]
+ );
+
+ const handleFeedbackFormReset = useCallback(() => setSelectedAction(undefined), [setSelectedAction]);
+
+ const FeedbackComponent = useMemo(
+ () => (
+
+ ),
+ [feedbackActions, feedbackActionsPlacement, handleFeedbackActionClick, isFeedbackLoopSupported, selectedAction]
);
+
+ const FeedbackFormComponent = useMemo(
+ () => (
+
+ ),
+ [activity, handleFeedbackFormReset, selectedAction]
+ );
+
+ if (feedbackActionsPlacement === 'activity-actions' && isFeedbackLoopSupported) {
+ return (
+