Feature: Added renderTo prop instead of appendToBody#45
Conversation
…MLElement and updated test modals
🦋 Changeset detectedLatest commit: 6610bbf The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
andrewrubin
left a comment
There was a problem hiding this comment.
Thanks @pravton!
Couple comments from me here, and some ideas which might help to solve the hydration issues we were discussing.
Let me know if any of my feedback isn't clear 🙏
| const getModalRoot = (renderTo?: string | HTMLElement) => { | ||
| if (typeof window !== "undefined") { | ||
| if (typeof renderTo === "string") { | ||
| return document.querySelector(renderTo) || document.body |
There was a problem hiding this comment.
I think we need to add some error handling here. If a user is passing an invalid selector, they won't know why their modal is being appended to the body. Maybe we just error instead of falling back to document.body?
There was a problem hiding this comment.
Oh yes, I guess an error would be more helpful than appending body, I will add some error handling here.
There was a problem hiding this comment.
@pravton I think there may've been a misunderstanding here based on what I'm seeing from the example.
If you tweak the renderTo examples where there's a string value passed in, the modal still opens when I believe what @andrewrubin meant is that if the element is not found, no modal should be opened.
There was a problem hiding this comment.
Thanks Harvey! The intension was to keep the render in-place regardless since we don't use typescript in most cases, but based on Marlon's suggestion, we should be good once we remove the string/query selector.
|
|
||
| export function Modal({ renderTo, className, ...props }: ModalProps) { | ||
| const modalRoot = getModalRoot(renderTo) | ||
| const appendToBody = modalRoot ? modalRoot === document.body : false |
There was a problem hiding this comment.
I know modalRoot does a check, but document.body will always be undefined on SSR here. Could be part of the hydration issue you were describing.
I think we could also rename this appendToBody variable to be a little more indicative of what its purpose is (being used to determine whether the body locks scrolling)
There was a problem hiding this comment.
Hmm yes, but when I tried to return null based on the client side logic, based on this https://nextjs.org/docs/messages/react-hydration-error because "there was a difference between the React tree that was pre-rendered from the server". So we should be fine if nothing is returned based on client side logic.
and that make sense, I will rename the appendToBody..
| } | ||
|
|
||
| export function Modal({ appendToBody, className, ...props }: ModalProps) { | ||
| const getModalRoot = (renderTo?: string | HTMLElement) => { |
There was a problem hiding this comment.
It might be worth memoizing this value as modalRoot within the component itself, rather than declaring a helper function outside.
There was a problem hiding this comment.
Fore sure, i will memorize this function within the component.
…pdated variable name
|
@andrewrubin I have updated based on your suggestions, let me know if this works or if anything needs to be addressed or any other suggestions. |
| const modalRoot = useMemo(() => { | ||
| if (!isClient) return null | ||
|
|
||
| if (typeof renderTo === "string") { |
There was a problem hiding this comment.
@andrewrubin @pravton Tested locally and things look fine, but this could potentially be an issue in the event that there's multiple elements on the page with the same selector (in this case, classname); unless we're looking to only have at max, 3 modals - one for each type (body, selector, hash) - emptying and populating content on open/close.
Thinking that we could have the string value query for an element by id or by data-modal-id - working on an example atm, but lmkwyt.
There was a problem hiding this comment.
I think we should instead remove the option to pass a string and only accept an HTMLElement. It makes this more intention as the user will have to handle nullish values.
It also feel more reacty that way as you can use focus more on using refs.
There was a problem hiding this comment.
Hmm, yeah that makes sense! I will refactor this to only accept HTML element and I agree, feels more reacty this way..
| const getModalRoot = (renderTo?: string | HTMLElement) => { | ||
| if (typeof window !== "undefined") { | ||
| if (typeof renderTo === "string") { | ||
| return document.querySelector(renderTo) || document.body |
There was a problem hiding this comment.
@pravton I think there may've been a misunderstanding here based on what I'm seeing from the example.
If you tweak the renderTo examples where there's a string value passed in, the modal still opens when I believe what @andrewrubin meant is that if the element is not found, no modal should be opened.
There was a problem hiding this comment.
I think we should NOT default to document.body and make that parameter mandatory instead of optional. Rendering in place by default.
Also following my other comment, we shouldn't accept a string for this I think, just an HTMLElement.
We should also make positon: fixed default regardless and let the user handle a differerent position strategy if they want.
Rendering in place with position: fixed covers 90% of the use cases and avoids all these SSR issues.
These changes force this prop to be more intentional overall.
…nd only accept HTMLElement
|
@andrewrubin @marlonmarcello @rvno So I made few changes based on the comments above,
|
andrewrubin
left a comment
There was a problem hiding this comment.
These updates are great @pravton 👏 I think it solves all the issues nicely. Couple final ideas/questions from me.
There was a problem hiding this comment.
I think it would be beneficial to show an example in main.tsx of appending a modal to the body, thoughts?
There was a problem hiding this comment.
For sure, yeah that would be helpful! I will add an example.
|
@andrewrubin So I have added an example on the main appending the modal to body and updated the text as I see fit, but let me know if that needs to be updated or the example. |
|
Great work @pravton ! |
|
okay! Looking great @pravton Next step here is to bump the version using the It looks like the docs for that are here: https://github.com/changesets/changesets/blob/main/docs/prereleases.md, but I'm tagging @marlonmarcello to see if he has some quick tips for us on this. |
|
Thanks for the version bump @pravton Let's keep this open for now, we'll just need to publish a beta version before merging this, so that we can test. I think the easiest option is to do that locally. Will make a note to do this when I have a moment! |
There was a problem hiding this comment.
@pravton , sorry mate, I didn't realize this PR was to a release candidate and not main. In that case, we're good to merge this!
After, would you mind making a PR to main, for the release candidate? I will do the beta release from there
Description
This PR addresses issue #20 by removing the appendToBody prop and introducing the renderTo prop for the Modal component.
Solution
Additional Notes
This is a breaking change.
Screenshots
demo.mp4