|
| 1 | +# Extending Web Chat with custom UI components |
| 2 | + |
| 3 | +This sample shows how to extend the default UI for Web Chat to include your own UI components. This allows developers to create features within the Web Chat UI without having to re-implement already existing UI elements. For example, this sample will show how to insert a custom button between the chat transcript and the chat message box. |
| 4 | + |
| 5 | +# Test out the hosted sample |
| 6 | + |
| 7 | +- [Try out MockBot](https://microsoft.github.io/BotFramework-WebChat/06.recomposing-ui/e.extending-ui) |
| 8 | + |
| 9 | +# Things to try out |
| 10 | + |
| 11 | +- Click the custom "Say Hello!" button. |
| 12 | + |
| 13 | +# Code |
| 14 | + |
| 15 | +This project is based on [`create-react-app`](https://github.com/facebook/create-react-app). |
| 16 | + |
| 17 | +The completed code contains multiple files. You can start by reading [`App.js`](https://github.com/microsoft/BotFramework-WebChat/tree/main/samples/06.recomposing-ui/e.extending-ui/src/App.js), which replaces the default Web Chat renderer with [`<CustomWebChat>`](https://github.com/microsoft/BotFramework-WebChat/tree/main/samples/06.recomposing-ui/e.extending-ui/src/CustomWebChat.js). The `<CustomWebChat>` component resembles the implementation of the [`<BasicWebChat>`](https://github.com/microsoft/BotFramework-WebChat/blob/main/packages/component/src/BasicWebChat.tsx) component that's used in the default Web Chat. |
| 18 | + |
| 19 | +> Jump to [completed code](#completed-code) to see the end-result `App.js`, `CustomWebChat.js`, and `HelloButton.js`. |
| 20 | +
|
| 21 | +## Overview |
| 22 | + |
| 23 | +### `App.js` |
| 24 | + |
| 25 | +[`App.js`](https://github.com/microsoft/BotFramework-WebChat/tree/main/samples/06.recomposing-ui/e.extending-ui/src/App.js) will set up the container for hosting Web Chat components. The container will be using Direct Line chat channel. `getDirectLineToken()` asynchronously retrieves and sets the Direct Line token (you'll need to implement this yourself). |
| 26 | + |
| 27 | +<!-- prettier-ignore-start --> |
| 28 | +```javascript |
| 29 | +const App = () => { |
| 30 | + const [directLine, setDirectLine] = React.useState(); |
| 31 | + |
| 32 | + if (!directLine) { |
| 33 | + getDirectLineToken().then(token => setDirectLine(createDirectLine({ token }))); |
| 34 | + } |
| 35 | + ... |
| 36 | +``` |
| 37 | +<!-- prettier-ignore-end --> |
| 38 | +
|
| 39 | +We then pass the `directLine` token to the `Composer` component when it has a value. The `Composer` component is provided by Web Chat to allow developers to compose their own UI. Any component that is a descendant of the `Composer` will have access to the context of the chat through higher-order components and React Hooks. |
| 40 | +
|
| 41 | +<!-- prettier-ignore-start --> |
| 42 | +```javascript |
| 43 | + ... |
| 44 | + return ( |
| 45 | + <React.Fragment> |
| 46 | + {!!directLine && ( |
| 47 | + <Components.Composer directLine={directLine}> |
| 48 | + <CustomWebChat /> |
| 49 | + </Components.Composer> |
| 50 | + )} |
| 51 | + </React.Fragment> |
| 52 | + ); |
| 53 | +} |
| 54 | +``` |
| 55 | +<!-- prettier-ignore-end --> |
| 56 | + |
| 57 | +### `CustomWebChat.js` |
| 58 | + |
| 59 | +The `App.js` implementation places a component named `CustomWebChat` as a child of the `Composer`. The purpose of this component is to resemble the [`BasicWebChat`](https://github.com/microsoft/BotFramework-WebChat/blob/main/packages/component/src/BasicWebChat.tsx) component provided by the Web Chat components library, but provide a way for us to add our own custom components into the UI. |
| 60 | + |
| 61 | +Lets start by first implementing the default `BasicWebChat` inside of `CustomWebChat`: |
| 62 | + |
| 63 | +<!-- prettier-ignore-start --> |
| 64 | +```javascript |
| 65 | +import { Components } from 'botframework-webchat-component'; |
| 66 | + |
| 67 | +const CustomWebChat = () => { |
| 68 | + return ( |
| 69 | + <Components.AccessKeySinkSurface> |
| 70 | + <Components.BasicToaster /> |
| 71 | + <Components.BasicTranscript /> |
| 72 | + <Components.BasicConnectivityStatus /> |
| 73 | + <Components.BasicSendBox /> |
| 74 | + </Components.AccessKeySinkSurface> |
| 75 | + ); |
| 76 | +}; |
| 77 | +``` |
| 78 | +<!-- prettier-ignore-end --> |
| 79 | + |
| 80 | +If you were to save, compile and run the code right now, you'll see the default Web Chat experience. This is because the `Basic*` components are the exact same components used by the `BasicWebChat` component. You can choose to modify these components however you like: remove, reorder, pass custom props (such as styling), etc. In this sample, however, we're going to demonstrate adding a completely new component to the UI. |
| 81 | + |
| 82 | +### `HelloButton.js` |
| 83 | + |
| 84 | +Our new component will be a "Say Hello!" button place right above the message box. When a user clicks this button, "Hello!" will be sent to the chat. This component will need to take advantage of the `useSendMessage` hook in order to be able to interact with the chat. |
| 85 | + |
| 86 | +<!-- prettier-ignore-start --> |
| 87 | +```javascript |
| 88 | +import { hooks } from 'botframework-webchat-component'; |
| 89 | + |
| 90 | +const { useSendMessage } = hooks; |
| 91 | + |
| 92 | +const HelloButton = () => { |
| 93 | + const sendMessage = useSendMessage(); |
| 94 | + |
| 95 | + return ( |
| 96 | + <button onClick={() => sendMessage("Hello!")}>Say Hello!</button> |
| 97 | + ); |
| 98 | +}; |
| 99 | +``` |
| 100 | +<!-- prettier-ignore-end --> |
| 101 | + |
| 102 | +We can then place our `HelloButton` component in the `CustomWebChat` component wherever we want. In our scenario, we want it right above the `BasicSendBox`. |
| 103 | + |
| 104 | +<!-- prettier-ignore-start --> |
| 105 | +```javascript |
| 106 | +import { Components } from 'botframework-webchat-component'; |
| 107 | +import HelloButton from './HelloButton'; |
| 108 | + |
| 109 | +const CustomWebChat = () => { |
| 110 | + return ( |
| 111 | + <Components.AccessKeySinkSurface> |
| 112 | + <Components.BasicToaster /> |
| 113 | + <Components.BasicTranscript /> |
| 114 | + <Components.BasicConnectivityStatus /> |
| 115 | + <HelloButton /> |
| 116 | + <Components.BasicSendBox /> |
| 117 | + </Components.AccessKeySinkSurface> |
| 118 | + ); |
| 119 | +}; |
| 120 | +``` |
| 121 | +<!-- prettier-ignore-end --> |
| 122 | + |
| 123 | +If you save, compile and run the code, you'll see a "Say Hello!" button right above the message box. When you click it, "Hello!" will be sent to the chat! |
| 124 | + |
| 125 | +## Completed Code |
| 126 | + |
| 127 | +`App.js` (simplified): |
| 128 | + |
| 129 | +<!-- prettier-ignore-start --> |
| 130 | +```javascript |
| 131 | +import { createDirectLine } from 'botframework-webchat'; |
| 132 | +import { Components } from 'botframework-webchat-component'; |
| 133 | +import React from 'react'; |
| 134 | +import CustomWebChat from './CustomWebChat'; |
| 135 | + |
| 136 | +// In this demo, we are using Direct Line token from MockBot. |
| 137 | +// To talk to your bot, you should use the token exchanged using your Direct Line secret. |
| 138 | +// You should never put the Direct Line secret in the browser or client app. |
| 139 | +// https://docs.microsoft.com/en-us/azure/bot-service/rest-api/bot-framework-rest-direct-line-3-0-authentication |
| 140 | + |
| 141 | +async function getDirectLineToken() { |
| 142 | + const res = await fetch('https://webchat-mockbot.azurewebsites.net/directline/token', { method: 'POST' }); |
| 143 | + const { token } = await res.json(); |
| 144 | + |
| 145 | + return token; |
| 146 | +} |
| 147 | + |
| 148 | +const App = () => { |
| 149 | + const [directLine, setDirectLine] = React.useState(); |
| 150 | + |
| 151 | + if (!directLine) { |
| 152 | + getDirectLineToken().then(token => setDirectLine(createDirectLine({ token }))); |
| 153 | + } |
| 154 | + |
| 155 | + return ( |
| 156 | + <React.Fragment> |
| 157 | + {!!directLine && ( |
| 158 | + <Components.Composer directLine={directLine}> |
| 159 | + <CustomWebChat /> |
| 160 | + </Components.Composer> |
| 161 | + )} |
| 162 | + </React.Fragment> |
| 163 | + ); |
| 164 | +}; |
| 165 | + |
| 166 | +export default App; |
| 167 | +``` |
| 168 | +<!-- prettier-ignore-end --> |
| 169 | + |
| 170 | +`CustomWebChat.js`: |
| 171 | + |
| 172 | +<!-- prettier-ignore-start --> |
| 173 | +```javascript |
| 174 | +import React from 'react'; |
| 175 | +import { Components } from 'botframework-webchat-component'; |
| 176 | +import HelloButton from './HelloButton'; |
| 177 | + |
| 178 | +const CustomWebChat = () => { |
| 179 | + return ( |
| 180 | + <Components.AccessKeySinkSurface> |
| 181 | + <Components.BasicToaster /> |
| 182 | + <Components.BasicTranscript /> |
| 183 | + <Components.BasicConnectivityStatus /> |
| 184 | + <HelloButton /> |
| 185 | + <Components.BasicSendBox /> |
| 186 | + </Components.AccessKeySinkSurface> |
| 187 | + ); |
| 188 | +}; |
| 189 | + |
| 190 | +export default CustomWebChat; |
| 191 | +``` |
| 192 | +<!-- prettier-ignore-end --> |
| 193 | + |
| 194 | +`HelloButton.js`: |
| 195 | + |
| 196 | +<!-- prettier-ignore-start --> |
| 197 | +```javascript |
| 198 | +import React from 'react'; |
| 199 | +import { hooks } from 'botframework-webchat-component'; |
| 200 | + |
| 201 | +const { useSendMessage } = hooks; |
| 202 | + |
| 203 | +const HelloButton = () => { |
| 204 | + const sendMessage = useSendMessage(); |
| 205 | + |
| 206 | + return ( |
| 207 | + <button onClick={() => sendMessage("Hello!")}>Say Hello!</button> |
| 208 | + ); |
| 209 | +}; |
| 210 | + |
| 211 | +export default HelloButton; |
| 212 | +``` |
| 213 | +<!-- prettier-ignore-end --> |
| 214 | + |
| 215 | +# Further reading |
| 216 | + |
| 217 | +## Full list of Web Chat hosted samples |
| 218 | + |
| 219 | +View the list of [available Web Chat samples](https://github.com/microsoft/BotFramework-WebChat/tree/main/samples) |
0 commit comments