Skip to content

Commit

Permalink
Merge pull request #404 from bartoval/refactor_topology_components
Browse files Browse the repository at this point in the history
refactor(Topology): ♻️ Abstract topology toolbar
  • Loading branch information
bartoval authored Apr 28, 2024
2 parents f5f663d + 4b208d0 commit 0948ef7
Show file tree
Hide file tree
Showing 7 changed files with 372 additions and 454 deletions.
14 changes: 0 additions & 14 deletions src/pages/Topology/__tests__/TopologyProcesses.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { FC, forwardRef, memo, Suspense, useImperativeHandle } from 'react';
import { Button } from '@patternfly/react-core';
import { fireEvent, render, screen, waitForElementToBeRemoved } from '@testing-library/react';
import { Server } from 'miragejs';
import * as router from 'react-router';

import { waitForElementToBeRemovedTimeout } from '@config/config';
import { getTestsIds } from '@config/testIds';
Expand All @@ -18,8 +17,6 @@ import TopologyProcesses from '../components/TopologyProcesses';
import { TopologyLabels } from '../Topology.enum';
import { NodeOrEdgeListProps } from '../Topology.interfaces';

const navigate = jest.fn();

const processesResults = processesData.results;
const servicesResults = servicesData.results;
const serviceIdSelected = servicesResults[2].identity;
Expand Down Expand Up @@ -105,17 +102,6 @@ describe('Topology Process', () => {
fireEvent.click(screen.getByText('onClickEdge'));
});

it('should clicking on a combo', async () => {
jest.spyOn(router, 'useNavigate').mockImplementation(() => navigate);

await waitForElementToBeRemoved(() => screen.queryByTestId(getTestsIds.loadingView()), {
timeout: waitForElementToBeRemovedTimeout
});

fireEvent.click(screen.getByText('onClickCombo'));
expect(navigate).toHaveBeenCalledTimes(1);
});

it('should save node positions and display info alert when handleSaveTopology is called', async () => {
await waitForElementToBeRemoved(() => screen.queryByTestId(getTestsIds.loadingView()), {
timeout: waitForElementToBeRemovedTimeout
Expand Down
33 changes: 33 additions & 0 deletions src/pages/Topology/__tests__/TopologyToasts.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { createRef } from 'react';

import { render, waitFor } from '@testing-library/react';
import eventUser from '@testing-library/user-event';

import AlertToast, { ToastExposeMethods } from '../components/TopologyToasts';

describe('AlertToast', () => {
it('should add a new alert when addMessage is called', async () => {
const toastRef = createRef<ToastExposeMethods>();
const { findByText } = render(<AlertToast ref={toastRef} />);

const addMessage = toastRef.current?.addMessage;
expect(addMessage).toBeDefined();

addMessage!('This is a toast message!');
expect(await findByText('This is a toast message!')).toBeInTheDocument();
});

it('should remove an alert when the close button is clicked', async () => {
const toastRef = createRef<ToastExposeMethods>();
const { getByTestId } = render(<AlertToast ref={toastRef} timeout={false} />);

const addMessage = toastRef.current?.addMessage;
expect(addMessage).toBeDefined();

addMessage!('This is a toast message!');
const toast = await waitFor(() => getByTestId(`sk-toast-0`));

await eventUser.click(toast.querySelector('button') as HTMLButtonElement);
expect(toast).not.toBeInTheDocument();
});
});
127 changes: 20 additions & 107 deletions src/pages/Topology/components/TopologyProcessGroups.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,22 @@
import { FC, Key, useCallback, useRef, useState, ComponentType } from 'react';
import { FC, useCallback, useRef, useState, ComponentType } from 'react';

import {
Alert,
AlertActionCloseButton,
AlertGroup,
AlertProps,
AlertVariant,
Button,
Checkbox,
Divider,
Stack,
StackItem,
Toolbar,
ToolbarContent,
ToolbarGroup,
ToolbarItem,
getUniqueId
} from '@patternfly/react-core';
import { Divider, Stack, StackItem } from '@patternfly/react-core';
import { useSuspenseQueries } from '@tanstack/react-query';
import { useNavigate } from 'react-router-dom';

import { RESTApi } from '@API/REST.api';
import { TOAST_VISIBILITY_TIMEOUT, UPDATE_INTERVAL } from '@config/config';
import { UPDATE_INTERVAL } from '@config/config';
import { LAYOUT_TOPOLOGY_DEFAULT, LAYOUT_TOPOLOGY_SINGLE_NODE } from '@core/components/Graph/Graph.constants';
import {
GraphNode,
GraphReactAdaptorExposedMethods,
GraphReactAdaptorProps
} from '@core/components/Graph/Graph.interfaces';
import GraphReactAdaptor from '@core/components/Graph/ReactAdaptor';
import NavigationViewLink from '@core/components/NavigationViewLink';
import { ProcessGroupsRoutesPaths, QueriesProcessGroups } from '@pages/ProcessGroups/ProcessGroups.enum';

import DisplayResources from './DisplayResources';
import AlertToasts, { ToastExposeMethods } from './TopologyToasts';
import TopologyToolbar from './TopologyToolbar';
import { TopologyController } from '../services';
import { TopologyLabels, QueriesTopology } from '../Topology.enum';

Expand All @@ -48,12 +32,12 @@ const TopologyProcessGroups: FC<{ id?: string; GraphComponent?: ComponentType<Gr
const navigate = useNavigate();

const [componentIdSelected, setComponentIdSelected] = useState<string | undefined>(componentId);
const [alerts, setAlerts] = useState<Partial<AlertProps>[]>([]);

const [showOnlyNeighbours, setShowOnlyNeighbours] = useState(false);
const [moveToNodeSelected, setMoveToNodeSelected] = useState(false);

const graphRef = useRef<GraphReactAdaptorExposedMethods>();
const toastRef = useRef<ToastExposeMethods>(null);

const [{ data: processGroups }, { data: processGroupsPairs }] = useSuspenseQueries({
queries: [
Expand All @@ -71,22 +55,10 @@ const TopologyProcessGroups: FC<{ id?: string; GraphComponent?: ComponentType<Gr
]
});

const addAlert = (title: string, variant: AlertProps['variant'], key: Key) => {
setAlerts((prevAlerts) => [...prevAlerts, { title, variant, key }]);
};

const removeAlert = (key: Key) => {
setAlerts((prevAlerts) => [...prevAlerts.filter((alert) => alert.key !== key)]);
};

const addInfoAlert = useCallback((message: string) => {
addAlert(message, 'info', getUniqueId());
}, []);

const handleSaveTopology = useCallback(() => {
graphRef?.current?.saveNodePositions();
addInfoAlert(TopologyLabels.ToastSave);
}, [addInfoAlert]);
toastRef.current?.addMessage(TopologyLabels.ToastSave);
}, []);

const handleComponentSelected = useCallback((id?: string) => {
setComponentIdSelected(id);
Expand Down Expand Up @@ -131,66 +103,17 @@ const TopologyProcessGroups: FC<{ id?: string; GraphComponent?: ComponentType<Gr
<>
<Stack>
<StackItem>
<Toolbar>
<ToolbarContent>
<ToolbarGroup>
<ToolbarItem>
<DisplayResources
id={componentIdSelected}
onSelect={handleComponentSelected}
placeholder={TopologyLabels.DisplayComponentsDefaultLabel}
options={nodes.map((node) => ({ name: node.label, identity: node.id }))}
/>
</ToolbarItem>

<ToolbarItem>
<Checkbox
label={TopologyLabels.CheckboxShowOnlyNeghbours}
isDisabled={!componentIdSelected}
isChecked={showOnlyNeighbours}
onChange={(_, checked) => {
handleShowOnlyNeighboursChecked(checked);
}}
id="showOnlyNeighboursCheckbox"
/>
</ToolbarItem>

<ToolbarItem>
<Checkbox
label={TopologyLabels.CheckboxMoveToNodeSelected}
isDisabled={!componentIdSelected || showOnlyNeighbours}
isChecked={moveToNodeSelected}
onChange={(_, checked) => {
handleMoveToNodeSelectedChecked(checked);
}}
id="moveToNodeSelectedCheckbox"
/>
</ToolbarItem>
</ToolbarGroup>

<ToolbarItem variant="separator" />

<ToolbarItem
spacer={{
default: 'spacerSm'
}}
>
<Button onClick={handleSaveTopology} variant="secondary">
{TopologyLabels.SaveButton}
</Button>
</ToolbarItem>

<ToolbarGroup align={{ default: 'alignRight' }}>
<ToolbarItem>
<NavigationViewLink
link={ProcessGroupsRoutesPaths.ProcessGroups}
linkLabel={TopologyLabels.ListView}
iconName="listIcon"
/>
</ToolbarItem>
</ToolbarGroup>
</ToolbarContent>
</Toolbar>
<TopologyToolbar
nodes={nodes}
onProcessSelected={handleComponentSelected}
nodeIdSelected={componentIdSelected}
showOnlyNeighbours={showOnlyNeighbours}
onShowOnlyNeighboursChecked={handleShowOnlyNeighboursChecked}
moveToNodeSelected={moveToNodeSelected}
onMoveToNodeSelectedChecked={handleMoveToNodeSelectedChecked}
onSaveTopology={handleSaveTopology}
linkToPage={ProcessGroupsRoutesPaths.ProcessGroups}
/>
<Divider />
</StackItem>

Expand All @@ -206,17 +129,7 @@ const TopologyProcessGroups: FC<{ id?: string; GraphComponent?: ComponentType<Gr
/>
</StackItem>
</Stack>
<AlertGroup isToast>
{alerts.map(({ key, title }) => (
<Alert
key={key}
timeout={TOAST_VISIBILITY_TIMEOUT}
variant={AlertVariant.info}
title={title}
actionClose={<AlertActionCloseButton title={title as string} onClose={() => removeAlert(key as Key)} />}
/>
))}
</AlertGroup>
<AlertToasts ref={toastRef} />
</>
);
};
Expand Down
Loading

0 comments on commit 0948ef7

Please sign in to comment.