Skip to content

Commit

Permalink
Inline preview customisation styles #4394
Browse files Browse the repository at this point in the history
ref DEV-1427
  • Loading branch information
tung2744 authored Jul 2, 2024
2 parents ad738f6 + 7f97255 commit 5f4cdf5
Show file tree
Hide file tree
Showing 32 changed files with 3,323 additions and 2,209 deletions.
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ ANALYTIC_EPOCH=2021-03-25

ELASTICSEARCH_URL=http://localhost:9200

AUTH_UI_WINDOW_MESSAGE_ALLOWED_ORIGINS=http://portal.localhost:8000

CORS_ALLOWED_ORIGINS=portal.localhost:8000

ALLOWED_FRAME_ANCESTORS=http://portal.localhost:8000
Expand Down
4 changes: 4 additions & 0 deletions authui/src/authflowv2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ import { AlertMessageController } from "./authflowv2/alert-message";
import { DismissKeyboardOnScrollController } from "./authflowv2/dismissKeyboard";
import { BodyScrollLockController } from "./authflowv2/bodyScrollLock";
import { ClickToSwitchController } from "./clickToSwitch";
import { InlinePreviewController } from "./inline-preview";
import { PreviewableResourceController } from "./previewable-resource";

axios.defaults.withCredentials = true;

Expand Down Expand Up @@ -100,5 +102,7 @@ Stimulus.register(
);
Stimulus.register("body-scroll-lock", BodyScrollLockController);
Stimulus.register("click-to-switch", ClickToSwitchController);
Stimulus.register("inline-preview", InlinePreviewController);
Stimulus.register("previewable-resource", PreviewableResourceController);

injectCSSAttrs(document.documentElement);
1 change: 1 addition & 0 deletions authui/src/authflowv2/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@
/* Alignment */
:root {
--alignment-logo: center; /* center / start / end / hidden */
--alignment-card: center; /* center / start / end */
--alignment-content: center; /* center / start / end */
}
}
46 changes: 43 additions & 3 deletions authui/src/authflowv2/components/layout.css
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
@layer components {
:root {
--layout__bg-color: var(--color-bg-base);
--layout__bg_position: center;
--layout__bg_repeat: no-repeat;
--layout__bg_size: cover;
}

:root.dark {
--layout__bg-color: var(--color-bg-base);
--layout__bg-image: ;
}

.layout--default {
@apply flex flex-col flex-1-0-auto;
@apply items-center justify-start;

background-color: var(--color-bg-base);
background-color: var(--layout__bg-color);
background-image: var(--layout__bg-image);
background-repeat: var(--layout__bg_repeat);
background-position: var(--layout__bg_position);
background-size: var(--layout__bg_size);
}

.layout--default .widget {
@apply flex-1-0-auto;

background-color: var(--color-bg-base);
}

.layout__header--default,
Expand All @@ -18,10 +32,36 @@
}

@media (min-width: theme("screens.tablet")) {
.layout--default {
:root[alignment-card="start"] & {
@apply items-start;
}

:root[alignment-card="center"] & {
@apply items-center;
}

:root[alignment-card="end"] & {
@apply items-end;
}
}

.layout__header--default,
.layout__footer--default {
@apply flex-1;
max-height: 160px;
}
}

@media (min-width: theme("screens.desktop")) {
.layout--default {
:root[alignment-card="start"] & {
@apply pl-60;
}

:root[alignment-card="end"] & {
@apply pr-60;
}
}
}
}
11 changes: 10 additions & 1 deletion authui/src/authflowv2/components/watermark.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
@layer components {
:root {
--watermark-display: inline-block;
}

html[data-watermark-enabled="false"] {
--watermark-display: hidden;
}

.watermark {
@apply inline-block h-[22px] w-[120px] bg-no-repeat bg-center;
@apply h-[22px] w-[120px] bg-no-repeat bg-center;
display: var(--watermark-display);
background-size: auto 22px;
background-image: url("./icons/raw/watermark.svg");
}
Expand Down
15 changes: 14 additions & 1 deletion authui/src/authflowv2/components/widget.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
--widget__border-radius: 0;
--widget__box-shadow: none;
--widget__border: none;
--widget__bg_position: center;
--widget__bg_repeat: no-repeat;
--widget__bg_size: cover;

@media (min-width: theme("screens.tablet")) {
--widget__border-radius: var(--border-radius-large);
Expand All @@ -14,14 +17,24 @@
}
}

:root.dark {
--widget__bg-color: var(--color-bg-base);
--widget__bg-image: ;
}

.widget {
width: var(--widget__width);
border-radius: var(--widget__border-radius);
border: var(--widget__border);
box-shadow: var(--widget__box-shadow);
background-color: var(--widget__bg-color);
@apply px-6 pt-8 pb-10;
background-image: var(--widget__bg-image);
background-repeat: var(--widget__bg_repeat);
background-position: var(--widget__bg_position);
background-size: var(--widget__bg_size);
@apply flex flex-col;
@apply relative;
@apply px-6 pt-8 pb-10;
}

.widget-content {
Expand Down
Binary file modified authui/src/authflowv2/icons/material-symbols-outlined-subset.ttf
Binary file not shown.
Binary file not shown.
1 change: 1 addition & 0 deletions authui/src/cssattrs.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const cssVarsToAttrs = {
"--alignment-logo": "alignment-logo",
"--alignment-card": "alignment-card",
"--alignment-content": "alignment-content",
};

Expand Down
94 changes: 94 additions & 0 deletions authui/src/inline-preview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { Controller } from "@hotwired/stimulus";
import { PreviewableResourceController } from "./previewable-resource";
import { injectCSSAttrs } from "./cssattrs";

interface PreviewCustomisationMessage {
cssVars: Record<string, string>;
images: Record<string, string | null>;
translations: Record<string, string>;
}

function parsePreviewCustomisationMessage(
message: any
): PreviewCustomisationMessage | null {
if (message.type !== "PreviewCustomisationMessage") {
return null;
}
return {
cssVars: message.cssVars ?? {},
images: message.images ?? {},
translations: message.translations ?? {},
};
}

export class InlinePreviewController extends Controller {
static outlets = ["previewable-resource"];
static values = {
isInlinePreview: Boolean,
};

declare previewableResourceOutlets: PreviewableResourceController[];
declare isInlinePreviewValue: boolean;

windowMessageAllowedOrigins!: string[];

connect(): void {
if (!this.isInlinePreviewValue) {
return;
}
const windowMessageAllowedOrigins = ((): string[] => {
const meta: HTMLMetaElement | null = document.querySelector(
"meta[name=x-window-message-allowed-origins]"
);
const content = meta?.content ?? "";
return content.split(",").map((origin) => origin.trim());
})();
this.windowMessageAllowedOrigins = windowMessageAllowedOrigins;
if (windowMessageAllowedOrigins.length === 0) {
return;
}
window.addEventListener("message", this.onReceiveMessage);
}

disconnect(): void {
window.removeEventListener("message", this.onReceiveMessage);
}

onReceiveMessage = (e: MessageEvent<any>): void => {
if (!this.windowMessageAllowedOrigins.includes(e.origin)) {
return;
}
const customisationMessage = parsePreviewCustomisationMessage(e.data);
if (customisationMessage == null) {
return;
}
const el = this.element as HTMLElement;
for (const [name, value] of Object.entries(customisationMessage.cssVars)) {
const currentStyle = el.style.getPropertyValue(name);
if (currentStyle !== value) {
el.style.setProperty(name, value);
}
}

const keyToPreviewableResourceController: Partial<
Record<string, PreviewableResourceController>
> = {};
this.previewableResourceOutlets.forEach((outlet) => {
keyToPreviewableResourceController[outlet.keyValue] = outlet;
});

for (const [key, value] of Object.entries(
customisationMessage.translations
)) {
const outlet = keyToPreviewableResourceController[key];
outlet?.setValue(value);
}

for (const [key, value] of Object.entries(customisationMessage.images)) {
const outlet = keyToPreviewableResourceController[key];
outlet?.setValue(value);
}

injectCSSAttrs(document.documentElement);
};
}
26 changes: 26 additions & 0 deletions authui/src/previewable-resource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Controller } from "@hotwired/stimulus";

export class PreviewableResourceController extends Controller {
static values = {
key: String,
changableAttribute: String,
original: String,
};

declare keyValue: string;
declare changableAttributeValue: string;
declare originalValue: string;

setMessage(message: string): void {
this.element.innerHTML = message;
}

setValue(value: string | null): void {
const valueToSet = value != null ? value : this.originalValue;
if (this.changableAttributeValue === "innerHTML") {
this.element.innerHTML = valueToSet;
} else {
this.element.setAttribute(this.changableAttributeValue, valueToSet);
}
}
}
62 changes: 33 additions & 29 deletions pkg/auth/handler/webapp/viewmodels/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
htmltemplate "html/template"
"net/http"
"net/url"
"strings"

"github.com/gorilla/csrf"
"golang.org/x/text/language"
Expand Down Expand Up @@ -73,8 +74,9 @@ type BaseViewModel struct {
GoogleTagManagerContainerID string
TutorialMessageType string
// HasThirdPartyApp indicates whether the project has third-party client
HasThirdPartyClient bool
AuthUISentryDSN string
HasThirdPartyClient bool
AuthUISentryDSN string
AuthUIWindowMessageAllowedOrigins string

FirstNonPasskeyPrimaryAuthenticatorType string
// websocket is used in interaction only, we disable it in authflow
Expand Down Expand Up @@ -142,23 +144,24 @@ type WebappOAuthClientResolver interface {
}

type BaseViewModeler struct {
TrustProxy config.TrustProxy
OAuth *config.OAuthConfig
AuthUI *config.UIConfig
AuthUIFeatureConfig *config.UIFeatureConfig
StaticAssets StaticAssetResolver
ForgotPassword *config.ForgotPasswordConfig
Authentication *config.AuthenticationConfig
GoogleTagManager *config.GoogleTagManagerConfig
ErrorCookie ErrorCookie
Translations TranslationService
Clock clock.Clock
FlashMessage FlashMessage
DefaultLanguageTag template.DefaultLanguageTag
SupportedLanguageTags template.SupportedLanguageTags
AuthUISentryDSN config.AuthUISentryDSN
OAuthClientResolver WebappOAuthClientResolver
Logger BaseLogger
TrustProxy config.TrustProxy
OAuth *config.OAuthConfig
AuthUI *config.UIConfig
AuthUIFeatureConfig *config.UIFeatureConfig
StaticAssets StaticAssetResolver
ForgotPassword *config.ForgotPasswordConfig
Authentication *config.AuthenticationConfig
GoogleTagManager *config.GoogleTagManagerConfig
ErrorCookie ErrorCookie
Translations TranslationService
Clock clock.Clock
FlashMessage FlashMessage
DefaultLanguageTag template.DefaultLanguageTag
SupportedLanguageTags template.SupportedLanguageTags
AuthUISentryDSN config.AuthUISentryDSN
AuthUIWindowMessageAllowedOrigins config.AuthUIWindowMessageAllowedOrigins
OAuthClientResolver WebappOAuthClientResolver
Logger BaseLogger
}

func (m *BaseViewModeler) ViewModelForAuthFlow(r *http.Request, rw http.ResponseWriter) BaseViewModel {
Expand Down Expand Up @@ -264,16 +267,17 @@ func (m *BaseViewModeler) ViewModel(r *http.Request, rw http.ResponseWriter) Bas
}
return webapp.MakeURL(u, path, outQuery).String()
},
ForgotPasswordEnabled: *m.ForgotPassword.Enabled,
PublicSignupDisabled: m.Authentication.PublicSignupDisabled,
PageLoadedAt: int(now),
FlashMessageType: m.FlashMessage.Pop(r, rw),
ResolvedLanguageTag: resolvedLanguageTag,
ResolvedCLDRLocale: locale,
HTMLDir: htmlDir,
GoogleTagManagerContainerID: m.GoogleTagManager.ContainerID,
HasThirdPartyClient: hasThirdPartyApp,
AuthUISentryDSN: string(m.AuthUISentryDSN),
ForgotPasswordEnabled: *m.ForgotPassword.Enabled,
PublicSignupDisabled: m.Authentication.PublicSignupDisabled,
PageLoadedAt: int(now),
FlashMessageType: m.FlashMessage.Pop(r, rw),
ResolvedLanguageTag: resolvedLanguageTag,
ResolvedCLDRLocale: locale,
HTMLDir: htmlDir,
GoogleTagManagerContainerID: m.GoogleTagManager.ContainerID,
HasThirdPartyClient: hasThirdPartyApp,
AuthUISentryDSN: string(m.AuthUISentryDSN),
AuthUIWindowMessageAllowedOrigins: strings.Join(m.AuthUIWindowMessageAllowedOrigins, ","),
LogUnknownError: func(err map[string]interface{}) string {
if err != nil {
m.Logger.WithFields(err).Errorf("unknown error: %v", err)
Expand Down
Loading

0 comments on commit 5f4cdf5

Please sign in to comment.