Coursemology/coursemology2

View on GitHub
client/app/bundles/course/group/components/GroupCard.jsx

Summary

Maintainability
A
1 hr
Test Coverage
import {
  Button,
  Card,
  CardActions,
  CardContent,
  CardHeader,
  IconButton,
  Tooltip,
} from '@mui/material';
import PropTypes from 'prop-types';

export const groupCardTitleButtonShape = PropTypes.shape({
  label: PropTypes.oneOfType([PropTypes.string, PropTypes.element]).isRequired,
  onClick: PropTypes.func.isRequired,
  isDisabled: PropTypes.bool,
  icon: PropTypes.element,
});

export const groupCardBottomButtonShape = PropTypes.shape({
  label: PropTypes.oneOfType([PropTypes.string, PropTypes.element]).isRequired,
  onClick: PropTypes.func.isRequired,
  isDisabled: PropTypes.bool,
  icon: PropTypes.element,
  isRight: PropTypes.bool,
});

const styles = {
  card: {
    marginBottom: '2rem',
  },
  cardHeader: {
    display: 'flex',
    justifyContent: 'space-between',
    alignItems: 'center',
    width: '100%',
  },
  cardHeaderFullWidthTitle: {
    width: '100%',
    paddingRight: 0,
  },
  title: {
    fontWeight: 'bold',
    marginTop: '0.5rem',
    marginBottom: 0,
  },
  body: {
    paddingTop: 0,
  },
  actions: {
    padding: 16,
    display: 'flex',
    justifyContent: 'space-between',
  },
  buttonsContainer: {
    display: 'flex',
    justifyContent: 'center',
    alignItems: 'center',
  },
  nonLastButton: {
    marginRight: '1rem',
  },
  iconButton: {
    height: 36,
    width: 36,
    padding: 6,
  },
};

function mapButtonObjectToElement(button, isLast) {
  if (button.icon) {
    return (
      <Tooltip key={`tooltip_${button.label.props.id}`} title={button.label}>
        <IconButton
          key={button.label.props.id}
          onClick={button.onClick}
          style={{
            ...styles.iconButton,
            ...(isLast ? {} : styles.nonLastButton),
          }}
        >
          {button.icon}
        </IconButton>
      </Tooltip>
    );
  }
  return (
    <Button
      key={button.label.props.id}
      color="primary"
      onClick={button.onClick}
      style={isLast ? undefined : styles.nonLastButton}
      variant="contained"
    >
      {button.label}
    </Button>
  );
}

/**
 * A wrapper around MUI card to help standardise styling for groups.
 */
const GroupCard = ({
  title,
  subtitle,
  titleButtons = [],
  bottomButtons = [],
  className = '',
  children,
}) => (
  <Card className={className} style={styles.card}>
    {title || subtitle ? (
      <CardHeader
        subheader={subtitle}
        subheaderTypographyProps={{ variant: 'subtitle2' }}
        title={
          <div style={styles.cardHeader}>
            <h3 style={styles.title}>{title}</h3>
            {titleButtons.length > 0 && (
              <div style={styles.buttonsContainer}>
                {titleButtons.map((button, index) =>
                  mapButtonObjectToElement(
                    button,
                    index === titleButtons.length - 1,
                  ),
                )}
              </div>
            )}
          </div>
        }
        titleTypographyProps={
          titleButtons.length > 0
            ? { style: styles.cardHeaderFullWidthTitle }
            : {}
        }
      />
    ) : null}
    <CardContent style={styles.body}>{children}</CardContent>
    {bottomButtons.length > 0 ? (
      <CardActions style={styles.actions}>
        <div>
          {bottomButtons
            .filter((b) => !b.isRight)
            .map((button, index) =>
              mapButtonObjectToElement(
                button,
                index === titleButtons.length - 1,
              ),
            )}
        </div>
        <div>
          {bottomButtons
            .filter((b) => b.isRight)
            .map((button, index) =>
              mapButtonObjectToElement(
                button,
                index === titleButtons.length - 1,
              ),
            )}
        </div>
      </CardActions>
    ) : null}
  </Card>
);

GroupCard.propTypes = {
  title: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
  subtitle: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
  titleButtons: PropTypes.arrayOf(groupCardTitleButtonShape),
  bottomButtons: PropTypes.arrayOf(groupCardBottomButtonShape),
  className: PropTypes.string,
  children: PropTypes.oneOfType([
    PropTypes.array,
    PropTypes.element,
    PropTypes.string,
    PropTypes.object,
  ]).isRequired,
};

export default GroupCard;