Source

stories/AppBar.jsx

/** @module AppBar */
import React from 'react';
import PropTypes from 'prop-types';

import { Box, Stack, AppBar as MUIAppBar, Toolbar } from '@mui/material';
import { useTheme } from '@mui/material/styles';

import Button from './Button';
import UserMenu from './UserMenu';

import PermissionFilter from './PermissionFilter';
//We are making the bold and, hopefully, correct assumption that your application will always use 'Can Sign In' as the permission string
import { ACLS } from '../constants';
import { functionOrDefault } from '../helpers';

/**
 * The default theme for the app bar
 * @type {object}
 */
const theTheme = {
  appBar: {
    logo: {
      height: '100%',
      width: 'auto',
      maxHeight: '44px',
      top: '3px',
      position: 'relative'
    },
    logoText: {
      height: '100%',
      width: 'auto',
      maxHeight: '44px',
      top: '3px',
      position: 'relative',
      maxWidth: '150px'
    },
  }
};

/**
 * App Bar Component for the application
 * @description This component is used to render the application's app bar. It is important to note if you are using the PermissionFilter component, you must have this component as a child of the useAuthProvider.
 * @function AppBar
 * @param {object} props
 * @param {object} [props.user] - The user object from the authState
 * @param {function} [props.onLogout] - A logout function to call when the user clicks the logout button
 * @param {function} [props.onLogin] - The login function to call when the user clicks the login button
 * @param {array} [props.navLinks] - An array of objects to render as nav links
 * @param {string} [props.navLinks[].title] - The label for the nav link
 * @param {string} [props.navLinks[].href] - The path for the nav link
 * @param {string} [props.logoUrl] - The url for the logo
 * @param {string} [props.buttonVariant] - The MUI variant name for the buttons creating by navLinks
 * @param {string} [props.logoText] - The text to place next to the logo on the app bar
 * @param {function} [props.renderLogo] - A function to overwrite the default renderer for the logo section - Optional
 */
const AppBar = ({ user, onLogin, onLogout, navLinks, logoUrl, buttonVariant = 'appbar', themeGroup, userLinks, showLoggingIn, logoText, renderLogo, ...props }) => {
  const theme = useTheme();
  const appBar = theme?.appBar || theTheme.appBar;

  // If a theme group was passed in, use that instead of the default
  const theming = themeGroup || appBar;
  const logoStyle = theming?.logo || theTheme.appBar.logo;
  const logoTextStyle = theming?.logoText || theTheme.appBar.logoText;

  // Helper render method to simplify the final render returned
  const renderMenu = () => {
    return (
      <Stack spacing={10} direction="row">
        <Stack spacing={2} direction="row">
          {navLinks.map(renderButton)}
        </Stack>
        {renderUserArea()}
      </Stack>
    );
  };

  // On the off chance a button is defined that does not need special permission render it with the PermissionFilter.
  const renderButton = (item, index) => {
    const theButton = (
      <Button
        end={true}
        variant={buttonVariant}
        disableElevation={true}
        key={index}
        href={item.href}
        label={item.title}
      ></Button>
    );

    if (item.permission) {
      return (
        <PermissionFilter key={index} permission={item.permission} showLoggingIn={showLoggingIn || false}>
          {theButton}
        </PermissionFilter>
      );
    }

    return theButton;
  };

  // Render the user area if the user is allowed to sign in.
  const renderUserArea = () => {
    return (
      <PermissionFilter permission={ACLS.SIGN_IN} debug="UserMenu" showLoggingIn={showLoggingIn || false} >
        <UserMenu
          user={user}
          onLogin={onLogin}
          onLogout={onLogout}
          sx={{ position: 'absolute', left: '0px' }}
          links={userLinks}
        />
      </PermissionFilter>
    );
  };

  const defaultLogoRenderer = (logoUrl, logoText) => {
    return (
      <Stack spacing={3} direction="row" >
        {logoUrl && (
          <img
            alt="Logo"
            src={logoUrl}
            style={logoStyle}
            className="appbar-logo"
          />
        )}
        {logoText ? (
          <Box sx={logoTextStyle} alt="Logo text">
            {logoText}
          </Box>
        ) : ''}
      </Stack>
    );
  };

  const theLogo = logoUrl || logoText || renderLogo ? functionOrDefault(renderLogo, defaultLogoRenderer) : null;

  return (
    <Box sx={{ flexGrow: 1, height: '64px' }}>
      <MUIAppBar
        sx={{ zIndex: (theme) => theme.zIndex.drawer + 1 }}
        position="relative"
        {...props}
      >
        <Toolbar>
          <Box sx={{ flexGrow: 1 }}>
            {theLogo && theLogo(logoUrl, logoText)}
          </Box>

          {renderMenu()}
        </Toolbar>
      </MUIAppBar>
    </Box>
  );
};

AppBar.propTypes = {
  onLogin: PropTypes.func,
  onLogout: PropTypes.func,
  logoText: PropTypes.string,
  renderLogo: PropTypes.func,
  user: PropTypes.shape({}),
  navLinks: PropTypes.array,
  logoUrl: PropTypes.string,
  userLinks: PropTypes.array,
  buttonVariant: PropTypes.string,
  themeGroup: PropTypes.shape({}),
  showLoggingIn: PropTypes.bool,
};

AppBar.defaultProps = {
  navLinks: [
    { title: 'Home', href: '/' }
  ],
  user: null,
  logoUrl:
    'https://wilcity.com/wp-content/uploads/2018/12/sample-logo-design-png-3-2.png', //This is a lorum ipusum logo
};

export default AppBar;