Skip to content
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

Render JS sidebar #370

Merged
merged 8 commits into from
Mar 6, 2020
Merged

Render JS sidebar #370

merged 8 commits into from
Mar 6, 2020

Conversation

wbamberg
Copy link
Collaborator

@wbamberg wbamberg commented Mar 3, 2020

This updates the renderer so it can render the JS sidebar discussed in mdn/stumptown-content#302 and implemented on the content side in mdn/stumptown-content#313.

Changes:

  • tell stumptown-renderer what to do about class_constructor ingredients. This is not related to the sidebar change itself, but is needed for us to render JS class pages at all.

  • update the sidebar rendering code. Before this change it expect a sidebar to contain only three levels. But the JS sidebar can and does include more levels than that, so this change should support arbitrary levels. Of course nesting too deep would make the sidebar unusable, but I think the author of the sidebar should have the responsibility of avoiding that.

  • update the sidebar styling: make it sticky and scrollable. We don't really need this but I think it is much better like this. This sidebar can get quite long, and having to scroll the page to scroll the sidebar seems broken. Note that the Azure docs, which we're partly using as a model for this, does the same: https://docs.microsoft.com/en-us/cli/azure/cdn/custom-domain?view=azure-cli-latest, as does w3schools.

  • update to the latest stumptown-content

I'd like to scroll the sidebar to the section containing the current page, but I'm not sure where to put that code in the renderer. I'd be very happy to get a pointer for that, but I'd also be OK to do this in a follow up.

@wbamberg wbamberg requested a review from peterbe March 3, 2020 22:22
@wbamberg
Copy link
Collaborator Author

wbamberg commented Mar 4, 2020

Sigh. I always forget to sign commits for this repo. What's the best way for me to fix it at this stage? OK that seemed to work.

Copy link
Contributor

@peterbe peterbe left a comment

Choose a reason for hiding this comment

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

Mostly minor things of nitty/stylistics with JSX etc. In fact, to back up my arguments I tested it in a local branch. So I'll send that over as a PR to your PR.


function SidebarLeaflets({ node }) {
function SidebarLeaf({ parent }) {
const titleNode = <h3>{parent.title}</h3>;
Copy link
Contributor

Choose a reason for hiding this comment

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

Why make a variable of this? It's not changing and it's not used repeatedly. I think you should just put the <h3>{parent.title}</h3> directly into line 135. And if that's not possible, if this is a hack due to mutations or something, then it needs to be why-explained with a code comment.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

return (
<li
key={childNode.uri}
className={childNode.isActive ? "active" : undefined}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
className={childNode.isActive ? "active" : undefined}
className={childNode.isActive && "active"}

that way you don't need to mention undefined. This works the same way and I think it looks cleaner

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry if it seemed like I "attacked" your diff. Yes, I should have caught that in the original contribution when this was introduced. Sorry.

return (
<details open={node.open}>
<summary>
{node.uri ? <a href={node.uri}> {node.title}</a> : node.title}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
{node.uri ? <a href={node.uri}> {node.title}</a> : node.title}
{node.uri ? <Link to={node.uri}>{node.title}</Link> : node.title}

<summary>
{node.uri ? <a href={node.uri}> {node.title}</a> : node.title}
</summary>
<ol>{listItems}</ol>
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 it's a bit of an anti-pattern in JSX to create JSX-node variables instead of inlining them. I'd much rather this was:

      <ol>
        {node.content.map(childNode => {
          if (childNode.content) {
            return (
              <li key={childNode.title}>
                <SidebarLeaflets node={childNode} />
              </li>
            );
          } else {
            return (
              <li
                key={childNode.uri}
                className={childNode.isActive && "active"}
              >
                <Link to={childNode.uri}>{childNode.title}</Link>
              </li>
            );
          }
        })}
      </ol>

One of the motivations is that it immediately tells the code-reader that there's no reason why we're looping through it first. Otherwise, I might ask myself "Why did this need to be done first? Is it keeping a counter? What's the hack?"
The other reason it's better to not define jsx-node variables is the location of them. Now you have space between two different concerns and it's can cause the code-reader to have to jump around more than necessary:

const listItems = { CONCERN 1 }
return <div>
    { CONCERN 2 }
    { CONCERN 3 }
    { CONCERN 4 }
    { CONCERN 5 }
    { CONCERN 1 }
</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.

// walk through the tree, annotating its nodes:
// * `isActive` if a node contains the link to the current page
// * `open` if a node is on the path to the `isActive` node
function setOpenNodes(node, pathname) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Stylistically, I don't like that it's called setOpenNodes and then it's used as a getter function, ie. you care about its return value.

Creating functions on-the-fly isn't great for performance but the effect is unnoticeable at this scale. But I think it's worth keeping fast-by-design in mind if this is ever going to scale.
The only variable here is the location.pathname which gets copied even though it's available as a closure.

The purpose of the function is to mutate the object and continue doing so recursively. Right?

@peterbe
Copy link
Contributor

peterbe commented Mar 5, 2020

I'd like to scroll the sidebar to the section containing the current page, but I'm not sure where to put that code in the renderer. I'd be very happy to get a pointer for that, but I'd also be OK to do this in a follow up.

Can you explain that? Perhaps my screen is just too large to be able to see how that would work?
But I think the ultimate answer is to do something like this:

useEffect(() => {
  const activeElement = document.querySelector('.sidebar li.active');
  if (activeElement && activeElement.scrollIntoView) { activeElement.scrollIntoView('smooth') }
}, [location]);

kinda

@wbamberg
Copy link
Collaborator Author

wbamberg commented Mar 6, 2020

This adds the isActive and open calculation back into the SSR code. We could simplify it a bit - in particular I think it would be totally safe to replace https://github.com/mdn/stumptown-renderer/pull/370/files#diff-80f9f740bfb8aeab2cbf6e6ef4165b0dR44-R54 with just https://github.com/mdn/stumptown-renderer/pull/370/files#diff-80f9f740bfb8aeab2cbf6e6ef4165b0dR52-R53.

We could also perhaps rename open to isOpen, to be consistent.

@wbamberg
Copy link
Collaborator Author

wbamberg commented Mar 6, 2020

But I think the ultimate answer is to do something like this:

Yes, that looks exactly what I'm looking for! Haven't tried it yet though.

@peterbe
Copy link
Contributor

peterbe commented Mar 6, 2020

But I think the ultimate answer is to do something like this:

Yes, that looks exactly what I'm looking for! Haven't tried it yet though.

Let's tackle it later then. Should be pretty straight forward.

Copy link
Contributor

@peterbe peterbe left a comment

Choose a reason for hiding this comment

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

Let's do it!

@peterbe peterbe merged commit 162994c into master Mar 6, 2020
@wbamberg
Copy link
Collaborator Author

wbamberg commented Mar 6, 2020

Thanks Peter!

@peterbe peterbe deleted the js-sidebar branch July 2, 2020 15:30
fiji-flo pushed a commit that referenced this pull request Feb 14, 2022
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.

2 participants