Leff
Leff

Reputation: 1360

createPortal not rendering children in a new window

I would like to render content in a new window. This is the component where I am doing it:

export const NewWindowPortal = ({ children }: { children: (props) => ReactNode }) => {
    const ref = useRef(null);
    const containerEl = useMemo(() => document.createElement("div"), []);

    useEffect(() => {
        const externalWindow = window.open("", "", "width=600,height=400,left=200,top=200");
        externalWindow?.document?.body?.appendChild(containerEl);
    }, []);

    return (
        <>
            <BodyShort>This is a new window</BodyShort>
            {createPortal(children({ ref }), containerEl)}
        </>
    );
};

And this is where I am calling this component:

{openInNewWindow && (
            <NewWindowPortal>
                {(props) => (
                    <CustomQuillEditor
                        ref={props.ref}
                        resize={resize}
                        readOnly={lesemodus || readOnly}
                        defaultValue={reformatText(value)}
                        onTextChange={onTextChange}
                    />
                )}
            </NewWindowPortal>
        )}

I can see that the new window opens, but nothing is rendered in it, it just an empty document. I see that if I just send a div as a child:

{openInNewWindow && <NewWindowPortal>{(props) => <div>{props.text}</div>}</NewWindowPortal>}

And change the NewWindowPortal to render it:

const text = "test";
const containerEl = useMemo(() => document.createElement("div"), []);

useEffect(() => {
    const externalWindow = window.open("", "", "width=600,height=400,left=200,top=200");
    externalWindow?.document?.body?.appendChild(containerEl);
}, []);

return (
    <>
        <BodyShort>This is a new window</BodyShort>
        {createPortal(children({ text }), containerEl)}
    </>
);

Then I get children rendered in NewWindowPortal. So, the issue is obviously in CustomQuillEditor:

export const CustomQuillEditor = ({ readOnly, defaultValue, onTextChange, ref, resize }: EditorProps) => {
    const containerRef = useRef(null);

    useEffect(() => {
        const container = containerRef.current;
        const editorContainer = container.appendChild(container.ownerDocument.createElement("div"));
        const quill = new Quill(editorContainer, {
            theme: "snow",
            readOnly,
            modules: {
                history: {},

                toolbar: readOnly
                    ? false
                    : {
                          container: [
                              ["bold", "italic", "underline", { header: 3 }],
                              // [{ 'color': "red" }, { 'background': "yellow" }]
                          ],
                      },
                clipboard: {
                    allowed: {
                        tags: ["strong", "h3", "h4", "em", "p", "br", "span", "u"],
                        // attributes: ['href', 'rel', 'target', 'class', "style"]
                        attributes: [],
                    },
                    customButtons: [],
                    keepSelection: true,
                    substituteBlockElements: true,
                    magicPasteLinks: false,
                    removeConsecutiveSubstitutionTags: false,
                },
            },
        });

        ref.current = quill;

        if (defaultValue) {
            const delta = quill.clipboard.convert({ html: defaultValue });
            quill.setContents(delta, "silent");
            quill.history.clear();
        }

        quill.on(Quill.events.TEXT_CHANGE, () => {
            if (quill.getLength() <= 1) {
                onTextChange("");
            } else {
                onTextChange(quill.getSemanticHTML().replaceAll("<p></p>", "<p><br/></p>"));
            }
        });

        return () => {
            ref.current = null;
            container.innerHTML = "";
        };
    }, [ref]);

    return (
        <div
            spellCheck={false}
            className={`ql-top-container ${readOnly ? "readonly" : ""} ${resize ? "resizable" : ""}`}
            ref={containerRef}
        ></div>
    );
};

How can I fix this to have CustomQuillEditor rendered as well?

Upvotes: 0

Views: 36

Answers (1)

Ninad
Ninad

Reputation: 46

Pass the ownerDocument to the child component, and use that ownerDocument when creating quill elements.

Change NewWindowPortal to pass ownerDocument to the child component:

return createPortal(children({ ownerDocument: externalWindowRef.current?.document }), containerEl);

Update CustomQuillEditor to use ownerDocument when creating elements:

const editorContainer = ownerDocument.createElement("div");
containerRef.current.appendChild(editorContainer);

This will initializes quill in the right context and editor will render properly in the new window.

Upvotes: 0

Related Questions