Skip to content
This repository has been archived by the owner on Nov 27, 2023. It is now read-only.

Contextual Mini Catalog #230

Merged
merged 11 commits into from
Feb 25, 2022
6 changes: 6 additions & 0 deletions src/api/StepsAndViewsProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ interface IStepsAndViewsProvider {
}

type StepsAndViewsAction =
| { type: 'ADD_STEP'; payload: { newStep: IStepProps } }
| { type: 'DELETE_STEP'; payload: { index: number } }
| { type: 'REPLACE_STEP'; payload: { newStep: IStepProps; oldStepIndex: number } }
| { type: 'UPDATE_INTEGRATION'; payload: any };
Expand All @@ -20,6 +21,11 @@ function stepsAndViewsReducer(state: IViewData, action: StepsAndViewsAction) {
const { type, payload } = action;

switch (type) {
case 'ADD_STEP': {
let newSteps = state.steps;
newSteps.push(payload.newStep);
return { ...state, steps: newSteps };
}
case 'DELETE_STEP': {
return {
...state,
Expand Down
33 changes: 30 additions & 3 deletions src/components/Catalog.css
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@

.step {
.catalog--step {
border-radius: 15px;
cursor: grab;
}

.stepImage {
.catalog--stepImage {
height: 75px;
width: 75px;
align-items: center;
Expand All @@ -14,8 +14,35 @@
position: relative;
}

.stepLabel {
.catalog--stepLabel {
margin-top: 5px !important;
}

/* MINI CATALOG */

/* Mini Catalog Search */
.miniCatalog--search.pf-c-toolbar__item {
width: 100%;
}

/* Mini Catalog Step (from list) */
button.miniCatalog--stepItem.pf-c-button.pf-m-tertiary {
--pf-c-button--after--BorderColor: transparent;
width: 100%;
}

button.miniCatalog--stepItem:hover {
background: #F5F5F5;
}

.miniCatalog--stepItem__grid {
line-height: 40px;
padding: 2px;
}

.miniCatalog--stepImage {
height: 40px;
width: 40px;
padding: 0 5px;
}

4 changes: 2 additions & 2 deletions src/components/Catalog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ const Catalog = (props: ICatalog) => {
<Grid md={6}>
<GridItem span={2}>
<Bullseye>
<img src={step.icon} className={'stepImage'} alt={'Step Image'} />
<img src={step.icon} className={'catalog--stepImage'} alt={'Step Image'} />
</Bullseye>
</GridItem>
<GridItem span={7}>
Expand All @@ -186,7 +186,7 @@ const Catalog = (props: ICatalog) => {
<CardBody>{shorten(step.description, 60)}</CardBody>
</GridItem>
<GridItem span={3}>
<Label color={'blue'} className={'stepLabel'}>
<Label color={'blue'} className={'catalog--stepLabel'}>
SOURCE
</Label>
</GridItem>
Expand Down
23 changes: 23 additions & 0 deletions src/components/MiniCatalog.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import catalog from '../data/catalog';
import { MiniCatalog, IMiniCatalog } from './MiniCatalog';
import { action } from '@storybook/addon-actions';
import { ComponentStory, ComponentMeta } from '@storybook/react';

export default {
title: 'Components/MiniCatalog',
component: MiniCatalog,
} as ComponentMeta<typeof MiniCatalog>;

const Template: ComponentStory<typeof MiniCatalog> = (props: IMiniCatalog) => {
return (
<div style={{ maxWidth: '20%' }}>
<MiniCatalog {...props} />
</div>
);
};

export const Primary = Template.bind({});
Primary.args = {
handleSelectStep: action('Step selected'),
steps: catalog,
};
12 changes: 12 additions & 0 deletions src/components/MiniCatalog.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { MiniCatalog } from './MiniCatalog';
import { screen } from '@testing-library/dom';
import { render } from '@testing-library/react';

describe('MiniCatalog.tsx', () => {
test('component renders correctly', () => {
render(<MiniCatalog />);

const element = screen.getByTestId('miniCatalog');
expect(element).toBeInTheDocument();
});
});
115 changes: 115 additions & 0 deletions src/components/MiniCatalog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import request from '../api/request';
import { IStepProps } from '../types';
import {
Bullseye,
Button,
Grid,
GridItem,
InputGroup,
TextInput,
Toolbar,
ToolbarContent,
ToolbarItem,
} from '@patternfly/react-core';
import { useEffect, useState } from 'react';

export interface IMiniCatalog {
handleSelectStep?: (selectedStep: any) => void;
steps?: IStepProps[];
}

export const MiniCatalog = (props: IMiniCatalog) => {
const [catalogData, setCatalogData] = useState<IStepProps[]>(props.steps ?? []);
const [query, setQuery] = useState(``);

/**
* Sort & fetch all Steps for the Catalog
*/
useEffect(() => {
if (!props.steps) {
const getCatalogData = async () => {
try {
const resp = await request.get({
endpoint: '/step',
});

const data = await resp.json();
data.sort((a: IStepProps, b: IStepProps) => a.name.localeCompare(b.name));
setCatalogData(data);
} catch (err) {
console.error(err);
}
};

getCatalogData().catch((e) => {
console.error(e);
});
}
}, []);

const changeSearch = (e: any) => {
setQuery(e);
};

function search(items: any[]) {
return items.filter((item) => item.name.toLowerCase().indexOf(query.toLowerCase()) > -1);
}

function handleSelectStep(selectedStep: any) {
if (props.handleSelectStep) {
props.handleSelectStep(selectedStep);
}
}

return (
<section data-testid={'miniCatalog'}>
<Toolbar id={'toolbar'} style={{ background: 'transparent' }}>
<ToolbarContent>
{
<ToolbarItem className={'miniCatalog--search'}>
<InputGroup>
<TextInput
name={'stepSearch'}
id={'stepSearch'}
type={'search'}
placeholder={'search for a step...'}
aria-label={'search for a step'}
value={query}
onChange={changeSearch}
/>
</InputGroup>
</ToolbarItem>
}
</ToolbarContent>
</Toolbar>
{catalogData &&
search(catalogData)
.slice(0, 5)
.map((step, idx) => {
return (
<Button
key={idx}
variant={'tertiary'}
onClick={() => {
handleSelectStep(step);
}}
className={'miniCatalog--stepItem'}
>
<Grid md={6} className={'miniCatalog--stepItem__grid'}>
<GridItem span={3}>
<Bullseye>
<img
src={step.icon}
className={'miniCatalog--stepImage'}
alt={'Step Image'}
/>
</Bullseye>
</GridItem>
<GridItem span={9}>{step.name}</GridItem>
</Grid>
</Button>
);
})}
</section>
);
};
8 changes: 6 additions & 2 deletions src/components/Visualization.css
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,16 @@
.stepNode__Add {
height: 6px;
position: absolute;
right: -10px;
top: 50%;
right: -8px;
top: 40%;
transform: translateY(-50%);
width: 6px;
}

.stepNode__Add button {
padding: 0;
}

.stepNode__Icon {
position: relative;
top: 55%;
Expand Down
8 changes: 3 additions & 5 deletions src/components/Visualization.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ const Visualization = ({ toggleCatalog }: IVisualization) => {
UUID: step.UUID,
onDropChange,
onElementClick,
onElementClickAdd,
onElementClickAdd: onSelectNewStep,
},
id: getId(),
position: { x: 0, y: window.innerHeight / 2 },
Expand Down Expand Up @@ -278,9 +278,8 @@ const Visualization = ({ toggleCatalog }: IVisualization) => {
setIsPanelExpanded(!isPanelExpanded);
};

const onElementClickAdd = (_e: any, element: any) => {
console.log('clicked!', element);
// add mini catalog
const onSelectNewStep = (selectedStep: IStepProps) => {
dispatch({ type: 'ADD_STEP', payload: { newStep: selectedStep } });
};

const onElementsRemove = (elementsToRemove: Elements<IVizStepProps[]>) =>
Expand Down Expand Up @@ -342,7 +341,6 @@ const Visualization = ({ toggleCatalog }: IVisualization) => {
onConnect={onConnect}
onDrop={onDrop}
onDragOver={onDragOver}
// onElementClick={onElementClick}
onElementsRemove={onElementsRemove}
onLoad={onLoad}
snapToGrid={true}
Expand Down
58 changes: 36 additions & 22 deletions src/components/VisualizationStep.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { MiniCatalog } from './MiniCatalog';
import './Visualization.css';
import { Button, Popover } from '@patternfly/react-core';
import { PlusCircleIcon } from '@patternfly/react-icons';
import { Handle, Position, useStoreState } from 'react-flow-renderer';

Expand All @@ -16,32 +18,44 @@ const VisualizationStep = ({ data }: any) => {

const onDropChange = (event: any) => data.onDropChange(event, data);
const onElementClick = (event: any) => data.onElementClick(event, data);
const onElementClickAdd = (event: any) => data.onElementClickAdd(event, data);
const onElementClickAdd = (selectedStep: any) => data.onElementClickAdd(selectedStep);

return (
<div
className={'stepNode'}
style={{ border: '2px solid ' + borderColor, borderRadius: '50%' }}
onDrop={onDropChange}
>
{data.connectorType !== 'END' && !isLastNode && (
<Handle type="source" position={Position.Right} id="b" style={{ borderRadius: 0 }} />
)}
{data.connectorType !== 'END' && isLastNode && (
<div className={'stepNode__Add'} onClick={onElementClickAdd}>
<PlusCircleIcon />
<>
<div
className={'stepNode'}
style={{ border: '2px solid ' + borderColor, borderRadius: '50%' }}
onDrop={onDropChange}
>
{data.connectorType !== 'END' && !isLastNode && (
<Handle type="source" position={Position.Right} id="b" style={{ borderRadius: 0 }} />
)}
{data.connectorType !== 'END' && isLastNode && (
<Popover
appendTo={() => document.body}
aria-label="Search for a step"
bodyContent={<MiniCatalog handleSelectStep={onElementClickAdd} />}
hideOnOutsideClick={true}
position={'auto'}
>
<div className={'stepNode__Add'}>
<Button variant="plain" aria-label="Action">
<PlusCircleIcon />
</Button>
</div>
</Popover>
)}
<div className={'stepNode__Icon'} onClick={onElementClick}>
<img src={data.icon} className="nodrag" alt={data.label} />
</div>
{data.connectorType !== 'START' && (
<Handle type="target" position={Position.Left} id="a" style={{ borderRadius: 0 }} />
)}
<div className={'stepNode__Label'} onClick={onElementClick}>
{data.label}
</div>
)}
<div className={'stepNode__Icon'} onClick={onElementClick}>
<img src={data.icon} className="nodrag" alt={data.label} />
</div>
{data.connectorType !== 'START' && (
<Handle type="target" position={Position.Left} id="a" style={{ borderRadius: 0 }} />
)}
<div className={'stepNode__Label'} onClick={onElementClick}>
{data.label}
</div>
</div>
</>
);
};

Expand Down
1 change: 1 addition & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * from './Extension';
export * from './JsonSchemaConfigurator';
export * from './MASAlerts';
export * from './MASLoading';
export * from './MiniCatalog';
export * from './StepErrorBoundary';
export * from './StepViews';
export * from './Visualization';
Expand Down