Skip to content

Commit

Permalink
fix: Warn users when trying to delete a container
Browse files Browse the repository at this point in the history
  • Loading branch information
Shivam Gupta committed May 8, 2024
1 parent b9effe1 commit 230e418
Show file tree
Hide file tree
Showing 13 changed files with 361 additions and 18 deletions.
6 changes: 6 additions & 0 deletions packages/ui-tests/cypress/support/next-commands/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,5 +121,11 @@ Cypress.Commands.add('deleteRoute', (index: number) => {
cy.get('button[data-testid^="delete-btn-route"]').then((buttons) => {
cy.wrap(buttons[index]).click();
});
cy.get('body').then(($body) => {
if ($body.find('.pf-m-danger').length) {
// Delete Confirmation Modal appeared, click on the confirm button
cy.get('.pf-m-danger').click();
}
});
cy.closeFlowsListIfVisible();
});
6 changes: 6 additions & 0 deletions packages/ui-tests/cypress/support/next-commands/design.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ Cypress.Commands.add('checkConfigInputObject', (inputName: string, value: string

Cypress.Commands.add('removeNodeByName', (nodeName: string, nodeIndex?: number) => {
cy.performNodeAction(nodeName, 'delete', nodeIndex);
cy.get('body').then(($body) => {
if ($body.find('.pf-m-danger').length) {
// Delete Confirmation Modal appeared, click on the confirm button
cy.get('.pf-m-danger').click();
}
});
cy.get(nodeName).should('not.exist');
// wait for the canvas rerender
cy.wait(1000);
Expand Down
33 changes: 27 additions & 6 deletions packages/ui/src/components/Visualization/Canvas/Canvas.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { TestProvidersWrapper } from '../../../stubs';
import { camelRouteJson } from '../../../stubs/camel-route';
import { kameletJson } from '../../../stubs/kamelet-route';
import { Canvas } from './Canvas';
import { DeleteModalContextProvider } from '../../../providers';

describe('Canvas', () => {
const entity = new CamelRouteVisualEntity(camelRouteJson);
Expand Down Expand Up @@ -56,9 +57,11 @@ describe('Canvas', () => {
} as unknown as VisibleFLowsContextResult,
});
const wrapper = render(
<Provider>
<Canvas entities={routeEntities} />
</Provider>,
<DeleteModalContextProvider>
<Provider>
<Canvas entities={routeEntities} />
</Provider>
</DeleteModalContextProvider>,
);

// Right click anywhere on the container label
Expand All @@ -75,6 +78,14 @@ describe('Canvas', () => {
fireEvent.click(deleteRoute);
});

// Deal with the Confirmation modal
const deleteConfirmation = screen.getByRole('button', { name: 'Confirm' });
expect(deleteConfirmation).toBeInTheDocument();

await act(async () => {
fireEvent.click(deleteConfirmation);
});

// Check if the remove function is called
expect(removeSpy).toHaveBeenCalled();
expect(removeSpy).toHaveBeenCalledWith('route-8888');
Expand All @@ -93,9 +104,11 @@ describe('Canvas', () => {
});

const wrapper = render(
<Provider>
<Canvas entities={kameletEntities} />
</Provider>,
<DeleteModalContextProvider>
<Provider>
<Canvas entities={kameletEntities} />
</Provider>
</DeleteModalContextProvider>,
);

// Right click anywhere on the container label
Expand All @@ -113,6 +126,14 @@ describe('Canvas', () => {
fireEvent.click(deleteKamelet);
});

// Deal with the Confirmation modal
const deleteConfirmation = screen.getByRole('button', { name: 'Confirm' });
expect(deleteConfirmation).toBeInTheDocument();

await act(async () => {
fireEvent.click(deleteConfirmation);
});

// Check if the remove function is called
expect(removeSpy).toHaveBeenCalled();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Table, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table';
import { FunctionComponent, useCallback, useContext, useRef } from 'react';
import { BaseVisualCamelEntity } from '../../../../models/visualization/base-visual-entity';
import { EntitiesContext } from '../../../../providers/entities.provider';
import { DeleteModalContext } from '../../../../providers/delete-modal.provider';
import { VisibleFlowsContext } from '../../../../providers/visible-flows.provider';
import { InlineEdit } from '../../../InlineEdit';
import './FlowsList.scss';
Expand All @@ -18,6 +19,7 @@ interface IFlowsList {
export const FlowsList: FunctionComponent<IFlowsList> = (props) => {
const { visualEntities, camelResource, updateEntitiesFromCamelResource } = useContext(EntitiesContext)!;
const { visibleFlows, visualFlowsApi } = useContext(VisibleFlowsContext)!;
const deleteModalContext = useContext(DeleteModalContext);

const isListEmpty = visualEntities.length === 0;

Expand Down Expand Up @@ -102,7 +104,14 @@ export const FlowsList: FunctionComponent<IFlowsList> = (props) => {
data-testid={`delete-btn-${flow.id}`}
icon={<TrashIcon />}
variant="plain"
onClick={(event) => {
onClick={async (event) => {
const isDeleteConfirmed = await deleteModalContext?.deleteConfirmation({
title: 'Permanently delete flow?',
text: 'Flow configuration and its children will be lost.',
});

if (!isDeleteConfirmed) return;

camelResource.removeEntity(flow.id);
updateEntitiesFromCamelResource();
/** Required to avoid closing the Dropdown after clicking in the icon */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,15 @@ export const CustomNodeWithSelection: typeof DefaultNode = withSelection()(
}

if (nodeInteractions.canRemoveStep) {
const childrenNodes = vizNode.getChildren();
const shouldConfirmBeforeDeletion = childrenNodes !== undefined && childrenNodes.length > 0;
items.push(
<ItemDeleteStep key="context-menu-item-delete" data-testid="context-menu-item-delete" vizNode={vizNode} />,
<ItemDeleteStep
key="context-menu-item-delete"
data-testid="context-menu-item-delete"
vizNode={vizNode}
loadModal={shouldConfirmBeforeDeletion}
/>,
);
}
if (nodeInteractions.canRemoveFlow) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ContextMenuItem } from '@patternfly/react-topology';
import { FunctionComponent, PropsWithChildren, useCallback, useContext } from 'react';
import { IDataTestID } from '../../../models';
import { IVisualizationNode } from '../../../models/visualization/base-visual-entity';
import { DeleteModalContext } from '../../../providers/delete-modal.provider';
import { EntitiesContext } from '../../../providers/entities.provider';

interface ItemDeleteGroupProps extends PropsWithChildren<IDataTestID> {
Expand All @@ -11,12 +12,21 @@ interface ItemDeleteGroupProps extends PropsWithChildren<IDataTestID> {

export const ItemDeleteGroup: FunctionComponent<ItemDeleteGroupProps> = (props) => {
const entitiesContext = useContext(EntitiesContext);
const deleteModalContext = useContext(DeleteModalContext);
const flowId = props.vizNode?.getBaseEntity()?.getId();

const onRemoveGroup = useCallback(() => {
const onRemoveGroup = useCallback(async () => {
/** Open delete confirm modal, get the confirmation */
const isDeleteConfirmed = await deleteModalContext?.deleteConfirmation({
title: 'Permanently delete flow?',
text: 'All steps will be lost.',
});

if (!isDeleteConfirmed) return;

entitiesContext?.camelResource.removeEntity(flowId);
entitiesContext?.updateEntitiesFromCamelResource();
}, [entitiesContext, flowId]);
}, [deleteModalContext, entitiesContext, flowId]);

return (
<ContextMenuItem onClick={onRemoveGroup} data-testid={props['data-testid']}>
Expand Down
17 changes: 15 additions & 2 deletions packages/ui/src/components/Visualization/Custom/ItemDeleteStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,31 @@ import { FunctionComponent, PropsWithChildren, useCallback, useContext } from 'r
import { IDataTestID } from '../../../models';
import { IVisualizationNode } from '../../../models/visualization/base-visual-entity';
import { EntitiesContext } from '../../../providers/entities.provider';
import { DeleteModalContext } from '../../../providers/delete-modal.provider';

interface ItemDeleteStepProps extends PropsWithChildren<IDataTestID> {
vizNode: IVisualizationNode;
loadModal: boolean;
}

export const ItemDeleteStep: FunctionComponent<ItemDeleteStepProps> = (props) => {
const entitiesContext = useContext(EntitiesContext);
const deleteModalContext = useContext(DeleteModalContext);

const onRemoveNode = useCallback(async () => {
if (props.loadModal) {
/** Open delete confirm modal, get the confirmation */
const isDeleteConfirmed = await deleteModalContext?.deleteConfirmation({
title: 'Permanently delete step?',
text: 'Step and its children will be lost.',
});

if (!isDeleteConfirmed) return;
}

const onRemoveNode = useCallback(() => {
props.vizNode?.removeChild();
entitiesContext?.updateEntitiesFromCamelResource();
}, [entitiesContext, props.vizNode]);
}, [deleteModalContext, entitiesContext, props.loadModal, props.vizNode]);

return (
<ContextMenuItem onClick={onRemoveNode} data-testid={props['data-testid']}>
Expand Down
5 changes: 4 additions & 1 deletion packages/ui/src/multiplying-architecture/KaotoBridge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { CatalogLoaderProvider } from '../providers/catalog.provider';
import { SchemasLoaderProvider } from '../providers/schemas.provider';
import { SourceCodeApiContext } from '../providers/source-code.provider';
import { VisibleFlowsProvider } from '../providers/visible-flows.provider';
import { DeleteModalContextProvider } from '../providers/delete-modal.provider';
import { EventNotifier } from '../utils';

interface KaotoBridgeProps {
Expand Down Expand Up @@ -144,7 +145,9 @@ export const KaotoBridge = forwardRef<EditorApi, PropsWithChildren<KaotoBridgePr
<CatalogLoaderProvider catalogUrl={props.catalogUrl}>
<CatalogTilesProvider>
<VisibleFlowsProvider>
<CatalogModalProvider>{props.children}</CatalogModalProvider>
<CatalogModalProvider>
<DeleteModalContextProvider>{props.children}</DeleteModalContextProvider>
</CatalogModalProvider>
</VisibleFlowsProvider>
</CatalogTilesProvider>
</CatalogLoaderProvider>
Expand Down
13 changes: 8 additions & 5 deletions packages/ui/src/pages/Design/DesignPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { CatalogModalProvider } from '../../providers/catalog-modal.provider';
import { EntitiesContext } from '../../providers/entities.provider';
import './DesignPage.scss';
import { ReturnToSourceCodeFallback } from './ReturnToSourceCodeFallback';
import { DeleteModalContextProvider } from '../../providers/delete-modal.provider';

export const DesignPage: FunctionComponent = () => {
const entitiesContext = useContext(EntitiesContext);
Expand All @@ -12,11 +13,13 @@ export const DesignPage: FunctionComponent = () => {
return (
<div className="canvas-page">
<CatalogModalProvider>
<Visualization
className="canvas-page__canvas"
entities={visualEntities}
fallback={<ReturnToSourceCodeFallback />}
/>
<DeleteModalContextProvider>
<Visualization
className="canvas-page__canvas"
entities={visualEntities}
fallback={<ReturnToSourceCodeFallback />}
/>
</DeleteModalContextProvider>
</CatalogModalProvider>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`DeleteModalProvider should allow consumers to update the modal title and text 1`] = `
<div
aria-describedby="pf-modal-part-4"
aria-labelledby="pf-modal-part-3"
aria-modal="true"
class="pf-v5-c-modal-box pf-m-warning pf-m-sm"
data-ouia-component-id="DeleteConfirmModal"
data-ouia-component-type="PF5/ModalContent"
data-ouia-safe="true"
id="pf-modal-part-2"
role="dialog"
>
<div
class="pf-v5-c-modal-box__close"
>
<button
aria-disabled="false"
aria-label="Close"
class="pf-v5-c-button pf-m-plain"
data-ouia-component-id="DeleteConfirmModal-ModalBoxCloseButton"
data-ouia-component-type="PF5/Button"
data-ouia-safe="true"
type="button"
>
<svg
aria-hidden="true"
class="pf-v5-svg"
fill="currentColor"
height="1em"
role="img"
viewBox="0 0 352 512"
width="1em"
>
<path
d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z"
/>
</svg>
</button>
</div>
<header
class="pf-v5-c-modal-box__header"
>
<h1
class="pf-v5-c-modal-box__title pf-m-icon"
id="pf-modal-part-3"
>
<span
class="pf-v5-c-modal-box__title-icon"
>
<svg
aria-hidden="true"
class="pf-v5-svg"
fill="currentColor"
height="1em"
role="img"
viewBox="0 0 576 512"
width="1em"
>
<path
d="M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"
/>
</svg>
</span>
<span
class="pf-v5-screen-reader"
>
Warning alert:
</span>
<span
class="pf-v5-c-modal-box__title-text"
>
Custom title
</span>
</h1>
</header>
<div
class="pf-v5-c-modal-box__body"
id="pf-modal-part-4"
>
Custom text
</div>
<footer
class="pf-v5-c-modal-box__footer"
>
<button
aria-disabled="false"
class="pf-v5-c-button pf-m-danger"
data-ouia-component-id="OUIA-Generated-Button-danger-3"
data-ouia-component-type="PF5/Button"
data-ouia-safe="true"
type="button"
>
Confirm
</button>
<button
aria-disabled="false"
class="pf-v5-c-button pf-m-link"
data-ouia-component-id="OUIA-Generated-Button-link-3"
data-ouia-component-type="PF5/Button"
data-ouia-safe="true"
type="button"
>
Cancel
</button>
</footer>
</div>
`;
Loading

0 comments on commit 230e418

Please sign in to comment.