From 0016e7a950a963e27ebc104203bb69dfd8485a3d Mon Sep 17 00:00:00 2001 From: "Adolfo R. Brandes" Date: Thu, 7 Nov 2024 16:30:10 -0300 Subject: [PATCH] docs: Add info on how to use frontend plugin slots It's now possible to configure frontend plugin slots across MFEs (notably, header and footer). This documents how this can be done as simply as possible. See #199 --- README.rst | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/README.rst b/README.rst index 18bb1c04..a315d27b 100644 --- a/README.rst +++ b/README.rst @@ -308,6 +308,120 @@ In case you need to run additional instructions just before the build step you c You can find more patches in the `patch catalog <#template-patch-catalog>`_ below. +Using Frontend Plugin Slots +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``mfe-dockerfile-pre-npm-build`` patch is also suitable for taking advantage of the ``env.config.jsx`` mechanism, which lets you define and use frontend plugins. For instance, you can use it to modify the footer across all MFEs without having to fork ``frontend-component-footer``. + +You can start by following the Tutor plugin tutorial at the `Adding new templates `_ section, though instead of adding a whole new Dockerfile, you'll just modify the exiting one. Your ``myplugin.py`` should look something like this: + +.. code-block:: python + + import os + from tutor import hooks + + # Add the template root folder + template_folder = os.path.join(os.path.dirname(__file__), "templates") + hooks.Filters.ENV_TEMPLATE_ROOTS.add_item(template_folder) + + # env.config.jsx files should live in `mfe/build/mfe`, which is also where the + # tutor-mfe Dockerfile goes. This is so it's more straightforward to copy it + # in, as seen below. + hooks.Filters.ENV_TEMPLATE_TARGETS.add_item(("mfe/build/mfe", "plugins")) + + # This patch gets inserted into the tutor-mfe Dockerfile. It copies our + # env.config.jsx into the root of each and every MFE before the npm build step. + hooks.Filters.ENV_PATCHES.add_item(("mfe-dockerfile-pre-npm-build", + "COPY env.config.jsx /openedx/app" + )) + +As for ``env.config.jsx``, as noted above, it should be put into a ``templates/mfe/build/mfe`` subdirectory below ``myplugin.py``. This is so it's easy to ``COPY`` it into the Docker image. In order for the file to work with all current MFEs, this is what it should look like: + +.. code-block:: javascript + + /* We can't just assume FPF exists, as it's not declared as a dependency in all + * MFEs. Therefore, we have to use dynamic imports to check if it's available. + * + * We also have to use a `try` block (as opposed to `.then()`), otherwise + * Webpack won't treat the import as optional. Hence the async function. + */ + async function getConfig () { + let config = {}; + + try { + const { DIRECT_PLUGIN, PLUGIN_OPERATIONS } = await import('@openedx/frontend-plugin-framework'); + + config = { + pluginSlots: { + footer_slot: { + plugins: [ + { + /* Hide the default footer. */ + op: PLUGIN_OPERATIONS.Hide, + widgetId: 'default_contents', + }, + { + /* Insert a custom footer. */ + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_footer', + type: DIRECT_PLUGIN, + RenderWidget: () => ( +

This is the footer.

+ ), + }, + }, + ] + } + } + }; + + /* NPM allows us to introspect the package name at build time, which in turn + * lets us tailor the configuration for individual MFEs. + */ + if (process.env.npm_package_name == '@edx/frontend-app-profile') { + config.pluginSlots.footer_slot.plugins[1].widget.RenderWidget = () => ( +

This is the profile MFE's footer.

+ ); + } + } catch { }; + + return config; + } + + export default getConfig; + +As you can see, by using the ``process.env.npm_package_name`` variable you can use a single ``env.config.jsx`` to selectively apply configuration to different MFEs. + +If, on the other hand, you prefer to maintain entirely separate ``env.config.jsx`` files, one for each mfe, that can also be done. For that you can rely on the ``mfe-dockerfile-pre-npm-build-*`` patches. For instance: + +.. code-block:: python + + hooks.Filters.ENV_PATCHES.add_items([ + ( + "mfe-dockerfile-pre-npm-build", + "COPY env.config.jsx /openedx/app" + ), + ( + "mfe-dockerfile-pre-npm-build-account", + "COPY env-account.config.jsx /openedx/app/env.config.jsx" + ), + ( + "mfe-dockerfile-pre-npm-build-profile", + "COPY env-profile.config.jsx /openedx/app/env.config.jsx" + ) + ]) + +In this case, there's a global ``env.config.jsx``, but it would get overwritten by the specific ones for the Account and Profile MFEs. Note that while they originally have different names, they should all still be copied to ``env.config.jsx``. + +You can find what slots a library or MFE supports by inspecting its ``src/plugin-slots`` directory on Github. For example: + +- `Header slots `_ +- `Learning MFE slots `_ +- `Learner dashboard MFE slots `_ + +For more information on how frontend plugin slots work, refer to the `Frontend Plugin Framework README `_. + Installing from a private npm registry ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~