Skip to content

Commit 5c209f8

Browse files
authored
Merge pull request #80 from MelanieH7/2024_sem2
FEAT: Menu Page, and updating url creation function Authored by: HumanGPT @MinniMinMin, @MaryHuang8, @MaryHuang8, @KittenPrincess1, @Guhh0001
2 parents 6a30599 + 8212f90 commit 5c209f8

36 files changed

+1645
-301
lines changed

.github/workflows/url.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: URL Navigation and Parameter Input CI
2+
3+
on:
4+
push:
5+
branches:
6+
- 2024_sem2
7+
pull_request:
8+
branches:
9+
- 2024_sem2
10+
11+
jobs:
12+
build:
13+
runs-on: ubuntu-latest
14+
15+
- name: Checkout 🛎️
16+
uses: actions/[email protected]
17+
with:
18+
persist-credentials: false
19+
20+
- name: Install dependencies
21+
run:
22+
npm install
23+
npm install jest supertest --save-dev
24+
25+
- name: Run tests 🔧
26+
run:
27+
npm start
28+
npm test-url

docs/Colour-Guidelines-20241011.pdf

347 KB
Binary file not shown.

docs/Homepage-Guidelines-20241020.pdf

188 KB
Binary file not shown.
Binary file not shown.
Binary file not shown.

package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
"start:win32": "SET NODE_OPTIONS=--openssl-legacy-provider && react-scripts start",
1616
"test": "react-scripts test",
1717
"test-uf": "npm test -- ./src/algorithms/controllers/unionFind.test.js",
18-
"test-234t": "npm test -- ./src/algorithms/controllers/TTFTree.test.js"
18+
"test-234t": "npm test -- ./src/algorithms/controllers/TTFTree.test.js",
19+
"test-url": "npm test -- ./src/algorithms/parameters/helpers/urlHelpers.test.js"
1920
},
2021
"husky": {
2122
"hooks": {
@@ -62,6 +63,7 @@
6263
"@mui/styles": "^5.14.4",
6364
"@testing-library/jest-dom": "^4.2.4",
6465
"@testing-library/react": "^12.1.2",
66+
"@testing-library/react-hooks": "^8.0.1",
6567
"@testing-library/user-event": "^14.4.3",
6668
"denque": "^2.0.1",
6769
"framer-motion": "^4.0.0",
@@ -90,9 +92,11 @@
9092
"eslint-plugin-jsx-a11y": "^6.7.1",
9193
"eslint-plugin-react": "^7.33.2",
9294
"husky": "^4.2.5",
95+
"jest": "^27.5.1",
9396
"lint-staged": "^10.1.3",
9497
"react": "^17.0.2",
9598
"react-test-renderer": "^17.0.2",
96-
"run-script-os": "^1.1.6"
99+
"run-script-os": "^1.1.6",
100+
"supertest": "^7.0.0"
97101
}
98102
}

src/App.js

Lines changed: 26 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@
22
import React, { useEffect, useState } from 'react';
33
import './styles/App.scss';
44
import Header from './components/top/Header';
5+
import AlgorithmMenu from './components/AlgorithmMenu';
56
import { ReactComponent as Circle } from './assets/icons/circle.svg';
67
import { ReactComponent as Direction } from './assets/icons/direction.svg';
78
import { GlobalProvider } from './context/GlobalState';
8-
import RightPanel from './components/right-panel';
99
import LeftPanel from './components/left-panel';
10+
import RightPanel from './components/right-panel';
1011
import MidPanel from './components/mid-panel';
1112
import ControlPanel from './components/mid-panel/ControlPanel';
1213
import Settings from './components/top/Settings';
1314
import {
14-
resizeWindow, startRightDrag, startBottomDrag, endDrag, onDrag, collapseLeftDrag, collapseBottomDrag, collapseRightDrag, addEvent,
15+
resizeWindow, startLeftDrag, startRightDrag, startBottomDrag, endDrag, onDrag, collapseLeftDrag, collapseBottomDrag, collapseRightDrag, addEvent,
1516
} from './BorderResize';
1617
import {
1718
setTheme,
@@ -23,35 +24,29 @@ import {
2324
SYSTEM_THEME_KEY,
2425
} from './components/top/helper';
2526

26-
2727
const DEFAULT_FONT_INCREMENT = 0;
28-
const LEFT_FONT_SIZE = 13;
28+
const LEFT_FONT_SIZE = 15;
2929
const MID_FONT_SIZE = 15;
3030
const RIGHT_FONT_SIZE = 15;
3131

3232
function App() {
3333
useEffect(() => {
34-
window.addEventListener('resize', (event) => {
35-
resizeWindow(event);
36-
});
37-
// eslint-disable-next-line no-unused-vars
38-
return (_) => { window.removeEventListener('resize', resizeWindow); };
39-
});
40-
41-
// add mouseout event listener to 'document' when App first mount
34+
window.addEventListener('resize', resizeWindow);
35+
return () => {
36+
window.removeEventListener('resize', resizeWindow);
37+
};
38+
}, []);
39+
4240
useEffect(() => {
4341
const mouseOutCallback = (e) => {
44-
// e = e || window.event;
4542
const from = e.relatedTarget || e.toElement;
4643
if (!from || from.nodeName === 'HTML') {
47-
// End dragging when mouse out of html
4844
endDrag();
4945
}
5046
};
5147

52-
addEvent(document, 'mouseout', mouseOutCallback);
48+
document.addEventListener('mouseout', mouseOutCallback);
5349

54-
// do not forget to remove event listener when the App component unmount
5550
return () => {
5651
document.removeEventListener('mouseout', mouseOutCallback);
5752
};
@@ -68,7 +63,6 @@ function App() {
6863
setFontSizeIncrease(fontSizeIncrease + val);
6964
};
7065

71-
7266
const initAlgoColor = () => {
7367
const algoTheme = getWithExpiry(ALGO_THEME_KEY);
7468
if (algoTheme === null) {
@@ -84,14 +78,12 @@ function App() {
8478
setAlgoTheme(id);
8579
};
8680

87-
8881
const initSystemColor = () => {
8982
const theme = getWithExpiry(SYSTEM_THEME_KEY);
9083
if (theme === null) {
9184
setTheme(getSystemColorMode());
9285
return getSystemColorMode();
9386
}
94-
9587
return theme;
9688
};
9789

@@ -102,14 +94,15 @@ function App() {
10294
};
10395

10496
useEffect(() => {
105-
setTheme(getWithExpiry(SYSTEM_THEME_KEY));
97+
const theme = getWithExpiry(SYSTEM_THEME_KEY);
98+
setTheme(theme);
10699
setAlgoTheme(getWithExpiry(ALGO_THEME_KEY));
107-
});
100+
document.documentElement.setAttribute('data-theme', theme === 'dark' ? 'dark' : 'light');
101+
}, []);
108102

109103
return (
110-
111104
<GlobalProvider>
112-
{ isSettingVisible ? (
105+
{isSettingVisible ? (
113106
<Settings
114107
onFontIncrease={onFontIncrease}
115108
onSetting={onSetting}
@@ -127,26 +120,31 @@ function App() {
127120
tabIndex="-1"
128121
onMouseMove={(event) => onDrag(event)}
129122
>
130-
<div id="header">
131-
<Header onSetting={onSetting} />
123+
<div id="header" className="header-container">
124+
<div className="header-left">
125+
<AlgorithmMenu />
126+
</div>
127+
<div className="header-right">
128+
<Header onSetting={onSetting} />
129+
</div>
132130
</div>
133131
<div id="leftcol">
134132
<LeftPanel
135133
fontSize={LEFT_FONT_SIZE}
136134
fontSizeIncrement={fontSizeIncrease}
137135
/>
138136
</div>
139-
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events */}
140137
<div
141138
id="leftdragbar"
142139
tabIndex="-1"
143140
aria-label="Move left drag bar"
144-
onClick={collapseLeftDrag}
141+
onDoubleClick={collapseLeftDrag}
142+
onMouseDown={startLeftDrag}
145143
role="button"
146144
className="dragbar"
147145
>
148146
<div id="draghandle" className="handle">
149-
<Direction id="leftdraghandle"/>
147+
<Direction id="leftdraghandle" />
150148
</div>
151149
</div>
152150
<div id="tabpages">
@@ -199,5 +197,4 @@ function App() {
199197
);
200198
}
201199

202-
203200
export default App;

src/algorithms/parameters/HSParam.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { withAlgorithmParams } from './helpers/urlHelpers' // Import this for UR
1010
import { URLContext } from '../../context/urlState.js';
1111
import '../../styles/Param.scss';
1212

13-
13+
// export const DEFAULT_NODES = genRandNumList(12, 1, 50);
1414
const DEFAULT_NODES = genRandNumList(10, 1, 100);
1515
const HEAP_SORT = 'Heap Sort';
1616
const HEAP_SORT_EXAMPLE = 'Please follow the example provided: 0,1,2,3,4';
@@ -20,13 +20,13 @@ function HeapsortParam({ list }) { // add the parsing parameters for your algori
2020
// const { alg, mode, param } = useUrlParams();
2121
// const {list, value, xyCoords, edgeWeights, start, end, string, pattern, union} = parseParam(param);
2222
// const { alg, mode, list } = withAlgorithmParams(HeapsortParam);
23-
const DEFAULT_NODES = genRandNumList.bind(null, 12, 1, 50); // Define the default list of nodes
23+
// const DEFAULT_NODES = genRandNumList.bind(null, 12, 1, 50); // Define the default list of nodes
2424
const [localNodes, setLocalNodes] = useState(list || DEFAULT_NODES);
2525
const [message, setMessage] = useState(null);
2626
const { setNodes } = useContext(URLContext);
2727

2828
useEffect(() => {
29-
setNodes(localNodes);
29+
setNodes(localNodes);
3030
}, [localNodes]);
3131

3232
return (

src/algorithms/parameters/helpers/EuclideanMatrixParams.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,7 @@ function EuclideanMatrixParams({
436436
// components/DataStructures/Graph/GraphRenderer/index.js) so
437437
// coordinates here can be updated
438438
const moveNode = (nodeID, x, y) => {
439+
console.log(['moveNode', nodeID, x, y]);
439440
const newData1 = data1.map((row, index) => {
440441
if (index === nodeID) {
441442
return {

src/algorithms/parameters/helpers/urlHelpers.js

Lines changed: 48 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import DEFAULT_NODES from '../../../algorithms/parameters/HSParam.js';
21
import React, { useState, useEffect, useMemo } from 'react';
32
import algorithms from '../../../algorithms';
43

@@ -18,21 +17,31 @@ import algorithms from '../../../algorithms';
1817
// const DEFAULT_MIN = '1'
1918
// const DEFAULT_MAX = '10'
2019

21-
const DEFAULT_ALGORITHM = 'heapSort';
22-
const DEFAULT_MODE = 'sort';
23-
const DEFAULT_LIST = '';
24-
const DEFAULT_VALUE = '';
25-
const DEFAULT_XY_COORDS = '';
26-
const DEFAULT_EDGE_WEIGHTS = '';
27-
const DEFAULT_SIZE = '';
28-
const DEFAULT_START = '';
29-
const DEFAULT_END = '';
30-
const DEFAULT_STRING = '';
31-
const DEFAULT_PATTERN = '';
32-
const DEFAULT_UNION = '';
33-
const DEFAULT_HEURISTIC = '';
34-
const DEFAULT_MIN = '';
35-
const DEFAULT_MAX = '';
20+
// List of valid parameter names
21+
const VALID_PARAM_NAMES = [
22+
'alg', 'mode', 'list', 'value', 'xyCoords', 'edgeWeights',
23+
'size', 'start', 'end', 'string', 'pattern', 'union',
24+
'heuristic', 'min', 'max'
25+
];
26+
27+
// Default values for each parameter
28+
const DEFAULT_VALUES = {
29+
alg: 'heapSort',
30+
mode: 'sort',
31+
list: '',
32+
value: '',
33+
xyCoords: '',
34+
edgeWeights: '',
35+
size: '',
36+
start: '',
37+
end: '',
38+
string: '',
39+
pattern: '',
40+
union: '',
41+
heuristic: '',
42+
min: '',
43+
max: ''
44+
};
3645

3746

3847
export function useUrlParams() {
@@ -50,39 +59,30 @@ export function useUrlParams() {
5059
}, []);
5160

5261
const urlParams = useMemo(() => new URLSearchParams(search), [search]);
53-
const alg = urlParams.get('alg') || DEFAULT_ALGORITHM; // Default algorithm
54-
const mode = urlParams.get('mode') || DEFAULT_MODE; // Default mode
55-
56-
// Helper function to handle both missing and 'null' values
57-
const getParamOrDefault = (param, defaultValue) => {
58-
return param === null || param === "null" ? defaultValue : param;
59-
};
60-
61-
// Parse individual parameters directly from URL
62-
const list = getParamOrDefault(urlParams.get('list'), DEFAULT_LIST);
63-
const value = getParamOrDefault(urlParams.get('value'), DEFAULT_VALUE);
64-
const xyCoords = getParamOrDefault(urlParams.get('xyCoords'), DEFAULT_XY_COORDS);
65-
const edgeWeights = getParamOrDefault(urlParams.get('edgeWeights'), DEFAULT_EDGE_WEIGHTS);
66-
const size = getParamOrDefault(urlParams.get('size'), DEFAULT_SIZE);
67-
const start = getParamOrDefault(urlParams.get('start'), DEFAULT_START);
68-
const end = getParamOrDefault(urlParams.get('end'), DEFAULT_END);
69-
const string = urlParams.get('string') || DEFAULT_STRING
70-
const pattern = urlParams.get('pattern') || DEFAULT_PATTERN;
71-
const union = getParamOrDefault(urlParams.get('union'), DEFAULT_UNION);
72-
const heuristic = getParamOrDefault(urlParams.get('heuristic'), DEFAULT_HEURISTIC);
73-
const min = getParamOrDefault(urlParams.get('min'), DEFAULT_MIN);
74-
const max = getParamOrDefault(urlParams.get('max'), DEFAULT_MAX);
75-
76-
77-
console.log("Raw URL alg:", urlParams.get('alg'));
78-
console.log("Raw URL mode:", urlParams.get('mode'));
79-
console.log("Parsed URL Params:", { list, value, xyCoords, edgeWeights, size, start, end, string, pattern, union, heuristic, min, max });
62+
const params = {};
63+
64+
// Filter and parse valid URL parameters
65+
VALID_PARAM_NAMES.forEach((name) => {
66+
const value = urlParams.get(name);
67+
params[name] = value !== null ? value : DEFAULT_VALUES[name];
68+
});
69+
70+
// Log a warning if there are any invalid parameters in the URL
71+
urlParams.forEach((_, key) => {
72+
if (!VALID_PARAM_NAMES.includes(key)) {
73+
console.warn(`Invalid URL parameter ignored: ${key}`);
74+
}
75+
});
8076

81-
return { alg, mode, list, value, xyCoords, edgeWeights, size, start, end, string, pattern, union, heuristic, min, max };
77+
// console.log("Raw URL alg:", urlParams.get('alg'));
78+
// console.log("Raw URL mode:", urlParams.get('mode'));
79+
// console.log("Parsed URL Params:", { list, value, xyCoords, edgeWeights, size, start, end, string, pattern, union, heuristic, min, max });
8280

81+
return params;
8382
}
8483

8584

85+
8686
function extractValue(paramString, key) {
8787
const regex = new RegExp(`${key}=([^;]*)`);
8888
const match = paramString.match(regex);
@@ -98,6 +98,10 @@ export const withAlgorithmParams = (WrappedComponent) => {
9898
return <div>Invalid algorithm specified</div>;
9999
}
100100

101+
if (!mode || !(mode in algorithms[alg].pseudocode)) {
102+
return <div>Invalid mode specified</div>;
103+
}
104+
101105
return <WrappedComponent
102106
alg={alg}
103107
mode={mode}

0 commit comments

Comments
 (0)