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

CBIIT / crdc-datahub-ui / 10598024957

28 Aug 2024 01:47PM UTC coverage: 45.608% (+0.4%) from 45.202%
10598024957

Pull #447

github

web-flow
Merge 7749ef234 into 0787fe862
Pull Request #447: CRDCDH-1444 QuickSight Dashboard Embedding

1778 of 4474 branches covered (39.74%)

Branch coverage included in aggregate %.

50 of 88 new or added lines in 10 files covered. (56.82%)

1 existing line in 1 file now uncovered.

2672 of 5283 relevant lines covered (50.58%)

127.95 hits per line

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

54.24
/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 { isLoggedIn, user, logout } = useAuthContext();
4✔
300
  const navigate = useNavigate();
4✔
301
  const location = useLocation();
4✔
302

303
  const [clickedTitle, setClickedTitle] = useState("");
4✔
304
  const [openAPITokenDialog, setOpenAPITokenDialog] = useState<boolean>(false);
4✔
305
  const [uploaderToolOpen, setUploaderToolOpen] = useState<boolean>(false);
4✔
306
  const [showLogoutAlert, setShowLogoutAlert] = useState<boolean>(false);
4✔
307
  const [restorePath, setRestorePath] = useState<string>(null);
4✔
308
  const dropdownSelection = useRef(null);
4✔
309
  const nameDropdownSelection = useRef(null);
4✔
310

311
  const clickableObject = navMobileList.filter(
4✔
312
    (item) => item.className === "navMobileItem clickable"
24✔
313
  );
314
  const clickableTitle = clickableObject.map((item) => item.name);
8✔
315
  const displayName = user?.firstName?.toUpperCase() || "N/A";
4✔
316

317
  clickableTitle.push(displayName);
4✔
318

319
  useOutsideAlerter(dropdownSelection, nameDropdownSelection);
4✔
320

321
  useEffect(() => {
4✔
322
    if (!isLoggedIn) {
4✔
323
      setClickedTitle("");
2✔
324
    }
325
  }, [isLoggedIn]);
326

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

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

345
  const onKeyPressHandler = (e) => {
4✔
346
    if (e.key === "Enter") {
×
347
      handleMenuClick(e);
×
348
    }
349
  };
350

351
  function shouldBeUnderlined(item) {
352
    const linkName = item.name;
20✔
353
    const correctPath = window.location.pathname;
20✔
354
    if (item.className === "navMobileItem") {
20✔
355
      return correctPath === item.link;
12✔
356
    }
357
    if (navbarSublists[linkName] === undefined) {
8!
358
      return false;
×
359
    }
360
    const linkNames = Object.values(navbarSublists[linkName]).map((e: NavBarSubItem) => e.link);
24✔
361
    return linkNames.includes(correctPath);
8✔
362
  }
363

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

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

374
    setRestorePath(location?.pathname);
×
375
  }, [location]);
376

377
  return (
4✔
378
    <Nav>
379
      <GenericAlert open={showLogoutAlert}>
380
        <span>You have been logged out.</span>
381
      </GenericAlert>
382
      <NavContainer>
383
        <UlContainer>
384
          {navMobileList.map((navItem) => {
385
            // If the user is not logged in and the item requires a role, don't show it
386
            if (Array.isArray(navItem?.roles) && !navItem.roles.includes(user?.role)) {
24✔
387
              return null;
4✔
388
            }
389

390
            return (
20✔
391
              <LiSection key={navItem.id}>
392
                {navItem.className === "navMobileItem" ? (
10✔
393
                  <div className="navTitle directLink">
394
                    <NavLink
395
                      to={navItem.link}
396
                      target={navItem.link.startsWith("https://") ? "_blank" : "_self"}
6✔
397
                    >
398
                      <div
399
                        id={navItem.id}
400
                        onKeyDown={onKeyPressHandler}
401
                        role="button"
402
                        tabIndex={0}
403
                        className={`navText directLink ${
404
                          shouldBeUnderlined(navItem) ? "shouldBeUnderlined" : ""
6!
405
                        }`}
406
                        onClick={handleMenuClick}
407
                      >
408
                        {navItem.name}
409
                      </div>
410
                    </NavLink>
411
                  </div>
412
                ) : (
413
                  <div className={clickedTitle === navItem.name ? "navTitleClicked" : "navTitle"}>
4!
414
                    <div
415
                      id={navItem.id}
416
                      onKeyDown={onKeyPressHandler}
417
                      role="button"
418
                      tabIndex={0}
419
                      className={`${
420
                        clickedTitle === navItem.name ? "navText clicked" : "navText"
4!
421
                      } ${shouldBeUnderlined(navItem) ? "shouldBeUnderlined" : ""}`}
4!
422
                      onClick={handleMenuClick}
423
                    >
424
                      {navItem.name}
425
                    </div>
426
                  </div>
427
                )}
428
              </LiSection>
429
            );
430
          })}
431
          <LiSection className={`name-dropdown-li${isLoggedIn ? "" : " login-button"}`}>
2✔
432
            {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/${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
            {(user?.role === "Admin" || user?.role === "Organization Owner") && (
4!
520
              <span className="dropdownItem">
521
                <Link
522
                  id="navbar-dropdown-item-name-user-manage"
523
                  to="/users"
524
                  className="dropdownItem"
525
                  onClick={() => setClickedTitle("")}
×
526
                >
527
                  Manage Users
528
                </Link>
529
              </span>
530
            )}
531
            {user?.role === "Admin" && (
2!
532
              <span className="dropdownItem">
533
                <Link
534
                  id="navbar-dropdown-item-name-organization-manage"
535
                  to="/organizations"
536
                  className="dropdownItem"
537
                  onClick={() => setClickedTitle("")}
×
538
                >
539
                  Manage Organizations
540
                </Link>
541
              </span>
542
            )}
543
            {(user?.role === "Submitter" || user?.role === "Organization Owner") && (
4!
544
              <span className="dropdownItem">
545
                <Button
546
                  id="navbar-dropdown-item-name-api-token"
547
                  className="dropdownItem dropdownItemButton"
548
                  onClick={() => setOpenAPITokenDialog(true)}
×
549
                >
550
                  API Token
551
                </Button>
552
              </span>
553
            )}
554
            <span
555
              id="navbar-dropdown-item-name-logout"
556
              role="button"
557
              tabIndex={0}
558
              className="dropdownItem"
559
              onClick={() => {
560
                setClickedTitle("");
×
561
                handleLogout();
×
562
              }}
563
              onKeyDown={(e) => {
564
                if (e.key === "Enter") {
×
565
                  setClickedTitle("");
×
566
                  handleLogout();
×
567
                }
568
              }}
569
            >
570
              Logout
571
            </span>
572
          </div>
573
        </NameDropdownContainer>
574
      </NameDropdown>
575
      <APITokenDialog open={openAPITokenDialog} onClose={() => setOpenAPITokenDialog(false)} />
×
576
      <UploaderToolDialog open={uploaderToolOpen} onClose={() => setUploaderToolOpen(false)} />
×
577
    </Nav>
578
  );
579
};
580

581
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

© 2025 Coveralls, Inc