diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..209440ea29 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +# Keep docker image clean of any development-generated artifacts by ignoring them on COPY +node_modules +public diff --git a/.gitignore b/.gitignore index 631d92dd9d..2f61b085dc 100644 --- a/.gitignore +++ b/.gitignore @@ -61,7 +61,7 @@ usa_spending.sh /scripts/sitemaps/sitemapFiles/*.xml # direnv -.envrc +\.envrc # IDE stuff .vscode diff --git a/Dockerfile b/Dockerfile index 05c2fa71ac..f4da5ffdae 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,13 +1,22 @@ FROM node:12.18.4 -RUN mkdir /node-workspace -COPY package.json /node-workspace -COPY package-lock.json /node-workspace +# Default environment variables +ENV ENV=prod USASPENDING_API=https://api.usaspending.gov/api/ MAPBOX_TOKEN='' GA_TRACKING_ID='' + +RUN mkdir /node-workspace && mkdir /test-results + +# Copy JUST the package files first. +# This allows Docker to NOT re-fetch all NPM packages if neither of these two files +# have changed, and instead use the cached layer containing all those dependent packages. +# Greatly speeds up repeated docker build calls on the same machine (like CI/CD boxes) +# by leveraging the docker image cache +COPY package.json package-lock.json /node-workspace/ WORKDIR /node-workspace +# Clean Node module dependencies and install them fresh RUN npm ci +# Now copy the remaining source files +# Files in .dockerignore will not be copied COPY . /node-workspace - -RUN mkdir /test-results diff --git a/README.md b/README.md index 23f260dfa8..4dd99dbdb5 100644 --- a/README.md +++ b/README.md @@ -5,74 +5,86 @@ [The USAspending website](https://www.usaspending.gov/) is the public-facing site offering information on Government spending for the United States. It utilizes the [Data Transparency User Interface Library](https://github.com/fedspendingtransparency/data-transparency-ui). ## Development Set Up -To run the USAspending website locally, follow the directions below. +- To contribute to frontend development, follow _Install and Configure_ below. +- To simply run the USAspending website locally, jump to _Build and Run with Docker_ below. -Assumptions: +_Assumptions_: * You're able to install software on your machine. * You have git installed on your machine and are able to clone code repositories from GitHub. If this isn't the case, the easiest way to get started is to install [GitHub Desktop](https://desktop.github.com/ "GitHub desktop"), available for Windows or Mac. * You're familiar with opening a terminal on your machine and using the command line as needed. -### Install Prerequisites and Code - -1. If you're not already running Node.js, download and run the installer for your operating system. We build using **Node.js 10.15.3 (LTS)** which we recommend downloading using [Node Version Manager (nvm)](https://github.com/nvm-sh/nvm). - -2. You also need npm. We use version `6.14.5`. - -```shell - npm i -g npm@6.14.5 -``` - -3. From the command line, clone the USAspending website repository from GitHub to your local machine: - -```shell - git clone https://github.com/fedspendingtransparency/usaspending-website.git -``` - -4. Change to the `usaspending-website` directory that was created when you cloned the USAspending Website repository. - -```shell - cd usaspending-website -``` - -5. Perform a clean install to get an exact dependency tree: - -```shell - npm ci -``` - -### Configuration File - -The USAspending website expects a certain number of environment variables to be defined at runtime. The important ones are listed below along with their default values: - -| ENV VAR | VALUE | -|----------------|----------------------------------| -| USASPENDING_API| https://api.usaspending.gov/api/ | -| MAPBOX_TOKEN | '' | -| GA_TRACKING_ID | '' | +### Install and Configure +_Get the code and install the runtime and dependencies_ + +1. From the command line, clone the USAspending website repository from GitHub to your local machine, and get into the root directory: + ```shell + $ git clone https://github.com/fedspendingtransparency/usaspending-website + $ cd usaspending-website + ``` +1. Download [Node Version Manager (`nvm`)](https://github.com/nvm-sh/nvm) and install **Node.js `12.18.4`** + ```shell + $ nvm install 12.18.4 + ``` +1. Set Node Package Manager (`npm`) CLI to version `6.14.5`. + ```shell + $ npm i -g npm@6.14.5 + ``` +1. Perform a clean install to get an exact dependency tree: + ```shell + $ npm ci + ``` +1. Run the site in a local dev server: + ```shell + $ npm start + ``` + +### Configuration +_Alter configuration to non-default values by changing environment variable values._ + +Our site makes use of certain environment variables. Defaults are provided, but can be overridden by `export` of new values in your local shell. These are the important ones: + +| ENV VAR | DEFAULT VALUE (if not set) | PURPOSE +|-------------------|----------------------------------|-----------------------------------------------------| +| `ENV` | prod | Determine bundling optimizations and feature flags | +| `USASPENDING_API` | https://api.usaspending.gov/api/ | API URL to get backend data | +| `MAPBOX_TOKEN` | '' | Product key for use of MapBox features | +| `GA_TRACKING_ID` | '' | Google Analytics key for anonymously tracking usage | ### Scripts +_Custom and life-cycle scripts to execute, as defined under the `scripts` property in `package.json`_ + +| Script | Output | +|--------------|-------------------------------------------------------------| +| `npm start` | dev server with hot reloading | +| `npm prod` | generates static assets w/ production optimization | +| `npm dev` | generates static assets w/ development optimization | +| `npm lint` | executes linter | +| `npm test` | executes unit tests | +| `npm sass` | dev server w/ scss source maps | +| `npm travis` | executes travis validation script | +| `npm ci` | clean existing Node dependencies and install dependencies | + +### Build and Run with Docker +Docker can be used to build static site artifacts and/or run the site locally. Ensure your environment variables are configured and use this "one-liner" (or decompose and run each `docker` command separately): + +```bash +docker build -t usaspending-website . && \ +docker run --rm -v $(pwd)/public:/node-workspace/public \ + -e ENV \ + -e USASPENDING_API \ + -e MAPBOX_TOKEN \ + -e GA_TRACKING_ID \ + usaspending-website npm run ${ENV} && \ +docker run --rm -v $(pwd)/public:/usr/share/nginx/html:ro -p 8020:80 nginx:1.18 +``` -| Script | Output | -|------------|-----------------------------------------------------| -| `npm start` | dev server with hot reloading | -| `npm prod` | generates static assets w/ production optimization | -| `npm dev` | generates static assets w/ development optimization | -| `npm lint` | executes linter | -| `npm test` | executes unit tests | -| `npm sass` | dev server w/ scss source maps | -| `npm travis` | executes travis validation script | +_The first two steps can take 5+ minutes each, so this could be a total of ~10 minutes before ready._ -### Deployment Build Step with DockerFile -When deploying we use the dockerfile for our build step. We can simulate that process as follows: +What this does: +1. Ensure your `usaspending-website` Docker image has the latest dependencies: +1. Generate and bundle static artifacts and output to `./public` +1. Mount the static artifacts to a running container of an nginx image and host at port `8020` -``` -# from usaspending-website root, build the container image -docker build -t usaspending-website . -# generate static artifacts in ./public -docker run -i --rm=true -v $(pwd)/public:/node-workspace/public usaspending-website /bin/sh -c 'npm run dev' -# mount the static artifacts to an nginx image and run -docker run -p 8020:80 -v $(pwd)/public:/usr/share/nginx/html:ro nginx -``` -The app should now be running at `http://localhost:8020`. +You should see console logs, and the app should now be running at `http://localhost:8020`. diff --git a/package-lock.json b/package-lock.json index face3ee336..e40e8d70a7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2057,9 +2057,9 @@ } }, "parse-json": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", - "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", @@ -3114,9 +3114,9 @@ } }, "parse-json": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", - "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", @@ -3683,9 +3683,9 @@ "integrity": "sha512-dyQxe6ukILV6qaEvxoKCIwhblgRjYp1ZGlClo4xvfbmxzFO5LYu7Tnrg2AZrRgN7VsSragsGcNjzUe9kCdKHYQ==" }, "@mapbox/tiny-sdf": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-1.1.1.tgz", - "integrity": "sha512-Ihn1nZcGIswJ5XGbgFAvVumOgWpvIjBX9jiRlIl46uQG9vJOF51ViBYHF95rEZupuyQbEmhLaDPLQlU7fUTsBg==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-1.2.0.tgz", + "integrity": "sha512-gy4o8kxsIQLSbY1etb+swWeXTateN6C9DrHeArrHsxuAnQFCh9MEKvy3b0C6QyMYZcrG6QEz4sJ/zr/ud9Zlgw==" }, "@mapbox/unitbezier": { "version": "0.0.0", @@ -4272,9 +4272,9 @@ } }, "@testing-library/user-event": { - "version": "12.6.0", - "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-12.6.0.tgz", - "integrity": "sha512-FNEH/HLmOk5GO70I52tKjs7WvGYckeE/SrnLX/ip7z2IGbffyd5zOUM1tZ10vsTphqm+VbDFI0oaXu0wcfQsAQ==", + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-12.6.1.tgz", + "integrity": "sha512-wirJeUQ1QE9jSdARFu3riFeBPac2SDhHNKMt1MSfmBb4xNfpVIiaMqfP5mTNlMQHq1ULCD7sxtmyfQrzQYFDpQ==", "dev": true, "requires": { "@babel/runtime": "^7.12.5" @@ -4513,9 +4513,9 @@ } }, "@types/json-schema": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", - "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==" + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", + "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==" }, "@types/json5": { "version": "0.0.29", @@ -4538,9 +4538,9 @@ "dev": true }, "@types/node": { - "version": "14.14.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.21.tgz", - "integrity": "sha512-cHYfKsnwllYhjOzuC5q1VpguABBeecUp24yFluHpn/BQaVxB1CuQ1FSRZCzrPxrkIfWISXV2LbeoBthLWg0+0A==", + "version": "14.14.22", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.22.tgz", + "integrity": "sha512-g+f/qj/cNcqKkc3tFqlXOYjrmZA+jNBiDzbP3kH+B+otKFqAdPgVTGP1IeKRdMml/aE69as5S4FqtxAbl+LaMw==", "dev": true }, "@types/normalize-package-data": { @@ -6586,14 +6586,14 @@ } }, "caniuse-db": { - "version": "1.0.30001177", - "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30001177.tgz", - "integrity": "sha512-bu5ZkVbh3c1i17W/AtuQ2O16jeDKIS8aTtAW1x+b+nYBWLb6x0lMrHfSHXuD+0eJnuKUBYFsZW4BlmvGoT0KgA==" + "version": "1.0.30001179", + "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30001179.tgz", + "integrity": "sha512-YgqeMhTgW75OI7emeIpVTol3E2EQTQJqUJp/R1uA6aYzXOSyvGAGAkadvSdYqB/JPDBkXWwT3fLSxA+AuqttlA==" }, "caniuse-lite": { - "version": "1.0.30001177", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001177.tgz", - "integrity": "sha512-6Ld7t3ifCL02jTj3MxPMM5wAYjbo4h/TAQGFTgv1inihP1tWnWp8mxxT4ut4JBEHLbpFXEXJJQ119JCJTBkYDw==" + "version": "1.0.30001179", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001179.tgz", + "integrity": "sha512-blMmO0QQujuUWZKyVrD1msR4WNDAqb/UPO1Sw2WWsQ7deoM5bJiicKnWJ1Y0NS/aGINSnKPIWBMw5luX+NDUCA==" }, "canvas-composite-types": { "version": "1.0.4", @@ -7456,16 +7456,16 @@ } }, "core-js": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.8.2.tgz", - "integrity": "sha512-FfApuSRgrR6G5s58casCBd9M2k+4ikuu4wbW6pJyYU7bd9zvFc9qf7vr5xmrZOhT9nn+8uwlH1oRR9jTnFoA3A==" + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.8.3.tgz", + "integrity": "sha512-KPYXeVZYemC2TkNEkX/01I+7yd+nX3KddKwZ1Ww7SKWdI2wQprSgLmrTddT8nw92AjEklTsPBoSdQBhbI1bQ6Q==" }, "core-js-compat": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.8.2.tgz", - "integrity": "sha512-LO8uL9lOIyRRrQmZxHZFl1RV+ZbcsAkFWTktn5SmH40WgLtSNYN4m4W2v9ONT147PxBY/XrRhrWq8TlvObyUjQ==", + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.8.3.tgz", + "integrity": "sha512-1sCb0wBXnBIL16pfFG1Gkvei6UzvKyTNYpiC41yrdjEv0UoJoq9E/abTMzyYJ6JpTkAj15dLjbqifIzEBDVvog==", "requires": { - "browserslist": "^4.16.0", + "browserslist": "^4.16.1", "semver": "7.0.0" }, "dependencies": { @@ -7489,9 +7489,9 @@ } }, "core-js-pure": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.8.2.tgz", - "integrity": "sha512-v6zfIQqL/pzTVAbZvYUozsxNfxcFb6Ks3ZfEbuneJl3FW9Jb8F6vLWB6f+qTmAu72msUdyb84V8d/yBFf7FNnw==", + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.8.3.tgz", + "integrity": "sha512-V5qQZVAr9K0xu7jXg1M7qTEwuxUgqr7dUOezGaNa7i+Xn9oXAU/d1fzqD9ObuwpVQOaorO5s70ckyi1woP9lVA==", "dev": true }, "core-util-is": { @@ -8288,8 +8288,8 @@ } }, "data-transparency-ui": { - "version": "github:fedspendingtransparency/data-transparency-ui#5e44f7f1b069aaace61cd161dd5d220fb05a42fc", - "from": "github:fedspendingtransparency/data-transparency-ui#v1.3.6", + "version": "github:fedspendingtransparency/data-transparency-ui#4c2ce1c0fb0e8071a2e55e15204e5dae464e06f9", + "from": "github:fedspendingtransparency/data-transparency-ui#v1.3.7", "requires": { "@fortawesome/fontawesome-svg-core": "^1.2.25", "@fortawesome/free-solid-svg-icons": "^5.11.2", @@ -8833,9 +8833,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.640", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.640.tgz", - "integrity": "sha512-cU6wQdXYzuSPzLdszsa4whStYfmU7CVNnG6c5z6/z9YlCOQ2Xh/uKB1gTxlIRr0ubgSg1/dZuSbUAoeESeQ3sQ==" + "version": "1.3.642", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.642.tgz", + "integrity": "sha512-cev+jOrz/Zm1i+Yh334Hed6lQVOkkemk2wRozfMF4MtTR7pxf3r3L5Rbd7uX1zMcEqVJ7alJBnJL7+JffkC6FQ==" }, "elliptic": { "version": "6.5.3", @@ -8980,16 +8980,16 @@ } }, "enzyme-adapter-react-16": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.15.5.tgz", - "integrity": "sha512-33yUJGT1nHFQlbVI5qdo5Pfqvu/h4qPwi1o0a6ZZsjpiqq92a3HjynDhwd1IeED+Su60HDWV8mxJqkTnLYdGkw==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.15.6.tgz", + "integrity": "sha512-yFlVJCXh8T+mcQo8M6my9sPgeGzj85HSHi6Apgf1Cvq/7EL/J9+1JoJmJsRxZgyTvPMAqOEpRSu/Ii/ZpyOk0g==", "dev": true, "requires": { - "enzyme-adapter-utils": "^1.13.1", + "enzyme-adapter-utils": "^1.14.0", "enzyme-shallow-equal": "^1.0.4", "has": "^1.0.3", - "object.assign": "^4.1.0", - "object.values": "^1.1.1", + "object.assign": "^4.1.2", + "object.values": "^1.1.2", "prop-types": "^15.7.2", "react-is": "^16.13.1", "react-test-renderer": "^16.0.0-0", @@ -9058,22 +9058,24 @@ } }, "es-abstract": { - "version": "1.18.0-next.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", - "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "version": "1.18.0-next.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.2.tgz", + "integrity": "sha512-Ih4ZMFHEtZupnUh6497zEL4y2+w8+1ljnCyaTa+adcoafI1GOvMwFlDjBLfWR7y9VLfrjRJe9ocuHY1PSR9jjw==", "requires": { + "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2", "has": "^1.0.3", "has-symbols": "^1.0.1", "is-callable": "^1.2.2", - "is-negative-zero": "^2.0.0", + "is-negative-zero": "^2.0.1", "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", + "object-inspect": "^1.9.0", "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.3", + "string.prototype.trimstart": "^1.0.3" } }, "es-to-primitive": { @@ -10241,9 +10243,9 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-glob": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz", - "integrity": "sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", + "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", "requires": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -10550,9 +10552,9 @@ } }, "flow-parser": { - "version": "0.142.0", - "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.142.0.tgz", - "integrity": "sha512-gkdbagtuYQw7fo/D1AwCsDpMyxp/bdZkgfq95ev2MoETD1OW84PhU+vitupwte+6AJc2MRJyCdgfWhan8AGhzA==" + "version": "0.143.0", + "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.143.0.tgz", + "integrity": "sha512-PCKiRLALyu/L7b8vN63TCgvpV+vZJVAijPna2SAsSjzDDvQryZjiQMGUIEbbkpGpWVjEtQqyImBK7vSK49ufMg==" }, "flow-remove-types": { "version": "1.2.3", @@ -13533,9 +13535,9 @@ } }, "parse-json": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", - "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", @@ -14827,9 +14829,9 @@ } }, "parse-json": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", - "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", @@ -15591,9 +15593,9 @@ } }, "parse-json": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", - "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", @@ -16038,9 +16040,9 @@ } }, "parse-json": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", - "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", @@ -16683,9 +16685,9 @@ } }, "parse-json": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", - "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", @@ -19280,9 +19282,9 @@ } }, "node-releases": { - "version": "1.1.69", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.69.tgz", - "integrity": "sha512-DGIjo79VDEyAnRlfSqYTsy+yoHd2IOjJiKUozD2MV2D85Vso6Bug56mb9tT/fY5Urt0iqk01H7x+llAruDR2zA==" + "version": "1.1.70", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.70.tgz", + "integrity": "sha512-Slf2s69+2/uAD79pVVQo8uSiC34+g8GWY8UH2Qtqv34ZfhYrxpYpfzs9Js9d6O0mbDmALuxaTlplnBTnSELcrw==" }, "node-sass": { "version": "4.14.1", @@ -20313,9 +20315,9 @@ }, "dependencies": { "parse-json": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", - "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "requires": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -22936,9 +22938,9 @@ } }, "protocol-buffers-schema": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.4.0.tgz", - "integrity": "sha512-G/2kcamPF2S49W5yaMGdIpkG6+5wZF0fzBteLKgEHjbNzqjZQ85aAs1iJGto31EJaSTkNvHs5IXuHSaTLWBAiA==" + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.5.1.tgz", + "integrity": "sha512-YVCvdhxWNDP8/nJDyXLuM+UFsuPk4+1PB7WGPVDzm3HTHbzFLxQYeW2iZpS4mmnXrQJGBzt230t/BbEb7PrQaw==" }, "protoduck": { "version": "5.0.1", @@ -23750,32 +23752,12 @@ "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==" }, "regexp.prototype.flags": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", - "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", + "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", - "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" } }, "regexpp": { @@ -28202,9 +28184,9 @@ }, "dependencies": { "mime": { - "version": "2.4.7", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.7.tgz", - "integrity": "sha512-dhNd1uA2u397uQk3Nv5LM4lm93WYDUXFn3Fu291FJerns4jyTudqhIWe4W04YLy7Uk1tm1Ore04NpjRvQp/NPA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.0.tgz", + "integrity": "sha512-ft3WayFSFUVBuJj7BMLKAQcSlItKtfjsKDDsii3rqFDAZ7t11zRe8ASw/GlmivGwVUYtwkQrxiGGpL6gFvB0ag==", "dev": true } } diff --git a/package.json b/package.json index 67c97f53ba..a44947b025 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "d3-sankey0.12.3": "npm:d3-sankey@0.12.3", "d3-scale": "^1.0.4", "d3-time": "^1.0.11", - "data-transparency-ui": "github:fedspendingtransparency/data-transparency-ui#v1.3.6", + "data-transparency-ui": "github:fedspendingtransparency/data-transparency-ui#v1.3.7", "date-fns": "^2.16.1", "file-loader": "^3.0.1", "fixed-data-table": "0.6.3", diff --git a/src/_scss/components/_newBadge.scss b/src/_scss/components/_newBadge.scss index 0a09a0a8e4..775d662a7c 100644 --- a/src/_scss/components/_newBadge.scss +++ b/src/_scss/components/_newBadge.scss @@ -10,20 +10,16 @@ color: #FFF; font-weight: 600; margin-right: rem(35); -} - -.covid-newbadge { - height: rem(24); - width: rem(47); - border-radius: rem(4); - background-color: $color-secondary-dark; - @include display(inline-flex); - @include justify-content(center); - @include align-items(center); - font-size: rem(14); - color: #FFF; - font-weight: 600; - margin-left: rem(10); + @media(max-width: $medium-screen) { + &.dropdown-item { + margin: 0 0 0 rem(5); + } + } + @include media($medium-screen) { + &.dropdown-item { + margin: auto; + } + } } .new-badge-outer { diff --git a/src/_scss/components/_stickyTable.scss b/src/_scss/components/_stickyTable.scss new file mode 100644 index 0000000000..d3c9f76dd7 --- /dev/null +++ b/src/_scss/components/_stickyTable.scss @@ -0,0 +1,155 @@ +.table-container { + height: rem(500); + overflow: scroll; + margin: 0; + border: rem(1) solid $color-gray-border; + .usda-table.usda-table-w-grid { + border-collapse: separate; + margin-top: 0; + margin-bottom: 0; + &.table-loading { + height: 100%; + } + thead { + tr th { + min-width: rem(300); + vertical-align: top; + } + .table-header__content_right + .tooltip-wrapper + div:first-of-type + .tooltip-spacer + .tooltip + .tooltip__interior + .tooltip-pointer { + &.left { + left: rem(-24); + } + &.right { + right: rem(8); + } + } + .nested-header { + .table-header__content { + padding: 1rem 0; + } + } + } + &.table-loading + tbody + tr:first-of-type + td + div:not(.usda-loading-animation__container):not(.usda-loading-animation):not(.loading-message) { + width: 100vw; + } + tbody { + .usda-table__row { + .usda-table__cell { + font-size: 1.4rem; + padding: 1.2rem; + @import '../pages/aboutTheData/actionCell'; + .generic-cell-content { + padding-right: 2.4rem; + .not-certified { + margin-left: rem(5); + background: #dce4ee; + border-radius: rem(2); + color: $color-base; + padding: rem(5); + font-size: rem(10); + font-weight: $font-semibold; + line-height: rem(13); + } + } + .matched-str { + font-weight: 600; + text-decoration: underline; + } + &:hover { + background-color: $color-primary-alt-lightest; + } + } + } + } + &.sticky-y-table { + @media (min-width: $medium-screen) { + thead { + tr th { + position: sticky; + position: -webkit-sticky; + top: 0; + z-index: 9; + border: rem(1) solid $color-gray-lighter; + // Set a higher z-index on the the user is hovering over so + // tooltips display above the stacking context of the other headers + &:hover { + z-index: 10; + } + } + tr:last-of-type th { + top: rem(57); + border: rem(1) solid $color-gray-lighter; + } + } + } + } + &.sticky-x-table { + @media (min-width: $medium-screen) { + thead { + tr:first-of-type { + .table-header:first-of-type { + position: sticky; + position: -webkit-sticky; + left: 0px; + z-index: 11; + box-shadow: 2px 1px 4px rgba(0, 0, 0, 0.2); + border-right: 1px solid $color-gray-lightest; + } + } + } + tbody { + .usda-table__row .usda-table__cell { + &:first-of-type { + position: sticky; + position: -webkit-sticky; + z-index: 10; + left: 0px; + box-shadow: 2px 1px 4px rgba(0, 0, 0, 0.2); + border-right: rem(1) solid $color-gray-lightest; + } + } + } + } + } + &.table-loading .usda-table__body tr td { + border: none; + } + } +} +// Pagination +.usa-dt-pagination { + padding: rem(20); + .usa-dt-pagination__totals { + text-align: center; + padding: rem(20); // extra padding on mobile + @media (min-width: $tablet-screen) { + text-align: left; + padding: rem(0); + } + } + .usa-dt-pagination__wrapper { + @include justify-content(center); // center on mobile + @media (min-width: $tablet-screen) { + @include justify-content(flex-end); + } + .pager { + padding: rem(20); // extra padding on mobile + @media (min-width: $tablet-screen) { + padding: 0; + } + .pager__item { + margin: 0; + } + } + } +} diff --git a/src/_scss/layouts/default/header/nav/_dropdownMenu.scss b/src/_scss/layouts/default/header/nav/_dropdownMenu.scss index a5a25a2b62..b8a7cca131 100644 --- a/src/_scss/layouts/default/header/nav/_dropdownMenu.scss +++ b/src/_scss/layouts/default/header/nav/_dropdownMenu.scss @@ -48,12 +48,16 @@ ul.nav-children__list { @include unstyled-list; position: absolute; - left: 0; width: rem(230); background-color: $color-white; box-shadow: 0 4px 5px 0 rgba(0,0,0,0.5); - border-top: 1px solid $dropdown-border; + &:not(.resources) { + left: 0; + } + &.resources { + right: 0; + } } hr.nav-children__list-separator { @@ -67,15 +71,13 @@ } a.nav-children__link { - display: block; - + @include display(flex); + @include align-items(center); font-size: rem(15); line-height: rem(19); color: $nav-color; font-weight: $font-semibold; - padding: rem(15) rem(20); - &:hover, &:active { text-decoration: none; background-color: $dropdown-hover; @@ -99,7 +101,6 @@ color: $nav-color; font-weight: $font-semibold; padding: rem(15) rem(20); - &:hover, &:active { background-color: $dropdown-hover; } diff --git a/src/_scss/layouts/default/header/nav/mobile/_dropdown.scss b/src/_scss/layouts/default/header/nav/mobile/_dropdown.scss index e819acb7db..7271792eb8 100644 --- a/src/_scss/layouts/default/header/nav/mobile/_dropdown.scss +++ b/src/_scss/layouts/default/header/nav/mobile/_dropdown.scss @@ -68,6 +68,9 @@ line-height: rem(28); color: $color-base; text-decoration: none; + @include display(flex); + @include justify-content(center); + @include align-items(center); &:hover, &:active, &.mobile-dropdown__link_active { text-decoration: none; diff --git a/src/_scss/pages/aboutTheData/_actionCell.scss b/src/_scss/pages/aboutTheData/_actionCell.scss index 4cd069997a..009cdef901 100644 --- a/src/_scss/pages/aboutTheData/_actionCell.scss +++ b/src/_scss/pages/aboutTheData/_actionCell.scss @@ -9,9 +9,6 @@ @include justify-content(space-between); @include align-items(center); color: $color-base; - padding: 1.2rem; - height: 6.6rem; - border: solid 3px transparent; .action-cell__text { padding-right: .8rem; } @@ -32,11 +29,10 @@ } &:hover { text-decoration: none; - background-color: $color-primary-alt-lightest; + background-color: $color-primary-alt-light; } } &:hover { - border-color: $color-primary; .action-cell__button { svg { @include display(flex); diff --git a/src/_scss/pages/aboutTheData/_agenciesPage.scss b/src/_scss/pages/aboutTheData/_agenciesPage.scss new file mode 100644 index 0000000000..1d06aeb30e --- /dev/null +++ b/src/_scss/pages/aboutTheData/_agenciesPage.scss @@ -0,0 +1,30 @@ +.heading-container { + .header { + margin-top: rem(30); + font-size: rem(24); + } + .sub-header { + color: $color-base; + font-size: rem(16); + font-weight: 300; + letter-spacing: 0; + line-height: rem(24); + margin-top: 0; + margin-bottom: rem(30); + max-width: rem(769); + } +} +@import './tableControls'; +.table-container.table-container_submissions .usda-table.usda-table-w-grid { + // Styles specific to the "Statistics by Reporting Period" tab + thead { + th { + &:last-of-type { + // Hide sorting for the Agency Comments column + .table-header__sort { + display: none; + } + } + } + } +} diff --git a/src/_scss/pages/aboutTheData/_agencyDetailsPage.scss b/src/_scss/pages/aboutTheData/_agencyDetailsPage.scss new file mode 100644 index 0000000000..6b3c6bc39e --- /dev/null +++ b/src/_scss/pages/aboutTheData/_agencyDetailsPage.scss @@ -0,0 +1,62 @@ +.heading-container { + .header { + margin-top: rem(10); + font-size: rem(32); + } + .back-link { + margin-top: rem(20); + margin-bottom: rem(20); + font-size: rem(18); + font-weight: 400; + letter-spacing: 0; + line-height: rem(23); + a { + text-decoration: none; + } + } + .agency-info { + width: 100%; + @include display(inline-flex); + @include flex-wrap(wrap); + margin-bottom: rem(30); + @media (max-width: $tablet-screen) { + margin-top: rem(10); + } + .more-info-note { + margin: 0; + padding: 0; + letter-spacing: 0; + font-size: rem(14); + line-height: rem(18); + } + + .agency-info__group { + &:first-of-type { + padding-right: rem(60); + } + h5 { + @include flex-wrap(wrap); + font-size: rem(16); + font-weight: 600; + line-height: rem(20); + margin: rem(5) 0; + } + + .agency-info__website { + font-size: rem(14); + line-height: rem(20); + margin: rem(14) 0; + @import 'components/externalLink'; + } + } + } +} + +.table-container .usda-table.usda-table-w-grid { + thead th:last-of-type { + // Hide sorting for the Agency Comments column + .table-header__sort { + display: none; + } + } +} diff --git a/src/_scss/pages/aboutTheData/_tableControls.scss b/src/_scss/pages/aboutTheData/_tableControls.scss new file mode 100644 index 0000000000..79dd9f99b8 --- /dev/null +++ b/src/_scss/pages/aboutTheData/_tableControls.scss @@ -0,0 +1,210 @@ +.table-controls { + max-width: 100%; + .usa-dt-tabs-mobile .usa-dt-picker__list, + .tooltip-spacer { + z-index: 12; + } + .usa-dt-tab-toggle:focus { + outline: none; + } + .table-controls__time-and-search { + padding-left: 0; + @media (min-width: $medium-screen) { + padding-left: rem(25); + } + margin-top: rem(25); + @include display(flex); + @include justify-content(flex-start); + @include align-items(flex-start); + @include flex-wrap(wrap); + @include media($tablet-screen) { + @include flex-wrap(nowrap); + } + .filter-container { + @include display(flex); + @include flex-direction(column); + .filter__title { + font-size: rem(14); + margin-left: rem(15); + color: #8c8c8c; + } + .period-picker, + .fy-picker { + .usa-dt-picker__button-icon svg { + color: #0074be; + } + .fy-loading { + svg { + margin-left: rem(10); + } + } + .usa-dt-picker__list { + z-index: 12; + min-width: rem(110); + } + .usa-dt-picker__button { + border-bottom: rem(2) solid #0074be; + margin-bottom: rem(20); + padding-bottom: rem(2); + .usa-dt-picker__button-text { + font-size: rem(18); + font-weight: 500; + line-height: rem(23); + color: $color-base; + } + } + .usa-dt-picker__list li.usa-dt-picker__list-item { + border: none; + .usa-dt-picker__item { + font-size: rem(14); + } + } + } + .period-picker { + .order-1 { + order: 1; + } + .order-2 { + order: 2; + } + .order-3 { + order: 3; + } + .order-4 { + order: 4; + } + .order-5 { + order: 5; + } + .order-6 { + order: 6; + } + .order-7 { + order: 7; + } + .order-8 { + order: 8; + } + .order-9 { + order: 9; + } + .order-10 { + order: 10; + } + .order-11 { + order: 11; + } + .usa-dt-picker__list { + @include display(flex); + @include flex-direction(column); + } + .usa-dt-picker__list li.usa-dt-picker__list-item { + &.last { + border-bottom: 1px solid $color-gray-lighter; + } + + button.usa-dt-picker__item { + padding: 0; + .period { + padding: rem(10) rem(15); + @include display(flex); + @include justify-content(flex-end); + &:not(.last) span { + width: 75%; + } + &.last span { + &:first-of-type { + font-weight: $font-semibold; + margin-right: auto; + } + &:last-of-type { + width: 75%; + } + } + &.disabled { + cursor: not-allowed; + span { + opacity: 0.4; + } + &:hover { + background-color: $color-white; + } + span { + color: $color-base; + } + } + } + } + &.quarter-selected-3:hover ~ .not-individually-selectable-p2 { + .usa-dt-picker__item .period.disabled { + background-color: $color-gray-lightest; + } + } + &.quarter-selected-6:hover { + & ~ .not-individually-selectable-p4, + & ~ .not-individually-selectable-p5 { + .usa-dt-picker__item .period.disabled { + background-color: $color-gray-lightest; + } + } + } + &.quarter-selected-9:hover { + & ~ .not-individually-selectable-p7, + & ~ .not-individually-selectable-p8 { + .usa-dt-picker__item .period.disabled { + background-color: $color-gray-lightest; + } + } + } + &.quarter-selected-12:hover { + & ~ .not-individually-selectable-p10, + & ~ .not-individually-selectable-p11 { + .usa-dt-picker__item .period.disabled { + background-color: $color-gray-lightest; + } + } + } + } + } + .usa-dt-search-bar { + padding: 0; + margin: rem(10) 0 rem(10) rem(15); + width: 100%; + height: rem(30); + border-radius: rem(3); + @include media($tablet-screen) { + width: rem(273); + margin: rem(10) 0 0 rem(15); + } + .usa-dt-search-bar__input { + padding: 0; + text-indent: rem(3); + &:focus { + outline: none; + } + } + .usa-dt-search-bar__button { + @include display(flex); + svg { + font-size: rem(17); + } + } + } + } + } + .table-type-toggle:focus { + outline: none; + } + .table-tab-label { + @include display(flex); + } + .tooltip-wrapper { + display: none; + @media (min-width: $tablet-screen) { + @include display(flex); + } + } + .usa-dt-picker__list { + max-width: rem(500); + } +} diff --git a/src/_scss/pages/aboutTheData/aboutTheData.scss b/src/_scss/pages/aboutTheData/aboutTheData.scss new file mode 100644 index 0000000000..a7de7dc00b --- /dev/null +++ b/src/_scss/pages/aboutTheData/aboutTheData.scss @@ -0,0 +1,59 @@ +.about-the-data { + @import 'all'; + @import 'layouts/default/default'; + @import 'layouts/default/stickyHeader/header'; + .main-content { + @import '../../mixins/fullSectionWrap'; + @include display(flex); + @include flex-wrap(wrap); + @include flex-direction(column); + width: 100%; + max-width: 160rem; + border-radius: rem(5) rem(5) 0 0; + background: $color-white; + padding: 0 rem(20); + margin: rem(40) auto; + .heading-container { + width: 100%; + .header { + margin-bottom: 0; + font-weight: $font-semibold; + letter-spacing: 0; + line-height: rem(59); + } + } + } + &.about-the-data_agencies-page { + @import './_agenciesPage'; + } + &.about-the-data_agency-details-page { + @import './_agencyDetailsPage'; + } + @import 'components/Note'; + .default-note { + font-style: italic; + margin-top: 0; + padding: 0; + letter-spacing: 0; + max-width: rem(873); + line-height: rem(22); + font-size: rem(14); + margin-bottom: rem(10); + @media (min-width: $tablet-screen) { + margin-bottom: rem(30); + } + } + .table-container { + max-width: 100%; + } + .usda-message { + background-color: $color-white; + width: 100vw; + } + @import 'components/stickyTable'; + .agency-table-download { + svg { + margin-right: rem(5); + } + } +} diff --git a/src/_scss/pages/aboutTheData/agenciesPage.scss b/src/_scss/pages/aboutTheData/agenciesPage.scss deleted file mode 100644 index fbdd91779e..0000000000 --- a/src/_scss/pages/aboutTheData/agenciesPage.scss +++ /dev/null @@ -1,399 +0,0 @@ -.usa-da__about-the-data__agencies-page { - @import "all"; - @import "layouts/default/default"; - @import "layouts/default/stickyHeader/header"; - .main-content { - @import "../../mixins/fullSectionWrap"; - @include display(flex); - @include flex-wrap(wrap); - @include flex-direction(column); - width: 100%; - max-width: 160rem; - border-radius: rem(5) rem(5) 0 0; - background: $color-white; - padding: 0 rem(20); - margin: rem(40) auto; - .heading-container { - width: 100%; - .header { - margin-top: rem(30); - margin-bottom: 0; - font-weight: $font-semibold; - font-size: rem(24); - letter-spacing: 0; - line-height: rem(59); - } - .sub-header { - color: $color-base; - font-size: rem(16); - font-weight: 300; - letter-spacing: 0; - line-height: rem(24); - margin-top: 0; - margin-bottom: rem(30); - max-width: rem(769); - } - } - .table-container, - .table-controls { - max-width: 100%; - } - .table-controls { - .usa-dt-tabs-mobile .usa-dt-picker__list { - z-index: 12; - } - .usa-dt-tab-toggle:focus { - outline: none; - } - .table-controls__time-and-search { - padding-left: 0; - @media (min-width: $medium-screen) { - padding-left: rem(25); - } - margin-top: rem(25); - @include display(flex); - @include justify-content(flex-start); - @include align-items(flex-start); - @include flex-wrap(wrap); - @include media($tablet-screen) { - @include flex-wrap(nowrap); - } - .filter-container { - @include display(flex); - @include flex-direction(column); - .filter__title { - font-size: rem(14); - margin-left: rem(15); - color: #8C8C8C; - } - .period-picker, - .fy-picker { - .usa-dt-picker__button-icon svg { - color: #0074BE; - } - .fy-loading { - svg { - margin-left: rem(10); - } - } - .usa-dt-picker__list { - z-index: 12; - min-width: rem(110); - } - .usa-dt-picker__button { - border-bottom: rem(2) solid #0074BE; - margin-bottom: rem(20); - padding-bottom: rem(2); - .usa-dt-picker__button-text { - font-size: rem(18); - font-weight: 500; - line-height: rem(23); - color: $color-base; - } - } - .usa-dt-picker__list li.usa-dt-picker__list-item { - border: none; - .usa-dt-picker__item { - font-size: rem(14); - } - } - } - .period-picker { - .order-1 { - order: 1; - } - .order-2 { - order: 2; - } - .order-3 { - order: 3; - } - .order-4 { - order: 4; - } - .order-5 { - order: 5; - } - .order-6 { - order: 6; - } - .order-7 { - order: 7; - } - .order-8 { - order: 8; - } - .order-9 { - order: 9; - } - .order-10 { - order: 10; - } - .order-11 { - order: 11; - } - .usa-dt-picker__list { - @include display(flex); - @include flex-direction(column); - } - .usa-dt-picker__list li.usa-dt-picker__list-item { - &.last { - border-bottom: 1px solid $color-gray-lighter; - } - - button.usa-dt-picker__item { - padding: 0; - .period { - padding: rem(10) rem(15); - @include display(flex); - @include justify-content(flex-end); - &:not(.last) span { - width: 75%; - } - &.last span { - &:first-of-type { - font-weight: $font-semibold; - margin-right: auto; - } - &:last-of-type { - width: 75%; - } - } - &.disabled { - cursor: not-allowed; - span { - opacity: 0.4; - } - &:hover { - background-color: $color-white; - } - span { - color: $color-base; - } - } - } - } - &.quarter-selected-3:hover ~ .not-individually-selectable-p2 { - .usa-dt-picker__item .period.disabled { - background-color: $color-gray-lightest; - } - } - &.quarter-selected-6:hover { - & ~ .not-individually-selectable-p4, - & ~ .not-individually-selectable-p5 { - .usa-dt-picker__item .period.disabled { - background-color: $color-gray-lightest; - } - } - } - &.quarter-selected-9:hover { - & ~ .not-individually-selectable-p7, - & ~ .not-individually-selectable-p8 { - .usa-dt-picker__item .period.disabled { - background-color: $color-gray-lightest; - } - } - } - &.quarter-selected-12:hover { - & ~ .not-individually-selectable-p10, - & ~ .not-individually-selectable-p11 { - .usa-dt-picker__item .period.disabled { - background-color: $color-gray-lightest; - } - } - } - } - } - .usa-dt-search-bar { - padding: 0; - margin: rem(10) 0 rem(10) rem(15); - width: 100%; - height: rem(30); - border-radius: rem(3); - @include media($tablet-screen) { - width: rem(273); - margin: rem(10) 0 0 rem(15); - } - .usa-dt-search-bar__input { - padding: 0; - text-indent: rem(3); - &:focus { - outline: none; - } - } - .usa-dt-search-bar__button { - @include display(flex); - svg { - font-size: rem(17); - } - } - } - } - } - .table-type-toggle:focus { - outline: none; - } - .table-tab-label { - @include display(flex); - } - .tooltip-wrapper { - display: none; - @media (min-width: $tablet-screen) { - @include display(flex); - } - } - .usa-dt-picker__list { - max-width: rem(500); - } - } - .table-container { - height: rem(500); - overflow: scroll; - margin: 0; - border: rem(1) solid $color-gray-border; - .usda-table.usda-table-w-grid { - border-collapse: separate; - margin-top:0; - margin-bottom:0; - &.table-loading { - height: 100%; - } - thead { - tr th { - min-width: rem(300); - } - .nested-header { - .table-header__content { - padding: 1rem 0; - } - } - } - tbody { - .loading-message { - text-align: center; - } - .usda-table__row { - height: 4.8rem; - .usda-table__cell { - font-size: 1.4rem; - padding: 0; // to allow entire cell to be hoverable - @import './actionCell'; - @include flex-wrap(no-wrap); - .generic-cell-content { - padding: 1.2rem; - .not-certified { - margin-left: rem(5); - background: #DCE4EE; - border-radius: rem(2); - color: $color-base; - padding: rem(5); - font-size: rem(10); - font-weight: $font-semibold; - line-height: rem(13); - } - } - .matched-str { - font-weight: 600; - text-decoration: underline; - } - } - } - } - &.sticky-y-table { - @media(min-width: $medium-screen) { - thead { - tr th { - position: sticky; - position: -webkit-sticky; - top: 0; - z-index: 9; - border: rem(1) solid $color-gray-lighter; - // Set a higher z-index on the the user is hovering over so - // tooltips display above the stacking context of the other headers - &:hover { - z-index: 10; - } - } - tr:last-of-type th { - top: rem(57); - border: rem(1) solid $color-gray-lighter; - } - } - } - } - &.sticky-x-table { - @media(min-width: $medium-screen) { - thead { - tr:first-of-type { - .table-header:first-of-type { - position: sticky; - position: -webkit-sticky; - left: 0px; - z-index: 11; - box-shadow: 2px 1px 4px rgba(0,0,0,.20); - border-right: 1px solid $color-gray-lightest; - } - } - } - tbody { - .usda-table__row .usda-table__cell { - &:first-of-type { - position: sticky; - position: -webkit-sticky; - z-index: 10; - left: 0px; - box-shadow: 2px 1px 4px rgba(0,0,0,.20); - border-right: rem(1) solid $color-gray-lightest; - } - } - } - } - } - &.table-loading .usda-table__body tr td { - border: none; - } - } - } - // Pagination - .usa-dt-pagination { - padding: rem(20); - .usa-dt-pagination__totals { - text-align: center; - padding: rem(20); // extra padding on mobile - @media(min-width: $tablet-screen) { - text-align: left; - padding: rem(0); - } - } - .usa-dt-pagination__wrapper { - @include justify-content(center); // center on mobile - @media(min-width: $tablet-screen) { - @include justify-content(flex-end); - } - .pager { - padding: rem(20); // extra padding on mobile - @media(min-width: $tablet-screen) { - padding: 0; - } - .pager__item { - margin: 0; - } - } - } - } - @import 'components/Note'; - .default-note { - font-style: italic; - margin-top: 0; - padding: 0; - letter-spacing: 0; - max-width: rem(873); - line-height: rem(22); - font-size: rem(14); - margin-bottom: rem(10); - @media(min-width: $tablet-screen) { - margin-bottom: rem(30); - } - } - } -} diff --git a/src/_scss/pages/aboutTheData/agencyDetailsPage.scss b/src/_scss/pages/aboutTheData/agencyDetailsPage.scss deleted file mode 100644 index 7b27787b69..0000000000 --- a/src/_scss/pages/aboutTheData/agencyDetailsPage.scss +++ /dev/null @@ -1,202 +0,0 @@ -.usa-da__about-the-data__agencies-page { - @import "all"; - @import "layouts/default/default"; - @import "layouts/default/stickyHeader/header"; - .main-content { - @import "../../mixins/fullSectionWrap"; - @include display(flex); - @include flex-wrap(wrap); - @include flex-direction(column); - margin: rem(40) auto; - background: $color-white; - border-radius: 0.5rem 0.5rem 0 0; - padding: 0 rem(20); - width: 100%; - max-width: 160rem; - .usda-message { - background-color: $color-white; - width: 100vw; - } - .table-container, - .table-controls { - width: 100%; - } - .table-controls { - .table-type-toggle:focus { - outline: none; - } - .table-tab-label { - @include display(flex); - } - .tooltip-wrapper { - display: none; - @media (min-width: $tablet-screen) { - @include display(flex); - } - } - } - .table-container { - overflow-x: scroll; - overflow-y: default; - border: rem(1) solid $color-gray-border; - .usda-table.usda-table-w-grid { - border-collapse: separate; - margin: 0; - thead { - tr th { - min-width: rem(300); - } - .nested-header { - .table-header__content { - padding: 1rem 0; - } - } - } - tbody { - .usda-table__row { - height: 4.8rem; - .usda-table__cell { - &:nth-of-type(2) { - // center-align the percentage column - text-align: center; - } - font-size: 1.4rem; - padding: 0; // to allow entire cell to be hoverable - @import './actionCell'; - .generic-cell-content { - padding: 1.2rem; - } - } - } - } - &.sticky-y-table { - @media(min-width: $medium-screen) { - thead { - th { - position: sticky; - position: -webkit-sticky; - z-index: 9; - top: 0; - border: rem(1) solid $color-gray-lighter; - // Set a higher z-index on the the user is hovering over so - // tooltips display above the stacking context of the other headers - &:hover { - z-index: 10; - } - } - } - } - } - &.sticky-x-table { - @media(min-width: $medium-screen) { - thead { - tr:first-of-type { - .table-header:first-of-type { - position: sticky; - position: -webkit-sticky; - left: 0px; - z-index: 11; - box-shadow: 2px 1px 4px rgba(0,0,0,.20); - border-right: 1px solid $color-gray-lightest; - } - } - } - tbody { - .usda-table__row .usda-table__cell { - &:first-of-type { - position: sticky; - position: -webkit-sticky; - z-index: 10; - left: 0px; - box-shadow: 2px 1px 4px rgba(0,0,0,.20); - border-right: rem(1) solid $color-gray-lightest; - } - } - } - } - } - } - } - - .agency-table-download { - svg { - margin-right: 0.5rem; - } - } - - .heading-container { - .header { - margin-top: rem(10); - margin-bottom: 0; - font-weight: $font-semibold; - font-size: rem(32); - letter-spacing: 0; - line-height: rem(59); - } - .back-link { - margin-top: rem(20); - margin-bottom: rem(20); - font-size: rem(18); - font-weight: 400; - letter-spacing: 0; - line-height: rem(23); - a { - text-decoration: none; - } - } - .sub-header { - margin-top: 0; - margin-bottom: rem(30); - font-size: rem(47); - font-weight: 300; - letter-spacing: 0; - line-height: rem(59); - } - .agency-info { - width: 100%; - @include display(inline-flex); - @include flex-wrap(wrap); - margin-bottom: rem(30); - @media (max-width: $tablet-screen) { - margin-top: rem(10); - } - .more-info-note { - margin: 0; - padding: 0; - letter-spacing: 0; - font-size: rem(14); - line-height: rem(18); - } - - .agency-info__group { - &:first-of-type { - padding-right: rem(60); - } - h5 { - @include flex-wrap(wrap); - font-size: rem(16); - font-weight: 600; - line-height: rem(20); - margin: rem(5) 0; - } - - .agency-info__website { - font-size: rem(14); - line-height: rem(20); - margin: rem(14) 0; - @import 'components/externalLink'; - } - } - } - } - @import 'components/Note'; - .default-note { - font-style: italic; - margin: 0; - padding: rem(30) rem(15); - letter-spacing: 0; - line-height: rem(22); - max-width: rem(873); - } - } - } diff --git a/src/_scss/pages/modals/aboutTheData/_aboutTheData.scss b/src/_scss/pages/modals/aboutTheData/_aboutTheData.scss index edfdbaa6ce..e73c5c2645 100644 --- a/src/_scss/pages/modals/aboutTheData/_aboutTheData.scss +++ b/src/_scss/pages/modals/aboutTheData/_aboutTheData.scss @@ -20,7 +20,7 @@ .usa-dt-modal__title { font-size: rem(22); line-height: rem(28); - padding: rem(11) 0; + padding: rem(5) 0; } .about-the-data-modal__fiscal-year-quarter-period { font-size: rem(16); @@ -52,7 +52,7 @@ font-size: rem(14); line-height: rem(18); .publication-dates__column-header-sub-title { - font-size: rem(12); + font-size: rem(14); line-height: rem(15); font-weight: $font-normal; } @@ -61,12 +61,22 @@ } } .usda-table__body { // removes loading border + .usda-table__row { + .usda-table__cell { + font-size: rem(14); + } + } tr { td { border: none; + .usda-loading-animation__container { + .loading-message { + text-align: center; + } + } } } - font-size: rem(12); + font-size: rem(14); .usda-table__row { .usda-table__cell { padding: rem(10) rem(15); @@ -95,7 +105,6 @@ .usda-table__body { .usda-table__row { .usda-table__cell:not(:first-child) { - text-align: right; padding-right: rem(42); } } @@ -119,7 +128,7 @@ .table-header__content { .table-header__label { .publication-dates__column-header-sub-title { - font-size: rem(12); + font-size: rem(14); line-height: rem(15); font-weight: $font-normal; } @@ -153,4 +162,45 @@ } } } + // unlinked data modal + &.unlinked-data-modal { + @include media($tablet-screen) { + max-width: rem(730); + table.usda-table { + margin-bottom: 0; + } + } + @include media($large-screen) { + max-width: rem(730); + min-width: rem(730); + } + .usa-dt-modal__section { + table.usda-table { + thead.usda-table__head { + border-bottom: none; + .usda-table__row { + th.table-header { + padding-top: 0; + } + .table-header:last-child { + vertical-align: top; + } + .table-header:not(:first-child) { + border-bottom: solid rem(1) $color-gray-light; + } + } + } + .usda-table__body { + .usda-table__row { + .table-header { + font-size: rem(14); + text-align: right; + border: none; + padding-left: 0; + } + } + } + } + } + } } diff --git a/src/js/components/aboutTheData/AboutTheDataModal.jsx b/src/js/components/aboutTheData/AboutTheDataModal.jsx index 09fe9e3ef2..2a1966fa56 100644 --- a/src/js/components/aboutTheData/AboutTheDataModal.jsx +++ b/src/js/components/aboutTheData/AboutTheDataModal.jsx @@ -44,7 +44,7 @@ const AboutTheDataModal = ({
-
{agencyData.agencyName}
+
{agencyData.agencyName ? agencyData.agencyName.toUpperCase() : ''}

{title}

diff --git a/src/js/components/aboutTheData/AboutTheDataPage.jsx b/src/js/components/aboutTheData/AboutTheDataPage.jsx index 161d2b6957..aaba61cf4d 100644 --- a/src/js/components/aboutTheData/AboutTheDataPage.jsx +++ b/src/js/components/aboutTheData/AboutTheDataPage.jsx @@ -6,7 +6,7 @@ import React, { useEffect, useState } from "react"; import PropTypes from 'prop-types'; import { TooltipComponent, TooltipWrapper, Tabs } from "data-transparency-ui"; -import { useParams } from "react-router-dom"; +import { useLocation } from "react-router-dom"; import Header from "containers/shared/HeaderContainer"; import Footer from "containers/Footer"; @@ -20,7 +20,7 @@ import { modalTitles, modalClassNames } from 'dataMapping/aboutTheData/modals'; import { tabTooltips } from './dataMapping/tooltipContentMapping'; import TimeFilters from "./TimeFilters"; -require("pages/aboutTheData/agenciesPage.scss"); +require("pages/aboutTheData/aboutTheData.scss"); const TableTabLabel = ({ label }) => { const tooltipComponent = ( @@ -45,7 +45,9 @@ const message = "All numeric figures in this table are calculated based on the s const AboutTheDataPage = ({ history }) => { - const { fy: urlFy, period: urlPeriod } = useParams(); + const query = new URLSearchParams(useLocation().search); + const urlFy = query.get('fy'); + const urlPeriod = query.get('period'); const [, submissionPeriods, { year: latestFy, period: latestPeriod }] = useLatestAccountData(); const [selectedFy, setSelectedFy] = useState(null); const [selectedPeriod, setSelectedPeriod] = useState(null); @@ -66,12 +68,18 @@ const AboutTheDataPage = ({ useEffect(() => { if ((!urlFy || !urlPeriod) && submissionPeriods.size && latestFy && latestPeriod) { - history.replace(`about-the-data/agencies/${latestFy}/${latestPeriod}`); + history.replace({ + pathname: `/submission-statistics/`, + search: `?${new URLSearchParams({ fy: latestFy, period: latestPeriod }).toString()}` + }); } - }, []); + }, [history, latestFy, latestPeriod, submissionPeriods.size, urlFy, urlPeriod]); const updateUrl = (newFy, newPeriod) => { - history.push({ pathname: `/about-the-data/agencies/${newFy}/${newPeriod}` }); + history.push({ + pathname: `/submission-statistics/`, + search: `?${new URLSearchParams({ fy: newFy, period: newPeriod }).toString()}` + }); }; const handleSwitchTab = (tab) => { @@ -98,7 +106,7 @@ const AboutTheDataPage = ({ }; return ( -
+
@@ -117,8 +125,8 @@ const AboutTheDataPage = ({ types={[ { internal: 'submissions', - label: "Statistics by Reporting Period", - labelContent: + label: "Statistics by Submission Period", + labelContent: }, { internal: 'publications', @@ -150,14 +158,13 @@ const AboutTheDataPage = ({ mounted={!!showModal.length} type={showModal} className={modalClassNames[showModal]} - title={modalTitles[showModal]} + title={modalTitles(modalData?.type)[showModal]} agencyData={{ ...modalData, fiscalYear: parseInt(selectedFy, 10), fiscalPeriod: parseInt(selectedPeriod?.id, 10) || 0 }} - closeModal={closeModal} - totalObligationsNotInGTAS={45999} /> + closeModal={closeModal} />
diff --git a/src/js/components/aboutTheData/AgencyDetailsPage.jsx b/src/js/components/aboutTheData/AgencyDetailsPage.jsx index f5aebf55b3..51cf99d812 100644 --- a/src/js/components/aboutTheData/AgencyDetailsPage.jsx +++ b/src/js/components/aboutTheData/AgencyDetailsPage.jsx @@ -21,7 +21,7 @@ import BaseAgencyOverview from 'models/v2/agencyV2/BaseAgencyOverview'; import ExternalLink from 'components/sharedComponents/ExternalLink'; import AboutTheDataModal from './AboutTheDataModal'; -require('pages/aboutTheData/agencyDetailsPage.scss'); +require('pages/aboutTheData/aboutTheData.scss'); const message = 'All numeric figures in this table are calculated based on the set of TAS owned by each agency, as opposed to the set of TAS that the agency directly reported to USAspending.gov. In the vast majority of cases, these are exactly the same (upwards of 95% of TAS—with these TAS representing over 99% of spending—are submitted and owned by the same agency). This display decision is consistent with our practice throughout the website of grouping TAS by the owning agency rather than the reporting agency. While reporting agencies are not identified in this table, they are available in the Custom Account Download in the reporting_agency_name field.'; @@ -78,7 +78,7 @@ const AgencyDetailsPage = () => { }, [agencyCode]); return ( -
+
@@ -95,7 +95,9 @@ const AgencyDetailsPage = () => { <>
-  Back to All Agencies + +  Back to All Agencies +

{agencyOverview.name}

@@ -132,10 +134,9 @@ const AgencyDetailsPage = () => { mounted={!!showModal.length} type={showModal} className={modalClassNames[showModal]} - title={modalTitles[showModal]} + title={modalTitles(modalData?.type)[showModal]} agencyData={modalData} - closeModal={closeModal} - totalObligationsNotInGTAS={45999} /> + closeModal={closeModal} />
diff --git a/src/js/components/aboutTheData/AgencyDownloadLinkCell.jsx b/src/js/components/aboutTheData/AgencyDownloadLinkCell.jsx index b62334ed1a..0a72478ffe 100644 --- a/src/js/components/aboutTheData/AgencyDownloadLinkCell.jsx +++ b/src/js/components/aboutTheData/AgencyDownloadLinkCell.jsx @@ -1,22 +1,21 @@ import React from 'react'; import PropTypes from 'prop-types'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import kGlobalConstants from 'GlobalConstants'; const propTypes = { file: PropTypes.string }; -const AgencyDownloadLinkCell = ({ file }) => ( +const AgencyDownloadLinkCell = ({ file }) => (file ? ( + aria-label="Download agency comments"> Download -); +) : '--'); AgencyDownloadLinkCell.propTypes = propTypes; export default AgencyDownloadLinkCell; diff --git a/src/js/components/aboutTheData/CellWithModal.jsx b/src/js/components/aboutTheData/CellWithModal.jsx index 630301c3ef..ce9de5d080 100644 --- a/src/js/components/aboutTheData/CellWithModal.jsx +++ b/src/js/components/aboutTheData/CellWithModal.jsx @@ -8,9 +8,9 @@ import PropTypes, { oneOfType, oneOf } from 'prop-types'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; const propTypes = { - data: oneOfType([PropTypes.string, PropTypes.object]), + data: oneOfType([PropTypes.string, PropTypes.object, PropTypes.number]), openModal: PropTypes.func.isRequired, - modalType: oneOf(['publicationDates', 'missingAccountBalance', 'reportingDifferences']).isRequired, + modalType: oneOf(['publicationDates', 'missingAccountBalance', 'reportingDifferences', 'unlinkedData']).isRequired, agencyData: PropTypes.object }; @@ -23,7 +23,7 @@ const CellWithModal = ({ {data} -
diff --git a/src/js/components/aboutTheData/DrilldownCell.jsx b/src/js/components/aboutTheData/DrilldownCell.jsx index 4c29329ace..00c803c1d3 100644 --- a/src/js/components/aboutTheData/DrilldownCell.jsx +++ b/src/js/components/aboutTheData/DrilldownCell.jsx @@ -25,7 +25,10 @@ const DrilldownCell = ({ {searchTerm ? replaceString(data, searchTerm, 'matched-str') : data} - +
diff --git a/src/js/components/aboutTheData/TimeFilters.jsx b/src/js/components/aboutTheData/TimeFilters.jsx index 63af93ffba..5bfd09052a 100644 --- a/src/js/components/aboutTheData/TimeFilters.jsx +++ b/src/js/components/aboutTheData/TimeFilters.jsx @@ -118,7 +118,7 @@ const TimePeriodFilters = ({ }, [submissionPeriods, urlFy, urlPeriod, latestPeriod, latestFy]); const generatePeriodDropdown = (fy, periods) => ( - parsePeriods(fy, periods) + parsePeriods(fy, periods.toJS()) .map((p) => ({ ...p, component: p.component, diff --git a/src/js/components/aboutTheData/dataMapping/modals/modalContentMapping.jsx b/src/js/components/aboutTheData/dataMapping/modals/modalContentMapping.jsx index 4cd777f08d..013f9bd0d1 100644 --- a/src/js/components/aboutTheData/dataMapping/modals/modalContentMapping.jsx +++ b/src/js/components/aboutTheData/dataMapping/modals/modalContentMapping.jsx @@ -2,9 +2,11 @@ import React from 'react'; import PublicationDatesContainer from 'containers/aboutTheData/modals/PublicationDatesContainer'; import MissingAccountBalanceContainer from 'containers/aboutTheData/modals/MissingAccountBalanceContainer'; import ReportingDifferencesContainer from 'containers/aboutTheData/modals/ReportingDifferencesContainer'; +import UnlinkedDataContainer from 'containers/aboutTheData/modals/UnlinkedDataContainer'; export const modalContentMapping = (data) => ({ publicationDates: (), missingAccountBalance: (), - reportingDifferences: () + reportingDifferences: (), + unlinkedData: () }); diff --git a/src/js/components/aboutTheData/dataMapping/tooltipContentMapping.jsx b/src/js/components/aboutTheData/dataMapping/tooltipContentMapping.jsx index 3617f0a039..fc1756d5e3 100644 --- a/src/js/components/aboutTheData/dataMapping/tooltipContentMapping.jsx +++ b/src/js/components/aboutTheData/dataMapping/tooltipContentMapping.jsx @@ -45,7 +45,7 @@ export const columnTooltips = {

), - 'Number of TASs Missing in Account Balance Data': ( + 'Number of TASs Missing from Account Balance Data': ( <>

Agencies submit account balance data grouped by Treasury Account Symbols (TAS) in two ways: 1) to USAspending.gov in File A and 2) to GTAS, a separate system that is the authoritative source of governmentwide TAS account balances. This column shows the number of TAS that are in GTAS but missing in USAspending.gov data. @@ -97,7 +97,7 @@ export const columnTooltips = {

), - 'Agency Disclosures': ( + 'Agency Comments': (

Agency Assurance Statements are optional and provided by agencies at the time they submit their data to USAspending.gov in the required dataset formats (File A, B, C, D1, and D2). For more information about the DATA Act reporting flow, visit https://fiscal.treasury.gov/files/data-transparency/daims-information-flow-diagram.pdf

diff --git a/src/js/components/award/Award.jsx b/src/js/components/award/Award.jsx index e2231349ec..3c193947eb 100644 --- a/src/js/components/award/Award.jsx +++ b/src/js/components/award/Award.jsx @@ -34,7 +34,8 @@ const propTypes = { isDownloadPending: PropTypes.bool, isSubAwardIdClicked: PropTypes.bool, subAwardIdClicked: PropTypes.func, - isLoading: PropTypes.bool + isLoading: PropTypes.bool, + defCodes: PropTypes.array }; const awardSections = [ @@ -108,7 +109,8 @@ export default class Award extends React.Component { counts={{ subawardCount: overview.subawardCount }} jumpToSection={this.jumpToSection} isSubAwardIdClicked={this.props.isSubAwardIdClicked} - subAwardIdClicked={this.props.subAwardIdClicked} /> + subAwardIdClicked={this.props.subAwardIdClicked} + defCodes={this.props.defCodes} /> ); } else if (overview.category === 'idv') { @@ -117,7 +119,8 @@ export default class Award extends React.Component { awardId={awardId} overview={overview} details={this.props.award.idvDetails} - jumpToSection={this.jumpToSection} /> + jumpToSection={this.jumpToSection} + defCodes={this.props.defCodes} /> ); } else if (this.props.noAward) { @@ -136,7 +139,8 @@ export default class Award extends React.Component { overview={overview} jumpToSection={this.jumpToSection} isSubAwardIdClicked={this.props.isSubAwardIdClicked} - subAwardIdClicked={this.props.subAwardIdClicked} /> + subAwardIdClicked={this.props.subAwardIdClicked} + defCodes={this.props.defCodes} /> ); } diff --git a/src/js/components/award/contract/ContractContent.jsx b/src/js/components/award/contract/ContractContent.jsx index db70cffb4d..387e7dced8 100644 --- a/src/js/components/award/contract/ContractContent.jsx +++ b/src/js/components/award/contract/ContractContent.jsx @@ -26,7 +26,8 @@ const propTypes = { jumpToSection: PropTypes.func, counts: PropTypes.object, isSubAwardIdClicked: PropTypes.bool, - subAwardIdClicked: PropTypes.func + subAwardIdClicked: PropTypes.func, + defCodes: PropTypes.array }; const ContractContent = ({ @@ -35,7 +36,8 @@ const ContractContent = ({ jumpToSection, counts, isSubAwardIdClicked, - subAwardIdClicked + subAwardIdClicked, + defCodes }) => { const [activeTab, setActiveTab] = useState('transaction'); @@ -49,7 +51,7 @@ const ContractContent = ({ }; const awardAmountData = Object.create(BaseAwardAmounts); - awardAmountData.populate(overview, overview.category); + awardAmountData.populate(overview, overview.category, defCodes); const jumpToTransactionHistoryTable = () => { setActiveTab('transaction'); diff --git a/src/js/components/award/financialAssistance/FinancialAssistanceContent.jsx b/src/js/components/award/financialAssistance/FinancialAssistanceContent.jsx index bb739f6fb7..f59cf0a4a8 100644 --- a/src/js/components/award/financialAssistance/FinancialAssistanceContent.jsx +++ b/src/js/components/award/financialAssistance/FinancialAssistanceContent.jsx @@ -26,7 +26,8 @@ const propTypes = { overview: PropTypes.object, jumpToSection: PropTypes.func, isSubAwardIdClicked: PropTypes.bool, - subAwardIdClicked: PropTypes.func + subAwardIdClicked: PropTypes.func, + defCodes: PropTypes.array }; const FinancialAssistanceContent = ({ @@ -34,7 +35,8 @@ const FinancialAssistanceContent = ({ overview = { generatedId: '', fileC: { obligations: [] } }, jumpToSection, isSubAwardIdClicked, - subAwardIdClicked + subAwardIdClicked, + defCodes }) => { const [activeTab, setActiveTab] = useState("transaction"); const [CFDAOverviewLinkClicked, setCFDAOverviewLinkClicked] = useState(false); @@ -70,7 +72,7 @@ const FinancialAssistanceContent = ({ }); const awardAmountData = Object.create(BaseAwardAmounts); - awardAmountData.populate(overview, overview.category); + awardAmountData.populate(overview, overview.category, defCodes); const [idLabel, identifier] = isAwardAggregate(overview.generatedId) ? ['URI', overview.uri] : ['FAIN', overview.fain]; const isGrant = overview.category === 'grant'; diff --git a/src/js/components/award/shared/awardAmountsSection/charts/SharedBarComponents.jsx b/src/js/components/award/shared/awardAmountsSection/charts/SharedBarComponents.jsx index 6059e8a9a8..d4bda42263 100644 --- a/src/js/components/award/shared/awardAmountsSection/charts/SharedBarComponents.jsx +++ b/src/js/components/award/shared/awardAmountsSection/charts/SharedBarComponents.jsx @@ -21,7 +21,7 @@ const BarValue = ({ onKeyPress={onEnter} onMouseMove={onEnter} onMouseEnter={onEnter} - onMousedown={onEnter} + onMouseDown={onEnter} onMouseLeave={onLeave} onClick={onEnter}>
diff --git a/src/js/components/homepage/Homepage.jsx b/src/js/components/homepage/Homepage.jsx index c33d2512ab..dc950559a4 100644 --- a/src/js/components/homepage/Homepage.jsx +++ b/src/js/components/homepage/Homepage.jsx @@ -7,15 +7,12 @@ import React from 'react'; import * as MetaTagHelper from 'helpers/metaTagHelper'; import Footer from 'containers/Footer'; -import GlobalConstants from 'GlobalConstants'; -import CovidHighlights from 'containers/covid19/homepage/CovidHighlights'; +import CovidHighlights from 'containers/homepage/CovidHighlights'; import Header from 'containers/shared/HeaderContainer'; import GlobalModalContainer from 'containers/globalModal/GlobalModalContainer'; import MetaTags from '../sharedComponents/metaTags/MetaTags'; - -import Hero from './hero/Hero'; import Features from './features/Features'; import Download from './download/Download'; import Community from './community/Community'; @@ -29,12 +26,7 @@ export default class Homepage extends React.Component {
- {GlobalConstants.CARES_ACT_RELEASED_2 && ( - - )} - {!GlobalConstants.CARES_ACT_RELEASED_2 && ( - - )} + diff --git a/src/js/components/homepage/features/Features.jsx b/src/js/components/homepage/features/Features.jsx index b04c58864c..cebeb26f8d 100644 --- a/src/js/components/homepage/features/Features.jsx +++ b/src/js/components/homepage/features/Features.jsx @@ -5,7 +5,7 @@ import React from 'react'; -import CovidFeatureContainer from 'containers/covid19/homepage/CovidFeatureContainer'; +import CovidFeatureContainer from 'containers/homepage/CovidFeatureContainer'; import kGlobalConstants from 'GlobalConstants'; import SpendingExplorerFeature from './SpendingExplorerFeature'; import SearchFeature from './SearchFeature'; diff --git a/src/js/components/sharedComponents/FloatingGlossaryButton.jsx b/src/js/components/sharedComponents/FloatingGlossaryButton.jsx index 75f594fd19..9eda11f533 100644 --- a/src/js/components/sharedComponents/FloatingGlossaryButton.jsx +++ b/src/js/components/sharedComponents/FloatingGlossaryButton.jsx @@ -47,7 +47,7 @@ export default class FloatingGlossaryButton extends React.Component { pageScrolled() { // find the header glossary button - const header = document.getElementById('header-glossary-button'); + const header = document.querySelector('.site-logo'); const headerBottom = header.getBoundingClientRect().top + (header.offsetHeight * 0.5); if (headerBottom <= 0 && this.state.hide) { diff --git a/src/js/components/sharedComponents/header/Dropdown.jsx b/src/js/components/sharedComponents/header/Dropdown.jsx index e46977f477..6999206c55 100644 --- a/src/js/components/sharedComponents/header/Dropdown.jsx +++ b/src/js/components/sharedComponents/header/Dropdown.jsx @@ -3,9 +3,8 @@ * Created by Kevin Li 1/18/18 */ -import React from 'react'; +import React, { useState } from 'react'; import PropTypes from 'prop-types'; -import kGlobalConstants from 'GlobalConstants'; import { AngleDown } from 'components/sharedComponents/icons/Icons'; import DropdownItem from './DropdownItem'; @@ -16,88 +15,74 @@ const propTypes = { items: PropTypes.array.isRequired }; -export default class Dropdown extends React.Component { - constructor(props) { - super(props); +const Dropdown = ({ + label, + title, + items +}) => { + const [expanded, setExpanded] = useState(false); - this.state = { - expanded: false - }; + const clickedButton = () => { + setExpanded(!expanded); + }; - this.clickedButton = this.clickedButton.bind(this); - this.expandMenu = this.expandMenu.bind(this); - this.collapseMenu = this.collapseMenu.bind(this); - } + const expandMenu = () => { + setExpanded(true); + }; - clickedButton() { - this.setState({ - expanded: !this.state.expanded - }); - } + const collapseMenu = () => { + setExpanded(false); + }; - expandMenu() { - this.setState({ - expanded: true - }); + let activeChildren = ''; + let activeParent = ''; + let iconAlt = 'Collapsed menu'; + if (expanded) { + activeChildren = 'nav-children_active'; + activeParent = 'nav-dropdown__parent_active'; + iconAlt = 'Expanded menu'; } - collapseMenu() { - this.setState({ - expanded: false - }); - } - - render() { - let activeChildren = ''; - let activeParent = ''; - let iconAlt = 'Collapsed menu'; - if (this.state.expanded) { - activeChildren = 'nav-children_active'; - activeParent = 'nav-dropdown__parent_active'; - iconAlt = 'Expanded menu'; - } - - const items = this.props.items.map((item, index) => ( - - )); + const containsNewNavItem = items.some(({ isNewTab }) => isNewTab); - return ( -
- -
-
    - {items} -
+
} + {label}
+
+ +
+ +
+
    + {items.map((item, index) => ( + + ))} +
- ); - } -} +
+ ); +}; Dropdown.propTypes = propTypes; + +export default Dropdown; + diff --git a/src/js/components/sharedComponents/header/DropdownItem.jsx b/src/js/components/sharedComponents/header/DropdownItem.jsx index ed75cac05a..7ad97a1716 100644 --- a/src/js/components/sharedComponents/header/DropdownItem.jsx +++ b/src/js/components/sharedComponents/header/DropdownItem.jsx @@ -5,10 +5,11 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Link } from 'react-router-dom'; +import { Link, useLocation } from 'react-router-dom'; import Analytics from 'helpers/analytics/Analytics'; import * as redirectHelper from 'helpers/redirectHelper'; +import { getNewUrlForGlossary } from 'helpers/glossaryHelper'; import DropdownComingSoon from './DropdownComingSoon'; @@ -16,9 +17,11 @@ const propTypes = { url: PropTypes.string, label: PropTypes.node, enabled: PropTypes.bool, - newTab: PropTypes.bool, + shouldOpenNewTab: PropTypes.bool, + isNewTab: PropTypes.bool, isFirst: PropTypes.bool, - externalLink: PropTypes.bool + externalLink: PropTypes.bool, + appendToExistingUrl: PropTypes.bool }; const clickedHeaderLink = (route) => { @@ -28,84 +31,104 @@ const clickedHeaderLink = (route) => { }); }; -export default class DropdownItem extends React.Component { - constructor(props) { - super(props); - - this.redirect = this.redirect.bind(this); +const DropdownItem = ({ + url = '', + label, + enabled = true, + shouldOpenNewTab = false, + externalLink = false, + isFirst = false, + isNewTab = false, + appendToExistingUrl = false +}) => { + const { pathname, search } = useLocation(); + const newUrl = appendToExistingUrl + ? getNewUrlForGlossary(pathname, url, search) + : url; + + const handleClick = () => { + redirectHelper.showRedirectModal(newUrl); + clickedHeaderLink(newUrl); + }; + + let className = 'nav-children__link_disabled'; + let comingSoon = ( +
+ +
+ ); + + const newLabel = isNewTab && enabled + ? ( + <> + {label} + NEW + + ) + : null; + + if (enabled) { + className = ''; + comingSoon = null; } - redirect() { - redirectHelper.showRedirectModal(this.props.url); - clickedHeaderLink(`${this.props.url}`); + const newTabProps = {}; + if (shouldOpenNewTab) { + newTabProps.target = '_blank'; + newTabProps.rel = 'noopener noreferrer'; } - render() { - let className = 'nav-children__link_disabled'; - let comingSoon = ( -
- -
+ let link = ( + + {!newLabel && label} + {newLabel} + {comingSoon} + + ); + + if (enabled && externalLink) { + // Trigger the redirect modal + link = ( + ); + } - if (this.props.enabled) { - className = ''; - comingSoon = null; - } - - const newTabProps = {}; - if (this.props.newTab) { - newTabProps.target = '_blank'; - newTabProps.rel = 'noopener noreferrer'; - } - - let link = ( - - {this.props.label} + {!newLabel && label} + {newLabel} {comingSoon} - + ); + } - if (this.props.enabled && this.props.externalLink) { - // Trigger the redirect modal - link = ( - - ); - } - - if (this.props.url.includes('http')) { - link = ( - - {this.props.label} - {comingSoon} - - ); - } - - let firstClass = ''; - if (this.props.isFirst) { - firstClass = 'nav-children__list-separator_hidden'; - } - - return ( -
  • -
    - {link} -
  • - ); + let firstClass = ''; + if (isFirst) { + firstClass = 'nav-children__list-separator_hidden'; } -} + + return ( +
  • +
    + {link} +
  • + ); +}; DropdownItem.propTypes = propTypes; + +export default DropdownItem; diff --git a/src/js/components/sharedComponents/header/NavBar.jsx b/src/js/components/sharedComponents/header/NavBar.jsx index 9055d5442b..27ea1339d4 100644 --- a/src/js/components/sharedComponents/header/NavBar.jsx +++ b/src/js/components/sharedComponents/header/NavBar.jsx @@ -7,13 +7,13 @@ import { faEnvelope } from "@fortawesome/free-regular-svg-icons"; import Analytics from 'helpers/analytics/Analytics'; import GlossaryButtonWrapperContainer from 'containers/glossary/GlossaryButtonWrapperContainer'; -import { searchOptions, profileOptions, downloadOptions } from 'dataMapping/navigation/menuOptions'; +import { searchOptions, profileOptions, downloadGlobalNavigationOptions, resourceOptions } from 'dataMapping/navigation/menuOptions'; import EmailSignUp from 'components/homepage/EmailSignUp'; -import { DEV } from '../../../GlobalConstants'; -import NavBarGlossaryLink from './NavBarGlossaryLink'; +import { DEV, QAT } from '../../../GlobalConstants'; import Dropdown from './Dropdown'; import MobileNav from './mobile/MobileNav'; +import NavBarGlossaryLink from './NavBarGlossaryLink'; const clickedHeaderLink = (route) => { Analytics.event({ @@ -22,6 +22,8 @@ const clickedHeaderLink = (route) => { }); }; +const isDevOrQat = (DEV || QAT); + export default class NavBar extends React.Component { constructor(props) { super(props); @@ -184,15 +186,23 @@ export default class NavBar extends React.Component { className="full-menu__item" role="menuitem"> + title={isDevOrQat ? "Download" : "Download Center"} + label={isDevOrQat ? "Download" : "Download Center"} + items={downloadGlobalNavigationOptions} /> -
  • + {isDevOrQat && ( +
  • + +
  • + )} + {!isDevOrQat && ( - + )}
    diff --git a/src/js/components/sharedComponents/header/mobile/MobileDropdown.jsx b/src/js/components/sharedComponents/header/mobile/MobileDropdown.jsx index f7b7307df4..81199ede26 100644 --- a/src/js/components/sharedComponents/header/mobile/MobileDropdown.jsx +++ b/src/js/components/sharedComponents/header/mobile/MobileDropdown.jsx @@ -6,7 +6,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import kGlobalConstants from 'GlobalConstants'; import { AngleUp, AngleDown } from 'components/sharedComponents/icons/Icons'; @@ -58,15 +57,19 @@ export default class MobileDropdown extends React.Component { const items = this.props.items.map((item) => ( )); + const containsNewNavItem = this.props.items.some(({ isNewTab }) => isNewTab); + return (
    {comingSoonDecorator} ); - - if (this.props.externalLink) { - // Trigger the redirect modal - link = ( -
  • - - {comingSoonDecorator} -
  • - ); - } - - return link; } -} + + return link; +}; MobileDropdownItem.propTypes = propTypes; + +export default MobileDropdownItem; diff --git a/src/js/components/sharedComponents/header/mobile/MobileNav.jsx b/src/js/components/sharedComponents/header/mobile/MobileNav.jsx index 419717b1de..bcb91aba30 100644 --- a/src/js/components/sharedComponents/header/mobile/MobileNav.jsx +++ b/src/js/components/sharedComponents/header/mobile/MobileNav.jsx @@ -8,10 +8,11 @@ import PropTypes from 'prop-types'; import { withRouter, Link } from 'react-router-dom'; import Analytics from 'helpers/analytics/Analytics'; +import { DEV, QAT } from 'GlobalConstants'; import GlossaryButtonWrapperContainer from 'containers/glossary/GlossaryButtonWrapperContainer'; -import { searchOptions, profileOptions, downloadOptions } from 'dataMapping/navigation/menuOptions'; +import { searchOptions, profileOptions, downloadGlobalNavigationOptions, resourceOptions } from 'dataMapping/navigation/menuOptions'; import MobileTop from './MobileTop'; import MobileGlossaryButton from './MobileGlossaryButton'; @@ -29,6 +30,8 @@ const propTypes = { location: PropTypes.object }; +const isDevOrQat = (DEV || QAT); + export class MobileNav extends React.Component { constructor(props) { super(props); @@ -99,17 +102,29 @@ export class MobileNav extends React.Component {

  • -
  • - -
    -
  • + {isDevOrQat && ( +
  • + +
    +
  • + )} + {!isDevOrQat && ( +
  • + +
    +
  • + )}
    diff --git a/src/js/containers/Footer.jsx b/src/js/containers/Footer.jsx index 8c24868972..670f7c6153 100644 --- a/src/js/containers/Footer.jsx +++ b/src/js/containers/Footer.jsx @@ -9,6 +9,7 @@ import React from 'react'; import { Link } from 'react-router-dom'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faFacebookSquare, faLinkedin, faGithub, faTwitter } from "@fortawesome/free-brands-svg-icons"; +import { QAT, DEV } from 'GlobalConstants'; import { showModal } from 'redux/actions/modal/modalActions'; @@ -34,6 +35,8 @@ const clickedFooterLink = (route) => { }); }; +const isDevOrQat = (DEV || QAT); + const Footer = ({ filters, redirectUser @@ -113,33 +116,35 @@ const Footer = ({
    -
    -
    + {!isDevOrQat && ( +
    +
    Resources -
    -
      -
    • - +
    +
      +
    • + Data Dictionary - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    -
    + + +
  • + +
  • +
  • + +
  • +
  • + +
  • + +
    + )}
    Developers @@ -155,8 +160,30 @@ const Footer = ({ link="https://github.com/fedspendingtransparency/usaspending-website/tree/master" title="Explore the Code" /> + {isDevOrQat && ( +
  • + +
  • + )}
    + {isDevOrQat && ( +
    +
    + Our Sites +
    + +
    + )}