Skip to content

Commit 4091100

Browse files
authored
Fluent: add dedicated loader animation (#5423)
* Fluent: add dedicated loader animation * Fix failing * Changelog * Fix import order
1 parent ae4a98f commit 4091100

10 files changed

+82
-8
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ Notes: web developers are advised to use [`~` (tilde range)](https://github.com/
7878
- New style option supports two values: `'activity-actions'` and `'activity-status'` (default)
7979
- When set to `'activity-actions'`, feedback buttons are displayed in the activity actions toolbar
8080
- When set to `'activity-status'`, feedback buttons appear in the activity status area (default behavior)
81+
- Added support for including activity ID and key into form data indicated by `data-webchat-include-activity-id` and `data-webchat-include-activity-key` attributes, in PR [#5418](https://github.com/microsoft/BotFramework-WebChat/pull/5418), by [@OEvgeny](https://github.com/OEvgeny)
82+
- Added dedicated loading animation for messages in preparing state for Fluent theme, in PR [#5423](https://github.com/microsoft/BotFramework-WebChat/pull/5423), by [@OEvgeny](https://github.com/OEvgeny)
8183

8284
### Changed
8385

@@ -2037,7 +2039,6 @@ It should check the result from downstream middleware. If it is falsy, it should
20372039
- Adds Direct Line Speech support, by [@compulim](https://github.com/compulim) in PR [#2621](https://github.com/microsoft/BotFramework-WebChat/pull/2621)
20382040
- Adds [`[email protected]`](https://npmjs.com/package/microsoft-cognitiveservices-speech-sdk), in PR [#2704](https://github.com/microsoft/BotFramework-WebChat/pull/2704)
20392041
- Fixes [#2692](https://github.com/microsoft/BotFramework-WebChat/issues/2692). Rename sample 17 to 17.a, by [@corinagum](https://github.com/corinagum) in PR [#2695](https://github.com/microsoft/BotFramework-WebChat/pull/2695)
2040-
- Added support for including activity ID and key into form data indicated by `data-webchat-include-activity-id` and `data-webchat-include-activity-key` attributes, in PR [#5418](https://github.com/microsoft/BotFramework-WebChat/pull/5418), by [@OEvgeny](https://github.com/OEvgeny)
20412042

20422043
### Fixed
20432044

Loading
Loading
192 Bytes
Loading

__tests__/html/fluentTheme/side-by-side.wide.dark.html

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -501,13 +501,23 @@
501501
type: 'message'
502502
}
503503
], [
504+
{
505+
timestamp: timestamp(),
506+
from: { "role": "user" },
507+
id: "6.0",
508+
text: "Start onboarding",
509+
type: "message"
510+
},
504511
{
505512
timestamp: timestamp(),
506513
from: { role: 'bot' },
507-
entities: [aiMessageEntity],
514+
entities: [{
515+
...aiMessageEntity,
516+
keywords: []
517+
}],
508518
id: "a4c0c01d-c06e-4dde-9278-265c607b545b",
509519
type: "typing",
510-
text: "Processing",
520+
text: "Processing your request…",
511521
channelData: {
512522
streamType: "informative",
513523
streamSequence: 1
@@ -532,7 +542,7 @@
532542
entities: [aiMessageEntity],
533543
id: "a4c0c01d-c06e-4dde-9278-265c607b545b-82",
534544
type: "message",
535-
text: "Hello there! I'm your Onboarding Buddy, here to guide you through your first days at our company. Let's go over some important information:\n\n- Your **orientation session** is scheduled for tomorrow at **9:00 AM** in the main conference room.\n\n- Don't forget to complete your **new hire paperwork** by the end of this week. This includes your tax forms, benefits enrollment, and emergency contact information.\n\nGood luck!",
545+
text: "- Don't forget to complete your **new hire paperwork** by the end of this week. This includes your tax forms, benefits enrollment, and emergency contact information.\n\nGood luck!",
536546
channelData: {
537547
streamType: "final",
538548
streamId: "a4c0c01d-c06e-4dde-9278-265c607b5457",

__tests__/html/fluentTheme/side-by-side.wide.html

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -511,13 +511,23 @@
511511
type: 'message'
512512
}
513513
], [
514+
{
515+
timestamp: timestamp(),
516+
from: { "role": "user" },
517+
id: "6.0",
518+
text: "Start onboarding",
519+
type: "message"
520+
},
514521
{
515522
timestamp: timestamp(),
516523
from: { role: 'bot' },
517-
entities: [aiMessageEntity],
524+
entities: [{
525+
...aiMessageEntity,
526+
keywords: []
527+
}],
518528
id: "a4c0c01d-c06e-4dde-9278-265c607b545b",
519529
type: "typing",
520-
text: "Processing",
530+
text: "Processing your request…",
521531
channelData: {
522532
streamType: "informative",
523533
streamSequence: 1
@@ -542,7 +552,7 @@
542552
entities: [aiMessageEntity],
543553
id: "a4c0c01d-c06e-4dde-9278-265c607b545b-82",
544554
type: "message",
545-
text: "Hello there! I'm your Onboarding Buddy, here to guide you through your first days at our company. Let's go over some important information:\n\n- Your **orientation session** is scheduled for tomorrow at **9:00 AM** in the main conference room.\n\n- Don't forget to complete your **new hire paperwork** by the end of this week. This includes your tax forms, benefits enrollment, and emergency contact information.\n\nGood luck!",
555+
text: "- Don't forget to complete your **new hire paperwork** by the end of this week. This includes your tax forms, benefits enrollment, and emergency contact information.\n\nGood luck!",
546556
channelData: {
547557
streamType: "final",
548558
streamId: "a4c0c01d-c06e-4dde-9278-265c607b5457",

packages/fluent-theme/src/components/activity/ActivityDecorator.module.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,12 @@
145145
&:has(:global(.border-loader)) {
146146
width: 100%;
147147
}
148+
149+
/* Ensure activity loader doesn't have bubble and shadow */
150+
&:has(:global(.activity-loader)) :global(.webchat__bubble__content) {
151+
background: transparent;
152+
box-shadow: none;
153+
}
148154
}
149155

150156
/* Message bubble content */
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
:global(.webchat-fluent) .activity-loader {
2+
flex: none;
3+
height: 8px;
4+
margin: var(--webchat-spacingHorizontalM) 0 0 18px;
5+
width: auto;
6+
7+
&.variant-fluent {
8+
margin: 0 0 var(--webchat-spacingHorizontalM) var(--webchat-spacingVerticalSNudge);
9+
}
10+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { useStyles } from 'botframework-webchat-styles/react';
2+
import cx from 'classnames';
3+
import React, { Fragment, memo, type ReactNode } from 'react';
4+
5+
import { useVariantClassName } from '../../styles';
6+
import styles from './ActivityLoader.module.css';
7+
8+
const loadingAnimationUrl =
9+
'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MDAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCA0MDAgMjAiPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0iYSIgeDE9IjAiIHgyPSIxMDAlIiB5MT0iMCIgeTI9IjAiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9IjAlIj48YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJzdG9wLWNvbG9yIiBkdXI9IjJzIiBrZXlUaW1lcz0iMDswLjI7MC4zMzswLjU7MC42NjswLjg7MSIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIHZhbHVlcz0iI2FkNWFlMTsjYWQ1YWUxOyMwRTk0RTE7IzBFOTRFMTsjNjY5ZmMyOyM2NjlmYzI7I2FkNWFlMSIvPjwvc3RvcD48c3RvcCBvZmZzZXQ9IjUwJSI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0ic3RvcC1jb2xvciIgZHVyPSIycyIga2V5VGltZXM9IjA7MC4yOzAuMzM7MC41OzAuNjY7MC44OzEiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiB2YWx1ZXM9IiNlOTYxOGQ7I2U5NjE4ZDsjNTdBQjgyOyM1N0FCODI7IzYzNzdlMDsjNjM3N2UwOyNlOTYxOGQiLz48L3N0b3A+PHN0b3Agb2Zmc2V0PSIxMDAlIj48YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJzdG9wLWNvbG9yIiBkdXI9IjJzIiBrZXlUaW1lcz0iMDswLjI7MC4zMzswLjU7MC42NjswLjg7MSIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIHZhbHVlcz0iI2ZkOWU1ZjsjZmQ5ZTVmOyNDNkMyMjU7I0M2QzIyNTsjOWI4MGVjOyM5YjgwZWM7I2ZkOWU1ZiIvPjwvc3RvcD48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48ZyBmaWxsPSJ1cmwoI2EpIj48cmVjdCBoZWlnaHQ9IjIwIiByeD0iMTAiPjxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9IngiIGR1cj0iMnMiIGtleVRpbWVzPSIwOzAuNTswLjY2OzEiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiB2YWx1ZXM9IjI2OzI2OzA7MCIvPjxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9IndpZHRoIiBkdXI9IjJzIiBrZXlUaW1lcz0iMDswLjI7MC4zMzswLjU7MC42NjsxIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgdmFsdWVzPSIyMDsyMDszMDszMDsyMDsyMCIvPjxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9Im9wYWNpdHkiIGR1cj0iMnMiIGtleVRpbWVzPSIwOzAuNTswLjY2OzEiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiB2YWx1ZXM9IjE7MTswOzAiLz48L3JlY3Q+PHJlY3QgaGVpZ2h0PSIyMCIgcng9IjEwIj48YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJ4IiBkdXI9IjJzIiBrZXlUaW1lcz0iMDswLjI7MC4zMzswLjU7MC42NjswLjg7MSIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIHZhbHVlcz0iNjI7NjI7NzI7NzI7MjY7MjY7MCIvPjxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9IndpZHRoIiBkdXI9IjJzIiBrZXlUaW1lcz0iMDswLjI7MC4zMzswLjU7MC42NjswLjg7MSIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIHZhbHVlcz0iMTA0OzEwNDsyMDsyMDs3MDs3MDsyMCIvPjxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9Im9wYWNpdHkiIGR1cj0iMnMiIGtleVRpbWVzPSIwOzAuODsxIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgdmFsdWVzPSIxOzE7MCIvPjwvcmVjdD48cmVjdCBoZWlnaHQ9IjIwIiByeD0iMTAiPjxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9IngiIGR1cj0iMnMiIGtleVRpbWVzPSIwOzAuMjswLjMzOzAuNTswLjY2OzAuODsxIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgdmFsdWVzPSIxODI7MTgyOzEwODsxMDg7MTEyOzExMjsyNiIvPjxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9IndpZHRoIiBkdXI9IjJzIiBrZXlUaW1lcz0iMDswLjI7MC4zMzswLjU7MC42NjsxIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgdmFsdWVzPSIyMDsyMDs2MDs2MDsyMDsyMCIvPjwvcmVjdD48cmVjdCBoZWlnaHQ9IjIwIiByeD0iMTAiPjxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9IngiIGR1cj0iMnMiIGtleVRpbWVzPSIwOzAuMjswLjMzOzAuNTswLjY2OzAuODsxIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgdmFsdWVzPSIyMTg7MjE4OzE4NDsxODQ7MTQ4OzE0ODs2MiIvPjxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9IndpZHRoIiBkdXI9IjJzIiBrZXlUaW1lcz0iMDswLjI7MC4zMzswLjU7MC42NjswLjg7MSIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIHZhbHVlcz0iNjA7NjA7ODA7ODA7NDA7NDA7MTA0Ii8+PC9yZWN0PjxyZWN0IGhlaWdodD0iMjAiIHJ4PSIxMCI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0ieCIgZHVyPSIycyIga2V5VGltZXM9IjA7MC4yOzAuMzM7MC41OzAuNjY7MC44OzEiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiB2YWx1ZXM9IjI5NDsyOTQ7MjgwOzI4MDsyMDQ7MjA0OzE4MiIvPjxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9IndpZHRoIiBkdXI9IjJzIiBrZXlUaW1lcz0iMDswLjI7MC4zMzswLjU7MC42NjswLjg7MSIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIHZhbHVlcz0iNDA7NDA7MjA7MjA7ODA7ODA7MjAiLz48L3JlY3Q+PHJlY3QgaGVpZ2h0PSIyMCIgcng9IjEwIj48YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJ4IiBkdXI9IjJzIiBrZXlUaW1lcz0iMDswLjI7MC4zMzswLjU7MC42NjswLjg7MSIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIHZhbHVlcz0iMzUwOzM1MDszMTY7MzE2OzMwMDszMDA7MjE4Ii8+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0id2lkdGgiIGR1cj0iMnMiIGtleVRpbWVzPSIwOzAuMjswLjMzOzAuNTswLjY2OzAuODsxIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgdmFsdWVzPSIyMDsyMDs2MDs2MDsyMDsyMDs2MCIvPjwvcmVjdD48cmVjdCBoZWlnaHQ9IjIwIiByeD0iMTAiPjxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9IngiIGR1cj0iMnMiIGtleVRpbWVzPSIwOzAuMjswLjMzOzAuNTswLjY2OzAuODsxIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgdmFsdWVzPSIzODY7Mzg2OzM5MjszOTI7MzM2OzMzNjsyOTQiLz48YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJ3aWR0aCIgZHVyPSIycyIga2V5VGltZXM9IjA7MC41OzAuNjY7MSIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIHZhbHVlcz0iMjA7MjA7NDA7NDAiLz48YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJvcGFjaXR5IiBkdXI9IjJzIiBrZXlUaW1lcz0iMDswLjU7MC42NjsxIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgdmFsdWVzPSIwOzA7MTsxIi8+PC9yZWN0PjxyZWN0IHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgcng9IjEwIj48YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJ4IiBkdXI9IjJzIiBrZXlUaW1lcz0iMDswLjI7MC4zMzswLjU7MC42NjswLjg7MSIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIHZhbHVlcz0iNDIyOzQyMjs0Mjg7NDI4OzM5MjszOTI7MzUwIi8+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0ib3BhY2l0eSIgZHVyPSIycyIga2V5VGltZXM9IjA7MC44OzEiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiB2YWx1ZXM9IjA7MDsxIi8+PC9yZWN0PjwvZz48L3N2Zz4=';
10+
11+
function FluentActivityLoader({ children }: Readonly<{ children?: ReactNode | undefined }>) {
12+
const classNames = useStyles(styles);
13+
const variantClassName = useVariantClassName(classNames);
14+
return (
15+
<Fragment>
16+
{children}
17+
<img
18+
alt=""
19+
className={cx(classNames['activity-loader'], variantClassName)}
20+
role="presentation"
21+
src={loadingAnimationUrl}
22+
/>
23+
</Fragment>
24+
);
25+
}
26+
27+
export default memo(FluentActivityLoader);

packages/fluent-theme/src/private/FluentThemeProvider.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { type ActivityMiddleware, type StyleOptions } from 'botframework-webchat-api';
2+
import { DecoratorComposer, DecoratorMiddleware } from 'botframework-webchat-api/decorator';
23
import { Components } from 'botframework-webchat-component';
34
import { WebChatDecorator } from 'botframework-webchat-component/decorator';
45
import React, { memo, type ReactNode } from 'react';
56

67
import { ActivityDecorator } from '../components/activity';
8+
import ActivityLoader from '../components/activity/ActivityLoader';
79
import { isPreChatMessageActivity, PreChatMessageActivity } from '../components/preChatActivity';
810
import { PrimarySendBox } from '../components/sendBox';
911
import { TelephoneKeypadProvider } from '../components/telephoneKeypad';
@@ -41,6 +43,12 @@ const activityMiddleware: readonly ActivityMiddleware[] = Object.freeze([
4143

4244
const sendBoxMiddleware = [() => () => () => PrimarySendBox];
4345

46+
const decoratorMiddleware: DecoratorMiddleware[] = [
47+
init =>
48+
init === 'activity border' &&
49+
(next => request => (request.livestreamingState === 'preparing' ? ActivityLoader : next(request)))
50+
];
51+
4452
const styles = createStyles();
4553

4654
const fluentStyleOptions: StyleOptions = Object.freeze({
@@ -57,7 +65,9 @@ const FluentThemeProvider = ({ children, variant = 'fluent' }: Props) => (
5765
styleOptions={fluentStyleOptions}
5866
styles={styles}
5967
>
60-
<WebChatDecorator>{children}</WebChatDecorator>
68+
<WebChatDecorator>
69+
<DecoratorComposer middleware={decoratorMiddleware}>{children}</DecoratorComposer>
70+
</WebChatDecorator>
6171
</ThemeProvider>
6272
</TelephoneKeypadProvider>
6373
</WebChatTheme>

0 commit comments

Comments
 (0)