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