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

Better add blocks #413

Merged
merged 24 commits into from
Sep 11, 2024
Merged

Better add blocks #413

merged 24 commits into from
Sep 11, 2024

Conversation

DivadNojnarg
Copy link
Collaborator

@DivadNojnarg DivadNojnarg commented Aug 26, 2024

This is similar to #325 but with 0 JavaScript (for easier customisation):

  • Add new category field to the registry: allows to sort blocks on the UI side. Default to uncategorized to avoid breaking change.
  • Add get_registry: renders as a dataframe for easier manipulation than available_blocks().
  • add_block_server is now a real module: it uses get_compatible_blocks that return a list of block compatible with the last stack block. This helper func can also be used without shiny. Block choices are rendered server side as a consequence of running get_compatible_blocks that listens to any block change and update the list of available blocks. The search/select input leverages shinyWidgets's virtual select.
  • On the UI side, add_block_ui is now a real UI module function.
  • Add remove_stack_server (code cleanup).
  • Fix tiny issue on the server side when removing a block from a stack: some reactive values were not updated correctly.

@DivadNojnarg DivadNojnarg requested a review from nbenn as a code owner August 26, 2024 16:03
@DivadNojnarg
Copy link
Collaborator Author

DivadNojnarg commented Aug 27, 2024

@nbenn This is good for review.

Other than that, the decision to drop the JavaScript from PR #325 is to have all the logic into one place for easier customisation, like we already discussed. If for instance, people are note happy with add_block_ui and add_block_server, they can write their own and that would work.

@DivadNojnarg
Copy link
Collaborator Author

DivadNojnarg commented Aug 27, 2024

@JohnCoene, @karmatarap and @nbenn preview of how it works:

How it looks for a single stack with 2 block packages loaded

Screen.Recording.2024-08-27.at.08.48.14.mov

For a workspace (display of the result block)

Screen.Recording.2024-08-27.at.09.59.52.mov

@JohnCoene
Copy link
Member

Is there any way to make the category not a breaking change?

@DivadNojnarg
Copy link
Collaborator Author

@JohnCoene Added a default "uncategorized" for category to register_block and register_blocks. This is what you would get without doing anything:

Screenshot 2024-09-02 at 13 55 57 Screenshot 2024-09-02 at 13 56 13

@JohnCoene
Copy link
Member

On an empty stack I see

image

Where I cannot add the codelist_block we created for the DPP.

https://github.com/BristolMyersSquibb/blockr.application/blob/f6fd05e5e6112ed7df8dd31561149baca529f64a/R/register.R#L12

@DivadNojnarg
Copy link
Collaborator Author

Either we have to explicitly pass category = data to the block to be considered as an entry point, or we need to add some logic that checks if the block input is NA, and assumes it can be seen as a data block by adding the data category on the fly. Maybe the second approach is too invasive and would have unwanted effects?

Can you try:

blockr::register_block(
    new_codelist_block,
    "Dummy data",
    "Create dummy data",
    input = NA_character_,
    output = "list",
    category = "data",
    package = pkgname,
    classes = c("codelist_block", "data_block")
  )

@JohnCoene
Copy link
Member

But how does the logic for what block can be added work? The block is valid since the input is NA, I would expect it to appear but maybe under "uncategorised".

@DivadNojnarg
Copy link
Collaborator Author

Until now, the logic was just looking at whether the block belonged to the data category. If un-categorized, even though the input was NA, nothing was made. I assume the assumption that when input is NA, the block is an entry point seems fair, so we don't need to rely on the category for this. I added some logic to handle this, which does this if the data block is labelled as un-categorized (or any other label):

Screenshot 2024-09-02 at 23 51 28

@JohnCoene
Copy link
Member

@DivadNojnarg Thank you, seems to work fine now. I will test some more. One thought is whether we need the "+" button, it feels like a needless click, shouldn't we just have to select the block we want (rather than select block then click "+")?

You may also want to test it with a lot of blocks to see performances (should be OK) and see how they display, it needs overflow-y to allow scroll.

@DivadNojnarg
Copy link
Collaborator Author

DivadNojnarg commented Sep 3, 2024

Thanks for the feedback. I've been experimenting a minimalistic approach:

  • Having a search/select element: the searchable items are populated server side as soon as the stack changes (adding or removing a block). Selecting the element adds it to the stack without needing an extra add button.
Screen.Recording.2024-09-03.at.15.47.12.mov

Advantages:

  • Search and select all in 1.
  • Virtual select, more performant with many blocks.
  • Less R code.
  • Works from the keyboard (search and press enter works).

Drawbacks:

  • Relies on the shinyWidget virtual select input (1 extra package dependency).
  • Description field is rather limited (no tooltip or popover).

What do you think @JohnCoene, @nbenn and @karmatarap ?

@DivadNojnarg
Copy link
Collaborator Author

@nbenn Good for final review ;)

@JohnCoene
Copy link
Member

I honestly prefer how it was before the search was added.

We don't use any of the space we have to display blocks: the select input is a small box and scrolling through is rather unwieldy.

Is there a way to make that fit the width and height of the offcanvas?

@DivadNojnarg
Copy link
Collaborator Author

DivadNojnarg commented Sep 6, 2024

Yes the width can be set to 100% of the offcanvas and we can display much more blocks on the vertical space. How many blocks would you like to see at once? 10? That's also configurable.

That would give this:

Screenshot 2024-09-06 at 13 34 27

@DivadNojnarg
Copy link
Collaborator Author

@JohnCoene. We can add custom renderers for the search items, to make them a bit nicer to look. This can also be useful to render thumbnails later, or any other registry information.

Screenshot 2024-09-09 at 09 47 41

Even extra tooltips and popovers.

Screenshot 2024-09-09 at 10 16 41

@JohnCoene
Copy link
Member

Should we automatically close the offcanvas after having selected a block?

Copy link
Collaborator

@nbenn nbenn left a comment

Choose a reason for hiding this comment

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

Lgtm. Some minor comments added.

Not for this PR, but something to start thinking about: How can we make it possible to add arbitrary further attributes to the registry? High level thoughts:

  • register_block(), new_block_descr(), ... would take ...
  • register_blockr_blocks() would run conditionally (on some blockr.auto_register_blocks option
  • the wrapping package can then provide its own .onLoad() hook to pass the desired attributes to register_block() et al.

What am I missing? @DivadNojnarg is there anything you might want to do differently here in anticipation of such an extension?

R/registry.R Outdated
constructor,
name,
description,
category = "uncategorized",
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there a reason for not moving "category" further down the arg list (given that we provide a default)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It was more a semantic reason, I thought category might be more important than the pkg name but than can move at the end.

R/registry.R Outdated
constructor,
name,
description,
category = "uncategorized",
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same comment as above.

attrs <- attributes(blk)
data.frame(
name = attrs[["name"]],
ctor = names(available_blocks())[[i]],
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do you need to expose a name for the constructor here? The names here are just the names used for constructor bindings in the block_registry environment. If a result_block is called result_block in this environment, this is to be treated more as a coincidence than a "rule".

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That's an artifact I needed for the accotdions and pills but we don't need anymore with the new virtual select. To cleanup.

tmp <- registry[is.na(registry$input), ]
# Drop result block if there are no other stacks
if (length(get_workspace_stacks()) <= 1) {
tmp <- tmp[tmp$ctor != "result_block", ]
Copy link
Collaborator

Choose a reason for hiding this comment

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

This does not look super robust to me: the name for this binding (within the block_registry env) is not part of any API really.

Also, we're hard-coding here some implicit assumption that has to be satisfied for some block class. Maybe this is beyond the scope for this PR, but what if a 3rd-party block implementation also would want such kind of special treatment? What kind of API could we offer to enable this? And if that sounds too complicated, is it worthwhile to do something (hacky) like this for the result block only?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We need a separate discussion on the registry. That goes beyond this PR.

R/stack.R Outdated
# Otherwise we compare the output of the last block
# and propose any of the block that have compatible input
ctor <- class(stack[[length(stack)]])[1]
last_blk_output <- registry[registry$ctor == ctor, "output"]
Copy link
Collaborator

Choose a reason for hiding this comment

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

Again, many implicit assumptions here: it is not guaranteed in any way that the (first entry) of a block class attribute is identical to the binding name of the block constructor in this lookup environment.

Why not compare the (full) class attribute to the classes registry "attribute"? That should be way more stable, no?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That would work. However, I find not optimal that we have to define the block classes within the constructor:

new_block(
    fields = fields,
    expr = quote(.(stack)),
    ...,
    class = c("result_block", "data_block")
  )

and that we have to do the same for the registry definition. But, again, we need to discuss about the registry separately.

@DivadNojnarg
Copy link
Collaborator Author

Should we automatically close the offcanvas after having selected a block?

I depends if you want to be able to add multiple blocks quickly by chaining the selection, which is possible right now. In that case, closing the offcanvas each time would be annoying.

@DivadNojnarg
Copy link
Collaborator Author

@nbenn Thanks for the comments. I fixed some issues, and also made the add_block stuff generic in case we want to support different options.

Copy link
Collaborator

@nbenn nbenn left a comment

Choose a reason for hiding this comment

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

Lgtm.

@DivadNojnarg DivadNojnarg merged commit f08f106 into main Sep 11, 2024
5 checks passed
@DivadNojnarg DivadNojnarg mentioned this pull request Sep 11, 2024
@ppssphysics ppssphysics mentioned this pull request Sep 13, 2024
@DivadNojnarg DivadNojnarg deleted the better-add-blocks branch November 18, 2024 15:39
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.

3 participants