From 600973fa22beb0b1b4b791fece9e8f66ab79472d Mon Sep 17 00:00:00 2001 From: Peter Hudec Date: Thu, 25 Jan 2024 17:18:34 +0000 Subject: [PATCH] Fixed a bug when WatchTextContent didnt work with a routed component --- src/client/components/WatchTextContent.jsx | 49 +++++++++++++------ .../cypress/specs/WatchTextContent.cy.jsx | 29 ++++++++++- 2 files changed, 61 insertions(+), 17 deletions(-) diff --git a/src/client/components/WatchTextContent.jsx b/src/client/components/WatchTextContent.jsx index 1d22264a1ac..e81ceae4043 100644 --- a/src/client/components/WatchTextContent.jsx +++ b/src/client/components/WatchTextContent.jsx @@ -1,12 +1,12 @@ -import { useEffect, useRef } from 'react' -import ReactDOM from 'react-dom' +import React, { useEffect, useRef } from 'react' /** * @function WatchTextContent * @description Calls {onTextContentChange} anytime the `textContent` - * of {children} changes. The {children} won't be rendered. + * of {children} changes. The {children} will be rendered hidden. * @param {Object} props - * @param {React.Children} props.children - Children + * @param {React.Children} props.children - Vdom to whose changes the component subscribes to. + * It will be rendered in a hidden div. * @param {(textContent: string) => void} props.onTextContentChange - A callback that will * be called anytime the `textContent` of this component changes with the value * of the current `textContent`. @@ -16,24 +16,41 @@ const WatchTextContent = ({ onTextContentChange, ...props }) => { const previousTextContent = useRef() useEffect(() => { - ref.current = document.createElement('div') - return () => { - ref.current.remove() - } - }, []) + // Detach the element so that the content is not searchable by Cypress tests. + // This is the only way to make an element really hidden from Cypress. + ref.current.remove() - useEffect(() => { - ReactDOM.render(props.children, ref.current) - // The most recent update only takes effect in the next event tick - setTimeout(() => { + onTextContentChange(ref.current.textContent) + previousTextContent.current = ref.current.textContent + + const observer = new MutationObserver(() => { if (ref.current.textContent !== previousTextContent.current) { onTextContentChange(ref.current.textContent) previousTextContent.current = ref.current.textContent } - }, 0) - }) + }) + + if (ref.current) { + observer.observe(ref.current, { + childList: true, + subtree: true, + attributes: true, + characterData: true, + }) + } + + return () => { + observer.disconnect() + } + }, []) - return null + return ( + // This wrapper is necessary because if we remove topmost element returned from render, + // React would throw a NotFoundError. +