fix(lib): 🐛 Safari context menu trap
This commit is contained in:
@ -16,19 +16,19 @@
|
||||
<body>
|
||||
<script>
|
||||
const { open } = Typebot.initBubble({
|
||||
publishId: "feedback-form",
|
||||
url: 'https://typebot.io/typebot-support',
|
||||
button: {
|
||||
color: "green",
|
||||
iconUrl: "https://image.flaticon.com/icons/png/512/5138/5138352.png",
|
||||
color: 'green',
|
||||
iconUrl: 'https://image.flaticon.com/icons/png/512/5138/5138352.png',
|
||||
},
|
||||
proactiveMessage: {
|
||||
avatarUrl:
|
||||
"https://images.unsplash.com/photo-1534528741775-53994a69daeb?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1000&q=80",
|
||||
'https://images.unsplash.com/photo-1534528741775-53994a69daeb?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1000&q=80',
|
||||
textContent: "Hey what's up?",
|
||||
delay: 1000,
|
||||
rememberClose: true,
|
||||
},
|
||||
});
|
||||
})
|
||||
</script>
|
||||
<div style="width: 100%; height: 300vh; background-color: aquamarine">
|
||||
<button onclick="(()=>{open()})()" style="width: 200px; height: 60px">
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "typebot-js",
|
||||
"version": "2.2.2",
|
||||
"version": "2.2.3",
|
||||
"main": "dist/index.js",
|
||||
"unpkg": "dist/index.umd.min.js",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
|
@ -1,28 +1,33 @@
|
||||
import { createIframe } from "../../iframe";
|
||||
import { IframeParams } from "../../types";
|
||||
import { createIframe } from '../../iframe'
|
||||
import { IframeParams } from '../../types'
|
||||
|
||||
export const createIframeContainer = (
|
||||
params: IframeParams
|
||||
): HTMLIFrameElement => {
|
||||
const iframe = createIframe({ ...params, loadWhenVisible: true });
|
||||
return iframe;
|
||||
};
|
||||
const iframe = createIframe({ ...params, loadWhenVisible: true })
|
||||
return iframe
|
||||
}
|
||||
|
||||
export const openIframe = (
|
||||
bubble: HTMLDivElement,
|
||||
iframe: HTMLIFrameElement
|
||||
): void => {
|
||||
loadTypebotIfFirstOpen(iframe);
|
||||
bubble.classList.add("iframe-opened");
|
||||
bubble.classList.remove("message-opened");
|
||||
};
|
||||
loadTypebotIfFirstOpen(iframe)
|
||||
iframe.style.display = 'flex'
|
||||
setTimeout(() => bubble.classList.add('iframe-opened'), 50)
|
||||
bubble.classList.remove('message-opened')
|
||||
}
|
||||
|
||||
export const closeIframe = (bubble: HTMLDivElement): void => {
|
||||
bubble.classList.remove("iframe-opened");
|
||||
};
|
||||
export const closeIframe = (
|
||||
bubble: HTMLDivElement,
|
||||
iframe: HTMLIFrameElement
|
||||
): void => {
|
||||
bubble.classList.remove('iframe-opened')
|
||||
setTimeout(() => (iframe.style.display = 'none'), 550)
|
||||
}
|
||||
|
||||
export const loadTypebotIfFirstOpen = (iframe: HTMLIFrameElement): void => {
|
||||
if (!iframe.dataset.src) return;
|
||||
iframe.src = iframe.dataset.src;
|
||||
iframe.removeAttribute("data-src");
|
||||
};
|
||||
if (!iframe.dataset.src) return
|
||||
iframe.src = iframe.dataset.src
|
||||
iframe.removeAttribute('data-src')
|
||||
}
|
||||
|
@ -60,9 +60,11 @@ const createBubble = (
|
||||
? addProactiveMessage(params.proactiveMessage, bubbleElement)
|
||||
: undefined
|
||||
const iframeElement = createIframeContainer(params)
|
||||
buttonElement.addEventListener('click', () =>
|
||||
onBubbleButtonClick(bubbleElement, iframeElement)
|
||||
)
|
||||
buttonElement.addEventListener('click', () => {
|
||||
iframeElement.style.display === 'none'
|
||||
? openIframe(bubbleElement, iframeElement)
|
||||
: closeIframe(bubbleElement, iframeElement)
|
||||
})
|
||||
if (proactiveMessageElement)
|
||||
proactiveMessageElement.addEventListener('click', () =>
|
||||
onProactiveMessageClick(bubbleElement, iframeElement)
|
||||
@ -71,15 +73,6 @@ const createBubble = (
|
||||
return { bubbleElement, proactiveMessageElement, iframeElement }
|
||||
}
|
||||
|
||||
const onBubbleButtonClick = (
|
||||
bubble: HTMLDivElement,
|
||||
iframe: HTMLIFrameElement
|
||||
): void => {
|
||||
loadTypebotIfFirstOpen(iframe)
|
||||
bubble.classList.toggle('iframe-opened')
|
||||
bubble.classList.remove('message-opened')
|
||||
}
|
||||
|
||||
const onProactiveMessageClick = (
|
||||
bubble: HTMLDivElement,
|
||||
iframe: HTMLIFrameElement
|
||||
@ -115,7 +108,7 @@ export const getBubbleActions = (
|
||||
openIframe(existingBubbleElement, existingIframeElement)
|
||||
},
|
||||
close: () => {
|
||||
closeIframe(existingBubbleElement)
|
||||
closeIframe(existingBubbleElement, existingIframeElement)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -61,7 +61,6 @@
|
||||
}
|
||||
|
||||
#typebot-bubble > iframe {
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
display: flex;
|
||||
border-radius: 10px;
|
||||
@ -83,7 +82,6 @@
|
||||
|
||||
#typebot-bubble.iframe-opened > iframe {
|
||||
transform: translate(0, 0);
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@ export const createIframe = ({
|
||||
iframe.classList.add('typebot-iframe')
|
||||
const { onNewVariableValue, onVideoPlayed } = iframeParams
|
||||
listenForTypebotMessages({ onNewVariableValue, onVideoPlayed })
|
||||
iframe.style.display = 'none'
|
||||
return iframe
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ beforeEach(() => {
|
||||
})
|
||||
|
||||
describe('openBubble', () => {
|
||||
it('should add the opened bubble', () => {
|
||||
it('should add the opened bubble', async () => {
|
||||
expect.assertions(3)
|
||||
const { open } = Typebot.initBubble({
|
||||
url: 'https://typebot.io/typebot-id',
|
||||
@ -13,6 +13,7 @@ describe('openBubble', () => {
|
||||
const bubble = document.getElementById('typebot-bubble') as HTMLDivElement
|
||||
expect(bubble.classList.contains('iframe-opened')).toBe(false)
|
||||
open()
|
||||
await new Promise((resolve) => setTimeout(resolve, 50))
|
||||
expect(bubble.classList.contains('iframe-opened')).toBe(true)
|
||||
expect(open).not.toThrow()
|
||||
})
|
||||
@ -35,13 +36,14 @@ describe('openBubble', () => {
|
||||
})
|
||||
|
||||
describe('closeBubble', () => {
|
||||
it('should remove the corresponding class', () => {
|
||||
it('should remove the corresponding class', async () => {
|
||||
expect.assertions(2)
|
||||
const { close, open } = Typebot.initBubble({
|
||||
url: 'https://typebot.io/typebot-id',
|
||||
})
|
||||
open()
|
||||
const bubble = document.getElementById('typebot-bubble') as HTMLDivElement
|
||||
await new Promise((resolve) => setTimeout(resolve, 50))
|
||||
expect(bubble.classList.contains('iframe-opened')).toBe(true)
|
||||
close()
|
||||
expect(bubble.classList.contains('iframe-opened')).toBe(false)
|
||||
@ -72,7 +74,7 @@ describe('openProactiveMessage', () => {
|
||||
})
|
||||
|
||||
describe('Request commands afterwards', () => {
|
||||
it('should return defined commands', () => {
|
||||
it('should return defined commands', async () => {
|
||||
Typebot.initBubble({
|
||||
proactiveMessage: {
|
||||
textContent: 'Hi click here!',
|
||||
@ -85,6 +87,7 @@ describe('Request commands afterwards', () => {
|
||||
expect(open).toBeDefined()
|
||||
expect(openProactiveMessage).toBeDefined()
|
||||
open()
|
||||
await new Promise((resolve) => setTimeout(resolve, 50))
|
||||
const bubble = document.getElementById('typebot-bubble') as HTMLDivElement
|
||||
expect(bubble.classList.contains('iframe-opened')).toBe(true)
|
||||
})
|
||||
|
@ -36,7 +36,7 @@ describe('initBubble', () => {
|
||||
})
|
||||
const bubble = document.querySelector('#typebot-bubble') as HTMLDivElement
|
||||
expect(bubble.classList.contains('iframe-opened')).toBe(false)
|
||||
await new Promise((r) => setTimeout(r, 1000))
|
||||
await new Promise((r) => setTimeout(r, 1050))
|
||||
expect(bubble.classList.contains('iframe-opened')).toBe(true)
|
||||
const rememberCloseDecisionFromStorage = localStorage.getItem(
|
||||
Typebot.localStorageKeys.rememberClose
|
||||
|
Reference in New Issue
Block a user