Skip to content

Commit

Permalink
[Tech Debt] Update docs throughout React components
Browse files Browse the repository at this point in the history
  • Loading branch information
levinmr committed May 7, 2024
1 parent bfc1c9e commit e157c5d
Show file tree
Hide file tree
Showing 30 changed files with 315 additions and 65 deletions.
88 changes: 64 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,18 @@

## analytics.usa.gov

Analytics.usa.gov is a product of the [Digital Analytics Program (DAP)](https://github.com/digital-analytics-program/gov-wide-code), which collects and publishes website analytics from thousands of public-facing US federal government websites per the ["Delivering a Digital-First Public Experience"](https://www.whitehouse.gov/wp-content/uploads/2023/09/M-23-22-Delivering-a-Digital-First-Public-Experience.pdf) requirement.
Analytics.usa.gov is a product of the [Digital Analytics Program (DAP)](https://github.com/digital-analytics-program/gov-wide-code),
which collects and publishes website analytics from thousands of public-facing
US federal government websites per the ["Delivering a Digital-First Public Experience"](https://www.whitehouse.gov/wp-content/uploads/2023/09/M-23-22-Delivering-a-Digital-First-Public-Experience.pdf)
requirement.

## About the components

Ths app uses [Jekyll](https://jekyllrb.com) to build the site, and [Sass](https://sass-lang.com/) for CSS. Javascript provided is a [webpacked](https://webpack.js.org/) aggregation of [several different modules](#javascript-modules), leveraging [d3](https://d3js.org/) for the visualizations. [Learn more on the webpack configuration](#webpack-configuration)
Ths app uses [Jekyll](https://jekyllrb.com) to build the site, and [Sass](https://sass-lang.com/)
for CSS. Javascript provided is a [webpacked](https://webpack.js.org/)
aggregation of [several different modules](#javascript-modules), leveraging
[React](https://react.dev/) and [d3](https://d3js.org/) for the visualizations.
[Learn more on the webpack configuration](#webpack-configuration)

This is the main repository for https://analytics.usa.gov.
Additional repositories are:
Expand Down Expand Up @@ -34,8 +41,8 @@ npm install

Linters run on the static files for the repo and ensure that the code is free
from syntax issues and that code style conforms to community best practices.
These checks are also enforced in CI. Run the linters with the following
commands:
These checks are also enforced in CI where possible. Run the linters with the
following commands:

```bash
# JavaScript
Expand All @@ -47,6 +54,11 @@ npm run lint:js
npm run lint:styles
```

```bash
# HTML
npm run lint:html
```

### Run the unit tests

Unit tests ensure that code works as expected and can be helpful in finding
Expand All @@ -59,9 +71,9 @@ npm test

### Install git hooks

There are some git hooks provided in the `./hooks` directory to help with
common development tasks. These will checkout current NPM packages on branch
change events, and run the linters and unit tests on pre-commit.
There are git hooks provided in the `./hooks` directory to help with common
development tasks. These will checkout current NPM packages on branch change
events, and run the linters and unit tests on pre-commit.

Install the provided hooks with the following command:

Expand All @@ -84,11 +96,13 @@ make changes to the source files locally.

### Developing with local data

The development settings assume data is available at `/ga4-data`. You can change this in `_development.yml`.
The development settings assume data is available at `/ga4-data`. You can change
this in `_development.yml`.

### Developing with real live data from `analytics-reporter`

If also working off of local data, e.g. using `analytics-reporter`, you will need to make the data available over HTTP _and_ through CORS.
If also working off of local data, e.g. using `analytics-reporter`, you will
need to make the data available over HTTP _and_ through CORS.

Various tools can do this. This project recommends using the Node module `serve`:

Expand All @@ -108,17 +122,33 @@ Then run `serve` from the output directory:
serve --cors
```

The data will be available at `http://localhost:4000` over CORS, with no path prefix. For example, device data will be at `http://localhost:4000/devices.json`.
The data will be available at `http://localhost:4000` over CORS, with no path
prefix. For example, device data will be at `http://localhost:4000/devices.json`.

### Adding Additional Agencies

1. Ensure that data is being collected for a specific agency's Google Analytics ID. Visit [18F's analytics-reporter](https://github.com/18F/analytics-reporter) for more information. Save the url path for the data collection path.
1. Create a new json object in the `/_data/agencies.json` file. The `slug` attribute of the object will be the url path. The `name` attribute is the Agency's name.
1. Ensure that data is being collected for a specific agency's Google Analytics
ID. Visit [18F's analytics-reporter](https://github.com/18F/analytics-reporter)
for more information. Save the url path for the data collection path.
1. Create a new json object in the `/_data/agencies.json` file. The `slug`
attribute of the object will be the url path. The `name` attribute is the
Agency's name.

### Javascript Modules
* **Index** - The entry point for the webpack bundler. Includes all React components and rendering logic.
* **js/components** the top level directory containing all React component definitions. The components which render charts contain the d3 logic for rendering the chart within them.
* **lib/chart_helpers** helper functions containing d3 logic which is shared between multiple charts.

* **Index** - The entry point for the webpack bundler. This pulls in all React
component JS to make it available for rendering.
* **js/components** the top level directory containing all React component
definitions. The components which render charts contain the d3 logic for
rendering the chart within them. Since this is not a full react app due to
many pages being generated statically by Jekyll, there are index.js files within
some component directories to run createRoot from the react-dom library to
inject React components at certain elements
* **lib/chart_helpers** helper functions containing d3 logic which is shared
between multiple charts.
* **lib/touchpoints.js** third party code from the [Touchpoints](https://touchpoints.app.cloud.gov/)
project integration, included in this repo to avoid Content Security Policy
problems from remote-hosted scripts.

### Deploying the app

Expand All @@ -131,13 +161,15 @@ the development environment.
It shouldn't be necessary to deploy manually, but with the Cloud Foundry CLI
installed, follow these steps to deploy.

To deploy to **analytics.usa.gov** after building the site with the details in `_config.yml`:
To deploy to **analytics.usa.gov** after building the site with the details in
`_config.yml`:

```bash
make deploy_production
```

To deploy to **analytics-staging.app.cloud.gov** after building the site with the details in `_config.yml` and `_staging.yml`:
To deploy to **analytics-staging.app.cloud.gov** after building the site with
the details in `_config.yml` and `_staging.yml`:

```bash
make deploy_staging
Expand All @@ -153,17 +185,21 @@ make deploy_staging

### Webpack Configuration

The application compiles es6 modules into web friendly js via Wepback and the [babel loader](https://webpack.js.org/loaders/babel-loader/).
The application compiles es6 modules into web friendly js via Wepback and the
[babel loader](https://webpack.js.org/loaders/babel-loader/).

The webpack configuration is set in the [wepback.config.js](./webpack.config.js).

The current configuration uses babel `present-env`.

The webpack also includes linting using [eslint](https://eslint.org/) leveraging the [AirBnb linting preset](https://www.npmjs.com/package/eslint-config-airbnb).
The webpack also includes linting using [eslint](https://eslint.org/) leveraging
[Prettier](https://prettier.io/), as well as community recommended style
guidlines for eslint and react.

The webconfig uses the [TerserWebpackPlugin](https://webpack.js.org/plugins/terser-webpack-plugin/) to minimize the bundle.
The webconfig uses the [TerserWebpackPlugin](https://webpack.js.org/plugins/terser-webpack-plugin/)
to minimize the bundle.

The resulting uglified bundle is build into `assest/bundle.js`.
The resulting uglified bundle is built into `assest/bundle.js`.

#### NPM webpack commands

Expand Down Expand Up @@ -191,8 +227,12 @@ Other organizations who have reused this project for their analytics dashboard:

### Public domain

This project is in the worldwide [public domain](LICENSE.md). As stated in [CONTRIBUTING](CONTRIBUTING.md):
This project is in the worldwide [public domain](LICENSE.md). As stated in
[CONTRIBUTING](CONTRIBUTING.md):

> This project is in the public domain within the United States, and copyright and related rights in the work worldwide are waived through the [CC0 1.0 Universal public domain dedication](https://creativecommons.org/publicdomain/zero/1.0/).
> This project is in the public domain within the United States, and copyright
> and related rights in the work worldwide are waived through the [CC0 1.0 Universal public domain dedication](https://creativecommons.org/publicdomain/zero/1.0/).
>
> All contributions to this project will be released under the CC0 dedication. By submitting a pull request, you are agreeing to comply with this waiver of copyright interest.
> All contributions to this project will be released under the CC0 dedication.
> By submitting a pull request, you are agreeing to comply with this waiver of
> copyright interest.
18 changes: 3 additions & 15 deletions _includes/charts.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,9 @@
= "agency" %} {% endif %}

<!--
JavaScript block hooks look for any element with both data-block and
data-source attributes:
<section data-block="users" data-source="path/to/data.json">
The "loading", "loaded", and "error" classes are set on this element
the data-source URL is loaded. Then the renderer is called on the first
child of the block with the "data" class:
<div class="data">
</div>
Some renderers expect to be called on HTML tables, in which
case you would use:
<table class="data">
</table>
</section>
-->

The contents of this page are populated by react components. See
./js/components/main_charts for the rendering logic.
-->
<main
id="main-charts-root"
class="grid-row"
Expand Down
5 changes: 5 additions & 0 deletions _includes/historical_data_download.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<!--
The contents of this page are populated by react components. See
./js/components/historical_data_download for the rendering logic.
-->
<main id="historical-data-download-root" dataURL="{{ site.data_url }}"></main>
25 changes: 4 additions & 21 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ module.exports = [
},
},
{
// Ignore the locally built site with minified JS.
// Include recommended linting rules from eslint, react, and prettier
//
// When linting ignore the locally compiled minified JS, other assets, and
// touchpoints JS which is third party code copied into this repo.
ignores: [
"_site/**/*.js",
"assets/**/*",
Expand All @@ -29,27 +32,7 @@ module.exports = [
"js/lib/touchpoints.js",
],
...eslintConfigs.recommended,
},
{
// Ignore the locally built site with minified JS.
ignores: [
"_site/**/*.js",
"assets/**/*",
"sass/**/*",
"ga4-data/**/*",
"js/lib/touchpoints.js",
],
...reactRecommended,
},
{
// Ignore the locally built site with minified JS.
ignores: [
"_site/**/*.js",
"assets/**/*",
"sass/**/*",
"ga4-data/**/*",
"js/lib/touchpoints.js",
],
...eslintPluginPrettierRecommended,
},
];
2 changes: 1 addition & 1 deletion js/components/agency_select/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import AgencySelect from "./AgencySelect";

/**
* Renders an AnalyticsSelect React component, when there is an element on the
* current page with id '#analytics-agency-select-root'.
* current page with id 'analytics-agency-select-root'.
*
* The AgencySelect component will be rendered as a child to the matching
* element.
Expand Down
15 changes: 15 additions & 0 deletions js/components/data_downloads/DataDownloads.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
import React from "react";
import PropTypes from "prop-types";

/**
* Creates tables with download links for many of the reports that are available
* in the analytics.usa.gov project's S3 bucket. Downloads are labelled by human
* readable description and by how often the report is updated. Downloads are
* available in JSON and CSV format.
*
* This component is using USWDS grid classes and expects it's parent element to
* have class 'grid-row'
*
* @param {String} dataURL the URL of the base location of the data to be
* downloaded. In production this is proxied and redirected to the S3 bucket URL
* by NGINX.
* @param {String} dataPrefix the path to add to the base URL to find data for
* the current agency.
*/
function DataDownloads({ dataURL, dataPrefix }) {
const hrefBase = `${dataURL}/${dataPrefix}`;
return (
Expand Down
2 changes: 1 addition & 1 deletion js/components/data_downloads/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import DataDownloads from "./DataDownloads";

/**
* Renders an DataDownloads React component, when there is an element on the
* current page with id '#data-downloads-root'.
* current page with id 'data-downloads-root'.
*
* The DataDownloads component will be rendered as a child to the matching
* element.
Expand Down
10 changes: 10 additions & 0 deletions js/components/main_charts/AverageEngagementDuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ import d3 from "d3";
import renderBlock from "../../lib/chart_helpers/renderblock";
import formatters from "../../lib/chart_helpers/formatters";

/**
* Retrieves the engagement duration report from the passed data URL and creates
* a visualization for the average engagement duration of users for the
* current agency. The average duration is calculated by taking the total user
* engagement duration and dividing by the number of visits.
*
* @param {String} dataHrefBase the URL of the base location of the data to be
* downloaded including the agency path. In production this is proxied and
* redirected to the S3 bucket URL.
*/
function AverageEngagementDuration({ dataHrefBase }) {
const dataURL = `${dataHrefBase}/engagement-duration-30-days.json`;
const ref = useRef(null);
Expand Down
9 changes: 9 additions & 0 deletions js/components/main_charts/BrowsersChart.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ import d3 from "d3";

import renderBlock from "../../lib/chart_helpers/renderblock";

/**
* Retrieves the browser report from the passed data URL and creates a
* visualization for the breakdown of browsers of users visiting sites for the
* current agency.
*
* @param {String} dataHrefBase the URL of the base location of the data to be
* downloaded including the agency path. In production this is proxied and
* redirected to the S3 bucket URL.
*/
function BrowsersChart({ dataHrefBase }) {
const dataURL = `${dataHrefBase}/browsers.json`;
const ref = useRef(null);
Expand Down
10 changes: 10 additions & 0 deletions js/components/main_charts/DeviceDemographics.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ import DevicesChart from "./DevicesChart";
import BrowsersChart from "./BrowsersChart";
import OperatingSystemsChart from "./OperatingSystemsChart";

/**
* Contains charts and other data visualizations for the user demographics
* section of the site. This component is mainly laying out the structure for
* the section and passes props necessary for getting data and displaying
* visualizations to child components.
*
* @param {String} dataHrefBase the URL of the base location of the data to be
* downloaded including the agency path. In production this is proxied and
* redirected to the S3 bucket URL.
*/
function DeviceDemographics({ dataHrefBase }) {
return (
<>
Expand Down
9 changes: 9 additions & 0 deletions js/components/main_charts/DevicesChart.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ import formatters from "../../lib/chart_helpers/formatters";
import renderBlock from "../../lib/chart_helpers/renderblock";
import transformers from "../../lib/chart_helpers/transformers";

/**
* Retrieves the devices report from the passed data URL and creates a
* visualization for the breakdown of devices of users visiting sites for the
* current agency.
*
* @param {String} dataHrefBase the URL of the base location of the data to be
* downloaded including the agency path. In production this is proxied and
* redirected to the S3 bucket URL.
*/
function DevicesChart({ dataHrefBase }) {
const dataURL = `${dataHrefBase}/devices.json`;
const ref = useRef(null);
Expand Down
10 changes: 10 additions & 0 deletions js/components/main_charts/Engagement.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ import PropTypes from "prop-types";
import EngagementRate from "./EngagementRate";
import AverageEngagementDuration from "./AverageEngagementDuration";

/**
* Contains charts and other data visualizations for the user engagement section
* of the site. This component is mainly laying out the structure for the
* section and passes props necessary for getting data and displaying
* visualizations to child components.
*
* @param {String} dataHrefBase the URL of the base location of the data to be
* downloaded including the agency path. In production this is proxied and
* redirected to the S3 bucket URL.
*/
function Engagement({ dataHrefBase }) {
return (
<>
Expand Down
9 changes: 9 additions & 0 deletions js/components/main_charts/EngagementRate.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ import d3 from "d3";
import renderBlock from "../../lib/chart_helpers/renderblock";
import formatters from "../../lib/chart_helpers/formatters";

/**
* Retrieves the engagement rate report from the passed data URL and creates a
* visualization for the engagement of users visiting sites for the current
* agency.
*
* @param {String} dataHrefBase the URL of the base location of the data to be
* downloaded including the agency path. In production this is proxied and
* redirected to the S3 bucket URL.
*/
function EngagementRate({ dataHrefBase }) {
const dataURL = `${dataHrefBase}/engagement-rate-30-days.json`;
const ref = useRef(null);
Expand Down
10 changes: 10 additions & 0 deletions js/components/main_charts/LocationsAndLanguages.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ import TopCitiesRealtime from "./TopCitiesRealtime";
import TopCountriesRealtime from "./TopCountriesRealtime";
import TopLanguagesHistorical from "./TopLanguagesHistorical";

/**
* Contains charts and other data visualizations for the user locations and
* languages section of the site. This component is mainly laying out the
* structure for the section and passes props necessary for getting data and
* displaying visualizations to child components.
*
* @param {String} dataHrefBase the URL of the base location of the data to be
* downloaded including the agency path. In production this is proxied and
* redirected to the S3 bucket URL.
*/
function LocationsAndLanguages({ dataHrefBase }) {
return (
<>
Expand Down
Loading

0 comments on commit e157c5d

Please sign in to comment.