Skip to content

Commit

Permalink
fix(react-formio): migrate FormBuilder to react hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
Romakita committed Jan 6, 2025
1 parent 8de6060 commit 41b3e6b
Show file tree
Hide file tree
Showing 12 changed files with 2,379 additions and 3,869 deletions.
2,042 changes: 2,042 additions & 0 deletions packages/react-formio/src/components/__fixtures__/form-wizard.fixture.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,177 +1,25 @@
import AllComponents from "formiojs/components";
import Components from "formiojs/components/Components";
import FormioFormBuilder from "formiojs/FormBuilder";
import cloneDeep from "lodash/cloneDeep";
import noop from "lodash/noop";
import { Component } from "react";

import { ComponentType } from "../../interfaces";
import { callLast } from "../../utils/callLast";

Components.setComponents(AllComponents);

const EVENTS = [
"addComponent",
"updateComponent",
"removeComponent",
"saveComponent",
"cancelComponent",
"moveComponent",
"editComponent",
"editJson",
"copyComponent",
"pasteComponent"
];

const EVENTS_CHANGE = ["addComponent", "saveComponent", "updateComponent", "removeComponent"];

async function createBuilder(el: Element, { components = [], display, options, onChange, events = {} }: any): Promise<void> {
const form = {
display,
components: [...components]
};

try {
const builder = await new FormioFormBuilder(el, form, { ...options }).ready;

const handleEvent = (event: string) => {
return (...args: any[]) => {
events[event] && events[event](...args);

if (EVENTS_CHANGE.includes(event)) {
onChange(builder.form.components);
}
};
};

EVENTS.forEach((event) => builder.on(event, callLast(handleEvent(event), 200)));

return builder;
} catch (er) {
console.error(er);
}
}

export interface FormBuilderProps {
components: ComponentType[];
display?: string;
options?: any;
builder?: any;
onChange?: (components: ComponentType[]) => void;
onAddComponent?: Function;
onUpdateComponent?: Function;
onRemoveComponent?: Function;
onSaveComponent?: Function;
onCancelComponent?: Function;
onMoveComponent?: Function;
onEditComponent?: Function;
onEditJson?: Function;
onCopyComponent?: Function;
onPasteComponent?: Function;
}

export class FormBuilder extends Component<FormBuilderProps, any> {
static defaultProps = {
options: {},
onChange: noop,
onReady: noop,
onDestroy: noop
};

private elRef: any;
private builderRef: any;

constructor(props: FormBuilderProps) {
super(props);

this.state = {
display: props.display,
components: cloneDeep(props.components)
};
this.elRef = null;
this.builderRef = null;
}

async componentDidMount(): Promise<void> {
await this.create(this.props);
}

async create(props: FormBuilderProps) {
const {
options,
display,
components,
onAddComponent,
onUpdateComponent,
onRemoveComponent,
onSaveComponent,
onCancelComponent,
onMoveComponent,
onEditComponent,
onEditJson,
onCopyComponent,
onPasteComponent
} = props;

this.builderRef = await createBuilder(this.elRef.firstChild, {
display,
options: { ...options },
components: cloneDeep(components),
onChange: this.whenComponentsChange.bind(this),
events: {
onAddComponent,
onUpdateComponent,
onRemoveComponent,
onSaveComponent,
onCancelComponent,
onMoveComponent,
onEditComponent,
onEditJson,
onCopyComponent,
onPasteComponent
}
});
}

componentWillUnmount(): void {
this.builderRef?.destroy();
}

// eslint-disable-next-line react/no-deprecated
async componentWillReceiveProps(nextProps: FormBuilderProps) {
if (this.builderRef) {
if (nextProps.display !== this.state.display) {
await this.create({
...this.props,
display: nextProps.display
});
} else if (nextProps.components !== this.state.components) {
this.builderRef.form = {
display: this.state.display,
components: nextProps.components
};
}
}
}

whenComponentsChange(components: ComponentType[]) {
this.setState({ components }, () => {
this.props?.onChange && this.props.onChange(components);
});
}

/* eslint-disable jsx-a11y/no-static-element-interactions */
/* eslint-disable jsx-a11y/click-events-have-key-events */
render() {
return (
<div
ref={(ref) => {
this.elRef = ref;
}}
onClick={(e) => e.stopPropagation()}
>
import type { CSSProperties } from "react";

import { useFormBuilder, UseFormBuilderProps } from "./useFormBuilder.hook";

export function FormBuilder({
className = "",
style = {},
["data-testid"]: dataTestId = "formio-builder-container",
...props
}: UseFormBuilderProps & {
className?: string;
style?: CSSProperties;
["data-testid"]?: string;
}) {
const renderElement = useFormBuilder(props);

return (
<div style={style} className={className} data-testid={dataTestId}>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */}
<div ref={renderElement} onClick={(e) => e.stopPropagation()}>
<div />
</div>
);
}
</div>
);
}
Loading

0 comments on commit 41b3e6b

Please sign in to comment.