Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
8798d81
web/elements: rename hasSlotted to findSlotted and refactor host styles
GirlBossRush Apr 9, 2026
0c8daa2
web/chips: tighten chip group rendering and add placeholder class
GirlBossRush Apr 9, 2026
6afe08d
web/elements: add scrollbar helpers and polish table styles
GirlBossRush Apr 9, 2026
b2f1533
web/buttons: support split-button Dropdown layout
GirlBossRush Apr 9, 2026
e995fa5
web/forms: improve form ARIA scaffolding and tighten group styles
GirlBossRush Apr 9, 2026
26b2cf9
web/elements: migrate modal plumbing to the native <dialog> element
GirlBossRush Apr 9, 2026
b833da7
web/wizards: refactor wizards to dialog-based flow
GirlBossRush Apr 9, 2026
5a35280
web/admin: migrate forms and list pages to dialog-based modals
GirlBossRush Apr 9, 2026
47b6090
web/test: update browser e2e tests for dialog-based flow
GirlBossRush Apr 9, 2026
50ef836
Fix visibility detection.
GirlBossRush Apr 10, 2026
c38a1f4
Fix layout, behavior.
GirlBossRush Apr 10, 2026
b86538f
Fix type.
GirlBossRush Apr 10, 2026
fc5b7c9
Flesh out test revisions.
GirlBossRush Apr 11, 2026
545175a
Fix type.
GirlBossRush Apr 10, 2026
ca05471
Format.
GirlBossRush Apr 11, 2026
847c5da
Merge branch 'main' into modal-revisions-3
GirlBossRush Apr 11, 2026
8eb54ea
Use plural path.
GirlBossRush Apr 11, 2026
f7a7312
Fix strict selector in Safari.
GirlBossRush Apr 11, 2026
24e9aac
Remove unused.
GirlBossRush Apr 11, 2026
32ac9ee
Spellcheck.
GirlBossRush Apr 11, 2026
54425ce
Partial type fix.
GirlBossRush Apr 11, 2026
9b32bde
Fix translation.
GirlBossRush Apr 11, 2026
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 web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"#styles/*.css": "./src/styles/*.css",
"#styles/*": "./src/styles/*.js",
"#common/*": "./src/common/*.js",
"#elements/dialogs": "./src/elements/dialogs/index.js",
"#elements/*.css": "./src/elements/*.css",
"#elements/*": "./src/elements/*.js",
"#components/*.css": "./src/components/*.css",
Expand Down
1 change: 1 addition & 0 deletions web/playwright.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export default defineConfig({
testIdAttribute: "data-test-id",
baseURL,
trace: "on-first-retry",
colorScheme: "dark",
launchOptions: {
logger: {
isEnabled() {
Expand Down
7 changes: 5 additions & 2 deletions web/src/admin/admin-settings/AdminSettingsFooterLinks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,16 @@ export class FooterLinkInput extends AKControlElement<FooterLink> {
@queryAll(".ak-form-control")
controls?: HTMLInputElement[];

json() {
@property({ type: String })
public name: string | null = null;

toJSON(): FooterLink {
return Object.fromEntries(
Array.from(this.controls ?? []).map((control) => [control.name, control.value]),
) as unknown as FooterLink;
}

get isValid() {
get valid() {
const href = this.json()?.href ?? "";
return hasLegalScheme(href) && URL.canParse(href);
}
Expand Down
18 changes: 16 additions & 2 deletions web/src/admin/admin-settings/AdminSettingsForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@ import { akFooterLinkInput, IFooterLinkInput } from "./AdminSettingsFooterLinks.
import { DEFAULT_CONFIG } from "#common/api/config";

import { Form } from "#elements/forms/Form";
import { SlottedTemplateResult } from "#elements/types";

import { AdminApi, FooterLink, Settings, SettingsRequest } from "@goauthentik/api";

import { msg } from "@lit/localize";
import { css, CSSResult, html, TemplateResult } from "lit";
import { css, CSSResult, html } from "lit";
import { customElement, property } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";

Expand Down Expand Up @@ -56,7 +57,20 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
return result;
}

protected override renderForm(): TemplateResult {
public override submitLabel = msg("Save changes");

public override renderHeader() {
return html`<div class="ak-c-form__header">
<h2 class="pf-c-title pf-m-2xl sr-only">${msg("Edit Settings")}</h2>
<div part="form-actions">${this.renderSubmitButton()}</div>
</div>`;
}

public override renderActions(): SlottedTemplateResult {
return null;
}

protected override renderForm(): SlottedTemplateResult {
const { settings } = this;

return html`
Expand Down
5 changes: 2 additions & 3 deletions web/src/admin/admin-settings/AdminSettingsPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,19 +66,18 @@ export class AdminSettingsPage extends AKElement {
if (!this.settings) return nothing;

return html`
<section class="pf-c-page__main-section pf-m-no-padding-mobile pf-l-grid pf-m-gutter">
<main class="pf-c-page__main-section pf-m-no-padding-mobile pf-l-grid pf-m-gutter">
<div class="pf-c-card">
<div class="pf-c-card__body">
<ak-admin-settings-form
id="form"
.settings=${this.settings}
action-label=${msg("Update settings")}
@ak-form-submitted=${{ handleEvent: this.#refresh, passive: true }}
>
</ak-admin-settings-form>
</div>
</div>
</section>
</main>
`;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const metadata: Meta<FooterLinkInput> = {
return;
}
const target = event.target as FooterLinkInput;
messages!.innerText = `${JSON.stringify(target.json(), null, 2)}\n\nValid: ${target.isValid ? "Yes" : "No"}`;
messages!.innerText = `${JSON.stringify(target.json(), null, 2)}\n\nValid: ${target.valid ? "Yes" : "No"}`;
});
}, 250);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const metadata: Meta<IArrayInput<unknown>> = {
return;
}
const target = event.target as FooterLinkInput;
messages!.innerText = `${JSON.stringify(target.json(), null, 2)}\n\nValid: ${target.isValid ? "Yes" : "No"}`;
messages!.innerText = `${JSON.stringify(target.json(), null, 2)}\n\nValid: ${target.valid ? "Yes" : "No"}`;
});
}, 250);

Expand Down
190 changes: 130 additions & 60 deletions web/src/admin/ak-about-modal.ts
Original file line number Diff line number Diff line change
@@ -1,72 +1,51 @@
import "#elements/EmptyState";
import "#elements/ak-progress-bar";

import { DEFAULT_CONFIG } from "#common/api/config";
import { globalAK } from "#common/global";

import { asInvoker } from "#elements/dialogs";
import { AKModal } from "#elements/dialogs/ak-modal";
import { WithBrandConfig } from "#elements/mixins/branding";
import { WithLicenseSummary } from "#elements/mixins/license";
import { AKModal } from "#elements/modals/ak-modal";
import { asInvoker } from "#elements/modals/utils";
import { SlottedTemplateResult } from "#elements/types";
import { ThemedImage } from "#elements/utils/images";

import { AdminApi, CapabilitiesEnum, LicenseSummaryStatusEnum } from "@goauthentik/api";
import {
AdminApi,
CapabilitiesEnum,
LicenseSummaryStatusEnum,
SystemInfo,
Version,
} from "@goauthentik/api";

import { msg } from "@lit/localize";
import { css, html, TemplateResult } from "lit";
import { css, html } from "lit";
import { ref } from "lit-html/directives/ref.js";
import { styleMap } from "lit-html/directives/style-map.js";
import { until } from "lit-html/directives/until.js";
import { customElement, state } from "lit/decorators.js";

import PFAbout from "@patternfly/patternfly/components/AboutModalBox/about-modal-box.css";

const DEFAULT_BRAND_IMAGE = "/static/dist/assets/images/flow_background.jpg";

type AboutEntry = [label: string, content: string | TemplateResult];
type AboutEntry = [label: string, content?: SlottedTemplateResult];

async function fetchAboutDetails(): Promise<AboutEntry[]> {
const api = new AdminApi(DEFAULT_CONFIG);

const [status, version] = await Promise.all([
api.adminSystemRetrieve(),
api.adminVersionRetrieve(),
]);

let build: string | TemplateResult = msg("Release");

if (globalAK().config.capabilities.includes(CapabilitiesEnum.CanDebug)) {
build = msg("Development");
} else if (version.buildHash) {
build = html`<a
rel="noopener noreferrer"
href="https://github.com/goauthentik/authentik/commit/${version.buildHash}"
target="_blank"
>${version.buildHash}</a
>`;
}

return [
[msg("Version"), version.versionCurrent],
[msg("UI Version"), import.meta.env.AK_VERSION],
[msg("Build"), build],
[msg("Python version"), status.runtime.pythonVersion],
[msg("Platform"), status.runtime.platform],
[msg("Kernel"), status.runtime.uname],
[
msg("OpenSSL"),
`${status.runtime.opensslVersion} ${status.runtime.opensslFipsEnabled ? "FIPS" : ""}`,
],
];
function renderEntry([label, content = null]: AboutEntry): SlottedTemplateResult {
return html`<dt>${label}</dt>
<dd>${content === null ? msg("Loading...") : content}</dd>`;
}

@customElement("ak-about-modal")
export class AboutModal extends WithLicenseSummary(WithBrandConfig(AKModal)) {
public static override formatARIALabel = () => msg("About authentik");
public override formatARIALabel = () => msg("About authentik");

public static hostStyles = [
...AKModal.hostStyles,
css`
dialog.ak-c-modal:has(ak-about-modal) {
--ak-c-modal--BackgroundColor: var(--pf-global--palette--black-900);
--ak-c-modal--BorderColor: var(--pf-global--palette--black-600);
.ak-c-dialog:has(ak-about-modal) {
--ak-c-dialog--BackgroundColor: var(--pf-global--palette--black-900);
--ak-c-dialog--BorderColor: var(--pf-global--palette--black-600);
}
`,
];
Expand All @@ -80,7 +59,7 @@ export class AboutModal extends WithLicenseSummary(WithBrandConfig(AKModal)) {
}

.pf-c-about-modal-box {
--pf-c-about-modal-box--BackgroundColor: var(--ak-c-modal--BackgroundColor);
--pf-c-about-modal-box--BackgroundColor: var(--ak-c-dialog--BackgroundColor);
width: unset;
height: 100%;
max-height: unset;
Expand All @@ -89,33 +68,118 @@ export class AboutModal extends WithLicenseSummary(WithBrandConfig(AKModal)) {
position: unset;
box-shadow: unset;
}

[part="brand"] {
position: relative;
}

[part="loading-bar"] {
position: absolute;
z-index: 1;
inset-block-start: 0;
inset-inline: 0;
}
`,
];

public static ariaLabel = msg("About authentik");

public static open = asInvoker(AboutModal);

#api = new AdminApi(DEFAULT_CONFIG);

protected canDebug = globalAK().config.capabilities.includes(CapabilitiesEnum.CanDebug);

@state()
protected entries: AboutEntry[] | null = null;
protected version: Version | null = null;

public refresh() {
return fetchAboutDetails().then((entries) => {
this.entries = entries;
@state()
protected systemInfo: SystemInfo | null = null;

@state()
protected refreshPromise: Promise<[Version, SystemInfo]> | null = null;

public refresh = (): void => {
const versionPromise = this.#api.adminVersionRetrieve();
const systemInfoPromise = this.#api.adminSystemRetrieve();

this.refreshPromise = Promise.all([versionPromise, systemInfoPromise]).then((result) => {
this.version = result[0];
this.systemInfo = result[1];

return result;
});
}
};

public connectedCallback(): void {
super.connectedCallback();
this.refresh();
}

protected renderVersionInfo = () => {
const { version } = this;

let build: SlottedTemplateResult = null;

if (this.canDebug) {
build = msg("Development");
} else if (version?.buildHash) {
build = html`<a
rel="noopener noreferrer"
href="https://github.com/goauthentik/authentik/commit/${version.buildHash}"
target="_blank"
>${version.buildHash}</a
>`;
} else if (version) {
build = msg("Release");
}

const entries: AboutEntry[] = [
[msg("Server Version"), version?.versionCurrent],
[msg("Build"), build],
];

return entries.map(renderEntry);
};

protected renderSystemInfo = () => {
const { runtime } = this.systemInfo || {};

const sslLabel = runtime
? `${runtime.opensslVersion} ${runtime.opensslFipsEnabled ? "FIPS" : ""}`
: null;

const entries: AboutEntry[] = [
[msg("Python version"), runtime?.pythonVersion],
[msg("Platform"), runtime?.platform],
[msg("OpenSSL"), sslLabel],
[
msg("Kernel"),
runtime?.uname ?? html`<div style="min-height: 3em;">${msg("Loading...")}</div>`,
],
];

return entries.map(renderEntry);
};

//#region Renderers

protected override renderCloseButton() {
return null;
}

protected renderLoadingBar(): SlottedTemplateResult {
return until(
this.refreshPromise?.then(() => null),
html`<ak-progress-bar
part="loading-bar"
indeterminate
?inert=${!!this.systemInfo && !!this.version}
label=${msg("Loading")}
></ak-progress-bar>`,
);
}

protected override render() {
let product = this.brandingTitle;

Expand All @@ -129,6 +193,7 @@ export class AboutModal extends WithLicenseSummary(WithBrandConfig(AKModal)) {
style=${styleMap({
"--pf-c-about-modal-box__hero--sm--BackgroundImage": `url(${DEFAULT_BRAND_IMAGE})`,
})}
part="box"
>
<div class="pf-c-about-modal-box__close">
<button
Expand All @@ -140,7 +205,8 @@ export class AboutModal extends WithLicenseSummary(WithBrandConfig(AKModal)) {
<i class="fas fa-times" aria-hidden="true"></i>
</button>
</div>
<div class="pf-c-about-modal-box__brand">
<div class="pf-c-about-modal-box__brand" part="brand">
${this.renderLoadingBar()}
${ThemedImage({
src: this.brandingFavicon,
alt: msg("authentik Logo"),
Expand All @@ -149,21 +215,25 @@ export class AboutModal extends WithLicenseSummary(WithBrandConfig(AKModal)) {
themedUrls: this.brandingFaviconThemedUrls,
})}
</div>
<div class="pf-c-about-modal-box__header">
<h1 class="pf-c-title pf-m-4xl" id="modal-title">${product}</h1>
<div class="pf-c-about-modal-box__header" part="header">
<h1 class="pf-c-title pf-m-4xl" id="modal-title" part="title">${product}</h1>
</div>
<div class="pf-c-about-modal-box__hero"></div>
<div class="pf-c-about-modal-box__content">
<div class="pf-c-about-modal-box__body">
<div class="pf-c-content">
${this.entries
? html`<dl>
${this.entries.map(([label, value]) => {
return html`<dt>${label}</dt>
<dd>${value}</dd>`;
})}
</dl>`
: html`<ak-empty-state loading></ak-empty-state>`}
<dl>
<dt>${msg("UI Version")}</dt>
<dd>${import.meta.env.AK_VERSION}</dd>
${until(
this.refreshPromise?.then(this.renderVersionInfo),
this.renderVersionInfo(),
)}
${until(
this.refreshPromise?.then(this.renderSystemInfo),
this.renderSystemInfo(),
)}
</dl>
</div>
</div>
<p class="pf-c-about-modal-box__strapline"></p>
Expand Down
Loading
Loading