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: adds support for loading external theme CSS for MFEs #440

Closed
wants to merge 59 commits into from
Closed
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
3cde02d
feat: add ability to dynamically load theme overrides
viktorrusakov Feb 2, 2023
f41dcb2
feat: adds support for loading external theme CSS for MFEs
adamstankiewicz Feb 15, 2023
2b19931
fix: handle missing theme config
adamstankiewicz Feb 15, 2023
347957b
fix: add env vars to env files
adamstankiewicz Feb 15, 2023
ef52e41
fix: remove unused code
adamstankiewicz Feb 15, 2023
2849bfd
chore: wip commit
adamstankiewicz Feb 24, 2023
a298bf5
fix: prefer runtime config for paragon theme
adamstankiewicz Feb 27, 2023
697e43b
fix: rebase on master and resolve package conflicts
adamstankiewicz Feb 27, 2023
fbfd722
fix: revert to original webpack.dev.config config
adamstankiewicz Feb 27, 2023
a2b3f99
chore: clean up unnecessary comment line
adamstankiewicz Feb 27, 2023
b477c8d
fix: grammar
adamstankiewicz May 20, 2023
916c1b3
Merge branch 'master' into ags/inject-theme-css
adamstankiewicz May 20, 2023
4b2038c
fix: remove duplicate import
adamstankiewicz May 20, 2023
26fbfac
chore: refresh package-lock.json to lockfileVersion 3
adamstankiewicz May 20, 2023
b259760
fix: clean up link nodes in document head when no longer needed
adamstankiewicz May 20, 2023
155bf03
fix: fallback to locally installed core and light theme css urls
adamstankiewicz May 27, 2023
e5ee81e
docs: update to docs
adamstankiewicz May 27, 2023
813169d
chore: update docs about
adamstankiewicz May 27, 2023
a714d49
fix: expose setThemeVariant
adamstankiewicz May 27, 2023
1e13ac3
Merge branch 'master' into ags/inject-theme-css
adamstankiewicz May 27, 2023
e2b0df9
docs: update docs
adamstankiewicz May 27, 2023
ea99e3c
fix: rebase with master and update based on PARAGON changes
adamstankiewicz May 28, 2023
00fd9c2
chore: remove support for env vars config for paragon dynamic theming
adamstankiewicz May 29, 2023
0da2cfa
Merge branch 'master' into ags/inject-theme-css
adamstankiewicz May 29, 2023
f24c336
chore: clean up package-lock
adamstankiewicz May 29, 2023
84b34ee
fix: updates
adamstankiewicz May 29, 2023
f9aa947
fix: one more update
adamstankiewicz May 29, 2023
fb70be9
fix: refresh package-lock.json
adamstankiewicz May 29, 2023
e0768ff
fix: refresh package-lock.json pt2
adamstankiewicz May 29, 2023
4c5f358
fix: updates
adamstankiewicz May 29, 2023
c957eb5
fix: update package-lock.json
adamstankiewicz May 29, 2023
2c386ac
fix: make it theme variant agnostic
adamstankiewicz May 31, 2023
340e259
docs: update howto theming guide
adamstankiewicz May 31, 2023
9b5bfa5
fix: ensure app loads without styles if the PARAGON_THEME_URLS and fa…
adamstankiewicz May 31, 2023
59401f5
fix: ensure fallback theme links are removed if they also error
adamstankiewicz May 31, 2023
46cb39a
docs: add link to mfe runtime config api adr
adamstankiewicz May 31, 2023
ebbe03a
fix: don't attempt to load paragon css urls if PARAGON_THEME_URLS is …
adamstankiewicz Jun 1, 2023
494ad62
Merge branch 'master' into ags/inject-theme-css
adamstankiewicz Jun 1, 2023
74f9f8f
fix: brand overrides
adamstankiewicz Jun 5, 2023
e3e5fe5
docs: fix code example
adamstankiewicz Jun 5, 2023
902e8f4
docs: add missing comma
adamstankiewicz Jun 5, 2023
0c73b5b
docs: update how to
adamstankiewicz Jun 5, 2023
b380c18
Merge branch 'master' into ags/inject-theme-css
adamstankiewicz Jul 15, 2023
67a0a1d
feat: support dark mode
adamstankiewicz Jul 15, 2023
c30ae3f
chore: update package-lock.json
adamstankiewicz Jul 15, 2023
e2a115e
chore: update package-lock.json take 2
adamstankiewicz Jul 15, 2023
33a8377
chore: remove console.log statements
adamstankiewicz Jul 15, 2023
45c727e
fix: ignore system preference change when theme variant set in locals…
adamstankiewicz Jul 16, 2023
f6d633c
chore: add tests for updates to AppProvider
adamstankiewicz Jul 16, 2023
d65acff
chore: update react-intl to pass peer dependencies after pinning all …
adamstankiewicz Jul 16, 2023
efdf60c
chore: split hooks.js up into separate files and begin some related t…
adamstankiewicz Jul 16, 2023
e96de6b
test: add testing to useParagonTheme hooks (#514)
dcoa Jul 21, 2023
8f39517
Merge branch 'master' into ags/inject-theme-css
adamstankiewicz Jul 21, 2023
cf10278
chore: update package-lock.json
adamstankiewicz Jul 21, 2023
8f0edb4
fix: update links in head and *isLoaded to true (#534)
monteri Dec 9, 2023
084a4ed
Merge branch 'master' into ags/inject-theme-css
adamstankiewicz Dec 9, 2023
b5f1588
test: add test to useParagonTheme hook and paragon utils (#525)
dcoa Dec 9, 2023
2b2772e
Merge branch 'ags/inject-theme-css' of github.com:openedx/frontend-pl…
adamstankiewicz Dec 9, 2023
3345dc0
fix: resolve lockfileVersion conflict after merge with master
adamstankiewicz Dec 9, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .env.development
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ STUDIO_BASE_URL=http://localhost:18010
MARKETING_SITE_BASE_URL=http://localhost:18000
ORDER_HISTORY_URL=http://localhost:1996/orders
REFRESH_ACCESS_TOKEN_ENDPOINT=http://localhost:18000/login_refresh
SEGMENT_KEY=''
SEGMENT_KEY=
SITE_NAME=localhost
USER_INFO_COOKIE_NAME=edx-user-info
LOGO_URL=https://edx-cdn.org/v3/default/logo.svg
Expand All @@ -27,4 +27,4 @@ FAVICON_URL=https://edx-cdn.org/v3/default/favicon.ico
IGNORED_ERROR_REGEX=
MFE_CONFIG_API_URL=
APP_ID=
SUPPORT_URL=https://support.edx.org
SUPPORT_URL=https://support.edx.org
Binary file added docs/how_tos/assets/paragon-theme-loader.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
76 changes: 76 additions & 0 deletions docs/how_tos/theming.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Theming support with Paragon

This document serves as a guide to using `@edx/frontend-platform` to support MFE theming with Paragon using theme CSS loaded externally (e.g., from a CDN). By serving CSS loaded externally, consuming applications of Paragon no longer need to be responsible for compiling the theme SCSS to CSS themselves and instead use a pre-compiled CSS file. In doing so, this allows making changes to the Paragon theme without needing to necessarily re-build and re-deploy all consuming applications. We would also get a meaningful gain in performance as loading the compiled theme CSS from an external CDN means micro-frontends (MFEs) can include cached styles instead of needing to load essentially duplicate theme styles as users navigate across different MFEs.

## Overview

![overview of paragon theme loader](./assets/paragon-theme-loader.png "Paragon theme loader")

## Theme URL configuration

Paragon supports 2 mechanisms for configuring the Paragon theme URLs:
* JavaScript-based configuration via `env.config.js`.
* MFE runtime configuration API via `edx-platform`

Using either configuration mechanism, a `PARAGON_THEME_URLS` configuration setting must be created to point to the externally hosted Paragon theme CSS files, e.g.:

```js
const config = {
PARAGON_THEME_URLS = {
core: 'https://cdn.jsdelivr.net/npm/@edx/paragon@$paragonVersion/dist/core.css',
variants: {
light: 'https://cdn.jsdelivr.net/npm/@edx/paragon@$paragonVersion/dist/light.css',
},
},
};
export default config;
```

### JavaScript-based configuration

Another approach to configuration with `@edx/frontend-platform` is to create a `env.config.js` file in the root of the repository, similar to the environment variable configuration mentioned above. However, in this case, the configuration is defined as a JavaScript file, which affords consumers to use more complex data types than just strings as in the environment variable approach.

To use this JavaScript-based configuration approach, you may set a `PARAGON_THEME_URLS` configuration variable in your `env.config.js` file:

```js
const config = {
PARAGON_THEME_URLS: {
core: 'https://cdn.jsdelivr.net/npm/@edx/paragon@$paragonVersion/dist/paragon.css',
variants: {
light: 'https://cdn.jsdelivr.net/npm/@edx/paragon@$paragonVersion/scss/core/css/variables.css',
},
},
};
export default config;
```


### MFE runtime configuration API

`@edx/frontend-platform` additionally supports loading application configuration from an API at runtime rather than environment variables. For example, in `edx-platform`, there is an API endpoint for MFE runtime configuration at `http://localhost:18000/api/mfe_config/v1`. The application configuration may be setup via Django settings as follows:
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: To me this paragraph sort of implies that the MFE config API in edx-platform is one example of how this can be done, rather than the sole way frontend-platform can get this info. (I mean, I guess someone could configure the frontend to point at some other service, but none exists!)

A link to the MFE config API doc here would probably be helpful.

Copy link
Member Author

@adamstankiewicz adamstankiewicz May 31, 2023

Choose a reason for hiding this comment

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

Nit: To me this paragraph sort of implies that the MFE config API in edx-platform is one example of how this can be done, rather than the sole way frontend-platform can get this info. (I mean, I guess someone could configure the frontend to point at some other service, but none exists!)

Agreed. Re-worded, and will push up a fix shortly.

A link to the MFE config API doc here would probably be helpful.

@davidjoy Is there a suitable link to include here? I'm not aware of one. I know I started to create this (currently 2U-internal) document describing the different configuration mechanisms because I couldn't find any other documentation about the MFE runtime config API.

That being said, yes, a link to some more detailed, public docs here would be helpful :)

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

I've updated the language to include this above suggested link.


```python
ENABLE_MFE_CONFIG_API = True
MFE_CONFIG = {}
MFE_CONFIG_OVERRIDES = {
'profile': {
'PARAGON_THEME_URLS': {
'core': 'https://cdn.jsdelivr.net/npm/@edx/paragon@$paragonVersion/dist/core.css',
'variants': {
'light': 'https://cdn.jsdelivr.net/npm/@edx/paragon@$paragonVersion/dist/light.css',
},
},
},
}
```

### Locally installed `@edx/paragon`

In the event the other Paragon CSS URLs are configured via one of the other documented mechanisms, but they fail to load (e.g., the CDN url throws a 404), `@edx/frontend-platform` will fallback to injecting the locally installed Paragon CSS from the consuming application into the HTML document.

If you would like to use the same version of the Paragon CSS URLs as the locally installed `@edx/paragon`, the configuration for the Paragon CSS URLs may contain a wildcard `$paragonVersion` which gets replaced with the locally installed version of `@edx/paragon` in the consuming application, e.g.:

```shell
https://cdn.jsdelivr.net/npm/@edx/paragon@$paragonVersion/dist/core.css
https://cdn.jsdelivr.net/npm/@edx/paragon@$paragonVersion/scss/core/css/variables.css
```
3 changes: 2 additions & 1 deletion example/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ import {
import { APP_INIT_ERROR, APP_READY, initialize } from '@edx/frontend-platform';
import { subscribe } from '@edx/frontend-platform/pubSub';

import './index.scss';
import ExamplePage from './ExamplePage';
import AuthenticatedPage from './AuthenticatedPage';

import './index.scss';

subscribe(APP_READY, () => {
ReactDOM.render(
<AppProvider>
Expand Down
Loading