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

CBIIT / crdc-datahub-ui / 14229012485

28 Mar 2025 05:16PM UTC coverage: 60.279% (+6.1%) from 54.222%
14229012485

push

github

web-flow
Merge pull request #655 from CBIIT/3.2.0

3.2.0 Release

3137 of 5624 branches covered (55.78%)

Branch coverage included in aggregate %.

991 of 1319 new or added lines in 98 files covered. (75.13%)

33 existing lines in 14 files now uncovered.

4372 of 6833 relevant lines covered (63.98%)

147.15 hits per line

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

36.81
/src/components/Header/components/HeaderTabletAndMobile.tsx
1
import React, { HTMLProps, useEffect, useState } from "react";
2
import { NavLink, Link, useNavigate, useLocation } from "react-router-dom";
3
import { styled } from "@mui/material";
4
import Logo from "./LogoMobile";
5
import menuClearIcon from "../../../assets/header/Menu_Cancel_Icon.svg";
6
import rightArrowIcon from "../../../assets/header/Right_Arrow.svg";
7
import leftArrowIcon from "../../../assets/header/Left_Arrow.svg";
8
import { HeaderLinks, HeaderSubLinks } from "../../../config/HeaderConfig";
9
import { useAuthContext } from "../../Contexts/AuthContext";
10
import GenericAlert from "../../GenericAlert";
11
import APITokenDialog from "../../APITokenDialog";
12
import UploaderToolDialog from "../../UploaderToolDialog";
13

14
const HeaderBanner = styled("div")({
2✔
15
  width: "100%",
16
});
17

18
const HeaderContainer = styled("div")({
2✔
19
  margin: "0 auto",
20
  paddingLeft: "16px",
21
  boxShadow: "-0.1px 6px 9px -6px rgba(0, 0, 0, 0.5)",
22
  "& .headerLowerContainer": {
23
    display: "flex",
24
    margin: "16px 0 4px 0",
25
    height: "51px",
26
  },
27
  "& .menuButton": {
28
    width: "89px",
29
    height: "45px",
30
    background: "#1F4671",
31
    borderRadius: "5px",
32
    fontFamily: "Open Sans",
33
    fontWeight: 700,
34
    fontSize: "20px",
35
    lineHeight: "45px",
36
    color: "#FFFFFF",
37
    textAlign: "center",
38
    "&:hover": {
39
      cursor: "pointer",
40
    },
41
  },
42
});
43

44
const NavMobileContainer = styled("div", {
2✔
45
  shouldForwardProp: (prop) => prop !== "display",
6✔
46
})<HTMLProps<HTMLDivElement> & { display: string }>(({ display }) => ({
2✔
47
  display,
48
  position: "absolute",
49
  left: 0,
50
  top: 0,
51
  height: "100%",
52
  width: "100%",
53
  zIndex: 1200,
54
}));
55

56
const MenuArea = styled("div")({
2✔
57
  height: "100%",
58
  width: "100%",
59
  display: "flex",
60
  "& .menuContainer": {
61
    background: "#ffffff",
62
    width: "300px",
63
    height: "100%",
64
    padding: "21px 16px",
65
  },
66
  "& .greyContainer": {
67
    width: "100%",
68
    height: "100%",
69
    background: "rgba(0,0,0,.2)",
70
  },
71
  "& .closeIcon": {
72
    height: "14px",
73
    marginBottom: "29px",
74
  },
75
  "& .closeIconImg": {
76
    float: "right",
77
    "&:hover": {
78
      cursor: "pointer",
79
    },
80
  },
81
  "& .backButton": {
82
    fontFamily: "Open Sans",
83
    fontWeight: 600,
84
    fontSize: "16px",
85
    lineHeight: "16px",
86
    color: "#007BBD",
87
    paddingLeft: "16px",
88
    background: `url(${leftArrowIcon}) left no-repeat`,
89
    "&:hover": {
90
      cursor: "pointer",
91
    },
92
  },
93
  "& .navMobileContainer": {
94
    padding: "24px 0 0 0",
95
    "& a": {
96
      textDecoration: "none",
97
      color: "#3D4551",
98
    },
99
  },
100
  "& .navMobileItem": {
101
    width: "268px",
102
    padding: "8px 24px 8px 16px",
103
    fontFamily: "Open Sans",
104
    fontWeight: 400,
105
    fontSize: "16px",
106
    lineHeight: "16px",
107
    borderTop: "1px solid #F0F0F0",
108
    borderBottom: "1px solid #F0F0F0",
109
    color: "#3D4551",
110
    "&:hover": {
111
      backgroundColor: "#f9f9f7",
112
    },
113
  },
114
  "& .SubItem": {
115
    paddingLeft: "24px",
116
  },
117
  "& .clickable": {
118
    background: `url(${rightArrowIcon}) 90% no-repeat`,
119
    cursor: "pointer",
120
  },
121
  "& .action": {
122
    cursor: "pointer",
123
  },
124
});
125

126
const Header = () => {
2✔
127
  const { isLoggedIn, user, logout } = useAuthContext();
2✔
128
  const navigate = useNavigate();
2✔
129
  const location = useLocation();
2✔
130

131
  const [navMobileDisplay, setNavMobileDisplay] = useState("none");
2✔
132
  const [openAPITokenDialog, setOpenAPITokenDialog] = useState<boolean>(false);
2✔
133
  const [uploaderToolOpen, setUploaderToolOpen] = useState<boolean>(false);
2✔
134
  const [selectedList, setSelectedList] = useState<NavBarItem[] | NavBarSubItem[]>(HeaderLinks);
2✔
135
  const [showLogoutAlert, setShowLogoutAlert] = useState<boolean>(false);
2✔
136
  const [restorePath, setRestorePath] = useState<string | null>(null);
2✔
137

138
  const displayName = user?.firstName || "N/A";
2✔
139

140
  const handleLogout = async () => {
2✔
141
    const logoutStatus = await logout?.();
×
142
    if (logoutStatus) {
×
143
      navigate("/");
×
144
      setShowLogoutAlert(true);
×
145
      setTimeout(() => setShowLogoutAlert(false), 10000);
×
146
    }
147
  };
148

149
  HeaderSubLinks[displayName] = [
2✔
150
    {
151
      name: "User Profile",
152
      link: `/profile/${user?._id}`,
153
      id: "navbar-dropdown-item-user-profile",
154
      className: "navMobileSubItem",
155
    },
156
    {
157
      name: "Uploader CLI Tool",
158
      onClick: () => setUploaderToolOpen(true),
×
159
      id: "navbar-dropdown-item-uploader-tool",
160
      className: "navMobileSubItem action",
161
    },
162
    {
163
      name: "API Token",
NEW
164
      onClick: () => setOpenAPITokenDialog(true),
×
165
      id: "navbar-dropdown-item-api-token",
166
      className: "navMobileSubItem action",
167
      permissions: ["data_submission:create"],
168
    },
169
    {
170
      name: "Manage Studies",
171
      link: "/studies",
172
      id: "navbar-dropdown-item-studies-manage",
173
      className: "navMobileSubItem",
174
      permissions: ["study:manage"],
175
    },
176
    {
177
      name: "Manage Programs",
178
      link: "/programs",
179
      id: "navbar-dropdown-item-program-manage",
180
      className: "navMobileSubItem",
181
      permissions: ["program:manage"],
182
    },
183
    {
184
      name: "Manage Users",
185
      link: "/users",
186
      id: "navbar-dropdown-item-user-manage",
187
      className: "navMobileSubItem",
188
      permissions: ["user:manage"],
189
    },
190
    {
191
      name: "Logout",
192
      link: "/logout",
193
      id: "navbar-dropdown-item-logout",
194
      className: "navMobileSubItem",
195
    },
196
  ];
197

198
  const clickNavItem = (e) => {
2✔
199
    const clickTitle = e.target.textContent;
×
NEW
200
    setSelectedList(HeaderSubLinks[clickTitle]);
×
201
  };
202

203
  useEffect(() => {
2✔
204
    if (!location?.pathname || location?.pathname === "/") {
2!
205
      setRestorePath(null);
2✔
206
      return;
2✔
207
    }
208

209
    setRestorePath(location?.pathname);
×
210
  }, [location]);
211

212
  return (
2✔
213
    <>
214
      <GenericAlert open={showLogoutAlert}>
215
        <span>You have been logged out.</span>
216
      </GenericAlert>
217
      <HeaderBanner data-testid="navigation-header-mobile">
218
        <HeaderContainer>
219
          <Logo />
220
          <div className="headerLowerContainer">
221
            <div
222
              role="button"
223
              id="header-navbar-open-menu-button"
224
              tabIndex={0}
225
              className="menuButton"
226
              onKeyDown={(e) => {
227
                if (e.key === "Enter") {
×
228
                  setNavMobileDisplay("block");
×
229
                }
230
              }}
231
              onClick={() => setNavMobileDisplay("block")}
×
232
            >
233
              Menu
234
            </div>
235
          </div>
236
        </HeaderContainer>
237
      </HeaderBanner>
238
      <NavMobileContainer display={navMobileDisplay}>
239
        <MenuArea>
240
          <div className="menuContainer">
241
            <div
242
              role="button"
243
              id="navbar-close-navbar-button"
244
              tabIndex={0}
245
              className="closeIcon"
246
              onKeyDown={(e) => {
247
                if (e.key === "Enter") {
×
248
                  setNavMobileDisplay("none");
×
249
                }
250
              }}
251
              onClick={() => setNavMobileDisplay("none")}
×
252
            >
253
              <img className="closeIconImg" src={menuClearIcon} alt="menuClearButton" />
254
            </div>
255
            {selectedList !== HeaderLinks && (
1!
256
              <div
257
                role="button"
258
                id="navbar-back-to-main-menu-button"
259
                tabIndex={0}
260
                className="backButton"
261
                onKeyDown={(e) => {
262
                  if (e.key === "Enter") {
×
NEW
263
                    setSelectedList(HeaderLinks);
×
264
                  }
265
                }}
NEW
266
                onClick={() => setSelectedList(HeaderLinks)}
×
267
              >
268
                Main Menu
269
              </div>
270
            )}
271
            <div className="navMobileContainer">
272
              {selectedList?.map((navMobileItem: NavBarItem | NavBarSubItem) => {
273
                if (
12✔
274
                  navMobileItem?.permissions?.length > 0 &&
7✔
275
                  !navMobileItem?.permissions?.every(
276
                    (permission: AuthPermissions) => user?.permissions?.includes(permission)
2✔
277
                  )
278
                ) {
279
                  return null;
2✔
280
                }
281

282
                return (
10✔
283
                  <React.Fragment key={`mobile_${navMobileItem.id}`}>
284
                    {navMobileItem.className === "navMobileItem" && (
8✔
285
                      <NavLink
286
                        id={navMobileItem.id}
287
                        to={navMobileItem.link}
288
                        target={navMobileItem.link.startsWith("https://") ? "_blank" : "_self"}
3✔
289
                        onClick={() => setNavMobileDisplay("none")}
×
290
                      >
291
                        <div className="navMobileItem">{navMobileItem.name}</div>
292
                      </NavLink>
293
                    )}
294
                    {navMobileItem.className === "navMobileItem clickable" && (
7✔
295
                      <div
296
                        id={navMobileItem.id}
297
                        role="button"
298
                        tabIndex={0}
299
                        className="navMobileItem clickable"
300
                        onKeyDown={(e) => {
301
                          if (e.key === "Enter") {
×
302
                            clickNavItem(e);
×
303
                          }
304
                        }}
305
                        onClick={clickNavItem}
306
                      >
307
                        {navMobileItem.name}
308
                      </div>
309
                    )}
310
                    {navMobileItem.className === "navMobileSubItem action" &&
10!
311
                    "onClick" in navMobileItem &&
312
                    typeof navMobileItem.onClick === "function" ? (
313
                      <div
314
                        id={navMobileItem.id}
315
                        role="button"
316
                        tabIndex={0}
317
                        className="navMobileItem SubItem action"
318
                        onKeyDown={(e) => {
319
                          if (e.key === "Enter") {
×
320
                            navMobileItem.onClick();
×
321
                          }
322
                        }}
323
                        onClick={() => navMobileItem.onClick()}
×
324
                      >
325
                        {navMobileItem.name}
326
                      </div>
327
                    ) : null}
328
                    {navMobileItem.className === "navMobileSubItem" && (
5!
329
                      <Link
330
                        id={navMobileItem.id}
331
                        to={navMobileItem.link}
332
                        target={
333
                          navMobileItem.link.startsWith("https://") ||
×
334
                          navMobileItem.link.endsWith(".pdf")
335
                            ? "_blank"
336
                            : "_self"
337
                        }
338
                      >
339
                        <div
340
                          role="button"
341
                          tabIndex={0}
342
                          className="navMobileItem SubItem"
343
                          onKeyDown={(e) => {
344
                            if (e.key === "Enter") {
×
345
                              setNavMobileDisplay("none");
×
346
                              if (navMobileItem.name === "Logout") {
×
347
                                handleLogout();
×
NEW
348
                                setSelectedList(HeaderLinks);
×
349
                              }
350
                            }
351
                          }}
352
                          onClick={() => {
353
                            setNavMobileDisplay("none");
×
354
                            if (navMobileItem.name === "Logout") {
×
355
                              handleLogout();
×
NEW
356
                              setSelectedList(HeaderLinks);
×
357
                            }
358
                          }}
359
                        >
360
                          {navMobileItem.name}
361
                        </div>
362
                      </Link>
363
                    )}
364
                    {navMobileItem.className === "navMobileSubTitle" && (
5!
365
                      <div className="navMobileItem">{navMobileItem.name}</div>
366
                    )}
367
                  </React.Fragment>
368
                );
369
              })}
370
              {/* eslint-disable-next-line no-nested-ternary */}
371
              {selectedList === HeaderLinks ? (
1!
372
                isLoggedIn ? (
1!
373
                  <div
374
                    id="navbar-dropdown-name"
375
                    role="button"
376
                    tabIndex={0}
377
                    className="navMobileItem clickable"
378
                    onKeyDown={(e) => {
379
                      if (e.key === "Enter") {
×
380
                        clickNavItem(e);
×
381
                      }
382
                    }}
383
                    onClick={clickNavItem}
384
                  >
385
                    {displayName}
386
                  </div>
387
                ) : (
388
                  <Link id="navbar-link-login" to="/login" state={{ redirectState: restorePath }}>
389
                    <div
390
                      role="button"
391
                      tabIndex={0}
392
                      className="navMobileItem"
393
                      onKeyDown={(e) => {
394
                        if (e.key === "Enter") {
×
395
                          setNavMobileDisplay("none");
×
396
                        }
397
                      }}
398
                      onClick={() => setNavMobileDisplay("none")}
×
399
                    >
400
                      Login
401
                    </div>
402
                  </Link>
403
                )
404
              ) : null}
405
            </div>
406
          </div>
407
          <div
408
            role="button"
409
            id="navbar-close-navbar-grey-section"
410
            tabIndex={0}
411
            className="greyContainer"
412
            onKeyDown={(e) => {
413
              if (e.key === "Enter") {
×
414
                setNavMobileDisplay("none");
×
415
              }
416
            }}
417
            onClick={() => setNavMobileDisplay("none")}
×
418
            aria-label="greyContainer"
419
          />
420
        </MenuArea>
421
        <APITokenDialog open={openAPITokenDialog} onClose={() => setOpenAPITokenDialog(false)} />
×
422
        <UploaderToolDialog open={uploaderToolOpen} onClose={() => setUploaderToolOpen(false)} />
×
423
      </NavMobileContainer>
424
    </>
425
  );
426
};
427

428
export default Header;
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