major refactor

rename classes
added props configuration for custom fields

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
2026-04-27 12:42:18 +02:00
co-authored by Copilot
parent 798c5d69f9
commit 58a7ce20a7
10 changed files with 197 additions and 81 deletions
+98 -14
View File
@@ -3,25 +3,109 @@ import reactLogo from './assets/react.svg'
import viteLogo from './assets/vite.svg' import viteLogo from './assets/vite.svg'
import heroImg from './assets/hero.png' import heroImg from './assets/hero.png'
import './App.css' import './App.css'
import * as ElixForms from './components/ElixFormsReact'; import * as ElixForms from './components/ElixFormsComponent';
function App() { function App() {
const [count, setCount] = useState(0) const [count, setCount] = useState(0)
/** @type {import('./components/ElixFormsComponent').ElixFormsReact} */
var myElixFormsReact = new ElixForms.ElixFormsReact(); var myElixFormsReact = new ElixForms.ElixFormsReact();
myElixFormsReact.addTextInput("name", "Name", true);
myElixFormsReact.addTextAreaInput("description", "Description"); myElixFormsReact.props = {
myElixFormsReact.addNumberInput("age", "Age"); description: "This is a custom form created with ElixFormsReact component.",
myElixFormsReact.addDropdownInput("country", "Country", new Map<number, string>([ isDarkTheme: false,
[1, "Italy"], environmentMessage: "TESTING!",
[2, "USA"], additionalFieldsJson: JSON.stringify([
[3, "UK"] {
])); "key": "COL0002",
myElixFormsReact.addCheckboxInput("hobbies", "Hobbies", new Map<number, string>([ "type": "text",
[1, "Sports"], "label": "Campo STRING",
[2, "Music"], "required": false
[3, "Traveling"] },
])); {
"key": "COL0003",
"type": "textarea",
"label": "Campo TEXTAREA",
"required": false
},
{
"key": "COL0004",
"type": "boolean",
"label": "Campo Boolean",
"required": false
},
{
"key": "COL0005",
"type": "radio",
"label": "Campo _RADIO_",
"required": false,
"options": [
{ "value": 1, "label": "Opzione 1" },
{ "value": 2, "label": "Opzione 2" },
{ "value": 3, "label": "Altra opzione" }
]
},
{
"key": "COL0006",
"type": "checkbox",
"label": "Campo CHECKBOX",
"options": [
{ "value": 4, "label": "Check 1" },
{ "value": 5, "label": "Check due" },
{ "value": 6, "label": "Altro check" }
]
},
{
"key": "COL0015",
"type": "dropdown",
"label": "Validazione lista",
"required": true,
"options": [
{ "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" },
{ "value": 4, "label": "Goal 4: Quality education" },
{ "value": 5, "label": "Goal 5: Gender equality" },
{ "value": 6, "label": "Goal 6: Clean water and sanitation" },
{ "value": 7, "label": "Goal 7: Affordable and clean energy" },
{ "value": 8, "label": "Goal 8: Decent work and economic growth" },
{ "value": 9, "label": "Goal 9: Industry, Innovation, and Infrastructure" },
{ "value": 10, "label": "Goal 10: Reduced inequalities" },
{ "value": 11, "label": "Goal 11: Sustainable cities and communities" },
{ "value": 12, "label": "Goal 12: Responsible consumption and production" },
{ "value": 13, "label": "Goal 13: Climate action" },
{ "value": 14, "label": "Goal 14: Life below water" },
{ "value": 15, "label": "Goal 15: Life on land" },
{ "value": 16, "label": "Goal 16: Peace, justice and strong institutions" },
{ "value": 17, "label": "Goal 17: Partnerships for the goals" }
]
}
]),
customFormFieldsConfiguration: (
/** @type {import('./components/IElixFormsComponentProperties').ICustomFormFieldFactory} */
formFieldFactory
) => (
<>
{formFieldFactory.createTextInput("name", "Name", true)}
{formFieldFactory.createTextAreaInput("description", "Description")}
</>
)
};
// myElixFormsReact.addTextInput("name", "Name", true);
// myElixFormsReact.addTextAreaInput("description", "Description");
// myElixFormsReact.addNumberInput("age", "Age");
// myElixFormsReact.addDropdownInput("country", "Country", new Map<number, string>([
// [1, "Italy"],
// [2, "USA"],
// [3, "UK"]
// ]));
// myElixFormsReact.addCheckboxInput("hobbies", "Hobbies", new Map<number, string>([
// [1, "Sports"],
// [2, "Music"],
// [3, "Traveling"]
// ]));
return myElixFormsReact.render(); return myElixFormsReact.render();
} }
@@ -1,6 +1,6 @@
@import '~@fluentui/react/dist/sass/References.scss'; @import '~@fluentui/react/dist/sass/References.scss';
.elixFormsReact { .elixFormsComponent {
overflow: hidden; overflow: hidden;
padding: 1em; padding: 1em;
color: "[theme:bodyText, default: #323130]"; color: "[theme:bodyText, default: #323130]";
@@ -0,0 +1,16 @@
import * as React from 'react';
import ElixFormsComponentAbstract from './ElixFormsComponentAbstract';
export default class ElixFormsComponent extends ElixFormsComponentAbstract {
// public override createCustomFormFields(): React.ReactElement {
// // const textTest = this.CreateTextInput("COL0018", "TEXT INPUT");
// // const checkTest = this.CreateCheckboxInput("COL0090", "CHECKBOX INPUT", new Map<number, string>([ [1, "Option 1"], [2, "Option 2"], [3, "Option 3"] ]));
// // const radioTest = this.CreateRadioInput("COL0091", "RADIO INPUT", new Map<number, string>([ [1, "Option 1"], [2, "Option 2"], [3, "Option 3"] ]));
// // const boolTest = this.CreateBooleanInput("COL0092", "BOOLEAN INPUT");
// // const textareaTest = this.CreateTextAreaInput("COL0093", "TEXTAREA INPUT");
// return (<></>);
// }
}
export { ElixFormsComponent as ElixFormsReact };
@@ -1,13 +1,14 @@
import * as FluentUI from '@fluentui/react'; import * as FluentUI from '@fluentui/react';
import { IElixFormsReactFormState } from './IElixFormsReactFormState'; import { IElixFormsComponentFormState } from './IElixFormsComponentFormState';
import { IElixFormsReactProps } from './IElixFormsReactProps'; import { IElixFormsComponentProperties } from './IElixFormsComponentProperties';
import { ElixFormElement } from './ElixFormElement'; import { ElixFormsElement } from './ElixFormsElement';
import { QueryParamHelper } from './QueryParamHelper'; 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';
export default abstract class ElixFormsReactAbstract extends Component<IElixFormsReactProps, IElixFormsReactFormState> { export default abstract class ElixFormsComponentAbstract extends Component<IElixFormsComponentProperties, IElixFormsComponentFormState> {
constructor(props: IElixFormsReactProps) { constructor(props: IElixFormsComponentProperties) {
super(props); super(props);
this.state = { this.state = {
@@ -56,24 +57,38 @@ export default abstract class ElixFormsReactAbstract extends Component<IElixForm
); );
} }
public createCustomFormFields() : React.ReactElement { public createCustomFormFields(callback: (formFieldFactory: IElixFormsComponentCustomFormFieldFactory) => JSX.Element = () => <></>): JSX.Element {
// const textTest = this.CreateTextInput("COL0018", "TEXT INPUT"); const formFieldFactory = {
// const checkTest = this.CreateCheckboxInput("COL0090", "CHECKBOX INPUT", new Map<number, string>([ [1, "Option 1"], [2, "Option 2"], [3, "Option 3"] ])); createBooleanInput: (name: string, label: string, required: boolean = false) =>
// const radioTest = this.CreateRadioInput("COL0091", "RADIO INPUT", new Map<number, string>([ [1, "Option 1"], [2, "Option 2"], [3, "Option 3"] ])); this.createBooleanInput(name, label, required),
// const boolTest = this.CreateBooleanInput("COL0092", "BOOLEAN INPUT"); createCheckboxInput: (name: string, label: string, options: Map<number, string>, required: boolean = false) =>
// const textareaTest = this.CreateTextAreaInput("COL0093", "TEXTAREA INPUT"); this.createCheckboxInput(name, label, options, required),
createDropdownInput: (name: string, label: string, options: Map<number, string>, required: boolean = false) =>
this.createDropdownInput(name, label, options, required),
createHiddenInput: (name: string) =>
this.createHiddenInput(name),
createNumberInput: (name: string, label: string, required: boolean = false) =>
this.createNumberInput(name, label, required),
createRadioInput: (name: string, label: string, options: Map<number, string>, required: boolean = false) =>
this.createRadioInput(name, label, options, required),
createTextInput: (name: string, label: string, required: boolean = false) =>
this.createTextInput(name, label, required),
createTextAreaInput: (name: string, label: string, required: boolean = false) =>
this.createTextAreaInput(name, label, required),
};
return (<></>); return callback(formFieldFactory);
} }
public override render(): React.ReactElement<IElixFormsReactProps> { public override render(): React.ReactElement<IElixFormsComponentProperties> {
const { const {
description, description,
isDarkTheme, isDarkTheme,
environmentMessage, environmentMessage,
hasTeamsContext, hasTeamsContext,
userDisplayName, userDisplayName,
additionalFieldsJson additionalFieldsJson,
customFormFieldsConfiguration
} = this.props; } = this.props;
const queryParams = new URLSearchParams(window.location.search); const queryParams = new URLSearchParams(window.location.search);
@@ -82,17 +97,18 @@ export default abstract class ElixFormsReactAbstract extends Component<IElixForm
<section> <section>
{/* className={`${styles.elixFormsReact} ${hasTeamsContext ? styles.teams : ''}`} */} {/* className={`${styles.elixFormsReact} ${hasTeamsContext ? styles.teams : ''}`} */}
<div>{environmentMessage}</div> <div>{environmentMessage}</div>
<div>{description}</div>
<form action="https://procedure.unipr.it/rwe2/ComeBackToElixAndSave" method="post"> <form action="https://procedure.unipr.it/rwe2/ComeBackToElixAndSave" method="post">
{this.createMandatoryFormFields(queryParams)} {this.createMandatoryFormFields(queryParams)}
{this.createAdditionalFormFields()} {this.createAdditionalFormFields()}
{this.createCustomFormFields()} {this.createCustomFormFields(customFormFieldsConfiguration)}
<input type="submit" value="Submit"/> <input type="submit" value="Submit"/>
</form> </form>
</section> </section>
); );
} }
private createCheckboxInput(paramName: string, label: string, values: Map<number, string>, required: boolean = false) : ElixFormElement { private createCheckboxInput(paramName: string, label: string, values: Map<number, string>, 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)) {
@@ -120,17 +136,17 @@ export default abstract class ElixFormsReactAbstract extends Component<IElixForm
); );
}); });
return new ElixFormElement( return new ElixFormsElement(
<><FluentUI.Label htmlFor={paramName}>{label}</FluentUI.Label></>, <><FluentUI.Label htmlFor={paramName}>{label}</FluentUI.Label></>,
<><FluentUI.Stack tokens={stackTokens}> <><FluentUI.Stack tokens={stackTokens}>
{checkboxes} {checkboxes}
</FluentUI.Stack> </FluentUI.Stack>
<input type='hidden' id={paramName} name={paramName} value={this.state.formData[paramName] ?? ''} /> <input type='hidden' id={paramName} name={paramName} value={this.state.formData[paramName] ?? ''} />
</> </>
); ).render();
} }
private createRadioInput(paramName: string, label: string, values: Map<number, string>, required: boolean = false) : ElixFormElement { private createRadioInput(paramName: string, label: string, values: Map<number, string>, required: boolean = false) : JSX.Element {
const radioValue = QueryParamHelper.getOptionFromQuery(paramName); const radioValue = QueryParamHelper.getOptionFromQuery(paramName);
const radios: FluentUI.IChoiceGroupOption[] = []; const radios: FluentUI.IChoiceGroupOption[] = [];
@@ -147,7 +163,7 @@ export default abstract class ElixFormsReactAbstract extends Component<IElixForm
); );
}); });
return new ElixFormElement( return new ElixFormsElement(
<></>, <></>,
<FluentUI.ChoiceGroup <FluentUI.ChoiceGroup
name={paramName} name={paramName}
@@ -156,10 +172,10 @@ export default abstract class ElixFormsReactAbstract extends Component<IElixForm
required={required} required={required}
defaultSelectedKey={`${paramName}_${radioValue}`} defaultSelectedKey={`${paramName}_${radioValue}`}
/> />
); ).render();
} }
private createBooleanInput(paramName: string, label: string, required: boolean = false) : ElixFormElement { private createBooleanInput(paramName: string, label: string, required: boolean = false) : JSX.Element {
const boolValue = QueryParamHelper.getOptionFromQuery(paramName); const boolValue = QueryParamHelper.getOptionFromQuery(paramName);
const radios: FluentUI.IChoiceGroupOption[] = []; const radios: FluentUI.IChoiceGroupOption[] = [];
@@ -176,7 +192,7 @@ export default abstract class ElixFormsReactAbstract extends Component<IElixForm
); );
}); });
return new ElixFormElement( return new ElixFormsElement(
<></>, <></>,
<FluentUI.ChoiceGroup <FluentUI.ChoiceGroup
name={paramName} name={paramName}
@@ -185,49 +201,49 @@ export default abstract class ElixFormsReactAbstract extends Component<IElixForm
required={required} required={required}
defaultSelectedKey={`${paramName}_${boolValue}`} defaultSelectedKey={`${paramName}_${boolValue}`}
/> />
); ).render();
} }
private createTextAreaInput(paramName: string, label: string, required: boolean = false) : ElixFormElement { private createTextAreaInput(paramName: string, label: string, required: boolean = false) : JSX.Element {
const textareaValue = QueryParamHelper.getDecodedTextFromQuery(paramName) ?? ""; const textareaValue = QueryParamHelper.getDecodedTextFromQuery(paramName) ?? "";
return new ElixFormElement( 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} />
); ).render();
} }
private createTextInput(paramName: string, label: string, required: boolean = false) : ElixFormElement { private createTextInput(paramName: string, label: string, required: boolean = false) : JSX.Element {
const textValue = QueryParamHelper.getDecodedTextFromQuery(paramName) ?? ""; const textValue = QueryParamHelper.getDecodedTextFromQuery(paramName) ?? "";
return new ElixFormElement( 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} />
); ).render();
} }
private createHiddenInput(paramName: string) : JSX.Element { private createHiddenInput(paramName: string) : JSX.Element {
const textValue = QueryParamHelper.getDecodedTextFromQuery(paramName) ?? ""; const textValue = QueryParamHelper.getDecodedTextFromQuery(paramName) ?? "";
return <input type='hidden' id={paramName} name={paramName} defaultValue={textValue} />; return <><input type='text' id={paramName} name={paramName} defaultValue={textValue} /><br/></>;
} }
private createNumberInput(paramName: string, label: string, required: boolean = false) : ElixFormElement { private createNumberInput(paramName: string, label: string, required: boolean = false) : JSX.Element {
const textValue = QueryParamHelper.getDecodedTextFromQuery(paramName) ?? ""; const textValue = QueryParamHelper.getDecodedTextFromQuery(paramName) ?? "";
return new ElixFormElement( 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} />
); ).render();
} }
private createDropdownInput(paramName: string, label: string, values: Map<number, string>, required: boolean = false): ElixFormElement { private createDropdownInput(paramName: string, label: string, values: Map<number, string>, 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 = Array.from(values).map(([num, val]) => ({
key: num.toString(), key: num.toString(),
text: val text: val
})); }));
return new ElixFormElement( 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} />
); ).render();
} }
private renderField(field: any): JSX.Element { private renderField(field: any): JSX.Element {
@@ -235,25 +251,25 @@ export default abstract class ElixFormsReactAbstract extends Component<IElixForm
switch (field.type) { switch (field.type) {
case "text": case "text":
return this.createTextInput(field.key, field.label, field.required ?? false).render(); return this.createTextInput(field.key, field.label, field.required ?? false);
case "textarea": case "textarea":
return this.createTextAreaInput(field.key, field.label, field.required ?? false).render(); 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).render(); return this.createRadioInput(field.key, field.label, new Map<number, string>(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).render(); 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).render(); return this.createCheckboxInput(field.key, field.label, new Map<number, string>(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).render(); 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).render(); return this.createDropdownInput(field.key, field.label, new Map<number, string>(field.options.map((o: any) => [o.value, o.label])), field.required ?? false);
default: default:
return <></>; return <></>;
@@ -1,7 +1,7 @@
import React, { JSX } from "react"; import React, { JSX } from "react";
// Class to encapsulate the label and input elements // Class to encapsulate the label and input elements
export class ElixFormElement { export class ElixFormsElement {
label: JSX.Element; label: JSX.Element;
input: JSX.Element; input: JSX.Element;
separator: string; separator: string;
@@ -1,16 +0,0 @@
import * as React from 'react';
import ElixFormsReactAbstract from './ElixFormsReactAbstract';
export default class ElixFormsReact extends ElixFormsReactAbstract {
public override createCustomFormFields(): React.ReactElement {
// const textTest = this.CreateTextInput("COL0018", "TEXT INPUT");
// const checkTest = this.CreateCheckboxInput("COL0090", "CHECKBOX INPUT", new Map<number, string>([ [1, "Option 1"], [2, "Option 2"], [3, "Option 3"] ]));
// const radioTest = this.CreateRadioInput("COL0091", "RADIO INPUT", new Map<number, string>([ [1, "Option 1"], [2, "Option 2"], [3, "Option 3"] ]));
// const boolTest = this.CreateBooleanInput("COL0092", "BOOLEAN INPUT");
// const textareaTest = this.CreateTextAreaInput("COL0093", "TEXTAREA INPUT");
return (<></>);
}
}
export { ElixFormsReact };
@@ -0,0 +1,10 @@
import { JSX } from "react";
export type IElixFormsComponentCustomFormFieldFactory = {
createTextInput: (name: string, label: string, required?: boolean) => JSX.Element;
createTextAreaInput: (name: string, label: string, required?: boolean) => JSX.Element;
createNumberInput: (name: string, label: string, required?: boolean) => JSX.Element;
createDropdownInput: (name: string, label: string, options: Map<number, string>, required?: boolean) => JSX.Element;
createCheckboxInput: (name: string, label: string, options: Map<number, string>, required?: boolean) => JSX.Element;
};
@@ -0,0 +1,4 @@
export interface IElixFormsComponentFormState {
formData: { [key: string]: any; };
}
@@ -1,10 +1,16 @@
export interface IElixFormsReactProps { import { JSX } from "react";
import { IElixFormsComponentCustomFormFieldFactory } from "./IElixFormsComponentCustomFormFieldFactory";
export type ICustomFormFieldsConfiguration = (formFieldFactory: IElixFormsComponentCustomFormFieldFactory) => JSX.Element;
export interface IElixFormsComponentProperties {
description: string; description: string;
isDarkTheme: boolean; isDarkTheme: boolean;
environmentMessage: string; environmentMessage: string;
hasTeamsContext: boolean; hasTeamsContext: boolean;
userDisplayName: string; userDisplayName: string;
additionalFieldsJson?: string; additionalFieldsJson?: string;
customFormFieldsConfiguration?: ICustomFormFieldsConfiguration;
} }
// Additional Fields JSON Schema // Additional Fields JSON Schema
@@ -1,4 +0,0 @@
export interface IElixFormsReactFormState {
formData: { [key: string]: any; };
}