Skip to content

Commit

Permalink
feat: add hotjar library
Browse files Browse the repository at this point in the history
  • Loading branch information
long74100 committed Jun 22, 2022
1 parent e9ed4c8 commit 90cc131
Show file tree
Hide file tree
Showing 19 changed files with 1,362 additions and 374 deletions.
3 changes: 2 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ frontend-enterprise
- `@edx/frontend-enterprise-catalog-search </packages/catalog-search>`_
- `@edx/frontend-enterprise-logistration </packages/logistration>`_
- `@edx/frontend-enterprise-utils </packages/utils>`_
- `@edx/frontend-enterprise-hotjar </packages/hotjar>`

Each of these packages is published to NPM and have their own README files. The packages can be found in the ``packages/*`` folder.

Expand Down Expand Up @@ -92,7 +93,7 @@ Managing package dependencies

Each package in the monorepo contains its own package.json file and unique set of dependencies depending on their needs. However, issues may arise when importing conflicting versions of external packages (e.g., React) in multiple monorepo packages. This is because some dependencies only properly work when there is a single copy of the dependency to ensure the same version is used throughout an application. For example, ``react`` and ``react-dom`` are common offenders here as there can only be one copy of React used at any given time. If a library/app attempts to use more than one copy or differening versions of React, there will be unintended behavior and warnings.

To get around this issue of common/shared dependencies, we can rely on how NPM finds installed packages. If a package does not exist in ``node_modules`` for an individual package, NPM will look in ``node_modules`` further up the directory tree until it finds the package, or gets to the root of the repository.
To get around this issue of common/shared dependencies, we can rely on how NPM finds installed packages. If a package does not exist in ``node_modules`` for an individual package, NPM will look in ``node_modules`` further up the directory tree until it finds the package, or gets to the root of the repository.

NPM workspaces helps with this by hoisting installed packages to the root `node_modules` folder where they will be accessible to any package in the monorepo to ensure there is only one copy used throughout. These dependencies are still noted in each individual package.json file as both a peer dependency and a dev dependency.

Expand Down
705 changes: 340 additions & 365 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"clean": "npm run clean --workspaces && rm -rf ./node_modules",
"build": "npm run build --workspaces",
"test": "npm run test --workspaces",
"test:watch": "npm run test -- --watch",
"lint": "npm run lint --workspaces",
"lint:fix": "npm run lint:fix --workspaces",
"changed": "lerna changed"
Expand Down
7 changes: 4 additions & 3 deletions packages/catalog-search/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"lint": "fedx-scripts eslint --ext .js --ext .jsx .",
"lint:fix": "fedx-scripts eslint --fix --ext .js --ext .jsx .",
"snapshot": "fedx-scripts jest --updateSnapshot",
"test": "fedx-scripts jest --coverage --passWithNoTests"
"test": "fedx-scripts jest --coverage --passWithNoTests",
"test:watch": "npm run test -- --watch"
},
"author": "edX",
"license": "AGPL-3.0",
Expand All @@ -42,8 +43,8 @@
"prop-types": "15.7.2"
},
"devDependencies": {
"@edx/browserslist-config": "1.0.0",
"@edx/frontend-build": "9.1.4",
"@edx/browserslist-config": "1.1.0",
"@edx/frontend-build": "11.0.1",
"@edx/frontend-platform": "1.15.6",
"@edx/paragon": "19.25.1",
"@fortawesome/free-solid-svg-icons": "5.8.1",
Expand Down
5 changes: 5 additions & 0 deletions packages/hotjar/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
coverage
dist
node_modules
__mocks__/
__snapshots__/
7 changes: 7 additions & 0 deletions packages/hotjar/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const { createConfig } = require('@edx/frontend-build');
const extendESLintConfig = require('../../common/extendESLintConfig');

const config = createConfig('eslint');
extendESLintConfig(config);

module.exports = config;
3 changes: 3 additions & 0 deletions packages/hotjar/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
dist
coverage
7 changes: 7 additions & 0 deletions packages/hotjar/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
*.config.js*
coverage
Makefile
src/**/*.test.js
src/**/*.test.jsx
src/**/tests
src/**/setupTest.js
Empty file added packages/hotjar/CHANGELOG.md
Empty file.
661 changes: 661 additions & 0 deletions packages/hotjar/LICENSE

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions packages/hotjar/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
build:
make clean
../../node_modules/.bin/fedx-scripts babel src --out-dir dist --source-maps --ignore **/*.test.jsx,**/*.test.js,**/setupTest.js --copy-files
@# --copy-files will bring in everything else that wasn't processed by babel. Remove what we don't want.
@find dist -name '*.test.js*' -delete
cp ./package.json ./dist/package.json
cp ./LICENSE ./dist/LICENSE
cp ./README.rst ./dist/README.rst
cp ./CHANGELOG.md ./dist/CHANGELOG.md

clean:
rm -rf ./dist ./node_modules
15 changes: 15 additions & 0 deletions packages/hotjar/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
@edx/frontend-enterprise-hotjar
=============

|Build Status| |npm_version| |npm_downloads| |license|

This package exports useful React hooks, utility functions, and constants for using Hotjar.

.. |Build Status| image:: https://github.com/edx/frontend-enterprise/actions/workflows/release.yml/badge.svg
:target: https://github.com/edx/frontend-enterprise/actions
.. |npm_version| image:: https://img.shields.io/npm/v/@edx/frontend-enterprise-utils.svg
:target: @edx/frontend-enterprise-utils
.. |npm_downloads| image:: https://img.shields.io/npm/dt/@edx/frontend-enterprise-utils.svg
:target: @edx/frontend-enterprise-utils
.. |license| image:: https://img.shields.io/npm/l/@edx/frontend-enterprise-utils.svg
:target: @edx/frontend-enterprise-utils
3 changes: 3 additions & 0 deletions packages/hotjar/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const { createConfig } = require('@edx/frontend-build');

module.exports = createConfig('babel-preserve-modules');
7 changes: 7 additions & 0 deletions packages/hotjar/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const { createConfig } = require('@edx/frontend-build');
const extendJestConfig = require('../../common/extendJestConfig');

const config = createConfig('jest');
extendJestConfig(config);

module.exports = config;
52 changes: 52 additions & 0 deletions packages/hotjar/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"name": "@edx/frontend-enterprise-hotjar",
"version": "1.0.0",
"description": "Utils for Hotjar.",
"repository": {
"type": "git",
"url": "git+https://github.com/edx/frontend-enterprise.git"
},
"watch": {
"build": {
"patterns": [
"src"
],
"extensions": "js,jsx"
}
},
"scripts": {
"dev": "npx npm-watch build",
"clean": "make clean",
"build": "make build",
"i18n_extract": "BABEL_ENV=i18n fedx-scripts babel src --quiet > /dev/null",
"lint": "fedx-scripts eslint --ext .js --ext .jsx .",
"lint:fix": "fedx-scripts eslint --fix --ext .js --ext .jsx .",
"snapshot": "fedx-scripts jest --updateSnapshot",
"test": "fedx-scripts jest --coverage --passWithNoTests",
"test:watch": "npm run test -- --watch"
},
"author": "edX",
"license": "AGPL-3.0",
"homepage": "https://github.com/edx/frontend-enterprise#readme",
"publishConfig": {
"access": "public",
"directory": "dist"
},
"bugs": {
"url": "https://github.com/edx/frontend-enterprise/issues"
},
"sideEffects": false,
"dependencies": {},
"devDependencies": {
"@edx/browserslist-config": "1.1.0",
"@edx/frontend-build": "11.0.1",
"react": "16.14.0",
"react-dom": "16.14.0",
"react-router-dom": "5.2.0"
},
"peerDependencies": {
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-router-dom": "^5.2.0"
}
}
88 changes: 88 additions & 0 deletions packages/hotjar/src/hotjar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/* eslint-disable no-console */

export const appendHeadScript = ({
scriptText,
scriptId,
}) => {
try {
const existingScript = document.getElementById(
scriptId,
);

if (existingScript) {
console.error(`Script ${scriptId} already exists.`);
return true;
}

const script = existingScript || document.createElement('script');
script.id = scriptId;
script.innerText = scriptText;
script.crossOrigin = 'anonymous';

document.head.appendChild(script);

return true;
} catch {
console.error(`Failed to append script ${scriptId}.`);
return false;
}
};

export const createHotjarScriptText = ({
hotjarId,
hotjarVersion,
hotjarDebug,
}) => `(function(h,o,t,j,a,r){h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)};h._hjSettings={hjid:${hotjarId},hjsv:${hotjarVersion},hjdebug:${hotjarDebug}};a=o.getElementsByTagName('head')[0];r=o.createElement('script');r.async=1;r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv;a.appendChild(r);})(window,document,'https://static.hotjar.com/c/hotjar-','.js?sv=');`;

export const initializeHotjar = (
{
hotjarId,
hotjarVersion,
hotjarDebug,
onInitialize,
},
) => {
const hasWindow = typeof window !== 'undefined';

if (!hasWindow) { throw Error('Hotjar depends on window. Window is undefined.'); }

const hotjarScriptText = createHotjarScriptText({
hotjarId,
hotjarVersion,
hotjarDebug,
});
const isAppended = appendHeadScript({
scriptText: hotjarScriptText,
scriptId: 'hotjar-init-script',
});

if (isAppended && hasWindow && window.hj) {
if (onInitialize) {
onInitialize();
}
} else {
throw Error('Hotjar initialization failed!');
}
};

export const isHotjarInitialized = () => {
const hasWindow = typeof window !== 'undefined';
return hasWindow && window.hj;
};

export const identifyHotjar = ({
userId,
userInfo,
}) => {
const initialized = isHotjarInitialized();

if (initialized) {
return window.hj(
'identify',
userId,
userInfo,
);
}

throw Error('Hotjar is not available! Is Hotjar initialized?');
};
Loading

0 comments on commit 90cc131

Please sign in to comment.