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

CBIIT / crdc-datahub-ui / 10620943351

29 Aug 2024 06:52PM UTC coverage: 45.576% (+0.4%) from 45.203%
10620943351

Pull #447

github

web-flow
Merge e49ac8e05 into 8bedf7236
Pull Request #447: CRDCDH-1444 QuickSight Dashboard Embedding

1777 of 4478 branches covered (39.68%)

Branch coverage included in aggregate %.

50 of 90 new or added lines in 10 files covered. (55.56%)

1 existing line in 1 file now uncovered.

2673 of 5286 relevant lines covered (50.57%)

128.08 hits per line

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

53.93
/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 { GenerateApiTokenRoles } from "../../../config/AuthRoles";
8
import APITokenDialog from "../../../content/users/APITokenDialog";
9
import UploaderToolDialog from "../../UploaderToolDialog";
10

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

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

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

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

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

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

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

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

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

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

299
const NavBar = () => {
2✔
300
  const { isLoggedIn, user, logout } = useAuthContext();
4✔
301
  const navigate = useNavigate();
4✔
302
  const location = useLocation();
4✔
303

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

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

318
  clickableTitle.push(displayName);
4✔
319

320
  useOutsideAlerter(dropdownSelection, nameDropdownSelection);
4✔
321

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

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

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

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

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

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

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

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

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

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

582
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