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) => (