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

Rewrite the frontend #1856

Open
wants to merge 99 commits into
base: main
Choose a base branch
from
Open

Rewrite the frontend #1856

wants to merge 99 commits into from

Conversation

yuvipanda
Copy link
Collaborator

@yuvipanda yuvipanda commented May 29, 2024

/* If this file gets over 200 lines of code long (not counting docs / comments), start using a framework

This was the first line I wrote when I started writing the existing frontend, and of course that line is still there.

This PR cleans up and rewrites the entire frontend, to 'start using a framework'. There's no functional change to the UI itself, so it should be treated as a pure upgrade / refactor.

Demo

Main page

Screen.Recording.2024-05-15.at.6.03.12.PM.mov

Loading page

Screen.Recording.2024-05-23.at.8.21.36.AM.mov

Functional Status

The following functional pieces need to be completed:

  • Landing page link generator
  • Loading page
  • Actually launching servers correctly
  • Progress bars for launching
  • Stream logs
  • Favicon switching to indicate progress
  • About page
  • Extra scripts at the bottom (for google analytics / similar)
  • nbviewer in loading page
  • Help text in the main page
  • Banner on top
  • Donation button on top
  • Badge generator for markdown + rst
  • view raw logs
  • social cards
  • error page
  • fix copy button icon
  • Faithfully replicate layout
  • Fix markdown and rST icons

Technology changes

  • Upgrades to Bootstrap 5, latest version. Nicely matches JupyterHub upgrade in version 5.
  • Use react
  • Use JSDoc type annotations rather than typescript. It feels to me this gives me a good balance between the positives of optional type checking without the extra community investment needed for full typescript. This PR does switch the compiler we use from babel to tsc, but type checking is not enforced. We may choose to do so later, but not now.
  • Use react-router for URL parsing. This makes the BinderHub UI a SPA, which may be split into its own package separately in the future if so desired.
  • Move the _config endpoint to a refactored /api/repoproviders. This is with an eye on allowing us to implement Awesome bar/landing page redesign #844 eventually, as well as being able to implement the correct frontend bits in other users of the binderhub API (like https://github.com/yuvipanda/jupyterhub-fancy-profiles)
  • Deprecate direct google analytics functionality, where we embedded GA code into our source. Instead, extra_footer_scripts can continue to be used - that's what we use for matomo.

Functionality changes

  • Progress bar is now also shown in the loading page.
  • Learning from experience with nbgitpuller, the link generator now prefers outputting only urlpath - both when the user enters a file to open or a URL to open. This prevents the issue we had when we tried to change the default app that was going to open from classic notebook to lab, and broke a lot of people's stuff. By only outputting urlpath, URLs will always have this information encoded in them. The older filepath and labpath are still accepted as input, because Cool URIs don't break
  • Slightly better validation for the link generator, but most of this should be instead implemented as part of Awesome bar/landing page redesign #844

Maintenance changes

One primary goal here is to make the frontend safer to change, so it's less fragile and brittle.

  • Add some frontend tests. This should be much easier now thanks to all the componentization.
  • Provide thorough jsdoc inline documentation for everything
  • Refactor whatever is in binderhub-client package to make sure it only contains api-client related functionality - all UI stuff should be in a separate package.
  • Unify the current 'split' between the repo's root js/ and the binderhub/static/js sources of JS files. Pick up best practices from other Jupyterish projects for what to do here.

Timeline

My hope is to slowly but consistently work on this, and get it fully complete before end of June. I have also asked for some frontend review help from @batpad (either directly or via someone else), as he has significant experience in this kinda frontend work (even though he is less experienced in the JupyterHub community itself).

Fixes #774

@yuvipanda
Copy link
Collaborator Author

I've added functionality for the top banner here, and poked around to make sure that the existing banner can display well. It needs to be redone to use bootstrap 5 utility classes, but here it is:

        <div class="container-fluid position-relative" >
            <div>
                Thanks to <a href="https://www.ovh.com/">OVH</a>, <a href="https://notebooks.gesis.org">GESIS Notebooks</a> and <a href="https://curvenote.com">Curvenote</a> for supporting us! 🎉
                <br />
                mybinder.org has updated the base image to Ubuntu 22.04! See the <a href="https://repo2docker.readthedocs.io/en/latest/howto/breaking_changes.html">upgrade guide</a> for details.
            </div>

            <div class="top-0 end-0 position-absolute">
                <a class="btn" style="width:fit-content;height:fit-content;padding:10px;background-color:#e66581;color:white;font-weight:bold;"
                onmouseover="this.style.backgroundColor='#d15b75'" onmouseout="this.style.backgroundColor='#e66581'"
                href="https://numfocus.salsalabs.org/donate-to-binder" target="_blank">
                    🤍 Donate to mybinder.org!
                </a>
            </div>
        </div>

This works well!

@batpad
Copy link

batpad commented Jul 11, 2024

Have asked @oliverroick to do a review here since his React eyes are much, much sharper than mine.

Copy link
Contributor

@oliverroick oliverroick left a comment

Choose a reason for hiding this comment

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

From what I can tell, this looks pretty solid. I found two occasions where you probably don't need a useEffect hook and the launch logic could be improved, I think, although I don't have the full context to make a better suggestion.

binderhub/static/js/components/LinkGenerator.jsx Outdated Show resolved Hide resolved
binderhub/static/js/components/LinkGenerator.jsx Outdated Show resolved Hide resolved
Comment on lines 28 to 31
useEffect(() => {
// Start launching after the DOM has fully loaded
setTimeout(() => setIsLaunching(true), 1);
}, []);
Copy link
Contributor

Choose a reason for hiding this comment

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

This looks a bit flaky. Is there a specific element you're waiting for in the DOM tree? If so how is it created?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think I was trying to trigger this useEffect (https://github.com/jupyterhub/binderhub/pull/1856/files#diff-17ed39387f14113cf305cbfd7cc95014fc635d5988acbe542676dd87695c967cR185) and couldn't figure out how else to do it :( In general I think I was trying to trigger 'actions' in one component based on events in a different component, and without using reducers i think i'm hackily using these 'is' booleans to do so

binderhub/app.py Outdated Show resolved Hide resolved
binderhub/app.py Outdated Show resolved Hide resolved
class MainHandler(BaseHandler):
"""Main handler for requests"""
def initialize(self):
self.opengraph_title = "The Binder Project"
Copy link
Member

Choose a reason for hiding this comment

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

We should make this configurable in future, since not all BinderHubs are run by us.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

100%. Do you think that should be done as part of this PR?

Copy link
Member

Choose a reason for hiding this comment

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

No, that why I said in future 😄

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@manics i made it configurable :)

binderhub/repoproviders.py Show resolved Hide resolved
binderhub/repoproviders.py Outdated Show resolved Hide resolved
binderhub/static/js/App.jsx Outdated Show resolved Hide resolved
export const PAGE_CONFIG = window.pageConfig;

/**
* @typedef {object} RepoConfig
Copy link
Member

Choose a reason for hiding this comment

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

Is there any way we can verify type annotations if they're provided, to force people to keep them in sync with future code changes?

Copy link
Collaborator Author

@yuvipanda yuvipanda Dec 21, 2024

Choose a reason for hiding this comment

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

@manics soooo you can run npx tsc --noEmit --checkJs locally to have typescript validate these (it'll read JS, doesn't require typescript). However, the validation doesn't actually pass, mostly because our spec isn't fully complete. Given this PR has already taken so long, I want to not block this PR on that. Does that seem ok?

Copy link
Member

Choose a reason for hiding this comment

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

Sure, it's not a blocker. You're the only one who knows about it though, so can you make a note of it somewhere, either an issue, in the docs, or somewhere else?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@manics opened #1898!

binderhub/static/js/components/HowItWorks.jsx Show resolved Hide resolved
binderhub/static/js/components/LoadingIndicator.jsx Outdated Show resolved Hide resolved
binderhub/static/js/components/LoadingIndicator.jsx Outdated Show resolved Hide resolved
@manics
Copy link
Member

manics commented Dec 14, 2024

The new interface is a lot narrower than the existing one, and the form background is lighter which I find makes it difficult to distinguish the input fields from the text labels, especially since the input fields have placeholder text that looks very similar to the label text. Do you think you could change the styling to make the input fields more obvious?

image
image

@yuvipanda
Copy link
Collaborator Author

yuvipanda commented Dec 14, 2024

@manics the color contrast changes are the result of upgrading bootstrap. I do agree with you though, it could be better. I'll see what I can do.

nvm, i was wrong

@yuvipanda
Copy link
Collaborator Author

Screenshot 2024-12-14 at 6 02 16 PM

@manics this is how it looks now. what do you think?

While the form controls didn't flag in the WCAG Contrast Checker some other things did. I'll poke at those.

Copy link
Member

@manics manics left a comment

Choose a reason for hiding this comment

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

Looks a lot clearer!

binderhub/static/js/App.jsx Outdated Show resolved Hide resolved
binderhub/static/js/pages/HomePage.jsx Outdated Show resolved Hide resolved
binderhub/static/js/components/LinkGenerator.jsx Outdated Show resolved Hide resolved
@yuvipanda
Copy link
Collaborator Author

After working on jupyterhub/repo2docker#1393, I was able to get a better handle on what we actually support with hydroshare in binderhub and clarify that (only ids, not URLs). No change from before this PR, just clearer.

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.

Long term planning for the Frontend
7 participants