AEM 6560 - React SPA component for showing Alert Banner using Material UI

Goal

Add AEM SPA React Alert component showing a banner created using Material UI (MUI) at the top of page. Using MUI there are no global style-sheets, each component is independent and there are no css conflicts at page level. Responsive page layout is created using AEM Grid...

Thank you Joe Ritchie and team...

Demo | Package Install | Github


Banner (View as Published)



Component Dialog in Template



Solution

1) Create the project structure (for both React SPA and MPA authoring) with the following command using maven archetype - https://github.com/adobe/aem-project-archetype

mvn -B archetype:generate -D archetypeGroupId=com.adobe.granite.archetypes -D archetypeArtifactId=aem-project-archetype 
-D archetypeVersion=23  -D aemVersion=6.5.0  -D appTitle="Experience AEM SPA React"  -D appId="eaem-sites-spa-how-to-react"  -D groupId="com.eaem"  
-D frontendModule=react  -D includeExamples=n  -D includeErrorHandler=n -D includeDispatcherConfig=n

2) Remove all additional components created, except the following required for testing... (or download Package Install)

                                                          /apps/eaem-sites-spa-how-to-react/components/spa
                                                          /apps/eaem-sites-spa-how-to-react/components/page
                                                          /apps/eaem-sites-spa-how-to-react/components/text

3) Open a command prompt (terminal) at eaem-sites-react-spa-material-ui-banner\ui.frontend and install the typscript and material ui specific dependencies

                                                          npm install typescript
                                                          npm install @material-ui/core
                                                          npm install classnames

4) Create the component /apps/eaem-sites-spa-how-to-react/components/alert. In the next step we'd be creating the react render type script...



5) Add the component render script in eaemeaem-sites-react-spa-material-ui-banner\ui.frontend\src\components\Alert\EAEMAlert.tsx with the following code...

import { MapTo } from "@adobe/cq-react-editable-components";
import React, { FC, useState, useEffect } from "react";
import { Link } from "react-router-dom";
import {
  IconButton,
  Typography,
  createStyles,
  makeStyles,
  Theme,
  Portal,
  SvgIcon,
  SvgIconProps,
  Collapse
} from "@material-ui/core";
import CSS from "csstype";
import classNames from "classnames";

const iconStyles = makeStyles(() =>
  createStyles({
    root: {
      fontSize: 20
    }
  })
);

const EAEMCloseIcon: FC<SvgIconProps> = props => {
  const classes = iconStyles();

  return (
    <SvgIcon
      viewBox="0 0 20 20"
      {...props}
      className={classNames(classes.root, props.className)}
    >
      <title>Combined Shape</title>
      <desc>Created with Sketch.</desc>
      <g
        id="Symbols"
        stroke="none"
        strokeWidth="1"
        fill="none"
        fillRule="evenodd"
      >
        <g id="Grommet/X-Close" transform="translate(-15.000000, -15.000000)">
          <rect id="Rectangle" x="0" y="0" width="50" height="50"></rect>
          <path
            d="M34.3548387,15 L35,15.6451613 L25.645,24.999 L35,34.3548387 L34.3548387,35 L25,25.645 L15.6451613,35 L15,34.3548387 L24.354,25 L15,15.6451613 L15.6451613,15 L25,24.354 L34.3548387,15 Z"
            id="Combined-Shape"
            fill="currentColor"
          ></path>
        </g>
      </g>
    </SvgIcon>
  );
};

type AlertProps = {
  showAlert: string;
  text: string;
  linkURL: string;
};

const AlertEditConfig = {
  emptyLabel: "Alert - Shows banner at the top of page",

  isEmpty: function (props: any) {
    return !props || !props.text || props.text.trim().length < 1;
  }
};

const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    closeIcon: {
      fontSize: 12,
      color: "white",
      [theme.breakpoints.up("sm")]: {
        fontSize: 16
      }
    },
    container: {
      alignItems: "center",
      background: "black",
      display: "flex"
    },
    content: {
      color: "white",
      paddingTop: "15px",
      paddingBottom: "15px",
      flex: "1 1",
      fontFamily: 'Times, serif',
      fontSize: 16,
      textAlign: "center"
    }
  })
);

const EAEMAlert: FC<AlertProps> = props => {
  const classes = useStyles();

  const [open, setOpen] = useState(true);
  const [root, setRoot] = useState<HTMLElement | null>(null);

  useEffect(() => {
    setRoot(document.getElementById("eaem-alert-banner"));
  }, [root]);

  const handleClose = () => {
    setOpen(false);
  };

  if (!props.text || props.showAlert != "true") {
    return null;
  }

  let text = props.text.trim();

  if (text.startsWith("<p>") && text.endsWith("</p>")) {
    text = text.substring(3, text.lastIndexOf("</p>"));
  }

  return (
    <Portal container={root}>
      <Collapse in={open}>
        <Typography className={classes.container} component={"div"}>
          <div
            className={classes.content}
            dangerouslySetInnerHTML={{ __html: text }}
          />
          <IconButton onClick={handleClose}>
            <EAEMCloseIcon className={classes.closeIcon} />
          </IconButton>
        </Typography>
      </Collapse>
    </Portal>
  );
};

export default MapTo("eaem-sites-spa-how-to-react/components/alert")(EAEMAlert, AlertEditConfig);

6) Add the EAEMAlert.tsx path in eaem-sites-react-spa-material-ui-banner\ui.frontend\src\components\import-components.js

                                                          import './Page/Page';
                                                          import './Text/Text';
                                                          import './Title/Title';
                                                          import './Nav/Nav';
                                                          import './Image/Image';
                                                          import './Alert/EAEMAlert';


No comments:

Post a Comment