diff --git a/packages/self-service/vite.config.ts b/packages/self-service/vite.config.ts index 143962de4..a6bd670c6 100644 --- a/packages/self-service/vite.config.ts +++ b/packages/self-service/vite.config.ts @@ -8,7 +8,7 @@ const version = getWidgetVersion(); // https://vitejs.dev/config/ export default defineConfig({ - plugins: [react(), cssInjectedByJsPlugin()], + plugins: [react(), cssInjectedByJsPlugin({ styleId: 'sendbird-css-inject-id' })], build: { outDir: `./dist/${version}`, rollupOptions: { diff --git a/src/hooks/useStyledComponentsTarget.ts b/src/hooks/useStyledComponentsTarget.ts index e07ad3dce..83431e1fe 100644 --- a/src/hooks/useStyledComponentsTarget.ts +++ b/src/hooks/useStyledComponentsTarget.ts @@ -1,6 +1,8 @@ import { useLayoutEffect, useState } from 'react'; import { version } from 'styled-components/package.json'; +const StyledId = 'sendbird-css-inject-id'; + function isSCTarget(node: Node): node is HTMLStyleElement { return node instanceof HTMLStyleElement && node.getAttribute('data-styled-version') === version; } @@ -10,35 +12,46 @@ function isSCTarget(node: Node): node is HTMLStyleElement { * When styled-components, which has already been initialized, is re-added to the head, for example `document.head.innerHTML += ''`, the styles may not render correctly. * Therefore, the target is moved to the body tag. * Similarly, the issue could also rise in below cases and the hook handles them accordingly: - * - If styles are removed from , switch to . + * - If styles are removed from , re-add to head if head exists or switch to . * This is a short-term solution, and in the long run, we plan to remove styled-components altogether. * */ export function useStyledComponentsTarget() { const [target, setTarget] = useState(document.head); useLayoutEffect(() => { + const handleRemovedStyle = (styleElement: HTMLElement) => { + if (styleElement && styleElement.parentElement !== document.body) { + if (document.head) { + console.warn('[useStyledComponentsTarget]: Head exists, re-adding style element ${StyledId} to .'); + document.head.appendChild(styleElement); + setTarget(document.head); + } else { + console.warn('[useStyledComponentsTarget]: Head missing, moving style element ${StyledId} to .'); + document.body.appendChild(styleElement); + setTarget(document.body); + } + } + }; + const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { - // Case 1: Detect if styles are added to - if (mutation.target === document.head && mutation.addedNodes.length > 0) { - for (const node of mutation.addedNodes) { - if (isSCTarget(node)) { - console.warn('Styled Components styles re-injected, switching to '); - setTarget(document.body); - return; - } + // Handle added nodes + Array.from(mutation.addedNodes).forEach((node) => { + if (isSCTarget(node)) { + console.warn('[useStyledComponentsTarget]: Styled Components styles re-injected, switching to '); + setTarget(document.body); } - } - // Case 2: Detect if styles are removed from - if (mutation.target === document.head && mutation.removedNodes.length > 0) { - for (const node of mutation.removedNodes) { - if (isSCTarget(node)) { - console.warn('Styled Components styles removed, switching to '); - setTarget(document.body); - return; - } + }); + + // Handle removed nodes + Array.from(mutation.removedNodes).forEach((node) => { + if (isSCTarget(node)) { + console.warn('[useStyledComponentsTarget]: Styled Components styles removed, switching to '); + setTarget(document.body); + } else if (node instanceof HTMLElement && node.id === StyledId) { + handleRemovedStyle(node); } - } + }); }); });