diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 0000000..b9b9656
--- /dev/null
+++ b/.eslintignore
@@ -0,0 +1,3 @@
+/build/**
+tailwind.css
+/src/locales/**
diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 0000000..e185715
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,153 @@
+{
+ "parserOptions": {
+ "ecmaVersion": "latest",
+ "sourceType": "module"
+ },
+ "plugins": [
+ "@typescript-eslint",
+ "react",
+ "react-hooks"
+ ],
+ "extends": [
+ "airbnb",
+ "eslint:recommended",
+ "plugin:@typescript-eslint/recommended"
+ ],
+ "env": {
+ "browser": true,
+ "es2021": true
+ },
+ "globals": {
+ "cy": true,
+ "before": true,
+ "beforeEach": true,
+ "describe": true,
+ "document": true,
+ "window": true,
+ "it": true,
+ "expect": true,
+ "appPackage": true,
+ "Cypress": true
+ },
+ "settings": {
+ "import/core-modules": ["@dhis2/d2-i18n", "react"],
+ "import/resolver": {
+ "node": {
+ "extensions": [".js", ".jsx", ".ts", ".tsx"]
+ }
+ },
+ "import/ignore": [
+ "node_modules",
+ ".(json|css)$"
+ ]
+ },
+ "rules": {
+ "indent": [2, 4],
+ "complexity": "off",
+ "no-prototype-builtins": "off",
+ "no-redeclare": "error",
+ "no-plusplus": ["error", { "allowForLoopAfterthoughts": true }],
+ "jsx-quotes": ["error", "prefer-single"],
+ "no-console": ["error", {
+ "allow": ["warn", "error"]
+ }],
+ "comma-dangle": [
+ "error",
+ {
+ "arrays": "always-multiline",
+ "objects": "always-multiline",
+ "imports": "always-multiline",
+ "exports": "always-multiline",
+ "functions": "always-multiline"
+ }
+ ],
+ "no-multi-spaces": ["error", { "ignoreEOLComments": true }],
+ "no-return-assign": ["error", "except-parens"],
+ "react/jsx-indent": [1, 4],
+ "@typescript-eslint/ban-ts-comment": "off",
+ "@typescript-eslint/no-explicit-any": "off",
+ "implicit-arrow-linebreak": "off",
+ "react/jsx-indent-props": [2, 4],
+ "react/prefer-es6-class": [1, "always"],
+ "react/jsx-filename-extension": [1, { "extensions": [".ts", ".tsx"] }],
+ "react/no-unused-prop-types": [2, { "skipShapeProps": true }],
+ "react/forbid-prop-types": [0],
+ "@typescript-eslint/no-unnecessary-type-constraint": "warn",
+ "no-param-reassign": 0,
+ "react/prop-types": 0,
+ "import/prefer-default-export": 0,
+ "react/prefer-stateless-function": 0,
+ "no-unused-expressions": 0,
+ "linebreak-style": "off",
+ "@typescript-eslint/no-unused-vars": "error",
+ "react/jsx-no-bind": 0,
+ "react/require-default-props": 0,
+ "react/jsx-curly-brace-presence": 0,
+ "react/function-component-definition": 0,
+ "@typescript-eslint/no-empty-function": "off",
+ "import/no-extraneous-dependencies": [
+ "error",
+ {
+ "devDependencies": [
+ "**/*.test.js",
+ "cypress/**",
+ "cypress.config.ts"
+ ],
+ "optionalDependencies": false,
+ "peerDependencies": true
+ }
+ ],
+ "react/button-has-type": 0,
+ "react/sort-comp": [
+ 2,
+ {
+ "order": [
+ "static-methods",
+ "type-annotations",
+ "lifecycle",
+ "everything-else",
+ "render"
+ ],
+ "groups": {
+ "lifecycle": [
+ "displayName",
+ "propTypes",
+ "contextTypes",
+ "childContextTypes",
+ "mixins",
+ "statics",
+ "defaultProps",
+ "constructor",
+ "getDefaultProps",
+ "getInitialState",
+ "state",
+ "getChildContext",
+ "UNSAFE_componentWillMount",
+ "componentDidMount",
+ "UNSAFE_componentWillReceiveProps",
+ "shouldComponentUpdate",
+ "UNSAFE_componentWillUpdate",
+ "componentDidUpdate",
+ "componentWillUnmount"
+ ]
+ }
+ }
+ ],
+ "react-hooks/exhaustive-deps": "error",
+ "object-curly-newline": [
+ "error",
+ {
+ "ObjectExpression": { "multiline": true, "minProperties": 3 },
+ "ObjectPattern": { "multiline": true, "minProperties": 4 },
+ "ImportDeclaration": "never",
+ "ExportDeclaration": { "multiline": true, "minProperties": 3 }
+ }
+ ],
+ "max-len": ["error", { "code": 120 }],
+ "no-shadow": "off",
+ "react/jsx-props-no-spreading": "off",
+ "camelcase": "off",
+ "import/extensions": ["error", "never"]
+ }
+ }
+
\ No newline at end of file
diff --git a/d2.config.js b/d2.config.js
index adf62e9..3e6198b 100644
--- a/d2.config.js
+++ b/d2.config.js
@@ -1,9 +1,7 @@
const config = {
type: 'app',
- entryPoints: {
- plugin: './src/Plugin.tsx'
- },
-}
+ entryPoints: { plugin: './src/Plugin.tsx' },
+};
-module.exports = config
+module.exports = config;
diff --git a/package.json b/package.json
index 284f74d..dae1755 100644
--- a/package.json
+++ b/package.json
@@ -8,11 +8,19 @@
"build": "d2-app-scripts build",
"start": "d2-app-scripts start",
"test": "d2-app-scripts test",
- "deploy": "d2-app-scripts deploy"
+ "deploy": "d2-app-scripts deploy",
+ "tsc:check": "tsc --noEmit",
+ "lint": "eslint -c .eslintrc.json . --quiet"
},
"devDependencies": {
"@dhis2/cli-app-scripts": "^10.4.0",
+ "eslint": "8.56.0",
+ "eslint-config-airbnb": "^19.0.4",
+ "eslint-plugin-import": "^2.29.1",
+ "eslint-plugin-jsx-a11y": "^6.8.0",
+ "eslint-plugin-react": "^7.33.2",
"react-scripts": "5.1.0-next.14",
+ "typescript": "^4.8.3",
"tailwindcss": "^3.4.1"
},
"dependencies": {
@@ -23,10 +31,15 @@
"@tanstack/react-query": "^4",
"@types/chart.js": "^2.9.41",
"chart.js": "^3.0.0",
- "react-chartjs-2": "^5.2.0"
+ "classnames": "^2.5.1",
+ "react-chartjs-2": "^5.2.0",
+ "react-dom": "^17.0.0",
+ "react": "^17.0.0"
},
"resolutions": {
"@dhis2/ui": "^9.0.1",
- "@dhis2/app-runtime": "^3.10.2"
+ "@dhis2/app-runtime": "^3.10.2",
+ "react-dom": "^17.0.0",
+ "react": "^17.0.0"
}
}
diff --git a/src/App.js b/src/App.js
index 92af077..44625be 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,20 +1,18 @@
-import React from 'react'
-import Plugin from "./Plugin";
+import React from 'react';
+import Plugin from './Plugin';
import './tailwind.css';
import './index.css';
-const query = {
- me: {
- resource: 'me',
- },
-}
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+const query = { me: { resource: 'me' } };
const MyApp = () => (
+ // eslint-disable-next-line react/jsx-filename-extension
-)
+);
-export default MyApp
+export default MyApp;
diff --git a/src/App.test.js b/src/App.test.js
deleted file mode 100644
index 0a72d6b..0000000
--- a/src/App.test.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import { CustomDataProvider } from '@dhis2/app-runtime'
-import React from 'react'
-import ReactDOM from 'react-dom'
-import App from './App'
-
-it('renders without crashing', () => {
- const div = document.createElement('div')
- ReactDOM.render(
-
-
- ,
- div
- )
- ReactDOM.unmountComponentAtNode(div)
-})
diff --git a/src/Components/GrowthChart/GrowthChart.tsx b/src/Components/GrowthChart/GrowthChart.tsx
index 17f4473..a6bf657 100644
--- a/src/Components/GrowthChart/GrowthChart.tsx
+++ b/src/Components/GrowthChart/GrowthChart.tsx
@@ -1,12 +1,12 @@
-import React from "react";
-import { GrowthChartBuilder } from "./GrowthChartBuilder";
-import { chartData } from "../../DataSets/ChartData";
+import React from 'react';
+import { GrowthChartBuilder } from './GrowthChartBuilder';
+import { chartData } from '../../DataSets/ChartData';
import { useRangeTimePeriode } from './useRangeTimePeriode';
export const GrowthChart = () => {
- const { datasets, metadata } = chartData["Weight-for-age GIRLS"];
- const dataSetValues = datasets["Girls0to5Years"];
- const dataSetMetadata = metadata["Girls0to5Years"];
+ const { datasets, metadata } = chartData['Weight-for-age GIRLS'];
+ const dataSetValues = datasets.Girls0to5Years;
+ const dataSetMetadata = metadata.Girls0to5Years;
const xLabelValues = useRangeTimePeriode(dataSetMetadata.range.start, dataSetMetadata.range.end);
const keysDataSet = Object.keys(dataSetValues[0]);
@@ -15,10 +15,12 @@ export const GrowthChart = () => {
console.error('xLabelValues and dataSet should have the same length');
}
- return ;
+ return (
+
+ );
};
diff --git a/src/Components/GrowthChart/GrowthChartBuilder/GrowthChartBuilder.tsx b/src/Components/GrowthChart/GrowthChartBuilder/GrowthChartBuilder.tsx
index 8ffc4cd..015bb3d 100644
--- a/src/Components/GrowthChart/GrowthChartBuilder/GrowthChartBuilder.tsx
+++ b/src/Components/GrowthChart/GrowthChartBuilder/GrowthChartBuilder.tsx
@@ -1,16 +1,17 @@
import React from 'react';
-import i18n from '@dhis2/d2-i18n'
+import i18n from '@dhis2/d2-i18n';
import { Line } from 'react-chartjs-2';
import 'chart.js/auto';
import { ChartDataTypes } from '../../../types/chartDataTypes';
import { chartLineColorPicker } from '../../../utils/chartLineColorPicker';
-
-export const GrowthChartBuilder = ({ dataSetValues, dataSetMetadata, xLabelValues, keysDataSet }: ChartDataTypes) => {
+export const GrowthChartBuilder = ({
+ dataSetValues, dataSetMetadata, xLabelValues, keysDataSet,
+}: ChartDataTypes) => {
const data = {
labels: xLabelValues,
- datasets: keysDataSet.map(key => ({
- data: dataSetValues.map(entry => entry[key]),
+ datasets: keysDataSet.map((key) => ({
+ data: dataSetValues.map((entry) => entry[key]),
borderWidth: 0.9,
borderColor: chartLineColorPicker(key),
})),
@@ -20,7 +21,7 @@ export const GrowthChartBuilder = ({ dataSetValues, dataSetMetadata, xLabelValue
elements: { point: { radius: 0, hoverRadius: 0 } },
plugins: { legend: { display: false } },
scales: {
- x: { title: { display: true, text: i18n.t(`age (${dataSetMetadata.unit})`)} },
+ x: { title: { display: true, text: i18n.t(`age (${dataSetMetadata.unit})`) } },
y: { title: { display: true, text: dataSetMetadata.yaxis } },
},
};
diff --git a/src/Components/GrowthChart/GrowthChartBuilder/index.ts b/src/Components/GrowthChart/GrowthChartBuilder/index.ts
index 0c9b7c1..8f2e00d 100644
--- a/src/Components/GrowthChart/GrowthChartBuilder/index.ts
+++ b/src/Components/GrowthChart/GrowthChartBuilder/index.ts
@@ -1 +1 @@
-export { GrowthChartBuilder } from './GrowthChartBuilder'
\ No newline at end of file
+export { GrowthChartBuilder } from './GrowthChartBuilder';
diff --git a/src/Components/GrowthChart/useRangeTimePeriode.ts b/src/Components/GrowthChart/useRangeTimePeriode.ts
index a5e2643..f1bfc1e 100644
--- a/src/Components/GrowthChart/useRangeTimePeriode.ts
+++ b/src/Components/GrowthChart/useRangeTimePeriode.ts
@@ -1,3 +1,4 @@
-export const useRangeTimePeriode = (start: number, end: number) => {
- return Array.from({ length: end - start + 1 }, (_, index) => start + index);
-};
\ No newline at end of file
+export const useRangeTimePeriode = (start: number, end: number) => Array.from(
+ { length: end - start + 1 },
+ (_, index) => start + index,
+);
diff --git a/src/Components/WidgetCollapsible/IconButton/IconButton.tsx b/src/Components/WidgetCollapsible/IconButton/IconButton.tsx
index 71c656f..377e557 100644
--- a/src/Components/WidgetCollapsible/IconButton/IconButton.tsx
+++ b/src/Components/WidgetCollapsible/IconButton/IconButton.tsx
@@ -1,16 +1,15 @@
-// @flow
-import React from 'react';
+import React, { ReactNode } from 'react';
import cx from 'classnames';
import { colors } from '@dhis2/ui';
import { withStyles } from '@material-ui/core/styles';
-
type Props = {
- children: React$Node,
+ children: ReactNode,
className?: string,
dataTest?: string,
disabled?: boolean,
onClick?: () => void,
+ classes?: Record
};
const styles = {
@@ -40,14 +39,16 @@ const styles = {
},
};
-const IconButtonPlain = ({ children, className, dataTest, onClick, disabled, classes, ...passOnProps }: Props) => (
+const IconButtonPlain = ({
+ children, className, dataTest, onClick, disabled, classes, ...passOnProps
+}: Props) => (