Skip to content

Commit

Permalink
fix #62: support for file data for input type file (#63)
Browse files Browse the repository at this point in the history
* fix #43: stop navigation to disabled items with buttons

* fix #44: update change events

* fix #48: making spacing configurable

* enhancement #53: add support for icons

* fix #62: support for filedata for input file type
  • Loading branch information
manojadams committed Mar 26, 2024
1 parent 6f00f8d commit 9ad84ae
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 79 deletions.
30 changes: 15 additions & 15 deletions src/MetaformRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,23 +108,23 @@ export default class MetaFormRenderer extends React.Component<IFormRenderer> {
}

componentDidMount() {
this.metaform.listener(EVENTS.SUBMIT, (...params) => {
this.metaform.listener(EVENTS.SUBMIT, async (...params) => {
if (this.props.onSubmit) {
this.props.onSubmit(
FormUtils.updateFormData(this.metaform.form, {}, this.props.formatter ?? {}),
await FormUtils.updateFormData(this.metaform.form, {}, this.props.formatter ?? {}),
params
);
}
});
this.metaform.listener(EVENTS._FIELD_CHANGE, (params: IEventPayload) => {
this.metaform.listener(EVENTS._FIELD_CHANGE, async (params: IEventPayload) => {
let formData;
if (this.props.onChange) {
if (this.props.changeResponseMode === CHANGE_MODE.FORM_DATA) {
formData = FormUtils.updateFormData(this.metaform.form, {}, this.props.formatter ?? {});
formData = await FormUtils.updateFormData(this.metaform.form, {}, this.props.formatter ?? {});
} else if (this.props.changeResponseMode === CHANGE_MODE.SECTION_DATA) {
const page = this.metaform.getPage();
const sectionData = this.metaform.getSection(page.pageNumber);
formData = FormUtils.updateSectionFormData(sectionData, {}, this.props.formatter ?? {});
formData = await FormUtils.updateSectionFormData(sectionData, {}, this.props.formatter ?? {});
}
this.props.onChange(params.payload as IFieldChange, formData);
}
Expand Down Expand Up @@ -196,27 +196,27 @@ export default class MetaFormRenderer extends React.Component<IFormRenderer> {
return true;
}

handleCustom(e: React.MouseEvent) {
async handleCustom(e: React.MouseEvent) {
if (this.props.onCustom) {
this.props.onCustom(FormUtils.updateFormData(this.metaform.form, {}, this.props.formatter || {}), e);
this.props.onCustom(await FormUtils.updateFormData(this.metaform.form, {}, this.props.formatter || {}), e);
}
}

handlePrevious() {
async handlePrevious() {
this.lastAction = FORM_ACTION.PREVIOUS;
if (this.props.onPrevious) {
const page = this.metaform.getPage();
const nextResponseMode = this.props.nextResponseMode || NEXT_RESPONSE_MODE.FORM_DATA;
if (nextResponseMode === NEXT_RESPONSE_MODE.PAGE_DATA) {
const sectionData = this.metaform.getSection(page.pageNumber);
this.props.onPrevious(
FormUtils.updateSectionFormData(sectionData, {}, this.props.formatter || {}),
await FormUtils.updateSectionFormData(sectionData, {}, this.props.formatter || {}),
page.pageNumber
);
} else {
const formData = this.metaform.form;
this.props.onPrevious(
FormUtils.updateFormData(formData, {}, this.props.formatter || {}),
await FormUtils.updateFormData(formData, {}, this.props.formatter || {}),
page.pageNumber
);
}
Expand All @@ -231,34 +231,34 @@ export default class MetaFormRenderer extends React.Component<IFormRenderer> {
if (nextResponseMode === NEXT_RESPONSE_MODE.PAGE_DATA) {
const sectionData = this.metaform.getSection(page.pageNumber);
return await this.props.onNext(
FormUtils.updateSectionFormData(sectionData, {}, this.props.formatter || {}),
await FormUtils.updateSectionFormData(sectionData, {}, this.props.formatter || {}),
page.pageNumber
);
} else {
const formData = this.metaform.form;
return await this.props.onNext(
FormUtils.updateFormData(formData, {}, this.props.formatter || {}),
await FormUtils.updateFormData(formData, {}, this.props.formatter || {}),
page.pageNumber
);
}
}
return true;
}

handleSubmit(...params: Array<unknown>) {
async handleSubmit(...params: Array<unknown>) {
this.lastAction = FORM_ACTION.SUBMIT;
if (this.props.onSubmit) {
const nextResponseMode = this.props.nextResponseMode || NEXT_RESPONSE_MODE.FORM_DATA;
if (nextResponseMode === NEXT_RESPONSE_MODE.PAGE_DATA) {
const page = this.metaform.getPage();
const sectionData = this.metaform.getSection(page.pageNumber);
this.props.onSubmit(
FormUtils.updateSectionFormData(sectionData, {}, this.props.formatter || {}),
await FormUtils.updateSectionFormData(sectionData, {}, this.props.formatter || {}),
params
);
} else {
this.props.onSubmit(
FormUtils.updateFormData(this.metaform.form, {}, this.props.formatter || {}),
await FormUtils.updateFormData(this.metaform.form, {}, this.props.formatter || {}),
params
);
}
Expand Down
3 changes: 3 additions & 0 deletions src/constants/common-interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,11 @@ export interface IFieldConfig extends IApiConfig {

// autocomplete
autocomplete?: any;

Check warning on line 88 in src/constants/common-interface.ts

View workflow job for this annotation

GitHub Actions / build (16.x)

Unexpected any. Specify a different type

// file input
accept?: string;
blob?: boolean;

Check failure on line 93 in src/constants/common-interface.ts

View workflow job for this annotation

GitHub Actions / build (16.x)

Delete `····`
// template
template?: string;
}
Expand Down
153 changes: 89 additions & 64 deletions src/utils/FormUtil.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { IForm, IFormData, IFormField, IFormSection, IRequestBody } from "../constants/common-interface";
import { DEFAULT, DEFAULT_DATE_FORMAT, FORM_ACTION, _INTERNAL_VALUES } from "../constants/constants";
import { DEFAULT_DATE_FORMAT, FORM_ACTION, _INTERNAL_VALUES } from "../constants/constants";
import { CONTROLS } from "../constants/controls";
import { TiconPositionType, TValue } from "../constants/types";
import { Page } from "../core/Page";
import {
Expand Down Expand Up @@ -146,95 +147,119 @@ export default class FormUtils {
return newFormData;
}

static updateFormData(formData: IForm, newFormData: IFormData, formatter: IFormatterType) {
Object.keys(formData).forEach((key) => {
Object.keys(formData[key]).forEach((key2) => {
const prop = formData[key][key2].prop;
static getBase64(file: File) {
return new Promise((resolve, reject) => {
const fileReader = new FileReader();
fileReader.readAsDataURL(file);
fileReader.onload = () => resolve(fileReader.result);
fileReader.onerror = reject;
});
}

static async getFormFieldValue(formField: IFormField) {
switch (formField.displayType) {
case CONTROLS.FILE:
if (formField.files && formField.files.length > 0) {
return ({

Check failure on line 163 in src/utils/FormUtil.tsx

View workflow job for this annotation

GitHub Actions / build (16.x)

Delete `(`
name: formField.value,
[formField.config?.blob ? "file": "fileData"]: formField.config?.blob

Check failure on line 165 in src/utils/FormUtil.tsx

View workflow job for this annotation

GitHub Actions / build (16.x)

Replace `:·"fileData"]:·formField.config?.blob·` with `·:·"fileData"]:·formField.config?.blob`
? formField.files[0]
: await this.getBase64(formField.files[0])
});
}
return null;
default:
return formField.value;
}
}

static updateFormData(formData: IForm, newFormData: IFormData, formatter: IFormatterType): Promise<IFormData> {
return new Promise((resolve) => {
const allPendingUpdates = Object.keys(formData).flatMap((key) => {
return Object.keys(formData[key]).map((key2) => {
const prop = formData[key][key2].prop;
return this.getFormFieldValue(formData[key][key2]).then(fieldValue => {
if (prop) {
if (!newFormData[prop]) {
newFormData[prop] = {};
}
if (formatter[key2]) {
newFormData[prop][key2] = formatter[key2](fieldValue as TValue);
} else {
newFormData[prop][key2] = fieldValue;
}
} else {
// null prop is ignored
if (prop !== null) {
if (formatter[key2]) {
newFormData[key2] = formatter[key2](fieldValue as TValue);
} else {
newFormData[key2] = fieldValue as string;
}
}
}
})
});
});
Promise.all(allPendingUpdates)
.then(() => this.updateNestedFormData(newFormData))
.then(() => resolve(newFormData));
});
}

static async updateSectionFormData(formData: IFormSection | null, newFormData: IFormData, formatter: IFormatterType): Promise<IFormData> {
return new Promise((resolve) => {
if (!formData) {
resolve({});
return {};
}
const allPendingUpdates = Object.keys(formData).map(async (key) => {
const prop = formData[key].prop;
const formDataKey = formData[key];
const fieldValue = await this.getFormFieldValue(formDataKey) as string;
if (prop) {
if (!newFormData[prop]) {
newFormData[prop] = {};
if (!newFormData[prop as string]) {
newFormData[prop as string] = {};
}
if (formatter[key2]) {
newFormData[prop][key2] = formatter[key2](formData[key][key2].value);
if (formatter[key]) {
newFormData[prop as string][key] = formatter[key](fieldValue);
} else {
newFormData[prop][key2] = formData[key][key2].value;
newFormData[prop as string][key] = fieldValue;
}
} else {
// null prop is ignored
if (prop !== null) {
if (formatter[key2]) {
newFormData[key2] = formatter[key2](formData[key][key2].value);
if (formatter[key]) {
newFormData[key] = formatter[key](fieldValue as string);
} else {
newFormData[key2] = formData[key][key2].value as string;
newFormData[key] = fieldValue;
}
}
}
});
});
this.updateNestedFormData(newFormData);
return newFormData;
}

static updateSectionFormData(formData: IFormSection | null, newFormData: IFormData, formatter: IFormatterType) {
if (!formData) return {};
Object.keys(formData).forEach((key) => {
const prop = formData[key].prop;
const formDataKey = formData[key];
if (prop) {
if (!newFormData[prop as string]) {
newFormData[prop as string] = {};
}
if (formatter[key]) {
newFormData[prop as string][key] = formatter[key](formDataKey.value as string);
} else {
newFormData[prop as string][key] = formDataKey.value;
}
} else {
// null prop is ignored
if (prop !== null) {
if (formatter[key]) {
newFormData[key] = formatter[key](formDataKey.value as string);
} else {
const displayType = formDataKey.displayType ?? DEFAULT;
switch (displayType) {
case "file":
{
const files = formDataKey.files as Array<File>;
if (files && files.length > 0) {
newFormData[key] = files;
} else {
newFormData[key] = formDataKey.value as string;
}
}
break;
default:
newFormData[key] = formDataKey.value as string;
}
}
}
}
});
this.updateNestedFormData(newFormData);
return newFormData;
return Promise.all(allPendingUpdates)
.then(() => this.updateNestedFormData(newFormData))
.then(() => resolve(newFormData));
})
}

static updateNestedFormData(formData: IFormData) {
Object.keys(formData).forEach((key: string) => {
static async updateNestedFormData(formData: IFormData) {
Object.keys(formData).forEach(async (key: string) => {
if (typeof formData[key] === "object") {
const props = key.split("#");
if (props.length > 1) {
// is nested prop
if (!formData[props[0]]) {
formData[props[0]] = {};
}
this.updateNestedProp(formData[props[0]] as IFormData, props.slice(1), formData[key] as TValue);
await this.updateNestedProp(formData[props[0]] as IFormData, props.slice(1), formData[key] as TValue);
delete formData[key];
}
}
});
}

static updateNestedProp(formData: IFormData, props: Array<string>, value: TValue) {
static async updateNestedProp(formData: IFormData, props: Array<string>, value: TValue) {
if (props.length > 0) {
if (props.length === 1) {
if (formData) {
Expand All @@ -244,7 +269,7 @@ export default class FormUtils {
if (!formData[props[0]]) {
formData[props[0]] = {};
}
this.updateNestedProp(formData[props[0]] as IFormData, props.slice(1), value);
await this.updateNestedProp(formData[props[0]] as IFormData, props.slice(1), value);
}
}
}
Expand Down

0 comments on commit 9ad84ae

Please sign in to comment.