Skip to content

Commit 9ddea38

Browse files
committed
Add sample
1 parent 69cacaf commit 9ddea38

11 files changed

Lines changed: 173 additions & 68 deletions

File tree

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
.channel-message {
2+
align-items: center;
3+
display: flex;
4+
flex-direction: column;
5+
}
6+
7+
.channel-message__body {
8+
background-color: #666;
9+
border-radius: 100px;
10+
color: White;
11+
font-family: sans-serif;
12+
font-size: small;
13+
padding: 0.2em 0.5em;
14+
}

__tests__/html2/samples/middleware/channelMessage.html

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,9 @@
33
<head>
44
<title>Sample</title>
55
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
6-
<script type="importmap">
7-
{
8-
"imports": {
9-
"react": "https://esm.sh/react@18.3.1",
10-
"react-dom": "https://esm.sh/react-dom@18.3.1"
11-
}
12-
}
13-
</script>
14-
<script crossorigin="anonymous" src="https://esm.sh/tsx" type="module"></script>
156
<script crossorigin="anonymous" src="/test-harness.js"></script>
167
<script crossorigin="anonymous" src="/test-page-object.js"></script>
17-
<style type="text/css">
18-
.channel-message {
19-
align-items: center;
20-
display: flex;
21-
flex-direction: column;
22-
}
23-
24-
.channel-message__body {
25-
background-color: #666;
26-
border-radius: 100px;
27-
color: White;
28-
font-family: sans-serif;
29-
font-size: small;
30-
padding: .2em .5em;
31-
}
32-
</style>
8+
<link href="./channelMessage.css" rel="stylesheet" type="text/css" />
339
</head>
3410
<body>
3511
<main id="webchat"></main>

__tests__/html2/samples/middleware/channelMessage.tsx

Lines changed: 20 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,45 @@
1+
/* eslint-disable prefer-arrow-callback */
2+
3+
// Use code-splitting to speed up development time, just dummy import is sufficient.
4+
// esbuild will split out botframework-webchat* packages into their own chunks.
5+
await import('botframework-webchat');
6+
await import('botframework-webchat/middleware');
7+
8+
// #region Sample code
19
import ReactWebChat, { createStoreWithOptions } from 'botframework-webchat';
210
import { type WebChatActivity } from 'botframework-webchat-core';
311
import {
412
activityComponent,
513
createActivityPolyMiddleware,
6-
type ActivityPolyMiddlewareProps,
7-
type ActivityPolyMiddlewareRenderer
14+
type ActivityPolyMiddlewareProps
815
} from 'botframework-webchat/middleware';
9-
import React from 'react';
16+
import React, { memo } from 'react';
1017
import { render } from 'react-dom';
11-
12-
declare global {
13-
const host: any;
14-
const pageConditions: any;
15-
const pageElements: any;
16-
const run: (fn: () => Promise<void>) => Promise<void>;
17-
const testHelpers: any;
18-
}
19-
20-
// Use code-splitting to speed up development time, just dummy import is sufficient.
21-
// esbuild will split out botframework-webchat* packages into their own chunks.
22-
await import('botframework-webchat');
23-
await import('botframework-webchat/middleware');
18+
// #endregion Sample code
2419

2520
run(async () => {
2621
// #region Sample code
2722

28-
type MyActivityProps = ActivityPolyMiddlewareProps & {
29-
readonly activity: WebChatActivity & { type: 'message' };
30-
readonly render: ActivityPolyMiddlewareRenderer | undefined;
23+
type ChannelMessageProps = ActivityPolyMiddlewareProps & {
24+
readonly activity: WebChatActivity & {
25+
type: 'message';
26+
};
3127
};
3228

33-
function ChannelMessage({ activity }: MyActivityProps) {
29+
const ChannelMessage = memo<ChannelMessageProps>(function ChannelMessage({ activity }) {
3430
return (
3531
<div className="channel-message">
3632
<div className="channel-message__body">{activity.text}</div>
3733
</div>
3834
);
39-
}
35+
});
4036

4137
const polyMiddleware = [
4238
createActivityPolyMiddleware(next => request => {
4339
const { activity } = request;
4440

4541
if (activity.from.role === 'channel' && activity.type === 'message') {
46-
const render = next(request)?.render;
47-
48-
return activityComponent<MyActivityProps>(ChannelMessage, {
49-
activity,
50-
render
51-
});
42+
return activityComponent(ChannelMessage, { activity });
5243
}
5344

5445
return next(request);
@@ -68,13 +59,13 @@ run(async () => {
6859

6960
await pageConditions.uiConnected();
7061

71-
directLine.emulateIncomingActivity({
62+
await directLine.emulateIncomingActivity({
7263
from: { id: 'channel', role: 'channel' },
7364
text: 'An agent has joined the conversation',
7465
type: 'message'
7566
});
7667

77-
directLine.emulateIncomingActivity({
68+
await directLine.emulateIncomingActivity({
7869
from: { id: 'bot', role: 'bot' },
7970
text: 'Hello, World!',
8071
type: 'message'
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.message-border {
2+
outline-offset: -2px;
3+
outline-style: solid;
4+
outline-width: 2px;
5+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!doctype html>
2+
<html lang="en-US">
3+
<head>
4+
<title>Sample</title>
5+
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
6+
<script crossorigin="anonymous" src="/test-harness.js"></script>
7+
<script crossorigin="anonymous" src="/test-page-object.js"></script>
8+
<link href="./decorateMessage.css" rel="stylesheet" type="text/css" />
9+
</head>
10+
<body>
11+
<main id="webchat"></main>
12+
<script src="../dist/middleware/decorateMessage.js" type="module"></script>
13+
</body>
14+
</html>
8.73 KB
Loading
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/* eslint-disable prefer-arrow-callback */
2+
3+
// Use code-splitting to speed up development time, just dummy import is sufficient.
4+
// esbuild will split out botframework-webchat* packages into their own chunks.
5+
await import('botframework-webchat');
6+
await import('botframework-webchat/middleware');
7+
8+
// #region Sample code
9+
import ReactWebChat, { createStoreWithOptions, hooks } from 'botframework-webchat';
10+
import {
11+
activityComponent,
12+
createActivityPolyMiddleware,
13+
type ActivityPolyMiddlewareProps,
14+
type ActivityPolyMiddlewareRenderer
15+
} from 'botframework-webchat/middleware';
16+
import React, { Fragment, memo, useMemo } from 'react';
17+
import { render } from 'react-dom';
18+
19+
const { useStyleOptions } = hooks;
20+
// #endregion Sample code
21+
22+
run(async () => {
23+
// #region Sample code
24+
25+
type MessageBorderProps = ActivityPolyMiddlewareProps & {
26+
readonly render: ActivityPolyMiddlewareRenderer | undefined;
27+
};
28+
29+
const MessageBorder = memo<MessageBorderProps>(function MessageBorder({ render }) {
30+
const [{ accent }] = useStyleOptions();
31+
32+
const style = useMemo(() => ({ outlineColor: accent }), [accent]);
33+
34+
return (
35+
<div className="message-border" style={style}>
36+
<Fragment>{render?.({})}</Fragment>
37+
</div>
38+
);
39+
});
40+
41+
const polyMiddleware = [
42+
createActivityPolyMiddleware(next => request => {
43+
const { activity } = request;
44+
45+
if (activity.type === 'message') {
46+
const render = next(request)?.render;
47+
48+
return activityComponent<MessageBorderProps>(MessageBorder, {
49+
render
50+
});
51+
}
52+
53+
return next(request);
54+
})
55+
];
56+
57+
// #endregion Sample code
58+
59+
(window as any).WebChat = { createStoreWithOptions };
60+
61+
const { directLine, store } = testHelpers.createDirectLineEmulator();
62+
63+
render(
64+
<ReactWebChat directLine={directLine} polyMiddleware={polyMiddleware} store={store} />,
65+
document.getElementsByTagName('main')[0]!
66+
);
67+
68+
await pageConditions.uiConnected();
69+
70+
await directLine.emulateIncomingActivity({
71+
from: { id: 'bot', role: 'bot' },
72+
text: 'Hello, World!',
73+
type: 'message'
74+
});
75+
76+
await host.snapshot('local');
77+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
declare global {
2+
const host: any;
3+
const pageConditions: any;
4+
const pageElements: any;
5+
const run: (fn: () => Promise<void>) => Promise<void>;
6+
const testHelpers: any;
7+
}
8+
9+
export {};

docs/MIDDLEWARE.md

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,58 @@
11
# Middleware
22

3-
## Poly middleware
3+
## Cooking recipes
4+
5+
### Showing channel message as a badge
6+
7+
When the following activity is received, the middleware will display it in a badge.
8+
9+
#### Activity payload
10+
11+
```json
12+
{
13+
"from": { "id": "channel", "role": "channel" },
14+
"text": "An agent has joined the conversation",
15+
"type": "message"
16+
}
17+
```
18+
19+
#### Screenshot
20+
21+
![Web Chat showing channel message as a badge](./media/middleware/channel-message.png)
22+
23+
#### Code snippet
424

525
```tsx
626
import ReactWebChat, { createStoreWithOptions } from 'botframework-webchat';
727
import { type WebChatActivity } from 'botframework-webchat-core';
828
import {
929
activityComponent,
1030
createActivityPolyMiddleware,
11-
type ActivityPolyMiddlewareProps,
12-
type ActivityPolyMiddlewareRenderer
31+
type ActivityPolyMiddlewareProps
1332
} from 'botframework-webchat/middleware';
33+
import React, { memo } from 'react';
34+
import { render } from 'react-dom';
1435

15-
type MyActivityProps = ActivityPolyMiddlewareProps & {
16-
readonly activity: WebChatActivity & { type: 'message' };
17-
readonly render: ActivityPolyMiddlewareRenderer | undefined;
36+
type ChannelMessageProps = ActivityPolyMiddlewareProps & {
37+
readonly activity: WebChatActivity & {
38+
type: 'message';
39+
};
1840
};
1941

20-
function ChannelMessage({ activity }: MyActivityProps) {
42+
const ChannelMessage = memo<ChannelMessageProps>(function ChannelMessage({ activity }) {
2143
return (
2244
<div className="channel-message">
2345
<div className="channel-message__body">{activity.text}</div>
2446
</div>
2547
);
26-
}
48+
});
2749

2850
const polyMiddleware = [
2951
createActivityPolyMiddleware(next => request => {
3052
const { activity } = request;
3153

3254
if (activity.from.role === 'channel' && activity.type === 'message') {
33-
const render = next(request)?.render;
34-
35-
return activityComponent<MyActivityProps>(ChannelMessage, {
36-
activity,
37-
render
38-
});
55+
return activityComponent(ChannelMessage, { activity });
3956
}
4057

4158
return next(request);
23.8 KB
Loading

0 commit comments

Comments
 (0)