• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CBIIT / crdc-datahub-ui / 11074479132

27 Sep 2024 04:44PM UTC coverage: 44.982% (+26.5%) from 18.435%
11074479132

Pull #479

github

web-flow
Merge a0867d25a into 3d8b55818
Pull Request #479: 3.0.0 Release

1727 of 4418 branches covered (39.09%)

Branch coverage included in aggregate %.

2612 of 5228 relevant lines covered (49.96%)

128.96 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

52.91
/src/components/Header/components/NavbarDesktop.tsx
1
import React, { useEffect, useState, useRef } from "react";
2
import { NavLink, Link, useNavigate, useLocation } from "react-router-dom";
3
import { Button, styled } from "@mui/material";
4
import { useAuthContext } from "../../Contexts/AuthContext";
5
import GenericAlert from "../../GenericAlert";
6
import { navMobileList, navbarSublists } from "../../../config/globalHeaderData";
7
import APITokenDialog from "../../../content/users/APITokenDialog";
8
import UploaderToolDialog from "../../UploaderToolDialog";
9

10
const Nav = styled("div")({
2✔
11
  top: 0,
12
  left: 0,
13
  width: "100%",
14
  background: "#ffffff",
15
  boxShadow: "-0.1px 6px 9px -6px rgba(0, 0, 0, 0.5)",
16
  zIndex: 1100,
17
  position: "relative",
18
  "& .dropdownContainer": {
19
    margin: "0 auto",
20
    position: "relative",
21
    width: "1400px",
22
  },
23
  "& .loggedInName": {
24
    color: "#007BBD",
25
    textAlign: "right",
26
    fontSize: "14px",
27
    fontFamily: "Poppins",
28
    fontStyle: "normal",
29
    fontWeight: 600,
30
    lineHeight: "normal",
31
    letterSpacing: "0.42px",
32
    textDecoration: "none",
33
    textTransform: "uppercase",
34
    padding: "10px 0",
35
    marginBottom: "4.5px",
36
    marginRight: "40px",
37
  },
38
  "& .invisible": {
39
    visibility: "hidden",
40
  },
41
});
42

43
const NavContainer = styled("div")({
2✔
44
  margin: "0 auto",
45
  maxWidth: "1400px",
46
  textAlign: "left",
47
  position: "relative",
48
  display: "flex",
49
  justifyContent: "space-between",
50
  alignItems: "end",
51
  "#navbar-dropdown-name-container": {
52
    margin: 0,
53
  },
54
});
55

56
const UlContainer = styled("ul")({
2✔
57
  listStyle: "none",
58
  margin: 0,
59
  paddingTop: "17px",
60
  paddingLeft: "11px",
61
  display: "flex",
62
  width: "100%",
63
});
64

65
const LiSection = styled("li")({
2✔
66
  display: "inline-block",
67
  position: "relative",
68
  lineHeight: "50px",
69
  letterSpacing: "1px",
70
  textAlign: "center",
71
  transition: "all 0.3s ease-in-out",
72
  "& a": {
73
    color: "#585C65",
74
    textDecoration: "none",
75
  },
76
  "& .displayName": {
77
    color: "#007BBD",
78
    fontSize: "14px",
79
    lineHeight: "20px",
80
    padding: "10px 0",
81
    textAlign: "right",
82
    width: "fit-content",
83
  },
84
  "&.name-dropdown-li": {
85
    marginLeft: "auto",
86
  },
87
  "&.login-button": {
88
    lineHeight: "48px",
89
  },
90
  "& .navTitle": {
91
    display: "block",
92
    color: "#585C65",
93
    fontFamily: "poppins",
94
    fontSize: "17px",
95
    fontWeight: 700,
96
    lineHeight: "40px",
97
    letterSpacing: "normal",
98
    textDecoration: "none",
99
    margin: "0 5px",
100
    padding: "0 8px",
101
    userSelect: "none",
102
    borderTop: "4px solid transparent",
103
    borderLeft: "4px solid transparent",
104
    borderRight: "4px solid transparent",
105
    "&:hover": {
106
      cursor: "pointer",
107
    },
108
  },
109
  "& .navText": {
110
    borderBottom: "4px solid transparent",
111
    width: "fit-content",
112
    margin: "auto",
113
    "&:hover": {
114
      cursor: "pointer",
115
      color: "#3A75BD",
116
      borderBottom: "4px solid #3A75BD",
117
      "&::after": {
118
        content: '""',
119
        display: "inline-block",
120
        width: "6px",
121
        height: "6px",
122
        borderBottom: "1px solid #298085",
123
        borderLeft: "1px solid #298085",
124
        margin: "0 0 4px 8px",
125
        transform: "rotate(-45deg)",
126
        WebkitTransform: "rotate(-45deg)",
127
      },
128
    },
129
    "&::after": {
130
      content: '""',
131
      display: "inline-block",
132
      width: "6px",
133
      height: "6px",
134
      borderBottom: "1px solid #585C65",
135
      borderLeft: "1px solid #585C65",
136
      margin: "0 0 4px 8px",
137
      transform: "rotate(-45deg)",
138
      WebkitTransform: "rotate(-45deg)",
139
    },
140
  },
141
  "& .clicked": {
142
    color: "#FFFFFF",
143
    background: "#1F4671",
144
    "&::after": {
145
      borderTop: "1px solid #FFFFFF",
146
      borderRight: "1px solid #FFFFFF",
147
      borderBottom: "0",
148
      borderLeft: "0",
149
      margin: "0 0 0 8px",
150
    },
151
    "&:hover": {
152
      borderBottom: "4px solid #1F4671",
153
      color: "#FFFFFF",
154
      "&::after": {
155
        content: '""',
156
        display: "inline-block",
157
        width: "6px",
158
        height: "6px",
159
        borderTop: "1px solid #FFFFFF",
160
        borderRight: "1px solid #FFFFFF",
161
        borderBottom: "0",
162
        borderLeft: "0",
163
        margin: "0 0 0 8px",
164
        transform: "rotate(-45deg)",
165
        WebkitTransform: "rotate(-45deg)",
166
      },
167
    },
168
  },
169
  "& .directLink::after": {
170
    display: "none",
171
  },
172
  "& .directLink:hover::after": {
173
    display: "none",
174
  },
175
  "& .shouldBeUnderlined": {
176
    borderBottom: "4px solid #3A75BD !important",
177
  },
178
  "& .navTitleClicked": {
179
    display: "block",
180
    color: "#FFFFFF",
181
    fontFamily: "poppins",
182
    fontSize: "17px",
183
    fontWeight: 700,
184
    lineHeight: "40px",
185
    letterSpacing: "normal",
186
    textDecoration: "none",
187
    margin: "0 5px",
188
    padding: "0 8px",
189
    userSelect: "none",
190
    background: "#1F4671",
191
    borderTop: "4px solid #5786FF",
192
    borderLeft: "4px solid #5786FF",
193
    borderRight: "4px solid #5786FF",
194
  },
195
  "& .invisible": {
196
    visibility: "hidden",
197
  },
198
});
199

200
const Dropdown = styled("div")({
2✔
201
  top: "60.5px",
202
  left: 0,
203
  width: "100%",
204
  background: "#1F4671",
205
  zIndex: 1100,
206
  position: "absolute",
207
});
208

209
const NameDropdownContainer = styled("div")({
2✔
210
  margin: "0 auto",
211
  textAlign: "left",
212
  position: "relative",
213
  maxWidth: "1400px",
214
  "& .dropdownList": {
215
    background: "#1F4671",
216
    display: "inline-flex",
217
    gridTemplateColumns: "repeat(auto-fit, minmax(250px, 1fr))",
218
    padding: "32px 32px 0 32px",
219
  },
220
  "& .dropdownItem": {
221
    padding: "0 10px 52px 10px",
222
    textAlign: "left",
223
    fontFamily: "'Poppins', sans-serif",
224
    fontStyle: "normal",
225
    fontWeight: 600,
226
    fontSize: "20px",
227
    lineHeight: "110%",
228
    color: "#FFFFFF",
229
    textDecoration: "none",
230
    cursor: "pointer",
231
    "&:hover": {
232
      textDecoration: "underline",
233
    },
234
  },
235
  "& .dropdownItemButton": {
236
    paddingBottom: 0,
237
    textTransform: "none",
238
    "&:hover": {
239
      background: "transparent",
240
    },
241
  },
242
  "#navbar-dropdown-item-name-logout": {
243
    maxWidth: "200px",
244
  },
245
});
246

247
const NameDropdown = styled("div")({
2✔
248
  top: "60.5px",
249
  left: 0,
250
  width: "100%",
251
  background: "#1F4671",
252
  zIndex: 1100,
253
  position: "absolute",
254
});
255

256
const StyledLoginLink = styled(Link)({
2✔
257
  color: "#007BBD !important",
258
  textAlign: "right",
259
  fontSize: "14px",
260
  fontFamily: "Poppins",
261
  fontStyle: "normal",
262
  fontWeight: 600,
263
  lineHeight: "normal",
264
  letterSpacing: "0.42px",
265
  textDecoration: "none",
266
  textTransform: "uppercase",
267
  padding: "10px 0",
268
  marginBottom: "4.5px",
269
  marginRight: "32px",
270
});
271

272
const useOutsideAlerter = (ref1, ref2) => {
2✔
273
  useEffect(() => {
4✔
274
    function handleClickOutside(event) {
275
      if (
×
276
        !event.target ||
×
277
        (event.target.getAttribute("class") !== "dropdownList" &&
278
          ref1.current &&
279
          !ref1.current.contains(event.target) &&
280
          ref2.current &&
281
          !ref2.current.contains(event.target))
282
      ) {
283
        const toggle = document.getElementsByClassName("navText clicked");
×
284
        if (toggle[0] && !event.target.getAttribute("class")?.includes("navText clicked")) {
×
285
          const temp: HTMLElement = toggle[0] as HTMLElement;
×
286
          temp.click();
×
287
        }
288
      }
289
    }
290

291
    document.addEventListener("mousedown", handleClickOutside);
4✔
292
    return () => {
4✔
293
      document.removeEventListener("mousedown", handleClickOutside);
4✔
294
    };
295
  }, [ref1, ref2]);
296
};
297

298
const NavBar = () => {
2✔
299
  const [clickedTitle, setClickedTitle] = useState("");
4✔
300
  const [openAPITokenDialog, setOpenAPITokenDialog] = useState<boolean>(false);
4✔
301
  const [uploaderToolOpen, setUploaderToolOpen] = useState<boolean>(false);
4✔
302
  const dropdownSelection = useRef(null);
4✔
303
  const nameDropdownSelection = useRef(null);
4✔
304
  const clickableObject = navMobileList.filter(
4✔
305
    (item) => item.className === "navMobileItem clickable"
20✔
306
  );
307
  const clickableTitle = clickableObject.map((item) => item.name);
8✔
308
  const navigate = useNavigate();
4✔
309
  const authData = useAuthContext();
4✔
310
  const location = useLocation();
4✔
311
  const displayName = authData?.user?.firstName?.toUpperCase() || "N/A";
4✔
312
  const [showLogoutAlert, setShowLogoutAlert] = useState<boolean>(false);
4✔
313
  const [restorePath, setRestorePath] = useState<string>(null);
4✔
314

315
  clickableTitle.push(displayName);
4✔
316

317
  useOutsideAlerter(dropdownSelection, nameDropdownSelection);
4✔
318

319
  useEffect(() => {
4✔
320
    if (!authData.isLoggedIn) {
4✔
321
      setClickedTitle("");
2✔
322
    }
323
  }, [authData]);
324

325
  const handleLogout = async () => {
4✔
326
    setClickedTitle("");
×
327
    const logoutStatus = await authData.logout();
×
328
    if (logoutStatus) {
×
329
      navigate("/");
×
330
      setShowLogoutAlert(true);
×
331
      setTimeout(() => setShowLogoutAlert(false), 10000);
×
332
    }
333
  };
334

335
  const handleMenuClick = (e) => {
4✔
336
    if (e.target.innerText === clickedTitle || !clickableTitle.includes(e.target.innerText)) {
×
337
      setClickedTitle("");
×
338
    } else {
339
      setClickedTitle(e.target.innerText);
×
340
    }
341
  };
342

343
  const onKeyPressHandler = (e) => {
4✔
344
    if (e.key === "Enter") {
×
345
      handleMenuClick(e);
×
346
    }
347
  };
348
  type NavSubLinkData = {
349
    name: string;
350
    link: string;
351
    className: string;
352
  };
353
  function shouldBeUnderlined(item) {
354
    const linkName = item.name;
20✔
355
    const correctPath = window.location.pathname;
20✔
356
    if (item.className === "navMobileItem") {
20✔
357
      return correctPath === item.link;
12✔
358
    }
359
    if (navbarSublists[linkName] === undefined) {
8!
360
      return false;
×
361
    }
362
    const linkNames = Object.values(navbarSublists[linkName]).map((e: NavSubLinkData) => e.link);
24✔
363
    return linkNames.includes(correctPath);
8✔
364
  }
365

366
  useEffect(() => {
4✔
367
    setClickedTitle("");
4✔
368
  }, []);
369

370
  useEffect(() => {
4✔
371
    if (!location?.pathname || location?.pathname === "/") {
4!
372
      setRestorePath(null);
4✔
373
      return;
4✔
374
    }
375

376
    setRestorePath(location?.pathname);
×
377
  }, [location]);
378

379
  return (
4✔
380
    <Nav>
381
      <GenericAlert open={showLogoutAlert}>
382
        <span>You have been logged out.</span>
383
      </GenericAlert>
384
      <NavContainer>
385
        <UlContainer>
386
          {navMobileList.map((navMobileItem, idx) => {
387
            const navkey = `nav_${idx}`;
20✔
388
            return navMobileItem.className === "navMobileItem" ? (
20✔
389
              <LiSection key={navkey}>
390
                <div className="navTitle directLink">
391
                  <NavLink
392
                    to={navMobileItem.link}
393
                    target={navMobileItem.link.startsWith("https://") ? "_blank" : "_self"}
6✔
394
                  >
395
                    <div
396
                      id={navMobileItem.id}
397
                      onKeyDown={onKeyPressHandler}
398
                      role="button"
399
                      tabIndex={0}
400
                      className={`navText directLink ${
401
                        shouldBeUnderlined(navMobileItem) ? "shouldBeUnderlined" : ""
6!
402
                      }`}
403
                      onClick={handleMenuClick}
404
                    >
405
                      {navMobileItem.name}
406
                    </div>
407
                  </NavLink>
408
                </div>
409
              </LiSection>
410
            ) : (
411
              <LiSection key={navkey}>
412
                <div
413
                  className={clickedTitle === navMobileItem.name ? "navTitleClicked" : "navTitle"}
4!
414
                >
415
                  <div
416
                    id={navMobileItem.id}
417
                    onKeyDown={onKeyPressHandler}
418
                    role="button"
419
                    tabIndex={0}
420
                    className={`${
421
                      clickedTitle === navMobileItem.name ? "navText clicked" : "navText"
4!
422
                    } ${shouldBeUnderlined(navMobileItem) ? "shouldBeUnderlined" : ""}`}
4!
423
                    onClick={handleMenuClick}
424
                  >
425
                    {navMobileItem.name}
426
                  </div>
427
                </div>
428
              </LiSection>
429
            );
430
          })}
431
          <LiSection className={`name-dropdown-li${authData?.isLoggedIn ? "" : " login-button"}`}>
2✔
432
            {authData.isLoggedIn ? (
2✔
433
              <div
434
                id="navbar-dropdown-name-container"
435
                className={clickedTitle === displayName ? "navTitleClicked" : "navTitle"}
1!
436
              >
437
                <div
438
                  id="navbar-dropdown-name"
439
                  onKeyDown={onKeyPressHandler}
440
                  role="button"
441
                  tabIndex={0}
442
                  className={
443
                    clickedTitle === displayName
1!
444
                      ? "navText displayName clicked"
445
                      : "navText displayName"
446
                  }
447
                  onClick={handleMenuClick}
448
                >
449
                  {displayName}
450
                </div>
451
              </div>
452
            ) : (
453
              <StyledLoginLink
454
                id="header-navbar-login-button"
455
                to="/login"
456
                state={{ redirectURLOnLoginSuccess: restorePath }}
457
              >
458
                Login
459
              </StyledLoginLink>
460
            )}
461
          </LiSection>
462
        </UlContainer>
463
      </NavContainer>
464
      <Dropdown ref={dropdownSelection} className={clickedTitle === "" ? "invisible" : ""}>
2!
465
        <NameDropdownContainer>
466
          <div className="dropdownList">
467
            {clickedTitle !== "" && clickedTitle !== displayName
4!
468
              ? navbarSublists[clickedTitle]?.map((dropItem, idx) => {
469
                  const dropkey = `drop_${idx}`;
×
470
                  return (
×
471
                    dropItem.link && (
×
472
                      <Link
473
                        target={
474
                          dropItem.link.startsWith("https://") || dropItem.link.endsWith(".pdf")
×
475
                            ? "_blank"
476
                            : "_self"
477
                        }
478
                        id={dropItem.id}
479
                        to={dropItem.link}
480
                        className="dropdownItem"
481
                        key={dropkey}
482
                        onClick={() => setClickedTitle("")}
×
483
                      >
484
                        {dropItem.name}
485
                        <div className="dropdownItemText">{dropItem.text}</div>
486
                      </Link>
487
                    )
488
                  );
489
                })
490
              : null}
491
          </div>
492
        </NameDropdownContainer>
493
      </Dropdown>
494
      <NameDropdown
495
        ref={nameDropdownSelection}
496
        className={clickedTitle !== displayName ? "invisible" : ""}
2!
497
      >
498
        <NameDropdownContainer>
499
          <div className="dropdownList">
500
            <span className="dropdownItem">
501
              <Link
502
                id="navbar-dropdown-item-name-user-profile"
503
                to={`/profile/${authData?.user?._id}`}
504
                className="dropdownItem"
505
                onClick={() => setClickedTitle("")}
×
506
              >
507
                User Profile
508
              </Link>
509
            </span>
510
            <span className="dropdownItem">
511
              <Button
512
                id="navbar-dropdown-item-name-uploader-tool"
513
                className="dropdownItem dropdownItemButton"
514
                onClick={() => setUploaderToolOpen(true)}
×
515
              >
516
                Uploader CLI Tool
517
              </Button>
518
            </span>
519
            {(authData?.user?.role === "Admin" ||
4!
520
              authData?.user?.role === "Organization Owner") && (
521
              <span className="dropdownItem">
522
                <Link
523
                  id="navbar-dropdown-item-name-user-manage"
524
                  to="/users"
525
                  className="dropdownItem"
526
                  onClick={() => setClickedTitle("")}
×
527
                >
528
                  Manage Users
529
                </Link>
530
              </span>
531
            )}
532
            {authData?.user?.role === "Admin" && (
2!
533
              <span className="dropdownItem">
534
                <Link
535
                  id="navbar-dropdown-item-name-organization-manage"
536
                  to="/organizations"
537
                  className="dropdownItem"
538
                  onClick={() => setClickedTitle("")}
×
539
                >
540
                  Manage Organizations
541
                </Link>
542
              </span>
543
            )}
544
            {(authData?.user?.role === "Submitter" ||
4!
545
              authData?.user?.role === "Organization Owner") && (
546
              <span className="dropdownItem">
547
                <Button
548
                  id="navbar-dropdown-item-name-api-token"
549
                  className="dropdownItem dropdownItemButton"
550
                  onClick={() => setOpenAPITokenDialog(true)}
×
551
                >
552
                  API Token
553
                </Button>
554
              </span>
555
            )}
556
            <span
557
              id="navbar-dropdown-item-name-logout"
558
              role="button"
559
              tabIndex={0}
560
              className="dropdownItem"
561
              onClick={() => {
562
                setClickedTitle("");
×
563
                handleLogout();
×
564
              }}
565
              onKeyDown={(e) => {
566
                if (e.key === "Enter") {
×
567
                  setClickedTitle("");
×
568
                  handleLogout();
×
569
                }
570
              }}
571
            >
572
              Logout
573
            </span>
574
          </div>
575
        </NameDropdownContainer>
576
      </NameDropdown>
577
      <APITokenDialog open={openAPITokenDialog} onClose={() => setOpenAPITokenDialog(false)} />
×
578
      <UploaderToolDialog open={uploaderToolOpen} onClose={() => setUploaderToolOpen(false)} />
×
579
    </Nav>
580
  );
581
};
582

583
export default NavBar;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc