Skip to content

Add <HtmlPanel> component #4

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open

Add <HtmlPanel> component #4

wants to merge 17 commits into from

Conversation

Asha20
Copy link
Collaborator

@Asha20 Asha20 commented Nov 3, 2023

This PR adds the <HtmlPanel> component.

I've also updated the example app to include an HTML panel so you can see it in action.

Copy link
Contributor

@eteeselink eteeselink left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First half of my review, second half coming up

README.md Outdated
@@ -274,13 +274,28 @@ Accepted props:
loading
- `chatboxRef` (resp. `inboxRef`, `popupRef`) - Pass a ref (created with `useRef`) and it'll be set to the vanilla JS [Chatbox](https://talkjs.com/docs/Reference/JavaScript_Chat_SDK/Chatbox/) (resp. [Inbox](https://talkjs.com/docs/Reference/JavaScript_Chat_SDK/Inbox/), [Popup](https://talkjs.com/docs/Reference/JavaScript_Chat_SDK/Popup/)) instance. See [above](#using-refs) for an example.
- All [Talk.ChatboxOptions](https://talkjs.com/docs/Reference/JavaScript_Chat_SDK/Session/#ChatboxOptions)
- `children?: ReactNode` - Optional. You can provide an `<HtmlPanel>` component as a child to use [HTML Panels](https://talkjs.com/docs/Features/Customizations/HTML_Panels/).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should explicitly say that HtmlPanels can be the only direct children

README.md Outdated

Accepted props:

- `url: string` - The URL you want to load inside the HTML panel. The URL can be absolute or relative. We recommend using same origin pages to have better control of the page. Learn more about HTML Panels and same origin pages [here](https://talkjs.com/docs/Features/Customizations/HTML_Panels/)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be more precise, “to have better control” is a bit meaningless and also kinda wrong. It must be the same origin if they want to render children into it. If they don’t want to render their contents right here from React then the origin doesn’t matter. (Btw make sure you test both cases, ie that if there’s no children passed, and the panel has a different origin, there are no errors).

README.md Outdated

- `height?: number` - Optional. The panel height in pixels. Defaults to `100px`.

- `show?: boolean` - Optional. Sets the visibility of the panel. Defaults to `true`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth explaining how this is different vs conditionally rendering the HtmlPanel element

Copy link
Contributor

@eteeselink eteeselink left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

love it. got lots of nits tho cause i'm like that


return () => {
if (state.type === "loaded") {
state.panel.destroy();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't we always destroy it, regardless of loading state? else if customer code creates it and then quickly removes it, the chatbox will create it anyway.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(maybe just put the Promise<HtmlPanel> in the state, and do state.panelPromise.then(panel => panel.destroy()). i don't think there's a downside to already-resolved panels being destroyed in the next microtask instead of right there in the cleanup callback, right?

state.panel.hide();
}
}
}, [state, show]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this be state.type instead of the entire state? (same for height)

Comment on lines 89 to 92
<>
{state.type === "loaded" &&
createPortal(children, state.panel.window.document.body)}
</>
Copy link
Contributor

@eteeselink eteeselink Nov 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

magic, isn't it? :D

btw i think this needs some "if no children given, then no portal" type logic, so we dont get errors when people mount non-same-origin html panels (eg because they're SSR'ing something from somewhere else)

Comment on lines 47 to 52
const panel = await box.createHtmlPanel({
url,
conversation: conversationId,
height,
show,
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

meta comment, I just realized that a set of <HtmlPanel> components in a react app is semantically equivalent to the htmlPanelOptions we pass from the JSSDK in the frontend.

this is actually super nuts. we internally have a declarative data structure, we built an imperative interface around that to mutate that data structure (createHtmlPanel, destroy, show, etc), and now here we're building the same thing in the opposite direction!

i don't think we need to do anything rn with this observation but i'm thinking that maybe one day we should deprecate all create* and set* methods entirely in favour of some vanilla JS, react-y declarative "spec your chatbox here" interface. would make the entire react sdk a near no-op, also.

Copy link
Contributor

@eteeselink eteeselink left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love it. I think I reviewed everything now!

<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit meaningless, no? Maybe just keep the title out

<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
body {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe let's just give body, html a padding: 0 for good measure? (and eg a margin on the button). i expect anybody will want to do that, so the html panel behaves pretty much like a div.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what you mean here; the buttons already have margin: 0.6rem auto

<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about we prepend something here like

<link rel="stylesheet" href="./your-styles.css">
<!-- Put your app's CSS here. For instance, if your bundler generates a CSS file from all component styles, load it here as well and your components will be styled correctly inside the HTML Panel -->

Previously we'd incorrectly pass their `children` into `useUIBox` which
would cause the UIBoxes to be destroyed and re-initialized over and over.
@lechinoix
Copy link

Hello there ! Thank you so much for your Pull Request, I'll move to your fork as I definitely need it :) Might be nice to merge it at some point

@lechinoix
Copy link

@eteeselink Arf actually I cannot manage to use this branch directly, no types etc... Could you release it on a canary version or whatsoever ? It would be of great help :)

@Asha20
Copy link
Collaborator Author

Asha20 commented Jan 19, 2024

The experimental <HtmlPanel> component is now available by installing the @talkjs/react library with the next tag:

npm install @talkjs/react@next

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants