diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..d5486b6 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,76 @@ +{ + "env": { + "node": true + }, + "extends": [ + "eslint:recommended", + "plugin:prettier/recommended", + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/recommended-requiring-type-checking", + "prettier" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "project": true, + "tsconfigRootDir": "__dirname" + }, + "plugins": ["@typescript-eslint"], + "root": true, + "rules": { + // turned off to keep the diff small for now + "@typescript-eslint/no-floating-promises": "off", + "@typescript-eslint/no-misused-promises": "off", + // The following rules need `noImplicitAny` to be set to `true` in our tsconfig. They are too restrictive for now, but should be reconsidered in future + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-unsafe-call": "off", + "@typescript-eslint/no-unsafe-member-access": "off", + "@typescript-eslint/no-unsafe-return": "off", + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/unbound-method": "off", + // Nitpicky. Prefer `interface T` over type T + "@typescript-eslint/consistent-type-definitions": "off", + "@typescript-eslint/consistent-type-exports": "error", + // Use `import type` instead of `import` for type imports + "@typescript-eslint/consistent-type-imports": [ + "error", + { + "fixStyle": "inline-type-imports" + } + ], + // Use Array instead of T[] consistently + "@typescript-eslint/array-type": [ + "error", + { + "default": "generic" + } + ], + "no-console": ["error", { "allow": ["warn", "error"] }], + "no-restricted-imports": [ + "error", + { + "paths": [ + // These two rules ensure that we're importing lodash and lodash-es correctly. Not doing so can bloat our bundle size significantly. + { + "name": "lodash", + "message": "Import specific methods from `lodash`. e.g. `import map from 'lodash/map'`" + }, + { + "name": "lodash-es", + "importNames": ["default"], + "message": "Import specific methods from `lodash-es`. e.g. `import { map } from 'lodash-es'`" + }, + // These two rules ensure that we're importing Carbon components and icons from the correct packages (after v10). May be removed in the future. + { + "name": "carbon-components-react", + "message": "Import from `@carbon/react` directly. e.g. `import { Toggle } from '@carbon/react'`" + }, + { + "name": "@carbon/icons-react", + "message": "Import from `@carbon/react/icons`. e.g. `import { ChevronUp } from '@carbon/react/icons'`" + } + ] + } + ] + } +} diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 717be3b..0000000 --- a/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - "extends": [ - "eslint:recommended", - "ts-react-important-stuff", - "plugin:prettier/recommended", - 'react-app', - 'plugin:@typescript-eslint/recommended', - ], - "parser": "@typescript-eslint/parser", -} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aaf3880..f0f0c99 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: Node.js CI +name: OpenMRS CI on: push: @@ -25,7 +25,7 @@ jobs: node-version: "18" - run: yarn install --immutable - run: yarn verify - - run: yarn build + - run: yarn turbo build - name: Upload Artifacts uses: actions/upload-artifact@v4 with: @@ -57,7 +57,7 @@ jobs: run: yarn install --immutable - run: yarn version "$(node -e "console.log(require('semver').inc(require('./package.json').version, 'patch'))")-pre.${{ github.run_number }}" - - run: yarn build + - run: yarn turbo build - run: git config user.email "info@openmrs.org" && git config user.name "OpenMRS CI" - run: git add . && git commit -m "Prerelease version" --no-verify - run: yarn config set npmAuthToken "${NODE_AUTH_TOKEN}" && yarn npm publish --tag next @@ -102,6 +102,6 @@ jobs: with: node-version: "18" registry-url: 'https://registry.npmjs.org' - - run: yarn config set npmAuthToken "${NODE_AUTH_TOKEN}" && yarn npm publish + - run: yarn config set npmAuthToken "${NODE_AUTH_TOKEN}" && yarn npm publish env: NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} diff --git a/.gitignore b/.gitignore index d65a5aa..5cb9af9 100644 --- a/.gitignore +++ b/.gitignore @@ -73,3 +73,5 @@ dist/ !.yarn/releases !.yarn/sdks !.yarn/versions + +.turbo diff --git a/.husky/pre-push b/.husky/pre-push index d008e86..879f05d 100755 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -3,5 +3,4 @@ set -e # die on error -yarn run verify - +yarn verify diff --git a/package.json b/package.json index 033536e..b9c20f9 100644 --- a/package.json +++ b/package.json @@ -9,13 +9,13 @@ "scripts": { "start": "openmrs develop", "serve": "webpack serve --mode=development", - "build": "webpack --mode production", + "build": "webpack --mode production --color", "analyze": "webpack --mode=production --env.analyze=true", - "lint": "eslint \"src/**/*.ts\" \"src/**/*.tsx\" --fix --max-warnings=0 -c .eslintrc.js", + "lint": "eslint \"src/**/*.ts\" \"src/**/*.tsx\" --fix --max-warnings=0", "prettier": "prettier --write \"src/**/*.{ts,tsx}\" --list-different", "typescript": "tsc", "test": "jest --config jest.config.json --passWithNoTests", - "verify": "concurrently 'yarn:lint' 'yarn:test' 'yarn:typescript'", + "verify": "turbo lint typescript test", "coverage": "yarn test -- --coverage ", "postinstall": "husky install", "extract-translations": "i18next 'src/**/*.tsx' --config ./tools/i18next-parser.config.js" @@ -49,46 +49,47 @@ "swr": "2.x" }, "devDependencies": { - "@carbon/react": "^1.9.0", + "@carbon/react": "^1.37.0", "@openmrs/esm-framework": "next", - "@swc-node/loader": "^1.3.5", - "@swc/core": "^1.2.245", - "@swc/jest": "^0.2.22", - "@testing-library/dom": "^7.20.0", - "@testing-library/jest-dom": "^5.16.5", - "@testing-library/react": "^13.3.0", + "@swc-node/loader": "^1.3.7", + "@swc/core": "^1.3.84", + "@swc/jest": "^0.2.29", + "@testing-library/dom": "^7.31.2", + "@testing-library/jest-dom": "^5.17.0", + "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^14.4.3", - "@types/jest": "^28.1.7", - "@types/react-dom": "^18.0.6", + "@types/jest": "^28.1.8", + "@types/react-dom": "^18.2.7", "@types/react-router-dom": "^5.3.3", - "@types/testing-library__jest-dom": "^5.14.5", - "@types/webpack-env": "^1.16.0", - "@typescript-eslint/parser": "^5.14.0", - "concurrently": "^6.2.0", + "@types/testing-library__jest-dom": "^5.14.9", + "@types/webpack-env": "^1.18.1", + "@typescript-eslint/eslint-plugin": "^6.7.0", + "@typescript-eslint/parser": "^6.7.0", + "concurrently": "^6.5.1", "css-loader": "^6.8.1", - "eslint": "^8.20.0", - "eslint-config-prettier": "^8.3.0", + "eslint": "^8.49.0", + "eslint-config-prettier": "^8.10.0", "eslint-config-react-app": "^7.0.1", - "eslint-config-ts-react-important-stuff": "^3.0.0", "eslint-plugin-prettier": "^4.2.1", - "husky": "^8.0.1", + "husky": "^8.0.3", "identity-obj-proxy": "^3.0.0", "jest": "^28.1.3", "jest-cli": "^28.1.3", "jest-environment-jsdom": "^28.1.3", "lodash-es": "^4.17.21", "openmrs": "next", - "prettier": "^2.3.0", - "pretty-quick": "^3.1.0", + "prettier": "^2.8.8", + "pretty-quick": "^3.1.3", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-i18next": "^11.18.4", - "react-router-dom": "^6.3.0", - "semver": "^7.3.7", + "react-i18next": "^11.18.6", + "react-router-dom": "^6.15.0", + "semver": "^7.5.4", "swc-loader": "^0.2.3", "swr": "^2.2.4", - "typescript": "^4.7.3", - "webpack": "^5.73.0", + "turbo": "^1.10.13", + "typescript": "^4.9.5", + "webpack": "^5.88.2", "webpack-cli": "^5.1.4" }, "dependencies": { diff --git a/src/add-group-modal/styles.scss b/src/add-group-modal/styles.scss index 900c3c9..0557f11 100644 --- a/src/add-group-modal/styles.scss +++ b/src/add-group-modal/styles.scss @@ -1,5 +1,5 @@ -@use '@carbon/styles/scss/spacing'; @use '@carbon/colors'; +@use '@carbon/layout'; .modal { :global(.cds--modal) { @@ -36,7 +36,7 @@ .patientName { flex-grow: 1; - padding-left: spacing.$spacing-05; + padding-left: layout.$spacing-05; } .loading { @@ -45,5 +45,5 @@ flex-direction: column; justify-content: center; align-items: center; - row-gap: spacing.$spacing-05; + row-gap: layout.$spacing-05; } diff --git a/src/context/GroupFormWorkflowContext.tsx b/src/context/GroupFormWorkflowContext.tsx index c3ca1ae..9c4c5d3 100644 --- a/src/context/GroupFormWorkflowContext.tsx +++ b/src/context/GroupFormWorkflowContext.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useMemo, useReducer } from "react"; import reducer from "./GroupFormWorkflowReducer"; import { useParams } from "react-router-dom"; -import { Type, useSession } from "@openmrs/esm-framework"; +import { type Type, useSession } from "@openmrs/esm-framework"; import useGetSystemSetting from "../hooks/useGetSystemSetting"; interface ParamTypes { formUuid?: string; diff --git a/src/declarations.d.ts b/src/declarations.d.ts index bea7fc2..21b9066 100644 --- a/src/declarations.d.ts +++ b/src/declarations.d.ts @@ -1,4 +1,4 @@ +declare module "@carbon/react"; declare module "*.css"; declare module "*.scss"; -declare module "@carbon/react"; declare type SideNavProps = object; diff --git a/src/empty-state/styles.scss b/src/empty-state/styles.scss index aeec75a..9c5d549 100644 --- a/src/empty-state/styles.scss +++ b/src/empty-state/styles.scss @@ -1,36 +1,36 @@ -@import "~@openmrs/esm-styleguide/src/vars"; -@import "~carbon-components/src/globals/scss/vars"; -@import "~carbon-components/src/globals/scss/mixins"; +@use '@carbon/colors'; +@use '@carbon/layout'; +@use '@carbon/type'; .action { - margin-bottom: $spacing-03; + margin-bottom: layout.$spacing-03; } .content { - @include carbon--type-style("productive-heading-01"); - color: $text-02; - margin-top: $spacing-05; - margin-bottom: $spacing-03; + @include type.type-style("heading-compact-01"); + color: colors.$gray-70; + margin-top: layout.$spacing-05; + margin-bottom: layout.$spacing-03; } .desktopHeading { h4 { - @include carbon--type-style('productive-heading-02'); - color: $text-02; + @include type.type-style('heading-compact-02'); + color: colors.$gray-70; } } .tabletHeading { h4 { - @include carbon--type-style('productive-heading-03'); - color: $text-02; + @include type.type-style('heading-03'); + color: colors.$gray-70; } } .desktopHeading, .tabletHeading { text-align: left; text-transform: capitalize; - margin-bottom: $spacing-05; + margin-bottom: layout.$spacing-05; h4:after { content: ""; @@ -51,5 +51,5 @@ .tile { text-align: center; - border: 1px solid $ui-03; + border: 1px solid colors.$gray-20; } diff --git a/src/form-entry-workflow/form-review-card/styles.scss b/src/form-entry-workflow/form-review-card/styles.scss index 07569a1..44efddc 100644 --- a/src/form-entry-workflow/form-review-card/styles.scss +++ b/src/form-entry-workflow/form-review-card/styles.scss @@ -1,12 +1,10 @@ -@use '@carbon/styles/scss/spacing'; -// @use '@carbon/colors'; -@use '@carbon/styles/scss/type'; -@import '~@openmrs/esm-styleguide/src/vars'; - +@use '@carbon/colors'; +@use '@carbon/layout'; +@use '@carbon/type'; .formReviewCard { - background-color: $ui-02; - padding: spacing.$spacing-02; + background-color: colors.$white-0; + padding: layout.$spacing-02; } .formReviewCard :global(.cds--accordion) :global(.cds--accordion__item) { @@ -16,17 +14,17 @@ .formReviewCard :global(.cds--accordion__title) { display: flex; align-items: baseline; - column-gap: spacing.$spacing-05; + column-gap: layout.$spacing-05; } .formReviewCard :global(.cds--accordion__content) { - padding: spacing.$spacing-03; + padding: layout.$spacing-03; } .dataField { @include type.type-style('code-02'); - background-color: $ui-01; - padding: spacing.$spacing-03; + background-color: colors.$gray-10; + padding: layout.$spacing-03; } .displayName { diff --git a/src/form-entry-workflow/patient-banner/styles.scss b/src/form-entry-workflow/patient-banner/styles.scss index 683bd03..5ed544d 100644 --- a/src/form-entry-workflow/patient-banner/styles.scss +++ b/src/form-entry-workflow/patient-banner/styles.scss @@ -1,16 +1,15 @@ -@use '@carbon/styles/scss/spacing'; -// @use '@carbon/colors'; -@use '@carbon/styles/scss/type'; -@import '~@openmrs/esm-styleguide/src/vars'; +@use '@carbon/colors'; +@use '@carbon/layout'; +@use '@carbon/type'; .container { - height: spacing.$spacing-11; + height: layout.$spacing-11; display: flex; align-items: center; - background-color: $ui-02; - border-top: 0.0125rem solid $ui-03; - border-bottom: 0.0125rem solid $ui-03; - padding: 0 spacing.$spacing-05; + background-color: colors.$white-0; + border-top: 0.0125rem solid colors.$gray-20; + border-bottom: 0.0125rem solid colors.$gray-20; + padding: 0 layout.$spacing-05; } .photoPlaceholder { @@ -35,11 +34,11 @@ min-height: 2rem; } @include type.type-style('body-compact-02'); - color: $text-02; + color: colors.$gray-70; column-gap: 0.8rem; } .patientEditBtn { - color: $ui-05; - margin: spacing.$spacing-03; + color: colors.$gray-100; + margin: layout.$spacing-03; } diff --git a/src/form-entry-workflow/patient-search-header/styles.scss b/src/form-entry-workflow/patient-search-header/styles.scss index 9df1ba3..98d4176 100644 --- a/src/form-entry-workflow/patient-search-header/styles.scss +++ b/src/form-entry-workflow/patient-search-header/styles.scss @@ -1,16 +1,15 @@ -@use '@carbon/styles/scss/spacing'; -// @use '@carbon/colors'; -// @use '@carbon/styles/scss/type'; -@import '~@openmrs/esm-styleguide/src/vars'; +@use '@carbon/colors'; +@use '@carbon/layout'; +@use '@carbon/type'; .searchHeaderContainer { - height: spacing.$spacing-11; + height: layout.$spacing-11; display: flex; align-items: center; - background-color: $ui-02; - border-top: 0.0125rem solid $ui-03; - border-bottom: 0.0125rem solid $ui-03; - padding: 0 spacing.$spacing-05; + background-color: colors.$white-0; + border-top: 0.0125rem solid colors.$gray-20; + border-bottom: 0.0125rem solid colors.$gray-20; + padding: 0 layout.$spacing-05; } .searchBarWrapper { @@ -22,5 +21,5 @@ } .padded { - padding: spacing.$spacing-05; + padding: layout.$spacing-05; } diff --git a/src/form-entry-workflow/styles.scss b/src/form-entry-workflow/styles.scss index 504957d..40a6387 100644 --- a/src/form-entry-workflow/styles.scss +++ b/src/form-entry-workflow/styles.scss @@ -1,13 +1,11 @@ -@use '@carbon/styles/scss/spacing'; @use '@carbon/colors'; -@use '@carbon/styles/scss/type'; -@import '~@openmrs/esm-styleguide/src/vars'; - +@use '@carbon/layout'; +@use '@carbon/type'; .breadcrumbsContainer > div > div > nav { - background-color: $ui-02; - padding: spacing.$spacing-04 spacing.$spacing-05; - height: spacing.$spacing-08; + background-color: colors.$white-0; + padding: layout.$spacing-04 layout.$spacing-05; + height: layout.$spacing-08; } .workspaceWrapper { @@ -20,16 +18,16 @@ } .selectPatientMessage { - @include type.type-style('productive-heading-03'); - margin: spacing.$spacing-07; + @include type.type-style('heading-03'); + margin: layout.$spacing-07; text-align: center; } .formMainContent { display: flex; text-align: center; - margin-top: spacing.$spacing-05; - column-gap: spacing.$spacing-05; + margin-top: layout.$spacing-05; + column-gap: layout.$spacing-05; } .formContainer { @@ -50,14 +48,14 @@ } .patientCardsSection { - margin: spacing.$spacing-05 0; + margin: layout.$spacing-05 0; border-bottom: 1px solid colors.$gray-10; } .rightPanelActionButtons { display: flex; flex-direction: column; - row-gap: spacing.$spacing-03; + row-gap: layout.$spacing-03; & button { width: 100%; text-decoration: "none"; diff --git a/src/form-entry-workflow/workflow-review/styles.scss b/src/form-entry-workflow/workflow-review/styles.scss index 9e2407c..73242ad 100644 --- a/src/form-entry-workflow/workflow-review/styles.scss +++ b/src/form-entry-workflow/workflow-review/styles.scss @@ -1,7 +1,3 @@ -@import "~@openmrs/esm-styleguide/src/vars"; -@import "~carbon-components/src/globals/scss/vars"; -@import "~carbon-components/src/globals/scss/mixins"; - .workspaceWrapper { display: flex; justify-content: center; diff --git a/src/forms-page/FormsPage.tsx b/src/forms-page/FormsPage.tsx index 762fb0b..cb04d5e 100644 --- a/src/forms-page/FormsPage.tsx +++ b/src/forms-page/FormsPage.tsx @@ -1,7 +1,7 @@ import { useConfig, useSession } from "@openmrs/esm-framework"; import { Tab, Tabs, TabList, TabPanels, TabPanel } from "@carbon/react"; import React from "react"; -import { Config } from "../config-schema"; +import { type Config } from "../config-schema"; import { useGetAllForms } from "../hooks"; import FormsTable from "./forms-table"; import styles from "./styles.scss"; @@ -44,7 +44,7 @@ const prepareRowsForTable = (rawFormData) => { }; const FormsPage = () => { - const config = useConfig() as Config; + const config = useConfig(); const { t } = useTranslation(); const { formCategories, formCategoriesToShow } = config; const { forms, isLoading, error } = useGetAllForms(); diff --git a/src/forms-page/forms-table/styles.scss b/src/forms-page/forms-table/styles.scss index 09cf51f..a6fc206 100644 --- a/src/forms-page/forms-table/styles.scss +++ b/src/forms-page/forms-table/styles.scss @@ -1,11 +1,10 @@ -@import "~@openmrs/esm-styleguide/src/vars"; -@import "~carbon-components/src/globals/scss/vars"; -@import "~carbon-components/src/globals/scss/mixins"; +@use '@carbon/colors'; +@use '@carbon/layout'; .toolbarWrapper { position: relative; display: flex; - height: $spacing-09; + height: layout.$spacing-09; justify-content: flex-end; } @@ -15,6 +14,6 @@ } .inactiveLink { - color: $carbon--gray-40; + color: colors.$gray-40; cursor: not-allowed; } diff --git a/src/forms-page/styles.scss b/src/forms-page/styles.scss index 96b5e3e..566d034 100644 --- a/src/forms-page/styles.scss +++ b/src/forms-page/styles.scss @@ -1,11 +1,9 @@ -@use '@carbon/styles/scss/spacing'; -// @use '@carbon/styles/scss/type'; -// @import '~@openmrs/esm-styleguide/src/vars'; +@use '@carbon/layout'; .mainContent { - padding: spacing.$spacing-07; + padding: layout.$spacing-07; } .pageTitle { - margin-bottom: spacing.$spacing-06; + margin-bottom: layout.$spacing-06; } diff --git a/src/group-form-entry-workflow/GroupSessionWorkspace.tsx b/src/group-form-entry-workflow/GroupSessionWorkspace.tsx index e4f4793..30b743f 100644 --- a/src/group-form-entry-workflow/GroupSessionWorkspace.tsx +++ b/src/group-form-entry-workflow/GroupSessionWorkspace.tsx @@ -57,7 +57,7 @@ const WorkflowNavigationButtons = () => { > {isLastPatient ? t("saveForm", "Save Form") - : t("nextPatient", "Next Patient")} + : t("nextPatient", "Next patient")}