Merge branch 'web/appwizard-1-revisions-and-comments' into application-wizard-2-with-api-and-tests

* web/appwizard-1-revisions-and-comments:
  actually use assignValue or rather serializeFieldRecursive
  web: permit arrays to be sent in custom events without interpolation.
  web: laying the groundwork for future expansion
This commit is contained in:
Ken Sternberg 2023-10-02 13:01:05 -07:00
commit 2d183f6d6d
3 changed files with 22 additions and 20 deletions

View file

@ -54,9 +54,7 @@ export interface KeyUnknown {
* - The base class * - The base class
* - The "use `renderInlineForm` class * - The "use `renderInlineForm` class
* - The slotted class. * - The slotted class.
* 2. Ask why the form class won't render anything if it's not in the viewport. * 2. There is already specialization-by-type throughout all of our code.
* 3. Ask why there's so much slug management code.
* 4. There is already specialization-by-type throughout all of our code.
* Consider refactoring serializeForm() so that the conversions are on * Consider refactoring serializeForm() so that the conversions are on
* the input types, rather than here. (i.e. "Polymorphism is better than * the input types, rather than here. (i.e. "Polymorphism is better than
* switch.") * switch.")
@ -96,7 +94,7 @@ export abstract class Form<T> extends AKElement {
/** /**
* Called by the render function. Blocks rendering the form if the form is not within the * Called by the render function. Blocks rendering the form if the form is not within the
* viewport [2] * viewport.
*/ */
get isInViewport(): boolean { get isInViewport(): boolean {
const rect = this.getBoundingClientRect(); const rect = this.getBoundingClientRect();
@ -109,8 +107,8 @@ export abstract class Form<T> extends AKElement {
/** /**
* After rendering the form, if there is both a `name` and `slug` element within the form, * After rendering the form, if there is both a `name` and `slug` element within the form,
* events the `name` element so that the slug will always have a slugified version of the `name.`. This duplicates * events the `name` element so that the slug will always have a slugified version of the
* functionality within ak-form-element-horizontal. [3] * `name.`. This duplicates functionality within ak-form-element-horizontal.
*/ */
updated(): void { updated(): void {
this.shadowRoot this.shadowRoot
@ -199,17 +197,21 @@ export abstract class Form<T> extends AKElement {
"multiple" in inputElement.attributes "multiple" in inputElement.attributes
) { ) {
const selectElement = inputElement as unknown as HTMLSelectElement; const selectElement = inputElement as unknown as HTMLSelectElement;
json[element.name] = Array.from(selectElement.selectedOptions).map((v) => v.value); this.assignValue(
inputElement,
Array.from(selectElement.selectedOptions).map((v) => v.value),
json,
);
} else if ( } else if (
inputElement.tagName.toLowerCase() === "input" && inputElement.tagName.toLowerCase() === "input" &&
inputElement.type === "date" inputElement.type === "date"
) { ) {
json[element.name] = inputElement.valueAsDate; this.assignValue(inputElement, inputElement.valueAsDate, json);
} else if ( } else if (
inputElement.tagName.toLowerCase() === "input" && inputElement.tagName.toLowerCase() === "input" &&
inputElement.type === "datetime-local" inputElement.type === "datetime-local"
) { ) {
json[element.name] = new Date(inputElement.valueAsNumber); this.assignValue(inputElement, new Date(inputElement.valueAsNumber), json);
} else if ( } else if (
inputElement.tagName.toLowerCase() === "input" && inputElement.tagName.toLowerCase() === "input" &&
"type" in inputElement.dataset && "type" in inputElement.dataset &&
@ -217,19 +219,19 @@ export abstract class Form<T> extends AKElement {
) { ) {
// Workaround for Firefox <93, since 92 and older don't support // Workaround for Firefox <93, since 92 and older don't support
// datetime-local fields // datetime-local fields
json[element.name] = new Date(inputElement.value); this.assignValue(inputElement, new Date(inputElement.value), json);
} else if ( } else if (
inputElement.tagName.toLowerCase() === "input" && inputElement.tagName.toLowerCase() === "input" &&
inputElement.type === "checkbox" inputElement.type === "checkbox"
) { ) {
json[element.name] = inputElement.checked; this.assignValue(inputElement, inputElement.checked, json);
} else if ("selectedFlow" in inputElement) { } else if ("selectedFlow" in inputElement) {
json[element.name] = inputElement.value; this.assignValue(inputElement, inputElement.value, json);
} else if (inputElement.tagName.toLowerCase() === "ak-search-select") { } else if (inputElement.tagName.toLowerCase() === "ak-search-select") {
const select = inputElement as unknown as SearchSelect<unknown>; const select = inputElement as unknown as SearchSelect<unknown>;
try { try {
const value = select.toForm(); const value = select.toForm();
json[element.name] = value; this.assignValue(inputElement, value, json);
} catch (exc) { } catch (exc) {
if (exc instanceof PreventFormSubmit) { if (exc instanceof PreventFormSubmit) {
throw new PreventFormSubmit(exc.message, element); throw new PreventFormSubmit(exc.message, element);
@ -237,16 +239,16 @@ export abstract class Form<T> extends AKElement {
throw exc; throw exc;
} }
} else { } else {
this.serializeFieldRecursive(inputElement, inputElement.value, json); this.assignValue(inputElement, inputElement.value, json);
} }
}); });
return json as unknown as T; return json as unknown as T;
} }
/** /**
* As far as anyone can remember, this isn't being used. * Recursively assign `value` into `json` while interpreting the dot-path of `element.name`
*/ */
private serializeFieldRecursive( private assignValue(
element: HTMLInputElement, element: HTMLInputElement,
value: unknown, value: unknown,
json: { [key: string]: unknown }, json: { [key: string]: unknown },

View file

@ -10,9 +10,9 @@ import PFFormControl from "@patternfly/patternfly/components/FormControl/form-co
import { ErrorDetail } from "@goauthentik/api"; import { ErrorDetail } from "@goauthentik/api";
/** /**
* This is only used in two places, and in both cases is used primarily to display * This is used in two places outside of Flow, and in both cases is used primarily to
* content, not take input. It displays the TOPT QR code, and the static recovery * display content, not take input. It displays the TOPT QR code, and the static
* tokens. * recovery tokens. But it's used a lot in Flow.
*/ */
@customElement("ak-form-element") @customElement("ak-form-element")

View file

@ -12,7 +12,7 @@ export function CustomEmitterElement<T extends Constructor<LitElement>>(supercla
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
dispatchCustomEvent(eventName: string, detail: any = {}, options = {}) { dispatchCustomEvent(eventName: string, detail: any = {}, options = {}) {
const fullDetail = const fullDetail =
typeof detail === "object" typeof detail === "object" && !Array.isArray(detail)
? { ? {
target: this, target: this,
...detail, ...detail,