-
Notifications
You must be signed in to change notification settings - Fork 3.9k
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
✨ [bento][amp-sidebar] Add nav toolbar component on Preact side only #33244
Conversation
toolbar, | ||
'toolbar-target': toolbarTarget, | ||
children, | ||
...rest |
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.
Will add an as
prop when I do the AMP mode
PR
896e023
to
f62f164
Compare
f9bb691
to
170c623
Compare
|
||
return ( | ||
<> | ||
<nav ref={ref} toolbar={toolbar} toolbar-target={toolbarTarget} {...rest}> |
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.
<nav ref={ref} toolbar={toolbar} toolbar-target={toolbarTarget} {...rest}> | |
<nav ref={ref} toolbar={toolbar} toolbarTarget={toolbarTarget} {...rest}> |
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.
So I've updated the prop value from toolbar-target
to toolbarTarget
. This is the rendered output and should stay as toolbar-target
(this is the actual HTML attribute) that gets rendered out. (Should be in kebab case).
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.
Why is it important for it to be rendered out in kebab case?
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.
Html attributes are in kebab case? So the output would look like this:
<nav toolbar="(max-width: 500px)" toolbar-target="toolbar-target">...
whereas if we use camel case here it would output like this:
<nav toolbar="(max-width: 500px)" toolbartarget="toolbar-target">...
Also the first one is aligned with what our 0.1
code would look like :)
|
||
return ( | ||
<main> | ||
<SidebarWithActions | ||
side={side} | ||
style={{color: foregroundColor, backgroundColor}} | ||
backdropStyle={{backgroundColor: backdropColor}} | ||
> | ||
<SidebarToolbar toolbar={toolbarMedia} toolbar-target="toolbar-target"> | ||
<ul> | ||
<li>Toolbar Item 1</li> | ||
<li>Toolbar Item 2</li> | ||
</ul> | ||
</SidebarToolbar> | ||
</SidebarWithActions> | ||
<div id="toolbar-target"></div> | ||
</main> | ||
); |
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.
This is the toolbarTarget
as ref
idea:
return ( | |
<main> | |
<SidebarWithActions | |
side={side} | |
style={{color: foregroundColor, backgroundColor}} | |
backdropStyle={{backgroundColor: backdropColor}} | |
> | |
<SidebarToolbar toolbar={toolbarMedia} toolbar-target="toolbar-target"> | |
<ul> | |
<li>Toolbar Item 1</li> | |
<li>Toolbar Item 2</li> | |
</ul> | |
</SidebarToolbar> | |
</SidebarWithActions> | |
<div id="toolbar-target"></div> | |
</main> | |
); | |
const targetRef = useRef(null); | |
return ( | |
<main> | |
<SidebarWithActions | |
side={side} | |
style={{color: foregroundColor, backgroundColor}} | |
backdropStyle={{backgroundColor: backdropColor}} | |
> | |
<SidebarToolbar toolbar={toolbarMedia} toolbarTarget={targetRef}> | |
<ul> | |
<li>Toolbar Item 1</li> | |
<li>Toolbar Item 2</li> | |
</ul> | |
</SidebarToolbar> | |
</SidebarWithActions> | |
<div ref={targetRef}></div> | |
</main> | |
); |
Then your createPortal
line would look like:
{matches && toolbarTarget.current && createPortal(children, toolbarTarget.current)}
I'm actually not sure if it's advisable to pass a ref like this, and usually indicates that some outer context may be helpful to manage things more explicitly... But the current approach of querying the document doesn't allow for updating the portalled parent i.e. if the same id gets reassigned to a different element between renders.
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 see, I'm open to it either way. Let's see if anyone else feels strongly about it. If not, we can table it for now and explore further as a follow up.
// Must also check mounted as of #33244. This is because previously | ||
// sidebarElement and backdropElement would not be rendered when | ||
// mounted is false. This is no longer the case. | ||
if (!mounted || !sidebarElement || !backdropElement || !side) { | ||
return; |
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.
Added a check for mounted
here. Per the comment, we don't want animations running in an unmounted state. Previously checking sidebarElement
and backdropElement
refs were enough since they were never rendered in unmounted
state.
const isOpened = (sidebarElement) => { | ||
return !sidebarElement.className.includes('unmounted'); | ||
}; | ||
|
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.
How the unit tests check whether the sidebar is opened or not must be updated throughout the test.
className={objstr({ | ||
[backdropClassName]: backdropClassName, | ||
[classes.backdrop]: true, | ||
[classes.defaultBackdropStyles]: true, | ||
})} |
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.
nit: Place constant true
values at the beginning
className={objstr({ | |
[backdropClassName]: backdropClassName, | |
[classes.backdrop]: true, | |
[classes.defaultBackdropStyles]: true, | |
})} | |
className={objstr({ | |
[classes.backdrop]: true, | |
[classes.defaultBackdropStyles]: true, | |
[backdropClassName]: backdropClassName, | |
})} |
Also: if backdrop
and defaultBackdropStyles
don't occur independently, merge them.
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.
Thanks! Updated to put true
values at the top. Regarding the backdrop
and defaultBackdropStyles
, the reason these are split out is because the default
ones are meant to be overridable by the user, whereas the backdrop
class has styles that are built in to the sidebar. I sort of like having them split out to emphasize the distinction, but let me know if you feel strongly about it!
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 don't know if our JSS transform supports this (try it?) but how about exposing a single classname, but split them in definition anyhow?
// these can overridden
const defaultBackdropStyles = {
...
};
const backdrop = {
// x: "... !important",
...defaultBackdropStyles,
};
export {..., backdrop, ...};
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.
also, in backdrop
: shouldn't this be !important
?
zIndex: 2147483646, |
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.
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 went ahead and merged for now, but let me know if you think there should be any other changes and I can follow up!
70027f5
to
0906f71
Compare
</div> | ||
</> | ||
) | ||
<div className={objstr({[classes.unmounted]: !mounted})} part="wrapper"> |
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.
Why not apply this class to ContainWrapper
?
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.
ContainWrapper
does not contain the backdrop so I wanted the class to apply to everything. Is there something else I'm missing?
|
||
return ( | ||
<> | ||
<nav ref={ref} toolbar={toolbar} toolbar-target={toolbarTarget} {...rest}> |
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.
Why is it important for it to be rendered out in kebab case?
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.
LGTM
return; | ||
} | ||
|
||
const mediaQueryList = window.matchMedia(toolbar); |
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.
Can we rename toolbar
to be more descriptive? It's a media query, right?
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.
That works, I've updated it to mediaQueryProp
internally (within the component). I think we should keep the prop name toolbar
externally since it matches with the existing API for 0.1
. (In 0.1 the user would specify a toolbar as such:)
<nav toolbar="(media-query)" toolbar-target="elementID">
343229a
to
8602490
Compare
3/17
Updated the design to listen on the
MediaQueryList
returned from thewindow.matchMedia
call instead of listening on the window (per review comments).Amp-sidebar Tracker: #31366
Preact side of nav toolbar feature as discussed in Bento standup on 3/11
Design doc: Design Doc
To do: