Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ Notes: web developers are advised to use [`~` (tilde range)](https://github.com/
- Added support of [contentless activity in livestream](https://github.com/microsoft/BotFramework-WebChat/blob/main/docs/LIVESTREAMING.md#scenario-3-interim-activities-with-no-content), in PR [#5430](https://github.com/microsoft/BotFramework-WebChat/pull/5430), by [@compulim](https://github.com/compulim)
- Added sliding dots typing indicator in Fluent theme, in PR [#5447](https://github.com/microsoft/BotFramework-WebChat/pull/5447) and PR [#5448](https://github.com/microsoft/BotFramework-WebChat/pull/5448), by [@compulim](https://github.com/compulim)
- (Experimental) Add an ability to pass `completion` prop into Fluent send box and expose the component, in PR [#5466](https://github.com/microsoft/BotFramework-WebChat/pull/5466), by [@OEvgeny](https://github.com/OEvgeny)
- Added feedback form for like/dislike button when `feedbackActionsPlacement` is `"activity-actions"`, in PR [#5460](https://github.com/microsoft/BotFramework-WebChat/pull/5460), 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)
- 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), PR [5470](https://github.com/microsoft/BotFramework-WebChat/pull/5470) and PR [#5501](https://github.com/microsoft/BotFramework-WebChat/pull/5501) by [@lexi-taylor](https://github.com/lexi-taylor) and [@OEvgeny](https://github.com/OEvgeny)
- <kbd>ESCAPE</kbd> key should reset the feedback form, in PR [#5480](https://github.com/microsoft/BotFramework-WebChat/pull/5480), by [@compulim](https://github.com/compulim), in PR [#5493](https://github.com/microsoft/BotFramework-WebChat/pull/5493) by [@lexi-taylor](https://github.com/lexi-taylor)
- 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`
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions __tests__/html2/feedbackForm/feedback.form.scroll.fluent.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!doctype html>
<html lang="en-US">
<head>
<title>Feedback Form (fluent): Scrollbar behavior when input is long</title>
<script>
location.href = './feedback.form.scroll?theme=fluent';
</script>
</head>
<body></body>
</html>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
121 changes: 121 additions & 0 deletions __tests__/html2/feedbackForm/feedback.form.scroll.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
<!doctype html>
<html lang="en-US">
<head>
<title>Feedback Form: Scrollbar behavior when input is long</title>
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
<script type="importmap">
{
"imports": {
"react": "https://esm.sh/react@18.3.1",
"react-dom": "https://esm.sh/react-dom@18.3.1",
"react-dom/": "https://esm.sh/react-dom@18.3.1/",
"@fluentui/react-components": "https://esm.sh/@fluentui/react-components?deps=react@18.3.1&exports=FluentProvider,createDarkTheme,webLightTheme"
}
}
</script>
<script crossorigin="anonymous" src="/test-harness.js"></script>
<script crossorigin="anonymous" src="/test-page-object.js"></script>
<script type="module">
import React from 'react';
import ReactDOM from 'react-dom';
window.React = React;
window.ReactDOM = ReactDOM;
</script>
<script defer crossorigin="anonymous" src="/__dist__/webchat-es5.js"></script>
<script defer crossorigin="anonymous" src="/__dist__/botframework-webchat-fluent-theme.production.min.js"></script>
</head>

<body>
<main id="webchat"></main>
<script type="module">
run(async function () {
const {
WebChat: { testIds }
} = window; // Imports in UMD fashion.

const { directLine, store } = testHelpers.createDirectLineEmulator();

const { isFluentTheme } = renderWebChat(
{
directLine,
store,
styleOptions: { feedbackActionsPlacement: 'activity-actions' }
},
document.getElementById('webchat')
);

await pageConditions.uiConnected();
pageElements.byTestId(testIds.sendBoxTextBox).focus();

// GIVEN: An activity with feedback form.
await directLine.emulateIncomingActivity({
type: 'message',
id: 'a-00000',
timestamp: 0,
text: 'This is a test message to show feedback buttons',
from: {
role: 'bot'
},
locale: 'en-US',
entities: [],
channelData: {
feedbackLoop: {
type: 'default',
disclaimer: 'This is a test disclaimer message'
}
}
});

await pageConditions.numActivitiesShown(1);

// WHEN: Focus on the like button.
if (isFluentTheme) {
await host.sendShiftTab(1);
await host.sendKeys('ARROW_UP');
await host.sendKeys('ENTER', 'SPACE');
} else {
await host.sendShiftTab(3);
await host.sendKeys('ENTER', 'SPACE');
}

// THEN: The feedback form should be expanded.
await pageConditions.became(
'feedback form is open',
() => document.activeElement === pageElements.byTestId(testIds.feedbackSendBox),
1000
);

// WHEN: The form is filled with more than 10 lines of text.
await host.sendKeys('Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris '.repeat(12));

// THEN: The feedback form should have scroll visible
await host.snapshot('local');

// WHEN: The text area is not focused.
await host.sendTab(1);

// THEN: The feedback form should have scrollbar hidden.
await host.snapshot('local');

// WHEN: A feedback form with scroll is being submitted.
await directLine.actPostActivity(async () => {
await host.sendKeys('ENTER');
});


// THEN: Feedback form should be collapsed.
await expect(pageElements.byTestId(testIds.feedbackSendBox)).toBeFalsy();

// THEN: All feedback buttons should be grayed out.
await expect(
Array.from(pageElements.allByTestId(testIds.feedbackButton)).every(
buttonElement => buttonElement.getAttribute('aria-disabled') === 'true'
)
).toBe(true);

// THEN: Should match snapshot.
await host.snapshot('local');
});
</script>
</body>
</html>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 6 additions & 4 deletions packages/component/src/ActivityFeedback/ActivityFeedback.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
import classNames from 'classnames';
import { useStyles } from 'botframework-webchat-styles/react';
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 useActivityFeedbackHooks from './providers/useActivityFeedbackHooks';

import styles from './private/FeedbackForm.module.css';

function InternalActivityFeedback() {
const classNames = useStyles(styles);

const { useActions, useFeedbackText, useFocusFeedbackButton, useHasSubmitted, useSelectedAction, useSubmit } =
useActivityFeedbackHooks();

const [_, setFeedbackText] = useFeedbackText();
const [{ feedbackForm }] = useStyleSet();
const [actions] = useActions();
const [hasSubmitted] = useHasSubmitted();
const [selectedAction, setSelectedAction] = useSelectedAction();
Expand Down Expand Up @@ -62,7 +64,7 @@ function InternalActivityFeedback() {
return (
!!actions.length && (
<form
className={classNames('webchat__feedback-form', feedbackForm + '')}
className={classNames['feedback-form']}
onKeyDown={handleKeyDown}
onReset={handleReset}
onSubmit={handleSubmit}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
:global(.webchat) .feedback-form {
display: contents;

.feedback-form__vote-button-bar {
display: flex;
gap: 2px;
}

.feedback-form__form {
display: flex;
flex-direction: column;
gap: 4px;
position: relative;
/* The form should take the full width of the flex container width. */
width: 100%;
}

.feedback-form__form-header {
color: #373435;
display: flex;
flex-direction: column;
font-family: var(--webchat__font--primary);
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 20px;
}

.feedback-form__text-box {
background-color: #ffffff;
border-radius: 4px;
border: 1px solid #e8e8e8;
font-size: 14px;
line-height: 20px;
overflow: hidden;
padding: 8px 12px;
position: relative;

&::after {
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
border-bottom: 3px solid var(--webchat__color--accent);
bottom: -1px;
clip-path: inset(calc(100% - 3px) 50% 0 50%);
content: '';
height: 4px;
left: -1px;
position: absolute;
right: -1px;
transition: none;
}

&:focus-within {
border: 1px solid #e8e8e8;
}

&:focus-within::after {
clip-path: inset(calc(100% - 3px) 0 0 0);
transition: clip-path 200ms cubic-bezier(0, 0, 0, 1);
}
}

.feedback-form__text-area {
resize: none;
margin-inline-end: -8px;
}

.feedback-form__form-footer {
color: var(--webchat__color--subtle);
font-family: var(--webchat__font--primary);
font-size: 10px;
font-style: normal;
font-weight: 400;
line-height: 14px;
}

.feedback-form__submission-button-bar {
display: flex;
gap: 8px;
margin-block-start: 6px;
}

.feedback-form__submit-button {
background-color: var(--webchat__color--accent);
border-radius: 4px;
border: 1px solid var(--webchat__color--accent);
color: #ffffff;
cursor: pointer;
font-family: var(--webchat__font--primary);
font-size: 12px;
height: 24px;
padding: 0 8px;
}

.feedback-form__submit-button:hover {
background-color: #004a98;
border: 1px solid #004a98;
color: #ffffff;
}

.feedback-form__submit-button:active {
background-color: #004a98;
border: 1px solid #004a98;
color: #ffffff;
}

.feedback-form__cancel-button {
background-color: #ffffff;
border-radius: 4px;
border: 1px solid #e8e8e8;
color: var(--webchat__color--subtle);
cursor: pointer;
font-family: var(--webchat__font--primary);
font-size: 12px;
height: 24px;
padding: 0 8px;
}

.feedback-form__cancel-button:hover {
background-color: var(--webchat__color--subtle);
color: #ffffff;
}
}
37 changes: 22 additions & 15 deletions packages/component/src/ActivityFeedback/private/FeedbackForm.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import { hooks } from 'botframework-webchat-api';
import { useStyles } from 'botframework-webchat-styles/react';
import React, { memo, useCallback, useEffect, useRef, useState, type FormEventHandler } from 'react';

import Markdownable from '../../Attachment/Text/private/Markdownable';
import testIds from '../../testIds';
import { TextArea } from '../../TextArea';
import useActivityFeedbackHooks from '../providers/useActivityFeedbackHooks';
import FeedbackTextArea from './FeedbackTextArea';
import getDisclaimerFromReviewAction from './getDisclaimerFromReviewAction';

import styles from './FeedbackForm.module.css';

const { useLocalizer } = hooks;

function FeedbackForm() {
const classNames = useStyles(styles);
const { useFeedbackText, useSelectedAction } = useActivityFeedbackHooks();

const [selectedAction] = useSelectedAction();
Expand Down Expand Up @@ -37,22 +41,25 @@ function FeedbackForm() {
}, [feedbackTextAreaRef, hasFocus, setHasFocus]);

return (
<div className="webchat__feedback-form__form">
<span className="webchat__feedback-form__form-header">{localize('FEEDBACK_FORM_TITLE')}</span>
<FeedbackTextArea
data-testid={testIds.feedbackSendBox}
onInput={handleMessageChange}
placeholder={localize('FEEDBACK_FORM_PLACEHOLDER')}
ref={feedbackTextAreaRef}
startRows={3}
value={userFeedback}
/>
{disclaimer && <Markdownable className="webchat__feedback-form__form-footer" text={disclaimer} />}
<div className="webchat__feedback-form__submission-button-bar">
<button className="webchat__feedback-form__submit-button" type="submit">
<div className={classNames['feedback-form__form']}>
<span className={classNames['feedback-form__form-header']}>{localize('FEEDBACK_FORM_TITLE')}</span>
<div className={classNames['feedback-form__text-box']}>
<TextArea
className={classNames['feedback-form__text-area']}
data-testid={testIds.feedbackSendBox}
onInput={handleMessageChange}
placeholder={localize('FEEDBACK_FORM_PLACEHOLDER')}
ref={feedbackTextAreaRef}
startRows={3}
value={userFeedback}
/>
</div>
{disclaimer && <Markdownable className={classNames['feedback-form__form-footer']} text={disclaimer} />}
<div className={classNames['feedback-form__submission-button-bar']}>
<button className={classNames['feedback-form__submit-button']} type="submit">
{localize('FEEDBACK_FORM_SUBMIT_BUTTON_LABEL')}
</button>
<button className="webchat__feedback-form__cancel-button" type="reset">
<button className={classNames['feedback-form__cancel-button']} type="reset">
{localize('FEEDBACK_FORM_CANCEL_BUTTON_LABEL')}
</button>
</div>
Expand Down
Loading
Loading