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

feat: add examples page #942

Merged
merged 5 commits into from
Oct 23, 2024
Merged

Conversation

cdrage
Copy link
Contributor

@cdrage cdrage commented Oct 17, 2024

feat: add examples page

What does this PR do?

  • Minor edit to category (see json update)
  • Adds an example page (rendered portion) that shows all the examples we
    currently have
  • Pull and build image buttons
  • Reactively do in-progress for pulling

Screenshot / video of UI

Screen.Recording.2024-10-17.at.3.32.42.PM.mov

Screenshot 2024-10-17 at 3 34 15 PM
Screenshot 2024-10-17 at 3 34 25 PM

What issues does this PR fix or reference?

Closes #895

How to test this PR?

  1. Go to examples page
  2. Press pull image button
  3. Then build image button after it finishes

Signed-off-by: Charlie Drage [email protected]

@cdrage cdrage requested a review from a team as a code owner October 17, 2024 19:40
@cdrage cdrage requested review from dgolovin, jeffmaury and deboer-tim and removed request for a team October 17, 2024 19:40
@cdrage
Copy link
Contributor Author

cdrage commented Oct 17, 2024

Tests have been added! Ready for testing out and using 🚀

I have added some "follow up issues" to the topic here, especially lack of examples (only 3 "good" ones right now): #864 (comment)

const example = examples.find(example => example.image === msg.image);
if (example) {
example.state = 'pulled';
console.log(examples);
Copy link
Contributor

Choose a reason for hiding this comment

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

to be removed

// Function to update examples based on available images
function updateExamplesWithPulledImages() {
if (bootcAvailableImages) {
bootcAvailableImages.forEach(image => {
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 the linter is now recommending. for of pattern

bootcAvailableImages.forEach(image => {
// Only do it if there is a RepoTags
if (image.RepoTags) {
const [imageRepo, imageTag] = image.RepoTags[0].split(':');
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe use the optional ? pattern to reduce the if if if chaining

}

// Reactive statement to call the function each time bootcAvailableImages updates
$: if (bootcAvailableImages) {
Copy link
Contributor

Choose a reason for hiding this comment

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

might be a time to use $effect / svelte 5 runes

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can't as we use $$props.class in the Card (copied from AI lab and we try to keep consistent with them too) which we can then not use since it puts the svelte file in "runes mode"

Copy link
Contributor Author

@cdrage cdrage Oct 21, 2024

Choose a reason for hiding this comment

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

NVM, we don't actually use classes imported into it, so it's okay to switch to svelte 5 runes, updating now.

Could not get it to work reactively very well, even using $props as well as $effects. May just leave this like it is for now.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't see any reason why it would fail

<Card title={category.name} classes="{$$props.class} font-medium mt-4">
<div slot="content" class="w-full">
{#if examples.length === 0}
<div class="text-gray-400 mt-2">There is no example in this category.</div>
Copy link
Contributor

Choose a reason for hiding this comment

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

should ot use empty screen ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No need :) will always have examples!

Tried your suggestion though using empty screen but messed up formatting, so may be easier to just use this.

Copy link
Contributor

Choose a reason for hiding this comment

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

todo: if we always have examples, then do not add the if clause

import { render, fireEvent, screen } from '@testing-library/svelte';
import { expect, test, vi } from 'vitest';
import ExampleCard from './ExampleCard.svelte';
import { bootcClient } from '../api/client';
Copy link
Contributor

Choose a reason for hiding this comment

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

use absolute import

packages/frontend/src/lib/Card.svelte Show resolved Hide resolved
</script>

<a class="no-underline" href={href}>
<div class="{classes} rounded-md flex-nowrap overflow-hidden" role="region" aria-label={title ?? 'Card'}>
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 usually we're using another pattern than having an explicit classes parameter

Copy link
Contributor

Choose a reason for hiding this comment

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

We are doing this in a few places, often with different parameter names. In Svelte 5 you can do:

let {
  class: className = '',
}: {
  class?: string;
} = $props();

to make this cleaner, but since we ever only ever do classes="font-medium mt-4", maybe remove the parameter and put the styling in here?

packages/frontend/src/Examples.svelte Show resolved Hide resolved
@@ -38,7 +38,7 @@
"categories": [
{
"id": "fedora",
"name": "Fedora BootC Images"
"name": "Fedora Bootable Container Images"
Copy link
Contributor

Choose a reason for hiding this comment

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

could be a separate small PR

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ACK

Copy link
Contributor

@deboer-tim deboer-tim left a comment

Choose a reason for hiding this comment

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

Ditto to all the points @benoitf raised.

Worked fine when testing. The biggest gap to me was how to find details or instructions for each example. Why does it exist, what does it do, and how do I use it? The source goes to the repo root, then I have to guess at the folder and look at the readme. (and ok, Tailscale doesn't have anything)

Could we link directly to the folder, or ideally have a way to view the readme directly? Maybe needs a details page for each one?

Noted a few other minor usability issues:

  • The page appears, then we switch buttons from Pull to Build. Is the API fast enough that we could do this before opening the page, or can we delay adding the button until we know which it is? (might need to move Source button to Link on the left so it doesn't move).
  • Like on the dashboard, you don't know the image you're about to pull is huge. Would be nice to include the size on the card, or warn the user when they click on the button.
  • No pull task or progress. Unrelated to this issue so I opened up Usability of pulling images #944.

@cdrage
Copy link
Contributor Author

cdrage commented Oct 21, 2024

Ditto to all the points @benoitf raised.

Worked fine when testing. The biggest gap to me was how to find details or instructions for each example. Why does it exist, what does it do, and how do I use it? The source goes to the repo root, then I have to guess at the folder and look at the readme. (and ok, Tailscale doesn't have anything)

Could we link directly to the folder, or ideally have a way to view the readme directly? Maybe needs a details page for each one?

Noted a few other minor usability issues:

  • The page appears, then we switch buttons from Pull to Build. Is the API fast enough that we could do this before opening the page, or can we delay adding the button until we know which it is? (might need to move Source button to Link on the left so it doesn't move).
  • Like on the dashboard, you don't know the image you're about to pull is huge. Would be nice to include the size on the card, or warn the user when they click on the button.
  • No pull task or progress. Unrelated to this issue so I opened up Usability of pulling images #944.

We have https://gitlab.com/fedora/bootc/examples/-/issues/19 which I could work on next sprint with regards to descriptions of what each one does: #947

We have no way of knowing the size of the image before pulling, but we can at least add an approximate "size" on the description when pulling?

ACK on button delay / showing it correctly.

@cdrage cdrage force-pushed the add-examples-page branch 4 times, most recently from b1c339d to 9cef29a Compare October 21, 2024 14:59
@cdrage
Copy link
Contributor Author

cdrage commented Oct 21, 2024

@deboer-tim @benoitf

Updated the code based on the review:

  • Added size of the image (hardcoded in examples.json), makes it easier and less calls to check for size (honestly sizes will not change much, and this is just for showing examples)
  • Modified how we show the buttons on load, less flickering / changes.
  • Did not update to svelte5 (could not get it to work very well without causing extra changes in other svelte files)
  • Updated rest of minor code edits

UI update (shows size now):

Screenshot 2024-10-21 at 11 00 33 AM

@cdrage cdrage force-pushed the add-examples-page branch 2 times, most recently from d4a109a to 9a3da6f Compare October 21, 2024 20:45
@deboer-tim
Copy link
Contributor

I'm no longer getting Pull buttons when I don't have the image locally:
Screenshot 2024-10-21 at 5 03 30 PM

@cdrage cdrage force-pushed the add-examples-page branch 3 times, most recently from fffee57 to 8db094c Compare October 22, 2024 14:20
@cdrage
Copy link
Contributor Author

cdrage commented Oct 22, 2024

I'm no longer getting Pull buttons when I don't have the image locally: Screenshot 2024-10-21 at 5 03 30 PM

that's my mistake, I had messed up an if statement.

Latest commit I have also updated the styling, adding a "querying" button in case it takes a while to get the list of images, etc, as well as reduce the amount of flickering on initial load.

@deboer-tim deboer-tim added this to the 1.5 milestone Oct 22, 2024
@cdrage cdrage requested a review from benoitf October 22, 2024 16:10
Copy link
Contributor

@deboer-tim deboer-tim left a comment

Choose a reason for hiding this comment

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

Thanks for the changes, worked well in testing. We have some ideas for additional improvements open, but this delivers on the core requirement.

Some minor requests for change but otherwise this is good to go for me.

</script>

<a class="no-underline" href={href}>
<div class="{classes} rounded-md flex-nowrap overflow-hidden" role="region" aria-label={title ?? 'Card'}>
Copy link
Contributor

Choose a reason for hiding this comment

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

We are doing this in a few places, often with different parameter names. In Svelte 5 you can do:

let {
  class: className = '',
}: {
  class?: string;
} = $props();

to make this cleaner, but since we ever only ever do classes="font-medium mt-4", maybe remove the parameter and put the styling in here?

import { render, screen } from '@testing-library/svelte';
import { expect, test, vi } from 'vitest';
import type { Example, Category } from '/@shared/src/models/examples';
import ExamplesCard from './ExamplesCard.svelte';
Copy link
Contributor

Choose a reason for hiding this comment

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

component: ExamplesCard, test: ExampleCards. Can you rename one so that they match?

@@ -0,0 +1,56 @@
<script lang="ts">
import type { Example, Category } from '../../shared/src/models/examples';
Copy link
Contributor

Choose a reason for hiding this comment

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

Should really use /@shared/... here, and other places.

@cdrage
Copy link
Contributor Author

cdrage commented Oct 22, 2024

@deboer-tim @benoitf Updated based on the review comments and ready for another review 👍

@cdrage cdrage force-pushed the add-examples-page branch from a8eecb1 to 5647d96 Compare October 22, 2024 20:12
Copy link
Contributor

@deboer-tim deboer-tim left a comment

Choose a reason for hiding this comment

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

Thanks for the changes @cdrage, a nice little addition we can continue to expand on.


// Wait until example1 says Build image as it updates reactively
// same for example 2 but Pull image
waitFor(() => {
Copy link
Contributor

Choose a reason for hiding this comment

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

issue: missing await before waitFor

expect(buildImage1).toBeInTheDocument();
});

waitFor(() => {
Copy link
Contributor

Choose a reason for hiding this comment

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

issue: missing await before waitFor

Comment on lines 2 to 5
export let title: string | undefined = undefined;
export let description: string | undefined = undefined;

export let href: string | undefined = undefined;
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: use svelte5 runes

Suggested change
export let title: string | undefined = undefined;
export let description: string | undefined = undefined;
export let href: string | undefined = undefined;
interface Props {
title: string;
description: string;
href: string
}
let {title, description, href} : Props = $props();

export let href: string | undefined = undefined;
</script>

<a class="no-underline" href={href}>
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
<a class="no-underline" href={href}>
<a class="no-underline" {href}>

Comment on lines 30 to 41
// Function that takes something in MB and returns it in GB is more than 1GB
// in a nice string format
function formatStringSize(size: number): string {
if (size > 1000) {
return `${(size / 1000).toFixed(1)} GB`;
}
return `${size} MB`;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

todo: please use humansize library

https://www.npmjs.com/package/human-size

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I decided to use the filesize library https://www.npmjs.com/package/filesize which is used in podman desktop. the one you linked hasn't been updated in 10 years.

Copy link
Contributor

Choose a reason for hiding this comment

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

ah yes I think I took the wrong name but you figured it out
I mixed humanize-duration and this one

Comment on lines 40 to 42
$: {
example.state;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

issue: feels hacky

use svelte5 ($props, $effect, etc)

icon={faArrowDown}
aria-label="Pull image"
title="Pull image"
class="w-28"
Copy link
Contributor

Choose a reason for hiding this comment

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

question: do we need to set a size there ?

inProgress={pullInProgress}>Pull image</Button>
{:else}
<!-- Show a spinner / in progress for querying button instead if we are still loading information-->
<Button icon={faArrowDown} aria-label="Querying" title="Querying" class="w-28" inProgress={true}
Copy link
Contributor

Choose a reason for hiding this comment

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

question: same here, why do we need to set a size there ? (w-28)

}

// Reactive statement to call the function each time bootcAvailableImages updates
$: if (bootcAvailableImages) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't see any reason why it would fail

<Card title={category.name} classes="{$$props.class} font-medium mt-4">
<div slot="content" class="w-full">
{#if examples.length === 0}
<div class="text-gray-400 mt-2">There is no example in this category.</div>
Copy link
Contributor

Choose a reason for hiding this comment

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

todo: if we always have examples, then do not add the if clause

### What does this PR do?

* Minor edit to category (see json update)
* Adds an example page (rendered portion) that shows all the examples we
  currently have
* Pull and build image buttons
* Reactively do in-progress for pulling

### Screenshot / video of UI

<!-- If this PR is changing UI, please include
screenshots or screencasts showing the difference -->

### What issues does this PR fix or reference?

<!-- Include any related issues from Podman Desktop
repository (or from another issue tracker). -->

Closes podman-desktop#895

### How to test this PR?

<!-- Please explain steps to reproduce -->

1. Go to examples page
2. Press pull image button
3. Then build image button after it finishes

Signed-off-by: Charlie Drage <[email protected]>
Signed-off-by: Charlie Drage <[email protected]>
Signed-off-by: Charlie Drage <[email protected]>
Signed-off-by: Charlie Drage <[email protected]>
@cdrage cdrage force-pushed the add-examples-page branch 2 times, most recently from ec715a7 to ab53d85 Compare October 23, 2024 19:14
@cdrage
Copy link
Contributor Author

cdrage commented Oct 23, 2024

Updated @benoitf @benoitf

The more I use svelte5, the more I'm liking it! Much cleaner code.

I have updated it to svelte5 paradigms. I also decided to use https://www.npmjs.com/package/filesize library instead of humansize since it's the same on in Podman Desktop + is more updated.

Functionality of it working:

Screen.Recording.2024-10-23.at.3.17.35.PM.mov

@cdrage cdrage force-pushed the add-examples-page branch from ab53d85 to 2e21ee2 Compare October 23, 2024 19:16
@benoitf
Copy link
Contributor

benoitf commented Oct 23, 2024

yes the library name was wrongly spelled !

@benoitf
Copy link
Contributor

benoitf commented Oct 23, 2024

looks like there are some typechecking issue but approving

@cdrage cdrage force-pushed the add-examples-page branch 2 times, most recently from bfedf6d to 2e21ee2 Compare October 23, 2024 19:39
@cdrage
Copy link
Contributor Author

cdrage commented Oct 23, 2024

looks like there are some typechecking issue but approving

Thanks! Formatter had messed up and did not put {} around href. Added and updated now.

@cdrage cdrage enabled auto-merge (squash) October 23, 2024 19:39
@cdrage cdrage merged commit 1223e41 into podman-desktop:main Oct 23, 2024
10 checks passed
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.

Implementation: How to get examples from outside sources (bootc fedora examples)
3 participants