feat: push modal to shadow root in isolated shell mode
This commit is contained in:
@@ -15,6 +15,7 @@ const { modalType, closeOnBackdropClick, onClose } = defineProps({
|
||||
|
||||
const emit = defineEmits(['close']);
|
||||
const show = defineModel('show', { type: Boolean, default: false });
|
||||
const teleportTarget = ref(null);
|
||||
|
||||
const modalClassName = computed(() => {
|
||||
const modalClassNameMap = {
|
||||
@@ -25,13 +26,23 @@ const modalClassName = computed(() => {
|
||||
return `modal-mask skip-context-menu ${modalClassNameMap[modalType] || ''}`;
|
||||
});
|
||||
|
||||
const teleportTarget = computed(() => {
|
||||
const resolveTeleportTarget = () => {
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
if (window.__WOOT_ISOLATED_SHELL__) {
|
||||
console.log(document.getElementById('cw-modal-root'));
|
||||
return '#cw-modal-root';
|
||||
// Look for the specific chatwoot-message-list custom element
|
||||
const messageListElement = document.querySelector('chatwoot-message-list');
|
||||
if (messageListElement?.shadowRoot) {
|
||||
const modalRoot =
|
||||
messageListElement.shadowRoot.querySelector('#cw-modal-root');
|
||||
if (modalRoot) {
|
||||
teleportTarget.value = modalRoot;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 'body';
|
||||
});
|
||||
|
||||
teleportTarget.value = document.body;
|
||||
};
|
||||
|
||||
// [TODO] Revisit this logic to use outside click directive
|
||||
const mousedDownOnBackdrop = ref(false);
|
||||
@@ -66,7 +77,7 @@ useEventListener(document.body, 'mouseup', onMouseUp);
|
||||
useEventListener(document, 'keydown', onKeydown);
|
||||
|
||||
onMounted(() => {
|
||||
console.log('TESTING 15:10');
|
||||
resolveTeleportTarget();
|
||||
if (import.meta.env.DEV && onClose && typeof onClose === 'function') {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
@@ -77,7 +88,7 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Teleport :to="teleportTarget">
|
||||
<Teleport v-if="teleportTarget" :to="teleportTarget">
|
||||
<transition name="modal-fade">
|
||||
<div
|
||||
v-if="show"
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
# Modal teleport target escapes shadow DOM in embedded mode
|
||||
|
||||
## Context
|
||||
- Vue components are exported as custom elements for React, rendered inside a shadow DOM.
|
||||
- The modal component teleports its content; when it teleports to `document.body`, it leaves the shadow DOM and loses styles.
|
||||
|
||||
## Relevant files
|
||||
- `app/javascript/react-components/src/vue-components/ChatwootMessageListWebComponent.vue`
|
||||
- CE wrapper rendered inside the shadow root.
|
||||
- Currently includes both `#cw-app-root` (MessageList) and `#cw-modal-root` (intended modal host) inside the shadow DOM.
|
||||
- `app/javascript/dashboard/components/Modal.vue`
|
||||
- Modal component using `<Teleport>`.
|
||||
- Has logic to resolve the teleport target using `getRootNode` and a fallback to `document.body`.
|
||||
- Entry/UI setup: `app/javascript/entrypoints/ui.js` mounts the Vue app in non-CE mode (body teleport is fine there).
|
||||
|
||||
## Current behavior
|
||||
- In embedded/isolated mode (`window.__WOOT_ISOLATED_SHELL__` is true), the modal still teleports to `document.body` instead of the shadow DOM.
|
||||
- Because the modal node ends up in `body`, CE-injected styles do not apply; the modal appears unstyled.
|
||||
|
||||
## Desired behavior
|
||||
- When running inside the custom element (shadow DOM), the modal should teleport to `#cw-modal-root` inside that shadow root (the element already exists in the CE wrapper).
|
||||
- Only fall back to `document.body` when not in embedded mode or not in a shadow root.
|
||||
|
||||
## Likely fixes
|
||||
- In `Modal.vue`, resolve the teleport target on mount:
|
||||
- Use `getRootNode()` on the component root to detect a `ShadowRoot`.
|
||||
- If in a `ShadowRoot`, select `#cw-modal-root` there and use that element.
|
||||
- If not found or not in a shadow root, fall back to `document.body`.
|
||||
- Remove extra complexity (e.g., hidden anchors or element creation); the modal host is already present in the CE template.
|
||||
|
||||
## Acceptance criteria
|
||||
- In embedded mode, the modal stays inside the shadow DOM and is styled (teleport target is the shadow’s `#cw-modal-root`).
|
||||
- In normal app mode, behavior remains unchanged (teleport to `body`).
|
||||
Reference in New Issue
Block a user