From cf933bbfd59833f35f758302f39546e25cb9e7f1 Mon Sep 17 00:00:00 2001 From: Daniel Rafailov Date: Mon, 12 Oct 2026 10:03:52 -0400 Subject: [PATCH 1/7] converted Sidebar.js to Sidebar.tsx --- .../graph/{Sidebar.js => Sidebar.tsx} | 47 ++++++++++--------- 1 file changed, 24 insertions(+), 23 deletions(-) rename js/components/graph/{Sidebar.js => Sidebar.tsx} (85%) diff --git a/js/components/graph/Sidebar.js b/js/components/graph/Sidebar.tsx similarity index 85% rename from js/components/graph/Sidebar.js rename to js/components/graph/Sidebar.tsx index f73b52343..cb90f8846 100644 --- a/js/components/graph/Sidebar.js +++ b/js/components/graph/Sidebar.tsx @@ -1,7 +1,20 @@ -import React from "react" -import PropTypes from "prop-types" +import { useState } from "react" import Button from "./Button" +type Course = [courseId: string, courseLabel: string] + +interface SidebarProps { + fceCount: number + reset: () => void + activeCourses: Set + courses: Course[] + courseClick: (courseId: string | null) => void + xClick: (courseId: string | null) => void + sidebarItemClick?: () => void + onHover: () => void + onMouseLeave?: () => void +} + export default function Sidebar({ fceCount, reset, @@ -12,15 +25,15 @@ export default function Sidebar({ sidebarItemClick, onHover, onMouseLeave, -}) { - const [collapsed, setCollapsed] = React.useState(true) - const [results, setResults] = React.useState([]) +}: SidebarProps) { + const [collapsed, setCollapsed] = useState(true) + const [results, setResults] = useState([]) const toggleSidebar = () => { setCollapsed(!collapsed) } - const filteredSearch = query => { + const filteredSearch = (query: string) => { if (!query || !courses) { return } @@ -41,9 +54,9 @@ export default function Sidebar({ * If an invalid course label is supplied, `null` will be returned. * e.g. "CSC263/265" will resolve to "csc263265" * "CSC999" will resolve to `null` - * @return {string} course node id + * @return course node id */ - const courseIdFromLabel = courseLabel => { + const courseIdFromLabel = (courseLabel: string) => { for (let i = 0; i < courses.length; i++) { if (courses[i][1] === courseLabel) { return courses[i][0] @@ -55,7 +68,7 @@ export default function Sidebar({ // Sidebar rendering methods /** * Render the FCE counter above the sidebar on the left side. - * @return {HTMLDivElement} FCE to the DOM + * @return FCE to the DOM */ const renderFCE = () => { const fceString = Number.isInteger(fceCount) ? fceCount + ".0" : fceCount @@ -69,7 +82,7 @@ export default function Sidebar({ /** * Render the dropdown results within the sidebar dropdown. - * @return {HTMLDivElement} Searchbar to the DOM + * @return Searchbar to the DOM */ const renderDropdown = () => { if (courses) { @@ -97,7 +110,7 @@ export default function Sidebar({ /** * Render courses that are in the sidebar. - * @return {HTMLBodyElement} list of div's for each course that is active + * @return list of div's for each course that is active */ const renderActiveCourses = () => { const temp = activeCourses ? [...activeCourses] : [] @@ -173,15 +186,3 @@ export default function Sidebar({ ) } - -Sidebar.propTypes = { - fceCount: PropTypes.number, - reset: PropTypes.func, - activeCourses: PropTypes.instanceOf(Set), - courses: PropTypes.array, - courseClick: PropTypes.func, - xClick: PropTypes.func, - sidebarItemClick: PropTypes.func, - onHover: PropTypes.func, - onMouseLeave: PropTypes.func, -} From d7e4a3da6ec2d09f3f137cfd2c54f474c7d5311c Mon Sep 17 00:00:00 2001 From: Daniel Rafailov Date: Mon, 12 Oct 2026 10:18:54 -0400 Subject: [PATCH 2/7] made prop functions optional --- js/components/graph/Sidebar.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/js/components/graph/Sidebar.tsx b/js/components/graph/Sidebar.tsx index cb90f8846..72a7facbc 100644 --- a/js/components/graph/Sidebar.tsx +++ b/js/components/graph/Sidebar.tsx @@ -5,13 +5,13 @@ type Course = [courseId: string, courseLabel: string] interface SidebarProps { fceCount: number - reset: () => void + reset?: () => void activeCourses: Set courses: Course[] - courseClick: (courseId: string | null) => void - xClick: (courseId: string | null) => void + courseClick?: (courseId: string | null) => void + xClick?: (courseId: string | null) => void sidebarItemClick?: () => void - onHover: () => void + onHover?: () => void onMouseLeave?: () => void } @@ -124,14 +124,14 @@ export default function Sidebar({ key={`active ${course}`} data-testid={`test ${course}`} onClick={() => { - courseClick(courseIdFromLabel(course)) + courseClick?.(courseIdFromLabel(course)) }} className="course-selection" onMouseEnter={onHover} onMouseLeave={onMouseLeave} > {course} - From c1ace3b8e9dd5e8c462d83c32e068fb382a3d56f Mon Sep 17 00:00:00 2001 From: Daniel Rafailov Date: Mon, 12 Oct 2026 10:44:53 -0400 Subject: [PATCH 3/7] converted FocusBar.js to FocusBar.tsx --- .../graph/{FocusBar.js => FocusBar.tsx} | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) rename js/components/graph/{FocusBar.js => FocusBar.tsx} (82%) diff --git a/js/components/graph/FocusBar.js b/js/components/graph/FocusBar.tsx similarity index 82% rename from js/components/graph/FocusBar.js rename to js/components/graph/FocusBar.tsx index 6a9038954..ccec588dd 100644 --- a/js/components/graph/FocusBar.js +++ b/js/components/graph/FocusBar.tsx @@ -1,7 +1,12 @@ -import React from "react" -import PropTypes from "prop-types" +import { useState } from "react" import FocusTab from "./FocusTab" +interface FocusBarProps { + focusBarEnabled: boolean + highlightFocus: (id: string) => void + currFocus: string | null +} + // These lists are in reverse order to what ends up appearing on the screen const computerScienceFocusLabels = [ ["ASFOC1689J", "Web Technologies"], @@ -18,8 +23,12 @@ const computerScienceFocusLabels = [ /** * React component representing the focus menu bar */ -export default function FocusBar({ focusBarEnabled, highlightFocus, currFocus }) { - const [open, setOpen] = React.useState(false) +export default function FocusBar({ + focusBarEnabled, + highlightFocus, + currFocus, +}: FocusBarProps) { + const [open, setOpen] = useState(false) /** * Changes whether the focus bar is open or not @@ -61,9 +70,3 @@ export default function FocusBar({ focusBarEnabled, highlightFocus, currFocus }) ) } - -FocusBar.propTypes = { - focusBarEnabled: PropTypes.bool, - highlightFocus: PropTypes.func, - currFocus: PropTypes.string, -} From 025db6fd34a9ceaca3b77ee2c7b14a8176308383 Mon Sep 17 00:00:00 2001 From: Daniel Rafailov Date: Mon, 12 Oct 2026 12:35:07 -0400 Subject: [PATCH 4/7] converted NavBar.js.jsx to NavBar.tsx --- .../common/{NavBar.js.jsx => NavBar.tsx} | 32 ++++++++++++------- js/components/common/__tests__/NavBar.test.js | 2 +- js/components/generate/GenerateForm.js | 2 +- js/components/graph/Container.js | 2 +- js/components/graph/GraphDropdown.tsx | 8 ++--- js/components/graph/types.ts | 5 +++ js/components/grid/grid.tsx | 2 +- 7 files changed, 32 insertions(+), 21 deletions(-) rename js/components/common/{NavBar.js.jsx => NavBar.tsx} (79%) diff --git a/js/components/common/NavBar.js.jsx b/js/components/common/NavBar.tsx similarity index 79% rename from js/components/common/NavBar.js.jsx rename to js/components/common/NavBar.tsx index 788e43321..c7d24dcfb 100644 --- a/js/components/common/NavBar.js.jsx +++ b/js/components/common/NavBar.tsx @@ -1,16 +1,29 @@ -import React from "react" +import { useState } from "react" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" import { faDownload } from "@fortawesome/free-solid-svg-icons" import { Tooltip } from "react-tooltip" import GraphDropdown from "../graph/GraphDropdown" +import { Graph } from "../graph/types" +interface NavBarProps { + selected_page: string + open_modal: (() => void) | undefined + graphs?: Graph[] + updateGraph?: (graphName: string) => void +} /** * NavBar component. */ -export function NavBar({ selected_page, open_modal, graphs = [], updateGraph }) { - const isActive = page => (page === selected_page ? "selected-page" : undefined) - const [showGraphDropdown, setShowGraphDropdown] = React.useState(false) - const [dropdownTimeouts, setDropdownTimeouts] = React.useState([]) +export function NavBar({ + selected_page, + open_modal, + graphs = [], + updateGraph, +}: NavBarProps) { + const isActive = (page: string) => + page === selected_page ? "selected-page" : undefined + const [showGraphDropdown, setShowGraphDropdown] = useState(false) + const [dropdownTimeouts, setDropdownTimeouts] = useState([]) const clearDropdownTimeouts = () => { dropdownTimeouts.forEach(timeout => clearTimeout(timeout)) @@ -56,7 +69,7 @@ export function NavBar({ selected_page, open_modal, graphs = [], updateGraph }) {selected_page === "graph" && ( - + )} diff --git a/js/components/common/__tests__/NavBar.test.js b/js/components/common/__tests__/NavBar.test.js index 7ba136438..b7232ecda 100644 --- a/js/components/common/__tests__/NavBar.test.js +++ b/js/components/common/__tests__/NavBar.test.js @@ -1,6 +1,6 @@ import React from "react" import { render, screen, cleanup } from "@testing-library/react" -import { NavBar } from "../NavBar.js.jsx" +import { NavBar } from "../NavBar" import userEvent from "@testing-library/user-event" describe("test that navbar logo and items render correctly", () => { diff --git a/js/components/generate/GenerateForm.js b/js/components/generate/GenerateForm.js index 274e884de..0ecce4148 100644 --- a/js/components/generate/GenerateForm.js +++ b/js/components/generate/GenerateForm.js @@ -5,7 +5,7 @@ import { faWandSparkles } from "@fortawesome/free-solid-svg-icons" import { Tooltip } from "react-tooltip" import { Graph, populateHybridRelatives } from "../graph/Graph" import Disclaimer from "../common/Disclaimer" -import { NavBar } from "../common/NavBar.js.jsx" +import { NavBar } from "../common/NavBar" import AutocompleteDropdown from "./AutocompleteDropdown" export default class GenerateForm extends React.Component { diff --git a/js/components/graph/Container.js b/js/components/graph/Container.js index 01b28bac4..3243a4705 100644 --- a/js/components/graph/Container.js +++ b/js/components/graph/Container.js @@ -3,7 +3,7 @@ import PropTypes from "prop-types" import Disclaimer from "../common/Disclaimer" import { Graph } from "./Graph" import FocusBar from "./FocusBar" -import { NavBar } from "../common/NavBar.js.jsx" +import { NavBar } from "../common/NavBar" import { ExportModal } from "../common/export" import GraphFallback from "./GraphFallback" import { ErrorBoundary } from "react-error-boundary" diff --git a/js/components/graph/GraphDropdown.tsx b/js/components/graph/GraphDropdown.tsx index 0ca89b8bd..943f74829 100644 --- a/js/components/graph/GraphDropdown.tsx +++ b/js/components/graph/GraphDropdown.tsx @@ -1,13 +1,11 @@ -interface Graph { - title: string -} +import { Graph } from "./types" interface GraphDropdownProps { showGraphDropdown?: boolean onMouseEnter?: () => void onMouseLeave?: () => void graphs?: Graph[] - updateGraph: (title: string) => void + updateGraph?: (graphName: string) => void } export default function GraphDropdown({ @@ -32,7 +30,7 @@ export default function GraphDropdown({
  • updateGraph(graph.title)} + onClick={() => updateGraph?.(graph.title)} data-testid={"test-graph-" + i} > {graph.title} diff --git a/js/components/graph/types.ts b/js/components/graph/types.ts index a6bcdf097..3fafe0fa7 100644 --- a/js/components/graph/types.ts +++ b/js/components/graph/types.ts @@ -1,3 +1,8 @@ +/** Basic metadata for a course graph option. */ +export interface Graph { + title: string +} + /** A single line of text rendered inside a node/bool shape. */ export interface GraphTextTag { pos: [number, number] diff --git a/js/components/grid/grid.tsx b/js/components/grid/grid.tsx index f76fa8f59..19961a69a 100644 --- a/js/components/grid/grid.tsx +++ b/js/components/grid/grid.tsx @@ -11,7 +11,7 @@ import { ModuleRegistry, provideGlobalGridOptions, } from "ag-grid-community" -import { NavBar } from "../common/NavBar.js.jsx" +import { NavBar } from "../common/NavBar" // Register all community features ModuleRegistry.registerModules([AllCommunityModule]) From cb1f266685a20506a8438267a408b4ca6dfd87dc Mon Sep 17 00:00:00 2001 From: Daniel Rafailov Date: Mon, 12 Oct 2026 12:59:37 -0400 Subject: [PATCH 5/7] changed open_modal to explicit optional type --- js/components/common/NavBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/components/common/NavBar.tsx b/js/components/common/NavBar.tsx index c7d24dcfb..f1d26657b 100644 --- a/js/components/common/NavBar.tsx +++ b/js/components/common/NavBar.tsx @@ -7,7 +7,7 @@ import { Graph } from "../graph/types" interface NavBarProps { selected_page: string - open_modal: (() => void) | undefined + open_modal?: () => void graphs?: Graph[] updateGraph?: (graphName: string) => void } From ada157b8df444451ff65198bc05e7b9ce95d7859 Mon Sep 17 00:00:00 2001 From: Daniel Rafailov Date: Mon, 12 Oct 2026 13:04:24 -0400 Subject: [PATCH 6/7] Added changes to Changelog file --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02ebedcb4..6a015a3ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ ### 🔧 Internal changes +- Migrated `Sidebar.js`, `FocusBar.js`, and `NavBar.js.jsx` to TypeScript - Removed `Time'` data type in favour of `(MeetingId -> Times)` function type in `Database.Tables` - Refactored `Database.Tables.MeetTime'` into `Models.Meeting` and renamed it to `MeetingData` - Refactored `Database.Tables.Time` into `Models.Time` and renamed it to `TimeData` @@ -17,7 +18,7 @@ - Added a test case for the getGraphJSON function in `Controllers/Graph` - Migrated JS package manager from yarn to pnpm - Updated `react-leaflet` to v5.0.0 and `@eslint/compat` to v2.1.0 -- Added peer dependency overrides for `eslint-plugin-react` (for `eslint` v10) and `@babel/plugin-syntax-*` (for `@babel/core` v8.0.1 +- Added peer dependency overrides for `eslint-plugin-react` (for `eslint` v10) and `@babel/plugin-syntax-*` (for `@babel/core` v8.0.1) - Set `NODE_ENV=production` for webpack `build` command - Fixed dynamic graph centering bug by updating `parseTransform` in `app/Svg/Parser.hs` to parse multiple transform functions and removing `getShapesMinXY` in `js/components/graph/Graph.js` - Switched CI provider from CircleCI to GitHub Actions From e80275bc481da78f3884c98509f43eb9c2774295 Mon Sep 17 00:00:00 2001 From: Daniel Rafailov Date: Wed, 14 Oct 2026 09:44:57 -0400 Subject: [PATCH 7/7] Added return type annotations for all functions in Sidebar.tsx --- js/components/graph/Sidebar.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/js/components/graph/Sidebar.tsx b/js/components/graph/Sidebar.tsx index 72a7facbc..73280da60 100644 --- a/js/components/graph/Sidebar.tsx +++ b/js/components/graph/Sidebar.tsx @@ -25,15 +25,15 @@ export default function Sidebar({ sidebarItemClick, onHover, onMouseLeave, -}: SidebarProps) { +}: SidebarProps): React.JSX.Element { const [collapsed, setCollapsed] = useState(true) const [results, setResults] = useState([]) - const toggleSidebar = () => { + const toggleSidebar = (): void => { setCollapsed(!collapsed) } - const filteredSearch = (query: string) => { + const filteredSearch = (query: string): Course[] | undefined => { if (!query || !courses) { return } @@ -56,7 +56,7 @@ export default function Sidebar({ * "CSC999" will resolve to `null` * @return course node id */ - const courseIdFromLabel = (courseLabel: string) => { + const courseIdFromLabel = (courseLabel: string): string | null => { for (let i = 0; i < courses.length; i++) { if (courses[i][1] === courseLabel) { return courses[i][0] @@ -70,7 +70,7 @@ export default function Sidebar({ * Render the FCE counter above the sidebar on the left side. * @return FCE to the DOM */ - const renderFCE = () => { + const renderFCE = (): React.JSX.Element => { const fceString = Number.isInteger(fceCount) ? fceCount + ".0" : fceCount return ( @@ -84,7 +84,7 @@ export default function Sidebar({ * Render the dropdown results within the sidebar dropdown. * @return Searchbar to the DOM */ - const renderDropdown = () => { + const renderDropdown = (): React.JSX.Element | undefined => { if (courses) { const showDropdown = results ? "" : "hidden" const masterDropdown = `${showDropdown} search-dropdown` @@ -112,7 +112,7 @@ export default function Sidebar({ * Render courses that are in the sidebar. * @return list of div's for each course that is active */ - const renderActiveCourses = () => { + const renderActiveCourses = (): React.JSX.Element => { const temp = activeCourses ? [...activeCourses] : [] // sort the list of rendered courses, alphabetically temp.sort((a, b) => a.localeCompare(b))