Fixed linting issues, added container
This commit is contained in:
parent
2beeb06fc7
commit
034937a0fe
|
@ -0,0 +1,39 @@
|
|||
// For more information on how to run this SPFx project in a VS Code Remote Container, please visit https://aka.ms/spfx-devcontainer
|
||||
{
|
||||
"name": "SPFx 1.17.1",
|
||||
"image": "docker.io/m365pnp/spfx:1.17.1",
|
||||
// Set *default* container specific settings.json values on container create.
|
||||
"settings": {},
|
||||
// Add the IDs of extensions you want installed when the container is created.
|
||||
"extensions": [
|
||||
"editorconfig.editorconfig",
|
||||
"dbaeumer.vscode-eslint"
|
||||
],
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
"forwardPorts": [
|
||||
4321,
|
||||
35729
|
||||
],
|
||||
"portsAttributes": {
|
||||
"4321": {
|
||||
"protocol": "https",
|
||||
"label": "Manifest",
|
||||
"onAutoForward": "silent",
|
||||
"requireLocalPort": true
|
||||
},
|
||||
// Not needed for SPFx>= 1.12.1
|
||||
// "5432": {
|
||||
// "protocol": "https",
|
||||
// "label": "Workbench",
|
||||
// "onAutoForward": "silent"
|
||||
// },
|
||||
"35729": {
|
||||
"protocol": "https",
|
||||
"label": "LiveReload",
|
||||
"onAutoForward": "silent",
|
||||
"requireLocalPort": true
|
||||
}
|
||||
},
|
||||
"postCreateCommand": "bash .devcontainer/spfx-startup.sh",
|
||||
"remoteUser": "node"
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
echo
|
||||
echo -e "\e[1;94mInstalling Node dependencies\e[0m"
|
||||
npm install
|
||||
|
||||
## commands to create dev certificate and copy it to the root folder of the project
|
||||
echo
|
||||
echo -e "\e[1;94mGenerating dev certificate\e[0m"
|
||||
gulp trust-dev-cert
|
||||
|
||||
# Convert the generated PEM certificate to a CER certificate
|
||||
openssl x509 -inform PEM -in ~/.rushstack/rushstack-serve.pem -outform DER -out ./spfx-dev-cert.cer
|
||||
|
||||
# Copy the PEM ecrtificate for non-Windows hosts
|
||||
cp ~/.rushstack/rushstack-serve.pem ./spfx-dev-cert.pem
|
||||
|
||||
## add *.cer to .gitignore to prevent certificates from being saved in repo
|
||||
if ! grep -Fxq '*.cer' ./.gitignore
|
||||
then
|
||||
echo "# .CER Certificates" >> .gitignore
|
||||
echo "*.cer" >> .gitignore
|
||||
fi
|
||||
|
||||
## add *.pem to .gitignore to prevent certificates from being saved in repo
|
||||
if ! grep -Fxq '*.pem' ./.gitignore
|
||||
then
|
||||
echo "# .PEM Certificates" >> .gitignore
|
||||
echo "*.pem" >> .gitignore
|
||||
fi
|
||||
|
||||
echo
|
||||
echo -e "\e[1;92mReady!\e[0m"
|
||||
|
||||
echo -e "\n\e[1;94m**********\nOptional: if you plan on using gulp serve, don't forget to add the container certificate to your local machine. Please visit https://aka.ms/spfx-devcontainer for more information\n**********"
|
|
@ -1,5 +1,6 @@
|
|||
import { useState } from 'react';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||
export default function useObject<T>(InitialValue?: T) {
|
||||
const [value, setValue] = useState<T>(InitialValue ?? {} as T);
|
||||
const updateValue: (Updates: Partial<T>) => void = (Updates: Partial<T>) => setValue((prev) => ({ ...prev, ...Updates }))
|
||||
|
|
|
@ -2,5 +2,6 @@ import { IForm } from "./Form";
|
|||
|
||||
export interface SaveObject {
|
||||
form: IForm;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
response: any;
|
||||
}
|
|
@ -13,11 +13,11 @@ const UNSUPPORTED_LOOKUP_FIELDTYPES: FieldType[] = [FieldType.PlaceHolder, Field
|
|||
export const GetLookupFields: (Fields: IField[]) => IField[] = (Fields: IField[]) => {
|
||||
const arr: IField[] = [];
|
||||
|
||||
for (let field of Fields) {
|
||||
if (field.Type == FieldType.FieldGroup) {
|
||||
for (const field of Fields) {
|
||||
if (field.Type === FieldType.FieldGroup) {
|
||||
arr.push(...GetLookupFields((field as IGroupField).Fields))
|
||||
} else if (field.Type == FieldType.Conditional) {
|
||||
if ((field as IConditionalField).Field.Type == FieldType.FieldGroup) {
|
||||
} else if (field.Type === FieldType.Conditional) {
|
||||
if ((field as IConditionalField).Field.Type === FieldType.FieldGroup) {
|
||||
arr.push(...GetLookupFields(((field as IConditionalField).Field as IGroupField).Fields))
|
||||
} else {
|
||||
arr.push((field as IConditionalField).Field);
|
||||
|
@ -27,5 +27,5 @@ export const GetLookupFields: (Fields: IField[]) => IField[] = (Fields: IField[]
|
|||
}
|
||||
}
|
||||
|
||||
return arr.filter(x => x.DisplayName != null && !UNSUPPORTED_LOOKUP_FIELDTYPES.some(bannedType => bannedType == x.Type));;
|
||||
return arr.filter(x => x.DisplayName !== null && !UNSUPPORTED_LOOKUP_FIELDTYPES.some(bannedType => bannedType === x.Type));
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ export default class JsonFormWebPart extends BaseClientSideWebPart<IJsonFormWebP
|
|||
JsonForm,
|
||||
{
|
||||
Form: JSON.parse(this.properties.formJson),
|
||||
// eslint-disable-next-line no-return-assign
|
||||
SaveForm: (updated: IForm) => this.properties.formJson = JSON.stringify({ ...JSON.parse(this.properties.formJson), ...updated }, null, 2),
|
||||
Mode: this.displayMode,
|
||||
ListId: this.properties.listId,
|
||||
|
|
|
@ -6,6 +6,7 @@ import { Label } from 'office-ui-fabric-react';
|
|||
|
||||
export interface IFieldProps {
|
||||
onChange: (updates: object) => void;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
form: any;
|
||||
field: IField;
|
||||
readonly: boolean;
|
||||
|
@ -50,6 +51,7 @@ export const Field: React.FunctionComponent<IFieldProps> = (props: React.PropsWi
|
|||
/>;
|
||||
|
||||
case FieldType.Choice: return <Dropdown
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
options={(field as any as IChoiceField).Options.map(x => ({ key: x, text: x }))}
|
||||
selectedKey={form[field.Id] ?? ""}
|
||||
label={field.DisplayName}
|
||||
|
@ -57,32 +59,34 @@ export const Field: React.FunctionComponent<IFieldProps> = (props: React.PropsWi
|
|||
/>
|
||||
|
||||
case FieldType.MultiChoice: return <Dropdown
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
options={(field as any as IChoiceField).Options.map(x => ({ key: x, text: x }))}
|
||||
selectedKeys={form[field.Id] ?? []}
|
||||
label={field.DisplayName}
|
||||
multiSelect
|
||||
onChange={readonly ? null : (_, val) => {
|
||||
let selected: string[] = form[field.Id] ?? [];
|
||||
selected = val.selected ? [...selected, val.key as string] : selected.filter(x => x != val.key);
|
||||
selected = val.selected ? [...selected, val.key as string] : selected.filter(x => x !== val.key);
|
||||
onChange({ [field.Id]: selected })
|
||||
}}
|
||||
/>
|
||||
|
||||
case FieldType.FieldGroup: return <div>
|
||||
{(field.DisplayName != null || field.DisplayName != "") && <Label>{field.DisplayName}</Label>}
|
||||
{(field.DisplayName !== null || field.DisplayName !== "") && <Label>{field.DisplayName}</Label>}
|
||||
<div
|
||||
style={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: (field as IGroupField).Direction == GroupDirection.Horizontal ? `repeat(auto-fill,minmax(calc(${100 / (field as any as IGroupField).Fields.length}% - 10px),1fr))` : '',
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
gridTemplateColumns: (field as IGroupField).Direction === GroupDirection.Horizontal ? `repeat(auto-fill,minmax(calc(${100 / (field as any as IGroupField).Fields.length}% - 10px),1fr))` : '',
|
||||
gap: 10
|
||||
}}>
|
||||
{(field as IGroupField).Fields.map(f => <Field {...props} field={f} />)}
|
||||
{(field as IGroupField).Fields.map((f, index) => <Field {...props} field={f} key={index} />)}
|
||||
</div>
|
||||
</div>;
|
||||
|
||||
case FieldType.Conditional: {
|
||||
const f = (field as IConditionalField);
|
||||
const visible = form[f.LookupFieldId] == f.MatchValue
|
||||
const visible = form[f.LookupFieldId] === f.MatchValue
|
||||
if (!visible) return <></>
|
||||
return <Field readonly={readonly} field={f.Field} form={form} onChange={onChange} />
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/* eslint-disable @typescript-eslint/no-use-before-define */
|
||||
import * as React from 'react';
|
||||
import { FieldType, GroupDirection, IChoiceField, IConditionalField, IField, IGroupField } from '../../../../Models/FormField';
|
||||
import { ActionButton, ChoiceGroup, DefaultButton, Dialog, DialogFooter, Dropdown, Label, Position, PrimaryButton, SpinButton, Stack, TextField } from '@fluentui/react';
|
||||
|
@ -42,16 +43,16 @@ export const FieldEditorDialog: React.FunctionComponent<IFieldEditorDialogProps>
|
|||
const t = val.key as FieldType;
|
||||
const updates: Partial<IField | IChoiceField | IGroupField | IConditionalField> = { Type: t }
|
||||
|
||||
if (t == FieldType.Choice || t == FieldType.MultiChoice)
|
||||
if ((field as IChoiceField).Options == null)
|
||||
if (t === FieldType.Choice || t === FieldType.MultiChoice)
|
||||
if ((field as IChoiceField).Options === null)
|
||||
(updates as Partial<IChoiceField>).Options = [];
|
||||
|
||||
if (t == FieldType.Conditional)
|
||||
if ((field as IConditionalField).Field == null)
|
||||
if (t === FieldType.Conditional)
|
||||
if ((field as IConditionalField).Field === null)
|
||||
(updates as Partial<IConditionalField>).Field = NewField();
|
||||
|
||||
if (t == FieldType.FieldGroup)
|
||||
if ((field as IGroupField).Fields == null) {
|
||||
if (t === FieldType.FieldGroup)
|
||||
if ((field as IGroupField).Fields === null) {
|
||||
(updates as Partial<IGroupField>).Fields = [];
|
||||
(updates as Partial<IGroupField>).Direction = GroupDirection.Horizontal;
|
||||
}
|
||||
|
@ -60,12 +61,12 @@ export const FieldEditorDialog: React.FunctionComponent<IFieldEditorDialogProps>
|
|||
}}
|
||||
/>
|
||||
|
||||
{FieldType.Conditional != field.Type && <TextField label='Title' value={field.DisplayName} onChange={(_, val) => updateValue({ DisplayName: val })} />}
|
||||
{FieldType.Conditional !== field.Type && <TextField label='Title' value={field.DisplayName} onChange={(_, val) => updateValue({ DisplayName: val })} />}
|
||||
|
||||
|
||||
{[FieldType.Choice, FieldType.MultiChoice].some(x => x == field.Type) && <ChoiceFieldOptions field={field as IChoiceField} updateValue={updateValue} />}
|
||||
{FieldType.Conditional == field.Type && <ConditionalFieldOptions field={field as IConditionalField} updateValue={updateValue} allFieldsFlat={props.allFieldsFlat} />}
|
||||
{FieldType.FieldGroup == field.Type && <GroupFieldOptions field={field as IGroupField} updateValue={updateValue} />}
|
||||
{[FieldType.Choice, FieldType.MultiChoice].some(x => x === field.Type) && <ChoiceFieldOptions field={field as IChoiceField} updateValue={updateValue} />}
|
||||
{FieldType.Conditional === field.Type && <ConditionalFieldOptions field={field as IConditionalField} updateValue={updateValue} allFieldsFlat={props.allFieldsFlat} />}
|
||||
{FieldType.FieldGroup === field.Type && <GroupFieldOptions field={field as IGroupField} updateValue={updateValue} />}
|
||||
|
||||
<DialogFooter>
|
||||
<PrimaryButton text='Delete' iconProps={{ iconName: 'delete' }} onClick={() => props.delete()} styles={{ root: { backgroundColor: "#FF0000" }, rootHovered: { backgroundColor: "#D10000" }, rootChecked: { backgroundColor: "#A30000" } }} />
|
||||
|
@ -90,9 +91,12 @@ export const GroupFieldOptions: React.FunctionComponent<IGroupFieldOptionsProps>
|
|||
label='Direction'
|
||||
selectedKey={field.Direction}
|
||||
options={[
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
{ key: GroupDirection.Horizontal as any as string, text: "Horizontal", iconProps: { iconName: "AlignVerticalCenter" } },
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
{ key: GroupDirection.Vertical as any as string, text: "Vertical", iconProps: { iconName: "AlignHorizontalCenter" } }
|
||||
]}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
onChange={(_, val) => updateValue({ Direction: val.key as any as GroupDirection })}
|
||||
/>
|
||||
</>
|
||||
|
@ -110,7 +114,7 @@ const ChoiceFieldOptions: React.FunctionComponent<IChoiceFieldOptionsProps> = (p
|
|||
return (<>
|
||||
<Label>Options</Label>
|
||||
{(field as IChoiceField).Options.map((val, index) => {
|
||||
return <div style={{ display: "flex" }}>
|
||||
return <div style={{ display: "flex" }} key={index}>
|
||||
<TextField
|
||||
styles={{ root: { flexGrow: 1 } }}
|
||||
value={val} onChange={(_, val) => {
|
||||
|
@ -118,7 +122,7 @@ const ChoiceFieldOptions: React.FunctionComponent<IChoiceFieldOptionsProps> = (p
|
|||
options[index] = val
|
||||
updateValue({ Options: options });
|
||||
}} />
|
||||
<ActionButton iconProps={{ iconName: "Delete" }} onClick={() => updateValue({ Options: (field as IChoiceField).Options.filter((_, i) => i != index) })} />
|
||||
<ActionButton iconProps={{ iconName: "Delete" }} onClick={() => updateValue({ Options: (field as IChoiceField).Options.filter((_, i) => i !== index) })} />
|
||||
</div>
|
||||
})}
|
||||
<PrimaryButton iconProps={{ iconName: "Add" }} text='Add option' onClick={() => {
|
||||
|
@ -136,7 +140,7 @@ interface IConditionalFieldOptionsProps {
|
|||
|
||||
const ConditionalFieldOptions: React.FunctionComponent<IConditionalFieldOptionsProps> = (props: React.PropsWithChildren<IConditionalFieldOptionsProps>) => {
|
||||
const { allFieldsFlat, field, updateValue } = props
|
||||
const targetField = allFieldsFlat.filter(x => x.Id == (field as IConditionalField).LookupFieldId)[0]
|
||||
const targetField = allFieldsFlat.filter(x => x.Id === (field as IConditionalField).LookupFieldId)[0]
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -147,23 +151,23 @@ const ConditionalFieldOptions: React.FunctionComponent<IConditionalFieldOptionsP
|
|||
onChange={(_, val) => updateValue({ LookupFieldId: val.key as string })}
|
||||
/>
|
||||
|
||||
{targetField != null &&
|
||||
{targetField !== null &&
|
||||
<>
|
||||
{FieldType.Choice == targetField.Type && <Dropdown
|
||||
{FieldType.Choice === targetField.Type && <Dropdown
|
||||
label='Show if is equal to'
|
||||
options={(targetField as IChoiceField).Options.map(x => ({ key: x, text: x }))}
|
||||
selectedKey={(field as IConditionalField).MatchValue as string}
|
||||
onChange={(_, val) => updateValue({ MatchValue: val.text })}
|
||||
/>}
|
||||
|
||||
{FieldType.Boolean == targetField.Type && <Dropdown
|
||||
{FieldType.Boolean === targetField.Type && <Dropdown
|
||||
label='Show if is equal to'
|
||||
options={[{ key: true.toString(), text: "Yes" }, { key: false.toString(), text: "No" }]}
|
||||
selectedKey={(field as IConditionalField).MatchValue?.toString()}
|
||||
onChange={(_, val) => updateValue({ MatchValue: val.key == true.toString() })}
|
||||
onChange={(_, val) => updateValue({ MatchValue: val.key === true.toString() })}
|
||||
/>}
|
||||
|
||||
{FieldType.Number == targetField.Type && <SpinButton
|
||||
{FieldType.Number === targetField.Type && <SpinButton
|
||||
label='Show if is equal to'
|
||||
inputMode='numeric'
|
||||
labelPosition={Position.top}
|
||||
|
@ -172,7 +176,7 @@ const ConditionalFieldOptions: React.FunctionComponent<IConditionalFieldOptionsP
|
|||
/>
|
||||
}
|
||||
|
||||
{[FieldType.Text, FieldType.MultilineText].some(x => x == targetField.Type) && <TextField
|
||||
{[FieldType.Text, FieldType.MultilineText].some(x => x === targetField.Type) && <TextField
|
||||
value={(field as IConditionalField).MatchValue as string}
|
||||
label={"Value to look for"}
|
||||
onChange={(_, val) => updateValue({ MatchValue: val })}
|
||||
|
|
|
@ -17,7 +17,7 @@ const MAX_NUMBER_OF_ITEMS_IN_GROUP: number = 5;
|
|||
|
||||
export const FormFieldCustomizer: React.FunctionComponent<IFormFieldCustomizer> = (props: React.PropsWithChildren<IFormFieldCustomizer>) => {
|
||||
const { field } = props;
|
||||
const [shouldEdit, setShouldEdit] = React.useState<Boolean>(false);
|
||||
const [shouldEdit, setShouldEdit] = React.useState<boolean>(false);
|
||||
|
||||
const editDialog = <FieldEditorDialog
|
||||
allFieldsFlat={props.allFieldsFlat}
|
||||
|
@ -34,20 +34,21 @@ export const FormFieldCustomizer: React.FunctionComponent<IFormFieldCustomizer>
|
|||
/>
|
||||
|
||||
|
||||
if (FieldType.FieldGroup == field.Type) {
|
||||
if (FieldType.FieldGroup === field.Type) {
|
||||
const f = (field as IGroupField)
|
||||
const AtCapacity = f.Fields.length == MAX_NUMBER_OF_ITEMS_IN_GROUP;
|
||||
const AtCapacity = f.Fields.length === MAX_NUMBER_OF_ITEMS_IN_GROUP;
|
||||
return <div>
|
||||
{shouldEdit && editDialog}
|
||||
<div style={{ display: "flex" }}>
|
||||
{(f.DisplayName != null || f.DisplayName != "") && <Label disabled>{f.DisplayName}</Label>}
|
||||
{(f.DisplayName !== null || f.DisplayName !== "") && <Label disabled>{f.DisplayName}</Label>}
|
||||
<ActionButton iconProps={{ iconName: "Edit" }} onClick={() => setShouldEdit(true)} />
|
||||
<ActionButton iconProps={{ iconName: "Delete" }} onClick={() => props.delete()} />
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: (field as IGroupField).Direction == GroupDirection.Horizontal ? `repeat(auto-fill,minmax(calc(${100 / ((field as any as IGroupField).Fields.length + (!AtCapacity ? 1 : 0))}% - 10px),1fr))` : '',
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
gridTemplateColumns: (field as IGroupField).Direction === GroupDirection.Horizontal ? `repeat(auto-fill,minmax(calc(${100 / ((field as any as IGroupField).Fields.length + (!AtCapacity ? 1 : 0))}% - 10px),1fr))` : '',
|
||||
gap: 10
|
||||
}}>
|
||||
{f.Fields.map((child, index) => {
|
||||
|
@ -55,9 +56,9 @@ export const FormFieldCustomizer: React.FunctionComponent<IFormFieldCustomizer>
|
|||
key={child.Id}
|
||||
allFieldsFlat={props.allFieldsFlat}
|
||||
field={child}
|
||||
delete={() => props.update({ Fields: (field as IGroupField).Fields.filter(x => x.Id != child.Id) })}
|
||||
delete={() => props.update({ Fields: (field as IGroupField).Fields.filter(x => x.Id !== child.Id) })}
|
||||
update={(val) => {
|
||||
let children = cloneDeep(f.Fields)
|
||||
const children = cloneDeep(f.Fields)
|
||||
children[index] = { ...children[index], ...val };
|
||||
props.update({ Fields: children });
|
||||
}}
|
||||
|
@ -68,14 +69,14 @@ export const FormFieldCustomizer: React.FunctionComponent<IFormFieldCustomizer>
|
|||
</div>;
|
||||
}
|
||||
|
||||
if (FieldType.Conditional == field.Type) {
|
||||
if (FieldType.Conditional === field.Type) {
|
||||
const f = (field as IConditionalField);
|
||||
const lookupField = props.allFieldsFlat.filter(x => x.Id == f.LookupFieldId)[0];
|
||||
const lookupField = props.allFieldsFlat.filter(x => x.Id === f.LookupFieldId)[0];
|
||||
return <>
|
||||
{shouldEdit && editDialog}
|
||||
<div style={{ border: `1px solid ${getTheme().palette.themeDarkAlt}` }}>
|
||||
<div style={{ display: "flex", background: getTheme().palette.themeLighter, alignItems: 'center', paddingLeft: "1em" }}>
|
||||
<Label>Visible if '{lookupField?.DisplayName}' is equal to '{f.MatchValue?.toString()}'</Label>
|
||||
<Label>Visible if '{lookupField?.DisplayName}' is equal to '{f.MatchValue?.toString()}'</Label>
|
||||
<ActionButton iconProps={{ iconName: "Edit" }} onClick={() => setShouldEdit(true)} />
|
||||
<ActionButton iconProps={{ iconName: "Delete" }} onClick={() => props.delete()} />
|
||||
</div>
|
||||
|
@ -111,15 +112,15 @@ export const FormFieldCustomizer: React.FunctionComponent<IFormFieldCustomizer>
|
|||
{shouldEdit && editDialog}
|
||||
<div className={styles.EditField} onClick={() => setShouldEdit(true)}>
|
||||
|
||||
{FieldType.Label == field.Type && <Label styles={{ root: { cursor: "pointer" } }} disabled>{field.DisplayName}</Label>}
|
||||
{FieldType.Header == field.Type && <Text variant='xLarge'>{field.DisplayName}</Text>}
|
||||
{FieldType.Text == field.Type && <TextField {...genericProps} />}
|
||||
{FieldType.MultilineText == field.Type && <TextField {...genericProps} rows={5} multiline />}
|
||||
{FieldType.Number == field.Type && <SpinButton {...genericProps} inputMode='numeric' labelPosition={Position.top} />}
|
||||
{FieldType.Boolean == field.Type && <Checkbox {...genericProps} styles={{ root: { marginTop: "2.5em" } }} />}
|
||||
{FieldType.Choice == field.Type && <Dropdown {...genericProps} options={[]} />}
|
||||
{FieldType.MultiChoice == field.Type && <Dropdown {...genericProps} options={[]} multiSelect />}
|
||||
{FieldType.PlaceHolder == field.Type && <MessageBar messageBarType={MessageBarType.info} styles={{ root: { marginTop: "2.1em" } }}>Press here to setup the field!</MessageBar>}
|
||||
{FieldType.Label === field.Type && <Label styles={{ root: { cursor: "pointer" } }} disabled>{field.DisplayName}</Label>}
|
||||
{FieldType.Header === field.Type && <Text variant='xLarge'>{field.DisplayName}</Text>}
|
||||
{FieldType.Text === field.Type && <TextField {...genericProps} />}
|
||||
{FieldType.MultilineText === field.Type && <TextField {...genericProps} rows={5} multiline />}
|
||||
{FieldType.Number === field.Type && <SpinButton {...genericProps} inputMode='numeric' labelPosition={Position.top} />}
|
||||
{FieldType.Boolean === field.Type && <Checkbox {...genericProps} styles={{ root: { marginTop: "2.5em" } }} />}
|
||||
{FieldType.Choice === field.Type && <Dropdown {...genericProps} options={[]} />}
|
||||
{FieldType.MultiChoice === field.Type && <Dropdown {...genericProps} options={[]} multiSelect />}
|
||||
{FieldType.PlaceHolder === field.Type && <MessageBar messageBarType={MessageBarType.info} styles={{ root: { marginTop: "2.1em" } }}>Press here to setup the field!</MessageBar>}
|
||||
|
||||
</div>
|
||||
</span>
|
||||
|
|
|
@ -21,42 +21,44 @@ export interface IJsonFormProps {
|
|||
export const JsonForm: React.FunctionComponent<IJsonFormProps> = (props: React.PropsWithChildren<IJsonFormProps>) => {
|
||||
const { Mode, ServerRelativeUrl } = props;
|
||||
const { value: Form, updateValue: UpdateForm, overwriteData: __SETFORM } = useObject<IForm>(props.ServerRelativeUrl ? { Fields: [], Title: "" } : props.Form);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const { value: filledForm, updateValue, overwriteData: __SETFILLEDFORM } = useObject<any>();
|
||||
const { provider } = React.useContext(SPFxContext);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (ServerRelativeUrl != null) {
|
||||
const fetch = async () => {
|
||||
if (ServerRelativeUrl !== null) {
|
||||
const fetch = async (): Promise<void> => {
|
||||
const result = await provider.GetSubmission(ServerRelativeUrl);
|
||||
__SETFILLEDFORM(result.response);
|
||||
__SETFORM(result.form);
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
fetch();
|
||||
}
|
||||
}, [])
|
||||
|
||||
const saveForm = async () => {
|
||||
const saveForm = async (): Promise<void> => {
|
||||
const serverRelativeUrl = await provider.SaveSubmission({ form: Form, response: filledForm });
|
||||
var searchParams = new URLSearchParams(window.location.search);
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
searchParams.set(FILLED_FORM_QUERY_KEY, serverRelativeUrl);
|
||||
window.location.search = searchParams.toString();
|
||||
}
|
||||
|
||||
if (props.ListId == null || props.ListId == "")
|
||||
if (props.ListId === null || props.ListId === "")
|
||||
return <Placeholder description={'Open the property pane and select a list to store responses'} iconName={'Edit'} iconText={'Please configure web part'} />
|
||||
|
||||
return (
|
||||
<>
|
||||
<WebPartTitle displayMode={Mode} title={Form.Title} updateProperty={(val) => UpdateForm({ Title: val })} />
|
||||
|
||||
{Mode == DisplayMode.Read &&
|
||||
{Mode === DisplayMode.Read &&
|
||||
<>
|
||||
<Stack tokens={{ childrenGap: 5 }}>
|
||||
{Form.Fields.map(field => {
|
||||
return <Field readonly={props.ServerRelativeUrl != null} field={field} onChange={updateValue} form={filledForm} />
|
||||
{Form.Fields.map((field, index) => {
|
||||
return <Field readonly={props.ServerRelativeUrl !== null} field={field} onChange={updateValue} form={filledForm} key={index} />
|
||||
})}
|
||||
</Stack>
|
||||
{props.ServerRelativeUrl == null &&
|
||||
{props.ServerRelativeUrl === null &&
|
||||
<DialogFooter>
|
||||
<PrimaryButton text='Submit' iconProps={{ iconName: "Accept" }} onClick={() => saveForm()} />
|
||||
</DialogFooter>
|
||||
|
@ -64,19 +66,20 @@ export const JsonForm: React.FunctionComponent<IJsonFormProps> = (props: React.P
|
|||
</>
|
||||
}
|
||||
|
||||
{Mode == DisplayMode.Edit &&
|
||||
{Mode === DisplayMode.Edit &&
|
||||
<>
|
||||
<Stack tokens={{ childrenGap: 5 }}>
|
||||
{Form.Fields.map((Field, index) => {
|
||||
return <FormFieldCustomizer
|
||||
key={index}
|
||||
allFieldsFlat={GetLookupFields(Form.Fields)}
|
||||
field={Field}
|
||||
delete={() => {
|
||||
let fields = cloneDeep(Form.Fields).filter((x, i) => index != i);
|
||||
const fields = cloneDeep(Form.Fields).filter((x, i) => index !== i);
|
||||
UpdateForm({ Fields: fields });
|
||||
}}
|
||||
update={(val) => {
|
||||
let fields = cloneDeep(Form.Fields)
|
||||
const fields = cloneDeep(Form.Fields)
|
||||
fields[index] = { ...fields[index], ...val };
|
||||
UpdateForm({ Fields: fields });
|
||||
}}
|
||||
|
|
Loading…
Reference in New Issue