Skip to content

Commit fab912f

Browse files
committed
Cherry-pick #19141 to version-2025.12 (with conflicts)
This cherry-pick has conflicts that need manual resolution. Original PR: #19141 Original commit: 2c813cb
1 parent 1c0a3f9 commit fab912f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+1961
-1013
lines changed

tests/e2e/test_source_oauth_oauth2.py

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -89,26 +89,7 @@ def find_settings_tab_panel(self, tab_name: str, panel_content_selector: str):
8989

9090
interface = self.driver.find_element(By.CSS_SELECTOR, "ak-interface-user").shadow_root
9191

92-
interface_wait = WebDriverWait(interface, INTERFACE_TIMEOUT)
93-
94-
try:
95-
interface_wait.until(
96-
ec.presence_of_element_located((By.CSS_SELECTOR, "ak-interface-user-presentation"))
97-
)
98-
except TimeoutException:
99-
snippet = context.text.strip()[:1000].replace("\n", " ")
100-
self.fail(
101-
f"Timed out waiting for element text to appear at {self.driver.current_url}. "
102-
f"Current content: {snippet or '<empty>'}"
103-
)
104-
105-
interface_presentation = interface.find_element(
106-
By.CSS_SELECTOR, "ak-interface-user-presentation"
107-
).shadow_root
108-
109-
user_settings = interface_presentation.find_element(
110-
By.CSS_SELECTOR, "ak-user-settings"
111-
).shadow_root
92+
user_settings = interface.find_element(By.CSS_SELECTOR, "ak-user-settings").shadow_root
11293

11394
tab_panel = user_settings.find_element(By.CSS_SELECTOR, panel_content_selector).shadow_root
11495

web/logger/browser.js

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/**
2+
* @file Console logger for browser environments.
3+
*
4+
* @remarks
5+
* The repetition of log levels, typedefs, and method signatures is intentional
6+
* to give IDEs and type checkers a mapping of log methods to the TypeScript
7+
* provided JSDoc comments.
8+
*
9+
* Additionally, no wrapper functions are used to avoid the browser's console
10+
* reported call site being the wrapper instead of the actual caller.
11+
*/
12+
13+
/* eslint-disable no-console */
14+
15+
//#region Functions
16+
17+
/**
18+
* @typedef {object} Logger
19+
* @property {typeof console.info} info;
20+
* @property {typeof console.warn} warn;
21+
* @property {typeof console.error} error;
22+
* @property {typeof console.debug} debug;
23+
* @property {typeof console.trace} trace;
24+
*/
25+
26+
/**
27+
* Labels log levels in the browser console.
28+
*/
29+
const LogLevelLabel = /** @type {const} */ ({
30+
info: "[INFO]",
31+
warn: "[WARN]",
32+
error: "[ERROR]",
33+
debug: "[DEBUG]",
34+
trace: "[TRACE]",
35+
});
36+
37+
/**
38+
* @typedef {keyof typeof LogLevelLabel} LogLevel
39+
*/
40+
41+
/**
42+
* Predefined log levels.
43+
*/
44+
const LogLevels = /** @type {LogLevel[]} */ (Object.keys(LogLevelLabel));
45+
46+
/**
47+
* Colors for log levels in the browser console.
48+
*
49+
* @remarks
50+
*
51+
* The colors are derived from Carbon Design System's palette to ensure
52+
* sufficient contrast and accessibility across light and dark themes.
53+
*/
54+
const LogLevelColors = /** @type {const} */ ({
55+
info: `light-dark(#0043CE, #4589FF)`,
56+
warn: `light-dark(#F1C21B, #F1C21B)`,
57+
error: `light-dark(#DA1E28, #FA4D56)`,
58+
debug: `light-dark(#8A3FFC, #A56EFF)`,
59+
trace: `light-dark(#8A3FFC, #A56EFF)`,
60+
});
61+
62+
/**
63+
* Creates a logger with the given prefix.
64+
*
65+
* @param {string} [prefix]
66+
* @param {...string} args
67+
* @returns {Logger}
68+
*
69+
*/
70+
export function createLogger(prefix, ...args) {
71+
const suffix = prefix ? `(${prefix}):` : ":";
72+
73+
/**
74+
* @type {Partial<Logger>}
75+
*/
76+
const logger = {};
77+
78+
for (const level of LogLevels) {
79+
const label = LogLevelLabel[level];
80+
const color = LogLevelColors[level];
81+
82+
logger[level] = console[level].bind(
83+
console,
84+
`%c${label}%c ${suffix}%c`,
85+
`font-weight: 700; color: ${color};`,
86+
`font-weight: 600; color: CanvasText;`,
87+
"",
88+
...args,
89+
);
90+
}
91+
92+
return /** @type {Logger} */ (logger);
93+
}
94+
95+
//#endregion
96+
97+
//#region Console Logger
98+
99+
/**
100+
* @typedef {Logger & {prefix: (logPrefix: string) => Logger}} IConsoleLogger
101+
*/
102+
103+
/**
104+
* A singleton logger instance for the browser.
105+
*
106+
* ```js
107+
* import { ConsoleLogger } from "#logger/browser";
108+
*
109+
* ConsoleLogger.info("Hello, world!");
110+
* ```
111+
*
112+
* @implements {IConsoleLogger}
113+
* @runtime browser
114+
*/
115+
// @ts-expect-error Logging properties are dynamically assigned.
116+
export class ConsoleLogger {
117+
/** @type {typeof console.info} */
118+
static info;
119+
/** @type {typeof console.warn} */
120+
static warn;
121+
/** @type {typeof console.error} */
122+
static error;
123+
/** @type {typeof console.debug} */
124+
static debug;
125+
/** @type {typeof console.trace} */
126+
static trace;
127+
128+
/**
129+
* Creates a logger with the given prefix.
130+
* @param {string} logPrefix
131+
*/
132+
static prefix(logPrefix) {
133+
return createLogger(logPrefix);
134+
}
135+
}
136+
137+
Object.assign(ConsoleLogger, createLogger());
138+
139+
//#endregion

web/src/admin/AdminInterface/index.entrypoint.css

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,6 @@
1414
}
1515
}
1616

17-
.pf-c-page__main,
18-
.pf-c-drawer__content,
19-
.pf-c-page__drawer {
20-
z-index: auto !important;
21-
background-color: transparent;
22-
}
23-
24-
.display-none {
25-
display: none;
26-
}
27-
2817
ak-page-navbar {
2918
grid-area: header;
3019
}
@@ -43,5 +32,5 @@ ak-sidebar-item:active ak-sidebar-item::part(list-item) {
4332
}
4433

4534
.pf-c-drawer__panel {
46-
z-index: var(--pf-global--ZIndex--xl);
35+
background-color: transparent !important;
4736
}

web/src/admin/AdminInterface/index.entrypoint.ts

Lines changed: 39 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ import "#admin/AdminInterface/AboutModal";
22
import "#elements/banner/EnterpriseStatusBanner";
33
import "#elements/banner/VersionBanner";
44
import "#elements/messages/MessageContainer";
5-
import "#elements/notifications/APIDrawer";
6-
import "#elements/notifications/NotificationDrawer";
75
import "#elements/router/RouterOutlet";
86
import "#elements/sidebar/Sidebar";
97
import "#elements/sidebar/SidebarItem";
@@ -15,15 +13,22 @@ import {
1513
} from "./AdminSidebar.js";
1614

1715
import { isAPIResultReady } from "#common/api/responses";
18-
import { EVENT_API_DRAWER_TOGGLE, EVENT_NOTIFICATION_DRAWER_TOGGLE } from "#common/constants";
1916
import { configureSentry } from "#common/sentry/index";
2017
import { isGuest } from "#common/users";
21-
import { WebsocketClient } from "#common/ws";
18+
import { WebsocketClient } from "#common/ws/WebSocketClient";
2219

2320
import { AuthenticatedInterface } from "#elements/AuthenticatedInterface";
21+
import { listen } from "#elements/decorators/listen";
2422
import { WithCapabilitiesConfig } from "#elements/mixins/capabilities";
23+
import { WithNotifications } from "#elements/mixins/notifications";
2524
import { canAccessAdmin, WithSession } from "#elements/mixins/session";
26-
import { getURLParam, updateURLParams } from "#elements/router/RouteMatch";
25+
import { AKDrawerChangeEvent } from "#elements/notifications/events";
26+
import {
27+
DrawerState,
28+
persistDrawerParams,
29+
readDrawerParams,
30+
renderNotificationDrawerPanel,
31+
} from "#elements/notifications/utils";
2732

2833
import { PageNavMenuToggle } from "#components/ak-page-navbar";
2934

@@ -34,28 +39,36 @@ import { ROUTES } from "#admin/Routes";
3439
import { CapabilitiesEnum } from "@goauthentik/api";
3540

3641
import { CSSResult, html, nothing, PropertyValues, TemplateResult } from "lit";
37-
import { customElement, property, query } from "lit/decorators.js";
42+
import { customElement, property, query, state } from "lit/decorators.js";
3843
import { classMap } from "lit/directives/class-map.js";
3944

4045
import PFButton from "@patternfly/patternfly/components/Button/button.css";
4146
import PFDrawer from "@patternfly/patternfly/components/Drawer/drawer.css";
4247
import PFNav from "@patternfly/patternfly/components/Nav/nav.css";
4348
import PFPage from "@patternfly/patternfly/components/Page/page.css";
44-
import PFBase from "@patternfly/patternfly/patternfly-base.css";
4549

4650
if (process.env.NODE_ENV === "development") {
4751
await import("@goauthentik/esbuild-plugin-live-reload/client");
4852
}
4953

5054
@customElement("ak-interface-admin")
51-
export class AdminInterface extends WithCapabilitiesConfig(WithSession(AuthenticatedInterface)) {
52-
//#region Properties
55+
export class AdminInterface extends WithCapabilitiesConfig(
56+
WithNotifications(WithSession(AuthenticatedInterface)),
57+
) {
58+
//#region Styles
59+
60+
public static readonly styles: CSSResult[] = [
61+
// ---
62+
PFPage,
63+
PFButton,
64+
PFDrawer,
65+
PFNav,
66+
Styles,
67+
];
5368

54-
@property({ type: Boolean })
55-
public notificationDrawerOpen = getURLParam("notificationDrawerOpen", false);
69+
//#endregion
5670

57-
@property({ type: Boolean })
58-
public apiDrawerOpen = getURLParam("apiDrawerOpen", false);
71+
//#region Properties
5972

6073
@query("ak-about-modal")
6174
public aboutModal?: AboutModal;
@@ -74,19 +87,14 @@ export class AdminInterface extends WithCapabilitiesConfig(WithSession(Authentic
7487

7588
//#endregion
7689

77-
//#region Styles
90+
@state()
91+
protected drawer: DrawerState = readDrawerParams();
7892

79-
static styles: CSSResult[] = [
80-
// ---
81-
PFBase,
82-
PFPage,
83-
PFButton,
84-
PFDrawer,
85-
PFNav,
86-
Styles,
87-
];
88-
89-
//#endregion
93+
@listen(AKDrawerChangeEvent)
94+
protected drawerListener = (event: AKDrawerChangeEvent) => {
95+
this.drawer = event.drawer;
96+
persistDrawerParams(event.drawer);
97+
};
9098

9199
//#region Lifecycle
92100

@@ -99,6 +107,7 @@ export class AdminInterface extends WithCapabilitiesConfig(WithSession(Authentic
99107

100108
this.#sidebarMatcher = window.matchMedia("(min-width: 1200px)");
101109
this.sidebarOpen = this.#sidebarMatcher.matches;
110+
102111
this.addEventListener(PageNavMenuToggle.eventName, this.#onPageNavMenuEvent, {
103112
passive: true,
104113
});
@@ -107,27 +116,14 @@ export class AdminInterface extends WithCapabilitiesConfig(WithSession(Authentic
107116
public connectedCallback() {
108117
super.connectedCallback();
109118

110-
window.addEventListener(EVENT_NOTIFICATION_DRAWER_TOGGLE, () => {
111-
this.notificationDrawerOpen = !this.notificationDrawerOpen;
112-
updateURLParams({
113-
notificationDrawerOpen: this.notificationDrawerOpen,
114-
});
115-
});
116-
117-
window.addEventListener(EVENT_API_DRAWER_TOGGLE, () => {
118-
this.apiDrawerOpen = !this.apiDrawerOpen;
119-
updateURLParams({
120-
apiDrawerOpen: this.apiDrawerOpen,
121-
});
122-
});
123-
124119
this.#sidebarMatcher.addEventListener("change", this.#sidebarMediaQueryListener, {
125120
passive: true,
126121
});
127122
}
128123

129124
public disconnectedCallback(): void {
130125
super.disconnectedCallback();
126+
131127
this.#sidebarMatcher.removeEventListener("change", this.#sidebarMediaQueryListener);
132128

133129
WebsocketClient.close();
@@ -154,11 +150,10 @@ export class AdminInterface extends WithCapabilitiesConfig(WithSession(Authentic
154150
"pf-m-collapsed": !this.sidebarOpen,
155151
};
156152

157-
const drawerOpen = this.notificationDrawerOpen || this.apiDrawerOpen;
158-
153+
const openDrawerCount = (this.drawer.notifications ? 1 : 0) + (this.drawer.api ? 1 : 0);
159154
const drawerClasses = {
160-
"pf-m-expanded": drawerOpen,
161-
"pf-m-collapsed": !drawerOpen,
155+
"pf-m-expanded": openDrawerCount !== 0,
156+
"pf-m-collapsed": openDrawerCount === 0,
162157
};
163158

164159
return html`<div class="pf-c-page">
@@ -190,18 +185,7 @@ export class AdminInterface extends WithCapabilitiesConfig(WithSession(Authentic
190185
</ak-router-outlet>
191186
</div>
192187
</div>
193-
<ak-notification-drawer
194-
class="pf-c-drawer__panel pf-m-width-33 ${this.notificationDrawerOpen
195-
? ""
196-
: "display-none"}"
197-
?hidden=${!this.notificationDrawerOpen}
198-
></ak-notification-drawer>
199-
<ak-api-drawer
200-
class="pf-c-drawer__panel pf-m-width-33 ${this.apiDrawerOpen
201-
? ""
202-
: "display-none"}"
203-
?hidden=${!this.apiDrawerOpen}
204-
></ak-api-drawer>
188+
${renderNotificationDrawerPanel(this.drawer)}
205189
<ak-about-modal></ak-about-modal>
206190
</div>
207191
</div>

0 commit comments

Comments
 (0)