Repository navigation
Render with createRoot() in renderWebChat() when ReactDOM.render() is not available - #5963
Conversation
|
|
||
| // WHEN: Web Chat is rendered with createRoot(), as the error says. | ||
| createRoot(webChatElement).render( | ||
| createElement(ReactWebChat, { directLine, store, styleOptions: { hideSendBox: true } }) |
There was a problem hiding this comment.
Should we test it like *.esm.html, where it set the style options with { hideSendBox: false } on second render?
There was a problem hiding this comment.
Added in d57b101: after the first snapshot, the IIFE test renders <ReactWebChat> again on the same root with styleOptions: { hideSendBox: false }. The send box appears, the transcript stays the same element, and snapshot 2 shows the message with the send box, so the IIFE bundle handles a prop update on the page's React 19. renderWebChat() itself fails on the IIFE path before creating a root, so root reuse stays covered by the esm test.
There was a problem hiding this comment.
Updated in 0a88658: the IIFE test (React 19) now calls renderWebChat() itself with the page's root, then again on the same root with styleOptions: { hideSendBox: false }. The send box appears, the transcript stays the same element, snapshot 2 is unchanged, and root.unmount() removes Web Chat.
importReactDOMClient() always returns a promise; renderWebChat() returns Promise<void> and rejects with the createRoot() error instead of throwing synchronously or logging a failed import. React 16 to 18 still render synchronously. The IIFE test now also renders again with the send box on the same root.
…bchat-without-render
…er import react-dom/client Ported from 92bbd36 and d44517f. A DOM container (element, document, document fragment, by nodeType, checked first: custom elements such as Lit have their own render()) keeps ReactDOM.render() on React 16 to 18 and Preact; on React 19 it throws, asking for createRoot(element) instead. An object with render() is rendered with root.render() on any React version. Removes importReactDOMClient (and its static variant and esbuild plugin), createRootSchema and rootByElement: Web Chat no longer imports "react-dom/client" in any build. renderWebChat() is synchronous again and returns void, as in the last release.
… React 18, and a custom element with render() Ported from 2fb2d04: the pages create the root with createRoot() from "react-dom/client" of their import map and pass it; an element on React 19 throws, asking for a root. The IIFE test calls renderWebChat() again on the same root with { hideSendBox: false }.
…t 19, update the changelog fragment
…cument | DocumentFragment | null) or a root
… OEvgeny/dev-react-19-on-film-stack #5963 now takes renderWebChat(props, elementOrRoot), synchronous, and never imports "react-dom/client" (importReactDOMClient and its esbuild plugin removed). Conflicts resolved to #5963: packages/bundle/src/renderWebChat.tsx, renderWebChat.spec.tsx, and the react19/renderWebChat.{iife,root.react18.esm,customElement.react18.esm}.html tests (stage 4's own root tests are superseded by #5963's). Kept from this branch: React 19 tests, the Fluent IIFE React DOM fix, HOSTING.md React 19 preview section (updated: hosts pass createRoot(element); no "react-dom/client" in the import map for renderWebChat; npm bullet removed). react19/esm.renderWebChat.html now passes createRoot(element).
| "imports": { | ||
| "@testduet/wait-for": "https://esm.sh/@testduet/wait-for", | ||
| "botframework-webchat": "/__dist__/packages/bundle/static/botframework-webchat.js", | ||
| "react": "/__dist__/packages/bundle/static/react.js", |
There was a problem hiding this comment.
Should this use esm.sh/react@19.3.1 as well?
There was a problem hiding this comment.
Tried: the static build fails with "'react-dom' does not provide an export named 'render'", from react-film's import { render } from 'react-dom'. Real React 19 with the static build is tested on the react-film stack (react19/esm.renderWebChat.html); this page switches once react-film is gone (TODO updated in #5971).
| const webChatElement = document.getElementById('webchat'); | ||
|
|
||
| // GIVEN: "react-dom" without `ReactDOM.render()`, like React 19. | ||
| expect(ReactDOM.render).toBeUndefined(); |
There was a problem hiding this comment.
Add expect(ReactDOM.version).toBe(expect.stringMatching(/^19\./)) for clearer expectation.
There was a problem hiding this comment.
In #5971 (69ed9a9ed). This page runs React 18 with ReactDOM.render() removed (the static build cannot load real React 19 yet), so it expects /^18\./; the real React 19 pages (IIFE, Fluent IIFE, copy button) expect /^19\./. toEqual(), as toBe() ignores asymmetric matchers.
| // GIVEN: A root from createRoot(), which records what it renders. | ||
| const root = createRoot(document.getElementById('webchat')); | ||
| const renderedChildren = []; | ||
| const recordingRoot = { |
There was a problem hiding this comment.
Consider use jest-mock/spyOn.
There was a problem hiding this comment.
In #5971 (c1693e72d): spyOn(root, 'render') from jest-mock replaces the hand-written recording root; it still calls through.
| // The host created the root: render with it on any React version, it is the host's to unmount. | ||
| elementOrRoot.render(children); | ||
| } else { | ||
| throw new Error('botframework-webchat: renderWebChat() expects an HTMLElement or a root from createRoot().'); |
There was a problem hiding this comment.
Nitpick, to clearer on where createRoot() is from.
| throw new Error('botframework-webchat: renderWebChat() expects an HTMLElement or a root from createRoot().'); | |
| throw new Error('botframework-webchat: renderWebChat() expects an HTMLElement or a root from ReactDOM.createRoot().'); |
There was a problem hiding this comment.
In #5971 (46e59d55c): applied as suggested; the unit spec and the IIFE test expect the new message.
| ); | ||
|
|
||
| // Both `ReactDOM.render()` (React 18 or earlier) and `root.render()`. | ||
| const renderSchema = object({ render: function_() }); |
There was a problem hiding this comment.
Nitpick: maybe split them into 2 schemas for clearer code paths below.
reactRootSchema: object({ render: pipe(function_(), args(...)) })legacyReactDOMSchema: object({ render: pipe(function_(), args(...)) })
There was a problem hiding this comment.
#5963 merged just before I pushed this. The split is ready (legacyReactDOMSchema checked on ReactDOM, reactRootSchema on the root, same behaviour) and goes into the React 19 follow-up PR together with the other nits.
Changelog Entry
Added
renderWebChat(): added support of a root fromcreateRoot()for React 19, related to #5552 and #5850, in PR #5963, by @OEvgenyDescription
renderWebChat()calledReactDOM.render(), which React 19 removed. It now also takes a root fromcreateRoot()instead of the element; an element on React 19 throws, asking forcreateRoot(element).Design
react-dom/client(npm bundlers fail on it with React 16 and 17): the host creates the root.render()); React 16 to 18 keepReactDOM.render(), synchronously.Specific Changes
renderWebChat.tsx: element or root, typed;renderWebChat.spec.tsx: element, fragment, root, React 19 errorisomorphic-react-dom: bundled React DOM loads only withoutwindow.ReactDOM(crashed with React 19)react19/renderWebChat.{iife,esm}.html: element throws; snapshot 1 root, 2 same root shows the send boxreact19/renderWebChat.{root,customElement}.react18.esm.html: root; custom element withrender(); snapshot 1HOSTING.md: React 19 passescreateRoot(element).changelog/<PR#>-<slug>.mdReview Checklist
z-index)package.jsonandpackage-lock.jsonreviewed