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 #689

Open
wants to merge 24 commits into
base: master
Choose a base branch
from

Conversation

dcoa
Copy link
Contributor

@dcoa dcoa commented May 10, 2024

Description:

This PR updates the original one #440 closer to the master branch and adds some extra tests.

Please read the original PR for additional context.

Warning

The dist folder is included in the PR for testing purposes and will be removed before the merge.

Merge checklist:

  • Consider running your code modifications in the included example app within frontend-platform. This can be done by running npm start and opening http://localhost:8080.
  • Consider testing your code modifications in another local micro-frontend using local aliases configured via the module.config.js file in frontend-build.
  • Verify your commit title/body conforms to the conventional commits format (e.g., fix, feat) and is appropriate for your code change. Consider whether your code is a breaking change, and modify your commit accordingly.

Post merge:

  • After the build finishes for the merged commit, verify the new release has been pushed to NPM.

@openedx-webhooks
Copy link

openedx-webhooks commented May 10, 2024

Thanks for the pull request, @dcoa!

What's next?

Please work through the following steps to get your changes ready for engineering review:

🔘 Get product approval

If you haven't already, check this list to see if your contribution needs to go through the product review process.

  • If it does, you'll need to submit a product proposal for your contribution, and have it reviewed by the Product Working Group.
    • This process (including the steps you'll need to take) is documented here.
  • If it doesn't, simply proceed with the next step.

🔘 Provide context

To help your reviewers and other members of the community understand the purpose and larger context of your changes, feel free to add as much of the following information to the PR description as you can:

  • Dependencies

    This PR must be merged before / after / at the same time as ...

  • Blockers

    This PR is waiting for OEP-1234 to be accepted.

  • Timeline information

    This PR must be merged by XX date because ...

  • Partner information

    This is for a course on edx.org.

  • Supporting documentation
  • Relevant Open edX discussion forum threads

🔘 Get a green build

If one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green.

🔘 Let us know that your PR is ready for review:

Who will review my changes?

This repository is currently maintained by @openedx/committers-frontend. Tag them in a comment and let them know that your changes are ready for review.

Where can I find more information?

If you'd like to get more details on all aspects of the review process for open source pull requests (OSPRs), check out the following resources:

When can I expect my changes to be merged?

Our goal is to get community contributions seen and reviewed as efficiently as possible.

However, the amount of time that it takes to review and merge a PR can vary significantly based on factors such as:

  • The size and impact of the changes that it introduces
  • The need for product review
  • Maintenance status of the parent repository

💡 As a result it may take up to several weeks or months to complete a review and merge your PR.

@openedx-webhooks openedx-webhooks added the open-source-contribution PR author is not from Axim or 2U label May 10, 2024
Copy link

codecov bot commented May 10, 2024

Codecov Report

Attention: Patch coverage is 83.90093% with 52 lines in your changes missing coverage. Please review.

Project coverage is 83.64%. Comparing base (bc9c233) to head (837858e).

Files Patch % Lines
src/react/hooks/paragon/useParagonThemeVariants.js 81.57% 19 Missing and 2 partials ⚠️
src/react/hooks/paragon/useParagonThemeCore.js 80.95% 15 Missing and 1 partial ⚠️
src/react/hooks/paragon/utils.js 78.78% 7 Missing ⚠️
src/react/hooks/paragon/useParagonTheme.js 84.37% 4 Missing and 1 partial ⚠️
src/react/reducers.js 84.61% 2 Missing ⚠️
src/react/hooks/paragon/useParagonThemeUrls.js 96.77% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #689      +/-   ##
==========================================
+ Coverage   83.41%   83.64%   +0.23%     
==========================================
  Files          40       48       +8     
  Lines        1073     1388     +315     
  Branches      197      290      +93     
==========================================
+ Hits          895     1161     +266     
- Misses        166      211      +45     
- Partials       12       16       +4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@dcoa dcoa force-pushed the dcoa/design-tokens-support branch from aee582b to e81b549 Compare May 11, 2024 00:23
@dcoa dcoa changed the title [WIP] feat: adds support for loading external theme CSS for MFEs feat: adds support for loading external theme CSS for MFEs May 11, 2024
@dcoa dcoa requested a review from adamstankiewicz May 11, 2024 00:49
@@ -0,0 +1,204 @@
# Theming support with `@edx/paragon` and `@edx/brand`
Copy link
Member

@GlugovGrGlib GlugovGrGlib May 11, 2024

Choose a reason for hiding this comment

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

we need to refactor @edx/ to @openedx/ in docs, as since December npm packages for @openedx/paragon, @openedx/frontend-build, @openedx/frontend-plugin-framework, @openedx/brand-openedx
are published in the scope of @openedx/

Copy link
Member

@GlugovGrGlib GlugovGrGlib May 11, 2024

Choose a reason for hiding this comment

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

@brian-smith-tcril I couldn't find frontend-platform among npm published packages, shouldn't it be published with @openedx scope as well?
https://www.npmjs.com/search?q=%40openedx

Copy link
Contributor

Choose a reason for hiding this comment

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

I think that one is still only published to the @edx scope. I agree it would be good to make the switch but it hasn't made it to the top of the priority list yet.

package.json Outdated
"@openedx/frontend-build": "14.0.3",
"@openedx/paragon": "22.3.2",
"@openedx/frontend-build": "github:edunext/frontend-build#dcoa/design-tokens-support",
"@openedx/paragon": "npm:@openedx/paragon@^22.0.0-alpha.25",
Copy link
Member

Choose a reason for hiding this comment

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

It might worth to wait for the Paragon sync PR to be merged, so we won't downgrade paragon requirements, and still will be able to use the latest version of paragon.

@dcoa dcoa force-pushed the dcoa/design-tokens-support branch from 5b4c843 to ed293e3 Compare May 14, 2024 00:44
@PKulkoRaccoonGang
Copy link
Contributor

@dcoa I'm getting an error during build related to the env.config.js file

SyntaxError: Unexpected token 'export'

I tried to solve it by changing the export to module.exports = { config }; I succeeded, but the content of the example application is not rendered on http://localhost:8080.

Have you encountered such a problem?

@dcoa
Copy link
Contributor Author

dcoa commented May 21, 2024

@PKulkoRaccoonGang this is related to frontend-build changes, I'm checking right now

This line is making that the app is not rendering in frontend-platform https://github.com/openedx/frontend-build/pull/546/files#diff-22e13ddf245ea4fa81ca4d686dddf46fa9cbf70fbbff99991cccbf0c8ff82316R175

If you remove it, you will be able to see the app.

Update

I made an update to webpack dev config for example app and is working now.

Copy link
Contributor

@PKulkoRaccoonGang PKulkoRaccoonGang left a comment

Choose a reason for hiding this comment

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

@dcoa This looks great! I have already started working on Update openedx/frontend-app-discussions. I left a few questions about the operation of some features and the code in general.

`@openedx/frontend-platform` supports both `light` (required) and `dark` (optional) theme variants. The choice of which theme variant should be applied on page load is based on the following preference cascade:

1. **Get theme preference from localStorage.** Supports persisting and loading the user's preference for their selected theme variant, until cleared.
1. **Detect user system settings.** Rely on the `prefers-color-scheme` media query to detect if the user's system indicates a preference for dark mode. If so, use the default dark theme variant, if one is configured.
Copy link
Contributor

Choose a reason for hiding this comment

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

[question]: Is there now support for adding a dark/light theme (or other custom themes) when using locally installed Paragon CSS?

For example:

@use "@openedx/paragon/dist/core.min.css" as paragonCore;
@use "@openedx/paragon/dist/light.min.css" as paragonLight;
@use "@openedx/paragon/dist/dark.min.css" as paragonDark;

This works great using JS and MFE runtime configurations, but I have not been able to test it for local themes.

Copy link
Contributor Author

@dcoa dcoa May 27, 2024

Choose a reason for hiding this comment

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

The hook will fallback in the installed themes files if the URL is not provided or is not working.

If you define only the CSS inside SCSS file you can use the prefers-color-scheme to support dark/light modes. But you can not create a custom switch.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thank you! It would be great to explicitly indicate exactly how it is possible to use different imports for a light/dark theme in the documentation. I plan to do this in the migration documentation and for Paragon.

For example:

@use "@openedx/paragon/dist/core.min.css" as paragonCore;
@import url('@openedx/paragon/dist/light.min.css') (prefers-color-scheme: light);
@import url('@my-brand/dark.min.css') (prefers-color-scheme: dark);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

100% agree that the information in your example should be part of the Paragon and migration documents.

This is a different approach, the explanation of how to use variants is below, in the title Basic theme URL configuration.

I will improve one of the examples to use dark mode, to make it more explicit.


### Falling back to styles installed in consuming application

If any of the configured external `PARAGON_THEME_URLS` fail to load for whatever reason (e.g., CDN is down, URL is incorrectly configured), `@openedx/paragon` will attempt to fallback to the relevant files installed in `node_modules` from the consuming application.
Copy link
Contributor

Choose a reason for hiding this comment

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

When trying to make sure that if all themes are not loaded successfully, themes from the local version of Paragon will be loaded, I encountered the following problems:

image

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@PKulkoRaccoonGang thanks for the feedback, the fallback url was missing the publicPath I solved it.

image

Copy link
Contributor

Choose a reason for hiding this comment

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

We don’t receive undefined, but I couldn’t load styles from the locally installed Paragon package in node_modules. Could you check if getParagonThemeCss works correctly in a PR related to a frontend-build?

image

Copy link
Contributor Author

@dcoa dcoa Jun 1, 2024

Choose a reason for hiding this comment

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

The problem here is related to tutor-mfe, they define BASE_URL as the MFE_HOST (you can see here) thing that is not 100% true in dev mode because the URL needs the PORT that is specific for each MFE (you can see here )

You can add in the settings:

MFE_CONFIG_OVERRIDES = {
     "discussions": {
        "BASE_URL": "http://apps.local.edly.io:2002"
    }
}

### Basic theme URL configuration

Paragon supports 2 mechanisms for configuring the Paragon theme urls:
* JavaScript-based configuration via `env.config.js`
Copy link
Contributor

Choose a reason for hiding this comment

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

[question]: This approach works great in MFE, but I encountered a problem in frontend-build/example, unfortunately, the styles are not loading for me. Is everything working correctly?

For example: I tried adding a Button component with Paragon and noticed that the styles with Paragon were not being applied to this component


```shell
https://cdn.jsdelivr.net/npm/@openedx/paragon@$paragonVersion/dist/core.min.css
https://cdn.jsdelivr.net/npm/@openedx/paragon@$paragonVersion/dist/light.min.css
Copy link
Contributor

Choose a reason for hiding this comment

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

[question]: Is it worth adding an example of importing in SCSS format here? This also applies to the local import example @openedx/brand-openedx

For example:

@use "@openedx/paragon/dist/core.min.css" as paragonCore;
@use "@openedx/paragon/dist/light.min.css" as paragonLight;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, because the main intention here is to identify the version and load the external resource compatible with that specific version.

For example, my app has paragon 23.0.0-alpha.2, then the hook will replace $paragonVersion .

This is the definition
https://cdn.jsdelivr.net/npm/@openedx/paragon@$paragonVersion/dist/core.min.css

after processing
https://cdn.jsdelivr.net/npm/@openedx/paragon@$23.0.0-alpha.2/dist/core.min.css

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for the explanation 💯

Comment on lines 53 to 70
global.PARAGON_THEME = {
paragon: {
version: '1.0.0',
themeUrls: {
core: {
fileName: 'core.min.css',
},
defaults: {
light: 'light',
},
variants: {
light: {
fileName: 'light.min.css',
},
},
},
},
};
Copy link
Contributor

Choose a reason for hiding this comment

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

[nit]: We also use this configuration for the test in the file src/react/hooks/paragon/useParagonThemeVariants.test.js
I suggest putting this code into a separate file and reusing it to test both hooks

Copy link
Contributor Author

@dcoa dcoa May 28, 2024

Choose a reason for hiding this comment

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

I can get rid of this understanding that is being exported by frontend-build (including for testing)

Note: Once the frontend-build PR has been merged we need to update the devDependencies and peerDependencies

});

it('returns expected object when configuration is valid (both Paragon + brand)', () => {
const config = {
Copy link
Contributor

Choose a reason for hiding this comment

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

[optional]: I suggest reusing the config constant, which refers to an object with the necessary configuration for tests in this file. This will allow us to get rid of code duplication. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I prefer leaving this section as it is. The important part here is PARAGON_THEME_URLS, which changes in each test. It's easier to understand this way, I think.

Copy link
Contributor

Choose a reason for hiding this comment

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

Good, I agree with you, thanks

src/react/hooks/paragon/useParagonThemeUrls.js Outdated Show resolved Hide resolved
src/react/AppProvider.test.jsx Outdated Show resolved Hide resolved
});

it('calls useParagonTheme', () => {
const Component = (
Copy link
Contributor

Choose a reason for hiding this comment

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

[question]: Can we declare a global Сomponent constant for a block with paragon theme and brand tests and reuse it in tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I can declare the global component but this test doesn't cover the useParagonTheme behavior so it is not necessary to declare paragon theme and brand. This is focused on the setThemeVariant function and the theme state.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, thanks

src/react/hooks/paragon/useParagonThemeUrls.js Outdated Show resolved Hide resolved
@dcoa dcoa force-pushed the dcoa/design-tokens-support branch 4 times, most recently from a6a9211 to 229d79a Compare June 3, 2024 03:24
adamstankiewicz and others added 11 commits June 26, 2024 11:28
feat: adds support for loading external theme CSS for MFEs

Introduces `useAppTheme` in `AppProvider` to load/inject `<link>` elements for the core theme CSS and any theme variant CSS into the HTML document. Exposes the app theme state and a way to mutate it to consumers via `AppContext`.

fix: handle missing theme config

fix: add env vars to env files

fix: remove unused code

chore: wip commit

fix: prefer runtime config for paragon theme

fix: rebase on master and resolve package conflicts

fix: revert to original webpack.dev.config config

chore: clean up unnecessary comment line

fix: grammar

Co-authored-by: Adolfo R. Brandes <[email protected]>
chore: refresh package-lock.json to lockfileVersion 3

fix: clean up link nodes in document head when no longer needed

fix: fallback to locally installed core and light theme css urls

docs: update to docs

chore: update docs about

fix: expose setThemeVariant
fix: rebase with master and update based on PARAGON changes

chore: remove support for env vars config for paragon dynamic theming
fix: updates

fix: one more update

fix: refresh package-lock.json

fix: refresh package-lock.json pt2

fix: updates

fix: update package-lock.json

fix: make it theme variant agnostic

docs: update howto theming guide

fix: ensure app loads without styles if the PARAGON_THEME_URLS and fallback urls all fail to load

fix: ensure fallback theme links are removed if they also error

docs: add link to mfe runtime config api adr

fix: don't attempt to load paragon css urls if PARAGON_THEME_URLS is absent
docs: fix code example

docs: add missing comma

docs: update how to
chore: update package-lock.json

chore: update package-lock.json take 2

chore: remove console.log statements

fix: ignore system preference change when theme variant set in localstorage

chore: add tests for updates to AppProvider

chore: update react-intl to pass peer dependencies after pinning all deps

chore: split hooks.js up into separate files and begin some related tests

test: add testing to useParagonTheme hooks (openedx#514)

* test: add testing to useParagonThemeCore
* test: add test to useThemeVariants hook
* fix: Paragon definition and remove onload mock
* test: change test message to be clear
Copy link
Contributor

@brian-smith-tcril brian-smith-tcril left a comment

Choose a reason for hiding this comment

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

Overall this is looking great! I left a suggestion to clarify the documentation is referring to design tokens, and a few comments noting PRs that need to land before this can.

Thank you so much for this!

@@ -0,0 +1,250 @@
# Theming support with `@openedx/paragon` and `@openedx/brand-openedx`
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
# Theming support with `@openedx/paragon` and `@openedx/brand-openedx`
# Theming support with `@openedx/paragon` and `@openedx/brand-openedx`
> [!IMPORTANT]
> This document describes theming with design tokens.
> Information on theming MFEs that do not yet have design tokens support:
> * https://github.com/openedx/brand-openedx
> Information on the design tokens project:
> * https://github.com/openedx/paragon/blob/master/docs/decisions/0019-scaling-styles-with-design-tokens.rst
> * https://github.com/openedx/paragon/tree/alpha?tab=readme-ov-file#design-tokens

@@ -35,11 +35,12 @@
"devDependencies": {
"@edx/brand": "npm:@openedx/brand-openedx@^1.2.2",
"@edx/browserslist-config": "1.2.0",
"@openedx/frontend-build": "14.0.10",
"@openedx/frontend-build": "github:edunext/frontend-build#dcoa/design-tokens-support",
Copy link
Contributor

Choose a reason for hiding this comment

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

@@ -76,7 +77,7 @@
},
"peerDependencies": {
"@openedx/frontend-build": ">= 14.0.0",
"@openedx/paragon": ">= 21.5.7 < 23.0.0",
"@openedx/paragon": " 23.0.0-alpha.1 || >= 21.5.7 < 23.0.0",
Copy link
Contributor

Choose a reason for hiding this comment

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

Note

Waiting on Paragon alpha to be merged to master

@dcoa dcoa force-pushed the dcoa/design-tokens-support branch from 0b4c547 to 3576ee7 Compare August 14, 2024 23:45
@arbrandes
Copy link
Contributor

@davidjoy, this is a doozy to keep an eye on.

@davidjoy
Copy link
Contributor

Sure is. It looks like it was temporary, but just to explicitly note it: the dist folder has been checked in in this PR. We need to remember to remove that before merging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
open-source-contribution PR author is not from Axim or 2U
Projects
Status: Waiting on Author
Development

Successfully merging this pull request may close these issues.

8 participants