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

Feature/#79 Added delete shape via toolbar #126

Merged
merged 1 commit into from
Aug 6, 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
15 changes: 15 additions & 0 deletions src/common/components/icons/delete-icon.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export const DeleteIcon = () => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
viewBox="0 0 256 256"
>
<path
fill="currentColor"
d="M216 50h-42V40a22 22 0 0 0-22-22h-48a22 22 0 0 0-22 22v10H40a6 6 0 0 0 0 12h10v146a14 14 0 0 0 14 14h128a14 14 0 0 0 14-14V62h10a6 6 0 0 0 0-12M94 40a10 10 0 0 1 10-10h48a10 10 0 0 1 10 10v10H94Zm100 168a2 2 0 0 1-2 2H64a2 2 0 0 1-2-2V62h132Zm-84-104v64a6 6 0 0 1-12 0v-64a6 6 0 0 1 12 0m48 0v64a6 6 0 0 1-12 0v-64a6 6 0 0 1 12 0"
/>
</svg>
);
};
168 changes: 168 additions & 0 deletions src/core/providers/canvas/canvas.business.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import { describe, it, expect } from 'vitest';
import { removeShapeFromList } from './canvas.business';
import { ShapeModel } from '@/core/model';

describe('canvas.business', () => {
describe('removeShapeFromList', () => {
it('should return an empty array if shapeCollection is empty', () => {
// Arrange
const shapeId = '';
const shapeCollection: ShapeModel[] = [];

// Act
const result = removeShapeFromList(shapeId, shapeCollection);

// Assert
expect(result).toEqual([]);
});

it('should return the same array if shapeCollection has elements and shapeId is empty string', () => {
// Arrange
const shapeId = '';
const shapeCollection: ShapeModel[] = [
{
id: '1',
x: 0,
y: 0,
width: 10,
height: 10,
type: 'combobox',
allowsInlineEdition: false,
},
{
id: '2',
x: 1,
y: 1,
width: 20,
height: 20,
type: 'input',
allowsInlineEdition: true,
},
];

// Act
const result = removeShapeFromList(shapeId, shapeCollection);

// Assert
expect(result).toHaveLength(2);
expect(result).toEqual(shapeCollection);
});

it('should remove the shape with the specified shapeId if it exists in a collection with multiple elements', () => {
// Arrange
const shapeId = '2';
const shapeCollection: ShapeModel[] = [
{
id: '1',
x: 0,
y: 0,
width: 10,
height: 10,
type: 'combobox',
allowsInlineEdition: false,
},
{
id: '2',
x: 1,
y: 1,
width: 20,
height: 20,
type: 'input',
allowsInlineEdition: true,
},
{
id: '3',
x: 2,
y: 2,
width: 30,
height: 30,
type: 'button',
allowsInlineEdition: false,
},
{
id: '4',
x: 3,
y: 3,
width: 40,
height: 40,
type: 'checkbox',
allowsInlineEdition: true,
},
];

// Act
const result = removeShapeFromList(shapeId, shapeCollection);

// Assert
expect(result).toHaveLength(3);
expect(result).toEqual([
{
id: '1',
x: 0,
y: 0,
width: 10,
height: 10,
type: 'combobox',
allowsInlineEdition: false,
},
{
id: '3',
x: 2,
y: 2,
width: 30,
height: 30,
type: 'button',
allowsInlineEdition: false,
},
{
id: '4',
x: 3,
y: 3,
width: 40,
height: 40,
type: 'checkbox',
allowsInlineEdition: true,
},
]);
});

it('should return an empty array if the shape with the specified shapeId exists in a collection with a single element', () => {
// Arrange
const shapeId = '1';
const shapeCollection: ShapeModel[] = [
{
id: '1',
x: 0,
y: 0,
width: 10,
height: 10,
type: 'combobox',
allowsInlineEdition: false,
},
];

// Act
const result = removeShapeFromList(shapeId, shapeCollection);

// Assert
expect(result).toEqual([]);
});
});

it('shapeID contains a value and exists on the shape list (1 element)', () => {
const shapeId = '1';
const shapeCollection: ShapeModel[] = [
{
id: '1',
x: 0,
y: 0,
width: 10,
height: 10,
type: 'combobox',
allowsInlineEdition: false,
},
];
const result = removeShapeFromList(shapeId, shapeCollection);
expect(result).toEqual([]);
});
});
11 changes: 11 additions & 0 deletions src/core/providers/canvas/canvas.business.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { ShapeModel } from '@/core/model';

export const removeShapeFromList = (
shapeId: string,
shapeCollection: ShapeModel[]
): ShapeModel[] => {
if (shapeId === '') {
return shapeCollection;
}
return shapeCollection.filter(shape => shape.id !== shapeId);
};
1 change: 1 addition & 0 deletions src/core/providers/canvas/canvas.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@ export interface CanvasContextModel {
updateShapeSizeAndPosition: (id: string, position: Coord, size: Size) => void;
updateShapePosition: (id: string, position: Coord) => void;
selectionInfo: SelectionInfo;
deleteSelectedShape: () => void;
}
8 changes: 8 additions & 0 deletions src/core/providers/canvas/canvas.provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { CanvasContext } from './canvas.context';
import { useSelection } from './use-selection.hook';
import { createShape } from '@/pods/canvas/canvas.model';
import { v4 as uuidv4 } from 'uuid';
import { removeShapeFromList } from './canvas.business';

interface Props {
children: React.ReactNode;
Expand All @@ -16,6 +17,12 @@ export const CanvasProvider: React.FC<Props> = props => {

const selectionInfo = useSelection(shapes, setShapes);

const deleteSelectedShape = () => {
setShapes(prevShapes =>
removeShapeFromList(selectionInfo.selectedShapeId, prevShapes)
);
};

const clearCanvas = () => {
setShapes([]);
};
Expand Down Expand Up @@ -62,6 +69,7 @@ export const CanvasProvider: React.FC<Props> = props => {
pasteShape,
updateShapeSizeAndPosition,
updateShapePosition,
deleteSelectedShape,
}}
>
{children}
Expand Down
18 changes: 18 additions & 0 deletions src/pods/toolbar/components/delete-button/delete-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { DeleteIcon } from '@/common/components/icons/delete-icon.component';
import { ToolbarButton } from '@/pods/toolbar/components/toolbar-button/toolbar-button';
import classes from '@/pods/toolbar/toolbar.pod.module.css';
import { useCanvasContext } from '@/core/providers';
export const DeleteButton = () => {
const { deleteSelectedShape, selectionInfo } = useCanvasContext();

return (
<ToolbarButton
onClick={deleteSelectedShape}
className={classes.button}
disabled={selectionInfo.selectedShapeId ? false : true}
>
<DeleteIcon />
<span>Delete</span>
</ToolbarButton>
);
};
1 change: 1 addition & 0 deletions src/pods/toolbar/components/delete-button/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './delete-button';
2 changes: 2 additions & 0 deletions src/pods/toolbar/toolbar.pod.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { DeleteButton } from './components/delete-button';
import {
ZoomInButton,
ZoomOutButton,
Expand All @@ -22,6 +23,7 @@ export const ToolbarPod: React.FC = () => {
<UndoButton />
<RedoButton />
<ExportButton />
<DeleteButton />
<AboutButton />
</div>
);
Expand Down
Loading