Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a Tools collapsible panel #162

Merged
merged 6 commits into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion public/pages/workflow_detail/components/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export function WorkflowDetailHeader(props: WorkflowDetailHeaderProps) {

return (
<EuiPageHeader
style={{ marginTop: '-8px' }}
pageTitle={
<EuiFlexGroup direction="row" alignItems="flexEnd" gutterSize="m">
<EuiFlexItem grow={false}>{getTitle()}</EuiFlexItem>
Expand All @@ -55,7 +56,7 @@ export function WorkflowDetailHeader(props: WorkflowDetailHeaderProps) {
Export
</EuiButton>,
]}
bottomBorder={true}
bottomBorder={false}
/>
);
}
240 changes: 125 additions & 115 deletions public/pages/workflow_detail/resizable_workspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,20 @@
import React, { useRef, useState, useEffect } from 'react';
import { useSelector } from 'react-redux';
import { useHistory } from 'react-router-dom';
import { useReactFlow } from 'reactflow';
import { Form, Formik, FormikProps } from 'formik';
import * as yup from 'yup';
import { EuiFlexGroup, EuiFlexItem, EuiResizableContainer } from '@elastic/eui';
import {
EuiFlexGroup,
EuiFlexItem,
EuiPanel,
EuiResizableContainer,
EuiTitle,
} from '@elastic/eui';
import { getCore } from '../../services';

import {
Workflow,
ReactFlowComponent,
WORKFLOW_STATE,
ReactFlowEdge,
WorkflowFormValues,
WorkflowSchema,
WorkflowConfig,
Expand All @@ -35,20 +38,22 @@ import {
updateWorkflow,
useAppDispatch,
} from '../../store';
import { WorkflowInputs } from './workflow_inputs';
import { configToTemplateFlows } from './utils';
import { Workspace } from './workspace';

// styling
import './workspace/workspace-styles.scss';
import '../../global-styles.scss';
import { WorkflowInputs } from './workflow_inputs';
import { configToTemplateFlows } from './utils';
import { Tools } from './tools';

interface ResizableWorkspaceProps {
isNewWorkflow: boolean;
workflow?: Workflow;
}

const WORKFLOW_INPUTS_PANEL_ID = 'workflow_inputs_panel_id';
const TOOLS_PANEL_ID = 'tools_panel_id';

/**
* The overall workspace component that maintains state related to the 2 resizable
Expand All @@ -75,21 +80,29 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) {
// Validation states
const [formValidOnSubmit, setFormValidOnSubmit] = useState<boolean>(true);

// Component details side panel state
const [isDetailsPanelOpen, setisDetailsPanelOpen] = useState<boolean>(true);
const collapseFn = useRef(
// Workflow inputs side panel state
const [isWorkflowInputsPanelOpen, setIsWorkflowInputsPanelOpen] = useState<
boolean
>(true);
const collapseFnHorizontal = useRef(
(id: string, options: { direction: 'left' | 'right' }) => {}
);
const onToggleChange = () => {
collapseFn.current(WORKFLOW_INPUTS_PANEL_ID, { direction: 'left' });
setisDetailsPanelOpen(!isDetailsPanelOpen);
const onToggleWorkflowInputsChange = () => {
collapseFnHorizontal.current(WORKFLOW_INPUTS_PANEL_ID, {
direction: 'left',
});
setIsWorkflowInputsPanelOpen(!isWorkflowInputsPanelOpen);
};

// Selected component state
const reactFlowInstance = useReactFlow();
const [selectedComponent, setSelectedComponent] = useState<
ReactFlowComponent
>();
// Tools side panel state
const [isToolsPanelOpen, setIsToolsPanelOpen] = useState<boolean>(true);
const collapseFnVertical = useRef(
(id: string, options: { direction: 'top' | 'bottom' }) => {}
);
const onToggleToolsChange = () => {
collapseFnVertical.current(TOOLS_PANEL_ID, { direction: 'bottom' });
setIsToolsPanelOpen(!isToolsPanelOpen);
};

// Save/provision/deprovision button state
const isSaveable =
Expand All @@ -116,31 +129,6 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) {
const isLoadingGlobal =
loading || isProvisioning || isDeprovisioning || isSaving || isCreating;

/**
* Custom listener on when nodes are selected / de-selected. Passed to
* downstream ReactFlow components you can listen using
* the out-of-the-box useOnSelectionChange hook.
* - populate panel content appropriately
* - open the panel if a node is selected and the panel is closed
* - it is assumed that only one node can be selected at once
*/
function onSelectionChange({
nodes,
edges,
}: {
nodes: ReactFlowComponent[];
edges: ReactFlowEdge[];
}) {
if (nodes && nodes.length > 0) {
setSelectedComponent(nodes[0]);
if (!isDetailsPanelOpen) {
onToggleChange();
}
} else {
setSelectedComponent(undefined);
}
}

// Hook to update some default values for the workflow, if applicable.
// We need to handle different scenarios:
// 1. Rendering backend-only-created workflow / an already-created workflow with no ui_metadata.
Expand Down Expand Up @@ -171,19 +159,6 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) {
}
}, [props.workflow]);

// Hook to updated the selected ReactFlow component
useEffect(() => {
reactFlowInstance?.setNodes((nodes: ReactFlowComponent[]) =>
nodes.map((node) => {
node.data = {
...node.data,
selected: node.id === selectedComponent?.id ? true : false,
};
return node;
})
);
}, [selectedComponent]);

// Initialize the form state to an existing workflow, if applicable.
useEffect(() => {
if (workflow?.ui_metadata?.config) {
Expand All @@ -194,10 +169,6 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) {
}
}, [workflow]);

// TODO: leave as a placeholder for now. Current functionality is the workflow
// is readonly and only reacts/changes when the underlying form is updated.
function onNodesChange(nodes: ReactFlowComponent[]): void {}

/**
* Function to pass down to the Formik <Form> components as a listener to propagate
* form changes to this parent component to re-enable save button, etc.
Expand Down Expand Up @@ -275,41 +246,20 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) {
>
{(formikProps) => (
<Form>
{/*
TODO: finalize where/how to show invalidations
*/}
{/* {!formValidOnSubmit && (
<EuiCallOut
title="There are empty or invalid fields"
color="danger"
iconType="alert"
style={{ marginBottom: '16px' }}
>
Please address the highlighted fields and try saving again.
</EuiCallOut>
)}
{isDeprovisionable && isDirty && (
<EuiCallOut
title="The configured flow has been provisioned"
color="warning"
iconType="alert"
style={{ marginBottom: '16px' }}
>
Changes cannot be saved until the workflow has first been
deprovisioned.
</EuiCallOut>
)} */}
<EuiResizableContainer
direction="horizontal"
className="stretch-absolute"
style={{
marginLeft: '-8px',
marginLeft: isWorkflowInputsPanelOpen ? '-8px' : '0px',
marginTop: '-8px',
}}
>
{(EuiResizablePanel, EuiResizableButton, { togglePanel }) => {
if (togglePanel) {
collapseFn.current = (panelId: string, { direction }) =>
togglePanel(panelId, { direction });
collapseFnHorizontal.current = (
panelId: string,
{ direction }
) => togglePanel(panelId, { direction });
}

return (
Expand All @@ -320,46 +270,106 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) {
initialSize={50}
minSize="25%"
paddingSize="s"
onToggleCollapsedInternal={() => onToggleChange()}
onToggleCollapsedInternal={() =>
onToggleWorkflowInputsChange()
}
>
<EuiFlexGroup
direction="column"
gutterSize="s"
className="workspace-panel"
>
<EuiFlexItem>
<WorkflowInputs
workflow={props.workflow}
formikProps={formikProps}
onFormChange={onFormChange}
validateAndSubmit={validateAndSubmit}
/>
</EuiFlexItem>
</EuiFlexGroup>
<WorkflowInputs
workflow={props.workflow}
formikProps={formikProps}
onFormChange={onFormChange}
validateAndSubmit={validateAndSubmit}
/>
</EuiResizablePanel>
<EuiResizableButton />
<EuiResizablePanel
style={{ marginRight: '-16px' }}
style={{
marginRight: '-32px',
marginBottom: isToolsPanelOpen ? '0px' : '24px',
}}
mode="main"
initialSize={60}
minSize="25%"
paddingSize="s"
>
<EuiFlexGroup
direction="column"
gutterSize="s"
<EuiResizableContainer
className="workspace-panel"
direction="vertical"
style={{
marginLeft: '-8px',
marginTop: '-8px',
padding: 'none',
}}
>
<EuiFlexItem>
<Workspace
id="ingest"
workflow={workflow}
readonly={false}
onNodesChange={onNodesChange}
onSelectionChange={onSelectionChange}
/>
</EuiFlexItem>
</EuiFlexGroup>
{(
EuiResizablePanel,
EuiResizableButton,
{ togglePanel }
) => {
if (togglePanel) {
collapseFnVertical.current = (
panelId: string,
{ direction }
) =>
// ignore is added since docs are incorrectly missing "top" and "bottom"
// as valid direction options for vertically-configured resizable panels.
// @ts-ignore
togglePanel(panelId, { direction });
}

return (
<>
<EuiResizablePanel
mode="main"
initialSize={60}
minSize="25%"
paddingSize="s"
style={{ marginBottom: '-8px' }}
>
<EuiFlexGroup
direction="column"
gutterSize="s"
style={{ height: '100%' }}
>
<EuiFlexItem>
<Workspace
id="ingest"
workflow={workflow}
readonly={false}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiResizablePanel>
<EuiResizableButton />
<EuiResizablePanel
id={TOOLS_PANEL_ID}
mode="collapsible"
initialSize={50}
minSize="25%"
paddingSize="s"
onToggleCollapsedInternal={() =>
onToggleToolsChange()
}
style={{ marginBottom: '-24px' }}
>
<EuiFlexGroup
direction="column"
gutterSize="s"
style={{
height: '100%',
}}
>
<EuiFlexItem>
<EuiPanel paddingSize="m">
<Tools workflow={workflow} />
</EuiPanel>
</EuiFlexItem>
</EuiFlexGroup>
</EuiResizablePanel>
</>
);
}}
</EuiResizableContainer>
</EuiResizablePanel>
</>
);
Expand Down
38 changes: 0 additions & 38 deletions public/pages/workflow_detail/resources/resources.tsx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
* SPDX-License-Identifier: Apache-2.0
*/

export { Resources } from './resources';
export * from './tools';
Loading
Loading