diff --git a/CHANGELOG.md b/CHANGELOG.md index 06d825402c..2a39f3f111 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -87,6 +87,7 @@ Notes: web developers are advised to use [`~` (tilde range)](https://github.com/ - 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), PR [#5469](https://github.com/microsoft/BotFramework-WebChat/pull/5469), and PR [5470](https://github.com/microsoft/BotFramework-WebChat/pull/5470) by [@lexi-taylor](https://github.com/lexi-taylor) and [@OEvgeny](https://github.com/OEvgeny) + - ESCAPE key should reset the feedback form, in PR [#5480](https://github.com/microsoft/BotFramework-WebChat/pull/5480), by [@compulim](https://github.com/compulim) - Added multi-dimensional grouping, `styleOptions.groupActivitiesBy`, and `useGroupActivitiesByName` hook, in PR [#5471](https://github.com/microsoft/BotFramework-WebChat/pull/5471), by [@compulim](https://github.com/compulim) - Existing behavior will be kept and activities will be grouped by `sender` followed by `status` - `useGroupActivitiesByName` is favored over the existing `useGroupActivities` hook for performance reason @@ -221,6 +222,8 @@ Notes: web developers are advised to use [`~` (tilde range)](https://github.com/ - Fixed [#5472](https://github.com/microsoft/BotFramework-WebChat/issues/5472), ensure proper inheritance from the outside of Web Chat for visibility CSS property when hidden, in PR [#5473](https://github.com/microsoft/BotFramework-WebChat/pull/5473), by [@OEvgeny](https://github.com/OEvgeny) - Fixed [#5474](https://github.com/microsoft/BotFramework-WebChat/issues/5474). Disable AMD glue code in bundle, in PR [#5478](https://github.com/microsoft/BotFramework-WebChat/pull/5478), by [@compulim](https://github.com/compulim) - Downstreamers who use our CommonJS and ES Modules output with esbuild will need to disable AMD themselves to prevent conflict with RequireJS +- Fixed [#5479](https://github.com/microsoft/BotFramework-WebChat/issues/5479). Fixed feedback form buttons should not squash other buttons, in PR [#5480](https://github.com/microsoft/BotFramework-WebChat/pull/5480), by [@compulim](https://github.com/compulim) + - Migrated to radio button for like/dislike where form submission is required # Removed diff --git a/__tests__/__image_snapshots__/html/feedback-activity-status-basic-js-vote-button-should-display-1-snap.png b/__tests__/__image_snapshots__/html/feedback-activity-status-basic-js-vote-button-should-display-1-snap.png deleted file mode 100644 index 84d96156be..0000000000 Binary files a/__tests__/__image_snapshots__/html/feedback-activity-status-basic-js-vote-button-should-display-1-snap.png and /dev/null differ diff --git a/__tests__/__image_snapshots__/html/feedback-activity-status-basic-js-vote-button-should-display-2-snap.png b/__tests__/__image_snapshots__/html/feedback-activity-status-basic-js-vote-button-should-display-2-snap.png deleted file mode 100644 index 774267ef27..0000000000 Binary files a/__tests__/__image_snapshots__/html/feedback-activity-status-basic-js-vote-button-should-display-2-snap.png and /dev/null differ diff --git a/__tests__/__image_snapshots__/html/feedback-activity-status-basic-js-vote-button-should-display-3-snap.png b/__tests__/__image_snapshots__/html/feedback-activity-status-basic-js-vote-button-should-display-3-snap.png deleted file mode 100644 index c2fad81446..0000000000 Binary files a/__tests__/__image_snapshots__/html/feedback-activity-status-basic-js-vote-button-should-display-3-snap.png and /dev/null differ diff --git a/__tests__/__image_snapshots__/html/feedback-activity-status-basic-js-vote-button-should-display-4-snap.png b/__tests__/__image_snapshots__/html/feedback-activity-status-basic-js-vote-button-should-display-4-snap.png deleted file mode 100644 index 235ed230ef..0000000000 Binary files a/__tests__/__image_snapshots__/html/feedback-activity-status-basic-js-vote-button-should-display-4-snap.png and /dev/null differ diff --git a/__tests__/__image_snapshots__/html/feedback-activity-status-click-js-vote-button-should-send-event-on-click-1-snap.png b/__tests__/__image_snapshots__/html/feedback-activity-status-click-js-vote-button-should-send-event-on-click-1-snap.png deleted file mode 100644 index 84d96156be..0000000000 Binary files a/__tests__/__image_snapshots__/html/feedback-activity-status-click-js-vote-button-should-send-event-on-click-1-snap.png and /dev/null differ diff --git a/__tests__/__image_snapshots__/html/feedback-activity-status-click-js-vote-button-should-send-event-on-click-2-snap.png b/__tests__/__image_snapshots__/html/feedback-activity-status-click-js-vote-button-should-send-event-on-click-2-snap.png deleted file mode 100644 index 743997bb35..0000000000 Binary files a/__tests__/__image_snapshots__/html/feedback-activity-status-click-js-vote-button-should-send-event-on-click-2-snap.png and /dev/null differ diff --git a/__tests__/__image_snapshots__/html/feedback-activity-status-click-js-vote-button-should-send-event-on-click-3-snap.png b/__tests__/__image_snapshots__/html/feedback-activity-status-click-js-vote-button-should-send-event-on-click-3-snap.png deleted file mode 100644 index 05e3c87eb9..0000000000 Binary files a/__tests__/__image_snapshots__/html/feedback-activity-status-click-js-vote-button-should-send-event-on-click-3-snap.png and /dev/null differ diff --git a/__tests__/__image_snapshots__/html/feedback-activity-status-click-js-vote-button-should-send-event-on-click-4-snap.png b/__tests__/__image_snapshots__/html/feedback-activity-status-click-js-vote-button-should-send-event-on-click-4-snap.png deleted file mode 100644 index 05e3c87eb9..0000000000 Binary files a/__tests__/__image_snapshots__/html/feedback-activity-status-click-js-vote-button-should-send-event-on-click-4-snap.png and /dev/null differ diff --git a/__tests__/__image_snapshots__/html/side-by-side-wide-dark-js-fluent-theme-applied-dark-theme-applied-side-by-side-left-transcript-right-feedback-1-snap.png b/__tests__/__image_snapshots__/html/side-by-side-wide-dark-js-fluent-theme-applied-dark-theme-applied-side-by-side-left-transcript-right-feedback-1-snap.png index 3081e33b5d..ff056d2158 100644 Binary files a/__tests__/__image_snapshots__/html/side-by-side-wide-dark-js-fluent-theme-applied-dark-theme-applied-side-by-side-left-transcript-right-feedback-1-snap.png and b/__tests__/__image_snapshots__/html/side-by-side-wide-dark-js-fluent-theme-applied-dark-theme-applied-side-by-side-left-transcript-right-feedback-1-snap.png differ diff --git a/__tests__/__image_snapshots__/html/side-by-side-wide-dark-js-fluent-theme-applied-dark-theme-applied-side-by-side-left-transcript-right-feedback-2-snap.png b/__tests__/__image_snapshots__/html/side-by-side-wide-dark-js-fluent-theme-applied-dark-theme-applied-side-by-side-left-transcript-right-feedback-2-snap.png index e2c6064fe6..2335e78b6b 100644 Binary files a/__tests__/__image_snapshots__/html/side-by-side-wide-dark-js-fluent-theme-applied-dark-theme-applied-side-by-side-left-transcript-right-feedback-2-snap.png and b/__tests__/__image_snapshots__/html/side-by-side-wide-dark-js-fluent-theme-applied-dark-theme-applied-side-by-side-left-transcript-right-feedback-2-snap.png differ diff --git a/__tests__/__image_snapshots__/html/side-by-side-wide-dark-js-fluent-theme-applied-dark-theme-applied-side-by-side-left-transcript-right-feedback-3-snap.png b/__tests__/__image_snapshots__/html/side-by-side-wide-dark-js-fluent-theme-applied-dark-theme-applied-side-by-side-left-transcript-right-feedback-3-snap.png index 3d8f42c781..1d849a6c39 100644 Binary files a/__tests__/__image_snapshots__/html/side-by-side-wide-dark-js-fluent-theme-applied-dark-theme-applied-side-by-side-left-transcript-right-feedback-3-snap.png and b/__tests__/__image_snapshots__/html/side-by-side-wide-dark-js-fluent-theme-applied-dark-theme-applied-side-by-side-left-transcript-right-feedback-3-snap.png differ diff --git a/__tests__/__image_snapshots__/html/side-by-side-wide-dark-js-fluent-theme-applied-dark-theme-applied-side-by-side-left-transcript-right-feedback-4-snap.png b/__tests__/__image_snapshots__/html/side-by-side-wide-dark-js-fluent-theme-applied-dark-theme-applied-side-by-side-left-transcript-right-feedback-4-snap.png index bbbe30ff3e..94a1d50faa 100644 Binary files a/__tests__/__image_snapshots__/html/side-by-side-wide-dark-js-fluent-theme-applied-dark-theme-applied-side-by-side-left-transcript-right-feedback-4-snap.png and b/__tests__/__image_snapshots__/html/side-by-side-wide-dark-js-fluent-theme-applied-dark-theme-applied-side-by-side-left-transcript-right-feedback-4-snap.png differ diff --git a/__tests__/__image_snapshots__/html/side-by-side-wide-dark-js-fluent-theme-applied-dark-theme-applied-side-by-side-left-transcript-right-feedback-5-snap.png b/__tests__/__image_snapshots__/html/side-by-side-wide-dark-js-fluent-theme-applied-dark-theme-applied-side-by-side-left-transcript-right-feedback-5-snap.png index aac31bb094..8c63de3a4e 100644 Binary files a/__tests__/__image_snapshots__/html/side-by-side-wide-dark-js-fluent-theme-applied-dark-theme-applied-side-by-side-left-transcript-right-feedback-5-snap.png and b/__tests__/__image_snapshots__/html/side-by-side-wide-dark-js-fluent-theme-applied-dark-theme-applied-side-by-side-left-transcript-right-feedback-5-snap.png differ diff --git a/__tests__/__image_snapshots__/html/side-by-side-wide-dark-js-fluent-theme-applied-dark-theme-applied-side-by-side-left-transcript-right-feedback-6-snap.png b/__tests__/__image_snapshots__/html/side-by-side-wide-dark-js-fluent-theme-applied-dark-theme-applied-side-by-side-left-transcript-right-feedback-6-snap.png index a6b7dd4f1b..74545b9367 100644 Binary files a/__tests__/__image_snapshots__/html/side-by-side-wide-dark-js-fluent-theme-applied-dark-theme-applied-side-by-side-left-transcript-right-feedback-6-snap.png and b/__tests__/__image_snapshots__/html/side-by-side-wide-dark-js-fluent-theme-applied-dark-theme-applied-side-by-side-left-transcript-right-feedback-6-snap.png differ diff --git a/__tests__/__image_snapshots__/html/side-by-side-wide-dark-js-fluent-theme-applied-dark-theme-applied-side-by-side-left-transcript-right-feedback-7-snap.png b/__tests__/__image_snapshots__/html/side-by-side-wide-dark-js-fluent-theme-applied-dark-theme-applied-side-by-side-left-transcript-right-feedback-7-snap.png index dd048ebebd..c3cb51280c 100644 Binary files a/__tests__/__image_snapshots__/html/side-by-side-wide-dark-js-fluent-theme-applied-dark-theme-applied-side-by-side-left-transcript-right-feedback-7-snap.png and b/__tests__/__image_snapshots__/html/side-by-side-wide-dark-js-fluent-theme-applied-dark-theme-applied-side-by-side-left-transcript-right-feedback-7-snap.png differ diff --git a/__tests__/__image_snapshots__/html/side-by-side-wide-js-fluent-theme-applied-side-by-side-left-transcript-right-feedback-1-snap.png b/__tests__/__image_snapshots__/html/side-by-side-wide-js-fluent-theme-applied-side-by-side-left-transcript-right-feedback-1-snap.png index a2bbb5c017..5f282f1dc4 100644 Binary files a/__tests__/__image_snapshots__/html/side-by-side-wide-js-fluent-theme-applied-side-by-side-left-transcript-right-feedback-1-snap.png and b/__tests__/__image_snapshots__/html/side-by-side-wide-js-fluent-theme-applied-side-by-side-left-transcript-right-feedback-1-snap.png differ diff --git a/__tests__/__image_snapshots__/html/side-by-side-wide-js-fluent-theme-applied-side-by-side-left-transcript-right-feedback-2-snap.png b/__tests__/__image_snapshots__/html/side-by-side-wide-js-fluent-theme-applied-side-by-side-left-transcript-right-feedback-2-snap.png index b43f646781..a1f7bc10b7 100644 Binary files a/__tests__/__image_snapshots__/html/side-by-side-wide-js-fluent-theme-applied-side-by-side-left-transcript-right-feedback-2-snap.png and b/__tests__/__image_snapshots__/html/side-by-side-wide-js-fluent-theme-applied-side-by-side-left-transcript-right-feedback-2-snap.png differ diff --git a/__tests__/__image_snapshots__/html/side-by-side-wide-js-fluent-theme-applied-side-by-side-left-transcript-right-feedback-3-snap.png b/__tests__/__image_snapshots__/html/side-by-side-wide-js-fluent-theme-applied-side-by-side-left-transcript-right-feedback-3-snap.png index 27538955d3..4408a2fd31 100644 Binary files a/__tests__/__image_snapshots__/html/side-by-side-wide-js-fluent-theme-applied-side-by-side-left-transcript-right-feedback-3-snap.png and b/__tests__/__image_snapshots__/html/side-by-side-wide-js-fluent-theme-applied-side-by-side-left-transcript-right-feedback-3-snap.png differ diff --git a/__tests__/__image_snapshots__/html/side-by-side-wide-js-fluent-theme-applied-side-by-side-left-transcript-right-feedback-4-snap.png b/__tests__/__image_snapshots__/html/side-by-side-wide-js-fluent-theme-applied-side-by-side-left-transcript-right-feedback-4-snap.png index 0148a62862..0ad874050e 100644 Binary files a/__tests__/__image_snapshots__/html/side-by-side-wide-js-fluent-theme-applied-side-by-side-left-transcript-right-feedback-4-snap.png and b/__tests__/__image_snapshots__/html/side-by-side-wide-js-fluent-theme-applied-side-by-side-left-transcript-right-feedback-4-snap.png differ diff --git a/__tests__/__image_snapshots__/html/side-by-side-wide-js-fluent-theme-applied-side-by-side-left-transcript-right-feedback-5-snap.png b/__tests__/__image_snapshots__/html/side-by-side-wide-js-fluent-theme-applied-side-by-side-left-transcript-right-feedback-5-snap.png index fc0429e73e..c21de29aa0 100644 Binary files a/__tests__/__image_snapshots__/html/side-by-side-wide-js-fluent-theme-applied-side-by-side-left-transcript-right-feedback-5-snap.png and b/__tests__/__image_snapshots__/html/side-by-side-wide-js-fluent-theme-applied-side-by-side-left-transcript-right-feedback-5-snap.png differ diff --git a/__tests__/__image_snapshots__/html/side-by-side-wide-js-fluent-theme-applied-side-by-side-left-transcript-right-feedback-6-snap.png b/__tests__/__image_snapshots__/html/side-by-side-wide-js-fluent-theme-applied-side-by-side-left-transcript-right-feedback-6-snap.png index b59619851e..41a13f1f93 100644 Binary files a/__tests__/__image_snapshots__/html/side-by-side-wide-js-fluent-theme-applied-side-by-side-left-transcript-right-feedback-6-snap.png and b/__tests__/__image_snapshots__/html/side-by-side-wide-js-fluent-theme-applied-side-by-side-left-transcript-right-feedback-6-snap.png differ diff --git a/__tests__/__image_snapshots__/html/side-by-side-wide-js-fluent-theme-applied-side-by-side-left-transcript-right-feedback-7-snap.png b/__tests__/__image_snapshots__/html/side-by-side-wide-js-fluent-theme-applied-side-by-side-left-transcript-right-feedback-7-snap.png index 04ff78df03..d487c27acd 100644 Binary files a/__tests__/__image_snapshots__/html/side-by-side-wide-js-fluent-theme-applied-side-by-side-left-transcript-right-feedback-7-snap.png and b/__tests__/__image_snapshots__/html/side-by-side-wide-js-fluent-theme-applied-side-by-side-left-transcript-right-feedback-7-snap.png differ diff --git a/__tests__/html/feedbackActivityStatus.basic.js b/__tests__/html/feedbackActivityStatus.basic.js deleted file mode 100644 index 2d0c215fd1..0000000000 --- a/__tests__/html/feedbackActivityStatus.basic.js +++ /dev/null @@ -1,5 +0,0 @@ -/** @jest-environment ./packages/test/harness/src/host/jest/WebDriverEnvironment.js */ - -describe('vote button', () => { - test('should display', () => runHTML('feedbackActivityStatus.basic.html')); -}); diff --git a/__tests__/html/feedbackActivityStatus.click.js b/__tests__/html/feedbackActivityStatus.click.js deleted file mode 100644 index 532d4959f7..0000000000 --- a/__tests__/html/feedbackActivityStatus.click.js +++ /dev/null @@ -1,5 +0,0 @@ -/** @jest-environment ./packages/test/harness/src/host/jest/WebDriverEnvironment.js */ - -describe('vote button', () => { - test('should send event on click', () => runHTML('feedbackActivityStatus.click.html')); -}); diff --git a/__tests__/html/feedbackActivityStatus.single.js b/__tests__/html/feedbackActivityStatus.single.js deleted file mode 100644 index dd55168a1c..0000000000 --- a/__tests__/html/feedbackActivityStatus.single.js +++ /dev/null @@ -1,5 +0,0 @@ -/** @jest-environment ./packages/test/harness/src/host/jest/WebDriverEnvironment.js */ - -describe('vote button', () => { - test('should display a single button', () => runHTML('feedbackActivityStatus.single.html')); -}); diff --git a/__tests__/html/fluentTheme/side-by-side.wide.dark.html b/__tests__/html/fluentTheme/side-by-side.wide.dark.html index 4ab51fbd35..ec5156f86e 100644 --- a/__tests__/html/fluentTheme/side-by-side.wide.dark.html +++ b/__tests__/html/fluentTheme/side-by-side.wide.dark.html @@ -834,14 +834,15 @@ await host.snapshot(); await host.sendKeys('TAB'); await host.snapshot(); - await host.sendKeys('ENTER'); + await host.sendKeys('SPACE'); await host.snapshot(); await host.sendKeys('TAB'); await host.snapshot(); - await host.sendKeys('ENTER'); + await host.sendKeys('SPACE'); await host.snapshot(); - await host.sendKeys('ESCAPE'); - await host.sendKeys('ESCAPE'); + await host.sendKeys('SPACE'); // Clear feedback but still focused on feedback button + await host.sendKeys('ESCAPE'); // Focus on activity + await host.sendKeys('ESCAPE'); // Clear activity selection await host.snapshot(); } })); diff --git a/__tests__/html/fluentTheme/side-by-side.wide.html b/__tests__/html/fluentTheme/side-by-side.wide.html index 423e5fbd46..faabeee7f1 100644 --- a/__tests__/html/fluentTheme/side-by-side.wide.html +++ b/__tests__/html/fluentTheme/side-by-side.wide.html @@ -817,14 +817,15 @@ await host.snapshot(); await host.sendKeys('TAB'); await host.snapshot(); - await host.sendKeys('ENTER'); + await host.sendKeys('SPACE'); await host.snapshot(); await host.sendKeys('TAB'); await host.snapshot(); - await host.sendKeys('ENTER'); + await host.sendKeys('SPACE'); await host.snapshot(); - await host.sendKeys('ESCAPE'); - await host.sendKeys('ESCAPE'); + await host.sendKeys('SPACE'); // Clear feedback but still focused on feedback button + await host.sendKeys('ESCAPE'); // Focus on activity + await host.sendKeys('ESCAPE'); // Clear activity selection await host.snapshot(); } })); diff --git a/__tests__/html2/activity/feedback.activity.dismiss.html.snap-1.png b/__tests__/html2/activity/feedback.activity.dismiss.html.snap-1.png deleted file mode 100644 index 5f6585c6cb..0000000000 Binary files a/__tests__/html2/activity/feedback.activity.dismiss.html.snap-1.png and /dev/null differ diff --git a/__tests__/html2/activity/feedback.activity.dismiss.html.snap-2.png b/__tests__/html2/activity/feedback.activity.dismiss.html.snap-2.png deleted file mode 100644 index 9bd1a806fe..0000000000 Binary files a/__tests__/html2/activity/feedback.activity.dismiss.html.snap-2.png and /dev/null differ diff --git a/__tests__/html2/activity/feedback.activity.dismiss.html.snap-3.png b/__tests__/html2/activity/feedback.activity.dismiss.html.snap-3.png deleted file mode 100644 index bff2338747..0000000000 Binary files a/__tests__/html2/activity/feedback.activity.dismiss.html.snap-3.png and /dev/null differ diff --git a/__tests__/html2/activity/feedback.activity.dismiss.html.snap-4.png b/__tests__/html2/activity/feedback.activity.dismiss.html.snap-4.png deleted file mode 100644 index fd6fbb6689..0000000000 Binary files a/__tests__/html2/activity/feedback.activity.dismiss.html.snap-4.png and /dev/null differ diff --git a/__tests__/html2/activity/feedback.activity.html.snap-1.png b/__tests__/html2/activity/feedback.activity.html.snap-1.png deleted file mode 100644 index 67c845d8e8..0000000000 Binary files a/__tests__/html2/activity/feedback.activity.html.snap-1.png and /dev/null differ diff --git a/__tests__/html2/activity/feedback.activity.html.snap-2.png b/__tests__/html2/activity/feedback.activity.html.snap-2.png deleted file mode 100644 index 59396b69a4..0000000000 Binary files a/__tests__/html2/activity/feedback.activity.html.snap-2.png and /dev/null differ diff --git a/__tests__/html2/activity/feedback.activity.html.snap-3.png b/__tests__/html2/activity/feedback.activity.html.snap-3.png deleted file mode 100644 index ae9653483b..0000000000 Binary files a/__tests__/html2/activity/feedback.activity.html.snap-3.png and /dev/null differ diff --git a/__tests__/html2/activity/feedback.activity.html.snap-4.png b/__tests__/html2/activity/feedback.activity.html.snap-4.png deleted file mode 100644 index fb698d702d..0000000000 Binary files a/__tests__/html2/activity/feedback.activity.html.snap-4.png and /dev/null differ diff --git a/__tests__/html2/activity/feedback.activity.html.snap-5.png b/__tests__/html2/activity/feedback.activity.html.snap-5.png deleted file mode 100644 index afb993d76b..0000000000 Binary files a/__tests__/html2/activity/feedback.activity.html.snap-5.png and /dev/null differ diff --git a/__tests__/html2/activity/feedback.form.activity.html.snap-1.png b/__tests__/html2/activity/feedback.form.activity.html.snap-1.png deleted file mode 100644 index 708ad549a1..0000000000 Binary files a/__tests__/html2/activity/feedback.form.activity.html.snap-1.png and /dev/null 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 deleted file mode 100644 index a20059bfe8..0000000000 Binary files a/__tests__/html2/activity/feedback.form.activity.html.snap-2.png and /dev/null differ diff --git a/__tests__/html2/activity/feedback.status.html.snap-1.png b/__tests__/html2/activity/feedback.status.html.snap-1.png deleted file mode 100644 index 2807fc5937..0000000000 Binary files a/__tests__/html2/activity/feedback.status.html.snap-1.png and /dev/null differ diff --git a/__tests__/html2/activity/feedback.status.html.snap-2.png b/__tests__/html2/activity/feedback.status.html.snap-2.png deleted file mode 100644 index 64ba12a7b0..0000000000 Binary files a/__tests__/html2/activity/feedback.status.html.snap-2.png and /dev/null differ diff --git a/__tests__/html2/activity/feedback.status.html.snap-3.png b/__tests__/html2/activity/feedback.status.html.snap-3.png deleted file mode 100644 index e67c6a0b45..0000000000 Binary files a/__tests__/html2/activity/feedback.status.html.snap-3.png and /dev/null differ diff --git a/__tests__/html2/activity/feedback.status.html.snap-4.png b/__tests__/html2/activity/feedback.status.html.snap-4.png deleted file mode 100644 index d527b79069..0000000000 Binary files a/__tests__/html2/activity/feedback.status.html.snap-4.png and /dev/null differ diff --git a/__tests__/html2/activity/feedback.status.html.snap-5.png b/__tests__/html2/activity/feedback.status.html.snap-5.png deleted file mode 100644 index 1a4cb33859..0000000000 Binary files a/__tests__/html2/activity/feedback.status.html.snap-5.png and /dev/null differ diff --git a/__tests__/html2/feedbackForm/behavior.changeMind.keyboard.html b/__tests__/html2/feedbackForm/behavior.changeMind.keyboard.html new file mode 100644 index 0000000000..7b3bdf775f --- /dev/null +++ b/__tests__/html2/feedbackForm/behavior.changeMind.keyboard.html @@ -0,0 +1,130 @@ + + + + + + + + + + +
+ + + + diff --git a/__tests__/html2/feedbackForm/behavior.changeMind.mouse.html b/__tests__/html2/feedbackForm/behavior.changeMind.mouse.html new file mode 100644 index 0000000000..eacc67559d --- /dev/null +++ b/__tests__/html2/feedbackForm/behavior.changeMind.mouse.html @@ -0,0 +1,123 @@ + + + + + + + + + + +
+ + + + diff --git a/__tests__/html2/feedbackForm/behavior.noopByEnterKey.html b/__tests__/html2/feedbackForm/behavior.noopByEnterKey.html new file mode 100644 index 0000000000..33dfb1bc55 --- /dev/null +++ b/__tests__/html2/feedbackForm/behavior.noopByEnterKey.html @@ -0,0 +1,91 @@ + + + + + + + + + + +
+ + + diff --git a/__tests__/html2/feedbackForm/behavior.resetByEscapeKey.html b/__tests__/html2/feedbackForm/behavior.resetByEscapeKey.html new file mode 100644 index 0000000000..5853662308 --- /dev/null +++ b/__tests__/html2/feedbackForm/behavior.resetByEscapeKey.html @@ -0,0 +1,136 @@ + + + + + + + + + + +
+ + + + diff --git a/__tests__/html2/feedbackForm/behavior.resetByEscapeKey.html.snap-1.png b/__tests__/html2/feedbackForm/behavior.resetByEscapeKey.html.snap-1.png new file mode 100644 index 0000000000..5b8b75b2ae Binary files /dev/null and b/__tests__/html2/feedbackForm/behavior.resetByEscapeKey.html.snap-1.png differ diff --git a/__tests__/html2/feedbackForm/behavior.resetByEscapeKey.html.snap-2.png b/__tests__/html2/feedbackForm/behavior.resetByEscapeKey.html.snap-2.png new file mode 100644 index 0000000000..747cea12aa Binary files /dev/null and b/__tests__/html2/feedbackForm/behavior.resetByEscapeKey.html.snap-2.png differ diff --git a/__tests__/html2/feedbackForm/behavior.selectByClick.html b/__tests__/html2/feedbackForm/behavior.selectByClick.html new file mode 100644 index 0000000000..d92773b1b2 --- /dev/null +++ b/__tests__/html2/feedbackForm/behavior.selectByClick.html @@ -0,0 +1,99 @@ + + + + + + + + + + +
+ + + + diff --git a/__tests__/html2/feedbackForm/behavior.selectByClick.html.snap-1.png b/__tests__/html2/feedbackForm/behavior.selectByClick.html.snap-1.png new file mode 100644 index 0000000000..2c9ff4cbb2 Binary files /dev/null and b/__tests__/html2/feedbackForm/behavior.selectByClick.html.snap-1.png differ diff --git a/__tests__/html2/activity/feedback.activity.dismiss.html b/__tests__/html2/feedbackForm/feedback.activity.dismiss.html similarity index 99% rename from __tests__/html2/activity/feedback.activity.dismiss.html rename to __tests__/html2/feedbackForm/feedback.activity.dismiss.html index 92c3f6258c..9297963050 100644 --- a/__tests__/html2/activity/feedback.activity.dismiss.html +++ b/__tests__/html2/feedbackForm/feedback.activity.dismiss.html @@ -96,7 +96,7 @@ await new Promise(resolve => setTimeout(resolve, 400)); await host.snapshot('local'); - await host.sendKeys('ENTER'); + await host.sendKeys('SPACE'); await host.snapshot('local'); await directLine.emulateIncomingActivity({ diff --git a/__tests__/html2/feedbackForm/feedback.activity.dismiss.html.snap-1.png b/__tests__/html2/feedbackForm/feedback.activity.dismiss.html.snap-1.png new file mode 100644 index 0000000000..b34fa9a883 Binary files /dev/null and b/__tests__/html2/feedbackForm/feedback.activity.dismiss.html.snap-1.png differ diff --git a/__tests__/html2/feedbackForm/feedback.activity.dismiss.html.snap-2.png b/__tests__/html2/feedbackForm/feedback.activity.dismiss.html.snap-2.png new file mode 100644 index 0000000000..8f67c82737 Binary files /dev/null and b/__tests__/html2/feedbackForm/feedback.activity.dismiss.html.snap-2.png differ diff --git a/__tests__/html2/feedbackForm/feedback.activity.dismiss.html.snap-3.png b/__tests__/html2/feedbackForm/feedback.activity.dismiss.html.snap-3.png new file mode 100644 index 0000000000..13b832b375 Binary files /dev/null and b/__tests__/html2/feedbackForm/feedback.activity.dismiss.html.snap-3.png differ diff --git a/__tests__/html2/feedbackForm/feedback.activity.dismiss.html.snap-4.png b/__tests__/html2/feedbackForm/feedback.activity.dismiss.html.snap-4.png new file mode 100644 index 0000000000..fe1ec58bcc Binary files /dev/null and b/__tests__/html2/feedbackForm/feedback.activity.dismiss.html.snap-4.png differ diff --git a/__tests__/html2/activity/feedback.form.activity.html b/__tests__/html2/feedbackForm/feedback.form.activity.html similarity index 65% rename from __tests__/html2/activity/feedback.form.activity.html rename to __tests__/html2/feedbackForm/feedback.form.activity.html index 5ee654daf9..7b721a27f4 100644 --- a/__tests__/html2/activity/feedback.form.activity.html +++ b/__tests__/html2/feedbackForm/feedback.form.activity.html @@ -2,39 +2,40 @@ - - -
- + - -
- diff --git a/__tests__/html2/feedbackForm/feedback.status.html.snap-1.png b/__tests__/html2/feedbackForm/feedback.status.html.snap-1.png new file mode 100644 index 0000000000..2967e776bb Binary files /dev/null and b/__tests__/html2/feedbackForm/feedback.status.html.snap-1.png differ diff --git a/__tests__/html2/feedbackForm/feedback.status.html.snap-2.png b/__tests__/html2/feedbackForm/feedback.status.html.snap-2.png new file mode 100644 index 0000000000..537f6f8fe9 Binary files /dev/null and b/__tests__/html2/feedbackForm/feedback.status.html.snap-2.png differ diff --git a/__tests__/html2/feedbackForm/feedback.status.html.snap-3.png b/__tests__/html2/feedbackForm/feedback.status.html.snap-3.png new file mode 100644 index 0000000000..618b02ebb9 Binary files /dev/null and b/__tests__/html2/feedbackForm/feedback.status.html.snap-3.png differ diff --git a/__tests__/html2/feedbackForm/feedback.status.html.snap-4.png b/__tests__/html2/feedbackForm/feedback.status.html.snap-4.png new file mode 100644 index 0000000000..df35ee30d4 Binary files /dev/null and b/__tests__/html2/feedbackForm/feedback.status.html.snap-4.png differ diff --git a/__tests__/html2/feedbackForm/feedback.status.html.snap-5.png b/__tests__/html2/feedbackForm/feedback.status.html.snap-5.png new file mode 100644 index 0000000000..1c2096f2d4 Binary files /dev/null and b/__tests__/html2/feedbackForm/feedback.status.html.snap-5.png differ diff --git a/__tests__/html2/feedbackForm/layout.html b/__tests__/html2/feedbackForm/layout.html new file mode 100644 index 0000000000..361ce2bb58 --- /dev/null +++ b/__tests__/html2/feedbackForm/layout.html @@ -0,0 +1,101 @@ + + + + + + + + + + + + +
+ + + diff --git a/__tests__/html2/feedbackForm/layout.html.snap-1.png b/__tests__/html2/feedbackForm/layout.html.snap-1.png new file mode 100644 index 0000000000..d43d9686b8 Binary files /dev/null and b/__tests__/html2/feedbackForm/layout.html.snap-1.png differ diff --git a/__tests__/html2/feedbackForm/layout.html.snap-2.png b/__tests__/html2/feedbackForm/layout.html.snap-2.png new file mode 100644 index 0000000000..36ff95e9f6 Binary files /dev/null and b/__tests__/html2/feedbackForm/layout.html.snap-2.png differ diff --git a/__tests__/html2/feedbackForm/layout.userReview.html b/__tests__/html2/feedbackForm/layout.userReview.html new file mode 100644 index 0000000000..ef8bee212c --- /dev/null +++ b/__tests__/html2/feedbackForm/layout.userReview.html @@ -0,0 +1,105 @@ + + + + + + + + + + + + +
+ + + diff --git a/__tests__/html2/feedbackForm/layout.userReview.html.snap-1.png b/__tests__/html2/feedbackForm/layout.userReview.html.snap-1.png new file mode 100644 index 0000000000..30825fa5a3 Binary files /dev/null and b/__tests__/html2/feedbackForm/layout.userReview.html.snap-1.png differ diff --git a/__tests__/html2/feedbackForm/layout.userReview.html.snap-2.png b/__tests__/html2/feedbackForm/layout.userReview.html.snap-2.png new file mode 100644 index 0000000000..3a39c1910d Binary files /dev/null and b/__tests__/html2/feedbackForm/layout.userReview.html.snap-2.png differ diff --git a/__tests__/html2/feedbackForm/layout.userReview.html.snap-3.png b/__tests__/html2/feedbackForm/layout.userReview.html.snap-3.png new file mode 100644 index 0000000000..5b6d2076c1 Binary files /dev/null and b/__tests__/html2/feedbackForm/layout.userReview.html.snap-3.png differ diff --git a/__tests__/html2/feedbackForm/layout.withCopyButton.html b/__tests__/html2/feedbackForm/layout.withCopyButton.html new file mode 100644 index 0000000000..015edd2007 --- /dev/null +++ b/__tests__/html2/feedbackForm/layout.withCopyButton.html @@ -0,0 +1,101 @@ + + + + + + + + + + + + + +
+ + + diff --git a/__tests__/html2/feedbackForm/layout.withCopyButton.html.snap-1.png b/__tests__/html2/feedbackForm/layout.withCopyButton.html.snap-1.png new file mode 100644 index 0000000000..6b80da26a1 Binary files /dev/null and b/__tests__/html2/feedbackForm/layout.withCopyButton.html.snap-1.png differ diff --git a/__tests__/html2/feedbackForm/layout.withCopyButton.html.snap-2.png b/__tests__/html2/feedbackForm/layout.withCopyButton.html.snap-2.png new file mode 100644 index 0000000000..89eea79cb6 Binary files /dev/null and b/__tests__/html2/feedbackForm/layout.withCopyButton.html.snap-2.png differ diff --git a/__tests__/html2/feedbackForm/layout.withFluent.html b/__tests__/html2/feedbackForm/layout.withFluent.html new file mode 100644 index 0000000000..a93af0c0aa --- /dev/null +++ b/__tests__/html2/feedbackForm/layout.withFluent.html @@ -0,0 +1,9 @@ + + + + + + + diff --git a/__tests__/html2/feedbackForm/layout.withFluent.html.snap-1.png b/__tests__/html2/feedbackForm/layout.withFluent.html.snap-1.png new file mode 100644 index 0000000000..fcab043f5a Binary files /dev/null and b/__tests__/html2/feedbackForm/layout.withFluent.html.snap-1.png differ diff --git a/__tests__/html2/feedbackForm/layout.withFluent.html.snap-2.png b/__tests__/html2/feedbackForm/layout.withFluent.html.snap-2.png new file mode 100644 index 0000000000..9f09a0ecd3 Binary files /dev/null and b/__tests__/html2/feedbackForm/layout.withFluent.html.snap-2.png differ diff --git a/__tests__/html2/feedbackStatus/behavior.escapeKey.html b/__tests__/html2/feedbackStatus/behavior.escapeKey.html new file mode 100644 index 0000000000..30dc6bb076 --- /dev/null +++ b/__tests__/html2/feedbackStatus/behavior.escapeKey.html @@ -0,0 +1,95 @@ + + + + + + + + + + +
+ + + + diff --git a/__tests__/html2/activity/feedback.activity.html b/__tests__/html2/feedbackStatus/feedback.activity.html similarity index 98% rename from __tests__/html2/activity/feedback.activity.html rename to __tests__/html2/feedbackStatus/feedback.activity.html index c98b5787b2..b13cdf6592 100644 --- a/__tests__/html2/activity/feedback.activity.html +++ b/__tests__/html2/feedbackStatus/feedback.activity.html @@ -135,11 +135,11 @@ await new Promise(resolve => setTimeout(resolve, 400)); await host.snapshot('local'); - await host.sendKeys('ENTER'); + await host.sendKeys('SPACE'); await host.snapshot('local'); await host.sendKeys('TAB'); await host.snapshot('local'); - await host.sendKeys('ENTER'); + await host.sendKeys('SPACE'); await host.snapshot('local'); }); diff --git a/__tests__/html2/feedbackStatus/feedback.activity.html.snap-1.png b/__tests__/html2/feedbackStatus/feedback.activity.html.snap-1.png new file mode 100644 index 0000000000..c7f04beeaf Binary files /dev/null and b/__tests__/html2/feedbackStatus/feedback.activity.html.snap-1.png differ diff --git a/__tests__/html2/feedbackStatus/feedback.activity.html.snap-2.png b/__tests__/html2/feedbackStatus/feedback.activity.html.snap-2.png new file mode 100644 index 0000000000..3c6ed87a0c Binary files /dev/null and b/__tests__/html2/feedbackStatus/feedback.activity.html.snap-2.png differ diff --git a/__tests__/html2/feedbackStatus/feedback.activity.html.snap-3.png b/__tests__/html2/feedbackStatus/feedback.activity.html.snap-3.png new file mode 100644 index 0000000000..2be69c27d6 Binary files /dev/null and b/__tests__/html2/feedbackStatus/feedback.activity.html.snap-3.png differ diff --git a/__tests__/html2/feedbackStatus/feedback.activity.html.snap-4.png b/__tests__/html2/feedbackStatus/feedback.activity.html.snap-4.png new file mode 100644 index 0000000000..1c0dfb8699 Binary files /dev/null and b/__tests__/html2/feedbackStatus/feedback.activity.html.snap-4.png differ diff --git a/__tests__/html2/feedbackStatus/feedback.activity.html.snap-5.png b/__tests__/html2/feedbackStatus/feedback.activity.html.snap-5.png new file mode 100644 index 0000000000..a64deef13b Binary files /dev/null and b/__tests__/html2/feedbackStatus/feedback.activity.html.snap-5.png differ diff --git a/__tests__/html/feedbackActivityStatus.basic.html b/__tests__/html2/feedbackStatus/feedbackActivityStatus.basic.html similarity index 87% rename from __tests__/html/feedbackActivityStatus.basic.html rename to __tests__/html2/feedbackStatus/feedbackActivityStatus.basic.html index cdc4736b67..2ff7af5d3c 100644 --- a/__tests__/html/feedbackActivityStatus.basic.html +++ b/__tests__/html2/feedbackStatus/feedbackActivityStatus.basic.html @@ -1,4 +1,4 @@ - + @@ -9,6 +9,10 @@
diff --git a/__tests__/html2/feedbackStatus/feedbackActivityStatus.basic.html.snap-1.png b/__tests__/html2/feedbackStatus/feedbackActivityStatus.basic.html.snap-1.png new file mode 100644 index 0000000000..8c0a24ff13 Binary files /dev/null and b/__tests__/html2/feedbackStatus/feedbackActivityStatus.basic.html.snap-1.png differ diff --git a/__tests__/html2/feedbackStatus/feedbackActivityStatus.basic.html.snap-2.png b/__tests__/html2/feedbackStatus/feedbackActivityStatus.basic.html.snap-2.png new file mode 100644 index 0000000000..b6c0fc9339 Binary files /dev/null and b/__tests__/html2/feedbackStatus/feedbackActivityStatus.basic.html.snap-2.png differ diff --git a/__tests__/html2/feedbackStatus/feedbackActivityStatus.basic.html.snap-3.png b/__tests__/html2/feedbackStatus/feedbackActivityStatus.basic.html.snap-3.png new file mode 100644 index 0000000000..58f47243ce Binary files /dev/null and b/__tests__/html2/feedbackStatus/feedbackActivityStatus.basic.html.snap-3.png differ diff --git a/__tests__/html2/feedbackStatus/feedbackActivityStatus.basic.html.snap-4.png b/__tests__/html2/feedbackStatus/feedbackActivityStatus.basic.html.snap-4.png new file mode 100644 index 0000000000..4c5447255c Binary files /dev/null and b/__tests__/html2/feedbackStatus/feedbackActivityStatus.basic.html.snap-4.png differ diff --git a/__tests__/html/feedbackActivityStatus.click.html b/__tests__/html2/feedbackStatus/feedbackActivityStatus.click.html similarity index 91% rename from __tests__/html/feedbackActivityStatus.click.html rename to __tests__/html2/feedbackStatus/feedbackActivityStatus.click.html index 36629172ef..f45b602055 100644 --- a/__tests__/html/feedbackActivityStatus.click.html +++ b/__tests__/html2/feedbackStatus/feedbackActivityStatus.click.html @@ -10,6 +10,10 @@
diff --git a/__tests__/__image_snapshots__/html/feedback-activity-status-single-js-vote-button-should-display-a-single-button-1-snap.png b/__tests__/html2/feedbackStatus/feedbackActivityStatus.single.html.snap-1.png similarity index 100% rename from __tests__/__image_snapshots__/html/feedback-activity-status-single-js-vote-button-should-display-a-single-button-1-snap.png rename to __tests__/html2/feedbackStatus/feedbackActivityStatus.single.html.snap-1.png diff --git a/__tests__/html2/feedbackStatus/layout.html b/__tests__/html2/feedbackStatus/layout.html new file mode 100644 index 0000000000..d809f45197 --- /dev/null +++ b/__tests__/html2/feedbackStatus/layout.html @@ -0,0 +1,79 @@ + + + + + + + + + + +
+ + + + diff --git a/__tests__/html2/feedbackStatus/layout.html.snap-1.png b/__tests__/html2/feedbackStatus/layout.html.snap-1.png new file mode 100644 index 0000000000..60333903da Binary files /dev/null and b/__tests__/html2/feedbackStatus/layout.html.snap-1.png differ diff --git a/__tests__/html2/fluentTheme/defaultFeedback.activity.dark.html b/__tests__/html2/fluentTheme/defaultFeedback.activity.dark.html index cc3b7f65f1..fa0fab6460 100644 --- a/__tests__/html2/fluentTheme/defaultFeedback.activity.dark.html +++ b/__tests__/html2/fluentTheme/defaultFeedback.activity.dark.html @@ -100,9 +100,10 @@ await host.sendShiftTab(2); await host.sendKeys('ENTER'); - // Click like button - await host.sendKeys('ENTER'); + // WHEN: The like button is being clicked. + await host.sendKeys('SPACE'); + // THEN: Should open the feedback form. await pageConditions.became( 'feedback form is open', () => document.activeElement === pageElements.byTestId('feedback sendbox'), diff --git a/__tests__/html2/fluentTheme/defaultFeedback.activity.dark.html.snap-1.png b/__tests__/html2/fluentTheme/defaultFeedback.activity.dark.html.snap-1.png index 45f09f805c..ee393cc716 100644 Binary files a/__tests__/html2/fluentTheme/defaultFeedback.activity.dark.html.snap-1.png and b/__tests__/html2/fluentTheme/defaultFeedback.activity.dark.html.snap-1.png differ diff --git a/__tests__/html2/fluentTheme/defaultFeedback.activity.html b/__tests__/html2/fluentTheme/defaultFeedback.activity.html index 70065e3e4b..2ac2f31daa 100644 --- a/__tests__/html2/fluentTheme/defaultFeedback.activity.html +++ b/__tests__/html2/fluentTheme/defaultFeedback.activity.html @@ -59,16 +59,15 @@ await host.sendShiftTab(2); await host.sendKeys('ENTER'); - await host.sendKeys('ENTER'); + await host.sendKeys('SPACE'); await host.snapshot('local'); // Dismiss like button - await host.sendShiftTab(2); - await host.sendKeys('ENTER'); + await host.sendKeys('ESCAPE'); // Click like button - await host.sendKeys('ENTER'); + await host.sendKeys('SPACE'); await pageConditions.became( 'feedback form is open', @@ -82,13 +81,8 @@ await host.snapshot('local'); - pageElements.byTestId('send box text area').focus(); - await host.sendShiftTab(2); - - await host.sendKeys('ENTER'); // Send dislike - await host.sendTab(1); - await host.sendKeys('ENTER'); + await host.sendKeys('RIGHT', 'SPACE'); await pageConditions.became( 'feedback form is open', @@ -129,4 +123,4 @@ - \ No newline at end of file + diff --git a/__tests__/html2/fluentTheme/defaultFeedback.activity.html.snap-1.png b/__tests__/html2/fluentTheme/defaultFeedback.activity.html.snap-1.png index a1d5ec2fcb..bb1f8fdc08 100644 Binary files a/__tests__/html2/fluentTheme/defaultFeedback.activity.html.snap-1.png 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 index 66072faa38..81c7177f65 100644 Binary files a/__tests__/html2/fluentTheme/defaultFeedback.activity.html.snap-2.png 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 index 90219d52b5..41e6680631 100644 Binary files a/__tests__/html2/fluentTheme/defaultFeedback.activity.html.snap-3.png 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 index 138aa93cbc..30fc0d10c0 100644 Binary files a/__tests__/html2/fluentTheme/defaultFeedback.activity.html.snap-4.png and b/__tests__/html2/fluentTheme/defaultFeedback.activity.html.snap-4.png differ diff --git a/__tests__/html2/fluentTheme/feedback.form.markdown.html b/__tests__/html2/fluentTheme/feedback.form.markdown.html index 845daadc5a..1c486a3135 100644 --- a/__tests__/html2/fluentTheme/feedback.form.markdown.html +++ b/__tests__/html2/fluentTheme/feedback.form.markdown.html @@ -58,7 +58,7 @@ await host.sendShiftTab(2); await host.sendKeys('ENTER'); - await host.sendKeys('ENTER'); + await host.sendKeys('SPACE'); await pageConditions.became( 'feedback form is open', diff --git a/__tests__/html2/fluentTheme/feedback.form.markdown.html.snap-1.png b/__tests__/html2/fluentTheme/feedback.form.markdown.html.snap-1.png index ff4788368d..cf91727b99 100644 Binary files a/__tests__/html2/fluentTheme/feedback.form.markdown.html.snap-1.png and b/__tests__/html2/fluentTheme/feedback.form.markdown.html.snap-1.png differ diff --git a/__tests__/html2/fluentTheme/feedback.form.multiple.html b/__tests__/html2/fluentTheme/feedback.form.multiple.html index 124cf7131f..ebdcedf22b 100644 --- a/__tests__/html2/fluentTheme/feedback.form.multiple.html +++ b/__tests__/html2/fluentTheme/feedback.form.multiple.html @@ -81,7 +81,7 @@ await host.sendShiftTab(2); await host.sendKeys('ENTER'); - await host.sendKeys('ENTER'); + await host.sendKeys('SPACE'); await pageConditions.became( 'feedback form is open', diff --git a/__tests__/html2/fluentTheme/feedback.form.multiple.html.snap-1.png b/__tests__/html2/fluentTheme/feedback.form.multiple.html.snap-1.png index 368bdb8b59..674d5a4490 100644 Binary files a/__tests__/html2/fluentTheme/feedback.form.multiple.html.snap-1.png and b/__tests__/html2/fluentTheme/feedback.form.multiple.html.snap-1.png differ diff --git a/__tests__/html2/fluentTheme/feedbackForm.noDisclaimer.html b/__tests__/html2/fluentTheme/feedbackForm.noDisclaimer.html index 9de8a2b339..883e865d2c 100644 --- a/__tests__/html2/fluentTheme/feedbackForm.noDisclaimer.html +++ b/__tests__/html2/fluentTheme/feedbackForm.noDisclaimer.html @@ -56,7 +56,7 @@ await host.sendKeys('ENTER'); - await host.sendKeys('ENTER'); + await host.sendKeys('SPACE'); await pageConditions.became( 'feedback form is open', diff --git a/__tests__/html2/fluentTheme/feedbackForm.noDisclaimer.html.snap-1.png b/__tests__/html2/fluentTheme/feedbackForm.noDisclaimer.html.snap-1.png index b0cd409cea..a93485dcbf 100644 Binary files a/__tests__/html2/fluentTheme/feedbackForm.noDisclaimer.html.snap-1.png and b/__tests__/html2/fluentTheme/feedbackForm.noDisclaimer.html.snap-1.png differ diff --git a/__tests__/html2/preact/activity/feedback.status.html b/__tests__/html2/preact/activity/feedback.status.html index fb1673e0c8..c6f20b56e2 100644 --- a/__tests__/html2/preact/activity/feedback.status.html +++ b/__tests__/html2/preact/activity/feedback.status.html @@ -21,7 +21,8 @@ const { React: { useMemo }, WebChat: { - hooks: { useDirection } + hooks: { useDirection }, + testIds } } = window; @@ -139,7 +140,7 @@ await host.snapshot('local'); const [,, activityStatus] = pageElements.activityStatuses(); - const buttons = activityStatus.querySelectorAll('button'); + const buttons = activityStatus.querySelectorAll(`[data-testid="${testIds.feedbackButton}"]`); pageElements.sendBoxTextBox().focus(); @@ -168,7 +169,7 @@ await host.snapshot('local'); - await host.sendKeys('ENTER'); + await host.sendKeys('SPACE'); await host.snapshot('local'); }); diff --git a/__tests__/html2/preact/activity/feedback.status.html.snap-1.png b/__tests__/html2/preact/activity/feedback.status.html.snap-1.png index 2807fc5937..2967e776bb 100644 Binary files a/__tests__/html2/preact/activity/feedback.status.html.snap-1.png and b/__tests__/html2/preact/activity/feedback.status.html.snap-1.png differ diff --git a/__tests__/html2/preact/activity/feedback.status.html.snap-2.png b/__tests__/html2/preact/activity/feedback.status.html.snap-2.png index 64ba12a7b0..537f6f8fe9 100644 Binary files a/__tests__/html2/preact/activity/feedback.status.html.snap-2.png and b/__tests__/html2/preact/activity/feedback.status.html.snap-2.png differ diff --git a/__tests__/html2/preact/activity/feedback.status.html.snap-3.png b/__tests__/html2/preact/activity/feedback.status.html.snap-3.png index e67c6a0b45..b0006c584e 100644 Binary files a/__tests__/html2/preact/activity/feedback.status.html.snap-3.png and b/__tests__/html2/preact/activity/feedback.status.html.snap-3.png differ diff --git a/__tests__/html2/preact/activity/feedback.status.html.snap-4.png b/__tests__/html2/preact/activity/feedback.status.html.snap-4.png index d527b79069..df35ee30d4 100644 Binary files a/__tests__/html2/preact/activity/feedback.status.html.snap-4.png and b/__tests__/html2/preact/activity/feedback.status.html.snap-4.png differ diff --git a/__tests__/html2/preact/activity/feedback.status.html.snap-5.png b/__tests__/html2/preact/activity/feedback.status.html.snap-5.png index 1a4cb33859..1c2096f2d4 100644 Binary files a/__tests__/html2/preact/activity/feedback.status.html.snap-5.png and b/__tests__/html2/preact/activity/feedback.status.html.snap-5.png differ diff --git a/__tests__/html2/speech/comprehensive.html b/__tests__/html2/speech/comprehensive.html index 712a86f323..5558536658 100644 --- a/__tests__/html2/speech/comprehensive.html +++ b/__tests__/html2/speech/comprehensive.html @@ -85,7 +85,7 @@ expect(speechSynthesis).toHaveProperty('speaking', false); // THEN: Should construct SpeechRecognition(). - expect(ponyfill.SpeechRecognition).toHaveBeenCalledTimes(1); + await waitFor(() => expect(ponyfill.SpeechRecognition).toHaveBeenCalledTimes(1)); const { value: speechRecognition1 } = ponyfill.SpeechRecognition.mock.results[0]; diff --git a/__tests__/html2/speech/errorTelemetry.html b/__tests__/html2/speech/errorTelemetry.html index f456f1d6f0..3bfd809b32 100644 --- a/__tests__/html2/speech/errorTelemetry.html +++ b/__tests__/html2/speech/errorTelemetry.html @@ -78,7 +78,7 @@ ); // THEN: Should construct the SpeechRecognition() instance and call start(). - expect(ponyfill.SpeechRecognition).toHaveBeenCalledTimes(1); + await waitFor(() => expect(ponyfill.SpeechRecognition).toHaveBeenCalledTimes(1)); const { value: speechRecognition1 } = ponyfill.SpeechRecognition.mock.results[0]; diff --git a/packages/component/src/ActivityFeedback/ActivityFeedback.tsx b/packages/component/src/ActivityFeedback/ActivityFeedback.tsx index bb9caa8006..412f821fab 100644 --- a/packages/component/src/ActivityFeedback/ActivityFeedback.tsx +++ b/packages/component/src/ActivityFeedback/ActivityFeedback.tsx @@ -1,27 +1,85 @@ -import { WebChatActivity } from 'botframework-webchat-core'; -import React, { memo } from 'react'; -import FeedbackLoopWithMessage from './private/FeedbackLoopWithMessage'; -import FeedbackLoopWithoutMessage from './private/FeedbackLoopWithoutMessage'; +import classNames from 'classnames'; +import React, { memo, useCallback, useMemo, type FormEventHandler, type KeyboardEventHandler } from 'react'; +import { Extract, wrapWith } from 'react-wrap-with'; +import { useRefFrom } from 'use-ref-from'; + +import useStyleSet from '../hooks/useStyleSet'; +import FeedbackForm from './private/FeedbackForm'; +import FeedbackVoteButtonBar from './private/FeedbackVoteButtonBar'; +import isActionRequireReview from './private/isActionRequireReview'; import ActivityFeedbackComposer from './providers/ActivityFeedbackComposer'; -import useShouldShowFeedbackForm from './providers/useShouldShowFeedbackForm'; +import useActivityFeedbackHooks from './providers/useActivityFeedbackHooks'; + +function InternalActivityFeedback() { + const { useActions, useFeedbackText, useFocusFeedbackButton, useHasSubmitted, useSelectedAction, useSubmit } = + useActivityFeedbackHooks(); + + const [_, setFeedbackText] = useFeedbackText(); + const [{ feedbackForm }] = useStyleSet(); + const [actions] = useActions(); + const [hasSubmitted] = useHasSubmitted(); + const [selectedAction, setSelectedAction] = useSelectedAction(); + const focusFeedbackButton = useFocusFeedbackButton(); + const submit = useSubmit(); + + const firstActionRequireReview = useMemo(() => actions.find(isActionRequireReview), [actions]); + const selectedActionRef = useRefFrom(selectedAction); + + const handleReset = useCallback>(() => { + focusFeedbackButton(selectedActionRef.current); + + setFeedbackText(undefined); + setSelectedAction(undefined); + }, [focusFeedbackButton, selectedActionRef, setFeedbackText, setSelectedAction]); -const InternalActivityFeedback = memo(() => - useShouldShowFeedbackForm()[0] ? : -); + const handleSubmit = useCallback>( + event => { + event.preventDefault(); -InternalActivityFeedback.displayName = 'InternalActivityFeedback'; + submit(selectedActionRef.current); + }, + [selectedActionRef, submit] + ); + + const handleKeyDown = useCallback>( + event => { + // ESCAPE key should clear the feedback form and unselect like/dislike as they are radio button. + // In non-form mode, the like/dislike are actions, so they should not be unselected. + if (event.key === 'Escape' && isActionRequireReview(selectedActionRef.current)) { + event.stopPropagation(); + event.currentTarget.reset(); + } + }, + [selectedActionRef] + ); -type ActivityFeedbackProps = Readonly<{ - activity: WebChatActivity; -}>; + // Hide feedback form if feedback has already been submitted or it does not require UserReview. + const isExpanded = useMemo( + () => !hasSubmitted && selectedAction?.result?.['@type'] === 'UserReview', + [hasSubmitted, selectedAction] + ); -function ActivityFeedback({ activity }: ActivityFeedbackProps) { return ( - - - + !!actions.length && ( +
+ + {/* We put the form outside of the container to let it wrap to next line instead of keeping it the same line as the like/dislike buttons. */} + {isExpanded && } + + ) ); } +const ActivityFeedback = wrapWith(ActivityFeedbackComposer, { activity: Extract })(InternalActivityFeedback); + +ActivityFeedback.displayName = 'ActivityFeedback'; + export default memo(ActivityFeedback); -export { type ActivityFeedbackProps }; diff --git a/packages/component/src/ActivityFeedback/private/FeedbackForm.tsx b/packages/component/src/ActivityFeedback/private/FeedbackForm.tsx index ad027dd284..58ae712d52 100644 --- a/packages/component/src/ActivityFeedback/private/FeedbackForm.tsx +++ b/packages/component/src/ActivityFeedback/private/FeedbackForm.tsx @@ -1,51 +1,30 @@ import { hooks } from 'botframework-webchat-api'; -import classNames from 'classnames'; import React, { memo, useCallback, useEffect, useRef, useState, type FormEventHandler } from 'react'; -import { useRefFrom } from 'use-ref-from'; -import { useStateWithRef } from 'use-state-with-ref'; import Markdownable from '../../Attachment/Text/private/Markdownable'; -import useStyleSet from '../../hooks/useStyleSet'; import testIds from '../../testIds'; -import useActivity from '../providers/useActivity'; -import useSelectedAction from '../providers/useSelectedAction'; -import useSubmitCallback from '../providers/useSubmitCallback'; +import useActivityFeedbackHooks from '../providers/useActivityFeedbackHooks'; import FeedbackTextArea from './FeedbackTextArea'; -import getDisclaimer from './getDisclaimer'; +import getDisclaimerFromReviewAction from './getDisclaimerFromReviewAction'; const { useLocalizer } = hooks; function FeedbackForm() { - const [{ feedbackForm }] = useStyleSet(); - const [activity] = useActivity(); + const { useFeedbackText, useSelectedAction } = useActivityFeedbackHooks(); + + const [selectedAction] = useSelectedAction(); const [hasFocus, setHasFocus] = useState(false); - const [selectedAction, setSelectedAction] = useSelectedAction(); - const [userFeedback, setUserFeedback, userFeedbackRef] = useStateWithRef(''); + const [userFeedback, setUserFeedback] = useFeedbackText(); const feedbackTextAreaRef = useRef(null); const localize = useLocalizer(); - const submit = useSubmitCallback(); - - const disclaimer = getDisclaimer(activity); - const selectedActionRef = useRefFrom(selectedAction); - const handleCancelButtonClick = useCallback(() => { - setSelectedAction(undefined); - }, [setSelectedAction]); + const disclaimer = getDisclaimerFromReviewAction(selectedAction); const handleMessageChange: FormEventHandler = useCallback( ({ currentTarget: { value } }) => setUserFeedback(value), [setUserFeedback] ); - const handleSubmit = useCallback>( - event => { - event.preventDefault(); - - submit(selectedActionRef.current, userFeedbackRef.current); - }, - [selectedActionRef, submit, userFeedbackRef] - ); - useEffect(() => { // Will focus on the text area when: // 1. The component is mounted initially, or @@ -58,8 +37,8 @@ function FeedbackForm() { }, [feedbackTextAreaRef, hasFocus, setHasFocus]); return ( -
- {localize('FEEDBACK_FORM_TITLE')} +
+ {localize('FEEDBACK_FORM_TITLE')} - {disclaimer && } -
- -
- +
); } diff --git a/packages/component/src/ActivityFeedback/private/FeedbackLoopWithMessage.tsx b/packages/component/src/ActivityFeedback/private/FeedbackLoopWithMessage.tsx deleted file mode 100644 index ea6fe01fb5..0000000000 --- a/packages/component/src/ActivityFeedback/private/FeedbackLoopWithMessage.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import classNames from 'classnames'; -import React, { memo, useMemo } from 'react'; - -import useStyleSet from '../../hooks/useStyleSet'; -import useHasSubmitted from '../providers/useHasSubmitted'; -import useSelectedAction from '../providers/useSelectedAction'; -import FeedbackForm from './FeedbackForm'; -import FeedbackVoteButtonBar from './FeedbackVoteButtonBar'; - -function FeedbackLoopWithMessage() { - const [{ feedbackForm }] = useStyleSet(); - - const [hasSubmitted] = useHasSubmitted(); - const [selectedAction] = useSelectedAction(); - - // Hide feedback form if feedback has already been submitted - const isExpanded = useMemo(() => !hasSubmitted && !!selectedAction, [hasSubmitted, selectedAction]); - - return ( -
-
- -
- {isExpanded && } -
- ); -} - -export default memo(FeedbackLoopWithMessage); diff --git a/packages/component/src/ActivityFeedback/private/FeedbackLoopWithoutMessage.tsx b/packages/component/src/ActivityFeedback/private/FeedbackLoopWithoutMessage.tsx deleted file mode 100644 index dc172b0f7f..0000000000 --- a/packages/component/src/ActivityFeedback/private/FeedbackLoopWithoutMessage.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import React, { memo } from 'react'; -import FeedbackVoteButtonBar from './FeedbackVoteButtonBar'; - -function FeedbackLoopWithoutMessage() { - return ; -} - -export default memo(FeedbackLoopWithoutMessage); diff --git a/packages/component/src/ActivityFeedback/private/FeedbackTextArea.tsx b/packages/component/src/ActivityFeedback/private/FeedbackTextArea.tsx index de9b684c18..3659222006 100644 --- a/packages/component/src/ActivityFeedback/private/FeedbackTextArea.tsx +++ b/packages/component/src/ActivityFeedback/private/FeedbackTextArea.tsx @@ -12,6 +12,7 @@ import useStyleSet from '../../hooks/useStyleSet'; const { useUIState } = hooks; +// TODO: Need review. Dedupe with send box text area. const TextArea = forwardRef< HTMLTextAreaElement, Readonly<{ diff --git a/packages/component/src/ActivityFeedback/private/FeedbackVoteButton.tsx b/packages/component/src/ActivityFeedback/private/FeedbackVoteButton.tsx index 1bdc521ef8..1d83973c10 100644 --- a/packages/component/src/ActivityFeedback/private/FeedbackVoteButton.tsx +++ b/packages/component/src/ActivityFeedback/private/FeedbackVoteButton.tsx @@ -1,28 +1,52 @@ import { hooks } from 'botframework-webchat-api'; +import { validateProps } from 'botframework-webchat-api/internal'; import { onErrorResumeNext, parseVoteAction, type OrgSchemaAction } from 'botframework-webchat-core'; -import React, { memo, useCallback, useMemo } from 'react'; +import React, { memo, useCallback, useMemo, useRef } from 'react'; import { useRefFrom } from 'use-ref-from'; +import { custom, literal, object, optional, pipe, readonly, safeParse, union, type InferInput } from 'valibot'; -import classNames from 'classnames'; -import useHasSubmitted from '../providers/useHasSubmitted'; -import useSelectedAction from '../providers/useSelectedAction'; -import useShouldAllowResubmit from '../providers/useShouldAllowResubmit'; -import useShouldShowFeedbackForm from '../providers/useShouldShowFeedbackForm'; +import { useListenToActivityFeedbackFocus } from '../providers/private/FocusPropagation'; +import useActivityFeedbackHooks from '../providers/useActivityFeedbackHooks'; import ThumbButton from './ThumbButton'; +import canActionResubmit from './canActionResubmit'; +import isActionRequireReview from './isActionRequireReview'; const { useLocalizer, useStyleOptions } = hooks; -type FeedbackVoteButtonProps = Readonly<{ - action: OrgSchemaAction; -}>; +const feedbackVoteButtonPropsSchema = pipe( + object({ + action: custom( + value => + safeParse( + union([ + object({ + '@type': union([literal('DislikeAction'), literal('LikeAction')]) + }), + object({ + '@type': literal('VoteAction'), + actionOption: optional(union([literal('downvote'), literal('upvote')])) + }) + ]), + value + ).success + ), + as: union([literal('button'), literal('radio')]) + }), + readonly() +); + +type FeedbackVoteButtonProps = InferInput; + +function FeedbackVoteButton(props: FeedbackVoteButtonProps) { + const { useHasSubmitted, useSelectedAction: useSelectedActions } = useActivityFeedbackHooks(); + + const { action, as } = validateProps(feedbackVoteButtonPropsSchema, props); -function FeedbackVoteButton({ action }: FeedbackVoteButtonProps) { const [{ feedbackActionsPlacement }] = useStyleOptions(); const [hasSubmitted] = useHasSubmitted(); - const [selectedAction, setSelectedAction] = useSelectedAction(); - const [shouldAllowResubmit] = useShouldAllowResubmit(); - const [shouldShowFeedbackForm] = useShouldShowFeedbackForm(); + const [selectedAction, setSelectedAction] = useSelectedActions(); const actionRef = useRefFrom(action); + const buttonRef = useRef(null); const direction = useMemo(() => { if ( action['@type'] === 'DislikeAction' || @@ -42,22 +66,26 @@ function FeedbackVoteButton({ action }: FeedbackVoteButtonProps) { () => setSelectedAction(actionRef.current === selectedActionRef.current ? undefined : actionRef.current), [actionRef, selectedActionRef, setSelectedAction] ); - const disabled = hasSubmitted && !shouldAllowResubmit; + const disabled = hasSubmitted && !canActionResubmit(action); + + useListenToActivityFeedbackFocus( + useCallback(target => target === actionRef.current && buttonRef.current?.focus(), [actionRef]) + ); return ( ); } export default memo(FeedbackVoteButton); -export { type FeedbackVoteButtonProps }; +export { feedbackVoteButtonPropsSchema, type FeedbackVoteButtonProps }; diff --git a/packages/component/src/ActivityFeedback/private/FeedbackVoteButtonBar.tsx b/packages/component/src/ActivityFeedback/private/FeedbackVoteButtonBar.tsx index 111f60b4bc..ce6b408015 100644 --- a/packages/component/src/ActivityFeedback/private/FeedbackVoteButtonBar.tsx +++ b/packages/component/src/ActivityFeedback/private/FeedbackVoteButtonBar.tsx @@ -1,25 +1,34 @@ -import React, { Fragment, memo, type ReactNode } from 'react'; +import { validateProps } from 'botframework-webchat-api/internal'; +import React, { memo } from 'react'; +import { literal, object, pipe, readonly, union, type InferInput } from 'valibot'; -import useActions from '../providers/useActions'; +import useActivityFeedbackHooks from '../providers/useActivityFeedbackHooks'; import FeedbackVoteButton from './FeedbackVoteButton'; -type FeedbackVoteButtonBarProps = Readonly<{ - children?: ReactNode | undefined; -}>; +const feedbackVoteButtonBarPropsSchema = pipe( + object({ + buttonAs: union([literal('button'), literal('radio')]) + }), + readonly() +); + +type FeedbackVoteButtonBarProps = InferInput; + +function FeedbackVoteButtonBar(props: FeedbackVoteButtonBarProps) { + const { buttonAs } = validateProps(feedbackVoteButtonBarPropsSchema, props); + + const { useActions } = useActivityFeedbackHooks(); -function FeedbackVoteButtonBar() { const [actions] = useActions(); return ( - +
{actions.map((action, index) => ( - + ))} - +
); } -FeedbackVoteButtonBar.displayName = 'FeedbackVoteButtonBar'; - export default memo(FeedbackVoteButtonBar); -export { type FeedbackVoteButtonBarProps }; +export { feedbackVoteButtonBarPropsSchema, type FeedbackVoteButtonBarProps }; diff --git a/packages/component/src/ActivityFeedback/private/ThumbButton.Image.tsx b/packages/component/src/ActivityFeedback/private/ThumbButton.Image.tsx index 4ebf55e3de..cd84ab3e49 100644 --- a/packages/component/src/ActivityFeedback/private/ThumbButton.Image.tsx +++ b/packages/component/src/ActivityFeedback/private/ThumbButton.Image.tsx @@ -1,18 +1,27 @@ +import { validateProps } from 'botframework-webchat-api/internal'; import React, { memo } from 'react'; +import { boolean, literal, object, optional, pipe, readonly, string, union, type InferInput } from 'valibot'; import ThumbDislike16Filled from './ThumbDislike16Filled'; import ThumbDislike16Regular from './ThumbDislike16Regular'; import ThumbLike16Filled from './ThumbLike16Filled'; import ThumbLike16Regular from './ThumbLike16Regular'; -type Props = Readonly<{ - className?: string; - direction: 'down' | 'up'; - filled?: boolean; -}>; +const thumbButtonImagePropsSchema = pipe( + object({ + className: optional(string()), + direction: union([literal('down'), literal('up')]), + filled: optional(boolean()) + }), + readonly() +); + +type ThumbButtonImageProps = InferInput; -const ThumbButtonImage = memo(({ className, direction, filled = false }: Props) => - direction === 'down' ? ( +const ThumbButtonImage = memo((props: ThumbButtonImageProps) => { + const { className, direction, filled = false } = validateProps(thumbButtonImagePropsSchema, props); + + return direction === 'down' ? ( filled ? ( ) : ( @@ -22,9 +31,10 @@ const ThumbButtonImage = memo(({ className, direction, filled = false }: Props) ) : ( - ) -); + ); +}); ThumbButtonImage.displayName = 'ThumbButtonImage'; export default ThumbButtonImage; +export { thumbButtonImagePropsSchema, type ThumbButtonImageProps }; diff --git a/packages/component/src/ActivityFeedback/private/ThumbButton.tsx b/packages/component/src/ActivityFeedback/private/ThumbButton.tsx index ac5ce993c4..2a1a0bbc66 100644 --- a/packages/component/src/ActivityFeedback/private/ThumbButton.tsx +++ b/packages/component/src/ActivityFeedback/private/ThumbButton.tsx @@ -1,24 +1,40 @@ import { hooks } from 'botframework-webchat-api'; +import { validateProps } from 'botframework-webchat-api/internal'; import classNames from 'classnames'; -import React, { memo, useCallback, useMemo } from 'react'; +import React, { forwardRef, memo, useCallback, useMemo, type ForwardedRef, type KeyboardEventHandler } from 'react'; import { useRefFrom } from 'use-ref-from'; +import { boolean, function_, literal, object, optional, pipe, readonly, string, union, type InferInput } from 'valibot'; -import ThumbButtonImage from './ThumbButton.Image'; import useStyleSet from '../../hooks/useStyleSet'; +import testIds from '../../testIds'; import { Tooltip } from '../../Tooltip'; +import ThumbButtonImage from './ThumbButton.Image'; const { useLocalizer } = hooks; -type Props = Readonly<{ - className?: string | undefined; - direction: 'down' | 'up'; - disabled?: boolean | undefined; - onClick?: () => void; - pressed?: boolean; - title?: string | undefined; -}>; +const thumbButtonPropsSchema = pipe( + object({ + as: union([literal('button'), literal('radio')]), + className: optional(string()), + direction: union([literal('down'), literal('up')]), + disabled: optional(boolean()), + onClick: optional(function_()), + pressed: optional(boolean()), + size: union([literal('small'), literal('large')]), + submitted: optional(boolean()), + title: optional(string()) + }), + readonly() +); + +type ThumbButtonProps = InferInput; + +function ThumbButton(props: ThumbButtonProps, ref: ForwardedRef) { + const { as, className, direction, disabled, onClick, pressed, size, submitted, title } = validateProps( + thumbButtonPropsSchema, + props + ); -const ThumbButton = memo(({ className, direction, disabled, onClick, pressed, title }: Props) => { const [{ thumbButton }] = useStyleSet(); const localize = useLocalizer(); const onClickRef = useRefFrom(onClick); @@ -28,22 +44,49 @@ const ThumbButton = memo(({ className, direction, disabled, onClick, pressed, ti [direction, localize, title] ); - const handleClick = useCallback(() => !disabled && onClickRef.current?.(), [disabled, onClickRef]); + const handleClickOrChange = useCallback(() => !disabled && onClickRef.current?.(), [disabled, onClickRef]); + + const handleKeyDown = useCallback>(event => { + // Do not submit the
via ENTER key. + event.key === 'Enter' && event.preventDefault(); + }, []); return ( - + {buttonTitle} + ); -}); - -ThumbButton.displayName = 'ThumbButton'; +} -export default ThumbButton; +export default memo(forwardRef(ThumbButton)); +export { thumbButtonPropsSchema, type ThumbButtonProps }; diff --git a/packages/component/src/ActivityFeedback/private/ThumbDislike16Filled.tsx b/packages/component/src/ActivityFeedback/private/ThumbDislike16Filled.tsx index e9793cc4ed..6e402f2bf1 100644 --- a/packages/component/src/ActivityFeedback/private/ThumbDislike16Filled.tsx +++ b/packages/component/src/ActivityFeedback/private/ThumbDislike16Filled.tsx @@ -1,16 +1,36 @@ +import { validateProps } from 'botframework-webchat-api/internal'; import React, { memo } from 'react'; +import { object, optional, pipe, readonly, string, type InferInput } from 'valibot'; -type Props = Readonly<{ className?: string }>; +const thumbDislike16FilledPropsSchema = pipe( + object({ + className: optional(string()) + }), + readonly() +); -const ThumbDislike16Filled = memo(({ className }: Props) => ( - - - -)); +type ThumbDislike16FilledProps = InferInput; -ThumbDislike16Filled.displayName = 'ThumbDislike16Filled'; +function ThumbDislike16Filled(props: ThumbDislike16FilledProps) { + const { className } = validateProps(thumbDislike16FilledPropsSchema, props); -export default ThumbDislike16Filled; + return ( + + + + ); +} + +export default memo(ThumbDislike16Filled); +export { thumbDislike16FilledPropsSchema, type ThumbDislike16FilledProps }; diff --git a/packages/component/src/ActivityFeedback/private/ThumbDislike16Regular.tsx b/packages/component/src/ActivityFeedback/private/ThumbDislike16Regular.tsx index f1c88f74ed..ffd4c064cc 100644 --- a/packages/component/src/ActivityFeedback/private/ThumbDislike16Regular.tsx +++ b/packages/component/src/ActivityFeedback/private/ThumbDislike16Regular.tsx @@ -1,16 +1,37 @@ +import { validateProps } from 'botframework-webchat-api/internal'; import React, { memo } from 'react'; +import { object, optional, pipe, readonly, string, type InferInput } from 'valibot'; -type Props = Readonly<{ className?: string }>; +const thumbDislike16RegularPropsSchema = pipe( + object({ + className: optional(string()) + }), + readonly() +); -const ThumbDislike16Regular = memo(({ className }: Props) => ( - - - -)); +type ThumbDislike16RegularProps = InferInput; -ThumbDislike16Regular.displayName = 'ThumbDislike16Regular'; +function ThumbDislike16Regular(props: ThumbDislike16RegularProps) { + const { className } = validateProps(thumbDislike16RegularPropsSchema, props); -export default ThumbDislike16Regular; + return ( + + + + ); +} + +export default memo(ThumbDislike16Regular); + +export { thumbDislike16RegularPropsSchema, type ThumbDislike16RegularProps }; diff --git a/packages/component/src/ActivityFeedback/private/ThumbLike16Filled.tsx b/packages/component/src/ActivityFeedback/private/ThumbLike16Filled.tsx index b8eece9a3f..7f69232720 100644 --- a/packages/component/src/ActivityFeedback/private/ThumbLike16Filled.tsx +++ b/packages/component/src/ActivityFeedback/private/ThumbLike16Filled.tsx @@ -1,16 +1,36 @@ +import { validateProps } from 'botframework-webchat-api/internal'; import React, { memo } from 'react'; +import { object, optional, pipe, readonly, string, type InferInput } from 'valibot'; -type Props = Readonly<{ className?: string }>; +const thumbLike16FilledPropsSchema = pipe( + object({ + className: optional(string()) + }), + readonly() +); -const ThumbLike16Filled = memo(({ className }: Props) => ( - - - -)); +type ThumbLike16FilledProps = InferInput; -ThumbLike16Filled.displayName = 'ThumbLike16Filled'; +function ThumbLike16Filled(props: ThumbLike16FilledProps) { + const { className } = validateProps(thumbLike16FilledPropsSchema, props); -export default ThumbLike16Filled; + return ( + + + + ); +} + +export default memo(ThumbLike16Filled); +export { thumbLike16FilledPropsSchema, type ThumbLike16FilledProps }; diff --git a/packages/component/src/ActivityFeedback/private/ThumbLike16Regular.tsx b/packages/component/src/ActivityFeedback/private/ThumbLike16Regular.tsx index 1afa477d11..4321a7b747 100644 --- a/packages/component/src/ActivityFeedback/private/ThumbLike16Regular.tsx +++ b/packages/component/src/ActivityFeedback/private/ThumbLike16Regular.tsx @@ -1,16 +1,36 @@ +import { validateProps } from 'botframework-webchat-api/internal'; import React, { memo } from 'react'; +import { object, optional, pipe, readonly, string, type InferInput } from 'valibot'; -type Props = Readonly<{ className?: string }>; +const thumbLike16RegularPropsSchema = pipe( + object({ + className: optional(string()) + }), + readonly() +); -const ThumbLike16Regular = memo(({ className }: Props) => ( - - - -)); +type ThumbLike16RegularProps = InferInput; -ThumbLike16Regular.displayName = 'ThumbLike16Regular'; +function ThumbLike16Regular(props: ThumbLike16RegularProps) { + const { className } = validateProps(thumbLike16RegularPropsSchema, props); -export default ThumbLike16Regular; + return ( + + + + ); +} + +export default memo(ThumbLike16Regular); +export { thumbLike16RegularPropsSchema, type ThumbLike16RegularProps }; diff --git a/packages/component/src/ActivityFeedback/private/canActionResubmit.ts b/packages/component/src/ActivityFeedback/private/canActionResubmit.ts new file mode 100644 index 0000000000..d667c6a296 --- /dev/null +++ b/packages/component/src/ActivityFeedback/private/canActionResubmit.ts @@ -0,0 +1,6 @@ +import { type OrgSchemaAction } from 'botframework-webchat-core'; +import isActionRequireReview from './isActionRequireReview'; + +export default function canActionResubmit(action: OrgSchemaAction | undefined): boolean { + return !isActionRequireReview(action); +} diff --git a/packages/component/src/ActivityFeedback/private/getDisclaimer.ts b/packages/component/src/ActivityFeedback/private/getDisclaimerFromFeedbackLoop.ts similarity index 92% rename from packages/component/src/ActivityFeedback/private/getDisclaimer.ts rename to packages/component/src/ActivityFeedback/private/getDisclaimerFromFeedbackLoop.ts index 5872f96931..db23247918 100644 --- a/packages/component/src/ActivityFeedback/private/getDisclaimer.ts +++ b/packages/component/src/ActivityFeedback/private/getDisclaimerFromFeedbackLoop.ts @@ -1,6 +1,10 @@ import { type WebChatActivity } from 'botframework-webchat-core'; + import { hasDisclaimer } from './hasFeedbackLoop'; +/** + * @deprecated + */ export default function getDisclaimer(activity: WebChatActivity): string | undefined { return hasDisclaimer(activity) ? activity.channelData.feedbackLoop.disclaimer : undefined; } diff --git a/packages/component/src/ActivityFeedback/private/getDisclaimerFromReviewAction.ts b/packages/component/src/ActivityFeedback/private/getDisclaimerFromReviewAction.ts new file mode 100644 index 0000000000..3568644b85 --- /dev/null +++ b/packages/component/src/ActivityFeedback/private/getDisclaimerFromReviewAction.ts @@ -0,0 +1,9 @@ +import { type OrgSchemaAction, type OrgSchemaThing, type OrgSchemaUserReview } from 'botframework-webchat-core'; + +function isUserReview(thing: OrgSchemaThing | undefined): thing is OrgSchemaUserReview { + return thing?.['@type'] === 'UserReview'; +} + +export default function getDisclaimerFromActivity(action: OrgSchemaAction): string | undefined { + return isUserReview(action.result) ? action.result.reviewAspect : undefined; +} diff --git a/packages/component/src/ActivityFeedback/private/hasFeedbackLoop.ts b/packages/component/src/ActivityFeedback/private/hasFeedbackLoop.ts index 7fae93a243..bfd7e2e8e7 100644 --- a/packages/component/src/ActivityFeedback/private/hasFeedbackLoop.ts +++ b/packages/component/src/ActivityFeedback/private/hasFeedbackLoop.ts @@ -25,12 +25,18 @@ const feedbackLoopSchema = union([ type FeedbackActivity = WebChatActivity & InferOutput; +/** + * @deprecated + */ export function hasDisclaimer( activity: WebChatActivity ): activity is WebChatActivity & InferOutput { return safeParse(activityWithFeedbackLoopSchemaWithDisclaimer, activity).success; } +/** + * @deprecated + */ export default function hasFeedbackLoop(activity: WebChatActivity): activity is FeedbackActivity { return safeParse(feedbackLoopSchema, activity).success; } diff --git a/packages/component/src/ActivityFeedback/private/isActionRequireReview.ts b/packages/component/src/ActivityFeedback/private/isActionRequireReview.ts new file mode 100644 index 0000000000..4838378dd9 --- /dev/null +++ b/packages/component/src/ActivityFeedback/private/isActionRequireReview.ts @@ -0,0 +1,7 @@ +import { type OrgSchemaAction } from 'botframework-webchat-core'; + +export default function isActionRequireReview( + action: OrgSchemaAction | undefined +): action is OrgSchemaAction & { result: { '@type': 'UserReview' } } { + return action?.result?.['@type'] === 'UserReview'; +} diff --git a/packages/component/src/ActivityFeedback/providers/ActivityFeedbackComposer.tsx b/packages/component/src/ActivityFeedback/providers/ActivityFeedbackComposer.tsx index f1fc9ff4fc..b7f1560858 100644 --- a/packages/component/src/ActivityFeedback/providers/ActivityFeedbackComposer.tsx +++ b/packages/component/src/ActivityFeedback/providers/ActivityFeedbackComposer.tsx @@ -1,4 +1,5 @@ import { hooks } from 'botframework-webchat-api'; +import { validateProps } from 'botframework-webchat-api/internal'; import { getOrgSchemaMessage, parseAction, @@ -6,19 +7,31 @@ import { type WebChatActivity } from 'botframework-webchat-core'; import random from 'math-random'; -import React, { memo, useCallback, useMemo, useRef, useState, type ReactNode } from 'react'; +import React, { memo, useCallback, useMemo, useRef, useState, type Dispatch, type SetStateAction } from 'react'; +import { wrapWith } from 'react-wrap-with'; import { useRefFrom } from 'use-ref-from'; +import { useStateWithRef } from 'use-state-with-ref'; +import { custom, object, optional, pipe, readonly, safeParse, type InferInput } from 'valibot'; +import reactNode from '../../types/internal/reactNode'; import dereferenceBlankNodes from '../../Utils/JSONLinkedData/dereferenceBlankNodes'; +import canActionResubmit from '../private/canActionResubmit'; +import getDisclaimerFromFeedbackLoop from '../private/getDisclaimerFromFeedbackLoop'; import hasFeedbackLoop from '../private/hasFeedbackLoop'; import ActivityFeedbackContext, { type ActivityFeedbackContextType } from './private/ActivityFeedbackContext'; +import { ActivityFeedbackFocusPropagationScope, usePropagateActivityFeedbackFocus } from './private/FocusPropagation'; const { usePonyfill, usePostActivity } = hooks; -type ActivityFeedbackComposerProps = Readonly<{ - activity: WebChatActivity; - children?: ReactNode | undefined; -}>; +const activityFeedbackComposerPropsSchema = pipe( + object({ + activity: custom(value => safeParse(object({}), value).success), + children: optional(reactNode()) + }), + readonly() +); + +type ActivityFeedbackComposerProps = InferInput; type ActionState = Readonly<{ actionId: string; @@ -27,10 +40,12 @@ type ActionState = Readonly<{ const DEBOUNCE_TIMEOUT = 500; -function ActivityFeedbackComposer({ children, activity: activityFromProps }: ActivityFeedbackComposerProps) { - const [{ clearTimeout, setTimeout }] = usePonyfill(); +function ActivityFeedbackComposer(props: ActivityFeedbackComposerProps) { + const { children, activity: activityFromProps } = validateProps(activityFeedbackComposerPropsSchema, props); - const activity = useMemo( + const [{ clearTimeout, setTimeout }] = usePonyfill(); + const [feedbackText, setFeedbackText, feedbackTextRef] = useStateWithRef(); + const activity: WebChatActivity = useMemo( () => // Force enable feedback loop until service fixed their issue. hasFeedbackLoop(activityFromProps) @@ -44,8 +59,26 @@ function ActivityFeedbackComposer({ children, activity: activityFromProps }: Act type: 'https://schema.org/Message', keywords: [], potentialAction: [ - { '@type': 'LikeAction', actionStatus: 'PotentialActionStatus' }, - { '@type': 'DislikeAction', actionStatus: 'PotentialActionStatus' } + { + '@type': 'LikeAction', + actionStatus: 'PotentialActionStatus', + result: [ + { + '@type': 'UserReview', + reviewAspect: getDisclaimerFromFeedbackLoop(activityFromProps) + } + ] + }, + { + '@type': 'DislikeAction', + actionStatus: 'PotentialActionStatus', + result: [ + { + '@type': 'UserReview', + reviewAspect: getDisclaimerFromFeedbackLoop(activityFromProps) + } + ] + } ] } ] @@ -69,10 +102,28 @@ function ActivityFeedbackComposer({ children, activity: activityFromProps }: Act const rawActions = useMemo(() => { function patchActions(actions: readonly OrgSchemaAction[]) { - return actions.map(action => + actions = actions.map(action => // eslint-disable-next-line no-magic-numbers action['@id'] ? action : Object.freeze({ ...action, '@id': `_:${random().toString(36).substring(2, 7)}` }) ); + + const deprecatedFeedbackLoopChannelData = activity.channelData?.feedbackLoop; + + if (deprecatedFeedbackLoopChannelData) { + actions = actions.map(action => + action.result + ? action + : { + ...action, + result: { + '@type': 'UserReview', + description: deprecatedFeedbackLoopChannelData?.disclaimer + } + } + ); + } + + return actions; } try { @@ -86,15 +137,20 @@ function ActivityFeedbackComposer({ children, activity: activityFromProps }: Act ); if (reactActions.length) { - // actionStateRef.current = reactActions. return patchActions(reactActions); } const voteActions = Object.freeze( graph.filter(({ type }) => type === 'https://schema.org/VoteAction').map(parseAction) + // TODO: Instead of processing VoteAction, convert it to LikeAction/DislikeAction. + // .map(action => ({ + // ...action, + // '@type': action.actionOption === 'downvote' ? 'DislikeAction' : 'LikeAction' + // })) ); if (voteActions.length) { + // VoteAction is deprecated and was never published publicly. return patchActions(voteActions); } } catch { @@ -146,8 +202,8 @@ function ActivityFeedbackComposer({ children, activity: activityFromProps }: Act const hasSubmittedRef = useRefFrom(hasSubmitted); - const submitCallback = useCallback( - (action: OrgSchemaAction, feedbackText?: string | undefined) => { + const submit = useCallback( + (action: OrgSchemaAction) => { if (actionStateRef.current?.actionStatus === 'CompletedActionStatus') { return console.warn( 'botframework-webchat internal: useFeedbackActions().submitCallback() must not be called after feedback is completed, ignoring the call.' @@ -161,10 +217,11 @@ function ActivityFeedbackComposer({ children, activity: activityFromProps }: Act setActionStateWithRefresh(Object.freeze({ actionId: action['@id'], actionStatus: 'CompletedActionStatus' })); const { '@id': _id, actionStatus: _actionStatus, ...rest } = action; + const { current: feedbackText } = feedbackTextRef; const isLegacyAction = action['@type'] === 'VoteAction'; // TODO: We should update this to use W3C Hydra.1 - if (feedbackText) { + if (typeof feedbackText !== 'undefined') { postActivity({ name: 'message/submitAction', replyToId: activityRef.current.id, @@ -185,24 +242,7 @@ function ActivityFeedbackComposer({ children, activity: activityFromProps }: Act } as any); } }, - [actionsRef, actionStateRef, activityRef, postActivity, setActionStateWithRefresh] - ); - - const shouldShowFeedbackForm = hasFeedbackLoop(activity); - const shouldShowFeedbackFormRef = useRefFrom(shouldShowFeedbackForm); - - const shouldShowFeedbackFormState = useMemo( - () => Object.freeze([shouldShowFeedbackForm] as const), - [shouldShowFeedbackForm] - ); - - // TODO: What's the proper logic of "allow resubmission"? - // Right now, if feedback form is not shown, it will allow resubmission. - const shouldAllowResubmit = !shouldShowFeedbackForm; - const shouldAllowResubmitRef = useRefFrom(shouldAllowResubmit); - const shouldAllowResubmitState = useMemo( - () => Object.freeze([shouldAllowResubmit]), - [shouldAllowResubmit] + [actionsRef, actionStateRef, activityRef, feedbackTextRef, postActivity, setActionStateWithRefresh] ); const selectedAction = useMemo( @@ -217,7 +257,10 @@ function ActivityFeedbackComposer({ children, activity: activityFromProps }: Act const setSelectedAction = useCallback( (action: OrgSchemaAction | undefined) => { - if (hasSubmittedRef.current && !shouldAllowResubmitRef.current) { + // If the action require a UserReview, do not allow resubmit. + const shouldAllowResubmit = canActionResubmit(action); + + if (hasSubmittedRef.current && !shouldAllowResubmit) { return console.warn( 'botframework-webchat internal: useFeedbackActions().setSelectedAction() must not be called after feedback is completed as it does not allow resubmission, ignoring the call.' ); @@ -236,28 +279,18 @@ function ActivityFeedbackComposer({ children, activity: activityFromProps }: Act : undefined ); - if (!shouldShowFeedbackFormRef.current) { + if (shouldAllowResubmit) { clearTimeout(autoSubmitTimeoutRef.current); - if (action['@id']) { + if (action?.['@id']) { autoSubmitTimeoutRef.current = setTimeout( - () => submitCallback(actionsRef.current.find(({ '@id': id }) => id === action['@id'])), + () => submit(actionsRef.current.find(({ '@id': id }) => id === action['@id'])), DEBOUNCE_TIMEOUT ); } } }, - [ - actionsRef, - autoSubmitTimeoutRef, - clearTimeout, - hasSubmittedRef, - setActionStateWithRefresh, - setTimeout, - shouldAllowResubmitRef, - shouldShowFeedbackFormRef, - submitCallback - ] + [actionsRef, autoSubmitTimeoutRef, clearTimeout, hasSubmittedRef, setActionStateWithRefresh, setTimeout, submit] ); const selectedActionState = useMemo void]>( @@ -265,38 +298,44 @@ function ActivityFeedbackComposer({ children, activity: activityFromProps }: Act [selectedAction, setSelectedAction] ); - const actionsState = useMemo( + const actionsState = useMemo( () => Object.freeze([actions] as const), [actions] ); + const feedbackTextState = useMemo>]>( + () => Object.freeze([feedbackText, setFeedbackText]), + [feedbackText, setFeedbackText] + ); + const hasSubmittedState = useMemo(() => Object.freeze([hasSubmitted]), [hasSubmitted]); const activityState = useMemo(() => Object.freeze([activity]), [activity]); + const focusFeedbackButton = usePropagateActivityFeedbackFocus(); + + const useActions = useCallback(() => actionsState, [actionsState]); + const useActivity = useCallback(() => activityState, [activityState]); + const useFeedbackText = useCallback(() => feedbackTextState, [feedbackTextState]); + const useFocusFeedbackButton = useCallback(() => focusFeedbackButton, [focusFeedbackButton]); + const useHasSubmitted = useCallback(() => hasSubmittedState, [hasSubmittedState]); + const useSelectedActions = useCallback(() => selectedActionState, [selectedActionState]); + const useSubmit = useCallback(() => submit, [submit]); const context = useMemo( () => ({ - actionsState, - activityState, - hasSubmittedState, - selectedActionState, - shouldAllowResubmitState, - shouldShowFeedbackFormState, - submitCallback + useActions, + useActivity, + useFeedbackText, + useFocusFeedbackButton, + useHasSubmitted, + useSelectedAction: useSelectedActions, + useSubmit }), - [ - actionsState, - activityState, - hasSubmittedState, - selectedActionState, - shouldAllowResubmitState, - shouldShowFeedbackFormState, - submitCallback - ] + [useActions, useActivity, useFeedbackText, useFocusFeedbackButton, useHasSubmitted, useSelectedActions, useSubmit] ); return {children}; } -export default memo(ActivityFeedbackComposer); -export { type ActivityFeedbackComposerProps }; +export default memo(wrapWith(ActivityFeedbackFocusPropagationScope)(ActivityFeedbackComposer)); +export { activityFeedbackComposerPropsSchema, type ActivityFeedbackComposerProps }; diff --git a/packages/component/src/ActivityFeedback/providers/private/ActivityFeedbackContext.ts b/packages/component/src/ActivityFeedback/providers/private/ActivityFeedbackContext.ts index edf8f989c4..6c50b1315c 100644 --- a/packages/component/src/ActivityFeedback/providers/private/ActivityFeedbackContext.ts +++ b/packages/component/src/ActivityFeedback/providers/private/ActivityFeedbackContext.ts @@ -1,14 +1,14 @@ import { type OrgSchemaAction, type WebChatActivity } from 'botframework-webchat-core'; -import { createContext } from 'react'; +import { createContext, type Dispatch, type SetStateAction } from 'react'; type ActivityFeedbackContextType = Readonly<{ - actionsState: readonly [readonly OrgSchemaAction[]]; - hasSubmittedState: readonly [boolean]; - activityState: readonly [WebChatActivity]; - selectedActionState: readonly [OrgSchemaAction, (action: OrgSchemaAction) => void]; - shouldAllowResubmitState: readonly [boolean]; - shouldShowFeedbackFormState: readonly [boolean]; - submitCallback: (action: OrgSchemaAction) => void; + useActions: () => readonly [readonly OrgSchemaAction[]]; + useActivity: () => readonly [WebChatActivity]; + useFeedbackText: () => readonly [string, Dispatch>]; + useFocusFeedbackButton: () => (action: OrgSchemaAction) => void; + useHasSubmitted: () => readonly [boolean]; + useSelectedAction: () => readonly [OrgSchemaAction, (action: OrgSchemaAction) => void]; + useSubmit: () => (action: OrgSchemaAction) => void; }>; const ActivityFeedbackContext = createContext( diff --git a/packages/component/src/ActivityFeedback/providers/private/FocusPropagation.ts b/packages/component/src/ActivityFeedback/providers/private/FocusPropagation.ts new file mode 100644 index 0000000000..790afbc2b8 --- /dev/null +++ b/packages/component/src/ActivityFeedback/providers/private/FocusPropagation.ts @@ -0,0 +1,10 @@ +import { type OrgSchemaAction } from 'botframework-webchat-core'; +import { createPropagation } from 'use-propagate'; + +const { + PropagationScope: ActivityFeedbackFocusPropagationScope, + useListen: useListenToActivityFeedbackFocus, + usePropagate: usePropagateActivityFeedbackFocus +} = createPropagation(); + +export { ActivityFeedbackFocusPropagationScope, useListenToActivityFeedbackFocus, usePropagateActivityFeedbackFocus }; diff --git a/packages/component/src/ActivityFeedback/providers/useActions.ts b/packages/component/src/ActivityFeedback/providers/useActions.ts deleted file mode 100644 index 3d36445647..0000000000 --- a/packages/component/src/ActivityFeedback/providers/useActions.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { type OrgSchemaAction } from 'botframework-webchat-core'; -import useActivityFeedbackContext from './private/useActivityFeedbackContext'; - -export default function useActions(): readonly [readonly OrgSchemaAction[]] { - return useActivityFeedbackContext().actionsState; -} diff --git a/packages/component/src/ActivityFeedback/providers/useActivity.ts b/packages/component/src/ActivityFeedback/providers/useActivity.ts deleted file mode 100644 index a39e9d8f2e..0000000000 --- a/packages/component/src/ActivityFeedback/providers/useActivity.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { type WebChatActivity } from 'botframework-webchat-core'; -import useActivityFeedbackContext from './private/useActivityFeedbackContext'; - -export default function useActivity(): readonly [WebChatActivity] { - return useActivityFeedbackContext().activityState; -} diff --git a/packages/component/src/ActivityFeedback/providers/private/useActivityFeedbackContext.ts b/packages/component/src/ActivityFeedback/providers/useActivityFeedbackHooks.ts similarity index 53% rename from packages/component/src/ActivityFeedback/providers/private/useActivityFeedbackContext.ts rename to packages/component/src/ActivityFeedback/providers/useActivityFeedbackHooks.ts index 04c4ddbf27..bec2a8e566 100644 --- a/packages/component/src/ActivityFeedback/providers/private/useActivityFeedbackContext.ts +++ b/packages/component/src/ActivityFeedback/providers/useActivityFeedbackHooks.ts @@ -1,6 +1,6 @@ import { useContext } from 'react'; -import ActivityFeedbackContext, { type ActivityFeedbackContextType } from './ActivityFeedbackContext'; +import ActivityFeedbackContext, { type ActivityFeedbackContextType } from './private/ActivityFeedbackContext'; -export default function useActivityFeedbackContext(): ActivityFeedbackContextType { +export default function useActivityFeedbackHooks(): ActivityFeedbackContextType { return useContext(ActivityFeedbackContext); } diff --git a/packages/component/src/ActivityFeedback/providers/useHasSubmitted.ts b/packages/component/src/ActivityFeedback/providers/useHasSubmitted.ts deleted file mode 100644 index b8a56b27a3..0000000000 --- a/packages/component/src/ActivityFeedback/providers/useHasSubmitted.ts +++ /dev/null @@ -1,5 +0,0 @@ -import useActivityFeedbackContext from './private/useActivityFeedbackContext'; - -export default function useHasSubmitted(): readonly [boolean] { - return useActivityFeedbackContext().hasSubmittedState; -} diff --git a/packages/component/src/ActivityFeedback/providers/useSelectedAction.ts b/packages/component/src/ActivityFeedback/providers/useSelectedAction.ts deleted file mode 100644 index f8aed06a9a..0000000000 --- a/packages/component/src/ActivityFeedback/providers/useSelectedAction.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { type OrgSchemaAction } from 'botframework-webchat-core'; -import useActivityFeedbackContext from './private/useActivityFeedbackContext'; - -export default function useSelectedAction(): readonly [OrgSchemaAction, (target: OrgSchemaAction) => void] { - return useActivityFeedbackContext().selectedActionState; -} diff --git a/packages/component/src/ActivityFeedback/providers/useShouldAllowResubmit.ts b/packages/component/src/ActivityFeedback/providers/useShouldAllowResubmit.ts deleted file mode 100644 index bb9c8b1b13..0000000000 --- a/packages/component/src/ActivityFeedback/providers/useShouldAllowResubmit.ts +++ /dev/null @@ -1,5 +0,0 @@ -import useActivityFeedbackContext from './private/useActivityFeedbackContext'; - -export default function useShouldAllowResubmit(): readonly [boolean] { - return useActivityFeedbackContext().shouldAllowResubmitState; -} diff --git a/packages/component/src/ActivityFeedback/providers/useShouldShowFeedbackForm.ts b/packages/component/src/ActivityFeedback/providers/useShouldShowFeedbackForm.ts deleted file mode 100644 index f78952795d..0000000000 --- a/packages/component/src/ActivityFeedback/providers/useShouldShowFeedbackForm.ts +++ /dev/null @@ -1,5 +0,0 @@ -import useActivityFeedbackContext from './private/useActivityFeedbackContext'; - -export default function useShouldShowFeedbackForm(): readonly [boolean] { - return useActivityFeedbackContext().shouldShowFeedbackFormState; -} diff --git a/packages/component/src/ActivityFeedback/providers/useSubmitCallback.ts b/packages/component/src/ActivityFeedback/providers/useSubmitCallback.ts deleted file mode 100644 index dab8f9376f..0000000000 --- a/packages/component/src/ActivityFeedback/providers/useSubmitCallback.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { type OrgSchemaAction } from 'botframework-webchat-core'; -import useActivityFeedbackContext from './private/useActivityFeedbackContext'; - -export default function useSubmitCallback(): (target: OrgSchemaAction, message?: string | undefined) => void { - return useActivityFeedbackContext().submitCallback; -} diff --git a/packages/component/src/Styles/StyleSet/FeedbackForm.ts b/packages/component/src/Styles/StyleSet/FeedbackForm.ts index 368ee2ad5d..dc0180f84e 100644 --- a/packages/component/src/Styles/StyleSet/FeedbackForm.ts +++ b/packages/component/src/Styles/StyleSet/FeedbackForm.ts @@ -2,77 +2,96 @@ import CSSTokens from '../CSSTokens'; export default function createFeedbackFormStyle() { return { - '&.webchat__feedback-form__root-container': { - display: 'flex', - flexDirection: 'column', - gap: '8px', - width: '100%' - }, - '.webchat__feedback-form__root-child': { - display: 'flex' - }, - '.webchat__feedback-form': { - display: 'flex', - flexDirection: 'column', - gap: '4px', - position: 'relative' - }, - '.webchat__feedback-form__body': { - fontFamily: CSSTokens.FontPrimary, - fontStyle: 'normal', - fontWeight: 400, - fontSize: '14px', - lineHeight: '20px', - color: '#373435' - }, - '.webchat__feedback-form__caption': { - fontFamily: CSSTokens.FontPrimary, - fontStyle: 'normal', - fontWeight: 400, - fontSize: '10px', - lineHeight: '14px', - color: CSSTokens.ColorSubtle - }, - '.webchat__feedback-form__container': { - display: 'flex', - gap: '8px', - marginTop: '6px' - }, - '.webchat__feedback-form__submit-button': { - backgroundColor: CSSTokens.ColorAccent, - border: `1px solid ${CSSTokens.ColorAccent}`, - borderRadius: '4px', - color: '#FFFFFF', - cursor: 'pointer', - fontSize: '12px', - fontFamily: CSSTokens.FontPrimary, - height: '24px', - padding: '0 8px' - }, - '.webchat__feedback-form__submit-button:hover': { - backgroundColor: '#004a98', - border: '1px solid #004a98', - color: '#FFFFFF' - }, - '.webchat__feedback-form__submit-button:active': { - backgroundColor: '#004a98', - border: '1px solid #004a98', - color: '#FFFFFF' - }, - '.webchat__feedback-form__cancel-button': { - backgroundColor: '#FFFFFF', - borderRadius: '4px', - cursor: 'pointer', - fontSize: '12px', - border: '1px solid #E8E8E8', - color: CSSTokens.ColorSubtle, - fontFamily: CSSTokens.FontPrimary, - height: '24px', - padding: '0 8px' - }, - '.webchat__feedback-form__cancel-button:hover': { - backgroundColor: CSSTokens.ColorSubtle, - color: '#FFFFFF' + '&.webchat__feedback-form': { + display: 'contents', + + '& .webchat__feedback-form__vote-button-bar': { + display: 'flex', + gap: '2px' + }, + + '& .webchat__feedback-form__form': { + display: 'flex', + flexDirection: 'column', + gap: '4px', + position: 'relative', + // The form should take the full width of the flex container width. + width: '100%' + }, + + // TODO: Need review. + '& .webchat__feedback-form__form-header': { + color: '#373435', + display: 'flex', + flexDirection: 'column', + fontFamily: CSSTokens.FontPrimary, + fontSize: '14px', + fontStyle: 'normal', + fontWeight: 400, + lineHeight: '20px' + }, + + // TODO: Need review. + '& .webchat__feedback-form__form-footer': { + color: CSSTokens.ColorSubtle, + fontFamily: CSSTokens.FontPrimary, + fontSize: '10px', + fontStyle: 'normal', + fontWeight: 400, + lineHeight: '14px' + }, + + '& .webchat__feedback-form__submission-button-bar': { + display: 'flex', + gap: '8px', + marginTop: '6px' + }, + + // TODO: Need review. + '& .webchat__feedback-form__submit-button': { + backgroundColor: CSSTokens.ColorAccent, + border: `1px solid ${CSSTokens.ColorAccent}`, + borderRadius: '4px', + color: '#FFFFFF', + cursor: 'pointer', + fontSize: '12px', + fontFamily: CSSTokens.FontPrimary, + height: '24px', + padding: '0 8px' + }, + + // TODO: Need review. + '& .webchat__feedback-form__submit-button:hover': { + backgroundColor: '#004a98', + border: '1px solid #004a98', + color: '#FFFFFF' + }, + + // TODO: Need review. + '& .webchat__feedback-form__submit-button:active': { + backgroundColor: '#004a98', + border: '1px solid #004a98', + color: '#FFFFFF' + }, + + // TODO: Need review. + '& .webchat__feedback-form__cancel-button': { + backgroundColor: '#FFFFFF', + border: '1px solid #E8E8E8', + borderRadius: '4px', + color: CSSTokens.ColorSubtle, + cursor: 'pointer', + fontFamily: CSSTokens.FontPrimary, + fontSize: '12px', + height: '24px', + padding: '0 8px' + }, + + // TODO: Need review. + '& .webchat__feedback-form__cancel-button:hover': { + backgroundColor: CSSTokens.ColorSubtle, + color: 'White' + } } }; } diff --git a/packages/component/src/Styles/StyleSet/TextContent.ts b/packages/component/src/Styles/StyleSet/TextContent.ts index af171dc80e..91d491faa7 100644 --- a/packages/component/src/Styles/StyleSet/TextContent.ts +++ b/packages/component/src/Styles/StyleSet/TextContent.ts @@ -31,7 +31,8 @@ export default function createTextContentStyle() { '& .webchat__text-content__activity-actions': { alignSelf: 'flex-start', display: 'flex', - gap: `calc(${CSSTokens.PaddingRegular} / 2)`, + flexWrap: 'wrap', + gap: `8px calc(${CSSTokens.PaddingRegular} / 2)`, width: '100%' }, diff --git a/packages/component/src/Styles/StyleSet/ThumbButton.ts b/packages/component/src/Styles/StyleSet/ThumbButton.ts index 2187a6d3d0..b06a64343a 100644 --- a/packages/component/src/Styles/StyleSet/ThumbButton.ts +++ b/packages/component/src/Styles/StyleSet/ThumbButton.ts @@ -4,61 +4,38 @@ export default function () { return { '&.webchat__thumb-button': { alignItems: 'center', - appearance: 'none', - background: 'transparent', - border: 0, - borderRadius: 2, + borderRadius: '2px', boxSizing: 'content-box', display: 'grid', - gridTemplateAreas: `"icon"`, - height: 16, - justifyContent: 'center', - padding: 0, - width: 16, + gridTemplateAreas: `"main"`, + height: '16px', + width: '16px', - '&.webchat__thumb-button--large': { - border: '1px solid transparent', - borderRadius: '4px', - height: '20px', - padding: '5px', - width: '20px', - - '& .webchat__thumb-button__image': { - color: 'currentColor', - fontSize: '20px', - height: '1em', - width: '1em' - }, - - '&:hover, &:active, &.webchat__thumb-button--is-pressed': { - background: 'transparent', - color: CSSTokens.ColorAccent - }, - - '&[aria-disabled="true"]': { - color: CSSTokens.ColorSubtle - }, - - '&.webchat__thumb-button--submitted:not(.webchat__thumb-button--is-pressed) .webchat__tooltip': { - display: 'none' - }, + '& .webchat__thumb-button__input': { + appearance: 'none', + background: 'transparent', + border: 0, + gridArea: 'main', + height: '16px', + margin: 0, + opacity: 0, + padding: 0, + width: '16px', - '&.webchat__thumb-button--submitted .webchat__tooltip': { - '--webchat__tooltip-anchor-inline-start': '20%' + '&:active': { + background: '#EDEBE9' } }, - '&:active': { - background: '#EDEBE9' - }, - - '&:focus-visible': { - outline: 'solid 1px #605E5C' + '&:has(.webchat__thumb-button__input:focus-visible)': { + outline: 'solid 1px #605E5C' // has opacity of 0, we need to set the outline in the container. }, '& .webchat__thumb-button__image': { color: CSSTokens.ColorAccent, - gridArea: 'icon', + gridArea: 'main', + justifySelf: 'center', // Unsure why "justifyContent" doesn't work. + pointerEvents: 'none', visibility: 'hidden', width: 14, @@ -67,7 +44,7 @@ export default function () { } }, - '&:not([aria-disabled="true"]):hover, &.webchat__thumb-button--is-pressed': { + '&:has(.webchat__thumb-button__input:is(:not([aria-disabled="true"]):hover, :checked, [aria-pressed="true"]))': { '& .webchat__thumb-button__image': { '&.webchat__thumb-button__image--is-stroked': { visibility: 'hidden' @@ -77,6 +54,44 @@ export default function () { visibility: 'unset' } } + }, + + '&.webchat__thumb-button--large': { + border: '1px solid transparent', + borderRadius: '4px', + height: '30px', + width: '30px', + + '& .webchat__thumb-button__input': { + height: '30px', + width: '30px' + }, + + '& .webchat__thumb-button__image': { + color: 'currentColor', + fontSize: '20px', + height: '1em', + width: '1em' + }, + + '&:has(.webchat__thumb-button__input:is(:hover, :active, :checked, [aria-pressed="true"])) .webchat__thumb-button__image': + { + background: 'transparent', + color: CSSTokens.ColorAccent + }, + + '&:has(.webchat__thumb-button__input[aria-disabled="true"]) .webchat__thumb-button__image': { + color: CSSTokens.ColorSubtle + }, + + '&.webchat__thumb-button--has-submitted:has(.webchat__thumb-button__input:not(:checked):not([aria-pressed="true"])) .webchat__thumb-button__tooltip': + { + display: 'none' + }, + + '&.webchat__thumb-button--has-submitted .webchat__thumb-button__tooltip': { + '--webchat__tooltip-anchor-inline-start': '20%' + } } } }; diff --git a/packages/component/src/Styles/StyleSet/Tooltip.ts b/packages/component/src/Styles/StyleSet/Tooltip.ts index 33e74a6193..66fd76d03c 100644 --- a/packages/component/src/Styles/StyleSet/Tooltip.ts +++ b/packages/component/src/Styles/StyleSet/Tooltip.ts @@ -2,11 +2,15 @@ export default () => ({ '*:has(> &.webchat__tooltip)': { position: 'relative', - '&:is(:hover, :focus-visible, :active) > .webchat__tooltip': { - opacity: 1, - transitionDelay: '400ms' - } + // For