All files / src/components/CustomBreadcrumbsPage BreadcrumbsPage.tsx

89.58% Statements 43/48
75% Branches 27/36
90.9% Functions 10/11
90.9% Lines 40/44

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 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207                                2x     2x                   2x                                   2x 10x   10x             2x 10x 10x     2x         10x 6x     2x 10x 10x 10x   10x 10x             10x     10x 80x     10x   10x   10x         10x                 10x 18x     10x 150x 150x     10x 10x 10x       10x 10x 10x         10x                                                             10x             5x           10x                                                              
import { Stack, Box, BoxProps, styled } from "@mui/material";
import BreadcrumbsAppBar, {
  APP_BAR_HEIGHT,
  PageContainer,
} from "./BreadcrumbsAppBar";
import { memo, useCallback, useRef } from "react";
import { makeStyles } from "@mui/styles";
import { isMobileSafari } from "@utils/helpers";
import { useSelector } from "react-redux";
import { useBannerPush } from "@hooks/common/useBannerPush";
import { useCustomTheme } from "@theme/hooks/useCustomTheme";
import { detailsReportPages } from "@constants/constants";
import { useGetRebrandedTableBehaviour } from "./hooks/useGetRebrandedTableBehaviour";
import { useRouting } from "@hooks/common/useRouting";
import { useMatch } from "react-router-dom";
 
export const PAGE_PADDING_TOP = 16;
 
/** Route whose page renders its own in-banner breadcrumb — see `isDetailsPage`. */
const EVENT_DETAIL_PATH = "/merchant/events/:id";
 
interface MuiBoxProps extends BoxProps {
  isMasqueradeMode?: boolean;
}
 
interface StyleMap {
  [key: string]: string;
}
 
const cls: StyleMap = {
  "/provider/manage-money": "vlList",
  "/acquirer/manage-money": "vlList",
  "/provider/transactions": "vlList",
  "/acquirer/transactions": "vlList",
  "/acquirer/disputes": "vlList",
  "/merchant/manage-money": "vlList",
  "/merchant/fundraisers/:id": "vlList",
  "/merchant/events/:id": "vlList",
  "/merchant/invoices/:id": "vlList",
  "/merchant/memberships/:id": "vlList",
  "/merchant/sweepstakes/:id": "vlList",
  "/acquirer/transfers": "vlList",
  "/provider/transfers": "vlList",
  "/acquirer/settlements": "vlList",
  "/acquirer/settlements/:id": "vlList",
};
 
const getHeightBasis = (isFullHeight: boolean) => {
  Iif (isFullHeight) return "100%";
  // iOS Safari's `100vh` is the toolbars-collapsed large viewport.
  return isMobileSafari() ? `${window.innerHeight}px` : "100vh";
};
 
// Sole owner of this element's `max-height` — the JSS class declared it too, at
// equal specificity, so the winner was whichever style sheet loaded first. A
// sticky banner comes off the cap as much as a fixed one does: `RootContainer`
// is `overflow: hidden` and one viewport tall wherever the strip put it.
const getPageMaxHeight = (isFullHeight: boolean, reserved: number) => {
  const basis = getHeightBasis(isFullHeight);
  return reserved ? `calc(${basis} - ${reserved}px)` : basis;
};
 
const getPaddingTop = (
  isPermissionsPage: boolean,
  newTableApplied: boolean,
  isMobileView: boolean,
) => {
  if (isPermissionsPage || newTableApplied) return 0;
  return isMobileView ? "64px" : "16px";
};
 
const BreadcrumbsPageContainer = ({ children, ...rest }: MuiBoxProps) => {
  const { isMobileView } = useCustomTheme();
  const appBarRef = useRef<HTMLDivElement>(null);
  const { bannerOffset, bannerPush } = useBannerPush();
 
  const isSettingsPage = location.pathname.includes("/settings");
  const isMediaLibrarySection = location.pathname.includes("/media-library");
 
  // PayBuilder 032-b: the rebranded Event Detail view draws its own breadcrumb
  // inside the banner, on top of the animated backdrop. The shared app bar sits
  // above `PageContainer` — outside the backdrop — so leaving it on would only
  // add a white strip above the page. Other detail pages are unaffected until
  // they are rebranded too.
  const isRebrandedEventDetail = Boolean(useMatch(EVENT_DETAIL_PATH));
 
  const isDetailsPage =
    !isRebrandedEventDetail &&
    detailsReportPages.some((page) => location.pathname?.includes(page));
 
  const { isDisplayedTableNew: newTableApplied } =
    useGetRebrandedTableBehaviour();
 
  const { isPermissionsPage, isLegalDocumentDetailPage } = useRouting();
 
  const classes = useStyles({
    newTableApplied,
    isPermissionsPage: isPermissionsPage || isLegalDocumentDetailPage,
  });
 
  const handleScroll = useCallback((e: any) => {
    const { scrollTop } = e.currentTarget;
    if (scrollTop > 0) {
      appBarRef.current?.classList.add(classes.appBarActive);
    } else {
      appBarRef.current?.classList.remove(classes.appBarActive);
    }
  }, []);
 
  const isEnabledVl = useSelector<any, boolean>(
    (state: any) => state.app.enabledVL,
  );
 
  const path: keyof typeof cls = Object.keys(cls).find((key) => {
    const regex = new RegExp(`^${key.replace(/:[^/]+/g, "[^/]+")}$`);
    return regex.test(location.pathname);
  }) as keyof typeof cls;
 
  const dynamicStyleKey = cls[path];
  const dynamicStyle = classes[dynamicStyleKey as keyof typeof classes];
  const isFullHeight = isSettingsPage && !isMediaLibrarySection;
  // Only reserve the app-bar's height when the app bar is actually rendered
  // (isDetailsPage). On rebranded non-details pages the bar isn't shown, so
  // subtracting APP_BAR_HEIGHT left a phantom blank strip at the bottom.
  const reserved = (isDetailsPage ? APP_BAR_HEIGHT : 0) + bannerOffset;
  const maxHeight = getPageMaxHeight(isFullHeight, reserved);
  const paddingTop = getPaddingTop(
    isPermissionsPage || isLegalDocumentDetailPage,
    newTableApplied,
    isMobileView,
  );
  return (
    <RootContainer
      className={classes.pageContainerRebrandedBackground}
      {...rest}
    >
      {isDetailsPage && ( // TODO: Remove this once when all pages are rebranded
        <BreadcrumbsAppBar
          appBarRef={appBarRef}
          showOnlyAppBarNavigation={isDetailsPage}
        />
      )}
      <PageContainer
        id="page-container"
        isPermissionsPage={isPermissionsPage || isLegalDocumentDetailPage}
        className={`${classes.pageContainer} ${
          isEnabledVl ? dynamicStyle : ""
        }`}
        onScroll={handleScroll}
        mt={bannerPush ? `${bannerPush}px` : 0}
        maxHeight={maxHeight}
      >
        <Stack flexGrow={1} pt={paddingTop}>
          {children}
        </Stack>
      </PageContainer>
    </RootContainer>
  );
};
 
export default memo(BreadcrumbsPageContainer);
 
const RootContainer = styled(Box)(() => ({
  flexGrow: 1,
  overflow: "hidden",
  position: "relative",
  maxHeight: "100vh",
}));
 
const useStyles = makeStyles(({ palette }: any) => ({
  appBarActive: {
    boxShadow: "4px 10px 15px -3px #3131311A",
    background: "#FBFBFBE5",
    backdropFilter: "blur(4px)",
  },
  pageContainer: ({ newTableApplied, isPermissionsPage }: any) => ({
    ...(!isPermissionsPage && {
      paddingTop: "16px",
    }),
    overflowY: "scroll",
    height: "100%",
    "&::-webkit-scrollbar": {
      display: "none",
    },
    msOverflowStyle: "none",
    scrollbarWidth: "none",
    ...(newTableApplied && {
      // TODO: Remove this once we have rebranding completed
      paddingTop: 0,
      paddingLeft: 0,
      paddingRight: 0,
      paddingBottom: 0,
      width: "100%",
      maxWidth: "100%",
      height: "100%",
    }),
  }),
  pageContainerRebrandedBackground: {
    backgroundColor: palette.surface?.primary,
  },
  vlList: {
    maxWidth: "unset",
    paddingLeft: 0,
    paddingRight: 0,
  },
}));