-
Notifications
You must be signed in to change notification settings - Fork 4
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
base: master
Are you sure you want to change the base?
Changes from 3 commits
ffed3f0
914719e
7cfa463
5253bb7
2cde9eb
cbdf35b
faf5bdc
ac6d3ff
7367fd3
e66cdb9
359bb0f
f30cacf
045d7af
b74d9d3
92231d7
6217f1d
9c1cf8b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,7 @@ | ||
## v0.1.4 | ||
|
||
- Add `<HtmlPanel>` component | ||
|
||
## v0.1.3 | ||
|
||
- Add `signature?: string` prop to `Session` | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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/). | ||
|
||
Accepted events (props that start with "on"): | ||
|
||
- All events accepted by [`Talk.Chatbox`](https://talkjs.com/docs/Reference/JavaScript_Chat_SDK/Chatbox/#Chatbox__methods) (resp. [Inbox](https://talkjs.com/docs/Reference/JavaScript_Chat_SDK/Inbox/#Inbox__methods), [Popup](https://talkjs.com/docs/Reference/JavaScript_Chat_SDK/Popup/#Popup__methods)) | ||
|
||
Note: For `<Chatbox>` and `<Popup>`, you must provide exactly one of `conversationId` and `syncConversation`. For `<Inbox>`, leaving both unset selects the latest conversation this user participates in (if any). See [Inbox.select](https://talkjs.com/docs/Reference/JavaScript_Chat_SDK/Inbox/#Inbox__select) for more information. | ||
|
||
### `<HtmlPanel>` | ||
|
||
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/) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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). |
||
|
||
- `height?: number` - Optional. The panel height in pixels. Defaults to `100px`. | ||
|
||
- `show?: boolean` - Optional. Sets the visibility of the panel. Defaults to `true`. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Worth explaining how this is different vs conditionally rendering the HtmlPanel element |
||
|
||
- `conversationId?: string` - Optional. If given, the panel will only show up for the conversation that has an `id` matching the one given. | ||
|
||
- `children: React.ReactNode` - The content that gets rendered inside the `<body>` of the panel. | ||
|
||
|
||
## Contributing | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
<title>Document</title> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a bit meaningless, no? Maybe just keep the title out |
||
<style> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 --> |
||
body { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe let's just give There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure what you mean here; the buttons already have |
||
background-color: lightblue; | ||
} | ||
button { | ||
display: block; | ||
width: 10rem; | ||
margin: 0.6rem auto; | ||
} | ||
</style> | ||
</head> | ||
<body></body> | ||
</html> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
import { useContext, useEffect, useState } from "react"; | ||
import { createPortal } from "react-dom"; | ||
import Talk from "talkjs"; | ||
import { BoxContext } from "./MountedBox"; | ||
|
||
type HtmlPanelProps = { | ||
/** | ||
* 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 {@link https://talkjs.com/docs/Features/Customizations/HTML_Panels/ | here}. | ||
*/ | ||
url: string; | ||
|
||
/** The panel height in pixels. Defaults to `100px`. */ | ||
height?: number; | ||
|
||
/** Sets the visibility of the panel. Defaults to `true`. */ | ||
show?: boolean; | ||
|
||
/** If given, the panel will only show up for the conversation that has an `id` matching the one given. */ | ||
conversationId?: string; | ||
|
||
/** The content that gets rendered inside the `<body>` of the panel. */ | ||
children: React.ReactNode; | ||
}; | ||
|
||
type State = | ||
| { type: "none" } | ||
| { type: "loading" } | ||
| { type: "loaded"; panel: Talk.HtmlPanel }; | ||
|
||
export function HtmlPanel({ | ||
url, | ||
height = 100, | ||
show = true, | ||
conversationId, | ||
children, | ||
}: HtmlPanelProps) { | ||
const [state, setState] = useState<State>({ type: "none" }); | ||
const box = useContext(BoxContext); | ||
|
||
useEffect(() => { | ||
async function run() { | ||
if (state.type !== "none" || !box) return; | ||
|
||
setState({ type: "loading" }); | ||
const panel = await box.createHtmlPanel({ | ||
url, | ||
conversation: conversationId, | ||
height, | ||
show, | ||
}); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. meta comment, I just realized that a set of 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 |
||
await panel.windowLoadedPromise; | ||
setState({ type: "loaded", panel }); | ||
} | ||
|
||
run(); | ||
|
||
return () => { | ||
if (state.type === "loaded") { | ||
state.panel.destroy(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (maybe just put the |
||
setState({ type: "none" }); | ||
} | ||
}; | ||
// We intentionally exclude `height` and `show` from the dependency array so | ||
// that we update them later via methods instead of by re-creating the | ||
// entire panel from scratch each time. | ||
// | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, [state, url, box, conversationId]); | ||
|
||
useEffect(() => { | ||
if (state.type === "loaded") { | ||
state.panel.setHeight(height); | ||
} | ||
}, [state, height]); | ||
|
||
useEffect(() => { | ||
if (state.type === "loaded") { | ||
if (show) { | ||
state.panel.show(); | ||
} else { | ||
state.panel.hide(); | ||
} | ||
Asha20 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
}, [state, show]); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. shouldn't this be |
||
|
||
return ( | ||
<> | ||
{state.type === "loaded" && | ||
createPortal(children, state.panel.window.document.body)} | ||
</> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) |
||
); | ||
} |
There was a problem hiding this comment.
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