Skip to content

✨(frontend) add custom css style #771

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

Merged
merged 5 commits into from
Mar 31, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ and this project adheres to
## Added

- 📄(legal) Require contributors to sign a DCO #779
- ✨(frontend) add custom css style #771

## Changed

Expand Down
33 changes: 33 additions & 0 deletions docs/theming.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Runtime Theming 🎨

### How to Use

To use this feature, simply set the `FRONTEND_CSS_URL` environment variable to the URL of your custom CSS file. For example:

```javascript
FRONTEND_CSS_URL=http://anything/custom-style.css
```

Once you've set this variable, our application will load your custom CSS file and apply the styles to our frontend application.

### Benefits

This feature provides several benefits, including:

* **Easy customization** 🔄: With this feature, you can easily customize the look and feel of our application without requiring any code changes.
* **Flexibility** 🌈: You can use any CSS styles you like to create a custom theme that meets your needs.
* **Runtime theming** ⏱️: This feature allows you to change the theme of our application at runtime, without requiring a restart or recompilation.

### Example Use Case

Let's say you want to change the background color of our application to a custom color. You can create a custom CSS file with the following contents:

```css
body {
background-color: #3498db;
}
```

Then, set the `FRONTEND_CSS_URL` environment variable to the URL of your custom CSS file. Once you've done this, our application will load your custom CSS file and apply the styles, changing the background color to the custom color you specified.


1 change: 1 addition & 0 deletions src/backend/core/api/viewsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -1689,6 +1689,7 @@ def get(self, request):
"CRISP_WEBSITE_ID",
"ENVIRONMENT",
"FRONTEND_THEME",
"FRONTEND_CSS_URL",
"MEDIA_BASE_URL",
"POSTHOG_KEY",
"LANGUAGES",
Expand Down
2 changes: 2 additions & 0 deletions src/backend/core/tests/test_api_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
COLLABORATION_WS_URL="http://testcollab/",
CRISP_WEBSITE_ID="123",
FRONTEND_THEME="test-theme",
FRONTEND_CSS_URL="http://testcss/",
MEDIA_BASE_URL="http://testserver/",
POSTHOG_KEY={"id": "132456", "host": "https://eu.i.posthog-test.com"},
SENTRY_DSN="https://sentry.test/123",
Expand All @@ -39,6 +40,7 @@ def test_api_config(is_authenticated):
"CRISP_WEBSITE_ID": "123",
"ENVIRONMENT": "test",
"FRONTEND_THEME": "test-theme",
"FRONTEND_CSS_URL": "http://testcss/",
"LANGUAGES": [
["en-us", "English"],
["fr-fr", "Français"],
Expand Down
4 changes: 4 additions & 0 deletions src/backend/impress/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,10 @@ class Base(Configuration):
None, environ_name="FRONTEND_THEME", environ_prefix=None
)

FRONTEND_CSS_URL = values.Value(
None, environ_name="FRONTEND_CSS_URL", environ_prefix=None
)

# Posthog
POSTHOG_KEY = values.DictValue(
None, environ_name="POSTHOG_KEY", environ_prefix=None
Expand Down
44 changes: 30 additions & 14 deletions src/frontend/apps/e2e/__tests__/app-impress/config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import path from 'path';

import { expect, test } from '@playwright/test';

import { createDoc, verifyDocName } from './common';
import { createDoc } from './common';

const config = {
AI_FEATURE_ENABLED: true,
CRISP_WEBSITE_ID: null,
COLLABORATION_WS_URL: 'ws://localhost:4444/collaboration/ws/',
ENVIRONMENT: 'development',
FRONTEND_CSS_URL: null,
FRONTEND_THEME: 'default',
MEDIA_BASE_URL: 'http://localhost:8083',
LANGUAGES: [
Expand Down Expand Up @@ -99,22 +100,13 @@ test.describe('Config', () => {
page,
browserName,
}) => {
const webSocketPromise = page.waitForEvent('websocket', (webSocket) => {
return webSocket.url().includes('ws://localhost:4444/collaboration/ws/');
});

await page.goto('/');

const randomDoc = await createDoc(
page,
'doc-collaboration',
browserName,
1,
);
void createDoc(page, 'doc-collaboration', browserName, 1);

await verifyDocName(page, randomDoc[0]);

const webSocket = await webSocketPromise;
const webSocket = await page.waitForEvent('websocket', (webSocket) => {
return webSocket.url().includes('ws://localhost:4444/collaboration/ws/');
});
expect(webSocket.url()).toContain('ws://localhost:4444/collaboration/ws/');
});

Expand Down Expand Up @@ -173,6 +165,30 @@ test.describe('Config', () => {
page.locator('#crisp-chatbox').getByText('Invalid website'),
).toBeVisible();
});

test('it checks FRONTEND_CSS_URL config', async ({ page }) => {
await page.route('**/api/v1.0/config/', async (route) => {
const request = route.request();
if (request.method().includes('GET')) {
await route.fulfill({
json: {
...config,
FRONTEND_CSS_URL: 'http://localhost:123465/css/style.css',
},
});
} else {
await route.continue();
}
});

await page.goto('/');

await expect(
page
.locator('head link[href="http://localhost:123465/css/style.css"]')
.first(),
).toBeAttached();
});
});

test.describe('Config: Not loggued', () => {
Expand Down
14 changes: 7 additions & 7 deletions src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,18 +58,18 @@ test.describe('Doc Editor', () => {
* - signal of the backend to the collaborative server (connection should close)
* - reconnection to the collaborative server
*/
test('checks the connection with collaborative server', async ({
page,
browserName,
}) => {
test('checks the connection with collaborative server', async ({ page }) => {
let webSocketPromise = page.waitForEvent('websocket', (webSocket) => {
return webSocket
.url()
.includes('ws://localhost:4444/collaboration/ws/?room=');
});

const randomDoc = await createDoc(page, 'doc-editor', browserName, 1);
await verifyDocName(page, randomDoc[0]);
await page
.getByRole('button', {
name: 'New doc',
})
.click();

let webSocket = await webSocketPromise;
expect(webSocket.url()).toContain(
Expand Down Expand Up @@ -99,7 +99,7 @@ test.describe('Doc Editor', () => {
const wsClose = await wsClosePromise;
expect(wsClose.isClosed()).toBeTruthy();

// Checkt the ws is connected again
// Check the ws is connected again
webSocketPromise = page.waitForEvent('websocket', (webSocket) => {
return webSocket
.url()
Expand Down
4 changes: 2 additions & 2 deletions src/frontend/apps/e2e/__tests__/app-impress/doc-grid.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ test.describe('Document grid item options', () => {

test.describe('Documents filters', () => {
test('it checks the prebuild left panel filters', async ({ page }) => {
await page.goto('/');
void page.goto('/');

// All Docs
const response = await page.waitForResponse(
Expand Down Expand Up @@ -263,7 +263,7 @@ test.describe('Documents filters', () => {

test.describe('Documents Grid', () => {
test('checks all the elements are visible', async ({ page }) => {
await page.goto('/');
void page.goto('/');

let docs: SmallDoc[] = [];
const response = await page.waitForResponse(
Expand Down
1 change: 1 addition & 0 deletions src/frontend/apps/impress/src/components/BoxButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const BoxButton = forwardRef<HTMLDivElement, BoxButtonType>(
${$css || ''}
`}
{...props}
className={`--docs--box-button ${props.className || ''}`}
onClick={(event: React.MouseEvent<HTMLDivElement>) => {
if (props.disabled) {
return;
Expand Down
1 change: 1 addition & 0 deletions src/frontend/apps/impress/src/components/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const Card = ({

return (
<Box
className={`--docs--card ${props.className || ''}`}
$background="white"
$radius="4px"
$css={css`
Expand Down
2 changes: 2 additions & 0 deletions src/frontend/apps/impress/src/components/DropButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export const DropButton = ({
onPress={() => onOpenChangeHandler(true)}
aria-label={label}
$css={buttonCss}
className="--docs--drop-button"
>
{button}
</StyledButton>
Expand All @@ -79,6 +80,7 @@ export const DropButton = ({
triggerRef={triggerRef}
isOpen={isLocalOpen}
onOpenChange={onOpenChangeHandler}
className="--docs--drop-button-popover"
>
{children}
</StyledPopover>
Expand Down
1 change: 1 addition & 0 deletions src/frontend/apps/impress/src/components/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export const IconBG = ({ iconName, ...textProps }: IconBGProps) => {
$padding="4px"
$margin="auto"
{...textProps}
className={`--docs--icon-bg ${textProps.className || ''}`}
>
{iconName}
</Text>
Expand Down
5 changes: 4 additions & 1 deletion src/frontend/apps/impress/src/components/InfiniteScroll.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ export const InfiniteScroll = ({
};

return (
<Box {...boxProps}>
<Box
{...boxProps}
className={`--docs--infinite-scroll ${boxProps.className || ''}`}
>
{children}
<InView onChange={loadMore}>
{!isLoading && hasMore && (
Expand Down
1 change: 1 addition & 0 deletions src/frontend/apps/impress/src/components/LoadMoreText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const LoadMoreText = ({
$align="center"
$gap="0.4rem"
$padding={{ horizontal: '2xs', vertical: 'sm' }}
className="--docs--load-more"
>
<Icon
$theme="primary"
Expand Down
7 changes: 6 additions & 1 deletion src/frontend/apps/impress/src/components/TextErrors.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,12 @@ export const TextErrors = ({
const { t } = useTranslation();

return (
<AlertStyled canClose={canClose} type={VariantType.ERROR} icon={icon}>
<AlertStyled
canClose={canClose}
type={VariantType.ERROR}
icon={icon}
className="--docs--text-errors"
>
<Box $direction="column" $gap="0.2rem">
{causes &&
causes.map((cause, i) => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,6 @@ export const QuickSearchStyle = createGlobalStyle`
}
}



.c__modal__scroller:has(.quick-search-container),
.c__modal__scroller:has(.noPadding) {
padding: 0 !important;
Expand All @@ -138,6 +136,4 @@ export const QuickSearchStyle = createGlobalStyle`
margin-bottom: 0;
}
}


`;
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export const HorizontalSeparator = ({
? '#e5e5e533'
: colorsTokens()['greyscale-100']
}
className="--docs--horizontal-separator"
/>
);
};
18 changes: 13 additions & 5 deletions src/frontend/apps/impress/src/core/config/ConfigProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Loader } from '@openfun/cunningham-react';
import Head from 'next/head';
import { PropsWithChildren, useEffect } from 'react';

import { Box } from '@/components';
Expand Down Expand Up @@ -54,10 +55,17 @@ export const ConfigProvider = ({ children }: PropsWithChildren) => {
}

return (
<AnalyticsProvider>
<CrispProvider websiteId={conf?.CRISP_WEBSITE_ID}>
{children}
</CrispProvider>
</AnalyticsProvider>
<>
{conf?.FRONTEND_CSS_URL && (
<Head>
<link rel="stylesheet" href={conf?.FRONTEND_CSS_URL} />
</Head>
)}
<AnalyticsProvider>
<CrispProvider websiteId={conf?.CRISP_WEBSITE_ID}>
{children}
</CrispProvider>
</AnalyticsProvider>
Comment on lines +64 to +68
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe not in the approriate commit

Copy link
Collaborator Author

@AntoLC AntoLC Mar 31, 2025

Choose a reason for hiding this comment

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

It is the indentation that made it as a new piece of code.

</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ interface ConfigResponse {
COLLABORATION_WS_URL?: string;
CRISP_WEBSITE_ID?: string;
FRONTEND_THEME?: Theme;
FRONTEND_CSS_URL?: string;
MEDIA_BASE_URL?: string;
POSTHOG_KEY?: PostHogConf;
SENTRY_DSN?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,20 @@ export const ButtonLogin = () => {
onClick={() => gotoLogin()}
color="primary-text"
aria-label={t('Login')}
className="--docs--button-login"
>
{t('Login')}
</Button>
);
}

return (
<Button onClick={gotoLogout} color="primary-text" aria-label={t('Logout')}>
<Button
onClick={gotoLogout}
color="primary-text"
aria-label={t('Logout')}
className="--docs--button-logout"
>
{t('Logout')}
</Button>
);
Expand All @@ -45,6 +51,7 @@ export const ProConnectButton = () => {
}
`}
$radius="4px"
className="--docs--proconnect-button"
>
<ProConnectImg />
</BoxButton>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
$padding={{ top: 'md' }}
$background="white"
$css={cssEditor(readOnly)}
className="--docs--editor-container"
>
{errorAttachment && (
<Box $margin={{ bottom: 'big', top: 'none', horizontal: 'large' }}>
Expand Down Expand Up @@ -192,7 +193,7 @@ export const BlockNoteEditorVersion = ({
}, [setEditor, editor]);

return (
<Box $css={cssEditor(readOnly)}>
<Box $css={cssEditor(readOnly)} className="--docs--editor-container">
<BlockNoteView editor={editor} editable={!readOnly} theme="light" />
</Box>
);
Expand Down
Loading
Loading