From f179d6572e2e3776c53efbecb0ed9957af2ecd46 Mon Sep 17 00:00:00 2001 From: Ken Sternberg <133134217+kensternberg-authentik@users.noreply.github.com> Date: Fri, 16 Jun 2023 04:36:04 -0700 Subject: [PATCH] web: Storybook css import fix (#5964) * web: fix storybook `build` css import issue This is an incredibly frustrating issue, because Storybook works in `dev` mode but not in `build` mode, and that's not at all what you'd expecte from a mature piece of software. Lit uses the native CSS adoptedStylesheets field, which takes only a constructedStylesheet. Lit provides a way of generating those, but the imports from Patternfly (or any `.css` file) are text, and converting those to stylesheets required a bit of magic. What this means going forward is that any Storied components will have to have their CSS wrapped in a way that ensures it is managed correctly by Lit (well, to be pedantic, by the shadowDOM.adoptedStylesheets). That wrapper is provided and the components that need it have been wrapped. This problem deserves further investigation, but for the time being this actually does solve it with a minimum amount of surgical pain. * web: fix storybook build issue This commit further fixes the typing issues around strings, CSSResults, and CSSStyleSheets by providing overloaded functions that assist consumers in knowing that if they send an array to expect an array in return, and if they send a scalar expect a scalar in return. * replace any with unknown Signed-off-by: Jens Langhammer --------- Signed-off-by: Jens Langhammer Co-authored-by: Jens Langhammer --- web/src/common/utils.ts | 27 +++++++++++++++++++ web/src/elements/Base.ts | 6 ++--- .../user/LibraryPage/ApplicationEmptyState.ts | 5 ++-- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/web/src/common/utils.ts b/web/src/common/utils.ts index d1e59e4a9..1ae395b4e 100644 --- a/web/src/common/utils.ts +++ b/web/src/common/utils.ts @@ -1,5 +1,7 @@ import { SentryIgnoredError } from "@goauthentik/common/errors"; +import { CSSResult, css } from "lit"; + export function getCookie(name: string): string { let cookieValue = ""; if (document.cookie && document.cookie !== "") { @@ -115,3 +117,28 @@ export function dateTimeLocal(date: Date): string { const parts = localISOTime.split(":"); return `${parts[0]}:${parts[1]}`; } + +// Lit is extremely well-typed with regard to CSS, and Storybook's `build` does not currently have a +// coherent way of importing CSS-as-text into CSSStyleSheet. It works well when Storybook is running +// in `dev,` but in `build` it fails. Storied components will have to map their textual CSS imports +// using the function below. +type AdaptableStylesheet = Readonly; +type AdaptedStylesheets = CSSStyleSheet | CSSStyleSheet[]; + +const isCSSResult = (v: unknown): v is CSSResult => + v instanceof CSSResult && v.styleSheet !== undefined; + +// prettier-ignore +export const _adaptCSS = (sheet: AdaptableStylesheet): CSSStyleSheet => + (typeof sheet === "string" ? css([sheet] as unknown as TemplateStringsArray, ...[]).styleSheet + : isCSSResult(sheet) ? sheet.styleSheet + : sheet) as CSSStyleSheet; + +// Overloaded function definitions inform consumers that if you pass it an array, expect an array in +// return; if you pass it a scaler, expect a scalar in return. + +export function adaptCSS(sheet: AdaptableStylesheet): CSSStyleSheet; +export function adaptCSS(sheet: AdaptableStylesheet[]): CSSStyleSheet[]; +export function adaptCSS(sheet: AdaptableStylesheet | AdaptableStylesheet[]): AdaptedStylesheets { + return Array.isArray(sheet) ? sheet.map(_adaptCSS) : _adaptCSS(sheet); +} diff --git a/web/src/elements/Base.ts b/web/src/elements/Base.ts index 8ffbbf3dc..f0e0b6108 100644 --- a/web/src/elements/Base.ts +++ b/web/src/elements/Base.ts @@ -1,8 +1,9 @@ import { config, tenant } from "@goauthentik/common/api/config"; import { EVENT_LOCALE_CHANGE, EVENT_THEME_CHANGE } from "@goauthentik/common/constants"; import { UIConfig, uiConfig } from "@goauthentik/common/ui/config"; +import { adaptCSS } from "@goauthentik/common/utils"; -import { CSSResult, LitElement } from "lit"; +import { LitElement } from "lit"; import { state } from "lit/decorators.js"; import AKGlobal from "@goauthentik/common/styles/authentik.css"; @@ -68,8 +69,7 @@ export class AKElement extends LitElement { if ("ShadyDOM" in window) { styleRoot = document; } - const globalStyleSheet = AKGlobal instanceof CSSResult ? AKGlobal.styleSheet : AKGlobal; - styleRoot.adoptedStyleSheets = [...styleRoot.adoptedStyleSheets, globalStyleSheet]; + styleRoot.adoptedStyleSheets = adaptCSS([...styleRoot.adoptedStyleSheets, AKGlobal]); this._initTheme(styleRoot); this._initCustomCSS(styleRoot); return root; diff --git a/web/src/user/LibraryPage/ApplicationEmptyState.ts b/web/src/user/LibraryPage/ApplicationEmptyState.ts index 314837926..11871c139 100644 --- a/web/src/user/LibraryPage/ApplicationEmptyState.ts +++ b/web/src/user/LibraryPage/ApplicationEmptyState.ts @@ -1,4 +1,5 @@ import { docLink } from "@goauthentik/common/global"; +import { adaptCSS } from "@goauthentik/common/utils"; import { AKElement } from "@goauthentik/elements/Base"; import { paramURL } from "@goauthentik/elements/router/RouterOutlet"; @@ -19,7 +20,7 @@ import PFSpacing from "@patternfly/patternfly/utilities/Spacing/spacing.css"; * administrator, provide a link to the "Create a new application" page. */ -const styles = [ +const styles = adaptCSS([ PFBase, PFEmptyState, PFButton, @@ -31,7 +32,7 @@ const styles = [ font-weight: bold; } `, -]; +]); @customElement("ak-library-application-empty-list") export class LibraryPageApplicationEmptyList extends AKElement {