2.0.0 • Published 2 years ago

reis-integrations v2.0.0

Weekly downloads
-
License
-
Repository
-
Last release
2 years ago

REIS Web Components

Development

To start this repo in dev mode, run npm run dev. To start either the Screenshots or Documentation apps, run npm run docs

This repo uses @moodysanalytics/se3-react package. to run both projects in dev mode:

  1. On se3-react, run: npm run publishedComponents:dev
  2. On this repo, run: npm run dev:se3-local (restart this command to see types updated)

To come back to use the remote se3-react package, run npm i

Create Web Components

The web-compoonents are generated using the react-to-webcomponent library. Inside src/web-components folder there are all the react components that would be exported as a web component.

To export a new web component needs to use the createElement function on the src/index.tsx file. This function only requires as params the react component and the web component name with kebab-case.

Example:

import { AreaKPIs } from "./web-components/AreaKPIs/AreaKPIs";
import { createElement } from "./reactToWebComponent";

createElement(AreaKPIs, "reis-area-kpi");

To test the web component in development we use the public/index.html file. This file does not affect the deployed bundle.js. Only need add the web component:

<reis-area-kpi
  area-type="submarket"
  area-id="Apt,PX,10"
  api-key="superSecretKey"
  listen-set-area-event="true"
></reis-area-kpi>

Web component props

To handle the web component props, they must be added as default props with kebab-case.

AreaKPIs.defaultProps = {
  "area-type": null,
  "area-id": null,
  "api-key": null,
  "listen-set-area-event": false,
};

Web Comopnent styles

The web components created on this app are using a shadow dom to isolate the styles from the client app where they are used. For this reason, the component styles should be added inside the shadow dom.

To be able to use hot reload on development, the web components have no shadow dom on running npm run dev and the styles are embedded on the head with webpack as usual.

WCStyles

The WCStyles component is in charge to add the styles inside the shadow dom, it simply adds a <link rel="stylesheet"... with the propper url (uses PUBLIC_URL env var). This component should be added 1 time per web compoennt.

Example:

import { WCStyles } from "wc-shared/WCStyles/WCStyles";

render() {
  return (
    <>
      <WCStyles />
      <ComponentConsumingStyles />
    </>
  )
}

CSS Modules

CSS modules have full support on web components, same as se3React app.

Emotion CSS

Emotion css adds inline styles dynamically on the page header. Those styles are not available from inside the shadow dom.

The solution adopted is to use the custom component EmotionStyleSheetProvider on each web component (1 time per we-component) that uses the Emotion-css library. This component creates a style sheet provider to indicate a custom position to add the css dynamically instead of use the page header.

Web Components shared state

There is no state shared between components. Each component ask the data they need to our backend, the backend has a cache to fast response if the same data has been asked by many components.

The custom browser events are used to handle actions on components that affects other components state. A good example is the map component. Each click on a map polygon, should change the selected area, so each click emits an SET_AREA event that are listened by all the components that requires area data.

Example to emit event on the map component:

const onPolygonClick = (areaId: string) => {
  const areaEvent = new CustomEvent("SET_AREA", {
    bubbles: true,
    composed: true,
    detail: { areaType, areaId },
  });

  document.dispatchEvent(areaEvent);
};

Example to listen the area Event with a custom hook that can be used on any component:

// This custom hook listen the SET-AREA event and updates the state
export const useArea = ({ defaultArea, listenEventName }: UseArea) => {
  const [area, setArea] = useState(defaultArea);

  useEffect(() => {
    if (listenEventName) {
      const fn = (e: any) => {
        setArea(e.detail);
      };

      document.addEventListener(listenEventName, fn);
      return () => document.removeEventListener(listenEventName, fn);
    }
  });

  return area;
};

Web components data

The components created on se3 app gets their data from the se3-backend. As the client has not direct access to se3-backend, the web component is in charge to get this data after validate the user. To do this in a clean way, each web component has a Wrapper component that is in charge of retrieve the data.

Example:

export const AreaAskingRentVacancyTrendsWrapper = ({
  // transform props names from kebab-case (web-components) to camel-case (react)
  "api-key": apiKey,
  "area-type": defaultAreaType,
  "area-id": defaultAreaId,
  "listen-set-area-event": listenSetAreaEvent,
  "save-button": saveButton,
}: AreaAskingRentVacancyTrendsWrapperProps) => {
  // Listen area event, and get current area
  const { areaType, areaId } = useArea({
    defaultArea: {
      areaType: defaultAreaType,
      areaId: defaultAreaId,
    },
    listenEventName: listenSetAreaEvent ? AreaEvents.SET_AREA : undefined,
  });

  // Get data for current area
  const { data, loading, error } = useGetArea({
    areaType,
    areaId,
    apiKey,
  });

  if (error) {
    return (
      <LoadingContainer kind="error">
        Error loading area rent vacancy
      </LoadingContainer>
    );
  }

  if (loading) {
    return <LoadingContainer height="479px" />;
  }

  return (
    <AreaAskingRentVacancyTrends
      sectorId={data?.area?.sectorId}
      data={data?.areaRentAndVacancyTrends}
    />
  );
};

User validation to access SE3-backend data

Each component has an api-key prop. Web-Components-backend handle the valid api-keys and assign a list of whitelisted domains for each one. If this is valid, the Web-Components-backend is in charge to do the okta validation to the SE3-backend and retrieve the requested data.

Consume Web Comoponents

This repo uses react-create-app with some webpack modifications using Craco.

react-create-app generates a build folder with html, assets and js files. There is a simple gulp script to get all the js files from there and join them in a single versioned bundle on the dist folder that is consumed from an aws-s3 bucket.

For the most part, each component requires only a script to work (special cases are indicated on the documentation). Example:

<html>
  <head>
    <script src="https://web-components-fe.reis.com/bundle-v2.js"></script>
  </head>

  <body>
    <reis-area-kpi
      area-type="submarket"
      area-id="Apt,PX,10"
      api-key="superSecretKey"
      listen-set-area-event="true"
    ></reis-area-kpi>
  </body>
</html>

Web components documentation

The documentation folder handles a way to present documentation of each component to the client. It has an editable live preview of the component with the full html required to work. The staging docs are deployed here.

For display the documentation locally, run : npm run docs. This script run a build process, then it runs a local server thats open the documentation page.

By default opens the documentation pointing to staging. That is handled by the env vars on .env and /apps/shared/env.js. Optionally can be used query params to point to another env or another version:

  • env: development, staging or production
  • v: version number

For add a new component to the documentation follow these steps:

  1. Add a new folder on /apps/documentation/components with the component name
  2. Add these files on the folder:
    • index.js: exports the complete html required for the component as string
    • documentation.md: with all the required documentation in markdown format
  3. Add a new item on the comopnentList array on /apps/documentation/components/index.js with:
    {
      value: <the component name>,
      label: <the component display name, can have spaces>,
      previewHtml: <the imported content from index.js>
    }

Handle versions

The version number is handled by package.json file. If the major version is changed, the REACT_APP_MAJOR_VERSION var should be updated on the env vars and on the /apps/shared/env.js file. The last change is required to set the default version for the documentation page.

Web components screenshots

The screenshots of the web-components are handled by a backend service that uses puppeteer to capture screenshots of an specific url.

The url to get the screenshots is an app that lives in apps/screenshots. This app renders the required web component reading the url query params. The component query param is the component name, the env is the environment where to find the bundle.js file and all the rest are the component props.

To test this backend service there is a section in the documentation with more information and a form to test each component screenshot.

SE3 vs Web component differences

  • react-tiny-popover version: updated to v6.0.0
  • percentileRankSelect and AssetClassSelect components have an additional override prop for it's dropdown. If override is not null the dropdown is disabled and set to the override value
  • no-breakdown-link prop on Commercial Location Score is used to hide the "See Breakdown" hover-over link for taking screenshots of the components for Credit Lens reports
  • Download button is hidden on comps
  • Comps is limited to 50 properties
  • CLS component has additional props - "percentile-rank" and "asset-class" which passes the values down to the realted dropdowns. We also have a prop disable-dropdowns which disables both dropdowns and uses the components default value
  • map-search-table has max-selection prop that filters selected properties to the chosen max-selection.