Skip to content

Commit 5ee468f

Browse files
committed
fix tests
1 parent 4846f28 commit 5ee468f

10 files changed

Lines changed: 162 additions & 122 deletions

File tree

__tests__/html2/fluentTheme/defaultFeedback.activity.html

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
id: 'a-00000',
4040
timestamp: 0,
4141
text: 'This is a test message to show feedback buttons',
42+
replyToId: 'a-00001',
4243
from: {
4344
role: 'bot'
4445
},
@@ -54,30 +55,40 @@
5455

5556
await pageConditions.numActivitiesShown(1);
5657

57-
// pageElements.byTestId('send box text area').focus();
58-
// await host.sendKeys('ENTER');
59-
// await host.sendKeys('Test feedback');
58+
pageElements.byTestId('send box text area').focus();
59+
await host.sendShiftTab(2);
6060

61-
// const { activity } = await directLine.actPostActivity(async () => {
62-
// await host.sendTab(1);
63-
// await host.sendKeys('ENTER');
64-
// });
61+
await host.sendKeys('ENTER');
62+
await host.sendKeys('ENTER');
6563

66-
// expect(activity).toEqual(
67-
// expect.objectContaining({
68-
// type: 'invoke',
69-
// name: 'message/submitAction',
70-
// value: {
71-
// actionName: 'feedback',
72-
// actionValue: {
73-
// reaction: 'like',
74-
// feedback: {
75-
// feedbackText: expect.any(String)
76-
// }
77-
// }
78-
// }
79-
// })
80-
// );
64+
await pageConditions.became(
65+
'feedback form is open',
66+
() => document.activeElement === pageElements.byTestId('feedback sendbox'),
67+
1000
68+
);
69+
70+
await host.sendKeys('Test feedback');
71+
const { activity } = await directLine.actPostActivity(async () => {
72+
await host.sendTab(1);
73+
await host.sendKeys('ENTER');
74+
});
75+
76+
expect(activity).toEqual(
77+
expect.objectContaining({
78+
type: 'invoke',
79+
name: 'message/submitAction',
80+
replyToId: 'a-00001',
81+
value: {
82+
actionName: 'feedback',
83+
actionValue: {
84+
reaction: 'like',
85+
feedback: {
86+
feedbackText: expect.any(String)
87+
}
88+
}
89+
}
90+
})
91+
);
8192
});
8293
</script>
8394
</body>

packages/api/src/StyleOptions.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -957,6 +957,8 @@ type StyleOptions = {
957957
textBoxBorderColorOnFocus?: string;
958958
textBoxBorderColorOnActive?: string;
959959
textBoxTextColor?: string;
960+
textBoxMinHeight?: number;
961+
textBoxMinWidth?: number;
960962
/**
961963
* Use continuous mode for speech recognition. Default to `false`.
962964
*

packages/api/src/defaultStyleOptions.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,8 @@ const DEFAULT_OPTIONS: Required<StyleOptions> = {
322322
textBoxBorderColorOnFocus: '',
323323
textBoxBorderColorOnActive: '',
324324
textBoxTextColor: '',
325+
textBoxMinHeight: 40,
326+
textBoxMinWidth: 280,
325327

326328
// Speech recognition
327329
speechRecognitionContinuous: false

packages/component/src/Activity/ActivityFeedback.tsx

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { hooks } from 'botframework-webchat-api';
22
import { getOrgSchemaMessage, OrgSchemaAction, parseAction, WebChatActivity } from 'botframework-webchat-core';
33
import classNames from 'classnames';
4-
import React, { memo, useCallback, useMemo, useState } from 'react';
4+
import React, { memo, useCallback, useMemo, useRef, useState } from 'react';
55
import { defaultFeedbackEntities } from './private/DefaultFeedbackEntities';
66
import { isDefaultFeedbackActivity } from './private/isDefaultFeedbackActivity';
77

@@ -37,11 +37,11 @@ const useGetMessageThing = (activity: WebChatActivity) =>
3737
useMemo(() => {
3838
const { messageThing, graph } = parseActivity(activity.entities);
3939
if (messageThing?.potentialAction) {
40-
return { includeDefaultFeedback: false, messageThing, graph };
40+
return { isFeedbackLoopSupported: false, messageThing, graph };
4141
} else if (isDefaultFeedbackActivity(activity)) {
42-
return { includeDefaultFeedback: true, ...parseActivity([defaultFeedbackEntities]) };
42+
return { isFeedbackLoopSupported: true, ...parseActivity([defaultFeedbackEntities]) };
4343
}
44-
return { includeDefaultFeedback: false, messageThing, graph };
44+
return { isFeedbackLoopSupported: false, messageThing, graph };
4545
}, [activity]);
4646

4747
function ActivityFeedback({ activity }: ActivityFeedbackProps) {
@@ -50,8 +50,11 @@ function ActivityFeedback({ activity }: ActivityFeedbackProps) {
5050

5151
const [showFeedbackForm, setShowFeedbackForm] = useState(false);
5252
const [feedbackType, setFeedbackType] = useState<string | undefined>(undefined);
53+
const resetFeedbackRef = useRef<() => void>();
5354

54-
const { messageThing, graph, includeDefaultFeedback } = useGetMessageThing(activity);
55+
const { replyToId } = activity;
56+
57+
const { messageThing, graph, isFeedbackLoopSupported } = useGetMessageThing(activity);
5558

5659
const feedbackActions = useMemo<ReadonlySet<OrgSchemaAction>>(() => {
5760
try {
@@ -76,48 +79,54 @@ function ActivityFeedback({ activity }: ActivityFeedbackProps) {
7679

7780
const disclaimer = useMemo(
7881
() =>
79-
includeDefaultFeedback && isDefaultFeedbackActivity(activity)
82+
isFeedbackLoopSupported && isDefaultFeedbackActivity(activity)
8083
? activity.channelData.feedbackLoop?.disclaimer
8184
: undefined,
82-
[activity, includeDefaultFeedback]
85+
[activity, isFeedbackLoopSupported]
8386
);
8487

85-
const onFeedbackTypeChange = useCallback((feedbackType?: string) => {
86-
if (!feedbackType) {
87-
setFeedbackType(undefined);
88-
setShowFeedbackForm(false);
89-
} else {
90-
setFeedbackType(feedbackType);
91-
setShowFeedbackForm(true);
88+
const onFeedbackTypeChange = useCallback((newType?: string) => {
89+
setFeedbackType(newType);
90+
setShowFeedbackForm(newType !== undefined);
91+
if (newType === undefined) {
92+
resetFeedbackRef.current?.();
9293
}
9394
}, []);
9495

95-
const FeedbackComponent = (
96-
<Feedback
97-
actions={feedbackActions}
98-
className={classNames({
99-
'webchat__thumb-button--large': feedbackActionsPlacement === 'activity-actions'
100-
})}
101-
handleFeedbackActionClick={onFeedbackTypeChange}
102-
isFeedbackFormSupported={includeDefaultFeedback}
103-
/>
96+
const FeedbackComponent = useMemo(
97+
() => (
98+
<Feedback
99+
actions={feedbackActions}
100+
className={classNames({
101+
'webchat__thumb-button--large': feedbackActionsPlacement === 'activity-actions'
102+
})}
103+
handleFeedbackActionClick={onFeedbackTypeChange}
104+
isFeedbackFormSupported={isFeedbackLoopSupported}
105+
resetFeedbackRef={resetFeedbackRef}
106+
/>
107+
),
108+
[feedbackActions, feedbackActionsPlacement, isFeedbackLoopSupported, onFeedbackTypeChange]
104109
);
105110

106-
const FeedbackFormComponent = (
107-
<FeedbackForm
108-
disclaimer={disclaimer}
109-
feedbackType={feedbackType as FeedbackType}
110-
handeFeedbackTypeChange={onFeedbackTypeChange}
111-
/>
111+
const FeedbackFormComponent = useMemo(
112+
() => (
113+
<FeedbackForm
114+
disclaimer={disclaimer}
115+
feedbackType={feedbackType as FeedbackType}
116+
handeFeedbackTypeChange={onFeedbackTypeChange}
117+
replyToId={replyToId}
118+
/>
119+
),
120+
[disclaimer, feedbackType, onFeedbackTypeChange, replyToId]
112121
);
113122

114-
if (feedbackActionsPlacement === 'activity-actions' && showFeedbackForm && feedbackType) {
123+
if (feedbackActionsPlacement === 'activity-actions' && isFeedbackLoopSupported) {
115124
return (
116125
<div className={classNames('webchat__feedback-form__root__container', rootClassName)}>
117126
<div className={classNames('webchat__feedback-form__root__container__child', rootClassName)}>
118127
{FeedbackComponent}
119128
</div>
120-
{FeedbackFormComponent}
129+
{showFeedbackForm && feedbackType && FeedbackFormComponent}
121130
</div>
122131
);
123132
}

packages/component/src/Activity/private/Feedback.tsx

Lines changed: 66 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -13,73 +13,84 @@ type Props = Readonly<
1313
className?: string | undefined;
1414
isFeedbackFormSupported?: boolean;
1515
handleFeedbackActionClick: (feedbackType?: string) => void;
16+
resetFeedbackRef: React.MutableRefObject<() => void>;
1617
}>
1718
>;
1819

1920
const DEBOUNCE_TIMEOUT = 500;
2021

21-
const Feedback = memo(({ actions, className, isFeedbackFormSupported, handleFeedbackActionClick }: Props) => {
22-
const [{ clearTimeout, setTimeout }] = usePonyfill();
23-
const [selectedAction, setSelectedAction] = useState<OrgSchemaAction | undefined>();
24-
const postActivity = usePostActivity();
25-
const localize = useLocalizer();
22+
const Feedback = memo(
23+
({ actions, className, isFeedbackFormSupported, handleFeedbackActionClick, resetFeedbackRef }: Props) => {
24+
const [{ clearTimeout, setTimeout }] = usePonyfill();
25+
const [selectedAction, setSelectedAction] = useState<OrgSchemaAction | undefined>();
26+
const postActivity = usePostActivity();
27+
const localize = useLocalizer();
2628

27-
const postActivityRef = useRefFrom(postActivity);
29+
const postActivityRef = useRefFrom(postActivity);
2830

29-
useEffect(() => {
30-
if (!selectedAction) {
31-
return;
32-
}
31+
useEffect(() => {
32+
resetFeedbackRef.current = () => {
33+
setSelectedAction(undefined);
34+
};
35+
// Only want to set the ref once
36+
// eslint-disable-next-line react-hooks/exhaustive-deps
37+
}, []);
3338

34-
if (isFeedbackFormSupported) {
35-
handleFeedbackActionClick(selectedAction['@type']);
36-
return;
37-
}
39+
useEffect(() => {
40+
if (!selectedAction) {
41+
return;
42+
}
3843

39-
const timeout = setTimeout(
40-
() =>
41-
// TODO: We should update this to use W3C Hydra.1
42-
postActivityRef.current({
43-
entities: [selectedAction],
44-
name: 'webchat:activity-status/feedback',
45-
type: 'event'
46-
} as any),
47-
DEBOUNCE_TIMEOUT
48-
);
44+
if (isFeedbackFormSupported) {
45+
handleFeedbackActionClick(selectedAction['@type']);
46+
return;
47+
}
48+
49+
const timeout = setTimeout(
50+
() =>
51+
// TODO: We should update this to use W3C Hydra.1
52+
postActivityRef.current({
53+
entities: [selectedAction],
54+
name: 'webchat:activity-status/feedback',
55+
type: 'event'
56+
} as any),
57+
DEBOUNCE_TIMEOUT
58+
);
4959

50-
return () => clearTimeout(timeout);
51-
}, [clearTimeout, isFeedbackFormSupported, handleFeedbackActionClick, postActivityRef, selectedAction, setTimeout]);
60+
return () => clearTimeout(timeout);
61+
}, [clearTimeout, isFeedbackFormSupported, handleFeedbackActionClick, postActivityRef, selectedAction, setTimeout]);
5262

53-
const actionProps = useMemo(
54-
() =>
55-
[...actions].some(action => action.actionStatus === 'CompletedActionStatus')
56-
? {
57-
disabled: true,
58-
title: localize('VOTE_COMPLETE_ALT')
59-
}
60-
: undefined,
61-
[actions, localize]
62-
);
63+
const actionProps = useMemo(
64+
() =>
65+
[...actions].some(action => action.actionStatus === 'CompletedActionStatus')
66+
? {
67+
disabled: true,
68+
title: localize('VOTE_COMPLETE_ALT')
69+
}
70+
: undefined,
71+
[actions, localize]
72+
);
6373

64-
return (
65-
<Fragment>
66-
{[...actions].map((action, index) => (
67-
<FeedbackVoteButton
68-
action={action}
69-
className={className}
70-
key={action['@id'] || index}
71-
onClick={setSelectedAction}
72-
pressed={
73-
selectedAction === action ||
74-
action.actionStatus === 'CompletedActionStatus' ||
75-
action.actionStatus === 'ActiveActionStatus'
76-
}
77-
{...actionProps}
78-
/>
79-
))}
80-
</Fragment>
81-
);
82-
});
74+
return (
75+
<Fragment>
76+
{[...actions].map((action, index) => (
77+
<FeedbackVoteButton
78+
action={action}
79+
className={className}
80+
key={action['@id'] || index}
81+
onClick={setSelectedAction}
82+
pressed={
83+
selectedAction === action ||
84+
action.actionStatus === 'CompletedActionStatus' ||
85+
action.actionStatus === 'ActiveActionStatus'
86+
}
87+
{...actionProps}
88+
/>
89+
))}
90+
</Fragment>
91+
);
92+
}
93+
);
8394

8495
Feedback.displayName = 'ActivityStatusFeedback';
8596

0 commit comments

Comments
 (0)