Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fluent: allow displaying feedback inside activity actions toolbar #5407

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
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
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.
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.
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.
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.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
103 changes: 103 additions & 0 deletions __tests__/html/fluentTheme/side-by-side.wide.dark.html
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,91 @@

$ pip install numpy matplotlib`
}
], [
{
from:{
role: "bot"
},
id: "a-00000",
type: "message",
text: "This is compleded feedback action example.",
timestamp: timestamp(),
entities: [
{
...aiMessageEntity,
keywords: ['AIGeneratedContent', 'AllowCopy'],
potentialAction: [
{
"@type": "LikeAction",
actionStatus: "CompletedActionStatus",
target: {
"@type": "EntryPoint",
urlTemplate: "ms-directline://postback?interaction=like"
}
},
{
"@type": "DislikeAction",
actionStatus: "PotentialActionStatus",
result: {
"@type": "Review",
reviewBody: "I don't like it.",
"reviewBody-input": {
"@type": "PropertyValueSpecification",
valueMinLength: 3,
valueName: "reason"
}
},
target: {
"@type": "EntryPoint",
urlTemplate: "ms-directline://postback?interaction=dislike{&reason}"
}
}
]
}
]
},
{
from:{
role: "bot"
},
id: "a-00001",
type: "message",
text: "Hi! I'm Cody, the devbot. How can I help?",
timestamp: timestamp(),
entities: [
{
...aiMessageEntity,
keywords: ['AIGeneratedContent', 'AllowCopy'],
potentialAction: [
{
"@type": "LikeAction",
actionStatus: "PotentialActionStatus",
target: {
"@type": "EntryPoint",
urlTemplate: "ms-directline://postback?interaction=like"
}
},
{
"@type": "DislikeAction",
actionStatus: "PotentialActionStatus",
result: {
"@type": "Review",
reviewBody: "I don't like it.",
"reviewBody-input": {
"@type": "PropertyValueSpecification",
valueMinLength: 3,
valueName: "reason"
}
},
target: {
"@type": "EntryPoint",
urlTemplate: "ms-directline://postback?interaction=dislike{&reason}"
}
}
]
}
]
}
]];

const leftStore = testHelpers.createStore();
Expand Down Expand Up @@ -726,6 +811,24 @@
await host.snapshot();
await host.sendKeys('ENTER');
await host.snapshot();
},
likeDislike: async sendbox => {
sendbox.focus();
await host.sendShiftTab();
await host.sendKeys('ARROW_UP');
await host.sendKeys('ENTER');
await host.snapshot();
await host.sendKeys('TAB');
await host.snapshot();
await host.sendKeys('ENTER');
await host.snapshot();
await host.sendKeys('TAB');
await host.snapshot();
await host.sendKeys('ENTER');
await host.snapshot();
await host.sendKeys('ESCAPE');
await host.sendKeys('ESCAPE');
await host.snapshot();
}
}));

Expand Down
6 changes: 5 additions & 1 deletion __tests__/html/fluentTheme/side-by-side.wide.dark.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ describe('Fluent theme applied', () => {
test('side by side left - transcript, right - codeblock', () =>
runHTML('fluentTheme/side-by-side.wide.dark?transcript=0&transcript=5&focus=1&focus-preset=viewCode'));
test('side by side left - transcript, right - codeblock dark', () =>
runHTML('fluentTheme/side-by-side.wide.dark?transcript=0&transcript=5&focus=1&focus-preset=viewCode&codeBlockTheme=github-dark-default'));
runHTML(
'fluentTheme/side-by-side.wide.dark?transcript=0&transcript=5&focus=1&focus-preset=viewCode&codeBlockTheme=github-dark-default'
));
test('side by side left - transcript, right - feedback', () =>
runHTML('fluentTheme/side-by-side.wide.dark?transcript=0&transcript=6&focus=1&focus-preset=likeDislike'));
});
});
103 changes: 103 additions & 0 deletions __tests__/html/fluentTheme/side-by-side.wide.html
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,91 @@

$ pip install numpy matplotlib`
}
], [
{
from:{
role: "bot"
},
id: "a-00000",
type: "message",
text: "This is compleded feedback action example.",
timestamp: timestamp(),
entities: [
{
...aiMessageEntity,
keywords: ['AIGeneratedContent', 'AllowCopy'],
potentialAction: [
{
"@type": "LikeAction",
actionStatus: "CompletedActionStatus",
target: {
"@type": "EntryPoint",
urlTemplate: "ms-directline://postback?interaction=like"
}
},
{
"@type": "DislikeAction",
actionStatus: "PotentialActionStatus",
result: {
"@type": "Review",
reviewBody: "I don't like it.",
"reviewBody-input": {
"@type": "PropertyValueSpecification",
valueMinLength: 3,
valueName: "reason"
}
},
target: {
"@type": "EntryPoint",
urlTemplate: "ms-directline://postback?interaction=dislike{&reason}"
}
}
]
}
]
},
{
from:{
role: "bot"
},
id: "a-00001",
type: "message",
text: "Hi! I'm Cody, the devbot. How can I help?",
timestamp: timestamp(),
entities: [
{
...aiMessageEntity,
keywords: ['AIGeneratedContent', 'AllowCopy'],
potentialAction: [
{
"@type": "LikeAction",
actionStatus: "PotentialActionStatus",
target: {
"@type": "EntryPoint",
urlTemplate: "ms-directline://postback?interaction=like"
}
},
{
"@type": "DislikeAction",
actionStatus: "PotentialActionStatus",
result: {
"@type": "Review",
reviewBody: "I don't like it.",
"reviewBody-input": {
"@type": "PropertyValueSpecification",
valueMinLength: 3,
valueName: "reason"
}
},
target: {
"@type": "EntryPoint",
urlTemplate: "ms-directline://postback?interaction=dislike{&reason}"
}
}
]
}
]
}
]];

const leftStore = testHelpers.createStore();
Expand Down Expand Up @@ -709,6 +794,24 @@
await host.snapshot();
await host.sendKeys('ENTER');
await host.snapshot();
},
likeDislike: async sendbox => {
sendbox.focus();
await host.sendShiftTab();
await host.sendKeys('ARROW_UP');
await host.sendKeys('ENTER');
await host.snapshot();
await host.sendKeys('TAB');
await host.snapshot();
await host.sendKeys('ENTER');
await host.snapshot();
await host.sendKeys('TAB');
await host.snapshot();
await host.sendKeys('ENTER');
await host.snapshot();
await host.sendKeys('ESCAPE');
await host.sendKeys('ESCAPE');
await host.snapshot();
}
}));

Expand Down
6 changes: 5 additions & 1 deletion __tests__/html/fluentTheme/side-by-side.wide.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,9 @@ describe('Fluent theme applied', () => {
test('side by side left - transcript, right - codeblock', () =>
runHTML('fluentTheme/side-by-side.wide?transcript=0&transcript=5&focus=1&focus-preset=viewCode'));
test('side by side left - transcript, right - codeblock dark', () =>
runHTML('fluentTheme/side-by-side.wide?transcript=0&transcript=5&focus=1&focus-preset=viewCode&codeBlockTheme=github-dark-default'));
runHTML(
'fluentTheme/side-by-side.wide?transcript=0&transcript=5&focus=1&focus-preset=viewCode&codeBlockTheme=github-dark-default'
));
test('side by side left - transcript, right - feedback', () =>
runHTML('fluentTheme/side-by-side.wide?transcript=0&transcript=6&focus=1&focus-preset=likeDislike'));
});
12 changes: 12 additions & 0 deletions packages/api/src/StyleOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -928,6 +928,18 @@ type StyleOptions = {
* New in 4.19.0.
*/
codeBlockTheme?: 'github-light-default' | 'github-dark-default';

/**
* Feedback buttons placement
*
* - `'activity-actions'` - place feedback buttons inside activity actions
* - `'activity-status'` - place feedback buttons inside activity status
*
* @default 'activity-status'
*
* New in 4.19.0.
*/
feedbackActionsPlacement?: 'activity-actions' | 'activity-status';
};

// StrictStyleOptions is only used internally in Web Chat and for simplifying our code:
Expand Down
4 changes: 3 additions & 1 deletion packages/api/src/defaultStyleOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,9 @@ const DEFAULT_OPTIONS: Required<StyleOptions> = {
borderAnimationColor2: '#4DD3FF',
borderAnimationColor3: '#2B8DD8',

codeBlockTheme: 'github-light-default' as const
codeBlockTheme: 'github-light-default' as const,

feedbackActionsPlacement: 'activity-status' as const
};

export default DEFAULT_OPTIONS;
6 changes: 4 additions & 2 deletions packages/api/src/localization/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,10 @@
"_TYPING_INDICATOR_MULTIPLE_TEXT.comment": "This shows when two or more users are typing simultaneously.",
"TYPING_INDICATOR_MULTIPLE_TEXT": "$1 and others are typing.",
"VIEW_CODE_BUTTON_TEXT": "Code",
"VOTE_COMPLETE_ALT": "Feedback recorded",
"VOTE_COMPLETE_ALT.comment": "This is for screen reader. The label of a button with a thumb up/down icons. The label is used when feedback was recorded.",
"VOTE_DISLIKE_ALT": "Dislike",
"_VOTE_DISLIKE_ALT.comment": "This is for screen reader. The label of a button with a thumb up icon and is placed next to the timestamp. The button is for giving feedback that the end-user don't think the response is useful.",
"_VOTE_DISLIKE_ALT.comment": "This is for screen reader. The label of a button with a thumb up icon. The button is for giving feedback that the end-user don't think the response is useful.",
"VOTE_LIKE_ALT": "Like",
"_VOTE_LIKE_ALT.comment": "This is for screen reader. The label of a button with a thumb up icon and is placed next to the timestamp. The button is for giving feedback that the end-user think the response is useful."
"_VOTE_LIKE_ALT.comment": "This is for screen reader. The label of a button with a thumb up icon. The button is for giving feedback that the end-user think the response is useful."
}
53 changes: 53 additions & 0 deletions packages/component/src/Activity/ActivityFeedback.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { hooks } from 'botframework-webchat-api';
import { getOrgSchemaMessage, OrgSchemaAction, parseAction, WebChatActivity } from 'botframework-webchat-core';
import cx from 'classnames';
import React, { memo, useMemo } from 'react';

import Feedback from './private/Feedback';
import dereferenceBlankNodes from '../Utils/JSONLinkedData/dereferenceBlankNodes';

const { useStyleOptions } = hooks;

type ActivityFeedbackProps = Readonly<{
activity: WebChatActivity;
placement: 'activity-status' | 'activity-actions';
}>;

function ActivityFeedback({ activity, placement }: ActivityFeedbackProps) {
const [{ feedbackActionsPlacement }] = useStyleOptions();

const graph = useMemo(() => dereferenceBlankNodes(activity.entities || []), [activity.entities]);

const messageThing = useMemo(() => getOrgSchemaMessage(graph), [graph]);

const feedbackActions = useMemo<ReadonlySet<OrgSchemaAction> | undefined>(() => {
try {
const reactActions = (messageThing?.potentialAction || []).filter(
({ '@type': type }) => type === 'LikeAction' || type === 'DislikeAction'
);

if (reactActions.length) {
return Object.freeze(new Set(reactActions));
}

const voteActions = graph.filter(({ type }) => type === 'https://schema.org/VoteAction').map(parseAction);

if (voteActions.length) {
return Object.freeze(new Set(voteActions));
}
} catch {
// Intentionally left blank.
}
}, [graph, messageThing?.potentialAction]);

return feedbackActions?.size && placement === feedbackActionsPlacement ? (
<Feedback
actions={feedbackActions}
className={cx({
'webchat__thumb-button--large': feedbackActionsPlacement === 'activity-actions'
})}
/>
) : null;
}

export default memo(ActivityFeedback);
Loading
Loading