(project) add custom js support via config

From the config, we can add custom JS file URL
to be included in the frontend.
This commit is contained in:
Anthony LC
2025-12-22 16:02:28 +01:00
parent b78ad27a71
commit ea3a4a6da3
11 changed files with 95 additions and 7 deletions

View File

@@ -2197,6 +2197,7 @@ class ConfigView(drf.views.APIView):
"ENVIRONMENT",
"FRONTEND_CSS_URL",
"FRONTEND_HOMEPAGE_FEATURE_ENABLED",
"FRONTEND_JS_URL",
"FRONTEND_THEME",
"MEDIA_BASE_URL",
"POSTHOG_KEY",

View File

@@ -24,6 +24,7 @@ pytestmark = pytest.mark.django_db
COLLABORATION_WS_NOT_CONNECTED_READY_ONLY=True,
CRISP_WEBSITE_ID="123",
FRONTEND_CSS_URL="http://testcss/",
FRONTEND_JS_URL="http://testjs/",
FRONTEND_THEME="test-theme",
MEDIA_BASE_URL="http://testserver/",
POSTHOG_KEY={"id": "132456", "host": "https://eu.i.posthog-test.com"},
@@ -49,6 +50,7 @@ def test_api_config(is_authenticated):
"ENVIRONMENT": "test",
"FRONTEND_CSS_URL": "http://testcss/",
"FRONTEND_HOMEPAGE_FEATURE_ENABLED": True,
"FRONTEND_JS_URL": "http://testjs/",
"FRONTEND_THEME": "test-theme",
"LANGUAGES": [
["en-us", "English"],

View File

@@ -509,6 +509,9 @@ class Base(Configuration):
FRONTEND_CSS_URL = values.Value(
None, environ_name="FRONTEND_CSS_URL", environ_prefix=None
)
FRONTEND_JS_URL = values.Value(
None, environ_name="FRONTEND_JS_URL", environ_prefix=None
)
THEME_CUSTOMIZATION_FILE_PATH = values.Value(
os.path.join(BASE_DIR, "impress/configuration/theme/default.json"),

View File

@@ -126,6 +126,20 @@ test.describe('Config', () => {
).toBeAttached();
});
test('it checks FRONTEND_JS_URL config', async ({ page }) => {
await overrideConfig(page, {
FRONTEND_JS_URL: 'http://localhost:123465/js/script.js',
});
await page.goto('/');
await expect(
page
.locator('script[src="http://localhost:123465/js/script.js"]')
.first(),
).toBeAttached();
});
test('it checks theme_customization.translations config', async ({
page,
}) => {
@@ -145,10 +159,6 @@ test.describe('Config', () => {
await expect(page.getByText('MyCustomDocs')).toBeAttached();
});
});
test.describe('Config: Not logged', () => {
test.use({ storageState: { cookies: [], origins: [] } });
test('it checks the config api is called', async ({ page }) => {
const responsePromise = page.waitForResponse(
@@ -168,6 +178,10 @@ test.describe('Config: Not logged', () => {
expect(configApi).toStrictEqual(CONFIG_LEFT);
});
});
test.describe('Config: Not logged', () => {
test.use({ storageState: { cookies: [], origins: [] } });
test('it checks that theme is configured from config endpoint', async ({
page,

View File

@@ -10,6 +10,7 @@ export const CONFIG = {
COLLABORATION_WS_NOT_CONNECTED_READY_ONLY: true,
ENVIRONMENT: 'development',
FRONTEND_CSS_URL: null,
FRONTEND_JS_URL: null,
FRONTEND_HOMEPAGE_FEATURE_ENABLED: true,
FRONTEND_THEME: null,
MEDIA_BASE_URL: 'http://localhost:8083',

View File

@@ -1,5 +1,6 @@
import { Loader } from '@openfun/cunningham-react';
import Head from 'next/head';
import Script from 'next/script';
import { PropsWithChildren, useEffect, useRef } from 'react';
import { useTranslation } from 'react-i18next';
@@ -87,6 +88,9 @@ export const ConfigProvider = ({ children }: PropsWithChildren) => {
<link rel="stylesheet" href={conf?.FRONTEND_CSS_URL} />
</Head>
)}
{conf?.FRONTEND_JS_URL && (
<Script src={conf?.FRONTEND_JS_URL} strategy="afterInteractive" />
)}
<AnalyticsProvider>
<CrispProvider websiteId={conf?.CRISP_WEBSITE_ID}>
{children}

View File

@@ -21,6 +21,7 @@ export interface ConfigResponse {
ENVIRONMENT: string;
FRONTEND_CSS_URL?: string;
FRONTEND_HOMEPAGE_FEATURE_ENABLED?: boolean;
FRONTEND_JS_URL?: string;
FRONTEND_THEME?: Theme;
LANGUAGES: [string, string][];
LANGUAGE_CODE: string;

View File

@@ -29,6 +29,7 @@ export const Header = () => {
<SkipToContent />
<Box
as="header"
className="--docs--header"
role="banner"
$css={css`
position: fixed;
@@ -45,7 +46,6 @@ export const Header = () => {
border-bottom: 1px solid
var(--c--contextuals--border--surface--primary);
`}
className="--docs--header"
>
{!isDesktop && <ButtonTogglePanel />}
<StyledLink
@@ -88,7 +88,12 @@ export const Header = () => {
<LaGaufre />
</Box>
) : (
<Box $align="center" $gap={spacingsTokens['sm']} $direction="row">
<Box
className="--docs--header-block-right"
$align="center"
$gap={spacingsTokens['sm']}
$direction="row"
>
<ButtonLogin />
<LanguagePicker />
<LaGaufre />