Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Proposal

Proposer

Notes

Create an Open edX specific UI Plugin Framework in React

Braden MacDonald

Designing and implementing the plugin framework was surprisingly easy, and it’s been working well ever since. I put the design details and code on my blog: Creating a UI Plugin Framework in React 3. If anyone has questions about what I learned from that experience, I’m happy to answer them. But the main point I’d like to make is that it doesn’t have to be complicated, and in my experience it can be done quickly and easily, even for relatively complex applications (I had to support white labelling, runtime config, plugin-defined URLs, theming via plugins, etc.).

In fact, if you end up with an approach like I took for that project, rolling it out at first is [almost] as simple as putting <DefaultUiSlot> ... </DefaultUiSlot> around the major elements of your user interface. Ta-da, it’s customization-ready.

Design an approach similar to Docosaurus’s Swizzling mechanism

Braden MacDonald

Another well-known approach to UI plugins is Swizzling 1, used by Docusaurus themes to customize their React UI. It has some overlap with my approach, but some major differences. The ways in which you can modify components are more limited, but you can modify virtually any component without having to declare it as a “slot”.

Extend a POC developed by David Joy (Deactivated) at 2U that provides iframe based “slots” for frontend customization

Jason Wesson

I’ll add that another existing PR that was started by @djoy and that I’m working to finish is for enabling a React-based iFrame plugin that lives in openedx/frontend-platform.

In terms of its abilities, it allows for:

  • Customizable window width/height of plugin slot

  • Multiple child MFEs can live inside a single plugin slot

  • Designed for future plugin types (LTI, Build-time, Module Federation) to be rendered based on the child MFE’s configuration. Linked is where the iFrame case exists and where the other types implementation logic would exist.

As for the PR, what’s left is:

  • Determine the desired fallback for when a plugin fails to render ADR

  • Write a test that ensures the desired fallback works correctly

Build-time module replacement using Webpacks’s NormalModuleReplacementPlugin

Glib Glugovskiy

We are currently exploring the use of NPM overrides and additional Webpack scripting. These could be integrated into the frontend-build repository without necessitating the forking of other frontend-app-* repositories.

Last month, we initiated an internal discovery with the following objectives:

  • Identify methods to override JavaScript code in Micro-Frontends (MFEs) with minimal changes, possibly by leveraging existing solutions in frontend-build or frontend-platform repositories.

  • Use a single repository to manage all code overrides and new customizations across MFEs.

  • Our customizations should be compatible with the latest MFE versions with minimal adjustments required.

  • Share a working example or even a fully implemented solution, specifically tailored for the Palm release MFEs.

We’ve developed a Proof of Concept (POC) using Webpack’s NormalModuleReplacementPlugin to replace entire files (modules) at build time. You can find it here: mfe-overrides GitHub Repository. We’ve tested this on a live installation, and it’s functional. However, it’s still a work in progress and requires further refinement to serve as a comprehensive solution.

Consider an approach similar to backstage’s

Edward Zarecor

I’m including for completeness. There are a number of things that I don’t like about how Backstage does plugins. For example, you need to directly alter source of platform pages to include plugins in base pages. However, they have optimized dev tooling to support a vibrant ecosystem of plugins.

https://john-tucker.medium.com/backstage-plugins-by-example-part-1-a4737e21d256

https://john-tucker.medium.com/backstage-plugins-by-example-part-2-6ead20cb4c8d

https://backstage.io/docs/plugins/create-a-plugin/

Leverage the Piral framework

Adolfo Brandes & Pedro Martello

Slides: https://docs.google.com/presentation/d/14ZkIqYnc5EOSPujxylSfi7W5eJWXaguD-3ECLlURoKA/edit

You may (or may not) have heard of Piral, and how we’re trying to leverage it for the Open edX frontend. (Talked about it at Open edX Con 2023: https://youtu.be/I3ut1XtSJ6o?si=cG3QddRA-EG3Gfur&t=1870.) And while Piral need not concern itself with in-page customization - this can be achieved completely independently of it - it does provide mechanisms that can be used to achieve good customizabilitypluggability as well.

Mechanism 1: Extensions

  • A central SPA loads “pilets” (UMD modules, to which MFEs can be easily converted to) at runtime; which pilets are loaded is up to an external feed service, which can be as simple as a json file on S3

  • Pilets can define “extension slots”, which can then be filled by “extensions” on other pilets

  • An operator can then set up their pilet feed to serve “extension pilets”, essentially a pilet for each desired plugin, or a pilet with many plugins - it’s up to the operator

Pros
  • Runtime loading of plugins: think different plugins for different tenants

  • No need to rebuild, no messing with package.json

  • No need to predict/preassign NPM package names: all a plugin needs to know is the extension slot name

Cons
  • No predefined concept of widgets or actions (though it could probably be done)

  • Piral must be adopted and MFEs converted to pilets

  • We’d start depending on Piral for more than just MFE composition

Mechanism 2: Events

Piral provides a simple but flexible event system. It would be a simple matter for custom MFEs/pilets/extensions to listen to and react to known events emitted by any other MFE/pilet/extension.

Pros
  • openedx-events for the frontend! (Open question: would filters be possible?)

Cons
  • Piral dependency

...

Attribute

Poor

Decent

Solid

Maintainability

Requires maintaining a substantial fork

Better with notable flaws

Requires only Installing and enabling custom plugins

Upgradability (may be a subset of the above)

Each plugin must be manually verified between releases

Some verification required between releases, but it’s easy to find what changes are required

Stable API - plugin authors need not touch plugins between releases

Flexibility

Restrictive, fixed options.  Easy to hide things, but hard to add or change existing things

Better with notable flaws

High degree of control.  Can add, update and delete components

Approachability

Off the beaten track, surprising, steep ramp

Better with notable flaws

Simple and familiar patterns, reasonably easy to grok, few surprises.

Security

Introduces new attack vectors.

Better with notable flaws

Meets our existing security patterns and requirements

Performance

Significant performance tradeoffs, difficult to cache, large apps, poor sharing of libraries

Better with notable flaws

Optimized app sizes, maximal sharing of libraries, high-level of cachability at edge and client.

Accessibility

Hard to implement our standards for a11y, i18n, and l11n.

Better with notable flaws

Standards achieved via existing patterns

Operability

Must release to change any component.  Customizations are statically delivered

Better with notable flaws

Individual components are independently updatable.  Runtime configuration is available.  Split tests are possible.

...