Skip to content

Commit

Permalink
Change to use formiks useStore() for listening to node changes
Browse files Browse the repository at this point in the history
Signed-off-by: Tyler Ohlsen <[email protected]>
  • Loading branch information
ohltyler committed Oct 18, 2023
1 parent 30e062a commit 0d7af19
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 33 deletions.
1 change: 0 additions & 1 deletion public/component_types/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ export type WorkspaceFormValues = {
export type WorkspaceSchemaObj = {
[componentId: string]: ObjectSchema<any, any, any>;
};

export type WorkspaceSchema = ObjectSchema<WorkspaceSchemaObj>;

/**
Expand Down
56 changes: 32 additions & 24 deletions public/pages/workflow_detail/workspace/resizable_workspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
import React, { useRef, useState, useEffect } from 'react';
import { ReactFlowProvider } from 'reactflow';
import { Form, Formik } from 'formik';
import { ObjectSchema } from 'yup';
import * as yup from 'yup';
import { EuiButton, EuiResizableContainer } from '@elastic/eui';
import {
Workflow,
WorkspaceFormValues,
WorkspaceSchemaObj,
WorkspaceSchema,
ReactFlowComponent,
WorkspaceSchemaObj,
componentDataToFormik,
getComponentSchema,
} from '../../../../common';
Expand Down Expand Up @@ -60,23 +60,33 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) {
}
}, [props.workflow]);

// If a component is added or deleted in the workspace, we update the values.
// However, we cannot dynamically update the schema due to this known
// formik bug: https://github.com/jaredpalmer/formik/issues/3335
// So, we trigger this during validation when there is a mismatch
// in the count of component ids
function onComponentChange(values: WorkspaceFormValues): void {
const updatedComponentIds = Object.keys(values);
const newSchemaObj = {} as WorkspaceSchemaObj;
// Update the form values and validation schema when a node is added
// or removed from the workspace
function onNodesChange(nodes: ReactFlowComponent[]): void {
const updatedComponentIds = nodes.map((node) => node.id);
const existingComponentIds = Object.keys(formValues);
const updatedSchemaObj = {} as WorkspaceSchemaObj;

if (updatedComponentIds.length > existingComponentIds.length) {
// TODO: implement for when a node is added
} else if (updatedComponentIds.length < existingComponentIds.length) {
existingComponentIds.forEach((existingId) => {
if (updatedComponentIds.includes(existingId)) {
updatedSchemaObj[existingId] = formSchema.fields[
`${existingId}`
] as yup.ObjectSchema<any, any, any>;
} else {
delete formValues[`${existingId}`];
}
});
} else {
// if it is somehow triggered without node changes, be sure
// to prevent updating the form or schema
return;
}

Object.keys(formSchema.fields).forEach((componentId) => {
if (updatedComponentIds.includes(componentId)) {
newSchemaObj[componentId] = formSchema.fields[
`${componentId}`
] as ObjectSchema<any, any, any>;
}
});
setFormSchema(yup.object(newSchemaObj));
const updatedSchema = yup.object(updatedSchemaObj) as WorkspaceSchema;
setFormSchema(updatedSchema);
}

return (
Expand All @@ -89,11 +99,6 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) {
}}
validate={(values) => {
console.log('values on validate: ', values);
const componentCountValues = Object.keys(values).length;
const componentCountSchema = Object.keys(formSchema.fields).length;
if (componentCountSchema !== componentCountValues) {
onComponentChange(values);
}
}}
>
{(formikProps) => (
Expand All @@ -111,7 +116,10 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) {
return (
<ReactFlowProvider>
<EuiResizablePanel mode="main" initialSize={75} minSize="50%">
<Workspace workflow={props.workflow} />
<Workspace
workflow={props.workflow}
onNodesChange={onNodesChange}
/>
</EuiResizablePanel>
<EuiResizableButton />
<EuiResizablePanel
Expand Down
19 changes: 17 additions & 2 deletions public/pages/workflow_detail/workspace/workspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,16 @@ import ReactFlow, {
useEdgesState,
addEdge,
BackgroundVariant,
useStore,
} from 'reactflow';
import { EuiFlexItem, EuiFlexGroup } from '@elastic/eui';
import { rfContext, setDirty } from '../../../store';
import { IComponent, Workflow } from '../../../../common';
import {
IComponent,
IComponentData,
ReactFlowComponent,
Workflow,
} from '../../../../common';
import { generateId, initComponentData } from '../../../utils';
import { getCore } from '../../../services';
import { WorkspaceComponent } from '../workspace_component';
Expand All @@ -29,6 +35,7 @@ import '../workspace_edge/deletable-edge-styles.scss';

interface WorkspaceProps {
workflow?: Workflow;
onNodesChange: (nodes: ReactFlowComponent[]) => void;
}

const nodeTypes = { customComponent: WorkspaceComponent };
Expand All @@ -39,9 +46,17 @@ export function Workspace(props: WorkspaceProps) {
const reactFlowWrapper = useRef(null);
const { reactFlowInstance, setReactFlowInstance } = useContext(rfContext);

const [nodes, setNodes, onNodesChange] = useNodesState([]);
const [nodes, setNodes, onNodesChange] = useNodesState<IComponentData>([]);
const [edges, setEdges, onEdgesChange] = useEdgesState([]);

// Listener for node additions or deletions to propagate to parent component
const nodesLength = useStore(
(state) => Array.from(state.nodeInternals.values()).length || 0
);
useEffect(() => {
props.onNodesChange(nodes);
}, [nodesLength]);

const onConnect = useCallback(
(params) => {
const edge = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ interface WorkspaceComponentProps {
export function WorkspaceComponent(props: WorkspaceComponentProps) {
const component = props.data;
const { deleteNode } = useContext(rfContext);
const { values, validateForm } = useFormikContext<WorkspaceFormValues>();

return (
<EuiCard
Expand All @@ -47,11 +46,6 @@ export function WorkspaceComponent(props: WorkspaceComponentProps) {
iconType="trash"
onClick={() => {
deleteNode(component.id);
delete values[`${component.id}`];
validateForm();
// TODO: use below way instead of hacky change via validation.
// Formik bug reference: https://github.com/jaredpalmer/formik/issues/3335
// delete validationSchema[`${component.id}`];
}}
aria-label="Delete"
/>
Expand Down

0 comments on commit 0d7af19

Please sign in to comment.