add custom type to fix component rendering

This commit is contained in:
2026-05-25 15:02:48 +02:00
parent 38cb46cc53
commit 48bd4a1b10
4 changed files with 66 additions and 34 deletions
+5
View File
@@ -91,6 +91,11 @@ function App() {
<> <>
{formFieldFactory.createTextInput("name", "Name", true)} {formFieldFactory.createTextInput("name", "Name", true)}
{formFieldFactory.createTextAreaInput("description", "Description")} {formFieldFactory.createTextAreaInput("description", "Description")}
{formFieldFactory.createNumberInput("number", "Number")}
{formFieldFactory.createBooleanInput("boolean", "Boolean")}
{formFieldFactory.createRadioInput("radio", "Radio", [{ "value": 1, "label": "Opzione 1" }, { "value": 2, "label": "Opzione 2" }, { "value": 3, "label": "Altra opzione" }])}
{formFieldFactory.createDropdownInput("dropdown", "Dropdown", [{ "value": 0, "label": "Not applicable" }, { "value": 1, "label": "Goal 1: No poverty" }, { "value": 2, "label": "Goal 2: Zero hunger" }, { "value": 3, "label": "Goal 3: Good health and well-being" }])}
{formFieldFactory.createCheckboxInput("checkbox", "Checkbox", [{ "value": 0, "label": "Check 1" }, { "value": 1, "label": "Check 2" }, { "value": 2, "label": "Check 3" }])}
</> </>
) )
}; };
@@ -6,6 +6,7 @@ import { QueryParamHelper } from './QueryParamHelper';
import { Component, FormEvent, JSX } from 'react'; import { Component, FormEvent, JSX } from 'react';
import React from 'react'; import React from 'react';
import { IElixFormsComponentCustomFormFieldFactory } from './IElixFormsComponentCustomFormFieldFactory'; import { IElixFormsComponentCustomFormFieldFactory } from './IElixFormsComponentCustomFormFieldFactory';
import { ElixFormsCheckboxOption, ElixFormsDropdownOption, ElixFormsRadioOption } from './ElixFormsTypes';
export default abstract class ElixFormsComponentAbstract extends Component<IElixFormsComponentProperties, IElixFormsComponentFormState> { export default abstract class ElixFormsComponentAbstract extends Component<IElixFormsComponentProperties, IElixFormsComponentFormState> {
constructor(props: IElixFormsComponentProperties) { constructor(props: IElixFormsComponentProperties) {
@@ -61,15 +62,15 @@ export default abstract class ElixFormsComponentAbstract extends Component<IElix
const formFieldFactory = { const formFieldFactory = {
createBooleanInput: (name: string, label: string, required: boolean = false) => createBooleanInput: (name: string, label: string, required: boolean = false) =>
this.createBooleanInput(name, label, required), this.createBooleanInput(name, label, required),
createCheckboxInput: (name: string, label: string, options: Map<number, string>, required: boolean = false) => createCheckboxInput: (name: string, label: string, options: Array<ElixFormsCheckboxOption>, required: boolean = false) =>
this.createCheckboxInput(name, label, options, required), this.createCheckboxInput(name, label, options, required),
createDropdownInput: (name: string, label: string, options: Map<number, string>, required: boolean = false) => createDropdownInput: (name: string, label: string, options: Array<ElixFormsDropdownOption>, required: boolean = false) =>
this.createDropdownInput(name, label, options, required), this.createDropdownInput(name, label, options, required),
createHiddenInput: (name: string) => createHiddenInput: (name: string) =>
this.createHiddenInput(name), this.createHiddenInput(name),
createNumberInput: (name: string, label: string, required: boolean = false) => createNumberInput: (name: string, label: string, required: boolean = false) =>
this.createNumberInput(name, label, required), this.createNumberInput(name, label, required),
createRadioInput: (name: string, label: string, options: Map<number, string>, required: boolean = false) => createRadioInput: (name: string, label: string, options: Array<ElixFormsRadioOption>, required: boolean = false) =>
this.createRadioInput(name, label, options, required), this.createRadioInput(name, label, options, required),
createTextInput: (name: string, label: string, required: boolean = false) => createTextInput: (name: string, label: string, required: boolean = false) =>
this.createTextInput(name, label, required), this.createTextInput(name, label, required),
@@ -108,7 +109,7 @@ export default abstract class ElixFormsComponentAbstract extends Component<IElix
); );
} }
private createCheckboxInput(paramName: string, label: string, values: Map<number, string>, required: boolean = false) : JSX.Element { private createCheckboxInput(paramName: string, label: string, values: Array<ElixFormsCheckboxOption>, required: boolean = false) : JSX.Element {
const checkedValues = QueryParamHelper.getCheckedFromQuery(paramName); const checkedValues = QueryParamHelper.getCheckedFromQuery(paramName);
if (!this.checkboxValues.get(paramName)) { if (!this.checkboxValues.get(paramName)) {
@@ -118,17 +119,17 @@ export default abstract class ElixFormsComponentAbstract extends Component<IElix
const checkboxes: React.ReactElement[] = []; const checkboxes: React.ReactElement[] = [];
const stackTokens = { childrenGap: 10 }; const stackTokens = { childrenGap: 10 };
values.forEach((val, num) => { values.forEach((entry, entryIndex) => {
const id = `${paramName}_${num}`; const id = `${paramName}_${entryIndex}`;
checkboxes.push( checkboxes.push(
<FluentUI.Checkbox title={label} key={id} id={id} label={val} defaultChecked={checkedValues.indexOf(num) !== -1} required={required} <FluentUI.Checkbox title={label} key={id} id={id} label={entry.label} defaultChecked={checkedValues.indexOf(entryIndex) !== -1} required={required} className='isiportalPartialAdminCheckboxFieldItem'
onChange={(ev, checked) => { onChange={(_, checked) => {
const currentCheckboxValues = this.checkboxValues.get(paramName)!; const currentCheckboxValues = this.checkboxValues.get(paramName)!;
const index = currentCheckboxValues.indexOf(num) ?? -1; const checkboxIndex = currentCheckboxValues.indexOf(entryIndex) ?? -1;
if (checked && index === -1) { if (checked && checkboxIndex === -1) {
currentCheckboxValues.push(num); currentCheckboxValues.push(entryIndex);
} else if (!checked && index !== -1) { } else if (!checked && checkboxIndex !== -1) {
currentCheckboxValues.splice(index, 1); currentCheckboxValues.splice(checkboxIndex, 1);
} }
this.setState({ formData: { ...this.state.formData, [paramName]: currentCheckboxValues.join(',').trim() ?? '' } }, () => this.setState({ formData: { ...this.state.formData, [paramName]: currentCheckboxValues.join(',').trim() ?? '' } }, () =>
console.log(`Checkbox ${id} changed to ${checked}. Current values for ${paramName}: ${this.state.formData[paramName]}`)); console.log(`Checkbox ${id} changed to ${checked}. Current values for ${paramName}: ${this.state.formData[paramName]}`));
@@ -146,19 +147,20 @@ export default abstract class ElixFormsComponentAbstract extends Component<IElix
).render(); ).render();
} }
private createRadioInput(paramName: string, label: string, values: Map<number, string>, required: boolean = false) : JSX.Element { private createRadioInput(paramName: string, label: string, values: Array<ElixFormsRadioOption>, required: boolean = false) : JSX.Element {
const radioValue = QueryParamHelper.getOptionFromQuery(paramName); const radioValue = QueryParamHelper.getOptionFromQuery(paramName);
const radios: FluentUI.IChoiceGroupOption[] = []; const radios: FluentUI.IChoiceGroupOption[] = [];
values.forEach((val, num) => { values.forEach((entry, _) => {
const id = `${paramName}_${num}`; const id = `${paramName}_${entry.value}`;
radios.push( radios.push(
{ {
key: id, key: id,
text: val, text: entry.label,
id: id, id: id,
name: paramName, name: paramName,
value: num value: entry.value,
className: 'isiportalPartialAdminFormFieldRadio'
} }
); );
}); });
@@ -187,7 +189,8 @@ export default abstract class ElixFormsComponentAbstract extends Component<IElix
text: val.label, text: val.label,
id: id, id: id,
name: paramName, name: paramName,
value: val.value value: val.value,
className: 'isiportalPartialAdminFormFieldRadio'
} }
); );
}); });
@@ -208,7 +211,7 @@ export default abstract class ElixFormsComponentAbstract extends Component<IElix
const textareaValue = QueryParamHelper.getDecodedTextFromQuery(paramName) ?? ""; const textareaValue = QueryParamHelper.getDecodedTextFromQuery(paramName) ?? "";
return new ElixFormsElement( return new ElixFormsElement(
<FluentUI.Label htmlFor={paramName}>{label}</FluentUI.Label>, <FluentUI.Label htmlFor={paramName}>{label}</FluentUI.Label>,
<FluentUI.TextField id={paramName} name={paramName} defaultValue={textareaValue} multiline rows={4} required={required} /> <FluentUI.TextField id={paramName} name={paramName} defaultValue={textareaValue} multiline rows={4} required={required} className='isiportalPartialAdminFormFieldMultiLineText' />
).render(); ).render();
} }
@@ -216,7 +219,7 @@ export default abstract class ElixFormsComponentAbstract extends Component<IElix
const textValue = QueryParamHelper.getDecodedTextFromQuery(paramName) ?? ""; const textValue = QueryParamHelper.getDecodedTextFromQuery(paramName) ?? "";
return new ElixFormsElement( return new ElixFormsElement(
<FluentUI.Label htmlFor={paramName}>{label}</FluentUI.Label>, <FluentUI.Label htmlFor={paramName}>{label}</FluentUI.Label>,
<FluentUI.TextField id={paramName} name={paramName} defaultValue={textValue} required={required} /> <FluentUI.TextField id={paramName} name={paramName} defaultValue={textValue} required={required} className='isiportalPartialAdminFormFieldSingleLineText' />
).render(); ).render();
} }
@@ -229,20 +232,34 @@ export default abstract class ElixFormsComponentAbstract extends Component<IElix
const textValue = QueryParamHelper.getDecodedTextFromQuery(paramName) ?? ""; const textValue = QueryParamHelper.getDecodedTextFromQuery(paramName) ?? "";
return new ElixFormsElement( return new ElixFormsElement(
<FluentUI.Label htmlFor={paramName}>{label}</FluentUI.Label>, <FluentUI.Label htmlFor={paramName}>{label}</FluentUI.Label>,
<FluentUI.TextField id={paramName} name={paramName} defaultValue={textValue} type="number" required={required} /> <FluentUI.TextField id={paramName} name={paramName} defaultValue={textValue} type="number" required={required} className='isiportalPartialAdminFormFieldSingleLineText' />
).render(); ).render();
} }
private createDropdownInput(paramName: string, label: string, values: Map<number, string>, required: boolean = false): JSX.Element { private createDropdownInput(paramName: string, label: string, values: Array<ElixFormsDropdownOption>, required: boolean = false): JSX.Element {
const selectedValue = QueryParamHelper.getOptionFromQuery(paramName); const selectedValue = QueryParamHelper.getOptionFromQuery(paramName);
const options = Array.from(values).map(([num, val]) => ({ const options: FluentUI.IDropdownOption[] = [];
key: num.toString(),
text: val values.forEach(entry => {
})); const id = `${paramName}_${entry.value}`;
options.push(
{
key: id,
text: entry.label
}
);
});
return new ElixFormsElement( return new ElixFormsElement(
<FluentUI.Label htmlFor={paramName}>{label}</FluentUI.Label>, <FluentUI.Label htmlFor={paramName}>{label}</FluentUI.Label>,
<FluentUI.Dropdown id={paramName} options={options} selectedKey={selectedValue?.toString()} placeholder='---' required={required} /> <FluentUI.Dropdown
id={paramName}
options={options}
selectedKey={selectedValue?.toString()}
placeholder='---'
required={required}
className='isiportalPartialAdminFormFieldSelect'
/>
).render(); ).render();
} }
@@ -257,19 +274,25 @@ export default abstract class ElixFormsComponentAbstract extends Component<IElix
return this.createTextAreaInput(field.key, field.label, field.required ?? false); return this.createTextAreaInput(field.key, field.label, field.required ?? false);
case "radio": case "radio":
return this.createRadioInput(field.key, field.label, new Map<number, string>(field.options.map((o: any) => [o.value, o.label])), field.required ?? false); return this.createRadioInput(field.key, field.label,
new Array<ElixFormsRadioOption>().concat(...field.options.map((o: any) => [[o.value, o.label]])),
field.required ?? false);
case "boolean": case "boolean":
return this.createBooleanInput(field.key, field.label, field.required ?? false); return this.createBooleanInput(field.key, field.label, field.required ?? false);
case "checkbox": case "checkbox":
return this.createCheckboxInput(field.key, field.label, new Map<number, string>(field.options.map((o: any) => [o.value, o.label])), field.required ?? false); return this.createCheckboxInput(field.key, field.label,
new Array<ElixFormsCheckboxOption>().concat(...field.options.map((o: any) => [[o.value, o.label]])),
field.required ?? false);
case "number": case "number":
return this.createNumberInput(field.key, field.label, field.required ?? false); return this.createNumberInput(field.key, field.label, field.required ?? false);
case "dropdown": case "dropdown":
return this.createDropdownInput(field.key, field.label, new Map<number, string>(field.options.map((o: any) => [o.value, o.label])), field.required ?? false); return this.createDropdownInput(field.key, field.label,
new Array<ElixFormsDropdownOption>().concat(...field.options.map((o: any) => [[o.value, o.label]])),
field.required ?? false);
default: default:
return <></>; return <></>;
@@ -0,0 +1,3 @@
export type ElixFormsCheckboxOption = { value: number, label: string };
export type ElixFormsDropdownOption = { value: number, label: string };
export type ElixFormsRadioOption = { value: number, label: string };
@@ -1,12 +1,13 @@
import { JSX } from "react"; import { JSX } from "react";
import { ElixFormsCheckboxOption, ElixFormsDropdownOption, ElixFormsRadioOption } from "./ElixFormsTypes";
export type IElixFormsComponentCustomFormFieldFactory = { export type IElixFormsComponentCustomFormFieldFactory = {
createBooleanInput: (name: string, label: string, required?: boolean) => JSX.Element; createBooleanInput: (name: string, label: string, required?: boolean) => JSX.Element;
createCheckboxInput: (name: string, label: string, options: Map<number, string>, required?: boolean) => JSX.Element; createCheckboxInput: (name: string, label: string, options: Array<ElixFormsCheckboxOption>, required?: boolean) => JSX.Element;
createDropdownInput: (name: string, label: string, options: Map<number, string>, required?: boolean) => JSX.Element; createDropdownInput: (name: string, label: string, options: Array<ElixFormsDropdownOption>, required?: boolean) => JSX.Element;
createHiddenInput: (name: string) => JSX.Element; createHiddenInput: (name: string) => JSX.Element;
createNumberInput: (name: string, label: string, required?: boolean) => JSX.Element; createNumberInput: (name: string, label: string, required?: boolean) => JSX.Element;
createRadioInput: (name: string, label: string, options: Map<number, string>, required?: boolean) => JSX.Element; createRadioInput: (name: string, label: string, options: Array<ElixFormsRadioOption>, required?: boolean) => JSX.Element;
createTextInput: (name: string, label: string, required?: boolean) => JSX.Element; createTextInput: (name: string, label: string, required?: boolean) => JSX.Element;
createTextAreaInput: (name: string, label: string, required?: boolean) => JSX.Element; createTextAreaInput: (name: string, label: string, required?: boolean) => JSX.Element;
}; };