✨ [bento][amp-sidebar] Add nav toolbar component on Preact side only#33244
✨ [bento][amp-sidebar] Add nav toolbar component on Preact side only#33244krdwan merged 15 commits intoampproject:masterfrom
Conversation
| toolbar, | ||
| 'toolbar-target': toolbarTarget, | ||
| children, | ||
| ...rest |
There was a problem hiding this comment.
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.
| <nav ref={ref} toolbar={toolbar} toolbar-target={toolbarTarget} {...rest}> | |
| <nav ref={ref} toolbar={toolbar} toolbarTarget={toolbarTarget} {...rest}> |
There was a problem hiding this comment.
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.
Why is it important for it to be rendered out in kebab case?
There was a problem hiding this comment.
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.
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.
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.
| // 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.
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.
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.
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.
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.
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.
also, in backdrop: shouldn't this be !important?
There was a problem hiding this comment.
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.
Why not apply this class to ContainWrapper?
There was a problem hiding this comment.
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.
Why is it important for it to be rendered out in kebab case?
| return; | ||
| } | ||
|
|
||
| const mediaQueryList = window.matchMedia(toolbar); |
There was a problem hiding this comment.
Can we rename toolbar to be more descriptive? It's a media query, right?
There was a problem hiding this comment.
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
MediaQueryListreturned from thewindow.matchMediacall 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: