feat(lib): ✨ Add auto open delay for bubble embed
This commit is contained in:
5
.github/workflows/publish-lib-to-npm.yml
vendored
5
.github/workflows/publish-lib-to-npm.yml
vendored
@@ -3,7 +3,7 @@ name: Publish package to NPM
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- 'v*.*.*'
|
- 'js-lib-v*.*.*'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish:
|
publish:
|
||||||
@@ -15,10 +15,11 @@ jobs:
|
|||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-node@v1
|
- uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: 12
|
node-version: 14
|
||||||
- run: yarn
|
- run: yarn
|
||||||
- run: yarn test
|
- run: yarn test
|
||||||
- run: yarn build
|
- run: yarn build
|
||||||
- uses: JS-DevTools/npm-publish@v1
|
- uses: JS-DevTools/npm-publish@v1
|
||||||
with:
|
with:
|
||||||
|
package: './packages/typebot-js/package.json'
|
||||||
token: ${{ secrets.NPM_TOKEN }}
|
token: ${{ secrets.NPM_TOKEN }}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "typebot-js",
|
"name": "typebot-js",
|
||||||
"version": "2.1.3",
|
"version": "2.1.4",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"unpkg": "dist/index.umd.min.js",
|
"unpkg": "dist/index.umd.min.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -3,85 +3,91 @@ import {
|
|||||||
BubbleParams,
|
BubbleParams,
|
||||||
localStorageKeys,
|
localStorageKeys,
|
||||||
ProactiveMessageParams,
|
ProactiveMessageParams,
|
||||||
} from "../../types";
|
} from '../../types'
|
||||||
import { createButton } from "./button";
|
import { createButton } from './button'
|
||||||
import {
|
import {
|
||||||
closeIframe,
|
closeIframe,
|
||||||
createIframeContainer,
|
createIframeContainer,
|
||||||
loadTypebotIfFirstOpen,
|
loadTypebotIfFirstOpen,
|
||||||
openIframe,
|
openIframe,
|
||||||
} from "./iframe";
|
} from './iframe'
|
||||||
import {
|
import {
|
||||||
createProactiveMessage,
|
createProactiveMessage,
|
||||||
openProactiveMessage,
|
openProactiveMessage,
|
||||||
} from "./proactiveMessage";
|
} from './proactiveMessage'
|
||||||
import "./style.css";
|
import './style.css'
|
||||||
|
|
||||||
export const initBubble = (params: BubbleParams): BubbleActions => {
|
export const initBubble = (params: BubbleParams): BubbleActions => {
|
||||||
if (document.readyState !== "complete") {
|
if (document.readyState !== 'complete') {
|
||||||
window.addEventListener("load", () => initBubble(params));
|
window.addEventListener('load', () => initBubble(params))
|
||||||
return { close: () => {}, open: () => {} };
|
return { close: () => {}, open: () => {} }
|
||||||
}
|
}
|
||||||
const existingBubble = document.getElementById("typebot-bubble") as
|
const existingBubble = document.getElementById('typebot-bubble') as
|
||||||
| HTMLDivElement
|
| HTMLDivElement
|
||||||
| undefined;
|
| undefined
|
||||||
if (existingBubble) existingBubble.remove();
|
if (existingBubble) existingBubble.remove()
|
||||||
const { bubbleElement, proactiveMessageElement, iframeElement } =
|
const { bubbleElement, proactiveMessageElement, iframeElement } =
|
||||||
createBubble(params);
|
createBubble(params)
|
||||||
|
if (
|
||||||
|
(params.autoOpenDelay || params.autoOpenDelay === 0) &&
|
||||||
|
!hasBeenClosed()
|
||||||
|
) {
|
||||||
|
setRememberCloseInStorage()
|
||||||
|
setTimeout(
|
||||||
|
() => openIframe(bubbleElement, iframeElement),
|
||||||
|
params.autoOpenDelay
|
||||||
|
)
|
||||||
|
}
|
||||||
!document.body
|
!document.body
|
||||||
? (window.onload = () => document.body.appendChild(bubbleElement))
|
? (window.onload = () => document.body.appendChild(bubbleElement))
|
||||||
: document.body.appendChild(bubbleElement);
|
: document.body.appendChild(bubbleElement)
|
||||||
return getBubbleActions(
|
return getBubbleActions(bubbleElement, iframeElement, proactiveMessageElement)
|
||||||
bubbleElement,
|
}
|
||||||
iframeElement,
|
|
||||||
proactiveMessageElement
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const createBubble = (
|
const createBubble = (
|
||||||
params: BubbleParams
|
params: BubbleParams
|
||||||
): {
|
): {
|
||||||
bubbleElement: HTMLDivElement;
|
bubbleElement: HTMLDivElement
|
||||||
iframeElement: HTMLIFrameElement;
|
iframeElement: HTMLIFrameElement
|
||||||
proactiveMessageElement?: HTMLDivElement;
|
proactiveMessageElement?: HTMLDivElement
|
||||||
} => {
|
} => {
|
||||||
const bubbleElement = document.createElement("div");
|
const bubbleElement = document.createElement('div')
|
||||||
bubbleElement.id = "typebot-bubble";
|
bubbleElement.id = 'typebot-bubble'
|
||||||
const buttonElement = createButton(params.button);
|
const buttonElement = createButton(params.button)
|
||||||
bubbleElement.appendChild(buttonElement);
|
bubbleElement.appendChild(buttonElement)
|
||||||
const proactiveMessageElement =
|
const proactiveMessageElement =
|
||||||
params.proactiveMessage && !hasBeenClosed()
|
params.proactiveMessage && !hasBeenClosed()
|
||||||
? addProactiveMessage(params.proactiveMessage, bubbleElement)
|
? addProactiveMessage(params.proactiveMessage, bubbleElement)
|
||||||
: undefined;
|
: undefined
|
||||||
const iframeElement = createIframeContainer(params);
|
const iframeElement = createIframeContainer(params)
|
||||||
buttonElement.addEventListener("click", () =>
|
buttonElement.addEventListener('click', () =>
|
||||||
onBubbleButtonClick(bubbleElement, iframeElement)
|
onBubbleButtonClick(bubbleElement, iframeElement)
|
||||||
);
|
)
|
||||||
if (proactiveMessageElement)
|
if (proactiveMessageElement)
|
||||||
proactiveMessageElement.addEventListener("click", () =>
|
proactiveMessageElement.addEventListener('click', () =>
|
||||||
onProactiveMessageClick(bubbleElement, iframeElement)
|
onProactiveMessageClick(bubbleElement, iframeElement)
|
||||||
);
|
)
|
||||||
bubbleElement.appendChild(iframeElement);
|
bubbleElement.appendChild(iframeElement)
|
||||||
return { bubbleElement, proactiveMessageElement, iframeElement };
|
return { bubbleElement, proactiveMessageElement, iframeElement }
|
||||||
};
|
}
|
||||||
|
|
||||||
const onBubbleButtonClick = (
|
const onBubbleButtonClick = (
|
||||||
bubble: HTMLDivElement,
|
bubble: HTMLDivElement,
|
||||||
iframe: HTMLIFrameElement
|
iframe: HTMLIFrameElement
|
||||||
): void => {
|
): void => {
|
||||||
loadTypebotIfFirstOpen(iframe);
|
loadTypebotIfFirstOpen(iframe)
|
||||||
bubble.classList.toggle("iframe-opened");
|
bubble.classList.toggle('iframe-opened')
|
||||||
bubble.classList.remove("message-opened");
|
bubble.classList.remove('message-opened')
|
||||||
};
|
}
|
||||||
|
|
||||||
const onProactiveMessageClick = (
|
const onProactiveMessageClick = (
|
||||||
bubble: HTMLDivElement,
|
bubble: HTMLDivElement,
|
||||||
iframe: HTMLIFrameElement
|
iframe: HTMLIFrameElement
|
||||||
): void => {
|
): void => {
|
||||||
loadTypebotIfFirstOpen(iframe);
|
loadTypebotIfFirstOpen(iframe)
|
||||||
bubble.classList.add("iframe-opened");
|
bubble.classList.add('iframe-opened')
|
||||||
bubble.classList.remove("message-opened");
|
bubble.classList.remove('message-opened')
|
||||||
};
|
}
|
||||||
|
|
||||||
export const getBubbleActions = (
|
export const getBubbleActions = (
|
||||||
bubbleElement?: HTMLDivElement,
|
bubbleElement?: HTMLDivElement,
|
||||||
@@ -90,29 +96,29 @@ export const getBubbleActions = (
|
|||||||
): BubbleActions => {
|
): BubbleActions => {
|
||||||
const existingBubbleElement =
|
const existingBubbleElement =
|
||||||
bubbleElement ??
|
bubbleElement ??
|
||||||
(document.querySelector("#typebot-bubble") as HTMLDivElement);
|
(document.querySelector('#typebot-bubble') as HTMLDivElement)
|
||||||
const existingIframeElement =
|
const existingIframeElement =
|
||||||
iframeElement ??
|
iframeElement ??
|
||||||
(existingBubbleElement.querySelector(
|
(existingBubbleElement.querySelector(
|
||||||
".typebot-iframe"
|
'.typebot-iframe'
|
||||||
) as HTMLIFrameElement);
|
) as HTMLIFrameElement)
|
||||||
const existingProactiveMessage =
|
const existingProactiveMessage =
|
||||||
proactiveMessageElement ??
|
proactiveMessageElement ??
|
||||||
document.querySelector("#typebot-bubble .proactive-message");
|
document.querySelector('#typebot-bubble .proactive-message')
|
||||||
return {
|
return {
|
||||||
openProactiveMessage: existingProactiveMessage
|
openProactiveMessage: existingProactiveMessage
|
||||||
? () => {
|
? () => {
|
||||||
openProactiveMessage(existingBubbleElement);
|
openProactiveMessage(existingBubbleElement)
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
open: () => {
|
open: () => {
|
||||||
openIframe(existingBubbleElement, existingIframeElement);
|
openIframe(existingBubbleElement, existingIframeElement)
|
||||||
},
|
},
|
||||||
close: () => {
|
close: () => {
|
||||||
closeIframe(existingBubbleElement);
|
closeIframe(existingBubbleElement)
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const addProactiveMessage = (
|
const addProactiveMessage = (
|
||||||
proactiveMessage: ProactiveMessageParams,
|
proactiveMessage: ProactiveMessageParams,
|
||||||
@@ -121,14 +127,17 @@ const addProactiveMessage = (
|
|||||||
const proactiveMessageElement = createProactiveMessage(
|
const proactiveMessageElement = createProactiveMessage(
|
||||||
proactiveMessage,
|
proactiveMessage,
|
||||||
bubbleElement
|
bubbleElement
|
||||||
);
|
)
|
||||||
bubbleElement.appendChild(proactiveMessageElement);
|
bubbleElement.appendChild(proactiveMessageElement)
|
||||||
return proactiveMessageElement;
|
return proactiveMessageElement
|
||||||
};
|
}
|
||||||
|
|
||||||
const hasBeenClosed = () => {
|
const hasBeenClosed = () => {
|
||||||
const closeDecisionFromStorage = localStorage.getItem(
|
const closeDecisionFromStorage = localStorage.getItem(
|
||||||
localStorageKeys.rememberClose
|
localStorageKeys.rememberClose
|
||||||
);
|
)
|
||||||
return closeDecisionFromStorage ? true : false;
|
return closeDecisionFromStorage ? true : false
|
||||||
};
|
}
|
||||||
|
|
||||||
|
export const setRememberCloseInStorage = () =>
|
||||||
|
localStorage.setItem(localStorageKeys.rememberClose, 'true')
|
||||||
|
|||||||
@@ -1,66 +1,64 @@
|
|||||||
import { localStorageKeys, ProactiveMessageParams } from "../../types";
|
import { setRememberCloseInStorage } from '../chat/index'
|
||||||
import { closeSvgPath } from "./button";
|
import { ProactiveMessageParams } from '../../types'
|
||||||
|
import { closeSvgPath } from './button'
|
||||||
|
|
||||||
const createProactiveMessage = (
|
const createProactiveMessage = (
|
||||||
params: ProactiveMessageParams,
|
params: ProactiveMessageParams,
|
||||||
bubble: HTMLDivElement
|
bubble: HTMLDivElement
|
||||||
): HTMLDivElement => {
|
): HTMLDivElement => {
|
||||||
const container = document.createElement("div");
|
const container = document.createElement('div')
|
||||||
container.classList.add("proactive-message");
|
container.classList.add('proactive-message')
|
||||||
if (params.delay !== undefined) setOpenTimeout(bubble, params);
|
if (params.delay !== undefined) setOpenTimeout(bubble, params)
|
||||||
if (params.avatarUrl) container.appendChild(createAvatar(params.avatarUrl));
|
if (params.avatarUrl) container.appendChild(createAvatar(params.avatarUrl))
|
||||||
if (params.rememberClose) setRememberCloseInStorage();
|
container.appendChild(createTextElement(params.textContent))
|
||||||
container.appendChild(createTextElement(params.textContent));
|
container.appendChild(createCloseButton(bubble))
|
||||||
container.appendChild(createCloseButton(bubble));
|
return container
|
||||||
return container;
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const setOpenTimeout = (
|
const setOpenTimeout = (
|
||||||
bubble: HTMLDivElement,
|
bubble: HTMLDivElement,
|
||||||
params: ProactiveMessageParams
|
params: ProactiveMessageParams
|
||||||
) => {
|
) => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
openProactiveMessage(bubble);
|
openProactiveMessage(bubble)
|
||||||
}, params.delay);
|
}, params.delay)
|
||||||
};
|
}
|
||||||
|
|
||||||
const createAvatar = (avatarUrl: string): HTMLImageElement => {
|
const createAvatar = (avatarUrl: string): HTMLImageElement => {
|
||||||
const element = document.createElement("img");
|
const element = document.createElement('img')
|
||||||
element.src = avatarUrl;
|
element.src = avatarUrl
|
||||||
return element;
|
return element
|
||||||
};
|
}
|
||||||
|
|
||||||
const createTextElement = (text: string): HTMLParagraphElement => {
|
const createTextElement = (text: string): HTMLParagraphElement => {
|
||||||
const element = document.createElement("p");
|
const element = document.createElement('p')
|
||||||
element.innerHTML = text;
|
element.innerHTML = text
|
||||||
return element;
|
return element
|
||||||
};
|
}
|
||||||
|
|
||||||
const createCloseButton = (bubble: HTMLDivElement): HTMLButtonElement => {
|
const createCloseButton = (bubble: HTMLDivElement): HTMLButtonElement => {
|
||||||
const button = document.createElement("button");
|
const button = document.createElement('button')
|
||||||
button.classList.add("close-button");
|
button.classList.add('close-button')
|
||||||
button.innerHTML = `<svg viewBox="0 0 512 512">${closeSvgPath}</svg>`;
|
button.innerHTML = `<svg viewBox="0 0 512 512">${closeSvgPath}</svg>`
|
||||||
button.addEventListener("click", (e) => onCloseButtonClick(e, bubble));
|
button.addEventListener('click', (e) => onCloseButtonClick(e, bubble))
|
||||||
return button;
|
return button
|
||||||
};
|
}
|
||||||
|
|
||||||
const openProactiveMessage = (bubble: HTMLDivElement): void => {
|
const openProactiveMessage = (bubble: HTMLDivElement): void => {
|
||||||
bubble.classList.add("message-opened");
|
bubble.classList.add('message-opened')
|
||||||
};
|
}
|
||||||
|
|
||||||
const onCloseButtonClick = (
|
const onCloseButtonClick = (
|
||||||
e: Event,
|
e: Event,
|
||||||
proactiveMessageElement: HTMLDivElement
|
proactiveMessageElement: HTMLDivElement
|
||||||
) => {
|
) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation()
|
||||||
closeProactiveMessage(proactiveMessageElement);
|
closeProactiveMessage(proactiveMessageElement)
|
||||||
};
|
}
|
||||||
|
|
||||||
const closeProactiveMessage = (bubble: HTMLDivElement): void => {
|
const closeProactiveMessage = (bubble: HTMLDivElement): void => {
|
||||||
bubble.classList.remove("message-opened");
|
setRememberCloseInStorage()
|
||||||
};
|
bubble.classList.remove('message-opened')
|
||||||
|
}
|
||||||
|
|
||||||
const setRememberCloseInStorage = () =>
|
export { createProactiveMessage, openProactiveMessage, closeProactiveMessage }
|
||||||
localStorage.setItem(localStorageKeys.rememberClose, "true");
|
|
||||||
|
|
||||||
export { createProactiveMessage, openProactiveMessage, closeProactiveMessage };
|
|
||||||
|
|||||||
@@ -1,58 +1,58 @@
|
|||||||
import { DataFromTypebot, IframeCallbacks, IframeParams } from "../types";
|
import { DataFromTypebot, IframeCallbacks, IframeParams } from '../types'
|
||||||
import "./style.css";
|
import './style.css'
|
||||||
|
|
||||||
export const createIframe = ({
|
export const createIframe = ({
|
||||||
backgroundColor,
|
backgroundColor,
|
||||||
viewerHost = "https://typebot-viewer.vercel.app",
|
viewerHost = 'https://typebot-viewer.vercel.app',
|
||||||
|
isV1,
|
||||||
...iframeParams
|
...iframeParams
|
||||||
}: IframeParams): HTMLIFrameElement => {
|
}: IframeParams): HTMLIFrameElement => {
|
||||||
const { publishId, loadWhenVisible, hiddenVariables } = iframeParams;
|
const { publishId, loadWhenVisible, hiddenVariables } = iframeParams
|
||||||
const iframeUrl = `${viewerHost}/${publishId}${parseQueryParams(
|
const host = isV1 ? `https://bot.typebot.io` : viewerHost
|
||||||
hiddenVariables
|
const iframeUrl = `${host}/${publishId}${parseQueryParams(hiddenVariables)}`
|
||||||
)}`;
|
const iframe = document.createElement('iframe')
|
||||||
const iframe = document.createElement("iframe");
|
iframe.setAttribute(loadWhenVisible ? 'data-src' : 'src', iframeUrl)
|
||||||
iframe.setAttribute(loadWhenVisible ? "data-src" : "src", iframeUrl);
|
iframe.setAttribute('data-id', iframeParams.publishId)
|
||||||
iframe.setAttribute("data-id", iframeParams.publishId);
|
const randomThreeLettersId = Math.random().toString(36).substring(7)
|
||||||
const randomThreeLettersId = Math.random().toString(36).substring(7);
|
const uniqueId = `${publishId}-${randomThreeLettersId}`
|
||||||
const uniqueId = `${publishId}-${randomThreeLettersId}`;
|
iframe.setAttribute('id', uniqueId)
|
||||||
iframe.setAttribute("id", uniqueId);
|
if (backgroundColor) iframe.style.backgroundColor = backgroundColor
|
||||||
if (backgroundColor) iframe.style.backgroundColor = backgroundColor;
|
iframe.classList.add('typebot-iframe')
|
||||||
iframe.classList.add("typebot-iframe");
|
const { onNewVariableValue, onVideoPlayed } = iframeParams
|
||||||
const { onNewVariableValue, onVideoPlayed } = iframeParams;
|
listenForTypebotMessages({ onNewVariableValue, onVideoPlayed })
|
||||||
listenForTypebotMessages({ onNewVariableValue, onVideoPlayed });
|
return iframe
|
||||||
return iframe;
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const parseQueryParams = (starterVariables?: {
|
const parseQueryParams = (starterVariables?: {
|
||||||
[key: string]: string | undefined;
|
[key: string]: string | undefined
|
||||||
}): string => {
|
}): string => {
|
||||||
return parseHostnameQueryParam() + parseStarterVariables(starterVariables);
|
return parseHostnameQueryParam() + parseStarterVariables(starterVariables)
|
||||||
};
|
}
|
||||||
|
|
||||||
const parseHostnameQueryParam = () => {
|
const parseHostnameQueryParam = () => {
|
||||||
return `?hn=${window.location.hostname}`;
|
return `?hn=${window.location.hostname}`
|
||||||
};
|
}
|
||||||
|
|
||||||
const parseStarterVariables = (starterVariables?: {
|
const parseStarterVariables = (starterVariables?: {
|
||||||
[key: string]: string | undefined;
|
[key: string]: string | undefined
|
||||||
}) =>
|
}) =>
|
||||||
starterVariables
|
starterVariables
|
||||||
? `&${Object.keys(starterVariables)
|
? `&${Object.keys(starterVariables)
|
||||||
.filter((key) => starterVariables[key])
|
.filter((key) => starterVariables[key])
|
||||||
.map((key) => `${key}=${starterVariables[key]}`)
|
.map((key) => `${key}=${starterVariables[key]}`)
|
||||||
.join("&")}`
|
.join('&')}`
|
||||||
: "";
|
: ''
|
||||||
|
|
||||||
export const listenForTypebotMessages = (callbacks: IframeCallbacks) => {
|
export const listenForTypebotMessages = (callbacks: IframeCallbacks) => {
|
||||||
window.addEventListener("message", (event) => {
|
window.addEventListener('message', (event) => {
|
||||||
const data = event.data as { from?: "typebot" } & DataFromTypebot;
|
const data = event.data as { from?: 'typebot' } & DataFromTypebot
|
||||||
if (data.from === "typebot") processMessage(event.data, callbacks);
|
if (data.from === 'typebot') processMessage(event.data, callbacks)
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
const processMessage = (data: DataFromTypebot, callbacks: IframeCallbacks) => {
|
const processMessage = (data: DataFromTypebot, callbacks: IframeCallbacks) => {
|
||||||
if (data.redirectUrl) window.open(data.redirectUrl);
|
if (data.redirectUrl) window.open(data.redirectUrl)
|
||||||
if (data.newVariableValue && callbacks.onNewVariableValue)
|
if (data.newVariableValue && callbacks.onNewVariableValue)
|
||||||
callbacks.onNewVariableValue(data.newVariableValue);
|
callbacks.onNewVariableValue(data.newVariableValue)
|
||||||
if (data.videoPlayed && callbacks.onVideoPlayed) callbacks.onVideoPlayed();
|
if (data.videoPlayed && callbacks.onVideoPlayed) callbacks.onVideoPlayed()
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,60 +1,62 @@
|
|||||||
export type IframeParams = {
|
export type IframeParams = {
|
||||||
publishId: string;
|
publishId: string
|
||||||
viewerHost?: string;
|
isV1?: boolean
|
||||||
backgroundColor?: string;
|
viewerHost?: string
|
||||||
hiddenVariables?: { [key: string]: string | undefined };
|
backgroundColor?: string
|
||||||
customDomain?: string;
|
hiddenVariables?: { [key: string]: string | undefined }
|
||||||
loadWhenVisible?: boolean;
|
customDomain?: string
|
||||||
} & IframeCallbacks;
|
loadWhenVisible?: boolean
|
||||||
|
} & IframeCallbacks
|
||||||
|
|
||||||
export type IframeCallbacks = {
|
export type IframeCallbacks = {
|
||||||
onNewVariableValue?: (v: Variable) => void;
|
onNewVariableValue?: (v: Variable) => void
|
||||||
onVideoPlayed?: () => void;
|
onVideoPlayed?: () => void
|
||||||
};
|
}
|
||||||
|
|
||||||
export type PopupParams = {
|
export type PopupParams = {
|
||||||
delay?: number;
|
delay?: number
|
||||||
} & IframeParams;
|
} & IframeParams
|
||||||
|
|
||||||
export type PopupActions = {
|
export type PopupActions = {
|
||||||
open: () => void;
|
open: () => void
|
||||||
close: () => void;
|
close: () => void
|
||||||
};
|
}
|
||||||
|
|
||||||
export type BubbleParams = {
|
export type BubbleParams = {
|
||||||
button?: ButtonParams;
|
button?: ButtonParams
|
||||||
proactiveMessage?: ProactiveMessageParams;
|
proactiveMessage?: ProactiveMessageParams
|
||||||
} & IframeParams;
|
autoOpenDelay?: number
|
||||||
|
} & IframeParams
|
||||||
|
|
||||||
export type ButtonParams = {
|
export type ButtonParams = {
|
||||||
color?: string;
|
color?: string
|
||||||
iconUrl?: string;
|
iconUrl?: string
|
||||||
};
|
}
|
||||||
|
|
||||||
export type ProactiveMessageParams = {
|
export type ProactiveMessageParams = {
|
||||||
avatarUrl?: string;
|
avatarUrl?: string
|
||||||
textContent: string;
|
textContent: string
|
||||||
delay?: number;
|
delay?: number
|
||||||
rememberClose?: boolean;
|
rememberClose?: boolean
|
||||||
};
|
}
|
||||||
|
|
||||||
export type BubbleActions = {
|
export type BubbleActions = {
|
||||||
open: () => void;
|
open: () => void
|
||||||
close: () => void;
|
close: () => void
|
||||||
openProactiveMessage?: () => void;
|
openProactiveMessage?: () => void
|
||||||
};
|
}
|
||||||
|
|
||||||
export type Variable = {
|
export type Variable = {
|
||||||
name: string;
|
name: string
|
||||||
value: string;
|
value: string
|
||||||
};
|
}
|
||||||
|
|
||||||
export type DataFromTypebot = {
|
export type DataFromTypebot = {
|
||||||
redirectUrl?: string;
|
redirectUrl?: string
|
||||||
newVariableValue?: Variable;
|
newVariableValue?: Variable
|
||||||
videoPlayed?: boolean;
|
videoPlayed?: boolean
|
||||||
};
|
}
|
||||||
|
|
||||||
export const localStorageKeys = {
|
export const localStorageKeys = {
|
||||||
rememberClose: "rememberClose",
|
rememberClose: 'rememberClose',
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,30 +1,58 @@
|
|||||||
import * as Typebot from "../../src";
|
import * as Typebot from '../../src'
|
||||||
|
|
||||||
describe("initBubble", () => {
|
describe('initBubble', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
document.body.innerHTML = "";
|
document.body.innerHTML = ''
|
||||||
});
|
})
|
||||||
|
|
||||||
it("should initialize a bubble embed", () => {
|
it('should initialize a bubble embed', () => {
|
||||||
expect.assertions(2);
|
expect.assertions(2)
|
||||||
Typebot.initBubble({ publishId: "typebot-id" });
|
Typebot.initBubble({ publishId: 'typebot-id' })
|
||||||
const bubbleElement = document.getElementById("typebot-bubble");
|
const bubbleElement = document.getElementById('typebot-bubble')
|
||||||
const frame = document.getElementsByTagName("iframe")[0];
|
const frame = document.getElementsByTagName('iframe')[0]
|
||||||
expect(frame).toBeDefined();
|
expect(frame).toBeDefined()
|
||||||
expect(bubbleElement).toBeDefined();
|
expect(bubbleElement).toBeDefined()
|
||||||
});
|
})
|
||||||
|
|
||||||
it("should overwrite bubble if exists", () => {
|
it('should overwrite bubble if exists', () => {
|
||||||
expect.assertions(2);
|
expect.assertions(2)
|
||||||
Typebot.initBubble({
|
Typebot.initBubble({
|
||||||
publishId: "typebot-id",
|
publishId: 'typebot-id',
|
||||||
hiddenVariables: { var1: "test" },
|
hiddenVariables: { var1: 'test' },
|
||||||
});
|
})
|
||||||
Typebot.initBubble({ publishId: "typebot-id2" });
|
Typebot.initBubble({ publishId: 'typebot-id2' })
|
||||||
const frames = document.getElementsByTagName("iframe");
|
const frames = document.getElementsByTagName('iframe')
|
||||||
expect(frames).toHaveLength(1);
|
expect(frames).toHaveLength(1)
|
||||||
expect(frames[0].dataset.src).toBe(
|
expect(frames[0].dataset.src).toBe(
|
||||||
"https://typebot-viewer.vercel.app/typebot-id2?hn=localhost"
|
'https://typebot-viewer.vercel.app/typebot-id2?hn=localhost'
|
||||||
);
|
)
|
||||||
});
|
})
|
||||||
});
|
|
||||||
|
it('show open after the corresponding delay', async () => {
|
||||||
|
expect.assertions(3)
|
||||||
|
Typebot.initBubble({
|
||||||
|
autoOpenDelay: 1000,
|
||||||
|
publishId: 'typebot-id',
|
||||||
|
})
|
||||||
|
const bubble = document.querySelector('#typebot-bubble') as HTMLDivElement
|
||||||
|
expect(bubble.classList.contains('iframe-opened')).toBe(false)
|
||||||
|
await new Promise((r) => setTimeout(r, 1000))
|
||||||
|
expect(bubble.classList.contains('iframe-opened')).toBe(true)
|
||||||
|
const rememberCloseDecisionFromStorage = localStorage.getItem(
|
||||||
|
Typebot.localStorageKeys.rememberClose
|
||||||
|
)
|
||||||
|
expect(rememberCloseDecisionFromStorage).toBe('true')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should remember close decision if set to true', async () => {
|
||||||
|
expect.assertions(1)
|
||||||
|
localStorage.setItem(Typebot.localStorageKeys.rememberClose, 'true')
|
||||||
|
Typebot.initBubble({
|
||||||
|
autoOpenDelay: 1000,
|
||||||
|
publishId: 'typebot-id',
|
||||||
|
})
|
||||||
|
const bubble = document.querySelector('#typebot-bubble') as HTMLDivElement
|
||||||
|
await new Promise((r) => setTimeout(r, 1500))
|
||||||
|
expect(bubble.classList.contains('iframe-opened')).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|||||||
@@ -1,104 +1,77 @@
|
|||||||
import * as Typebot from "../../src";
|
import * as Typebot from '../../src'
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
document.body.innerHTML = "";
|
document.body.innerHTML = ''
|
||||||
});
|
})
|
||||||
|
|
||||||
it("should create the message", () => {
|
it('should create the message', () => {
|
||||||
expect.assertions(2);
|
expect.assertions(2)
|
||||||
Typebot.initBubble({
|
Typebot.initBubble({
|
||||||
proactiveMessage: { textContent: "Hi click here!" },
|
proactiveMessage: { textContent: 'Hi click here!' },
|
||||||
publishId: "typebot-id",
|
publishId: 'typebot-id',
|
||||||
});
|
})
|
||||||
const paragraphElement = document.querySelector(
|
const paragraphElement = document.querySelector(
|
||||||
"#typebot-bubble > .proactive-message > p"
|
'#typebot-bubble > .proactive-message > p'
|
||||||
);
|
)
|
||||||
const closeButton = document.querySelector(
|
const closeButton = document.querySelector(
|
||||||
"#typebot-bubble > .proactive-message > .close-button"
|
'#typebot-bubble > .proactive-message > .close-button'
|
||||||
);
|
)
|
||||||
expect(paragraphElement?.textContent).toBe("Hi click here!");
|
expect(paragraphElement?.textContent).toBe('Hi click here!')
|
||||||
expect(closeButton).toBeTruthy();
|
expect(closeButton).toBeTruthy()
|
||||||
});
|
})
|
||||||
|
|
||||||
it("should have the corresponding avatar", () => {
|
it('should have the corresponding avatar', () => {
|
||||||
expect.assertions(1);
|
expect.assertions(1)
|
||||||
Typebot.initBubble({
|
Typebot.initBubble({
|
||||||
proactiveMessage: {
|
proactiveMessage: {
|
||||||
textContent: "Hi click here!",
|
textContent: 'Hi click here!',
|
||||||
avatarUrl: "https://website.com/my-avatar.png",
|
avatarUrl: 'https://website.com/my-avatar.png',
|
||||||
},
|
},
|
||||||
publishId: "typebot-id",
|
publishId: 'typebot-id',
|
||||||
});
|
})
|
||||||
const avatarElement = document.querySelector(
|
const avatarElement = document.querySelector(
|
||||||
"#typebot-bubble > .proactive-message > img"
|
'#typebot-bubble > .proactive-message > img'
|
||||||
) as HTMLImageElement;
|
) as HTMLImageElement
|
||||||
expect(avatarElement.src).toBe("https://website.com/my-avatar.png");
|
expect(avatarElement.src).toBe('https://website.com/my-avatar.png')
|
||||||
});
|
})
|
||||||
|
|
||||||
it("shouldn't have opened class if delay not defined", () => {
|
it("shouldn't have opened class if delay not defined", () => {
|
||||||
expect.assertions(1);
|
expect.assertions(1)
|
||||||
Typebot.initBubble({
|
Typebot.initBubble({
|
||||||
proactiveMessage: {
|
proactiveMessage: {
|
||||||
textContent: "Hi click here!",
|
textContent: 'Hi click here!',
|
||||||
},
|
},
|
||||||
publishId: "typebot-id",
|
publishId: 'typebot-id',
|
||||||
});
|
})
|
||||||
const bubble = document.querySelector("#typebot-bubble") as HTMLDivElement;
|
const bubble = document.querySelector('#typebot-bubble') as HTMLDivElement
|
||||||
expect(bubble.classList.contains("message-opened")).toBe(false);
|
expect(bubble.classList.contains('message-opened')).toBe(false)
|
||||||
});
|
})
|
||||||
|
|
||||||
it("should show almost immediately if delay is 0", async () => {
|
it('should show almost immediately if delay is 0', async () => {
|
||||||
expect.assertions(1);
|
expect.assertions(1)
|
||||||
Typebot.initBubble({
|
Typebot.initBubble({
|
||||||
proactiveMessage: {
|
proactiveMessage: {
|
||||||
textContent: "Hi click here!",
|
textContent: 'Hi click here!',
|
||||||
delay: 0,
|
delay: 0,
|
||||||
},
|
},
|
||||||
publishId: "typebot-id",
|
publishId: 'typebot-id',
|
||||||
});
|
})
|
||||||
const bubble = document.querySelector("#typebot-bubble") as HTMLDivElement;
|
const bubble = document.querySelector('#typebot-bubble') as HTMLDivElement
|
||||||
await new Promise((r) => setTimeout(r, 1));
|
await new Promise((r) => setTimeout(r, 1))
|
||||||
expect(bubble.classList.contains("message-opened")).toBe(true);
|
expect(bubble.classList.contains('message-opened')).toBe(true)
|
||||||
});
|
})
|
||||||
|
|
||||||
it("show after the corresponding delay", async () => {
|
it('show after the corresponding delay', async () => {
|
||||||
expect.assertions(2);
|
expect.assertions(2)
|
||||||
Typebot.initBubble({
|
Typebot.initBubble({
|
||||||
proactiveMessage: {
|
proactiveMessage: {
|
||||||
textContent: "Hi click here!",
|
textContent: 'Hi click here!',
|
||||||
delay: 1000,
|
delay: 1000,
|
||||||
},
|
},
|
||||||
publishId: "typebot-id",
|
publishId: 'typebot-id',
|
||||||
});
|
})
|
||||||
const bubble = document.querySelector("#typebot-bubble") as HTMLDivElement;
|
const bubble = document.querySelector('#typebot-bubble') as HTMLDivElement
|
||||||
expect(bubble.classList.contains("message-opened")).toBe(false);
|
expect(bubble.classList.contains('message-opened')).toBe(false)
|
||||||
await new Promise((r) => setTimeout(r, 1000));
|
await new Promise((r) => setTimeout(r, 1000))
|
||||||
expect(bubble.classList.contains("message-opened")).toBe(true);
|
expect(bubble.classList.contains('message-opened')).toBe(true)
|
||||||
});
|
})
|
||||||
|
|
||||||
it("should remember close decision if set to true", () => {
|
|
||||||
expect.assertions(2);
|
|
||||||
Typebot.initBubble({
|
|
||||||
proactiveMessage: {
|
|
||||||
textContent: "Hi click here!",
|
|
||||||
delay: 1000,
|
|
||||||
},
|
|
||||||
publishId: "typebot-id",
|
|
||||||
});
|
|
||||||
const rememberCloseDecisionFromStorage = localStorage.getItem(
|
|
||||||
Typebot.localStorageKeys.rememberClose
|
|
||||||
);
|
|
||||||
expect(rememberCloseDecisionFromStorage).toBeNull();
|
|
||||||
Typebot.initBubble({
|
|
||||||
proactiveMessage: {
|
|
||||||
textContent: "Hi click here!",
|
|
||||||
delay: 1000,
|
|
||||||
rememberClose: true,
|
|
||||||
},
|
|
||||||
publishId: "typebot-id",
|
|
||||||
});
|
|
||||||
const refreshedRememberCloseDecisionFromStorage = localStorage.getItem(
|
|
||||||
Typebot.localStorageKeys.rememberClose
|
|
||||||
);
|
|
||||||
expect(refreshedRememberCloseDecisionFromStorage).toBe("true");
|
|
||||||
});
|
|
||||||
|
|||||||
Reference in New Issue
Block a user