Skip to content

Commit 182d185

Browse files
authored
fix: made some changes and fixes (#27)
BREAKING CHANGE: Removed the deprecated withWebChat hook. BREAKING CHANGE: Renamed the "renderCustomResponse" prop to "renderUserDefinedResponse". BREAKING CHANGE: Removed the CustomResponsePortalsContainer component. - Added support for the "userDefinedResponse" event added in web chat 8.2.0. - Fixed an order bug that can occur if a user defined response is fired during the render process in some cases. This meant reworking the portals container interface which was a breaking change and we decided to just remove it from the public API. - Added support for usages on cp4d so the library will properly calculate the endpoints for the web chat entry script.
1 parent 37af658 commit 182d185

12 files changed

+247
-620
lines changed

README.md

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,13 @@ function onBeforeRender(instance, setInstance) {
100100
}
101101
```
102102

103-
### Custom responses
103+
### User defined responses
104104

105-
This component is also capable of managing custom responses. To do so, you need to pass a `renderCustomResponse` function as a prop. This function should return a React component that will render the content for the specific message for that response. You should make sure that the `WebChatContainer` component does not get unmounted in the middle of the life of your application because it will lose all custom responses that were previously received by web chat.
105+
This component is also capable of managing user defined responses. To do so, you need to pass a `renderUserDefinedResponse` function as a render prop. This function should return a React component that will render the content for the specific message for that response. You should make sure that the `WebChatContainer` component does not get unmounted in the middle of the life of your application because it will lose all user defined responses that were previously received by web chat.
106+
107+
You should treat the `renderUserDefinedResponse` prop like any typical React render prop; it is different from the `userDefinedResponse` event or a typical event handler. The event is fired only once when web chat initially receives the response from the server. The `renderUserDefinedResponse` prop however is called every time the App re-renders and it should return an up-to-date React component for the provided message item just like the render function would for a typical React component.
108+
109+
Note: in web chat 8.2.0, the custom response event was renamed from `customResponse` to `userDefinedResponse`. If this library detects you are using a prior version of web chat, it will use the `customResponse` event instead of `userDefinedResponse`.
106110

107111
```javascript
108112
import React from 'react';
@@ -111,14 +115,14 @@ import { WebChatContainer } from '@ibm-watson/assistant-web-chat-react';
111115
const webChatOptions = { /* Web chat options */ };
112116

113117
function App() {
114-
return <WebChatContainer renderCustomResponse={renderCustomResponse} config={webChatOptions} />;
118+
return <WebChatContainer renderUserDefinedResponse={renderUserDefinedResponse} config={webChatOptions} />;
115119
}
116120

117-
function renderCustomResponse(event) {
118-
// The event here will contain details for each custom response that needs to be rendered.
121+
function renderUserDefinedResponse(event) {
122+
// The event here will contain details for each user defined response that needs to be rendered.
119123
// The "user_defined_type" property is just an example; it is not required. You can use any other property or
120124
// condition you want here. This makes it easier to handle different response types if you have more than
121-
// one custom response type.
125+
// one user defined response type.
122126
if (event.data.message.user_defined && event.data.message.user_defined.user_defined_type === 'my-custom-type') {
123127
return <div>My custom content</div>
124128
}
@@ -164,7 +168,7 @@ function App() {
164168

165169
### WebChatContainer API
166170

167-
The `WebChatContainer` function is a functional component that will load and render an instance of web chat when it is mounted and destroy that instance when unmounted. If the web chat configuration options change, it will also destroy the previous web chat and create a new one with the new configuration. It can also manage React portals for custom responses.
171+
The `WebChatContainer` function is a functional component that will load and render an instance of web chat when it is mounted and destroy that instance when unmounted. If the web chat configuration options change, it will also destroy the previous web chat and create a new one with the new configuration. It can also manage React portals for user defined responses.
168172

169173
Note that this component will call the [web chat render](https://web-chat.global.assistant.watson.cloud.ibm.com/docs.html?to=api-instance-methods#render) method for you. You do not need to call it yourself. You can use the `onBeforeRender` or `onAfterRender` prop to execute operations before or after render is called.
170174

@@ -178,7 +182,7 @@ Note that this component will call the [web chat render](https://web-chat.global
178182
| instanceRef | No | MutableRefObject | A convenience prop that is a reference to the web chat instance. This component will set the value of this ref using the `current` property when the instance has been created. |
179183
| onBeforeRender | No | function | This is a callback function that is called after web chat has been loaded and before the `render` function is called. This function is passed a single argument which is the instance of web chat that was loaded. This function can be used to obtain a reference to the web chat instance if you want to make use of the instance methods that are available. |
180184
| onAfterRender | No | function | This is a callback function that is called after web chat has been loaded and after the `render` function is called. This function is passed a single argument which is the instance of web chat that was loaded. This function can be used to obtain a reference to the web chat instance if you want to make use of the instance methods that are available. |
181-
| renderCustomResponse | No | function | This function is a callback function that will be called by this container to render custom responses. If this prop is provided, then the container will listen for custom response events from web chat and will generate a React portal for each event. This function will be called once during component render for each custom response event. This function takes two arguments. The first is the [custom response event](https://web-chat.global.assistant.watson.cloud.ibm.com/docs.html?to=api-events#customresponse) that triggered the custom response. The second is a convenience argument that is the instance of web chat. The function should return a `ReactNode` that renders the custom content for the response. |
185+
| renderUserDefinedResponse | No | function | This function is a callback function that will be called by this container to render user defined responses. If this prop is provided, then the container will listen for user defined response events from web chat and will generate a React portal for each event. This function will be called once during component render for each user defined response event. This function takes two arguments. The first is the [user defined response event](https://web-chat.global.assistant.watson.cloud.ibm.com/docs.html?to=api-events#userDefinedResponse) that triggered the user defined response. The second is a convenience argument that is the instance of web chat. The function should return a `ReactNode` that renders the user defined content for the response. |
182186

183187
`WebChatCustomElement` inherits all of the props from `WebChatContainer`. It also has the following additional optional props.
184188

package-lock.json

Lines changed: 26 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
"@types/proper-url-join": "^2.1.1",
4242
"@types/react": "^16.14.17",
4343
"@types/react-dom": "^16.9.14",
44+
"@types/semver-compare": "^1.0.3",
4445
"@typescript-eslint/eslint-plugin": "^5.30.6",
4546
"@typescript-eslint/parser": "^5.30.6",
4647
"babel-core": "^6.26.3",
@@ -83,5 +84,8 @@
8384
],
8485
"publishConfig": {
8586
"access": "public"
87+
},
88+
"dependencies": {
89+
"semver-compare": "^1.0.0"
8690
}
8791
}

src/CustomResponsePortalsContainer.tsx

Lines changed: 0 additions & 97 deletions
This file was deleted.
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/**
2+
* (C) Copyright IBM Corp. 2022, 2024.
3+
*
4+
* Licensed under the MIT License (the "License"); you may not use this file except in compliance with
5+
* the License. You may obtain a copy of the License at
6+
*
7+
* https://opensource.org/licenses/MIT
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*
13+
*/
14+
15+
import React, { ReactNode } from 'react';
16+
import ReactDOM from 'react-dom';
17+
import { UserDefinedResponseEvent } from './types/UserDefinedResponseEvent';
18+
import { WebChatInstance } from './types/WebChatInstance';
19+
import { RenderUserDefinedResponse } from './WebChatContainer';
20+
21+
interface UserDefinedResponsePortalsContainer {
22+
/**
23+
* The instance of a web chat that this component will register listeners on.
24+
*/
25+
webChatInstance: WebChatInstance;
26+
27+
/**
28+
* The function that this component will use to request the actual React content to display for each user defined
29+
* response.
30+
*/
31+
renderResponse: RenderUserDefinedResponse;
32+
33+
/**
34+
* The list of events that were fired that contain all the responses to render.
35+
*/
36+
userDefinedResponseEvents: UserDefinedResponseEvent[];
37+
}
38+
39+
/**
40+
* This is a utility component that is used to manage all the user defined responses that are rendered by web chat.
41+
* When a user defined response message is received by web chat, it will fire a "userDefinedResponse" event that
42+
* provides an HTML element to which your application can attach user defined content. React portals are a mechanism
43+
* that allows you to render a component in your React application but attach that component to the HTML element
44+
* that was provided by web chat.
45+
*
46+
* This component will render a portal for each user defined response. The contents of that portal will be
47+
* determined by calling the provided "renderResponse" render prop.
48+
*/
49+
function UserDefinedResponsePortalsContainer({
50+
webChatInstance,
51+
renderResponse,
52+
userDefinedResponseEvents,
53+
}: UserDefinedResponsePortalsContainer) {
54+
// All we need to do to enable the React portals is to render each portal somewhere in your application (it
55+
// doesn't really matter where).
56+
return (
57+
<>
58+
{userDefinedResponseEvents.map(function mapEvent(event, index) {
59+
return (
60+
// eslint-disable-next-line react/no-array-index-key
61+
<UserDefinedResponseComponentPortal key={index} hostElement={event.data.element}>
62+
{renderResponse(event, webChatInstance)}
63+
</UserDefinedResponseComponentPortal>
64+
);
65+
})}
66+
</>
67+
);
68+
}
69+
70+
/**
71+
* This is the component that will attach a React portal to the given host element. The host element is the element
72+
* provided by web chat where your user defined response will be displayed in the DOM. This portal will attach any React
73+
* children passed to it under this component so you can render the response using your own React application. Those
74+
* children will be rendered under the given element where it lives in the DOM.
75+
*/
76+
function UserDefinedResponseComponentPortal({
77+
hostElement,
78+
children,
79+
}: {
80+
hostElement: HTMLElement;
81+
children: ReactNode;
82+
}) {
83+
return ReactDOM.createPortal(children, hostElement);
84+
}
85+
86+
const UserDefinedResponsePortalsContainerExport = React.memo(UserDefinedResponsePortalsContainer);
87+
export { UserDefinedResponsePortalsContainerExport as UserDefinedResponsePortalsContainer };

0 commit comments

Comments
 (0)