Skip to content

WIP: Installation widget #212

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

Closed
wants to merge 15 commits into from
Closed

Conversation

MarsBarLee
Copy link
Contributor

Fixes gh-94

Made an interactive installation widget, similar to PyTorch's widget in their website. It's placed after the 'Key Features' section.

I tried two designs for the buttons: circular (like the buttons for the interactive shell) and rounded edges (like the Key Features and Case Studies section).

I currently prefer the rounded edges, but what do you guys think?
image

For the hover color, I prefer the light blue, since it's a stronger contrast than the grey.

What do you all think?
image

@rgommers will put in the final, proper commands, since I'm currently using placeholder commands.

For squashing the commits, should I squash the commits from master that aren't about the installation widget, into a single commit for the installation widget? I pulled those changes from the master into my feature branch in the middle of working on it.

@stefanv
Copy link
Contributor

stefanv commented Apr 6, 2020

This looks great, @MarsBarLee! (I personally like the square buttons, but either seems fine.) Some comments:

  • Bootstrap is quite heavy to pull in just for one widget; maybe easier to just lay this one out by hand, with some custom css?
  • Instead of using strings inside of the Javascript, it would be good to have these defined externally: either in the config file, or in external files. If you use variables inside the Hugo config, you can use Hugo to compile the Javascript file to fill out relevant variables.

@MarsBarLee
Copy link
Contributor Author

MarsBarLee commented Apr 6, 2020

@stefanv Thanks for the suggestions! I'll try without Bootstrap then- I used it mostly for the grid
system, but even then I had some trouble with column gutters vs my own CSS spacing.

To clarify, are you suggesting to have the strings in the config.yaml and pull them in the JS file with template partials?

@rgommers
Copy link
Member

rgommers commented Apr 6, 2020

Looks nice @MarsBarLee! My vote would go to round buttons and light blue hover color.

@stefanv
Copy link
Contributor

stefanv commented Apr 6, 2020

To clarify, are you suggesting to have the strings in the config.yaml and pull them in the JS file with template partials?

Right, so they can easily be customized without touching the Javascript.

@joelachance
Copy link
Collaborator

@MarsBarLee I like the rounded buttons and light blue as well, but any version of what you have looks great and would work well.

I agree about not pulling in bootstrap for this one, I think we can get a grid without it (although it's quite handy to have!). I also agree with @stefanv 's second point about putting values in the config. It's not exactly like a template literal, Hugo has its own way of parsing data from the config:

config.yaml

params:
  hero:
    title: NumPy

some-partial.html

{{- $hero       := .Site.Params.hero }}
{{- $title      := index $hero "title" }}

<div>{{ $title }}</div>

Hope this helps.

@joelachance
Copy link
Collaborator

@MarsBarLee , something like this might help kickstart writing some columns. There's a lot of posts out there like this so you don't have to build your own (if you don't want to): https://stackoverflow.com/questions/55374456/how-to-use-grid-system-without-bootstrap

@MarsBarLee
Copy link
Contributor Author

@stefanv @joelachance

I finished doing the CSS without Bootstrap but I'm hitting a block with moving variables from the JS file to the Hugo config.yaml file.

This is an example of how the config.yaml file looks now

  installationWidget:
    setup: 
    - text: linux_py36_conda
    - text: linux_py37_conda
    command: 
    - text: conda install linux 3.6
    - text: conda install linux 3.7
# ... repeat for rest of commands

This is an example of installationWidget.html

{{- $installationWidget := .Site.Params.installationWidget }}
{{- $setup   := index $installationWidget "setup" }}
{{- $command    := index $installationWidget "command" }}
...
{{ if eq $setup.text "linux_py36_conda" }}
	<div><h1>{{ $command.text }}</h1></div>
{{ end }}
{{ if eq $setup.text "linux_py37_conda" }}
	<div><h1>{{ $command.text }}</h1></div>
{{ end }}
// ... repeat for rest of commands

The build runs, but it seems like while I moved the commands/content from the installationWidget.js into config.yaml, I'm also putting the logic into the HTML file rather than the JS file. It's also not updating the commands based on the buttons the user clicks.

Can I track live changes in the DOM using the Hugo templating in the HTML file?

In my previous version of installationWidget.js, I used getElementsByClassName and getElementsByID to get live HTMLCollections and live Nodes from the HTML to update the command based on the buttons the user selects via switch cases. However, I'm not sure how I can get track live changes in the DOM using the Hugo templates.

I don't think I can use Hugo templates in installationWidget.js to retrieve the commands from the config.yaml file either.

I also looked into declaring JS file as a variable in the front of the HTML, and call upon the JS script in the Hugo templates.

My build in my initial PR works, so I'm considering keeping it and adding the CSS changes.

Note that there's more work to do after merging; this is the best
content we have now, but there's a lot that's outdated or not
NumPy-specific enough.  Also we need to say something about how to
build depending on where Python itself comes from (e.g. conda is
different).
@rgommers
Copy link
Member

rgommers commented Apr 8, 2020

@MarsBarLee one thing I noticed is that the widget is on the front page now:

image

It should live on the Install page instead, replacing this part:

image

Also, it's really wide for me. I feel like it anyway would look better if the spacing between the buttons was much less, perhaps 50% of what it is now in both horizontal and vertical dimensions.

@joelachance
Copy link
Collaborator

@MarsBarLee, first, I cloned your branch and am noticing how much work you've put into this. So, thank you! This is nontrivial, kudos for all of the work.

Next, I'm going to eat my words since I don't think the simplest thing to do is put the commands in the config. We 100% need some Javascript to complete this task, so I think it's not unreasonable to store those values in an object. I'll also note here that we do have jQuery on the page and we can take advantage of that. It'll keep things a bit easier to read.

I wasn't quite sure how to offer an overall improvement based on what we have, but the feeling I'm getting is that we can make things a bit more concise. Below is an over simplified solution that might spur some inspiration (please don't feel that we need to go in this direction if you don't want to). You can copy that into an html file and open it in a browser.

I think putting things in objects vs. a switch statement might be better and a bit more readable, while reducing our JS footprint. JS is expensive to ship, so less is definitely more. If you're interested, this post offers some really good info on shipping JS these days: https://v8.dev/blog/cost-of-javascript-2019

After implementation, I do think we can comb over some styling quickly to fix some small things. +1 on @rgommers comment, i noticed when 'source' is selected the buttons move, and I think mobile is worth taking a quick look at.

<html>
    <button id="macOS" value="os:macOS" onclick="select(event)">macOS</button>
    <button id="linux" value="os:linux" onclick="select(event)">Linux</button>

    <div id="instructions">none</div>

    <script>
        // Default selections
        const selections = {
            os: 'macOS',
            package: 'conda',
            python: '37'
        }

        const commands = {
            'macOS:conda:37': 'pip install numpy',
            'linux:conda:37': 'apt-get install numpy!'
        }

        // Gets our buttons so we can collect their values
        const mac = document.getElementById('macOS');
        const linux = document.getElementById('linux');
        const instructions = document.getElementById('instructions');

        // Adds an event listener when a user clicks.
        mac.addEventListener('click', select);
        linux.addEventListener('click', select);

        // Gets the value from the button clicked, updates our selections
        function select(e) {
            e.preventDefault();
            // Gets our key and value from the clicked element 'value' attribute.
            const [os, value] = e.target.value.split(':');
            // Assigns value to our selections.
            selections[os] = value;

            // At this point, we have updated selections, and now we can update our instructions.
            // Below combines our selections into a string, fetches our command, assigns to our 'instructions' element.
            const combinedSelections = `${selections.os}:${selections.package}:${selections.python}`
            instructions.textContent = commands[combinedSelections];
        }
    </script>
</html>

Let me know what you think!

@InessaPawson
Copy link
Member

The square buttons would be my preference. I like the choice of light blue for the hover.
Also, I suggest reducing the spacing between the widget buttons, positioning them similar to the PyTorch version.
Impressive job overall, @MarsBarLee!

@stefanv
Copy link
Contributor

stefanv commented Apr 9, 2020

@MarsBarLee @joelachance I was wondering how to inject the installation parameters into the widget. I can think of two ways: 1) compile them into the template as <div data-mac-conda-37="{{ .Site.Where.That.String.Lives }}"></div> or 2) compile a JS file as a resource and define the variables there. Then include the file into the template.

Thoughts?

@joelachance
Copy link
Collaborator

@stefanv I think we may need to go with the second if we go the templating route. We'll need to handle what installation instruction is rendered using JS, so we'll need some definition inside our JS.

There is potential of a readability trade-off between using more dynamic logic vs. defining everything in the JS. I can see both ways, and i say worth a shot! @MarsBarLee ?

@stefanv
Copy link
Contributor

stefanv commented Apr 10, 2020

I've made a proof of concept PR here: MarsBarLee#1 (passes part of the config through to JavaScript in installation widget file).

@MarsBarLee
Copy link
Contributor Author

MarsBarLee commented Apr 10, 2020

@joelachance I'm making my way through the guide you linked on shipping+cost of JS. I thought about storing the commands as an object's value a well, but I initially went with the switch since it was faster to implement. But when weighing against the JS footprint, I see how an object is better for the big picture.

@stefanv I knew a little about Hugo pipes, but didn't use or think of ExecuteAsTemplate before. I'll do more reading on it and expand on your proof of concept.

Thanks for the feedback on styling everyone!

@MarsBarLee
Copy link
Contributor Author

Hi @stefanv, I took some time to read up on Hugo resources, pipes and bundling. I want to check in with you if my understanding is correct as I move into implementing the changes.

The left side is based on the Hugo docs you linked earlier to 'compile a JS file as a resource'. The right side is how I understand your proof of concept.
Is there anything missing or misunderstood in my understanding?

ask2

I'm now working integrating your proof of concept into my branch, and fiddling around to see what I can do with the nil error message.

@stefanv
Copy link
Contributor

stefanv commented Apr 28, 2020

@MarsBarLee Could you check whether my PR branch executes on your system?

What is happening here:

  1. The installation options (from the yaml file) are converted to a JSON string and injected into the JavaScript file, where it is interpreted and saved as a variable.

  2. To make this possible, the JavaScript file, which uses templating, has to be compiled first. To do this, you:

    a. Load the file via resources.Get
    b. Execute the file as a template via resources.ExecuteAsTemplate

@melissawm
Copy link
Member

Hi @MarsBarLee ! Is this something you are still hoping to work on, or can someone take over this PR?

@InessaPawson
Copy link
Member

@MarsBarLee I'll close this PR since it has been inactive for 3+ years. Feel free to reopen it if you'd like to continue working on the widget.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

#Content-Review: All typos will be fixed against this ticket-issue number.
6 participants