0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-03-24 22:41:28 -05:00

feat: custom css (#3155)

This commit is contained in:
Gao Sun 2023-02-21 10:55:44 +08:00 committed by GitHub
parent c93d744041
commit 1ef5519e75
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 74 additions and 43 deletions

View file

@ -91,4 +91,5 @@ export const mockSignInExperience: SignInExperience = {
},
socialSignInConnectorTargets: ['github', 'facebook', 'wechat'],
signInMode: SignInMode.SignInAndRegister,
customCss: null,
};

View file

@ -35,7 +35,7 @@ describe('sign-in-experience query', () => {
it('findDefaultSignInExperience', async () => {
/* eslint-disable sql/no-unsafe-query */
const expectSql = `
select "tenant_id", "id", "color", "branding", "language_info", "terms_of_use_url", "sign_in", "sign_up", "social_sign_in_connector_targets", "sign_in_mode"
select "tenant_id", "id", "color", "branding", "language_info", "terms_of_use_url", "sign_in", "sign_up", "social_sign_in_connector_targets", "sign_in_mode", "custom_css"
from "sign_in_experiences"
where "id"=$1
`;

View file

@ -0,0 +1,20 @@
import { sql } from 'slonik';
import type { AlterationScript } from '../lib/types/alteration.js';
const alteration: AlterationScript = {
up: async (pool) => {
await pool.query(sql`
alter table sign_in_experiences
add column if not exists custom_css text;
`);
},
down: async (pool) => {
await pool.query(sql`
alter table sign_in_experiences
drop column custom_css;
`);
},
};
export default alteration;

View file

@ -1,55 +1,55 @@
import { generateDarkColor } from '@logto/core-kit';
import type { CreateSignInExperience } from '../db-entries/index.js';
import type { SignInExperience } from '../db-entries/index.js';
import { SignInMode } from '../db-entries/index.js';
import { BrandingStyle, SignInIdentifier } from '../foundations/index.js';
import { defaultTenantId } from './tenant.js';
const defaultPrimaryColor = '#6139F6';
export const createDefaultSignInExperience = (
forTenantId: string
): Readonly<CreateSignInExperience> => ({
tenantId: forTenantId,
id: 'default',
color: {
primaryColor: defaultPrimaryColor,
isDarkModeEnabled: false,
darkPrimaryColor: generateDarkColor(defaultPrimaryColor),
},
branding: {
style: BrandingStyle.Logo,
logoUrl: 'https://logto.io/logo.svg',
darkLogoUrl: 'https://logto.io/logo-dark.svg',
},
languageInfo: {
autoDetect: true,
fallbackLanguage: 'en',
},
termsOfUseUrl: null,
signUp: {
identifiers: [SignInIdentifier.Username],
password: true,
verify: false,
},
signIn: {
methods: [
{
identifier: SignInIdentifier.Username,
password: true,
verificationCode: false,
isPasswordPrimary: true,
},
],
},
socialSignInConnectorTargets: [],
signInMode: SignInMode.SignInAndRegister,
});
export const createDefaultSignInExperience = (forTenantId: string): Readonly<SignInExperience> =>
Object.freeze({
tenantId: forTenantId,
id: 'default',
color: {
primaryColor: defaultPrimaryColor,
isDarkModeEnabled: false,
darkPrimaryColor: generateDarkColor(defaultPrimaryColor),
},
branding: {
style: BrandingStyle.Logo,
logoUrl: 'https://logto.io/logo.svg',
darkLogoUrl: 'https://logto.io/logo-dark.svg',
},
languageInfo: {
autoDetect: true,
fallbackLanguage: 'en' as const,
},
termsOfUseUrl: null,
signUp: {
identifiers: [SignInIdentifier.Username],
password: true,
verify: false,
},
signIn: {
methods: [
{
identifier: SignInIdentifier.Username,
password: true,
verificationCode: false,
isPasswordPrimary: true,
},
],
},
socialSignInConnectorTargets: [],
signInMode: SignInMode.SignInAndRegister,
customCss: null,
});
/** @deprecated Use `createDefaultSignInExperience()` instead. */
export const defaultSignInExperience = createDefaultSignInExperience(defaultTenantId);
export const adminConsoleSignInExperience: CreateSignInExperience = {
export const adminConsoleSignInExperience: Readonly<SignInExperience> = Object.freeze({
...defaultSignInExperience,
color: {
...defaultSignInExperience.color,
@ -61,4 +61,4 @@ export const adminConsoleSignInExperience: CreateSignInExperience = {
darkLogoUrl: 'https://logto.io/logo-dark.svg',
slogan: 'admin_console.welcome.title', // TODO: @simeng should we programmatically support an i18n key for slogan?
},
};
});

View file

@ -12,5 +12,6 @@ create table sign_in_experiences (
sign_up jsonb /* @use SignUp */ not null,
social_sign_in_connector_targets jsonb /* @use ConnectorTargets */ not null default '[]'::jsonb,
sign_in_mode sign_in_mode not null default 'SignInAndRegister',
custom_css text,
primary key (tenant_id, id)
);

View file

@ -1,5 +1,5 @@
import { SignInMode } from '@logto/schemas';
import { useEffect } from 'react';
import { useEffect, useRef } from 'react';
import { Route, Routes, BrowserRouter, Navigate } from 'react-router-dom';
import AppBoundary from './containers/AppBoundary';
@ -29,8 +29,13 @@ import './scss/normalized.scss';
const App = () => {
const { context, Provider } = usePageContext();
const { experienceSettings, setLoading, setExperienceSettings } = context;
const customCssRef = useRef(document.createElement('style'));
const [isPreview] = usePreview(context);
useEffect(() => {
document.head.append(customCssRef.current);
}, []);
useEffect(() => {
if (isPreview) {
return;
@ -38,6 +43,8 @@ const App = () => {
(async () => {
const settings = await getSignInExperienceSettings();
// eslint-disable-next-line @silverhand/fp/no-mutation
customCssRef.current.textContent = settings.customCss;
// Note: i18n must be initialized ahead of page render
await initI18n(settings.languageInfo);

View file

@ -209,6 +209,7 @@ export const mockSignInExperience: SignInExperience = {
},
socialSignInConnectorTargets: ['BE8QXN0VsrOH7xdWFDJZ9', 'lcXT4o2GSjbV9kg2shZC7'],
signInMode: SignInMode.SignInAndRegister,
customCss: null,
};
export const mockSignInExperienceSettings: SignInExperienceResponse = {
@ -230,6 +231,7 @@ export const mockSignInExperienceSettings: SignInExperienceResponse = {
email: true,
phone: true,
},
customCss: null,
};
const usernameSettings = {