botframework-webchat-fluent-theme 4.18.1-main.20240831.f4058ce → 4.18.1-main.20240911.3e47786
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/botframework-webchat-fluent-theme.css.map +1 -1
- package/dist/botframework-webchat-fluent-theme.d.mts +2 -0
- package/dist/botframework-webchat-fluent-theme.d.ts +2 -0
- package/dist/botframework-webchat-fluent-theme.development.css.map +1 -1
- package/dist/botframework-webchat-fluent-theme.development.js +8 -8
- package/dist/botframework-webchat-fluent-theme.development.js.map +1 -1
- package/dist/botframework-webchat-fluent-theme.js +1 -1
- package/dist/botframework-webchat-fluent-theme.js.map +1 -1
- package/dist/botframework-webchat-fluent-theme.mjs +1 -1
- package/dist/botframework-webchat-fluent-theme.mjs.map +1 -1
- package/dist/botframework-webchat-fluent-theme.production.min.css.map +1 -1
- package/dist/botframework-webchat-fluent-theme.production.min.js +8 -8
- package/dist/botframework-webchat-fluent-theme.production.min.js.map +1 -1
- package/package.json +4 -4
- package/src/components/activity/ActivityDecorator.module.css +35 -17
- package/src/components/activity/CopilotMessageHeader.module.css +5 -3
- package/src/components/activity/CopilotMessageHeader.tsx +20 -12
- package/src/components/activity/index.ts +1 -0
- package/src/components/activity/private/isAIGeneratedActivity.ts +5 -0
- package/src/components/activity/private/useActivityAuthor.ts +16 -0
- package/src/components/activity/private/useActivityStyleOptions.ts +19 -0
- package/src/components/decorator/private/BorderFlair.module.css +4 -0
- package/src/components/decorator/private/BorderFlair.tsx +15 -2
- package/src/components/linerActivity/private/LinerActivity.tsx +1 -1
- package/src/components/linerActivity/private/LinerMessageActivity.module.css +4 -0
- package/src/components/preChatActivity/PreChatMessageActivity.module.css +21 -7
- package/src/components/preChatActivity/PreChatMessageActivity.tsx +32 -26
- package/src/components/preChatActivity/StarterPromptsCardAction.module.css +55 -16
- package/src/components/preChatActivity/StarterPromptsCardAction.tsx +30 -24
- package/src/components/preChatActivity/StarterPromptsToolbar.tsx +23 -10
- package/src/components/sendBox/Attachments.tsx +5 -4
- package/src/components/sendBox/SendBox.module.css +7 -0
- package/src/components/sendBox/SendBox.tsx +31 -17
- package/src/components/sendBox/TextArea.tsx +50 -30
- package/src/components/sendBox/Toolbar.module.css +7 -1
- package/src/components/sendBox/Toolbar.tsx +17 -7
- package/src/components/suggestedActions/AccessibleButton.tsx +15 -13
- package/src/components/suggestedActions/SuggestedAction.module.css +8 -7
- package/src/components/suggestedActions/SuggestedAction.tsx +7 -4
- package/src/components/suggestedActions/SuggestedActions.tsx +3 -2
- package/src/components/theme/Theme.module.css +19 -8
- package/src/private/FluentThemeProvider.tsx +2 -0
- package/src/testIds.ts +2 -0
|
@@ -1,34 +1,47 @@
|
|
|
1
|
+
import { hooks } from 'botframework-webchat-api';
|
|
1
2
|
import { type DirectLineCardAction } from 'botframework-webchat-core';
|
|
2
3
|
import cx from 'classnames';
|
|
3
|
-
import React, {
|
|
4
|
+
import React, { Fragment, memo } from 'react';
|
|
4
5
|
import { useStyles } from '../../styles/index.js';
|
|
5
6
|
import StarterPromptsCardAction from './StarterPromptsCardAction.js';
|
|
6
7
|
import styles from './StarterPromptsToolbar.module.css';
|
|
7
8
|
|
|
9
|
+
const { useUIState } = hooks;
|
|
10
|
+
|
|
8
11
|
type Props = Readonly<{
|
|
9
12
|
cardActions: readonly DirectLineCardAction[];
|
|
10
13
|
className?: string | undefined;
|
|
11
|
-
children?: ReactNode | undefined;
|
|
12
14
|
}>;
|
|
13
15
|
|
|
14
|
-
const
|
|
16
|
+
const StarterPromptsToolbar = ({ cardActions, className }: Props) => {
|
|
15
17
|
const classNames = useStyles(styles);
|
|
18
|
+
const [uiState] = useUIState();
|
|
16
19
|
|
|
17
20
|
return (
|
|
18
21
|
// TODO: Accessibility-wise, this should be role="toolbar" with keyboard navigation.
|
|
19
22
|
<div className={cx(className, classNames['pre-chat-message-activity__card-action-toolbar'])}>
|
|
20
23
|
<div className={classNames['pre-chat-message-activity__card-action-toolbar-grid']}>
|
|
21
|
-
{
|
|
24
|
+
{uiState === 'blueprint' ? (
|
|
25
|
+
<Fragment>
|
|
26
|
+
<StarterPromptsCardAction />
|
|
27
|
+
<StarterPromptsCardAction />
|
|
28
|
+
<StarterPromptsCardAction />
|
|
29
|
+
</Fragment>
|
|
30
|
+
) : (
|
|
22
31
|
cardActions
|
|
23
|
-
.filter<
|
|
24
|
-
DirectLineCardAction & { type: 'messageBack' }
|
|
25
|
-
|
|
26
|
-
|
|
32
|
+
.filter<DirectLineCardAction & { type: 'messageBack' }>(
|
|
33
|
+
(card: DirectLineCardAction): card is DirectLineCardAction & { type: 'messageBack' } =>
|
|
34
|
+
card.type === 'messageBack'
|
|
35
|
+
)
|
|
36
|
+
// There is no other usable keys in card actions.
|
|
37
|
+
// eslint-disable-next-line react/no-array-index-key
|
|
38
|
+
.map((cardAction, index) => <StarterPromptsCardAction key={index} messageBackAction={cardAction} />)
|
|
39
|
+
)}
|
|
27
40
|
</div>
|
|
28
41
|
</div>
|
|
29
42
|
);
|
|
30
43
|
};
|
|
31
44
|
|
|
32
|
-
|
|
45
|
+
StarterPromptsToolbar.displayName = 'StarterPromptsToolbar';
|
|
33
46
|
|
|
34
|
-
export default memo(
|
|
47
|
+
export default memo(StarterPromptsToolbar);
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { hooks } from 'botframework-webchat-component';
|
|
2
|
-
import React, { memo } from 'react';
|
|
3
2
|
import cx from 'classnames';
|
|
4
|
-
import
|
|
3
|
+
import React, { memo } from 'react';
|
|
5
4
|
import { useStyles } from '../../styles';
|
|
5
|
+
import styles from './Attachments.module.css';
|
|
6
6
|
|
|
7
|
-
const { useLocalizer } = hooks;
|
|
7
|
+
const { useLocalizer, useUIState } = hooks;
|
|
8
8
|
|
|
9
9
|
const attachmentsPluralStringIds = {
|
|
10
10
|
one: 'TEXT_INPUT_ATTACHMENTS_ONE',
|
|
@@ -21,10 +21,11 @@ function Attachments({
|
|
|
21
21
|
readonly attachments: readonly Readonly<{ blob: Blob | File; thumbnailURL?: URL | undefined }>[];
|
|
22
22
|
readonly className?: string | undefined;
|
|
23
23
|
}>) {
|
|
24
|
+
const [uiState] = useUIState();
|
|
24
25
|
const classNames = useStyles(styles);
|
|
25
26
|
const localizeWithPlural = useLocalizer({ plural: true });
|
|
26
27
|
|
|
27
|
-
return attachments.length ? (
|
|
28
|
+
return uiState !== 'blueprint' && attachments.length ? (
|
|
28
29
|
<div className={cx(classNames['sendbox__attachment'], className)}>
|
|
29
30
|
{localizeWithPlural(attachmentsPluralStringIds, attachments.length)}
|
|
30
31
|
</div>
|
|
@@ -10,6 +10,13 @@
|
|
|
10
10
|
--webchat-sendbox-border-radius: var(--webchat-borderRadiusLarge);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
/* Copilot variant */
|
|
14
|
+
:global(.webchat-fluent) .sendbox.variant-copilot {
|
|
15
|
+
.sendbox__text-counter:not(.sendbox__text-counter--error) {
|
|
16
|
+
visibility: hidden;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
13
20
|
:global(.webchat-fluent) .sendbox__sendbox {
|
|
14
21
|
background-color: var(--webchat-colorNeutralBackground1);
|
|
15
22
|
border-radius: var(--webchat-sendbox-border-radius);
|
|
@@ -3,7 +3,7 @@ import cx from 'classnames';
|
|
|
3
3
|
import React, { memo, useCallback, useRef, useState, type FormEventHandler, type MouseEventHandler } from 'react';
|
|
4
4
|
import { useRefFrom } from 'use-ref-from';
|
|
5
5
|
import { SendIcon } from '../../icons';
|
|
6
|
-
import { useStyles } from '../../styles';
|
|
6
|
+
import { useStyles, useVariantClassName } from '../../styles';
|
|
7
7
|
import testIds from '../../testIds';
|
|
8
8
|
import { DropZone } from '../dropZone';
|
|
9
9
|
import { SuggestedActions } from '../suggestedActions';
|
|
@@ -27,7 +27,8 @@ const {
|
|
|
27
27
|
useSendBoxAttachments,
|
|
28
28
|
useSendBoxValue,
|
|
29
29
|
useSendMessage,
|
|
30
|
-
useStyleOptions
|
|
30
|
+
useStyleOptions,
|
|
31
|
+
useUIState
|
|
31
32
|
} = hooks;
|
|
32
33
|
|
|
33
34
|
type Props = Readonly<{
|
|
@@ -37,23 +38,31 @@ type Props = Readonly<{
|
|
|
37
38
|
}>;
|
|
38
39
|
|
|
39
40
|
function SendBox(props: Props) {
|
|
40
|
-
const inputRef = useRef<HTMLTextAreaElement>(null);
|
|
41
|
-
const [localMessage, setLocalMessage] = useState('');
|
|
42
|
-
const [globalMessage, setGlobalMessage] = useSendBoxValue();
|
|
43
|
-
const message = props.isPrimary ? globalMessage : localMessage;
|
|
44
|
-
const setMessage = props.isPrimary ? setGlobalMessage : setLocalMessage;
|
|
45
|
-
const [attachments, setAttachments] = useSendBoxAttachments();
|
|
46
41
|
const [{ hideTelephoneKeypadButton, hideUploadButton, maxMessageLength }] = useStyleOptions();
|
|
47
|
-
const
|
|
42
|
+
const [attachments, setAttachments] = useSendBoxAttachments();
|
|
43
|
+
const [globalMessage, setGlobalMessage] = useSendBoxValue();
|
|
44
|
+
const [localMessage, setLocalMessage] = useState('');
|
|
45
|
+
const [telephoneKeypadShown] = useTelephoneKeypadShown();
|
|
46
|
+
const [uiState] = useUIState();
|
|
48
47
|
const classNames = useStyles(styles);
|
|
48
|
+
const variantClassName = useVariantClassName(styles);
|
|
49
|
+
const errorMessageId = useUniqueId('sendbox__error-message-id');
|
|
50
|
+
const inputRef = useRef<HTMLTextAreaElement>(null);
|
|
49
51
|
const localize = useLocalizer();
|
|
50
|
-
const sendMessage = useSendMessage();
|
|
51
52
|
const makeThumbnail = useMakeThumbnail();
|
|
52
|
-
const
|
|
53
|
-
const [errorMessage, commitLatestError] = useSubmitError({ message, attachments });
|
|
54
|
-
const [telephoneKeypadShown] = useTelephoneKeypadShown();
|
|
53
|
+
const sendMessage = useSendMessage();
|
|
55
54
|
const setFocus = useFocus();
|
|
56
55
|
|
|
56
|
+
const message = props.isPrimary ? globalMessage : localMessage;
|
|
57
|
+
const setMessage = props.isPrimary ? setGlobalMessage : setLocalMessage;
|
|
58
|
+
const isBlueprint = uiState === 'blueprint';
|
|
59
|
+
|
|
60
|
+
const [errorMessage, commitLatestError] = useSubmitError({ message, attachments });
|
|
61
|
+
const isMessageLengthExceeded = !!maxMessageLength && message.length > maxMessageLength;
|
|
62
|
+
const shouldShowMessageLength =
|
|
63
|
+
!isBlueprint && !telephoneKeypadShown && maxMessageLength && isFinite(maxMessageLength);
|
|
64
|
+
const shouldShowTelephoneKeypad = !isBlueprint && telephoneKeypadShown;
|
|
65
|
+
|
|
57
66
|
useRegisterFocusSendBox(
|
|
58
67
|
useCallback(
|
|
59
68
|
({ noKeyboard, waitUntil }: SendBoxFocusOptions) => {
|
|
@@ -174,7 +183,12 @@ function SendBox(props: Props) {
|
|
|
174
183
|
};
|
|
175
184
|
|
|
176
185
|
return (
|
|
177
|
-
<form
|
|
186
|
+
<form
|
|
187
|
+
{...aria}
|
|
188
|
+
className={cx(classNames['sendbox'], variantClassName, props.className)}
|
|
189
|
+
data-testid={testIds.sendBoxContainer}
|
|
190
|
+
onSubmit={handleFormSubmit}
|
|
191
|
+
>
|
|
178
192
|
<SuggestedActions />
|
|
179
193
|
<div
|
|
180
194
|
className={cx(classNames['sendbox__sendbox'])}
|
|
@@ -185,7 +199,7 @@ function SendBox(props: Props) {
|
|
|
185
199
|
aria-label={isMessageLengthExceeded ? localize('TEXT_INPUT_LENGTH_EXCEEDED_ALT') : localize('TEXT_INPUT_ALT')}
|
|
186
200
|
className={cx(classNames['sendbox__sendbox-text'], classNames['sendbox__text-area--in-grid'])}
|
|
187
201
|
data-testid={testIds.sendBoxTextBox}
|
|
188
|
-
hidden={
|
|
202
|
+
hidden={shouldShowTelephoneKeypad}
|
|
189
203
|
onInput={handleMessageChange}
|
|
190
204
|
placeholder={props.placeholder ?? localize('TEXT_INPUT_PLACEHOLDER')}
|
|
191
205
|
ref={inputRef}
|
|
@@ -199,7 +213,7 @@ function SendBox(props: Props) {
|
|
|
199
213
|
/>
|
|
200
214
|
<Attachments attachments={attachments} className={classNames['sendbox__attachment--in-grid']} />
|
|
201
215
|
<div className={cx(classNames['sendbox__sendbox-controls'], classNames['sendbox__sendbox-controls--in-grid'])}>
|
|
202
|
-
{
|
|
216
|
+
{shouldShowMessageLength && (
|
|
203
217
|
<div
|
|
204
218
|
className={cx(classNames['sendbox__text-counter'], {
|
|
205
219
|
[classNames['sendbox__text-counter--error']]: isMessageLengthExceeded
|
|
@@ -215,7 +229,7 @@ function SendBox(props: Props) {
|
|
|
215
229
|
<ToolbarButton
|
|
216
230
|
aria-label={localize('TEXT_INPUT_SEND_BUTTON_ALT')}
|
|
217
231
|
data-testid={testIds.sendBoxSendButton}
|
|
218
|
-
disabled={isMessageLengthExceeded ||
|
|
232
|
+
disabled={isMessageLengthExceeded || shouldShowTelephoneKeypad}
|
|
219
233
|
type="submit"
|
|
220
234
|
>
|
|
221
235
|
<SendIcon />
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import { hooks } from 'botframework-webchat-api';
|
|
1
2
|
import cx from 'classnames';
|
|
2
|
-
import React, { forwardRef, useCallback, type FormEventHandler, type KeyboardEventHandler } from 'react';
|
|
3
|
+
import React, { forwardRef, Fragment, useCallback, type FormEventHandler, type KeyboardEventHandler } from 'react';
|
|
3
4
|
import { useStyles } from '../../styles';
|
|
4
5
|
import styles from './TextArea.module.css';
|
|
5
6
|
|
|
7
|
+
const { useUIState } = hooks;
|
|
8
|
+
|
|
6
9
|
const TextArea = forwardRef<
|
|
7
10
|
HTMLTextAreaElement,
|
|
8
11
|
Readonly<{
|
|
@@ -25,8 +28,11 @@ const TextArea = forwardRef<
|
|
|
25
28
|
value?: string | undefined;
|
|
26
29
|
}>
|
|
27
30
|
>((props, ref) => {
|
|
31
|
+
const [uiState] = useUIState();
|
|
28
32
|
const classNames = useStyles(styles);
|
|
29
33
|
|
|
34
|
+
const disabled = uiState === 'disabled';
|
|
35
|
+
|
|
30
36
|
const handleKeyDown = useCallback<KeyboardEventHandler<HTMLTextAreaElement>>(event => {
|
|
31
37
|
// Shift+Enter adds a new line
|
|
32
38
|
// Enter requests related form submission
|
|
@@ -43,39 +49,53 @@ const TextArea = forwardRef<
|
|
|
43
49
|
<div
|
|
44
50
|
className={cx(
|
|
45
51
|
classNames['sendbox__text-area'],
|
|
46
|
-
{
|
|
47
|
-
[classNames['sendbox__text-area--hidden']]: props.hidden
|
|
48
|
-
},
|
|
52
|
+
{ [classNames['sendbox__text-area--hidden']]: props.hidden },
|
|
49
53
|
props.className
|
|
50
54
|
)}
|
|
51
55
|
role={props.hidden ? 'hidden' : undefined}
|
|
52
56
|
>
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
57
|
+
{uiState === 'blueprint' ? (
|
|
58
|
+
<div
|
|
59
|
+
className={cx(
|
|
60
|
+
classNames['sendbox__text-area-doppelganger'],
|
|
61
|
+
classNames['sendbox__text-area-input--scroll'],
|
|
62
|
+
classNames['sendbox__text-area-shared']
|
|
63
|
+
)}
|
|
64
|
+
>
|
|
65
|
+
{' '}
|
|
66
|
+
</div>
|
|
67
|
+
) : (
|
|
68
|
+
<Fragment>
|
|
69
|
+
<div
|
|
70
|
+
className={cx(
|
|
71
|
+
classNames['sendbox__text-area-doppelganger'],
|
|
72
|
+
classNames['sendbox__text-area-input--scroll'],
|
|
73
|
+
classNames['sendbox__text-area-shared']
|
|
74
|
+
)}
|
|
75
|
+
>
|
|
76
|
+
{props.value || props.placeholder}{' '}
|
|
77
|
+
</div>
|
|
78
|
+
<textarea
|
|
79
|
+
aria-disabled={disabled}
|
|
80
|
+
aria-label={props['aria-label']}
|
|
81
|
+
className={cx(
|
|
82
|
+
classNames['sendbox__text-area-input'],
|
|
83
|
+
classNames['sendbox__text-area-input--scroll'],
|
|
84
|
+
classNames['sendbox__text-area-shared']
|
|
85
|
+
)}
|
|
86
|
+
data-testid={props['data-testid']}
|
|
87
|
+
onInput={props.onInput}
|
|
88
|
+
onKeyDown={handleKeyDown}
|
|
89
|
+
placeholder={props.placeholder}
|
|
90
|
+
readOnly={disabled}
|
|
91
|
+
ref={ref}
|
|
92
|
+
rows={props.startRows ?? 1}
|
|
93
|
+
// eslint-disable-next-line no-magic-numbers
|
|
94
|
+
tabIndex={props.hidden ? -1 : undefined}
|
|
95
|
+
value={props.value}
|
|
96
|
+
/>
|
|
97
|
+
</Fragment>
|
|
98
|
+
)}
|
|
79
99
|
</div>
|
|
80
100
|
);
|
|
81
101
|
});
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
:global(.webchat-fluent) .sendbox__toolbar {
|
|
2
|
+
--webchat__sendbox-button-height: 32px;
|
|
3
|
+
|
|
2
4
|
display: flex;
|
|
3
5
|
gap: 4px;
|
|
6
|
+
height: var(--webchat__sendbox-button-height);
|
|
4
7
|
margin-inline-start: auto;
|
|
5
8
|
}
|
|
6
9
|
|
|
@@ -14,9 +17,9 @@
|
|
|
14
17
|
color: currentColor;
|
|
15
18
|
cursor: pointer;
|
|
16
19
|
display: flex;
|
|
20
|
+
height: var(--webchat__sendbox-button-height);
|
|
17
21
|
justify-content: center;
|
|
18
22
|
padding: 3px;
|
|
19
|
-
width: 32px;
|
|
20
23
|
|
|
21
24
|
> svg {
|
|
22
25
|
font-size: 20px;
|
|
@@ -26,14 +29,17 @@
|
|
|
26
29
|
&.sendbox__toolbar-button--selected {
|
|
27
30
|
color: var(--webchat-colorNeutralForeground2BrandSelected);
|
|
28
31
|
}
|
|
32
|
+
|
|
29
33
|
@media (hover: hover) {
|
|
30
34
|
&:not([aria-disabled="true"]):hover {
|
|
31
35
|
color: var(--webchat-colorNeutralForeground2BrandHover);
|
|
32
36
|
}
|
|
33
37
|
}
|
|
38
|
+
|
|
34
39
|
&:not([aria-disabled="true"]):active {
|
|
35
40
|
color: var(--webchat-colorNeutralForeground2BrandPressed);
|
|
36
41
|
}
|
|
42
|
+
|
|
37
43
|
&[aria-disabled="true"] {
|
|
38
44
|
color: var(--webchat-colorNeutralForegroundDisabled);
|
|
39
45
|
cursor: not-allowed;
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import { hooks } from 'botframework-webchat-api';
|
|
1
2
|
import cx from 'classnames';
|
|
2
3
|
import React, { memo, type MouseEventHandler, type ReactNode } from 'react';
|
|
3
|
-
import styles from './Toolbar.module.css';
|
|
4
4
|
import { useStyles } from '../../styles';
|
|
5
|
+
import styles from './Toolbar.module.css';
|
|
6
|
+
|
|
7
|
+
const { useUIState } = hooks;
|
|
5
8
|
|
|
6
9
|
const preventDefaultHandler: MouseEventHandler<HTMLButtonElement> = event => event.preventDefault();
|
|
7
10
|
|
|
@@ -19,20 +22,22 @@ export const ToolbarButton = memo(
|
|
|
19
22
|
}>
|
|
20
23
|
) => {
|
|
21
24
|
const classNames = useStyles(styles);
|
|
25
|
+
const [uiState] = useUIState();
|
|
26
|
+
|
|
27
|
+
const disabled = props.disabled || uiState === 'disabled';
|
|
22
28
|
|
|
23
29
|
return (
|
|
24
30
|
<button
|
|
31
|
+
aria-disabled={disabled ? 'true' : undefined}
|
|
25
32
|
aria-label={props['aria-label']}
|
|
26
33
|
className={cx(classNames['sendbox__toolbar-button'], props.className, {
|
|
27
34
|
[classNames['sendbox__toolbar-button--selected']]: props.selected
|
|
28
35
|
})}
|
|
29
36
|
data-testid={props['data-testid']}
|
|
30
|
-
onClick={
|
|
37
|
+
onClick={disabled ? preventDefaultHandler : props.onClick}
|
|
38
|
+
// eslint-disable-next-line no-magic-numbers
|
|
39
|
+
tabIndex={disabled ? -1 : undefined}
|
|
31
40
|
type={props.type === 'submit' ? 'submit' : 'button'}
|
|
32
|
-
{...(props.disabled && {
|
|
33
|
-
'aria-disabled': 'true',
|
|
34
|
-
tabIndex: -1
|
|
35
|
-
})}
|
|
36
41
|
>
|
|
37
42
|
{props.children}
|
|
38
43
|
</button>
|
|
@@ -43,9 +48,14 @@ export const ToolbarButton = memo(
|
|
|
43
48
|
ToolbarButton.displayName = 'ToolbarButton';
|
|
44
49
|
|
|
45
50
|
export const Toolbar = memo((props: Readonly<{ children?: ReactNode | undefined; className?: string | undefined }>) => {
|
|
51
|
+
const [uiState] = useUIState();
|
|
46
52
|
const classNames = useStyles(styles);
|
|
47
53
|
|
|
48
|
-
return
|
|
54
|
+
return (
|
|
55
|
+
<div className={cx(classNames['sendbox__toolbar'], props.className)}>
|
|
56
|
+
{uiState !== 'blueprint' && props.children}
|
|
57
|
+
</div>
|
|
58
|
+
);
|
|
49
59
|
});
|
|
50
60
|
|
|
51
61
|
Toolbar.displayName = 'Toolbar';
|
|
@@ -3,12 +3,13 @@ import React, { MouseEventHandler, ReactNode, forwardRef, memo, useRef } from 'r
|
|
|
3
3
|
const preventDefaultHandler: MouseEventHandler<HTMLButtonElement> = event => event.preventDefault();
|
|
4
4
|
|
|
5
5
|
type AccessibleButtonProps = Readonly<{
|
|
6
|
+
'aria-hidden'?: boolean | undefined;
|
|
7
|
+
'data-testid'?: string | undefined;
|
|
8
|
+
children?: ReactNode | undefined;
|
|
6
9
|
className?: string | undefined;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
onClick?: MouseEventHandler<HTMLButtonElement>;
|
|
11
|
-
tabIndex?: number;
|
|
10
|
+
disabled?: boolean | undefined;
|
|
11
|
+
onClick?: MouseEventHandler<HTMLButtonElement> | undefined;
|
|
12
|
+
tabIndex?: number | undefined;
|
|
12
13
|
type: 'button';
|
|
13
14
|
}>;
|
|
14
15
|
|
|
@@ -31,23 +32,24 @@ type AccessibleButtonProps = Readonly<{
|
|
|
31
32
|
// - If the widget is contained by a <form>, the developer need to filter out some `onSubmit` event caused by this widget
|
|
32
33
|
|
|
33
34
|
const AccessibleButton = forwardRef<HTMLButtonElement, AccessibleButtonProps>(
|
|
34
|
-
(
|
|
35
|
+
(
|
|
36
|
+
{ 'aria-hidden': ariaHidden, children, className, 'data-testid': dataTestId, disabled, onClick, tabIndex },
|
|
37
|
+
forwardedRef
|
|
38
|
+
) => {
|
|
35
39
|
const targetRef = useRef<HTMLButtonElement>(null);
|
|
36
40
|
|
|
37
41
|
const ref = forwardedRef || targetRef;
|
|
38
42
|
|
|
39
43
|
return (
|
|
40
44
|
<button
|
|
41
|
-
aria-disabled={disabled ? 'true' :
|
|
45
|
+
aria-disabled={disabled ? 'true' : undefined}
|
|
42
46
|
aria-hidden={ariaHidden}
|
|
47
|
+
className={className}
|
|
48
|
+
data-testid={dataTestId}
|
|
43
49
|
onClick={disabled ? preventDefaultHandler : onClick}
|
|
44
50
|
ref={ref}
|
|
45
|
-
|
|
46
|
-
{
|
|
47
|
-
'aria-disabled': 'true',
|
|
48
|
-
tabIndex: -1
|
|
49
|
-
})}
|
|
50
|
-
{...props}
|
|
51
|
+
// eslint-disable-next-line no-magic-numbers
|
|
52
|
+
tabIndex={disabled ? -1 : tabIndex}
|
|
51
53
|
type="button"
|
|
52
54
|
>
|
|
53
55
|
{children}
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
:global(.webchat-fluent) .suggested-action {
|
|
2
2
|
align-items: center;
|
|
3
3
|
background: transparent;
|
|
4
|
-
border-radius:
|
|
5
|
-
border:
|
|
4
|
+
border-radius: var(--webchat-borderRadiusXLarge);
|
|
5
|
+
border: var(--webchat-strokeWidthThin) solid var(--webchat-colorBrandStroke2);
|
|
6
6
|
color: currentColor;
|
|
7
7
|
cursor: pointer;
|
|
8
8
|
display: flex;
|
|
9
|
-
font-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
font-family: var(--webchat__font--primary);
|
|
10
|
+
font-size: var(--webchat-fontSizeBase200);
|
|
11
|
+
gap: var(--webchat-spacingHorizontalXS);
|
|
12
|
+
padding: var(--webchat-spacingVerticalXS) var(--webchat-spacingHorizontalS);
|
|
12
13
|
text-align: start;
|
|
13
|
-
transition: all
|
|
14
|
+
transition: all var(--webchat-durationNormal) var(--webchat-curveDecelerateMid);
|
|
14
15
|
|
|
15
16
|
@media (hover: hover) {
|
|
16
17
|
&:not([aria-disabled="true"]):hover {
|
|
@@ -29,7 +30,7 @@
|
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
:global(.webchat-fluent) .suggested-action__image {
|
|
32
|
-
font-size:
|
|
33
|
+
font-size: var(--webchat-fontSizeBase200);
|
|
33
34
|
height: 1em;
|
|
34
35
|
width: 1em;
|
|
35
36
|
}
|
|
@@ -2,12 +2,14 @@ import { hooks } from 'botframework-webchat-component';
|
|
|
2
2
|
import { type DirectLineCardAction } from 'botframework-webchat-core';
|
|
3
3
|
import cx from 'classnames';
|
|
4
4
|
import React, { MouseEventHandler, memo, useCallback } from 'react';
|
|
5
|
-
|
|
5
|
+
|
|
6
6
|
import { useStyles } from '../../styles';
|
|
7
|
+
import testIds from '../../testIds';
|
|
7
8
|
import AccessibleButton from './AccessibleButton';
|
|
8
9
|
import { useRovingFocusItemRef } from './private/rovingFocus';
|
|
10
|
+
import styles from './SuggestedAction.module.css';
|
|
9
11
|
|
|
10
|
-
const {
|
|
12
|
+
const { useFocus, usePerformCardAction, useScrollToEnd, useStyleSet, useSuggestedActions, useUIState } = hooks;
|
|
11
13
|
|
|
12
14
|
type SuggestedActionProps = Readonly<{
|
|
13
15
|
buttonText: string | undefined;
|
|
@@ -44,7 +46,7 @@ function SuggestedAction({
|
|
|
44
46
|
}: SuggestedActionProps) {
|
|
45
47
|
const [_, setSuggestedActions] = useSuggestedActions();
|
|
46
48
|
const [{ suggestedAction: suggestedActionStyleSet }] = useStyleSet();
|
|
47
|
-
const [
|
|
49
|
+
const [uiState] = useUIState();
|
|
48
50
|
const focus = useFocus();
|
|
49
51
|
const focusRef = useRovingFocusItemRef<HTMLButtonElement>(itemIndex);
|
|
50
52
|
const performCardAction = usePerformCardAction();
|
|
@@ -75,7 +77,8 @@ function SuggestedAction({
|
|
|
75
77
|
return (
|
|
76
78
|
<AccessibleButton
|
|
77
79
|
className={cx(classNames['suggested-action'], suggestedActionStyleSet + '', (className || '') + '')}
|
|
78
|
-
|
|
80
|
+
data-testid={testIds.sendBoxSuggestedAction}
|
|
81
|
+
disabled={uiState === 'disabled'}
|
|
79
82
|
onClick={handleClick}
|
|
80
83
|
ref={focusRef}
|
|
81
84
|
type="button"
|
|
@@ -8,7 +8,7 @@ import RovingFocusProvider from './private/rovingFocus';
|
|
|
8
8
|
import SuggestedAction from './SuggestedAction';
|
|
9
9
|
import styles from './SuggestedActions.module.css';
|
|
10
10
|
|
|
11
|
-
const { useFocus, useLocalizer, useStyleOptions, useStyleSet, useSuggestedActions } = hooks;
|
|
11
|
+
const { useFocus, useLocalizer, useStyleOptions, useStyleSet, useSuggestedActions, useUIState } = hooks;
|
|
12
12
|
|
|
13
13
|
function SuggestedActionStackedOrFlowContainer(
|
|
14
14
|
props: Readonly<{
|
|
@@ -19,6 +19,7 @@ function SuggestedActionStackedOrFlowContainer(
|
|
|
19
19
|
) {
|
|
20
20
|
const [{ suggestedActionLayout }] = useStyleOptions();
|
|
21
21
|
const [{ suggestedActions: suggestedActionsStyleSet }] = useStyleSet();
|
|
22
|
+
const [uiState] = useUIState();
|
|
22
23
|
const classNames = useStyles(styles);
|
|
23
24
|
|
|
24
25
|
return (
|
|
@@ -36,7 +37,7 @@ function SuggestedActionStackedOrFlowContainer(
|
|
|
36
37
|
)}
|
|
37
38
|
role="toolbar"
|
|
38
39
|
>
|
|
39
|
-
{
|
|
40
|
+
{uiState !== 'blueprint' && props.children}
|
|
40
41
|
</div>
|
|
41
42
|
);
|
|
42
43
|
}
|
|
@@ -22,6 +22,9 @@
|
|
|
22
22
|
--webchat-colorNeutralBackground5: var(--colorNeutralBackground5, #ebebeb);
|
|
23
23
|
--webchat-colorNeutralBackground6: var(--colorNeutralBackground6, #e6e6e6);
|
|
24
24
|
|
|
25
|
+
--webchat-colorNeutralStencil1: var(--colorNeutralStencil1, #e6e6e6); /* #575757 for dark mode */
|
|
26
|
+
--webchat-colorNeutralStencil2: var(--colorNeutralStencil2, #fafafa); /* #333333 for dark mode */
|
|
27
|
+
|
|
25
28
|
--webchat-colorTransparentBackground: var(--colorTransparentBackground, rgba(0, 0, 0, 0.4));
|
|
26
29
|
|
|
27
30
|
--webchat-colorNeutralStrokeDisabled: var(--colorNeutralStrokeDisabled, #e0e0e0);
|
|
@@ -257,18 +260,26 @@
|
|
|
257
260
|
display: none;
|
|
258
261
|
}
|
|
259
262
|
|
|
260
|
-
:global(.webchat__bubble)::after,
|
|
261
|
-
|
|
262
|
-
position: absolute;
|
|
263
|
-
inset: -2px;
|
|
264
|
-
border: var(--webchat-strokeWidthThick) solid var(--webchat-colorStrokeFocus2);
|
|
263
|
+
:global(.webchat__bubble)::after,
|
|
264
|
+
:global(.pre-chat-message-activity)::after {
|
|
265
265
|
border-radius: var(--webchat__bubble--border-radius);
|
|
266
|
+
content: '';
|
|
267
|
+
inset: 0;
|
|
268
|
+
outline-offset: 0;
|
|
269
|
+
outline: var(--webchat-strokeWidthThick) solid var(--webchat-colorStrokeFocus2);
|
|
266
270
|
pointer-events: none;
|
|
271
|
+
position: absolute;
|
|
267
272
|
}
|
|
268
273
|
|
|
269
274
|
:global(.pre-chat-message-activity)::after {
|
|
270
|
-
|
|
271
|
-
|
|
275
|
+
border-radius: var(--webchat-borderRadiusMedium);
|
|
276
|
+
outline-offset: -3px;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
:global(.liner-message-activity__text) {
|
|
280
|
+
border-radius: 20px;
|
|
281
|
+
outline-offset: 4px;
|
|
282
|
+
outline: var(--webchat-strokeWidthThick) solid var(--webchat-colorStrokeFocus2);
|
|
272
283
|
}
|
|
273
284
|
}
|
|
274
285
|
|
|
@@ -356,7 +367,7 @@
|
|
|
356
367
|
outline-offset: calc(var(--webchat-strokeWidthThick) * -1);
|
|
357
368
|
}
|
|
358
369
|
|
|
359
|
-
|
|
370
|
+
&[aria-disabled="true"] {
|
|
360
371
|
background: var(--webchat-colorNeutralBackgroundDisabled);
|
|
361
372
|
border: var(--webchat-strokeWidthThin) solid var(--webchat-colorNeutralStrokeDisabled);
|
|
362
373
|
color: var(--webchat-colorNeutralForegroundDisabled);
|
|
@@ -21,9 +21,11 @@ const activityMiddleware: readonly ActivityMiddleware[] = Object.freeze([
|
|
|
21
21
|
(...args) => {
|
|
22
22
|
const activity = args[0]?.activity;
|
|
23
23
|
|
|
24
|
+
// TODO: Should show pre-chat only when it is the very first message in the chat history.
|
|
24
25
|
if (isPreChatMessageActivity(activity)) {
|
|
25
26
|
return () => <PreChatMessageActivity activity={activity} />;
|
|
26
27
|
}
|
|
28
|
+
|
|
27
29
|
if (isLinerMessageActivity(activity)) {
|
|
28
30
|
return () => <LinerMessageActivity activity={activity} />;
|
|
29
31
|
}
|
package/src/testIds.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
const testIds = {
|
|
2
2
|
preChatMessageActivityStarterPromptsCardAction: 'pre-chat message activity starter prompts card action',
|
|
3
|
+
sendBoxContainer: 'send box container',
|
|
3
4
|
sendBoxDropZone: 'send box drop zone',
|
|
4
5
|
sendBoxSendButton: 'send box send button',
|
|
6
|
+
sendBoxSuggestedAction: 'send box suggested action',
|
|
5
7
|
sendBoxTextBox: 'send box text area',
|
|
6
8
|
sendBoxTelephoneKeypadButton1: `send box telephone keypad button 1`,
|
|
7
9
|
sendBoxTelephoneKeypadButton2: `send box telephone keypad button 2`,
|