mirror of
https://github.com/goauthentik/authentik
synced 2026-05-07 07:32:23 +02:00
Compare commits
2 Commits
blueprint_
...
lit-jsx-co
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d019488bec | ||
|
|
6694529317 |
23
web/package-lock.json
generated
23
web/package-lock.json
generated
@@ -27,6 +27,7 @@
|
||||
"@goauthentik/core": "^1.0.0",
|
||||
"@goauthentik/esbuild-plugin-live-reload": "^1.1.0",
|
||||
"@goauthentik/eslint-config": "^1.0.5",
|
||||
"@goauthentik/lit-jsx": "^1.0.0",
|
||||
"@goauthentik/prettier-config": "^3.1.0",
|
||||
"@goauthentik/tsconfig": "^1.0.4",
|
||||
"@hcaptcha/types": "^1.0.4",
|
||||
@@ -1548,6 +1549,10 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@goauthentik/lit-jsx": {
|
||||
"resolved": "packages/lit-jsx",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@goauthentik/prettier-config": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/prettier-config/-/prettier-config-3.1.0.tgz",
|
||||
@@ -26972,6 +26977,24 @@
|
||||
"esbuild": "^0.25.5"
|
||||
}
|
||||
},
|
||||
"packages/lit-jsx": {
|
||||
"name": "@goauthentik/lit-jsx",
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@goauthentik/tsconfig": "^1.0.4",
|
||||
"@types/react": "^19.1.8",
|
||||
"@types/react-dom": "^19.1.6",
|
||||
"lit": "^3.3.1",
|
||||
"typescript": "^5.8.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=24"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"lit": "^3.3.1"
|
||||
}
|
||||
},
|
||||
"packages/monorepo": {
|
||||
"name": "@goauthentik/monorepo",
|
||||
"version": "1.0.0",
|
||||
|
||||
@@ -98,6 +98,7 @@
|
||||
"@goauthentik/core": "^1.0.0",
|
||||
"@goauthentik/esbuild-plugin-live-reload": "^1.1.0",
|
||||
"@goauthentik/eslint-config": "^1.0.5",
|
||||
"@goauthentik/lit-jsx": "^1.0.0",
|
||||
"@goauthentik/prettier-config": "^3.1.0",
|
||||
"@goauthentik/tsconfig": "^1.0.4",
|
||||
"@hcaptcha/types": "^1.0.4",
|
||||
|
||||
31
web/packages/lit-jsx/jsx-runtime.d.ts
vendored
Normal file
31
web/packages/lit-jsx/jsx-runtime.d.ts
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
import { JSX } from "react";
|
||||
|
||||
import { nothing, TemplateResult } from "lit";
|
||||
|
||||
export = LitJSX;
|
||||
export as namespace LitJSX;
|
||||
|
||||
declare namespace LitJSX {
|
||||
export type CustomElementConstructor<T extends HTMLElement = HTMLElement> = new (
|
||||
...args: any[]
|
||||
) => T;
|
||||
|
||||
export type CustomElementComponent<P = any> = (props: P) => ElementType;
|
||||
|
||||
export type ElementType<
|
||||
P = any,
|
||||
Tag extends keyof React.JSX.IntrinsicElements = keyof React.JSX.IntrinsicElements,
|
||||
> =
|
||||
| { [K in Tag]: P extends React.JSX.IntrinsicElements[K] ? K : never }[Tag]
|
||||
| CustomElementConstructor<P>;
|
||||
|
||||
export interface Fragment {
|
||||
children: LitJSX.ElementType | LitJSX.ElementType[];
|
||||
}
|
||||
|
||||
export type LitNode = JSX.Element | ElementType | string | TemplateResult | typeof nothing;
|
||||
|
||||
export type FC<P = any> = (props: P) => LitNode | LitNode[];
|
||||
|
||||
export { JSX };
|
||||
}
|
||||
47
web/packages/lit-jsx/jsx-runtime.js
Normal file
47
web/packages/lit-jsx/jsx-runtime.js
Normal file
@@ -0,0 +1,47 @@
|
||||
import { createElement } from "./utils.js";
|
||||
|
||||
/**
|
||||
* @param {unknown} elementLike
|
||||
* @returns {elementLike is LitJSX.CustomElementConstructor}
|
||||
*/
|
||||
function isCustomElementConstructor(elementLike) {
|
||||
return typeof elementLike === "function" && elementLike.prototype instanceof HTMLElement;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {LitJSX.ElementType | LitJSX.CustomElementComponent} elementLike
|
||||
* @param {unknown} props
|
||||
*/
|
||||
export function jsx(elementLike, props) {
|
||||
console.log({ elementLike, props });
|
||||
if (typeof elementLike === "function") {
|
||||
if (isCustomElementConstructor(elementLike)) {
|
||||
const tagName = customElements.getName(elementLike);
|
||||
|
||||
if (!tagName) {
|
||||
throw new Error(`Custom element ${elementLike.name} is not registered`);
|
||||
}
|
||||
|
||||
// Render the custom web component as any other html element.
|
||||
return createElement(tagName, props);
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
return elementLike(props);
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
return createElement(elementLike, props);
|
||||
}
|
||||
|
||||
export { jsx as jsxs };
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {LitJSX.Fragment} fragment
|
||||
* @returns {LitJSX.ElementType[]}
|
||||
*/
|
||||
export function Fragment(fragment) {
|
||||
return Array.isArray(fragment.children) ? fragment.children : [fragment.children];
|
||||
}
|
||||
32
web/packages/lit-jsx/package.json
Normal file
32
web/packages/lit-jsx/package.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "@goauthentik/lit-jsx",
|
||||
"version": "1.0.0",
|
||||
"description": "JSX Runtime for Lit",
|
||||
"license": "MIT",
|
||||
"main": "tsconfig.json",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"import": "./index.js",
|
||||
"types": "./index.d.ts"
|
||||
},
|
||||
"./jsx-runtime": "./jsx-runtime.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@goauthentik/tsconfig": "^1.0.4",
|
||||
"@types/react": "^19.1.8",
|
||||
"@types/react-dom": "^19.1.6",
|
||||
"lit": "^3.3.1",
|
||||
"typescript": "^5.8.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"lit": "^3.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=24"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
9
web/packages/lit-jsx/tsconfig.json
Normal file
9
web/packages/lit-jsx/tsconfig.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "@goauthentik/tsconfig",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"checkJs": true,
|
||||
"emitDeclarationOnly": true,
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext"]
|
||||
}
|
||||
}
|
||||
63
web/packages/lit-jsx/utils.js
Normal file
63
web/packages/lit-jsx/utils.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import { spread } from "@open-wc/lit-helpers";
|
||||
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
import { ref } from "lit/directives/ref.js";
|
||||
import { html, unsafeStatic } from "lit/static-html.js";
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} tagName
|
||||
* @param {Record<PropertyKey, unknown>} props
|
||||
* @returns
|
||||
*/
|
||||
export function parseProps(tagName, props) {
|
||||
/**
|
||||
* @type {Record<PropertyKey, unknown>}
|
||||
*/
|
||||
const spreadable = {};
|
||||
const ElementConstructor = customElements.get(tagName);
|
||||
|
||||
for (const [propName, value] of Object.entries(props)) {
|
||||
if (propName === "htmlFor") {
|
||||
spreadable.for = value;
|
||||
continue;
|
||||
}
|
||||
if (propName.startsWith("on")) {
|
||||
const eventName = propName.slice(2).toLowerCase();
|
||||
spreadable[`@${eventName}`] = value;
|
||||
} else if (typeof value === "boolean") {
|
||||
spreadable[`?${propName}`] = value;
|
||||
} else if (ElementConstructor) {
|
||||
spreadable[`.${propName}`] = value;
|
||||
} else {
|
||||
spreadable[`${propName}`] = value;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(spreadable);
|
||||
|
||||
return spreadable;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} tagName
|
||||
* @param {*} props
|
||||
* @returns
|
||||
*/
|
||||
export function createElement(
|
||||
tagName,
|
||||
{ className, children, ref: refProp, style = {}, ...props },
|
||||
) {
|
||||
const tag = unsafeStatic(tagName);
|
||||
|
||||
const result = html`
|
||||
<${tag} class="${ifDefined(className)}"
|
||||
${ref(refProp)}
|
||||
${spread(parseProps(tagName, props))}>
|
||||
${children}
|
||||
</${tag}>
|
||||
`;
|
||||
|
||||
return result;
|
||||
}
|
||||
22
web/src/components/ak-field-errors.tsx
Normal file
22
web/src/components/ak-field-errors.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import { ErrorDetail } from "@goauthentik/api";
|
||||
|
||||
import { nothing } from "lit";
|
||||
|
||||
export interface AKFormErrorsProps {
|
||||
errors?: ErrorDetail[];
|
||||
}
|
||||
|
||||
export const AKFormErrors: LitJSX.FC<AKFormErrorsProps> = ({ errors } = {}) => {
|
||||
if (!errors?.length) return nothing;
|
||||
|
||||
return errors.map((error) => {
|
||||
return (
|
||||
<p className="pf-c-form__helper-text pf-m-error">
|
||||
<span className="pf-c-form__helper-text-icon">
|
||||
<i className="fas fa-exclamation-circle" aria-hidden="true"></i>
|
||||
</span>
|
||||
{error.string}
|
||||
</p>
|
||||
);
|
||||
});
|
||||
};
|
||||
30
web/src/components/ak-label.tsx
Normal file
30
web/src/components/ak-label.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import { FC } from "@goauthentik/lit-jsx/jsx-runtime";
|
||||
|
||||
import type { LabelHTMLAttributes } from "react";
|
||||
|
||||
import { nothing } from "lit";
|
||||
|
||||
export interface FormLabelProps extends LabelHTMLAttributes<HTMLLabelElement> {
|
||||
required?: boolean;
|
||||
children?: string;
|
||||
}
|
||||
|
||||
export const AKLabel: FC<FormLabelProps> = ({
|
||||
required,
|
||||
htmlFor,
|
||||
children,
|
||||
...labelAttributes
|
||||
} = {}) => {
|
||||
if (!children) return nothing;
|
||||
|
||||
return (
|
||||
<label className="pf-c-form__label" htmlFor={htmlFor} {...labelAttributes}>
|
||||
<span className="pf-c-form__label-text">{children}</span>
|
||||
{required ? (
|
||||
<span className="pf-c-form__label-required" aria-hidden="true">
|
||||
*
|
||||
</span>
|
||||
) : null}
|
||||
</label>
|
||||
);
|
||||
};
|
||||
@@ -21,6 +21,7 @@ import diffGrammar from "highlight.js/lib/languages/diff";
|
||||
import confGrammar from "highlight.js/lib/languages/ini";
|
||||
import nginxGrammar from "highlight.js/lib/languages/nginx";
|
||||
import { common } from "lowlight";
|
||||
import React from "react";
|
||||
import { createRoot, Root } from "react-dom/client";
|
||||
import * as runtime from "react/jsx-runtime";
|
||||
import rehypeHighlight, { Options as HighlightOptions } from "rehype-highlight";
|
||||
@@ -229,15 +230,17 @@ export class AKMDX extends AKElement {
|
||||
const { frontmatter = {} } = mdxExports;
|
||||
|
||||
this.#reactRoot.render(
|
||||
<MDXModuleContext.Provider value={mdxModule}>
|
||||
<Content
|
||||
frontmatter={frontmatter}
|
||||
components={{
|
||||
React.createElement(
|
||||
MDXModuleContext.Provider,
|
||||
{ value: mdxModule },
|
||||
React.createElement(Content, {
|
||||
frontmatter,
|
||||
components: {
|
||||
wrapper: MDXWrapper,
|
||||
a: MDXAnchor,
|
||||
}}
|
||||
/>
|
||||
</MDXModuleContext.Provider>,
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,15 +49,15 @@ export const MDXAnchor = ({
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<a
|
||||
href={href}
|
||||
onClick={interceptHeadingLinks}
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
return React.createElement(
|
||||
"a",
|
||||
{
|
||||
href,
|
||||
onClick: interceptHeadingLinks,
|
||||
rel: "noopener noreferrer",
|
||||
target: "_blank",
|
||||
...props,
|
||||
},
|
||||
children,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -13,8 +13,8 @@ export const MDXWrapper = ({ children, frontmatter }: MDXWrapperProps) => {
|
||||
const nextChildren = React.Children.toArray(children);
|
||||
|
||||
if (title) {
|
||||
nextChildren.unshift(<h1 key="header-title">{title}</h1>);
|
||||
nextChildren.unshift(React.createElement("h1", { key: "header-title" }, title));
|
||||
}
|
||||
|
||||
return <div className="pf-c-content">{nextChildren}</div>;
|
||||
return React.createElement("div", { className: "pf-c-content" }, nextChildren);
|
||||
};
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
import { AKElement } from "#elements/Base";
|
||||
|
||||
import { ErrorDetail } from "@goauthentik/api";
|
||||
|
||||
import { CSSResult, html, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
/**
|
||||
* This is used in two places outside of Flow, and in both cases is used primarily to
|
||||
* display content, not take input. It displays the TOTP QR code, and the static
|
||||
* recovery tokens. But it's used a lot in Flow.
|
||||
*/
|
||||
|
||||
@customElement("ak-form-element")
|
||||
export class FormElement extends AKElement {
|
||||
static styles: CSSResult[] = [PFBase, PFForm, PFFormControl];
|
||||
|
||||
@property()
|
||||
label?: string;
|
||||
|
||||
@property({ type: Boolean })
|
||||
required = false;
|
||||
|
||||
@property({ attribute: false })
|
||||
set errors(value: ErrorDetail[] | undefined) {
|
||||
this._errors = value;
|
||||
const hasError = (value || []).length > 0;
|
||||
this.querySelectorAll("input").forEach((input) => {
|
||||
input.setAttribute("aria-invalid", hasError.toString());
|
||||
});
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
_errors?: ErrorDetail[];
|
||||
|
||||
updated(): void {
|
||||
this.querySelectorAll<HTMLInputElement>("input[autofocus]").forEach((input) => {
|
||||
input.focus();
|
||||
});
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
return html`<div class="pf-c-form__group">
|
||||
<label class="pf-c-form__label">
|
||||
<span class="pf-c-form__label-text">${this.label}</span>
|
||||
${this.required
|
||||
? html`<span class="pf-c-form__label-required" aria-hidden="true">*</span>`
|
||||
: html``}
|
||||
</label>
|
||||
<slot></slot>
|
||||
${(this._errors || []).map((error) => {
|
||||
return html`<p class="pf-c-form__helper-text pf-m-error">
|
||||
<span class="pf-c-form__helper-text-icon">
|
||||
<i class="fas fa-exclamation-circle" aria-hidden="true"></i> </span
|
||||
>${error.string}
|
||||
</p>`;
|
||||
})}
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ak-form-element": FormElement;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
import "#elements/forms/FormElement";
|
||||
|
||||
import { AKElement } from "#elements/Base";
|
||||
import { bound } from "#elements/decorators/bound";
|
||||
import { isActiveElement } from "#elements/utils/focus";
|
||||
|
||||
import { AKLabel } from "#components/ak-label";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
@@ -12,6 +12,7 @@ import { ifDefined } from "lit/directives/if-defined.js";
|
||||
import { createRef, ref, Ref } from "lit/directives/ref.js";
|
||||
|
||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
||||
import PFInputGroup from "@patternfly/patternfly/components/InputGroup/input-group.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
@@ -40,7 +41,7 @@ const Visibility = {
|
||||
|
||||
@customElement("ak-flow-input-password")
|
||||
export class InputPassword extends AKElement {
|
||||
static styles = [PFBase, PFInputGroup, PFFormControl, PFButton];
|
||||
static styles = [PFBase, PFForm, PFInputGroup, PFFormControl, PFButton];
|
||||
|
||||
//#region Properties
|
||||
|
||||
@@ -50,7 +51,7 @@ export class InputPassword extends AKElement {
|
||||
* @attr
|
||||
*/
|
||||
@property({ type: String, attribute: "input-id" })
|
||||
inputId = "ak-stage-password-input";
|
||||
public inputID = "ak-stage-password-input";
|
||||
|
||||
/**
|
||||
* The name of the input field.
|
||||
@@ -58,7 +59,7 @@ export class InputPassword extends AKElement {
|
||||
* @attr
|
||||
*/
|
||||
@property({ type: String })
|
||||
name = "password";
|
||||
public name = "password";
|
||||
|
||||
/**
|
||||
* The label for the input field.
|
||||
@@ -66,7 +67,7 @@ export class InputPassword extends AKElement {
|
||||
* @attr
|
||||
*/
|
||||
@property({ type: String })
|
||||
label = msg("Password");
|
||||
public label = msg("Password");
|
||||
|
||||
/**
|
||||
* The placeholder text for the input field.
|
||||
@@ -74,7 +75,7 @@ export class InputPassword extends AKElement {
|
||||
* @attr
|
||||
*/
|
||||
@property({ type: String })
|
||||
placeholder = msg("Please enter your password");
|
||||
public placeholder = msg("Please enter your password");
|
||||
|
||||
/**
|
||||
* The initial value of the input field.
|
||||
@@ -82,20 +83,20 @@ export class InputPassword extends AKElement {
|
||||
* @attr
|
||||
*/
|
||||
@property({ type: String, attribute: "prefill" })
|
||||
initialValue = "";
|
||||
public initialValue = "";
|
||||
|
||||
/**
|
||||
* The errors for the input field.
|
||||
*/
|
||||
@property({ type: Object })
|
||||
errors: Record<string, string> = {};
|
||||
public errors: Record<string, string> = {};
|
||||
|
||||
/**
|
||||
* Forwarded to the input tag's aria-invalid attribute, if set
|
||||
* @attr
|
||||
*/
|
||||
@property({ type: String })
|
||||
invalid?: string;
|
||||
public invalid?: string;
|
||||
|
||||
/**
|
||||
* Whether to allow the user to toggle the visibility of the password.
|
||||
@@ -103,7 +104,7 @@ export class InputPassword extends AKElement {
|
||||
* @attr
|
||||
*/
|
||||
@property({ type: Boolean, attribute: "allow-show-password" })
|
||||
allowShowPassword = false;
|
||||
public allowShowPassword = false;
|
||||
|
||||
/**
|
||||
* Whether the password is currently visible.
|
||||
@@ -111,7 +112,7 @@ export class InputPassword extends AKElement {
|
||||
* @attr
|
||||
*/
|
||||
@property({ type: Boolean, attribute: "password-visible" })
|
||||
passwordVisible = false;
|
||||
public passwordVisible = false;
|
||||
|
||||
/**
|
||||
* Automatically grab focus after rendering.
|
||||
@@ -119,15 +120,15 @@ export class InputPassword extends AKElement {
|
||||
* @attr
|
||||
*/
|
||||
@property({ type: Boolean, attribute: "grab-focus" })
|
||||
grabFocus = false;
|
||||
public grabFocus = false;
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Refs
|
||||
|
||||
inputRef: Ref<HTMLInputElement> = createRef();
|
||||
public inputRef: Ref<HTMLInputElement> = createRef();
|
||||
|
||||
toggleVisibilityRef: Ref<HTMLButtonElement> = createRef();
|
||||
public toggleVisibilityRef: Ref<HTMLButtonElement> = createRef();
|
||||
|
||||
//#endregion
|
||||
|
||||
@@ -137,7 +138,7 @@ export class InputPassword extends AKElement {
|
||||
* Whether the caps lock key is enabled.
|
||||
*/
|
||||
@state()
|
||||
capsLock = false;
|
||||
public capsLock = false;
|
||||
|
||||
//#endregion
|
||||
|
||||
@@ -314,37 +315,33 @@ export class InputPassword extends AKElement {
|
||||
}
|
||||
|
||||
render() {
|
||||
return html` <ak-form-element
|
||||
label="${this.label}"
|
||||
required
|
||||
class="pf-c-form__group"
|
||||
.errors=${this.errors}
|
||||
>
|
||||
<div class="pf-c-form__group-control">
|
||||
<div class="pf-c-input-group">
|
||||
<input
|
||||
type=${this.passwordVisible ? "text" : "password"}
|
||||
id=${this.inputId}
|
||||
name=${this.name}
|
||||
placeholder=${this.placeholder}
|
||||
autocomplete="current-password"
|
||||
class="${classMap({
|
||||
"pf-c-form-control": true,
|
||||
"pf-m-icon": true,
|
||||
"pf-m-caps-lock": this.capsLock,
|
||||
})}"
|
||||
required
|
||||
aria-invalid=${ifDefined(this.invalid)}
|
||||
value=${this.initialValue}
|
||||
${ref(this.inputRef)}
|
||||
/>
|
||||
return html` ${AKLabel({ required: true, htmlFor: this.inputID, children: this.label })}
|
||||
<div class="pf-c-form__group">
|
||||
<div class="pf-c-form__group-control">
|
||||
<div class="pf-c-input-group">
|
||||
<input
|
||||
type=${this.passwordVisible ? "text" : "password"}
|
||||
id=${this.inputID}
|
||||
name=${this.name}
|
||||
placeholder=${this.placeholder}
|
||||
autocomplete="current-password"
|
||||
class="${classMap({
|
||||
"pf-c-form-control": true,
|
||||
"pf-m-icon": true,
|
||||
"pf-m-caps-lock": this.capsLock,
|
||||
})}"
|
||||
required
|
||||
aria-invalid=${ifDefined(this.invalid)}
|
||||
value=${this.initialValue}
|
||||
${ref(this.inputRef)}
|
||||
/>
|
||||
|
||||
${this.renderVisibilityToggle()}
|
||||
${this.renderVisibilityToggle()}
|
||||
</div>
|
||||
|
||||
${this.renderHelperText()}
|
||||
</div>
|
||||
|
||||
${this.renderHelperText()}
|
||||
</div>
|
||||
</ak-form-element>`;
|
||||
</div>`;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import "#elements/forms/FormElement";
|
||||
import "#flow/FormStatic";
|
||||
import "#flow/components/ak-flow-card";
|
||||
|
||||
import { AKFormErrors } from "#components/ak-field-errors";
|
||||
import { AKLabel } from "#components/ak-label";
|
||||
|
||||
import { BaseStage } from "#flow/stages/base";
|
||||
|
||||
import {
|
||||
@@ -16,6 +18,7 @@ import { customElement } from "lit/decorators.js";
|
||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
||||
import PFInputGroup from "@patternfly/patternfly/components/InputGroup/input-group.css";
|
||||
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
|
||||
import PFTitle from "@patternfly/patternfly/components/Title/title.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
@@ -25,15 +28,28 @@ export class OAuth2DeviceCode extends BaseStage<
|
||||
OAuthDeviceCodeChallenge,
|
||||
OAuthDeviceCodeChallengeResponseRequest
|
||||
> {
|
||||
static styles: CSSResult[] = [PFBase, PFLogin, PFForm, PFFormControl, PFTitle, PFButton];
|
||||
static styles: CSSResult[] = [
|
||||
PFBase,
|
||||
PFLogin,
|
||||
PFForm,
|
||||
PFFormControl,
|
||||
PFTitle,
|
||||
PFButton,
|
||||
PFInputGroup,
|
||||
];
|
||||
|
||||
render(): TemplateResult {
|
||||
return html`<ak-flow-card .challenge=${this.challenge}>
|
||||
<form
|
||||
class="pf-c-form"
|
||||
@submit=${this.submitForm}
|
||||
>
|
||||
<form class="pf-c-form" @submit=${this.submitForm}>
|
||||
<div class="pf-c-form__group">
|
||||
${AKLabel({
|
||||
required: true,
|
||||
htmlFor: "device-code-input",
|
||||
children: msg("Device Code"),
|
||||
})}
|
||||
|
||||
<input
|
||||
id="device-code-input"
|
||||
type="text"
|
||||
name="code"
|
||||
inputmode="numeric"
|
||||
@@ -45,7 +61,8 @@ export class OAuth2DeviceCode extends BaseStage<
|
||||
value=""
|
||||
required
|
||||
/>
|
||||
</ak-form-element>
|
||||
${AKFormErrors({ errors: this.challenge.responseErrors?.code })}
|
||||
</div>
|
||||
|
||||
<div class="pf-c-form__group pf-m-action">
|
||||
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import "#elements/forms/FormElement";
|
||||
import "#flow/FormStatic";
|
||||
import "#flow/components/ak-flow-card";
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import "#elements/forms/FormElement";
|
||||
import "#flow/FormStatic";
|
||||
import "#flow/components/ak-flow-card";
|
||||
|
||||
import { AKFormErrors } from "#components/ak-field-errors";
|
||||
import { AKLabel } from "#components/ak-label";
|
||||
|
||||
import { BaseStage } from "#flow/stages/base";
|
||||
|
||||
import {
|
||||
@@ -18,6 +20,7 @@ import PFAlert from "@patternfly/patternfly/components/Alert/alert.css";
|
||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
||||
import PFInputGroup from "@patternfly/patternfly/components/InputGroup/input-group.css";
|
||||
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
|
||||
import PFTitle from "@patternfly/patternfly/components/Title/title.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
@@ -33,6 +36,7 @@ export class AuthenticatorEmailStage extends BaseStage<
|
||||
PFLogin,
|
||||
PFForm,
|
||||
PFFormControl,
|
||||
PFInputGroup,
|
||||
PFTitle,
|
||||
PFButton,
|
||||
];
|
||||
@@ -51,13 +55,14 @@ export class AuthenticatorEmailStage extends BaseStage<
|
||||
>
|
||||
</div>
|
||||
</ak-form-static>
|
||||
<ak-form-element
|
||||
label="${msg("Configure your email")}"
|
||||
required
|
||||
class="pf-c-form__group"
|
||||
.errors=${(this.challenge?.responseErrors || {}).email}
|
||||
>
|
||||
<div class="pf-c-form__group">
|
||||
${AKLabel({
|
||||
required: true,
|
||||
htmlFor: "email-input",
|
||||
children: msg("Configure your email"),
|
||||
})}
|
||||
<input
|
||||
id="email-input"
|
||||
type="email"
|
||||
name="email"
|
||||
placeholder="${msg("Please enter your email address.")}"
|
||||
@@ -66,7 +71,8 @@ export class AuthenticatorEmailStage extends BaseStage<
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element>
|
||||
${AKFormErrors({ errors: this.challenge.responseErrors?.email })}
|
||||
</div>
|
||||
${this.renderNonFieldErrors()}
|
||||
<div class="pf-c-form__group pf-m-action">
|
||||
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
|
||||
@@ -93,13 +99,10 @@ export class AuthenticatorEmailStage extends BaseStage<
|
||||
A verification token has been sent to your configured email address
|
||||
${ifDefined(this.challenge.email)}
|
||||
<form class="pf-c-form" @submit=${this.submitForm}>
|
||||
<ak-form-element
|
||||
label="${msg("Code")}"
|
||||
required
|
||||
class="pf-c-form__group"
|
||||
.errors=${(this.challenge?.responseErrors || {}).code}
|
||||
>
|
||||
<div class="pf-c-form__group">
|
||||
${AKLabel({ required: true, htmlFor: "code-input", children: msg("Code") })}
|
||||
<input
|
||||
id="code-input"
|
||||
type="text"
|
||||
name="code"
|
||||
inputmode="numeric"
|
||||
@@ -110,7 +113,8 @@ export class AuthenticatorEmailStage extends BaseStage<
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element>
|
||||
${AKFormErrors({ errors: this.challenge.responseErrors?.code })}
|
||||
</div>
|
||||
${this.renderNonFieldErrors()}
|
||||
<div class="pf-c-form__group pf-m-action">
|
||||
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import "#elements/forms/FormElement";
|
||||
import "#flow/FormStatic";
|
||||
import "#flow/components/ak-flow-card";
|
||||
|
||||
import { AKFormErrors } from "#components/ak-field-errors";
|
||||
import { AKLabel } from "#components/ak-label";
|
||||
|
||||
import { BaseStage } from "#flow/stages/base";
|
||||
|
||||
import {
|
||||
@@ -18,6 +20,7 @@ import PFAlert from "@patternfly/patternfly/components/Alert/alert.css";
|
||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
||||
import PFInputGroup from "@patternfly/patternfly/components/InputGroup/input-group.css";
|
||||
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
|
||||
import PFTitle from "@patternfly/patternfly/components/Title/title.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
@@ -33,15 +36,18 @@ export class AuthenticatorSMSStage extends BaseStage<
|
||||
PFLogin,
|
||||
PFForm,
|
||||
PFFormControl,
|
||||
PFInputGroup,
|
||||
PFTitle,
|
||||
PFButton,
|
||||
];
|
||||
|
||||
renderPhoneNumber(): TemplateResult {
|
||||
return html`<ak-flow-card .challenge=${this.challenge}>
|
||||
<form
|
||||
class="pf-c-form"
|
||||
@submit=${this.submitForm}
|
||||
<form class="pf-c-form" @submit=${this.submitForm}>
|
||||
<ak-form-static
|
||||
class="pf-c-form__group"
|
||||
userAvatar=${this.challenge.pendingUserAvatar}
|
||||
user=${this.challenge.pendingUser}
|
||||
>
|
||||
<div slot="link">
|
||||
<a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}"
|
||||
@@ -49,12 +55,13 @@ export class AuthenticatorSMSStage extends BaseStage<
|
||||
>
|
||||
</div>
|
||||
</ak-form-static>
|
||||
<ak-form-element
|
||||
label="${msg("Phone number")}"
|
||||
required
|
||||
class="pf-c-form__group"
|
||||
.errors=${(this.challenge?.responseErrors || {}).phone_number}
|
||||
>
|
||||
<div class="pf-c-form__group">
|
||||
${AKLabel({
|
||||
required: true,
|
||||
htmlFor: "phone-number-input",
|
||||
children: msg("Phone number"),
|
||||
})}
|
||||
|
||||
<input
|
||||
type="tel"
|
||||
name="phoneNumber"
|
||||
@@ -64,7 +71,8 @@ export class AuthenticatorSMSStage extends BaseStage<
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element>
|
||||
${AKFormErrors({ errors: this.challenge.responseErrors?.phone_number })}
|
||||
</div>
|
||||
${this.renderNonFieldErrors()}
|
||||
<div class="pf-c-form__group pf-m-action">
|
||||
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
|
||||
@@ -89,13 +97,10 @@ export class AuthenticatorSMSStage extends BaseStage<
|
||||
>
|
||||
</div>
|
||||
</ak-form-static>
|
||||
<ak-form-element
|
||||
label="${msg("Code")}"
|
||||
required
|
||||
class="pf-c-form__group"
|
||||
.errors=${(this.challenge?.responseErrors || {}).code}
|
||||
>
|
||||
<div class="pf-c-form__group">
|
||||
${AKLabel({ required: true, htmlFor: "sms-code-input", children: msg("Code") })}
|
||||
<input
|
||||
id="sms-code-input"
|
||||
type="text"
|
||||
name="code"
|
||||
inputmode="numeric"
|
||||
@@ -106,7 +111,8 @@ export class AuthenticatorSMSStage extends BaseStage<
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element>
|
||||
${AKFormErrors({ errors: this.challenge.responseErrors?.code })}
|
||||
</div>
|
||||
${this.renderNonFieldErrors()}
|
||||
<div class="pf-c-form__group pf-m-action">
|
||||
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import "#elements/forms/FormElement";
|
||||
import "#flow/FormStatic";
|
||||
import "#flow/components/ak-flow-card";
|
||||
|
||||
@@ -64,13 +63,13 @@ export class AuthenticatorStaticStage extends BaseStage<
|
||||
>
|
||||
</div>
|
||||
</ak-form-static>
|
||||
<ak-form-element label="" class="pf-c-form__group">
|
||||
<div class="pf-c-form__group">
|
||||
<ul>
|
||||
${this.challenge.codes.map((token) => {
|
||||
return html`<li class="pf-m-monospace">${token}</li>`;
|
||||
})}
|
||||
</ul>
|
||||
</ak-form-element>
|
||||
</div>
|
||||
<p>${msg("Make sure to keep these tokens in a safe place.")}</p>
|
||||
|
||||
<div class="pf-c-form__group pf-m-action">
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import "#elements/forms/FormElement";
|
||||
import "#flow/FormStatic";
|
||||
import "#flow/components/ak-flow-card";
|
||||
import "webcomponent-qr-code";
|
||||
@@ -7,6 +6,9 @@ import { MessageLevel } from "#common/messages";
|
||||
|
||||
import { showMessage } from "#elements/messages/MessageContainer";
|
||||
|
||||
import { AKFormErrors } from "#components/ak-field-errors";
|
||||
import { AKLabel } from "#components/ak-label";
|
||||
|
||||
import { BaseStage } from "#flow/stages/base";
|
||||
|
||||
import {
|
||||
@@ -22,6 +24,7 @@ import { ifDefined } from "lit/directives/if-defined.js";
|
||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
||||
import PFInputGroup from "@patternfly/patternfly/components/InputGroup/input-group.css";
|
||||
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
|
||||
import PFTitle from "@patternfly/patternfly/components/Title/title.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
@@ -36,6 +39,7 @@ export class AuthenticatorTOTPStage extends BaseStage<
|
||||
PFLogin,
|
||||
PFForm,
|
||||
PFFormControl,
|
||||
PFInputGroup,
|
||||
PFTitle,
|
||||
PFButton,
|
||||
css`
|
||||
@@ -62,7 +66,8 @@ export class AuthenticatorTOTPStage extends BaseStage<
|
||||
</div>
|
||||
</ak-form-static>
|
||||
<input type="hidden" name="otp_uri" value=${this.challenge.configUrl} />
|
||||
<ak-form-element>
|
||||
|
||||
<div class="pf-c-form__group">
|
||||
<div class="qr-container">
|
||||
<qr-code data="${this.challenge.configUrl}"></qr-code>
|
||||
<button
|
||||
@@ -92,20 +97,20 @@ export class AuthenticatorTOTPStage extends BaseStage<
|
||||
${msg("Copy")}
|
||||
</button>
|
||||
</div>
|
||||
</ak-form-element>
|
||||
</div>
|
||||
<p>
|
||||
${msg(
|
||||
"Please scan the QR code above using the Microsoft Authenticator, Google Authenticator, or other authenticator apps on your device, and enter the code the device displays below to finish setting up the MFA device.",
|
||||
)}
|
||||
</p>
|
||||
<ak-form-element
|
||||
label="${msg("Code")}"
|
||||
required
|
||||
class="pf-c-form__group"
|
||||
.errors=${(this.challenge?.responseErrors || {}).code}
|
||||
>
|
||||
<!-- @ts-ignore -->
|
||||
<div class="pf-c-form__group">
|
||||
${AKLabel({
|
||||
required: true,
|
||||
htmlFor: "totp-code-input",
|
||||
children: msg("Code"),
|
||||
})}
|
||||
<input
|
||||
id="totp-code-input"
|
||||
type="text"
|
||||
name="code"
|
||||
inputmode="numeric"
|
||||
@@ -117,7 +122,8 @@ export class AuthenticatorTOTPStage extends BaseStage<
|
||||
spellcheck="false"
|
||||
required
|
||||
/>
|
||||
</ak-form-element>
|
||||
${AKFormErrors({ errors: this.challenge.responseErrors?.code })}
|
||||
</div>
|
||||
|
||||
<div class="pf-c-form__group pf-m-action">
|
||||
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import "#elements/forms/FormElement";
|
||||
import "#flow/components/ak-flow-card";
|
||||
|
||||
import { AKFormErrors } from "#components/ak-field-errors";
|
||||
import { AKLabel } from "#components/ak-label";
|
||||
|
||||
import { BaseDeviceStage } from "#flow/stages/authenticator_validate/base";
|
||||
import { PasswordManagerPrefill } from "#flow/stages/identification/IdentificationStage";
|
||||
|
||||
@@ -74,16 +76,17 @@ export class AuthenticatorValidateStageWebCode extends BaseDeviceStage<
|
||||
<i class="fa ${this.deviceIcon()}" aria-hidden="true"></i>
|
||||
<p>${this.deviceMessage()}</p>
|
||||
</div>
|
||||
<ak-form-element
|
||||
label="${this.deviceChallenge?.deviceClass === DeviceClassesEnum.Static
|
||||
? msg("Static token")
|
||||
: msg("Authentication code")}"
|
||||
required
|
||||
class="pf-c-form__group"
|
||||
.errors=${(this.challenge?.responseErrors || {}).code}
|
||||
>
|
||||
<!-- @ts-ignore -->
|
||||
<div class="pf-c-form__group">
|
||||
${AKLabel({
|
||||
required: true,
|
||||
htmlFor: "validation-code-input",
|
||||
children:
|
||||
this.deviceChallenge?.deviceClass === DeviceClassesEnum.Static
|
||||
? msg("Static token")
|
||||
: msg("Authentication code"),
|
||||
})}
|
||||
<input
|
||||
id="validation-code-input"
|
||||
type="text"
|
||||
name="code"
|
||||
inputmode="${this.deviceChallenge?.deviceClass === DeviceClassesEnum.Static
|
||||
@@ -99,7 +102,8 @@ export class AuthenticatorValidateStageWebCode extends BaseDeviceStage<
|
||||
value="${PasswordManagerPrefill.totp || ""}"
|
||||
required
|
||||
/>
|
||||
</ak-form-element>
|
||||
${AKFormErrors({ errors: this.challenge.responseErrors?.code })}
|
||||
</div>
|
||||
|
||||
<div class="pf-c-form__group pf-m-action">
|
||||
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import "#elements/EmptyState";
|
||||
import "#elements/forms/FormElement";
|
||||
|
||||
import { BaseDeviceStage } from "#flow/stages/authenticator_validate/base";
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import { property } from "lit/decorators.js";
|
||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
||||
import PFInputGroup from "@patternfly/patternfly/components/InputGroup/input-group.css";
|
||||
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
|
||||
import PFTitle from "@patternfly/patternfly/components/Title/title.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
@@ -29,6 +30,7 @@ export class BaseDeviceStage<
|
||||
PFLogin,
|
||||
PFForm,
|
||||
PFFormControl,
|
||||
PFInputGroup,
|
||||
PFTitle,
|
||||
PFButton,
|
||||
css`
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import "#elements/forms/FormElement";
|
||||
import "#flow/FormStatic";
|
||||
import "#flow/components/ak-flow-card";
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import "#elements/Divider";
|
||||
import "#elements/EmptyState";
|
||||
import "#elements/forms/FormElement";
|
||||
import "#flow/components/ak-flow-card";
|
||||
import "#flow/components/ak-flow-password-input";
|
||||
import "#flow/stages/captcha/CaptchaStage";
|
||||
|
||||
import { AKFormErrors } from "#components/ak-field-errors";
|
||||
import { AKLabel } from "#components/ak-label";
|
||||
|
||||
import { renderSourceIcon } from "#admin/sources/utils";
|
||||
|
||||
import { BaseStage } from "#flow/stages/base";
|
||||
@@ -20,7 +22,7 @@ import {
|
||||
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { css, CSSResult, html, nothing, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { createRef, ref } from "lit/directives/ref.js";
|
||||
|
||||
import PFAlert from "@patternfly/patternfly/components/Alert/alert.css";
|
||||
@@ -87,6 +89,14 @@ export class IdentificationStage extends BaseStage<
|
||||
`,
|
||||
];
|
||||
|
||||
/**
|
||||
* The ID of the input field.
|
||||
*
|
||||
* @attr
|
||||
*/
|
||||
@property({ type: String, attribute: "input-id" })
|
||||
public inputID = "ak-identifier-input";
|
||||
|
||||
#form?: HTMLFormElement;
|
||||
|
||||
#rememberMe = new AkRememberMeController(this);
|
||||
@@ -301,6 +311,7 @@ export class IdentificationStage extends BaseStage<
|
||||
[UserFieldsEnum.Upn]: msg("UPN"),
|
||||
};
|
||||
const label = OR_LIST_FORMATTERS.format(fields.map((f) => uiFields[f]));
|
||||
|
||||
return html`${this.challenge.flowDesignation === FlowDesignationEnum.Recovery
|
||||
? html`
|
||||
<p>
|
||||
@@ -310,13 +321,10 @@ export class IdentificationStage extends BaseStage<
|
||||
</p>
|
||||
`
|
||||
: nothing}
|
||||
<ak-form-element
|
||||
label=${label}
|
||||
required
|
||||
class="pf-c-form__group"
|
||||
.errors=${(this.challenge.responseErrors || {}).uid_field}
|
||||
>
|
||||
<div class="pf-c-form__group">
|
||||
${AKLabel({ required: true, htmlFor: this.inputID, children: label })}
|
||||
<input
|
||||
id=${this.inputID}
|
||||
type=${type}
|
||||
name="uidField"
|
||||
placeholder=${label}
|
||||
@@ -328,12 +336,13 @@ export class IdentificationStage extends BaseStage<
|
||||
required
|
||||
/>
|
||||
${this.#rememberMe.render()}
|
||||
</ak-form-element>
|
||||
${AKFormErrors({ errors: this.challenge.responseErrors?.uid_field })}
|
||||
</div>
|
||||
${this.challenge.passwordFields
|
||||
? html`
|
||||
<ak-flow-input-password
|
||||
label=${msg("Password")}
|
||||
inputId="ak-stage-identification-password"
|
||||
input-idd="ak-stage-identification-password"
|
||||
required
|
||||
class="pf-c-form__group"
|
||||
.errors=${(this.challenge?.responseErrors || {}).password}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import "#elements/forms/FormElement";
|
||||
import "#flow/FormStatic";
|
||||
import "#flow/components/ak-flow-card";
|
||||
import "#flow/components/ak-flow-password-input";
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import "#elements/Divider";
|
||||
import "#elements/forms/FormElement";
|
||||
import "#flow/components/ak-flow-card";
|
||||
|
||||
import { LOCALES } from "#elements/ak-locale-context/definitions";
|
||||
import { CapabilitiesEnum, WithCapabilitiesConfig } from "#elements/mixins/capabilities";
|
||||
|
||||
import { AKFormErrors } from "#components/ak-field-errors";
|
||||
import { AKLabel } from "#components/ak-label";
|
||||
|
||||
import { BaseStage } from "#flow/stages/base";
|
||||
|
||||
import {
|
||||
@@ -24,6 +26,7 @@ import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
import PFCheck from "@patternfly/patternfly/components/Check/check.css";
|
||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
||||
import PFInputGroup from "@patternfly/patternfly/components/InputGroup/input-group.css";
|
||||
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
|
||||
import PFTitle from "@patternfly/patternfly/components/Title/title.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
@@ -38,6 +41,7 @@ export class PromptStage extends WithCapabilitiesConfig(
|
||||
PFAlert,
|
||||
PFForm,
|
||||
PFFormControl,
|
||||
PFInputGroup,
|
||||
PFTitle,
|
||||
PFButton,
|
||||
PFCheck,
|
||||
@@ -55,6 +59,7 @@ export class PromptStage extends WithCapabilitiesConfig(
|
||||
case PromptTypeEnum.Text:
|
||||
return html`<input
|
||||
type="text"
|
||||
id="field-${prompt.fieldKey}"
|
||||
name="${prompt.fieldKey}"
|
||||
placeholder="${prompt.placeholder}"
|
||||
autocomplete="off"
|
||||
@@ -64,6 +69,7 @@ export class PromptStage extends WithCapabilitiesConfig(
|
||||
/>`;
|
||||
case PromptTypeEnum.TextArea:
|
||||
return html`<textarea
|
||||
id="field-${prompt.fieldKey}"
|
||||
name="${prompt.fieldKey}"
|
||||
placeholder="${prompt.placeholder}"
|
||||
autocomplete="off"
|
||||
@@ -75,6 +81,7 @@ ${prompt.initialValue}</textarea
|
||||
case PromptTypeEnum.TextReadOnly:
|
||||
return html`<input
|
||||
type="text"
|
||||
id="field-${prompt.fieldKey}"
|
||||
name="${prompt.fieldKey}"
|
||||
placeholder="${prompt.placeholder}"
|
||||
class="pf-c-form-control"
|
||||
@@ -83,6 +90,7 @@ ${prompt.initialValue}</textarea
|
||||
/>`;
|
||||
case PromptTypeEnum.TextAreaReadOnly:
|
||||
return html`<textarea
|
||||
id="field-${prompt.fieldKey}"
|
||||
name="${prompt.fieldKey}"
|
||||
placeholder="${prompt.placeholder}"
|
||||
class="pf-c-form-control"
|
||||
@@ -93,6 +101,7 @@ ${prompt.initialValue}</textarea
|
||||
case PromptTypeEnum.Username:
|
||||
return html`<input
|
||||
type="text"
|
||||
id="field-${prompt.fieldKey}"
|
||||
name="${prompt.fieldKey}"
|
||||
placeholder="${prompt.placeholder}"
|
||||
autocomplete="username"
|
||||
@@ -104,6 +113,7 @@ ${prompt.initialValue}</textarea
|
||||
case PromptTypeEnum.Email:
|
||||
return html`<input
|
||||
type="email"
|
||||
id="field-${prompt.fieldKey}"
|
||||
name="${prompt.fieldKey}"
|
||||
placeholder="${prompt.placeholder}"
|
||||
class="pf-c-form-control"
|
||||
@@ -113,6 +123,7 @@ ${prompt.initialValue}</textarea
|
||||
case PromptTypeEnum.Password:
|
||||
return html`<input
|
||||
type="password"
|
||||
id="field-${prompt.fieldKey}"
|
||||
name="${prompt.fieldKey}"
|
||||
placeholder="${prompt.placeholder}"
|
||||
autocomplete="new-password"
|
||||
@@ -122,6 +133,7 @@ ${prompt.initialValue}</textarea
|
||||
case PromptTypeEnum.Number:
|
||||
return html`<input
|
||||
type="number"
|
||||
id="field-${prompt.fieldKey}"
|
||||
name="${prompt.fieldKey}"
|
||||
placeholder="${prompt.placeholder}"
|
||||
class="pf-c-form-control"
|
||||
@@ -131,6 +143,7 @@ ${prompt.initialValue}</textarea
|
||||
case PromptTypeEnum.Date:
|
||||
return html`<input
|
||||
type="date"
|
||||
id="field-${prompt.fieldKey}"
|
||||
name="${prompt.fieldKey}"
|
||||
placeholder="${prompt.placeholder}"
|
||||
class="pf-c-form-control"
|
||||
@@ -140,6 +153,7 @@ ${prompt.initialValue}</textarea
|
||||
case PromptTypeEnum.DateTime:
|
||||
return html`<input
|
||||
type="datetime"
|
||||
id="field-${prompt.fieldKey}"
|
||||
name="${prompt.fieldKey}"
|
||||
placeholder="${prompt.placeholder}"
|
||||
class="pf-c-form-control"
|
||||
@@ -149,6 +163,7 @@ ${prompt.initialValue}</textarea
|
||||
case PromptTypeEnum.File:
|
||||
return html`<input
|
||||
type="file"
|
||||
id="field-${prompt.fieldKey}"
|
||||
name="${prompt.fieldKey}"
|
||||
placeholder="${prompt.placeholder}"
|
||||
class="pf-c-form-control"
|
||||
@@ -160,6 +175,7 @@ ${prompt.initialValue}</textarea
|
||||
case PromptTypeEnum.Hidden:
|
||||
return html`<input
|
||||
type="hidden"
|
||||
id="field-${prompt.fieldKey}"
|
||||
name="${prompt.fieldKey}"
|
||||
value="${prompt.initialValue}"
|
||||
class="pf-c-form-control"
|
||||
@@ -168,7 +184,11 @@ ${prompt.initialValue}</textarea
|
||||
case PromptTypeEnum.Static:
|
||||
return html`<p>${unsafeHTML(prompt.initialValue)}</p>`;
|
||||
case PromptTypeEnum.Dropdown:
|
||||
return html`<select class="pf-c-form-control" name="${prompt.fieldKey}">
|
||||
return html`<select
|
||||
class="pf-c-form-control"
|
||||
id="field-${prompt.fieldKey}"
|
||||
name="${prompt.fieldKey}"
|
||||
>
|
||||
${prompt.choices?.map((choice) => {
|
||||
return html`<option
|
||||
value="${choice}"
|
||||
@@ -256,14 +276,17 @@ ${prompt.initialValue}</textarea
|
||||
</div>`;
|
||||
}
|
||||
if (this.shouldRenderInWrapper(prompt)) {
|
||||
return html`<ak-form-element
|
||||
label="${prompt.label}"
|
||||
?required="${prompt.required}"
|
||||
class="pf-c-form__group"
|
||||
.errors=${(this.challenge?.responseErrors || {})[prompt.fieldKey]}
|
||||
>
|
||||
const errors = (this.challenge?.responseErrors || {})[prompt.fieldKey];
|
||||
|
||||
return html`<div class="pf-c-form__group">
|
||||
${AKLabel({
|
||||
required: prompt.required,
|
||||
htmlFor: `field-${prompt.fieldKey}`,
|
||||
children: prompt.label,
|
||||
})}
|
||||
${this.renderPromptInner(prompt)} ${this.renderPromptHelpText(prompt)}
|
||||
</ak-form-element>`;
|
||||
${AKFormErrors({ errors })}
|
||||
</div>`;
|
||||
}
|
||||
return html` ${this.renderPromptInner(prompt)} ${this.renderPromptHelpText(prompt)}`;
|
||||
}
|
||||
@@ -279,9 +302,7 @@ ${prompt.initialValue}</textarea
|
||||
render(): TemplateResult {
|
||||
return html`<ak-flow-card .challenge=${this.challenge}>
|
||||
<form class="pf-c-form" @submit=${this.submitForm}>
|
||||
${this.challenge.fields.map((prompt) => {
|
||||
return this.renderField(prompt);
|
||||
})}
|
||||
${this.challenge.fields.map((prompt) => this.renderField(prompt))}
|
||||
${this.renderNonFieldErrors()} ${this.renderContinue()}
|
||||
</form>
|
||||
</ak-flow-card>`;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import "#elements/forms/FormElement";
|
||||
import "#flow/FormStatic";
|
||||
import "#flow/components/ak-flow-card";
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"baseUrl": ".",
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "@goauthentik/lit-jsx",
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||
// TODO: We should enable this when when we're ready to enforce it.
|
||||
"noUncheckedIndexedAccess": false,
|
||||
|
||||
Reference in New Issue
Block a user