Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

### 🔧 Internal changes

- Refactor GraphDropdown component from being a child of Graph to being a child of NavBar

## [0.7.2] - 2025-12-10

### ✨ New features/enhancements
Expand Down
38 changes: 36 additions & 2 deletions js/components/common/NavBar.js.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,32 @@ import React 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"

/**
* NavBar component.
*/
export function NavBar({ selected_page, open_modal }) {
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([])

const clearDropdownTimeouts = () => {
dropdownTimeouts.forEach(timeout => clearTimeout(timeout))
setDropdownTimeouts([])
}

const handleShowGraphDropdown = () => {
clearDropdownTimeouts()
setShowGraphDropdown(true)
}

const handleHideGraphDropdown = () => {
const timeout = setTimeout(() => {
setShowGraphDropdown(false)
}, 500)
setDropdownTimeouts(dropdownTimeouts.concat(timeout))
}

return (
<nav className="row header">
Expand All @@ -26,8 +46,22 @@ export function NavBar({ selected_page, open_modal }) {
{/* Navigation links */}
<div className="nav-middle">
<ul id="nav-links">
<li id="nav-graph" className={isActive("graph")}>
<li
id="nav-graph"
className={`${isActive("graph")} ${graphs.length > 0 ? "show-dropdown-arrow" : ""}`}
onMouseEnter={handleShowGraphDropdown}
onMouseLeave={handleHideGraphDropdown}
>
<a href="/graph">Graph</a>
{selected_page === "graph" && (
<GraphDropdown
showGraphDropdown={showGraphDropdown}
onMouseMove={handleShowGraphDropdown}
onMouseLeave={handleHideGraphDropdown}
graphs={graphs}
updateGraph={updateGraph}
/>
)}
</li>
<li id="nav-grid" className={isActive("grid")}>
<a href="/grid">Grid</a>
Expand Down
2 changes: 1 addition & 1 deletion js/components/graph/Container.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export default class Container extends React.Component {
render() {
return (
<div>
<NavBar selected_page="graph" open_modal={this.openExportModal}></NavBar>
<NavBar selected_page="graph" open_modal={this.openExportModal} graphs={this.state.graphs} updateGraph={this.updateGraph}></NavBar>
<ExportModal
page="graph"
open={this.state.modalOpen}
Expand Down
62 changes: 1 addition & 61 deletions js/components/graph/Graph.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import Edge from "./Edge"
import Node from "./Node"
import Button from "./Button"
import InfoBox from "./InfoBox"
import GraphDropdown from "./GraphDropdown"
import Sidebar from "./Sidebar"
import { parseAnd } from "../../util/util.js"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
Expand All @@ -24,8 +23,7 @@ const ZOOM_ENUM = {
ZOOM_IN: 1,
}
const TIMEOUT_NAMES_ENUM = {
INFOBOX: 0,
DROPDOWN: 1,
INFOBOX: 0
}

export class Graph extends React.Component {
Expand All @@ -44,7 +42,6 @@ export class Graph extends React.Component {
highlightedNodesFocus: [],
highlightedNodesDeps: [],
infoboxTimeouts: [],
dropdownTimeouts: [],
width: window.innerWidth,
height: window.innerHeight,
zoomFactor: 1,
Expand All @@ -68,7 +65,6 @@ export class Graph extends React.Component {
panStartX: 0,
panStartY: 0,
showCourseModal: false,
showGraphDropdown: false,
selectedNodes: new Set(),
}
this.exportModal = React.createRef()
Expand All @@ -83,15 +79,6 @@ export class Graph extends React.Component {
// can't detect keydown event when adding event listener to react-graph
document.body.addEventListener("keydown", this.onKeyDown)

if (document.querySelector("#nav-graph > a")) {
document
.querySelector("#nav-graph > a")
.addEventListener("mouseenter", this.setShowGraphDropdown)
document
.querySelector("#nav-graph > a")
.addEventListener("mouseleave", this.hideGraphDropdown)
}

if (document.querySelector(".sidebar")) {
document
.querySelector(".sidebar")
Expand All @@ -107,23 +94,13 @@ export class Graph extends React.Component {

componentWillUnmount() {
this.state.infoboxTimeouts.forEach(timeout => clearTimeout(timeout))
this.state.dropdownTimeouts.forEach(timeout => clearTimeout(timeout))
document.body.removeEventListener("keydown", this.onKeyDown)

if (document.getElementById("nav-export")) {
document
.getElementById("nav-export")
.removeEventListener("click", this.exportModal.current.openModal)
}

if (document.querySelector("#nav-graph > a")) {
document
.querySelector("#nav-graph > a")
.removeEventListener("mouseenter", this.setShowGraphDropdown)
document
.querySelector("#nav-graph > a")
.removeEventListener("mouseleave", this.hideGraphDropdown)
}
}

getGraph = () => {
Expand Down Expand Up @@ -361,19 +338,6 @@ export class Graph extends React.Component {
}
}

clearAllTimeouts = timeoutName => {
switch (timeoutName) {
case TIMEOUT_NAMES_ENUM.INFOBOX:
this.state.infoboxTimeouts.forEach(timeout => clearTimeout(timeout))
this.setState({ infoboxTimeouts: [] })
break
case TIMEOUT_NAMES_ENUM.DROPDOWN:
this.state.dropdownTimeouts.forEach(timeout => clearTimeout(timeout))
this.setState({ dropdownTimeouts: [] })
break
}
}

/**
* Update the status of the Edge, based on the status of the Node/Bool it points from/to.
*/
Expand Down Expand Up @@ -489,8 +453,6 @@ export class Graph extends React.Component {
const currentNode = this.state.nodesJSON[courseId]
this.focusPrereqs(courseId)

this.clearAllTimeouts(TIMEOUT_NAMES_ENUM.INFOBOX)

let xPos = currentNode.pos[0]
let yPos = currentNode.pos[1]
const rightSide = xPos > 222
Expand Down Expand Up @@ -679,7 +641,6 @@ export class Graph extends React.Component {
}

infoBoxMouseEnter = () => {
this.clearAllTimeouts(TIMEOUT_NAMES_ENUM.INFOBOX)
this.setState({ showInfoBox: true, isMouseOnInfoBox: true })
}

Expand All @@ -702,20 +663,6 @@ export class Graph extends React.Component {
})
}

setShowGraphDropdown = () => {
this.clearAllTimeouts(TIMEOUT_NAMES_ENUM.DROPDOWN)
this.setState({ showGraphDropdown: true })
}

hideGraphDropdown = () => {
const timeout = setTimeout(() => {
this.setState({ showGraphDropdown: false })
}, 500)
this.setState({
dropdownTimeouts: this.state.dropdownTimeouts.concat(timeout),
})
}

onClose = () => {
this.setState({ showCourseModal: false })
}
Expand Down Expand Up @@ -1654,13 +1601,6 @@ export class Graph extends React.Component {
onClose={this.onClose}
/>
<ExportModal context="graph" session="" ref={this.exportModal} />
<GraphDropdown
showGraphDropdown={this.state.showGraphDropdown}
onMouseMove={this.setShowGraphDropdown}
onMouseLeave={this.hideGraphDropdown}
graphs={this.props.graphs}
updateGraph={this.props.updateGraph}
/>
{Object.keys(this.state.nodesJSON).length > 1 && (
<div className="graph-button-group">
<div className="button-group">
Expand Down
35 changes: 8 additions & 27 deletions js/components/graph/GraphDropdown.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,17 @@
import React from "react"
import PropTypes from "prop-types"

export default function GraphDropdown({
showGraphDropdown,
onMouseMove,
onMouseLeave,
graphs = [],
updateGraph,
}) {
let className = "hidden"
let graphTabLeft = 0
if (graphs.length !== 0 && document.querySelector("#nav-graph")) {
const navGraph = document.querySelector("#nav-graph")
if (graphs.length === 0) {
navGraph.classList.remove("show-dropdown-arrow")
} else {
if (!navGraph.classList.contains("show-dropdown-arrow")) {
navGraph.classList.add("show-dropdown-arrow")
}
if (showGraphDropdown) {
graphTabLeft = navGraph.getBoundingClientRect().left
className = "graph-dropdown-display"
}
}
}
export default function GraphDropdown({showGraphDropdown, onMouseEnter, onMouseLeave, graphs = [], updateGraph }) {
const className = showGraphDropdown && graphs.length > 0
? "graph-dropdown-display"
: "hidden"

return (
<ul
className={className}
onMouseMove={onMouseMove}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
data-testid={"test-graph-dropdown"}
style={{ left: graphTabLeft }}
data-testid="test-graph-dropdown"
>
{graphs.map((graph, i) => {
return (
Expand All @@ -54,7 +35,7 @@ GraphDropdown.defaultProps = {

GraphDropdown.propTypes = {
showGraphDropdown: PropTypes.bool,
onMouseMove: PropTypes.func,
onMouseEnter: PropTypes.func,
onMouseLeave: PropTypes.func,
graphs: PropTypes.array,
updateGraph: PropTypes.func,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,5 @@ exports[`GraphDropdown should match snapshot 1`] = `
<ul
class="hidden"
data-testid="test-graph-dropdown"
style="left: 0px;"
/>
`;
8 changes: 8 additions & 0 deletions style/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2204,14 +2204,22 @@ main-filter input {
display: none;
}

#nav-graph {
position: relative;
}

.graph-dropdown-display {
left: 0px;
top: 100%;
z-index: 10;
background-color: #5c497e;
color: #fff;
column-count: 2;
display: inline-block;
font-size: 15px;
list-style: none;
padding: 8px;
min-width: 300px;
position: absolute;
text-align: left;
}
Expand Down