feat: make experiment control buttons prettier
Use actual icons for the play, pause, skip, etc. buttons.
This commit is contained in:
22
src/App.module.css
Normal file
22
src/App.module.css
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
/* Spread header items. Make home button centered until there is not enough space. */
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 1rem;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
/* Center each */
|
||||||
|
& > div {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
/* Except first and last */
|
||||||
|
& > div:first-child {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
& > div:last-child {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ import UserManual from './pages/Manuals/Manuals.tsx';
|
|||||||
import VisProg from "./pages/VisProgPage/VisProg.tsx";
|
import VisProg from "./pages/VisProgPage/VisProg.tsx";
|
||||||
import {useState} from "react";
|
import {useState} from "react";
|
||||||
import Logging from "./components/Logging/Logging.tsx";
|
import Logging from "./components/Logging/Logging.tsx";
|
||||||
|
import styles from "./App.module.css";
|
||||||
|
|
||||||
|
|
||||||
function App(){
|
function App(){
|
||||||
@@ -15,10 +16,10 @@ function App(){
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<header>
|
<header className={styles.header}>
|
||||||
<span>© Utrecht University (ICS)</span>
|
<div><span>© Utrecht University (ICS)</span></div>
|
||||||
<Link to={"/"}>Home</Link>
|
<div><Link to={"/"}>Home</Link></div>
|
||||||
<button onClick={() => setShowLogs(!showLogs)}>Developer Logs</button>
|
<div><button onClick={() => setShowLogs(!showLogs)}>Developer Logs</button></div>
|
||||||
</header>
|
</header>
|
||||||
<div className={"flex-row justify-center flex-1 min-height-0"}>
|
<div className={"flex-row justify-center flex-1 min-height-0"}>
|
||||||
<main className={"flex-col align-center flex-1 scroll-y"}>
|
<main className={"flex-col align-center flex-1 scroll-y"}>
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
export default function Redo({ fill }: { fill?: string }) {
|
|
||||||
return <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill={fill ?? "canvas"}>
|
|
||||||
<path d="M390.98-191.87q-98.44 0-168.77-65.27-70.34-65.27-70.34-161.43 0-96.15 70.34-161.54 70.33-65.39 168.77-65.39h244.11l-98.98-98.98 63.65-63.65L808.13-600 599.76-391.87l-63.65-63.65 98.98-98.98H390.98q-60.13 0-104.12 38.92-43.99 38.93-43.99 96.78 0 57.84 43.99 96.89 43.99 39.04 104.12 39.04h286.15v91H390.98Z"/>
|
|
||||||
</svg>;
|
|
||||||
}
|
|
||||||
5
src/components/Icons/Stop.tsx
Normal file
5
src/components/Icons/Stop.tsx
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export default function Stop({ fill }: { fill?: string }) {
|
||||||
|
return <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill={fill ?? "canvas"}>
|
||||||
|
<path d="M240-240v-480h480v480H240Z"/>
|
||||||
|
</svg>;
|
||||||
|
}
|
||||||
@@ -46,6 +46,7 @@ University within the Software Project course.
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
padding: .5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,11 @@ import {
|
|||||||
RobotConnected
|
RobotConnected
|
||||||
} from './MonitoringPageComponents';
|
} from './MonitoringPageComponents';
|
||||||
import ExperimentLogs from "./components/ExperimentLogs.tsx";
|
import ExperimentLogs from "./components/ExperimentLogs.tsx";
|
||||||
|
import Pause from "../../components/Icons/Pause.tsx";
|
||||||
|
import Play from "../../components/Icons/Play.tsx";
|
||||||
|
import Next from "../../components/Icons/Next.tsx";
|
||||||
|
import Replay from "../../components/Icons/Replay.tsx";
|
||||||
|
import Stop from "../../components/Icons/Stop.tsx";
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
// 1. State management
|
// 1. State management
|
||||||
@@ -238,34 +243,39 @@ function ControlPanel({
|
|||||||
<h3>Experiment Controls</h3>
|
<h3>Experiment Controls</h3>
|
||||||
<div className={styles.controlsButtons}>
|
<div className={styles.controlsButtons}>
|
||||||
<button
|
<button
|
||||||
|
aria-label="Pause"
|
||||||
className={!isPlaying ? styles.pausePlayActive : styles.pausePlayInactive}
|
className={!isPlaying ? styles.pausePlayActive : styles.pausePlayInactive}
|
||||||
onClick={() => onAction("pause")}
|
onClick={() => onAction("pause")}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
>❚❚</button>
|
><Pause fill={"white"} /></button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
aria-label="Play"
|
||||||
className={isPlaying ? styles.pausePlayActive : styles.pausePlayInactive}
|
className={isPlaying ? styles.pausePlayActive : styles.pausePlayInactive}
|
||||||
onClick={() => onAction("play")}
|
onClick={() => onAction("play")}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
>▶</button>
|
><Play fill={"white"} /></button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
aria-label="Next phase"
|
||||||
className={styles.next}
|
className={styles.next}
|
||||||
onClick={() => onAction("nextPhase")}
|
onClick={() => onAction("nextPhase")}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
>⏭</button>
|
><Next fill={"white"} /></button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
aria-label="Reset experiment"
|
||||||
className={styles.restartExperiment}
|
className={styles.restartExperiment}
|
||||||
onClick={onReset}
|
onClick={onReset}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
>⟲</button>
|
><Replay fill={"white"} /></button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
aria-label="Stop"
|
||||||
className={styles.stop}
|
className={styles.stop}
|
||||||
onClick={() => onAction("stop")}
|
onClick={() => onAction("stop")}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
>⏹</button>
|
><Stop fill={"white"} /></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ describe('MonitoringPage', () => {
|
|||||||
describe('Control Buttons', () => {
|
describe('Control Buttons', () => {
|
||||||
test('Pause calls API and updates UI', async () => {
|
test('Pause calls API and updates UI', async () => {
|
||||||
render(<MonitoringPage />);
|
render(<MonitoringPage />);
|
||||||
const pauseBtn = screen.getByText('❚❚');
|
const pauseBtn = screen.getByRole('button', { name: /pause/i });
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
fireEvent.click(pauseBtn);
|
fireEvent.click(pauseBtn);
|
||||||
@@ -134,7 +134,7 @@ describe('MonitoringPage', () => {
|
|||||||
|
|
||||||
test('Play calls API and updates UI', async () => {
|
test('Play calls API and updates UI', async () => {
|
||||||
render(<MonitoringPage />);
|
render(<MonitoringPage />);
|
||||||
const playBtn = screen.getByText('▶');
|
const playBtn = screen.getByRole('button', { name: /play/i });
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
fireEvent.click(playBtn);
|
fireEvent.click(playBtn);
|
||||||
@@ -146,7 +146,7 @@ describe('MonitoringPage', () => {
|
|||||||
test('Next Phase calls API', async () => {
|
test('Next Phase calls API', async () => {
|
||||||
render(<MonitoringPage />);
|
render(<MonitoringPage />);
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
fireEvent.click(screen.getByText('⏭'));
|
fireEvent.click(screen.getByRole('button', { name: /next phase/i }));
|
||||||
});
|
});
|
||||||
expect(MonitoringAPI.nextPhase).toHaveBeenCalled();
|
expect(MonitoringAPI.nextPhase).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
@@ -158,7 +158,7 @@ describe('MonitoringPage', () => {
|
|||||||
(VisProg.graphReducer as jest.Mock).mockReturnValue([{ id: 'new-phase' }]);
|
(VisProg.graphReducer as jest.Mock).mockReturnValue([{ id: 'new-phase' }]);
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
fireEvent.click(screen.getByText('⟲'));
|
fireEvent.click(screen.getByRole('button', { name: /reset experiment/i }));
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(VisProg.graphReducer).toHaveBeenCalled();
|
expect(VisProg.graphReducer).toHaveBeenCalled();
|
||||||
@@ -172,7 +172,7 @@ describe('MonitoringPage', () => {
|
|||||||
|
|
||||||
render(<MonitoringPage />);
|
render(<MonitoringPage />);
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
fireEvent.click(screen.getByText('⟲'));
|
fireEvent.click(screen.getByRole('button', { name: /reset experiment/i }));
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(consoleSpy).toHaveBeenCalledWith('Failed to reset program:', expect.any(Error));
|
expect(consoleSpy).toHaveBeenCalledWith('Failed to reset program:', expect.any(Error));
|
||||||
|
|||||||
Reference in New Issue
Block a user