Custom Webchat


View on Github


Objective

This template allows you to create custom elements for Webchat. All custom components are stored in src/webchat.

src/webchat/index.js

export const webchat = {
// Webchat styling
theme: {},
// Webchat features
persistentMenu: [],
blockInputs: [],
enableEmojiPicker: {true|false},
enableUserInput: {true|false},
enableAttachments: {true|false},
enableAnimations: {true|false},
visibility: {true|false|'dynamic'|() => Boolean()},
coverComponent: React.Component,
// Webchat listeners
onInit: app => {},
onOpen: app => {},
onClose: app => {},
onMessage: app => {}
}

This Webchat template allows definitions that enable you to configure the following examples:

Theme

Features

  • persistentMenu: A persistent menu can be accessed from a small button in the input text box. It allows the user to access popular functionalities anywhere in the conversation.
  • blockInputs: Enables you to define which user input is forbidden. It is useful to prevent the user from entering a credit card number.
  • enableEmojiPicker: Enables the emoji picker. It can also be defined in theme.userInput.enableEmojiPicker. The false value is set by default.
  • enableAttachments: Authorizes user media attachments or not (true|false). It can also be defined in theme.userInput.attachments.enable. The false value is set by default.
  • enableUserInput: Enables the user typing zone (true|false). It can also be defined in theme.userInput.enable. true by default.
  • enableAnimations: Enables webchat animations (true|false). true by default. It can also be defined in theme.animations.enable.
  • visibility: To make webchat visible or not. If you want to use the settings defined in Hubtype Desk, you must set this value to 'dynamic'. You can pass a boolean true to make it visible or false to make it invisible, or you can define a function returning a boolean resolving the visibility.
  • coverComponent: A React.Component shown the first time a user interacts with the Webchat.

Listeners

Listeners are useful to react to some events when using the webchat.

  • onInit: This event is triggered once the webchat is loaded in the webpage.
  • onOpen: This event is triggered once the webchat has been opened.
  • onClose: This event is triggered once the webchat has been closed.
  • onMessage: This event is triggered once a message is sent by the enduser or the bot.

Webchat Styles

Here is a list of available styles. You need to modify them inside src/webchat/index.js's theme object:

General Style

Botonic allows you to modify the following styles:

  • mobileBreakpoint: Width (in pixels) for mobile responsive design. Set to 460 pixels by default.
  • style: The main characteristics of webchat such as size, background color, etc.
  • webviewStyle: The main characteristics of your webviews such as size, background color, etc. It can also be defined in webview.style.
  • webviewHeaderStyle: Styles for the webview header. It can also be defined in webview.header.style.
  • triggerButtonImage: Launcher icon to toggle the webchat. It can also be defined in triggerButton.image.
  • triggerButtonStyle: Launcher icon styles. It can also be defined in triggerButton.style.
  • brandColor: The main color palette of the webchat widget. It can also be defined in brand.color.
  • brandImage: It sets an image for both headerImage and botMessageImage. It can also be defined in brand.image.
  • headerImage: The image displayed in the header. Overwrites the one set in brandImage. You can set it to null to disable it. It can also be defined in header.image.
  • headerTitle: Displays webchat title. It can also be defined in header.title.
  • headerSubtitle: Displays webchat subtitle. It can also be defined in header.subtitle.
  • headerStyle: Header styles. It can also be defined in header.style.
  • buttonStyle: Button styles. It can also be defined in button.style.
  • buttonHoverBackground: Background color when hovering over a button. It can also be defined in button.hoverBackground.
  • buttonHoverTextColor: Text color when hovering over a button. It can also be defined in button.hoverTextColor.
  • replyStyle: Styles for replies. It can also be defined in reply.style.
  • alignReplies: Aligns replies at left, center or right. It can also be defined in replies.align.
  • wrapReplies: Whether replies be displayed on a single row nowrap (horizontal scrolling is displayed when required) or it is wrapped in different lines wrap. It can also be defined in replies.wrap.
  • botMessageImage: The image displayed next to the bot's chat bubble. Overwrites the one set in brandImage. You can set it to null to disable it. It can also be defined in message.bot.image.
  • botMessageStyle: Styles of bot message. It can also be defined in message.bot.style.
  • userMessageStyle: Styles of user message. It can also be defined in message.user.style.
  • introImage: An introductory image shown the first time the conversation is initialized. It can also be defined in intro.image.
  • introStyle: Styles of the introductory image. It can also be defined in intro.style.
  • textPlaceholder: Text placeholder in the input text. It can also be defined in userInput.box.placeholder.
  • enableUserInput: Set it to false if you want to disable text input. It can also be defined in userInput.enable. true by default.
  • userInputStyle: Styles for the bottom area of the user input. It can also be defined in userInput.style.
  • userInputBoxStyle: Styles for the text input box. It can also be defined in userInput.box.style.
  • emojiPicker: Set it to true to enable the emoji picker. It can also be defined in userInput.emojiPicker. false by default.
  • blockInputs: The inputs not allowed by the bot. It can also be defined in userInput.blockInputs.
  • persistentMenu: An array containing the options of your persistent menu. It can also be defined in userInput.persistentMenu.
  • enableAttachments: Whether to allow user media attachments or not (true|false). It can also be defined in userInput.attachments.enable. false by default.
  • scrollbar: Refer to the example below src/webchat/index.js for a complete description of scrollbar's customizable attributes.

Note: By specifying the fontFamily attribute in style property, you can overwrite your webchat entire typography.

Custom ComponentsYou can also create your own components from scratch.
  • customIntro: React component that is injected where introImage is displayed. It can also be defined inintro.custom.
  • customTrigger: React component that is injected in the Launcher icon. It can also be defined intriggerButton.custom.
  • customHeader: React Component that is injected in the Header of the widget. It can also be defined inheader.custom.
  • customReply: React Component that is injected in the Reply components of the widget. It can also be defined inreply.custom.
  • customButton: React Component that is injected in the Button components of the widget. It can also be defined inbutton.custom.
  • customSendButton: A fully customizable send button. It can also be defined inuserInput.sendButton.custom.
  • customMenuButton: A fully customizable button for trigger the persistent menu. It can also be defined inuserInput.menuButton.custom.

Note: Custom components redefine completely the components that are replacing, so the styles in conflicts with these components are overridden.

Custom Message StylesAdditionally, you can create the kind of messages you want:
  • customMessageTypes: Array of React components representing your messages. It can also be defined in message.customTypes.

Custom Message Styles

src/webchat/calendar-message.js

import React from 'react'
import { WebchatContext, customMessage } from '@botonic/react'
import Calendar from 'react-calendar'
class CalendarMessage extends React.Component {
static contextType = WebchatContext
render() {
return (
<>
<Calendar
onChange={date =>
this.context.sendText(`Booked for ${date.toLocaleDateString()}`)
}
/>
<p>{this.props.children}</p>
</>
)
}
}
export default customMessage({
name: 'calendar',
component: CalendarMessage,
})

Take a look at the infographics below to know exactly what are the parts that are customized when changing the theme attributes.

Example

Custom Component

In this example, we are putting CSS styles into a custom 'quickreply'. It's very important to put {props.children} inside the container, in this case the <div> tag:

src/webchat/custom-reply.js

import React from 'react'
export const CustomReply = props => (
<div
style={{
color: '#0000ff',
border: '2px solid #0000ff',
backgroundColor: 'white',
borderRadius: 30,
padding: 8,
cursor: 'pointer',
}}
>
{props.children}
</div>
)

This is an example of a custom Reply for our webchat. We can put all the fancy css styles we want. This is a static object, so it's not applied in any component, so we don't have to put {props.children} inside any container.

src/webchat/custom-intro.js

import React from 'react'
import { staticAsset } from '@botonic/react'
import Img from '../assets/intro-image.jpg'
export const CustomIntro = () => (
<img height={'50%'} width={'100%'} src={staticAsset(Img)} />
)

Note: When you define custom components that use static assets as shown in the example above, you must use the the method staticAsset from @botonic/react in order to get it working properly in production environment.

src/webchat/index.js

import launcherIcon from '../assets/launcher-logo.png'
import IntroImage from '../assets/intro-image.jpg'
import C3POLogo from '../assets/c3po-logo.png'
import R2D2Logo from '../assets/r2d2-logo.png'
import CalendarMessage from './calendar-message'
import { CustomTrigger } from './custom-trigger'
import { CustomHeader } from './custom-header'
import { CustomIntro } from './custom-intro'
import { CustomReply } from './custom-reply'
import { CustomButton } from './custom-button'
export const webchat = {
theme: {
mobileBreakpoint: 460,
style: {
position: 'fixed',
right: 20,
bottom: 20,
width: 400,
height: 500,
margin: 'auto',
backgroundColor: 'white',
borderRadius: 25,
boxShadow: '0 0 50px rgba(0,0,255,.30)',
overflow: 'hidden',
backgroundImage:
'linear-gradient(to top, #ffffff,#ffffff 11%,#9a9ae3 40%,#0000ff 85%,#0000ff 85%)',
fontFamily: '"Comic Sans MS", cursive, sans-serif',
},
webview: {
style: {
top: 0,
right: 0,
height: 500,
width: '100%',
},
header: {
style: {
background: '#6677FF',
},
},
},
brand: {
// color: 'blue',
image: R2D2Logo,
},
triggerButton: {
image: launcherIcon,
style: {
width: '200px',
},
// custom: CustomTrigger,
},
intro: {
// image: IntroImage,
// style: {
// padding: 20
// }
custom: CustomIntro,
},
header: {
title: 'My customized webchat',
subtitle: 'R2D2',
image: R2D2Logo,
style: {
height: 70,
},
// custom: CustomHeader
},
/*
* brandImage will set both headerImage and botMessageImage with its current logo
* you can overwrite these values by redefining them individually
*/
message: {
bot: {
image: C3POLogo, // set it to 'null' to hide this image
style: {
border: 'none',
color: 'black',
borderRadius: '20px',
background: '#e1fcfb',
},
},
user: {
style: {
// border:'none',
color: 'white',
background: '#2b81b6',
borderRadius: '10px',
},
},
customTypes: [CalendarMessage],
},
button: {
style: {
color: 'black',
background: 'white',
borderRadius: 20,
},
hoverBackground: '#b3fcfa',
hoverTextColor: 'black',
// custom: CustomButton,
},
replies: {
align: 'center',
wrap: 'nowrap',
},
reply: {
style: {
color: 'black',
background: '#e1fcfb',
borderColor: 'black',
},
// custom: CustomReply,
},
userInput: {
style: {
background: 'black',
},
box: {
style: {
border: '2px solid #2b81b6',
color: '#2b81b6',
background: '#F0F0F0',
width: '90%',
borderRadius: 20,
paddingLeft: 20,
marginRight: 10,
},
placeholder: 'Type something...',
},
// enable: false,
attachments: {
enable: true,
},
emojiPicker: {
enable: true
},
// These are the set of inputs which are not allowed.
blockInputs: [
{
match: [/ugly/i, /bastard/i],
message: 'We cannot tolerate these kind of words.',
},
],
persistentMenu: [
{ label: 'Help', payload: 'help' },
{
label: 'See docs',
url: 'https://docs.botonic.io',
},
{ closeLabel: 'Close' },
],
},
scrollbar: {
// enable: false,
autoHide: true,
thumb: {
opacity: 1,
// color: 'yellow',
bgcolor:
'linear-gradient(-131deg,rgba(231, 176, 43) 0%,rgb(193, 62, 81) 100%);',
border: '20px',
},
// track: {
// color: 'black',
// bgcolor:
// 'linear-gradient(-131deg,rgba(50, 40, 43) 0%,rgb(125, 62, 81) 100%);',
// border: '20px',
// },
},
},

All these changes can be tested using the botonic serve command (npm run start on Windows).

Example

Persistent Menu

By adding the following lines of code in the webchat's object, you can enable a persistent menu. You can redefine the label for closing the persistent menu with closeLabel. Default text is 'Cancel'.

persistentMenu: [
{ label: "Home", payload: "hi" },
{ label: "Human Agent", payload: "help" },
{ closeLabel: "Close Menu" },
],
Example

The persistent menu accepts the same properties as Botonic Buttons, e.g.: payload and url.

Menu Trigger Button Customization

You can first customize the persistent menu button with the theme.customMenuButton or theme.userInput.menuButton.custom properties.

Example

To do so:

  1. Add an image for the button in the Assets folder.
  2. Create a custom-menu-button.js file.
  3. Customize the button as in the example below.

src/webchat/custom-menu-button.js

import React from 'react'
import styled from 'styled-components'
export const IconContainer = styled.div`
cursor: pointer;
width: 56px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
`
export const Icon = styled.img`
width: 18px;
`
export const CustomMenuButton = () => (
<IconContainer>
<Icon src={'https://image.flaticon.com/icons/svg/2948/2948037.svg'} />
</IconContainer>
)

Menu Customization

1. Creating a Custom Menu Button

In order to have your entire persistent menu customized, first you will need to create a unitary button for each of the options. To do so, you can create a component like this one:

src/webchat/custom-persistent-menu-button.js

import React, { useContext } from 'react'
import styled from 'styled-components'
import { WebchatContext } from '@botonic/react'
import { Icon, IconContainer } from './custom-menu-button'
const StyledButton = styled.div`
cursor: pointer;
height: 50px;
width: 100%;
background: white;
display: flex;
justify-content: left;
align-items: center;
`
const Text = styled.p`
@import url('https://fonts.googleapis.com/css?family=Lato:300,400,700');
font-family: Lato;
font-size: 15px;
font-weight: 400;
color: black;
text-align: left;
`
export const CustomPersistentMenuButton = props => {
const { sendInput } = useContext(WebchatContext)
return (
<StyledButton
onClick={() => {
props.payload
? sendInput({
type: 'text',
data: String(props.label),
payload: props.payload,
})
: props.onClick()
}}
>
<IconContainer>
<Icon src={props.img} />
</IconContainer>
<Text
style={{
color: 'black',
}}
>
{props.label}
</Text>
</StyledButton>
)
}

2. Use Custom Menu Buttons in CustomPersistentMenu

Then to customize the persistent menu, you must use the theme.userInput.menu.custom or theme.customPersistentMenu property. To do so:

  1. Create a custom-persistent-menu.js file.
  2. Customize the menu as in the example below.
import React from 'react'
import styled from 'styled-components'
import { CustomPersistentMenuButton } from './custom-persistent-menu-button'
const ButtonsContainer = styled.div`
width: 100%;
bottom: 0;
position: absolute;
z-index: 2;
text-align: center;
`
export const CustomPersistentMenu = ({ onClick, options }) => {
return (
<ButtonsContainer>
<CustomPersistentMenuButton
label={options[0].label}
payload={options[0].payload}
img={'https://image.flaticon.com/icons/svg/2948/2948025.svg'}
/>
<CustomPersistentMenuButton
label={options[1].label}
payload={options[1].payload}
img={'https://image.flaticon.com/icons/svg/2948/2948059.svg'}
/>
<CustomPersistentMenuButton
label={options[2].closeLabel}
onClick={onClick}
img={'https://image.flaticon.com/icons/svg/271/271203.svg'}
/>
</ButtonsContainer>
)
}

Dark background

To darken the webchat in the background and focus on the persistent menu only. Can be defined under the properties theme.darkBackgroundMenu or theme.menu.darkBackground.

Example

This is how it should look like your final webchat file.

src/webchat/index.js

import { CustomPersistentMenu } from './custom-persistent-menu'
import { CustomMenuButton } from './custom-menu-button'
export const webchat = {
theme: {
userInput: {
persistentMenu: [
{ label: 'Home', payload: 'hi' },
{ label: 'Human Agent', payload: 'help' },
{ closeLabel: 'Close Menu' },
],
menu: {
darkBackground: true,
custom: CustomPersistentMenu,
},
menuButton: {
custom: CustomMenuButton,
},
},
},
}

Timestamp

To add and customize the time and date (color, position, locale) below the chat message, add this example content to the webchat index.js file.

export const webchat = {
theme: {
message: {
style: {
paddingRight: '5px',
},
timestamps: {
locale: 'en',
format: 'LT',
style: {
color: 'black',
fontSize: 10,
},
},
},
},
}

Note: to define the locale format, refer to https://momentjs.com/

Block Inputs

For security reasons or to avoid harmful or threatening messages, you can block these inputs:

blockInputs: [
{
match: [/ugly/, /bastard/],
message: 'We cannot tolerate these kind of words.',
},
]
Example

Once the specified inputs are matched:

  • The user input is not displayed in the message history.
  • The configured message is displayed.
  • The bot does not receive the blocked message.
The fallback content to display on prerendering