diff --git a/.eslintrc.js b/.eslintrc.js index f760b896..a6a6b90d 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,41 +1,16 @@ module.exports = { - "extends": [ - "airbnb", - "plugin:prettier/recommended" - ], - "plugins": [ - "react-hooks", - "prettier" - ], - "rules": { - "jsx-a11y/label-has-for": 0, - "jsx-a11y/label-has-associated-control": 0, - "jsx-a11y/no-noninteractive-tabindex": 0, - "jsx-a11y/control-has-associated-label": 0, - "react-hooks/rules-of-hooks": "error", - "react-hooks/exhaustive-deps": "warn", - "import/no-extraneous-dependencies": [ - "error", - {"devDependencies": true} - ], - "no-use-before-define": 0, - "no-param-reassign": 0, - "no-console": 0, - "func-names": 0, - "prefer-template": 0, - "consistent-return": 0, - "react/jsx-filename-extension": 0, - "react/jsx-props-no-spreading": 0, - "react/jsx-one-expression-per-line": 0, - "react/prop-types": 0, - "react/forbid-prop-types": 0, - "react/no-unescaped-entities": 0, - "react/destructuring-assignment": 0, - "react/no-this-in-sfc": 0, - "prettier/prettier": 0 - }, - "env": { - "browser": true, - "jest": true - } + extends: ['airbnb', 'plugin:prettier/recommended'], + plugins: ['react-hooks', 'prettier'], + rules: { + 'jsx-a11y/label-has-for': 0, + 'jsx-a11y/label-has-associated-control': 0, + 'react-hooks/rules-of-hooks': 'error', + 'react-hooks/exhaustive-deps': 'warn', + 'import/no-extraneous-dependencies': ['error', { devDependencies: true }], + 'react/jsx-props-no-spreading': 0, + }, + env: { + browser: true, + jest: true, + }, }; diff --git a/package.json b/package.json index 5f389263..77a36f56 100644 --- a/package.json +++ b/package.json @@ -3,12 +3,13 @@ "version": "0.1.0", "private": true, "dependencies": { - "auth0-js": "9.14.3", + "auth0-js": "9.15.0", "axios": "^0.21.1", "bootstrap": "4.6.0", "chart.js": "^2.9.3", "d3": "^5.14.2", "dayjs": "1.8.25", + "ga-gtag": "^1.0.1", "history": "4.10.1", "jquery": "^3.5.1", "lodash": "^4.17.11", @@ -18,12 +19,11 @@ "react": "17.0.1", "react-chartjs-2": "^2.8.0", "react-dom": "17.0.1", - "react-ga": "^3.1.2", "react-google-recaptcha": "^2.1.0", "react-particles-js": "3.4.1", "react-redux": "^7.2.1", "react-router-dom": "^5.1.2", - "react-scripts": "4.0.2", + "react-scripts": "4.0.3", "react-spring": "^8.0.20", "redux": "^4.0.1", "redux-thunk": "^2.3.0", @@ -49,17 +49,17 @@ "not op_mini all" ], "devDependencies": { - "@storybook/addon-actions": "6.1.18", - "@storybook/addon-links": "6.1.18", - "@storybook/addon-storyshots": "6.1.18", - "@storybook/addons": "6.1.18", + "@storybook/addon-actions": "6.1.21", + "@storybook/addon-links": "6.1.21", + "@storybook/addon-storyshots": "6.1.21", + "@storybook/addons": "6.1.21", "@storybook/preset-create-react-app": "^3.1.4", - "@storybook/react": "6.1.18", - "@wojtekmaj/enzyme-adapter-react-17": "^0.4.1", + "@storybook/react": "6.1.21", + "@wojtekmaj/enzyme-adapter-react-17": "0.6.0", "enzyme": "3.11.0", "enzyme-adapter-react-16": "1.15.6", "eslint-config-airbnb": "^18.2.0", - "eslint-config-prettier": "7.2.0", + "eslint-config-prettier": "8.1.0", "eslint-plugin-prettier": "^3.1.4", "eslint-plugin-react-hooks": "^4.0.8", "jest-canvas-mock": "^2.2.0", @@ -67,6 +67,6 @@ "sass": "1.32.8", "storybook-react-router": "1.0.8", "stylelint": "^13.2.0", - "stylelint-config-recommended": "^3.0.0" + "stylelint-config-recommended": "4.0.0" } } diff --git a/src/AnalysisPage/humanDataAnalysis.jsx b/src/AnalysisPage/humanDataAnalysis.jsx deleted file mode 100644 index ad958723..00000000 --- a/src/AnalysisPage/humanDataAnalysis.jsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import HumanGeneMetaAnalysis from './humanGeneMetaAnalysis'; - -export default function HumanDataAnalysis({ analysis, subAnalysis }) { - if (analysis === 'META_ANALYSIS') { - switch (subAnalysis) { - case 'META_ANALYSIS_PUBLIC_DATA': - return ; - default: - return null; - } - } -} - -HumanDataAnalysis.propTypes = { - analysis: PropTypes.string.isRequired, - subAnalysis: PropTypes.string.isRequired, -}; diff --git a/src/AnalysisPage/humanGeneMetaAnalysis.jsx b/src/AnalysisPage/humanGeneMetaAnalysis.jsx deleted file mode 100644 index dd54de73..00000000 --- a/src/AnalysisPage/humanGeneMetaAnalysis.jsx +++ /dev/null @@ -1,197 +0,0 @@ -import React, { useState } from 'react'; -import HumanMetaAnalysisAcuteMuscleGenes from '../data/humanMetaAnalysisAcuteMuscleGenes'; - -import PlotFOXO1 from '../assets/plots/analysis/meta-analysis/human/acute-muscle/FOXO1.png'; -import PlotID1 from '../assets/plots/analysis/meta-analysis/human/acute-muscle/ID1.png'; -import PlotPPARGC1A from '../assets/plots/analysis/meta-analysis/human/acute-muscle/PPARGC1A.png'; -import PlotSMAD3 from '../assets/plots/analysis/meta-analysis/human/acute-muscle/SMAD3.png'; -import PlotVEGFA from '../assets/plots/analysis/meta-analysis/human/acute-muscle/VEGFA.png'; - -const plotMapping = { - FOXO1: PlotFOXO1, - ID1: PlotID1, - PPARGC1A: PlotPPARGC1A, - SMAD3: PlotSMAD3, - VEGFA: PlotVEGFA, -}; - -const humanMetaAnalysisAcuteMuscleGeneStats = require('../data/human_meta_analysis_acute_muscle_gene_stats'); - -/** - * Functional component to render human meta-analysis acute muscle data visualization - * It uses internal states not shared by other components - * - * @return {Object} JSX representation of the meta analysis data visualization - */ -function HumanGeneMetaAnalysis() { - // Local states - const [inputValue, setInputValue] = useState(''); - const [gene, setGene] = useState(''); - - /** - * Simple Math.round method - * alternative #1 - Math.round(num * 10) / 10; //*** returns 1 decimal - * alternative #2 - Math.round((num + 0.00001) * 100) / 100; //*** returns 2 decimals - */ - const classificationMathRound = (number, decimals) => { - return Number(Math.round(number + ('e' + decimals)) + ('e-' + decimals)); - }; - - const geneObj = humanMetaAnalysisAcuteMuscleGeneStats - .find(item => item.Symbol === gene.toUpperCase()); - - // Renders found gene stats info in the gene search panel - const renderGeneInfo = () => { - if (geneObj && Object.keys(geneObj).length) { - const geneObjClone = { ...geneObj }; - delete geneObjClone.Summary; - - return ( -
- - - {Object.entries(geneObjClone).map(([key, value]) => { - return ( - - - - - ); - })} - - - - -
{`${key}:`}{!Number.isNaN(classificationMathRound(Number(value), 2)) ? classificationMathRound(Number(value), 2) : value}
-
Summary:
- {geneObj.Summary} -
-
- ); - } - - return
No matching gene found.
; - }; - - const geneAanlysisDataArray = HumanMetaAnalysisAcuteMuscleGenes[gene.toUpperCase()] - ? HumanMetaAnalysisAcuteMuscleGenes[gene.toUpperCase()] : null; - - // Renders table head of gene meta-analysis - const renderMetaAnalysisTableHead = () => { - return ( - - Cohort ID - Geo ID - Training - Avg Age - Age SD - SDD - - ); - }; - - // Renders individual rows of gene meta-analysis data - const renderMetaAnalysisTableRows = (data) => { - const rows = data.map(item => ( - - {item.V1} - {item.gse} - {item.training} - {classificationMathRound(Number(item.avg_age), 2)} - {classificationMathRound(Number(item.age_sd), 2)} - {classificationMathRound(Number(item.sdd), 2)} - - )); - - return rows; - }; - - // Renders meta-analysis of a gene for acute muscle - const renderMetaAnalysisData = () => { - if (geneAanlysisDataArray && geneAanlysisDataArray.length) { - return ( -
-
-
- - - {renderMetaAnalysisTableHead()} - - - {renderMetaAnalysisTableRows(geneAanlysisDataArray)} - -
-
-
- info - A cohort can have more than a single data point in a time window. -
-
-
- {gene.toUpperCase()} -
-
- ); - } - - return null; - }; - - return ( -
-
- info - - The following analyses use existing published data sets. They - do not represent the complete meta-analysis data set. Only - 5 genes (FOXO1, ID1, PPARGC1A, SMAD3, VEGFA) are available for - searching in this release. - - -
-
- {/* gene search */} -
-
-
Select a gene:
-
-
-
- { e.preventDefault(); setInputValue(e.target.value); }} - /> -
- -
-
-
-
- {renderGeneInfo()} -
-
- {/* meta-analysis acute muscle data */} -
-
{`Meta-Analysis${geneObj && geneObj.Symbol ? ` of ${geneObj.Symbol.toUpperCase()}` : ''} for Acute Muscle`}
- {renderMetaAnalysisData()} -
-
-
- ); -} - -export default HumanGeneMetaAnalysis; diff --git a/src/AnnouncementsPage/announcementsPage.jsx b/src/AnnouncementsPage/announcementsPage.jsx index eb6cde67..8502eacf 100644 --- a/src/AnnouncementsPage/announcementsPage.jsx +++ b/src/AnnouncementsPage/announcementsPage.jsx @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { Link } from 'react-router-dom'; import dayjs from 'dayjs'; -import { TrackEvent } from '../GoogleAnalytics/googleAnalytics'; +import { trackEvent } from '../GoogleAnalytics/googleAnalytics'; import AuthContentContainer from '../lib/ui/authContentContainer'; const announcementData = require('./announcements'); @@ -77,7 +77,7 @@ function AnnouncementEntry({ entry }) { className="inline-link-with-icon" target="_blank" rel="noopener noreferrer" - onClick={TrackEvent.bind( + onClick={trackEvent.bind( this, link.gaEventCategory, link.gaEventAction, diff --git a/src/App/__test__/App.test.jsx b/src/App/__test__/App.test.jsx index e51fd948..614ac833 100644 --- a/src/App/__test__/App.test.jsx +++ b/src/App/__test__/App.test.jsx @@ -5,8 +5,8 @@ import App from '../App'; const testUser = require('../../testData/testUser'); -// Mocking ReactGA for Google Analytics -jest.mock('react-ga'); +// Mocking Google Analytics +jest.mock('ga-gtag'); describe('', () => { let component; diff --git a/src/GoogleAnalytics/googleAnalytics.jsx b/src/GoogleAnalytics/googleAnalytics.jsx index c2fab877..492e70d5 100644 --- a/src/GoogleAnalytics/googleAnalytics.jsx +++ b/src/GoogleAnalytics/googleAnalytics.jsx @@ -1,7 +1,7 @@ import React, { useEffect } from 'react'; -import ReactGA from 'react-ga'; +import gtag from 'ga-gtag'; -export const initializeReactGA = () => { +const trackingId = () => { // available tracking Ids for motrpac apps const trackers = { 'dev.motrpac-data.org': 'UA-137588878-5', @@ -35,21 +35,12 @@ export const initializeReactGA = () => { // use correct tracking Id based on hostname const tracker = trackers[analyticsTrackerHostname]; - ReactGA.initialize(tracker, { - gaOptions: { - cookieDomain: 'none', - siteSpeedSampleRate: 100, - }, - }); + return tracker; }; -export const withTracker = (WrappedComponent, options = {}) => { +export const withTracker = (WrappedComponent) => { const trackPage = (page) => { - ReactGA.set({ - page, - ...options, - }); - ReactGA.pageview(page); + gtag('config', trackingId(), {'page_path': page, 'site_speed_sample_rate' : 100}); }; const HOC = (props) => { @@ -64,16 +55,11 @@ export const withTracker = (WrappedComponent, options = {}) => { return HOC; }; -/** - * TrackEvent - Add custom tracking event. - * @param {string} category - * @param {string} action - * @param {string} label - */ -export const TrackEvent = (category, action, label) => { - ReactGA.event({ - category, - action, - label, +export const trackEvent = (category, action, label) => { + gtag('event', action, { + event_category: category, + event_label: label, }); }; + +export default trackingId; diff --git a/src/LandingPage/landingPage.jsx b/src/LandingPage/landingPage.jsx index c5d10404..7d277f20 100644 --- a/src/LandingPage/landingPage.jsx +++ b/src/LandingPage/landingPage.jsx @@ -4,7 +4,7 @@ import { connect } from 'react-redux'; import { Redirect } from 'react-router-dom'; import Particles from 'react-particles-js'; import { useSpring, animated } from 'react-spring'; -import { TrackEvent } from '../GoogleAnalytics/googleAnalytics'; +import { trackEvent } from '../GoogleAnalytics/googleAnalytics'; import LogoAnimation from '../assets/LandingPageGraphics/LogoAnimation_03082019-yellow_pipelineball_left.gif'; import LayerRunner from '../assets/LandingPageGraphics/Data_Layer_Runner.png'; import HealthyHeart from '../assets/LandingPageGraphics/Infographic_Healthy_Heart.png'; @@ -97,7 +97,7 @@ export function LandingPage({ isAuthenticated, profile }) { className="inline-link-with-icon" target="_blank" rel="noopener noreferrer" - onClick={TrackEvent.bind( + onClick={trackEvent.bind( this, 'MoTrPAC Marker Paper', 'Cell Publication', @@ -118,7 +118,7 @@ export function LandingPage({ isAuthenticated, profile }) { className="inline-link-with-icon" target="_blank" rel="noopener noreferrer" - onClick={TrackEvent.bind( + onClick={trackEvent.bind( this, 'MoTrPAC Marker Paper', 'NIH Press Release', diff --git a/src/ReleasePage/releaseEntry.jsx b/src/ReleasePage/releaseEntry.jsx index 70c30a34..edcedc47 100644 --- a/src/ReleasePage/releaseEntry.jsx +++ b/src/ReleasePage/releaseEntry.jsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; import PropTypes from 'prop-types'; import axios from 'axios'; -import { TrackEvent } from '../GoogleAnalytics/googleAnalytics'; +import { trackEvent } from '../GoogleAnalytics/googleAnalytics'; import IconSet from '../lib/iconSet'; import ToolTip from '../lib/ui/tooltip'; import ExternalLink from '../lib/ui/externalLink'; @@ -38,6 +38,7 @@ function ReleaseEntry({ profile, currentView }) { status: null, file: null, message: '', + releaseVersion: null, }); const [visibleReleases, setVisibleReleases] = useState(2); @@ -113,7 +114,7 @@ function ReleaseEntry({ profile, currentView }) { } // Fetch file url from Google Storage API - function fetchFile(bucket, filename) { + function fetchFile(bucket, filename, version) { return axios.get(`https://data-link-access.motrpac-data.org/${bucket}/${filename}`) .then((response) => { setFileUrl(response.data.url); @@ -121,6 +122,7 @@ function ReleaseEntry({ profile, currentView }) { status: 'success', file: filename, message: 'Click this link to download the requested file.', + releaseVersion: version, }); setFetching(false); }).catch((err) => { @@ -130,31 +132,39 @@ function ReleaseEntry({ profile, currentView }) { status: 'error', file: filename, message: 'Error occurred. Please close the dialog box and try again.', + releaseVersion: version, }); setFetching(false); }); } - // Handle modal download button click event - function handleGAEvent(releaseVersion) { - TrackEvent(`Release ${releaseVersion} Downloads (${currentView})`, modalStatus.file, profile.user_metadata.name); - } - // Render modal message - function renderModalMessage(releaseVersion) { + function renderModalMessage() { if (modalStatus.status !== 'success') { return {modalStatus.message}; } return ( - {modalStatus.message} + + {modalStatus.message} + ); } // Render modal - function renderModal(releaseVersion) { + function renderModal() { return ( {currentView === 'internal' && release.raw_files diff --git a/src/assets/plots/analysis/meta-analysis/human/acute-muscle/FOXO1.png b/src/assets/plots/analysis/meta-analysis/human/acute-muscle/FOXO1.png deleted file mode 100644 index e776506e..00000000 Binary files a/src/assets/plots/analysis/meta-analysis/human/acute-muscle/FOXO1.png and /dev/null differ diff --git a/src/assets/plots/analysis/meta-analysis/human/acute-muscle/ID1.png b/src/assets/plots/analysis/meta-analysis/human/acute-muscle/ID1.png deleted file mode 100644 index e776506e..00000000 Binary files a/src/assets/plots/analysis/meta-analysis/human/acute-muscle/ID1.png and /dev/null differ diff --git a/src/assets/plots/analysis/meta-analysis/human/acute-muscle/PPARGC1A.png b/src/assets/plots/analysis/meta-analysis/human/acute-muscle/PPARGC1A.png deleted file mode 100644 index e776506e..00000000 Binary files a/src/assets/plots/analysis/meta-analysis/human/acute-muscle/PPARGC1A.png and /dev/null differ diff --git a/src/assets/plots/analysis/meta-analysis/human/acute-muscle/SMAD3.png b/src/assets/plots/analysis/meta-analysis/human/acute-muscle/SMAD3.png deleted file mode 100644 index e776506e..00000000 Binary files a/src/assets/plots/analysis/meta-analysis/human/acute-muscle/SMAD3.png and /dev/null differ diff --git a/src/assets/plots/analysis/meta-analysis/human/acute-muscle/VEGFA.png b/src/assets/plots/analysis/meta-analysis/human/acute-muscle/VEGFA.png deleted file mode 100644 index e776506e..00000000 Binary files a/src/assets/plots/analysis/meta-analysis/human/acute-muscle/VEGFA.png and /dev/null differ diff --git a/src/data/humanMetaAnalysisAcuteMuscleGenes.js b/src/data/humanMetaAnalysisAcuteMuscleGenes.js deleted file mode 100644 index d9a1275d..00000000 --- a/src/data/humanMetaAnalysisAcuteMuscleGenes.js +++ /dev/null @@ -1,15 +0,0 @@ -const FOXO1 = require('./human_acute_muscle_genes/FOXO1'); -const ID1 = require('./human_acute_muscle_genes/ID1'); -const PPARGC1A = require('./human_acute_muscle_genes/PPARGC1A'); -const SMAD3 = require('./human_acute_muscle_genes/SMAD3'); -const VEGFA = require('./human_acute_muscle_genes/VEGFA'); - -const HumanMetaAnalysisAcuteMuscleGenes = { - FOXO1, - ID1, - PPARGC1A, - SMAD3, - VEGFA, -}; - -export default HumanMetaAnalysisAcuteMuscleGenes; diff --git a/src/data/human_acute_muscle_genes/FOXO1.json b/src/data/human_acute_muscle_genes/FOXO1.json deleted file mode 100644 index 710e9394..00000000 --- a/src/data/human_acute_muscle_genes/FOXO1.json +++ /dev/null @@ -1,6 +0,0 @@ -[ - { - "tissue": "muscle", - "training": "endurance" - } -] diff --git a/src/data/human_acute_muscle_genes/ID1.json b/src/data/human_acute_muscle_genes/ID1.json deleted file mode 100644 index 710e9394..00000000 --- a/src/data/human_acute_muscle_genes/ID1.json +++ /dev/null @@ -1,6 +0,0 @@ -[ - { - "tissue": "muscle", - "training": "endurance" - } -] diff --git a/src/data/human_acute_muscle_genes/PPARGC1A.json b/src/data/human_acute_muscle_genes/PPARGC1A.json deleted file mode 100644 index 710e9394..00000000 --- a/src/data/human_acute_muscle_genes/PPARGC1A.json +++ /dev/null @@ -1,6 +0,0 @@ -[ - { - "tissue": "muscle", - "training": "endurance" - } -] diff --git a/src/data/human_acute_muscle_genes/SMAD3.json b/src/data/human_acute_muscle_genes/SMAD3.json deleted file mode 100644 index 710e9394..00000000 --- a/src/data/human_acute_muscle_genes/SMAD3.json +++ /dev/null @@ -1,6 +0,0 @@ -[ - { - "tissue": "muscle", - "training": "endurance" - } -] diff --git a/src/data/human_acute_muscle_genes/VEGFA.json b/src/data/human_acute_muscle_genes/VEGFA.json deleted file mode 100644 index 710e9394..00000000 --- a/src/data/human_acute_muscle_genes/VEGFA.json +++ /dev/null @@ -1,6 +0,0 @@ -[ - { - "tissue": "muscle", - "training": "endurance" - } -] diff --git a/src/data/human_meta_analysis_acute_muscle_gene_stats.json b/src/data/human_meta_analysis_acute_muscle_gene_stats.json deleted file mode 100644 index 39d5161f..00000000 --- a/src/data/human_meta_analysis_acute_muscle_gene_stats.json +++ /dev/null @@ -1,8 +0,0 @@ -[ - { - "Symbol": "PCMTD2", - "Entrez": "55251", - "SelectedModel": "training", - "Selected": "FALSE" - } -] diff --git a/src/index.js b/src/index.js index 7f330630..55e25025 100644 --- a/src/index.js +++ b/src/index.js @@ -1,12 +1,12 @@ import React from 'react'; import { render } from 'react-dom'; -import { initializeReactGA } from './GoogleAnalytics/googleAnalytics'; +import { install } from 'ga-gtag'; +import trackingId from './GoogleAnalytics/googleAnalytics'; import App from './App/App'; import * as serviceWorker from './serviceWorker'; import './main.css'; -// Initialize Google Analytics -initializeReactGA(); +install(trackingId()); render(, document.getElementById('root'));