budibase icon indicating copy to clipboard operation
budibase copied to clipboard

Block ejection

Open aptkingston opened this issue 2 years ago • 3 comments

This PR adds the ability to eject blocks. "Ejecting" a block means breaking it down into it's core components, replacing the block.

TLDR:

  • Blocks can be ejected into their core components
  • All blocks support this
  • All settings, bindings, styles and custom CSS are properly supported. Everything should be identical after ejection and function fully.
  • Conditional UI rules on blocks are not copied and will be dropped when ejecting
  • All blocks have been updated to be "clean" - they only use our components with our settings and styles, i.e. they are fully replicable without any custom CSS that is impossible to inject via the builder

Block ejection

Here's how a table block looks before and after being ejected. Before: image

After: image

Blocks are ejected by using the component dropdown menu, or by the corresponding shortcut of ctrl/cmd+e: image

Blocks can also be ejected via a dedicated button in the settings panel: image

Block ejection required confirmation: image

Other stuff

Block ejection is quite complicated, as it relies on building up our JSON component structure inside the client library, at runtime, based on the current settings. Since it needs to be done inside the client library, we needed a way to be able to pass events from the builder to the app preview in a different way than normal. The builder already passes events to the app preview to tell it what to display, but this same approach wouldn't work for block ejection as we don't want the preview to initialise itself again, which it would do if we passed additional metadata into our current event.

To solve this, there's a new framework in place to allow the builder to send arbitrary events to the client preview. This can be called by store.actions.preview.sendEvent(name: string, payload: any).

info fields in the manifest are now displayed beside their relevant settings rather than at the very bottom of component settings. This will help resolve multiple cases of people not seeing this important information: image

Fixed a few bugs concerning the target of component actions when using keyboard shortcuts, and tidied up and refactored the component settings panel.

Technical implementation

Block ejection is asynchronous and depends on message passing betweent he builder and iframe app preview. The overall flow for block ejection is as follows:

  • When the app preview renders, all blocks are registered into a central block store in the client library, so we can interact with them at runtime via a limited API they provide
  • The builder passes a message to the preview requesting block ejection for a certain block ID
  • The client library handles this messages - getting the block instance from the block store for the specified ID, and calling eject on it, which is currently the only function in the exposed block API
  • The block component now serialises its entire component tree into our JSON structure, then sends a message back to the builder saying ejection has been completed, passing back the ID of the block and the new ejected definition
  • The builder completes the process by replacing the block with the ejected definition, making sure any block child components are still inserted in the correct place

Serialising blocks at runtime was not too difficult as this was already how blocks function - we create a fake JSON structure for each component inside the block, allowing the client library to treat it as any other component. The changes needed for ejection primarily involved being able to properly assemble every component back into the correct place and order, since we have arbitrary depths of component trees and order is crucial.

aptkingston avatar Aug 24 '22 09:08 aptkingston

Codecov Report

Merging #7427 (3bfc911) into develop (8a5545f) will not change coverage. The diff coverage is n/a.

@@           Coverage Diff            @@
##           develop    #7427   +/-   ##
========================================
  Coverage    67.44%   67.44%           
========================================
  Files          125      125           
  Lines         4211     4211           
  Branches       669      669           
========================================
  Hits          2840     2840           
  Misses         961      961           
  Partials       410      410           

:mega: We’re building smart automated test selection to slash your CI/CD build times. Learn more

codecov-commenter avatar Aug 24 '22 09:08 codecov-commenter

This is super cool 🤩 Looking forward to using blocks more often now that I don't have to worry about lock in!

Just wondering if the method of ejecting is clear enough to the user. Feels a little bit hidden within the component menu. Should there be an 'Eject button' more visible within the block settings?

melohagan avatar Aug 24 '22 09:08 melohagan

Updated to add an "eject block" button to the settings panel for all blocks: image

This button comes with a confirmation prompt (also appears when ejecting via context menu): image

aptkingston avatar Aug 30 '22 13:08 aptkingston