Skip to content

Commit

Permalink
Custom control panel (#48)
Browse files Browse the repository at this point in the history
* updte docker ignore

* Move the MiniMapBtn and LayoutBtn to the new ControlCenter component which wraps and extends react-flow's Controls component

    ControlCenter's layout button toggle the layout direction (horizontal or vertical)

    Control Center renders a layout button

    minimap control button toggle map visibility

    Added benefit, this eliminates all the css associated with the previous control buttons

    render MiniMapBtn in out ControlCenter

    convert MiniMapBtn and LayoutBtn's from html native <button/> elements to react flow <ControlButton/> elements

    move ControlCenter component source tree to under the Tree component directory

    remove control buttons from Header

* update css so our usage do not use react-flow internals

* 0.5.0
  • Loading branch information
dpgraham4401 authored Feb 29, 2024
1 parent f54b902 commit 1d23ef1
Show file tree
Hide file tree
Showing 19 changed files with 179 additions and 207 deletions.
24 changes: 19 additions & 5 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
# Configs
.gitignore
.dockerignore
README.md
.editorconfig
.eslintrc.cjs
.prettier*
.github
__mocks__
docs
node_modules/
**/.idea
.run
coverage
.git
Dockerfile
**/compose.*


# Documentation
**/README.md
docs
LICENSE


# TS/JS
**/node_modules/
**/*.d.ts
**/*.spec.*
__mocks__
**/coverage
**/build
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "the-manifest-game",
"private": true,
"version": "0.4.1",
"version": "0.5.0",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
14 changes: 2 additions & 12 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import { Spinner } from 'components/Spinner/Spinner';
import { Tree } from 'components/Tree/Tree';
import { useDecisionTree } from 'hooks';
import { useFetchConfig } from 'hooks/useFetchConfig/useFetchConfig';
import { useTreeDirection } from 'hooks/useTreeDirection/useTreeDirection';
import { useState } from 'react';

/**
* App - responsible for rendering the decision tree
Expand All @@ -15,25 +13,17 @@ import { useState } from 'react';
export default function App() {
const title = import.meta.env.VITE_APP_TITLE ?? 'The Manifest Game';
const { config, isLoading: configIsLoading, error: configError } = useFetchConfig(defaultTree);
const [direction, setDirection] = useTreeDirection();
const { nodes, edges, onClick } = useDecisionTree(config);
const [mapVisible, setMapVisible] = useState(true);

return (
<>
<Header
treeTitle={title}
direction={direction}
setDirection={setDirection}
mapVisible={mapVisible}
setMapVisible={setMapVisible}
/>
<Header treeTitle={title} />
{configIsLoading ? (
<Spinner />
) : configError ? (
<ErrorMsg message={'Error parsing the Decision Tree'} />
) : (
<Tree nodes={nodes} edges={edges} onClick={onClick} mapVisible={mapVisible} />
<Tree nodes={nodes} edges={edges} onClick={onClick} />
)}
</>
);
Expand Down
28 changes: 0 additions & 28 deletions src/components/Header/Controls/LayoutBtn/layoutbtn.module.css

This file was deleted.

20 changes: 0 additions & 20 deletions src/components/Header/Controls/MiniMapBtn/MiniMapBtn.tsx

This file was deleted.

28 changes: 0 additions & 28 deletions src/components/Header/Controls/MiniMapBtn/minimapbtn.module.css

This file was deleted.

56 changes: 3 additions & 53 deletions src/components/Header/Header.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,10 @@
import '@testing-library/jest-dom';
import { cleanup, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { DagDirection } from 'store/DagSlice/dagSlice';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { afterEach, describe, expect, it } from 'vitest';
import { Header } from './Header';

const TestComponent = ({
title,
setDirection,
direction,
mapVisible,
setMapVisible,
}: {
title?: string;
setDirection?: (dir: DagDirection) => void;
direction?: DagDirection;
mapVisible?: boolean;
setMapVisible?: (visible: boolean) => void;
}) => {
return (
<Header
treeTitle={title ?? 'foo'}
direction={direction ?? 'TB'}
setDirection={setDirection ?? vi.fn()}
mapVisible={mapVisible ?? true}
setMapVisible={setMapVisible ?? vi.fn()}
/>
);
const TestComponent = ({ title }: { title?: string }) => {
return <Header treeTitle={title ?? 'foo'} />;
};

describe('Header', () => {
Expand All @@ -36,32 +14,4 @@ describe('Header', () => {
render(<TestComponent title={title} />);
expect(screen.getByText(title)).toBeInTheDocument();
});
it('renders a layout toggle button', () => {
render(<TestComponent />);
expect(screen.getByRole('button', { name: /layout/i })).toBeInTheDocument();
});
it('toggles the layout direction', async () => {
const user = userEvent.setup();
const setDirection = vi.fn();
const { rerender } = render(<TestComponent setDirection={setDirection} direction={'LR'} />);
await user.click(screen.getByRole('button', { name: /layout/i }));
expect(setDirection).toHaveBeenCalled();
rerender(<TestComponent setDirection={setDirection} />);
await user.click(screen.getByRole('button', { name: /layout/i }));
expect(setDirection).toHaveBeenCalled();
});
it('renders a map toggle button', () => {
render(<TestComponent />);
expect(screen.getByRole('button', { name: /minimap/i })).toBeInTheDocument();
});
it('toggles the minimap visibility', async () => {
const user = userEvent.setup();
const setMapVisible = vi.fn();
const { rerender } = render(<TestComponent mapVisible={true} setMapVisible={setMapVisible} />);
await user.click(screen.getByRole('button', { name: /minimap/i }));
expect(setMapVisible).toHaveBeenCalled();
rerender(<TestComponent setMapVisible={setMapVisible} />);
await user.click(screen.getByRole('button', { name: /minimap/i }));
expect(setMapVisible).toHaveBeenCalled();
});
});
29 changes: 1 addition & 28 deletions src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,15 @@
import { LayoutBtn } from 'components/Header/Controls/LayoutBtn/LayoutBtn';
import { MiniMapBtn } from 'components/Header/Controls/MiniMapBtn/MiniMapBtn';
import { DagDirection } from 'store/DagSlice/dagSlice';
import styles from './header.module.css';

interface HeaderProps {
treeTitle: string;
direction: DagDirection;
setDirection: (direction: DagDirection) => void;
mapVisible: boolean;
setMapVisible: (visible: boolean) => void;
}

export const Header = ({
treeTitle,
setDirection,
direction,
setMapVisible,
mapVisible,
}: HeaderProps) => {
const isHorizontal = direction === 'LR';

const toggleDirection = () => {
setDirection(direction === 'TB' ? 'LR' : 'TB');
};

const toggleMap = () => {
setMapVisible(!mapVisible);
};

export const Header = ({ treeTitle }: HeaderProps) => {
return (
<>
<div className={styles.treeHeader}>
<div className={styles.headerBanner}>
<h1>{treeTitle}</h1>
<div className={styles.headerControls}>
<LayoutBtn isHorizontal={isHorizontal} toggleDirection={toggleDirection} />
<MiniMapBtn visible={mapVisible} toggleMap={toggleMap} />
</div>
</div>
</div>
</>
Expand Down
5 changes: 0 additions & 5 deletions src/components/Header/header.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,3 @@
margin: 0;
}

.headerControls {
display: flex;
align-items: center;
justify-content: end;
}
70 changes: 70 additions & 0 deletions src/components/Tree/ControlCenter/ControlCenter.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import '@testing-library/jest-dom';
import { cleanup, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { ControlCenter } from 'components/Tree/ControlCenter/index';
import { ReactFlowProvider } from 'reactflow';
import { DagDirection } from 'store/DagSlice/dagSlice';
import { afterEach, describe, expect, it, vi } from 'vitest';

interface TestComponentProps {
mapVisible?: boolean;
setMapVisible?: (visible: boolean) => void;
direction?: DagDirection;
setDirection?: (direction: DagDirection) => void;
}

const TestComponent = ({ ...props }: TestComponentProps) => {
const dummyFunc = () => undefined;
const mapVisible = props?.mapVisible ?? true;
const onClick = props?.setMapVisible ?? dummyFunc;
const setDirection = props?.setDirection ?? dummyFunc;
const direction = props?.direction ?? 'TB';

return (
<ReactFlowProvider>
<ControlCenter
mapVisible={mapVisible}
setMapVisible={onClick}
direction={direction}
setDirection={setDirection}
/>
</ReactFlowProvider>
);
};

afterEach(() => cleanup());

describe('ControlCenter', () => {
it('renders', () => {
render(<TestComponent />);
expect(screen.getByTestId('controlCenter')).toBeInTheDocument();
});
it('renders a map toggle button', () => {
render(<TestComponent />);
expect(screen.getByRole('button', { name: /minimap/i })).toBeInTheDocument();
});
it('toggles the minimap visibility', async () => {
const user = userEvent.setup();
const setMapVisible = vi.fn();
const { rerender } = render(<TestComponent mapVisible={true} setMapVisible={setMapVisible} />);
await user.click(screen.getByRole('button', { name: /minimap/i }));
expect(setMapVisible).toHaveBeenCalled();
rerender(<TestComponent setMapVisible={setMapVisible} />);
await user.click(screen.getByRole('button', { name: /minimap/i }));
expect(setMapVisible).toHaveBeenCalled();
});
it('renders a layout toggle button', () => {
render(<TestComponent />);
expect(screen.getByRole('button', { name: /layout/i })).toBeInTheDocument();
});
it('toggles the layout direction', async () => {
const user = userEvent.setup();
const setDirection = vi.fn();
const { rerender } = render(<TestComponent setDirection={setDirection} direction={'LR'} />);
await user.click(screen.getByRole('button', { name: /layout/i }));
expect(setDirection).toHaveBeenCalled();
rerender(<TestComponent setDirection={setDirection} />);
await user.click(screen.getByRole('button', { name: /layout/i }));
expect(setDirection).toHaveBeenCalled();
});
});
Loading

0 comments on commit 1d23ef1

Please sign in to comment.