All files / client/src/components Tabs.tsx

93.75% Statements 15/16
100% Branches 2/2
100% Functions 10/10
93.75% Lines 15/16

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91                                      356x           743x                               743x                         1x   365x       1x         1x       737x         1x   735x 735x       735x 735x       1x 732x      
import { ReactElement } from "react";
import { Navigate, NavLink, Route, Routes } from "react-router";
import styled from "styled-components";
import { IconName } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { last } from "remeda";
 
interface TabContent {
  headerText: string;
  path: string;
  element: React.ReactNode;
  icon?: IconName;
  "data-testid"?: string;
}
 
interface Props {
  tabContents: TabContent[];
}
 
export const Tabs = ({ tabContents }: Props): ReactElement => {
  return (
    <>
      <TabsNav>
        <TabsList>
          {tabContents.map((t) => (
            <ListItem key={t.headerText}>
              <StyledLink to={t.path} data-testid={t["data-testid"]}>
                {t.icon && <StyledIcon icon={t.icon} aria-hidden={true} />}
                {t.headerText}
              </StyledLink>
            </ListItem>
          ))}
        </TabsList>
      </TabsNav>
      <Routes>
        {/* If no tab selected, select first tab */}
        <Route
          path={""}
          element={<Navigate to={tabContents[0].path} replace={true} />}
        />
        {tabContents.map((t) => {
          return (
            <Route
              path={last(t.path.split("/"))}
              element={t.element}
              key={t.path}
            />
          );
        })}
      </Routes>
    </>
  );
};
 
const TabsNav = styled.nav`
  display: flex;
  border-bottom: 1px solid ${(props) => props.theme.tabBorder};
  padding-bottom: 6px;
`;
 
const TabsList = styled.ul`
  list-style-type: none;
  margin-left: 0;
`;
 
const ListItem = styled.li`
  float: left;
  padding-right: 16px;
 
  @media (min-width: ${(props) => props.theme.breakpointDesktop}) {
    padding-right: 24px;
  }
`;
 
const StyledLink = styled(NavLink)`
  text-decoration: none;
  font-size: ${(props) => props.theme.fontSizeNormal};
  color: ${(props) => props.theme.textInactiveTab};
  padding-bottom: 4px;
 
  &.active {
    color: ${(props) => props.theme.textActiveTab};
    border-bottom: 3px solid ${(props) => props.theme.textActiveTab};
  }
`;
 
const StyledIcon = styled(FontAwesomeIcon)`
  font-size: ${(props) => props.theme.iconSizeNormal};
  margin-right: 6px;
`;