Skip to content

Commit

Permalink
Merge pull request #126 from Lemoncode/feature/#79-delete-shape-via-t…
Browse files Browse the repository at this point in the history
…oolbar

Feature/#79 Added delete shape via toolbar
  • Loading branch information
brauliodiez authored Aug 6, 2024
2 parents 4031b40 + 011ab4e commit a9b646b
Show file tree
Hide file tree
Showing 8 changed files with 224 additions and 0 deletions.
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

0 comments on commit a9b646b

Please sign in to comment.