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 26 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
06eada1
feat: add ability to dynamically load theme overrides
adamstankiewicz May 20, 2023
d59e866
fix: remove duplicate import
adamstankiewicz May 27, 2023
a2c61eb
docs: update docs
adamstankiewicz May 29, 2023
adc080f
chore: clean up package-lock
adamstankiewicz Jun 1, 2023
efc759d
fix: brand overrides
adamstankiewicz Jun 5, 2023
65fcf0b
feat: support dark mode
dcoa Jul 21, 2023
20a9a1e
fix: update dependenices and AppProvider test
dcoa May 8, 2024
4bb5ea0
style: fix variable name and funtion
dcoa May 10, 2024
d4c817b
test: add useParagonTheme test
dcoa May 11, 2024
7f5f669
docs: update scope of paragon and brand for openedx
dcoa May 14, 2024
dc026c0
refactor: allow multiple replacement in version replacement
dcoa May 17, 2024
7ed517b
refactor: update webpack dev config
dcoa May 22, 2024
5bb7dbf
refactor: avoid duplicated links if the theme varian is preloaded
dcoa May 23, 2024
9beaa1a
refactor: update useParagonTheme when some variables are missing
dcoa May 23, 2024
5a5ce86
test: add new test csse for useParagonThemeCore
dcoa May 23, 2024
73fae22
refactor: use modules for config file
dcoa May 23, 2024
01ff88b
fix: fallback url
dcoa May 28, 2024
38cc052
style: fix imports order
dcoa May 28, 2024
c15a7f1
refactor: create isEmptyObject and fallbackThemeUrl utils
dcoa May 28, 2024
e8af8b6
test: refactor testing fot app provider, theme core hook, theme varia…
dcoa May 28, 2024
e43db9c
docs: improve themening documentation
dcoa Jun 3, 2024
9f6f445
chore: update dependencies and regenerate package-lock
dcoa Jun 26, 2024
0542b45
temp: add dist folder for testing purposes
dcoa Jul 22, 2024
818ae1c
refactor: allow env PARAGON_THEME_URLS definition
dcoa Dec 2, 2024
57c209f
test: regenerate dist folder for testing
dcoa Dec 2, 2024
1d824e9
fix: remove scss imports for example app
dcoa Dec 2, 2024
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
5 changes: 3 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,5 @@ 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
PARAGON_THEME_URLS={}
1 change: 1 addition & 0 deletions .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ IGNORED_ERROR_REGEX=
MFE_CONFIG_API_URL=
APP_ID=
SUPPORT_URL=https://support.edx.org
PARAGON_THEME_URLS={}
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
.idea
.vscode
coverage
dist
src/i18n/transifex_input.json
node_modules
/docs/api
Expand Down
661 changes: 661 additions & 0 deletions dist/LICENSE

Large diffs are not rendered by default.

155 changes: 155 additions & 0 deletions dist/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
[![Build Status](https://github.com/openedx/frontend-platform/actions/workflows/ci.yml/badge.svg)](https://github.com/openedx/frontend-platform/actions/workflows/ci.yml)
[![Codecov](https://img.shields.io/codecov/c/github/edx/frontend-platform)](https://codecov.io/gh/edx/frontend-platform)
[![NPM Version](https://img.shields.io/npm/v/@edx/frontend-platform.svg)](https://www.npmjs.com/package/@edx/frontend-platform)
[![npm_downloads](https://img.shields.io/npm/dt/@edx/frontend-platform.svg)](https://www.npmjs.com/package/@edx/frontend-platform)
[![license](https://img.shields.io/npm/l/@edx/frontend-platform.svg)](https://github.com/openedx/frontend-platform/blob/master/LICENSE)
[![semantic release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)

# Overview

See the [GitHub Pages site for the complete documentation](https://openedx.github.io/frontend-platform/).

frontend-platform is a modest application framework for Open edX micro-frontend applications and their supporting libraries. It provides several foundational services that all Open edX micro-frontends should have:

| Service | Module location |
|------------------------------------|----------------------------------|
| Analytics | @edx/frontend-platform/analytics |
| Logging | @edx/frontend-platform/logging |
| Authenticated API client (auth) | @edx/frontend-platform/auth |
| Internationalization (i18n) | @edx/frontend-platform/i18n |
| Misc (init, config, pubSub, utils) | @edx/frontend-platform |

-------------------------------------------------------------------------

In addition, frontend-platform provides an extensible application initialization lifecycle to help manage the configuration of the above services, freeing application developers to focus on feature development.

## Getting started

### One-time setup if you have not upgraded node/npm
IMPORTANT: There is now a new node/npm version being used by frontend-platform as of
https://github.com/openedx/frontend-platform/pull/259

#### Install nvm
This is highly recommended to be able to leverage different node/npm versions.
For a some time, different repositories may be using different versions of node/npm.

Alternatively, please install node16 and npm8 for use with this repository.

#### Switch to node/npm version for this repo
```nvm use```
if you don't have the right node/npm versions, nvm will instruct you to install those

#### Clean out old node modules and reinstall
This step is needed because node now uses a different package lock format, and it's important to reinstall
dependencies based on this new package file. Delete node_modules, and issue an `npm ci`


### Standard getting started steps

1. `npm install`
2. `npm start`
3. Open http://localhost:8080 to view the example app.

## Architecture

The four foundational services listed above (analytics, auth, i18n, and logging) are provided as imports to applications via frontend-platform's API layer. The initialization sequence creates an instance of each service and exposes its methods as functional exports, creating a layer of abstraction between service implementations and their usage in application code.

Each type of service has a documented API contract which service implementations must fulfill. This allows different service implementations to be used as necessary without updates to consuming applications.

### Service architecture

Internally, service implementations are strictly isolated from the rest of the platform. They are classes that take their dependencies as arguments to their constructor. This means, for instance, if analytics depends on logging, it takes a reference to an instance fulfilling the `LoggingService` interface as an option when it's instantiated. It cannot import from the logging module directly. Put another way, the default service implementations may be co-located with the service interfaces for convenience, but they can theoretically live in their own repository and it wouldn't require any refactoring.

Likewise, platform code should not make use of service methods that are not part of the documented interface for the same reasons.

### Application initialization

frontend-platform provides an `initialize()` function which bootstraps and configures an application. The `initialize()` function uses a set of [sensible defaults](https://en.wikipedia.org/wiki/Convention_over_configuration) unless otherwise specified, bootstrapping the application with services reflecting Open edX's best practices around analytics, authentication, internationalization, and logging.

The initialization process proceeds in a series of phases, giving the initializing application code opportunities to hook into the process and do custom setup as desired:

- Before initialization
- Pub/Sub initialized
- Environment config document loaded
- Logging service initialized
- Authentication service initialized
- Analytics service initialized
- Internationalization service initialized
- Application ready

Most applications won't need to do anything special at all.

### Application configuration

When the application loads, a list of known environment variables is loaded from `process.env` into an object which it exposes via `getConfig` - the point here is primarily to isolate our code from usages of `process.env` which may not always be the way we choose to configure our apps. The application initialization lifecycle supports runtime configuration as well via the `config` handler, documented in the [initialize function](https://edx.github.io/frontend-platform/module-Initialization.html#~initialize). If you want to get a variable into the config that it’s not expecting, you can use [`mergeConfig`](https://edx.github.io/frontend-platform/module-Config.html#~mergeConfig) during initialization to add it in from `process.env`.

Such an example might look like this:

```
initialize({
// ... other initialization options
handlers: {
config: () => {
mergeConfig({
CUSTOM_VARIABLE: process.env.CUSTOM_VARIABLE || null,
}, 'Custom app config');
},
},
});
```

When using runtime configuration via `mergeConfig` noted above, `getConfig` must be called within a component's render lifecycle for the added keys and values to be returned in the configuration object. If `getConfig` is called outside of a component's render lifecycle, the custom configuration key/value pairs will not initially be part of the object returned by `getConfig`. For example:

```jsx
import { getConfig } from '@edx/frontend-platform/config';

// The runtime configuration `CUSTOM_VARIABLE` added in the above code snippet will not appear here. This is
// because `getConfig` is called before `mergeConfig` is executed to add the custom runtime configuration.
console.log(getConfig().CUSTOM_VARIABLE); // returns undefined

const ExampleComponent = () => {
// This returns the value as expected since it is called after `mergeConfig` has already been executed.
console.log(getConfig().CUSTOM_VARIABLE)
};
```

### Service interfaces

Each service (analytics, auth, i18n, logging) provided by frontend-platform has an API contract which all implementations of that service are guaranteed to fulfill. Applications that use frontend-platform can use its configured services via a convenient set of exported functions. An application that wants to use the service interfaces need only initialize them via the initialize() function, optionally providing custom service interfaces as desired (you probably won't need to).

![Service interface](service-interface.png)

### Service implementations

This repository contains default service implementations for convenience. These implementations are co-located with their consuming service interfaces for ease of development, though the two should remain _strictly_ modular and separate.

The included service implementations are:

- New Relic (logging)
- Segment (analytics)
- Axios/JWT (auth)
- React Intl (i18n)

NOTE: As of this writing, i18n is _not_ configurable. The `initialize()` function does not allow applications to supply an alternate i18n implementation; this is because the interface and implementation for i18n has not yet been separated and modularized.

# Local Development & Testing Locally

When making changes to frontend-platform, be sure to manually run the included example app located in `./example`. The example app includes 2 routes to test for both unauthenticated and authenticated users. To start the example app, run `npm start` from the root directory.

If you want to test changes to frontend-platform against a micro-frontend locally, follow the directions here: https://github.com/openedx/frontend-build#local-module-configuration-for-webpack

# Production Deployment Strategy

For any MFE built on top of the frontend-platform, the deployment strategy will be something like the following:

1. Run the build script with environment variables on the command line to pass in any relevant config. Example:

```bash
NODE_ENV=development BASE_URL=open.edx.org ETC=etc npm run build
```

This will create a dist/ directory that contains the deployable artifacts.

2. Copy the contents of dist/ to a web server.

3. Configure the platform to point at your MFE. (details on this coming soon)
75 changes: 75 additions & 0 deletions dist/analytics/MockAnalyticsService.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions dist/analytics/MockAnalyticsService.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading