From 27159f53d3314427fd043ea3a66170990e4539ef Mon Sep 17 00:00:00 2001 From: Siklos Date: Thu, 4 Aug 2022 20:05:12 +0200 Subject: [PATCH 1/9] Update readme --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 5ecff80..3f089ad 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # SVG Layout Designer React +[![Build Status](https://drone.siklos-chaneru.duckdns.org/api/badges/Siklos/svg-layout-designer-react/status.svg)](https://drone.siklos-chaneru.duckdns.org/Siklos/svg-layout-designer-react) + +[![Build Status](https://drone.siklos-chaneru.duckdns.org/api/badges/Siklos/svg-layout-designer-react/status.svg?ref=refs/heads/dev)](https://drone.siklos-chaneru.duckdns.org/Siklos/svg-layout-designer-react) + An svg layout designer. # Getting Started From 126b34fda46607a101e779fa8bb46785f0169ebc Mon Sep 17 00:00:00 2001 From: Siklos Date: Sat, 6 Aug 2022 15:24:01 +0200 Subject: [PATCH 2/9] Added Sidebar test --- src/Components/Sidebar/Sidebar.test.tsx | 53 ++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/src/Components/Sidebar/Sidebar.test.tsx b/src/Components/Sidebar/Sidebar.test.tsx index c17f48e..3613fc8 100644 --- a/src/Components/Sidebar/Sidebar.test.tsx +++ b/src/Components/Sidebar/Sidebar.test.tsx @@ -1,19 +1,60 @@ import * as React from 'react'; -import { describe, test, expect } from 'vitest'; -import { render, screen } from '../../utils/test-utils'; +import { describe, test, expect, vi } from 'vitest'; +import { findByText, fireEvent, render, screen } from '../../utils/test-utils'; import Sidebar from './Sidebar'; -describe('Sidebar test', () => { - test('Start empty', () => { +describe.concurrent('Sidebar open', () => { + it('Start default', async() => { + const handleClick = vi.fn(); render( {}} + onClick={handleClick} buttonOnClick={() => {}} /> ); + const stuff = screen.queryByText(/stuff/i); + const close = screen.getByText(/close/i); - expect(screen.getByText(/Components/i)).toBeDefined(); + expect(screen.getByText(/Components/i).classList.contains('left-0')).toBeDefined(); + expect(stuff).toBeNull(); + fireEvent.click(close); + expect(handleClick).toHaveBeenCalledTimes(1); + }); + + it('Start close', async() => { + render( {}} + buttonOnClick={() => {}} + />); + + const stuff = screen.queryByText(/stuff/i); + expect(screen.getByText(/Components/i).classList.contains('-left-64')).toBeDefined(); + expect(stuff).toBeNull(); + }); + + it('With stuff', async() => { + const handleButtonClick = vi.fn(); + render( {}} + buttonOnClick={handleButtonClick} + />); + const stuff = screen.getByText(/stuff/i); + + expect(stuff).toBeDefined(); + fireEvent.click(stuff); + expect(handleButtonClick).toHaveBeenCalledTimes(1); }); }); From 7e4dbd9e2db4ec78320cc696677c8dc21e17fbc3 Mon Sep 17 00:00:00 2001 From: Siklos Date: Sat, 6 Aug 2022 15:27:57 +0200 Subject: [PATCH 3/9] Update some tests --- src/Components/Sidebar/Sidebar.test.tsx | 7 +++++-- src/test/api.test.tsx | 8 ++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/Components/Sidebar/Sidebar.test.tsx b/src/Components/Sidebar/Sidebar.test.tsx index 3613fc8..be836a9 100644 --- a/src/Components/Sidebar/Sidebar.test.tsx +++ b/src/Components/Sidebar/Sidebar.test.tsx @@ -37,11 +37,14 @@ describe.concurrent('Sidebar open', () => { }); it('With stuff', async() => { - const handleButtonClick = vi.fn(); + const Type = 'stuff'; + const handleButtonClick = vi.fn((type: string) => { + expect(type).toBe(Type); + }); render( { - test('Load environment', () => { +describe.concurrent('API test', () => { + it('Load environment', () => { const url = import.meta.env.VITE_API_URL; expect(url).toBe('http://localhost:5000'); }); - test('Fetch configuration', async() => { + it('Fetch configuration', async() => { const configuration = await fetchConfiguration(); expect(configuration.MainContainer).toBeDefined(); expect(configuration.MainContainer.Height).toBeGreaterThan(0); From 548b70f951b0fa65734cc7b17f61d5d0fcfd4b4c Mon Sep 17 00:00:00 2001 From: Siklos Date: Sat, 6 Aug 2022 16:29:32 +0200 Subject: [PATCH 4/9] Added test for properties --- src/Components/Properties/Properties.test.tsx | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 src/Components/Properties/Properties.test.tsx diff --git a/src/Components/Properties/Properties.test.tsx b/src/Components/Properties/Properties.test.tsx new file mode 100644 index 0000000..05949a0 --- /dev/null +++ b/src/Components/Properties/Properties.test.tsx @@ -0,0 +1,75 @@ +import { fireEvent, render, screen } from '@testing-library/react'; +import * as React from 'react'; +import { describe, it, vi } from 'vitest'; +import { Properties } from './Properties'; + +describe.concurrent('Properties', () => { + it('No properties', () => { + render( {}} + />); + + expect(screen.queryByText(/property-/i)).toBeNull(); + }); + + it('Some properties', () => { + const prop = { + id: 'stuff', + parentId: 'parentId', + x: 1, + y: 1 + }; + + const handleChange = vi.fn((key, value) => { + (prop as any)[key] = value; + }); + + const { container, rerender } = render(); + + let propertyId = container.querySelector('#property-id'); + let propertyParentId = container.querySelector('#property-parentId'); + let propertyX = container.querySelector('#property-x'); + let propertyY = container.querySelector('#property-y'); + expect(propertyId).toBeDefined(); + expect((propertyId as HTMLInputElement).value).toBe('stuff'); + expect(propertyParentId).toBeDefined(); + expect((propertyParentId as HTMLInputElement).value).toBe('parentId'); + expect(propertyX).toBeDefined(); + expect((propertyX as HTMLInputElement).value).toBe('1'); + expect(propertyY).toBeDefined(); + expect((propertyY as HTMLInputElement).value).toBe('1'); + + fireEvent.change(propertyId as Element, { target: { value: 'stuffed' } }); + fireEvent.change(propertyParentId as Element, { target: { value: 'parentedId' } }); + fireEvent.change(propertyX as Element, { target: { value: '2' } }); + fireEvent.change(propertyY as Element, { target: { value: '2' } }); + expect(handleChange).toBeCalledTimes(4); + + expect(prop.id).toBe('stuffed'); + expect(prop.parentId).toBe('parentedId'); + expect(prop.x).toBe('2'); + expect(prop.y).toBe('2'); + rerender(); + + + propertyId = container.querySelector('#property-id'); + propertyParentId = container.querySelector('#property-parentId'); + propertyX = container.querySelector('#property-x'); + propertyY = container.querySelector('#property-y'); + expect(propertyId).toBeDefined(); + expect((propertyId as HTMLInputElement).value).toBe('stuffed'); + expect(propertyParentId).toBeDefined(); + expect((propertyParentId as HTMLInputElement).value).toBe('parentedId'); + expect(propertyX).toBeDefined(); + expect((propertyX as HTMLInputElement).value).toBe('2'); + expect(propertyY).toBeDefined(); + expect((propertyY as HTMLInputElement).value).toBe('2'); + }); +}); From db0c2fe051357a24c47153cecb34516589d4a538 Mon Sep 17 00:00:00 2001 From: Siklos Date: Sat, 6 Aug 2022 16:29:51 +0200 Subject: [PATCH 5/9] Remove expect calls in callback functions --- src/Components/Sidebar/Sidebar.test.tsx | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Components/Sidebar/Sidebar.test.tsx b/src/Components/Sidebar/Sidebar.test.tsx index be836a9..8f283eb 100644 --- a/src/Components/Sidebar/Sidebar.test.tsx +++ b/src/Components/Sidebar/Sidebar.test.tsx @@ -3,8 +3,8 @@ import { describe, test, expect, vi } from 'vitest'; import { findByText, fireEvent, render, screen } from '../../utils/test-utils'; import Sidebar from './Sidebar'; -describe.concurrent('Sidebar open', () => { - it('Start default', async() => { +describe.concurrent('Sidebar', () => { + it('Start default', () => { const handleClick = vi.fn(); render( { expect(handleClick).toHaveBeenCalledTimes(1); }); - it('Start close', async() => { + it('Start close', () => { render( { expect(stuff).toBeNull(); }); - it('With stuff', async() => { + it('With stuff', () => { const Type = 'stuff'; - const handleButtonClick = vi.fn((type: string) => { - expect(type).toBe(Type); - }); + const handleButtonClick = vi.fn(); render( Date: Sat, 6 Aug 2022 20:31:42 +0200 Subject: [PATCH 6/9] Improve Properties tests --- src/Components/Properties/Properties.test.tsx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Components/Properties/Properties.test.tsx b/src/Components/Properties/Properties.test.tsx index 05949a0..1ce9d25 100644 --- a/src/Components/Properties/Properties.test.tsx +++ b/src/Components/Properties/Properties.test.tsx @@ -10,7 +10,10 @@ describe.concurrent('Properties', () => { onChange={() => {}} />); - expect(screen.queryByText(/property-/i)).toBeNull(); + expect(screen.queryByText('id')).toBeNull(); + expect(screen.queryByText('parentId')).toBeNull(); + expect(screen.queryByText('x')).toBeNull(); + expect(screen.queryByText('y')).toBeNull(); }); it('Some properties', () => { @@ -30,6 +33,11 @@ describe.concurrent('Properties', () => { onChange={handleChange} />); + expect(screen.queryByText('id')).toBeDefined(); + expect(screen.queryByText('parentId')).toBeDefined(); + expect(screen.queryByText('x')).toBeDefined(); + expect(screen.queryByText('y')).toBeDefined(); + let propertyId = container.querySelector('#property-id'); let propertyParentId = container.querySelector('#property-parentId'); let propertyX = container.querySelector('#property-x'); @@ -58,7 +66,6 @@ describe.concurrent('Properties', () => { onChange={handleChange} />); - propertyId = container.querySelector('#property-id'); propertyParentId = container.querySelector('#property-parentId'); propertyX = container.querySelector('#property-x'); From 4efbc33893f85ae9729c7c1d69764e6f38468d5d Mon Sep 17 00:00:00 2001 From: Siklos Date: Sun, 7 Aug 2022 15:20:17 +0200 Subject: [PATCH 7/9] Allow to start from scratch with a default config --- src/App.tsx | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 705e40d..263827b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -100,8 +100,39 @@ export class App extends React.Component { historyCurrentStep: 0, isLoaded: true }); - }, (error) => { throw new Error(error); } - ); + }, (error) => { + // TODO: Implement an alert component + console.warn('[NewEditor] Could not fetch resource from API. Returning default.', error); + const MainContainer = new ContainerModel( + null, + { + id: 'main', + parentId: 'null', + x: 0, + y: 0, + width: DEFAULT_CONFIG.MainContainer.Width, + height: DEFAULT_CONFIG.MainContainer.Height, + fillOpacity: DEFAULT_CONFIG.MainContainer.Style.fillOpacity, + stroke: DEFAULT_CONFIG.MainContainer.Style.stroke, + } + ); + + // Save the configuration and the new MainContainer + // and default the selected container to it + this.setState({ + configuration: DEFAULT_CONFIG, + history: + [ + { + MainContainer, + SelectedContainer: MainContainer, + TypeCounters: {} + } + ], + historyCurrentStep: 0, + isLoaded: true + }); + }); } public LoadEditor(files: FileList | null): void { @@ -182,3 +213,28 @@ export async function fetchConfiguration(): Promise { xhr.send(); }); } + + +const DEFAULT_CONFIG: Configuration = { + AvailableContainers: [ + { + Type: 'Container', + Width: 75, + Height: 100, + Style: { + fillOpacity: 0, + stroke: 'green' + } + } + ], + AvailableSymbols: [], + MainContainer: { + Type: 'Container', + Width: 2000, + Height: 100, + Style: { + fillOpacity: 0, + stroke: 'black' + } + } +} From 2d048df3fe47dd645d5c96d3a9431990307313e0 Mon Sep 17 00:00:00 2001 From: Siklos Date: Sun, 7 Aug 2022 15:59:16 +0200 Subject: [PATCH 8/9] Add tests for elements sidebar --- .../ElementsSidebar/ElementsSidebar.test.tsx | 242 ++++++++++++++++++ 1 file changed, 242 insertions(+) create mode 100644 src/Components/ElementsSidebar/ElementsSidebar.test.tsx diff --git a/src/Components/ElementsSidebar/ElementsSidebar.test.tsx b/src/Components/ElementsSidebar/ElementsSidebar.test.tsx new file mode 100644 index 0000000..d52fe97 --- /dev/null +++ b/src/Components/ElementsSidebar/ElementsSidebar.test.tsx @@ -0,0 +1,242 @@ +import { describe, test, expect, vi } from 'vitest'; +import * as React from 'react'; +import { fireEvent, render, screen } from '../../utils/test-utils'; +import { ElementsSidebar } from './ElementsSidebar'; +import { IContainerModel } from '../../Interfaces/ContainerModel'; + +describe.concurrent('Elements sidebar', () => { + it('No elements', () => { + render( {}} + onPropertyChange={() => {}} + selectContainer={() => {}} + />); + + expect(screen.getByText(/Elements/i)); + expect(screen.queryByText('id')).toBeNull(); + expect(screen.queryByText(/main/i)).toBeNull(); + }); + + it('With a MainContainer', () => { + render( {}} + onPropertyChange={() => {}} + selectContainer={() => {}} + />); + + expect(screen.getByText(/Elements/i)); + expect(screen.queryByText('id')).toBeNull(); + expect(screen.getByText(/main/i)); + }); + + it('With a selected MainContainer', () => { + const MainContainer = { + children: [], + parent: null, + properties: { + id: 'main', + parentId: '', + x: 0, + y: 0, + width: 2000, + height: 100 + }, + userData: {} + }; + + const { container } = render( {}} + onPropertyChange={() => {}} + selectContainer={() => {}} + />); + + expect(screen.getByText(/Elements/i)); + expect(screen.getByText(/main/i)); + expect(screen.queryByText('id')).toBeDefined(); + expect(screen.queryByText('parentId')).toBeDefined(); + expect(screen.queryByText('x')).toBeDefined(); + expect(screen.queryByText('y')).toBeDefined(); + expect(screen.queryByText('width')).toBeDefined(); + expect(screen.queryByText('height')).toBeDefined(); + const propertyId = container.querySelector('#property-id'); + const propertyParentId = container.querySelector('#property-parentId'); + const propertyX = container.querySelector('#property-x'); + const propertyY = container.querySelector('#property-y'); + const propertyWidth = container.querySelector('#property-width'); + const propertyHeight = container.querySelector('#property-height'); + expect((propertyId as HTMLInputElement).value).toBe(MainContainer.properties.id.toString()); + expect(propertyParentId).toBeDefined(); + expect((propertyParentId as HTMLInputElement).value).toBe(''); + expect(propertyX).toBeDefined(); + expect((propertyX as HTMLInputElement).value).toBe(MainContainer.properties.x.toString()); + expect(propertyY).toBeDefined(); + expect((propertyY as HTMLInputElement).value).toBe(MainContainer.properties.y.toString()); + expect(propertyWidth).toBeDefined(); + expect((propertyWidth as HTMLInputElement).value).toBe(MainContainer.properties.width.toString()); + expect(propertyHeight).toBeDefined(); + expect((propertyHeight as HTMLInputElement).value).toBe(MainContainer.properties.height.toString()); + }); + + it('With multiple containers', () => { + const children: IContainerModel[] = []; + const MainContainer = { + children, + parent: null, + properties: { + id: 'main', + parentId: '', + x: 0, + y: 0, + width: 2000, + height: 100 + }, + userData: {} + }; + + children.push( + { + children: [], + parent: MainContainer, + properties: { + id: 'child-1', + parentId: 'main', + x: 0, + y: 0, + width: 0, + height: 0 + }, + userData: {} + } + ); + + children.push( + { + children: [], + parent: MainContainer, + properties: { + id: 'child-2', + parentId: 'main', + x: 0, + y: 0, + width: 0, + height: 0 + }, + userData: {} + } + ); + + render( {}} + onPropertyChange={() => {}} + selectContainer={() => {}} + />); + + expect(screen.getByText(/Elements/i)); + expect(screen.queryByText('id')).toBeDefined(); + expect(screen.getByText(/main/i)); + expect(screen.getByText(/child-1/i)); + expect(screen.getByText(/child-2/i)); + }); + + it('With multiple containers, change selection', () => { + const children: IContainerModel[] = []; + const MainContainer: IContainerModel = { + children, + parent: null, + properties: { + id: 'main', + parentId: '', + x: 0, + y: 0, + width: 2000, + height: 100 + }, + userData: {} + }; + + const child1Model: IContainerModel = { + children: [], + parent: MainContainer, + properties: { + id: 'child-1', + parentId: 'main', + x: 0, + y: 0, + width: 0, + height: 0 + }, + userData: {} + }; + children.push(child1Model); + + let SelectedContainer = MainContainer; + const selectContainer = vi.fn((container: IContainerModel) => { + SelectedContainer = container; + }); + + const { container, rerender } = render( {}} + onPropertyChange={() => {}} + selectContainer={selectContainer} + />); + + expect(screen.getByText(/Elements/i)); + expect(screen.queryByText('id')).toBeDefined(); + expect(screen.getByText(/main/i)); + const child1 = screen.getByText(/child-1/i); + expect(child1); + const propertyId = container.querySelector('#property-id'); + const propertyParentId = container.querySelector('#property-parentId'); + expect((propertyId as HTMLInputElement).value).toBe(MainContainer.properties.id.toString()); + expect((propertyParentId as HTMLInputElement).value).toBe(''); + + fireEvent.click(child1); + + rerender( {}} + onPropertyChange={() => {}} + selectContainer={selectContainer} + />); + + expect((propertyId as HTMLInputElement).value === 'main').toBeFalsy(); + expect((propertyParentId as HTMLInputElement).value === '').toBeFalsy(); + expect((propertyId as HTMLInputElement).value).toBe(child1Model.properties.id.toString()); + expect((propertyParentId as HTMLInputElement).value).toBe(child1Model.properties.parentId?.toString()); + }); +}); From 3fa33161574d33bef312dc191e1dee9089228d10 Mon Sep 17 00:00:00 2001 From: Siklos Date: Sun, 7 Aug 2022 16:06:20 +0200 Subject: [PATCH 9/9] Fix Floating button hidden behind editor --- src/Components/UI/UI.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Components/UI/UI.tsx b/src/Components/UI/UI.tsx index 6476af5..7748c67 100644 --- a/src/Components/UI/UI.tsx +++ b/src/Components/UI/UI.tsx @@ -117,7 +117,7 @@ export class UI extends React.PureComponent { ☰ History - +