diff --git a/.eslintignore b/.eslintignore index fa95f169b..b0f3418e2 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1,2 @@ lint-staged.config.js +jest.config.js diff --git a/.eslintrc b/.eslintrc index f930c1b4d..25f0ddaa5 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,15 +1,10 @@ { "plugins": ["jest"], "extends": [ + "@doist/eslint-config/recommended-requiring-type-checking", + "@doist/eslint-config/react", "react-app", - "prettier/@typescript-eslint", - "plugin:prettier/recommended", - "plugin:jest/recommended", - "prettier", - "plugin:@typescript-eslint/eslint-recommended", - "plugin:@typescript-eslint/recommended", - "plugin:@typescript-eslint/recommended-requiring-type-checking", - "prettier/@typescript-eslint" + "plugin:jest/recommended" ], "parser": "@typescript-eslint/parser", "parserOptions": { @@ -30,9 +25,8 @@ "webpack.config.*" ], "rules": { - "prettier/prettier": "error", - "react/no-did-mount-set-state": "error", - "react/no-did-update-set-state": "error", + "func-style": "off", + "import/no-default-export": "off", // Legacy API. "react/no-find-dom-node": "off", "@typescript-eslint/explicit-module-boundary-types": "off", "@typescript-eslint/ban-ts-comment": "off" @@ -43,6 +37,7 @@ // jest mocks are hard to type, allow incomplete types in tests. "files": ["stories/**/*", "*.test.*"], "rules": { + "react/no-unescaped-entities": "off", "@typescript-eslint/no-unsafe-member-access": "off", "@typescript-eslint/no-unsafe-call": "off", "@typescript-eslint/no-unsafe-assignment": "off", diff --git a/package-lock.json b/package-lock.json index 3566c5cad..e47b182d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3830,6 +3830,26 @@ "minimist": "^1.2.0" } }, + "@doist/eslint-config": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@doist/eslint-config/-/eslint-config-3.0.0.tgz", + "integrity": "sha512-INf7T5PSIru8WOEIIRRr9ZebW9lWAtX2urp9hs4VW5+8c7QtMAC7SuiIb3REO6EtEEoV43m9WnEAtS8jVZlH3w==", + "dev": true, + "requires": { + "eslint-config-prettier": "^6.15.0" + }, + "dependencies": { + "eslint-config-prettier": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.15.0.tgz", + "integrity": "sha512-a1+kOYLR8wMGustcgAjdydMsQ2A/2ipRPwRKUmfYaSxc9ZPcrku080Ctl6zrZzZNs/U82MjSv+qKREkoq3bJaw==", + "dev": true, + "requires": { + "get-stdin": "^6.0.0" + } + } + } + }, "@doist/prettier-config": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@doist/prettier-config/-/prettier-config-3.0.5.tgz", diff --git a/package.json b/package.json index a1e6b7545..dfb52962c 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "@babel/preset-react": "^7.0.0", "@babel/preset-typescript": "^7.10.1", "@babel/register": "^7.0.0", + "@doist/eslint-config": "^3.0.0", "@doist/prettier-config": "^3.0.5", "@storybook/addon-actions": "^5.3.18", "@storybook/addon-docs": "^5.3.18", diff --git a/src/components/avatar/utils.ts b/src/components/avatar/utils.ts index 56fef547c..68ca5b3b5 100644 --- a/src/components/avatar/utils.ts +++ b/src/components/avatar/utils.ts @@ -8,6 +8,8 @@ function getInitials(name?: string) { const lastInitial = seed[seed.length - 1] let initials = firstInitial[0] + // Better readable this way. + // eslint-disable-next-line @typescript-eslint/prefer-string-starts-ends-with if (firstInitial[0] !== lastInitial[0]) { initials += lastInitial[0] } diff --git a/src/components/button/button.tsx b/src/components/button/button.tsx index f0d4336bb..2b8eb8024 100644 --- a/src/components/button/button.tsx +++ b/src/components/button/button.tsx @@ -1,5 +1,4 @@ import React from 'react' -import PropTypes from 'prop-types' import classNames from 'classnames' import { Tooltip } from '../tooltip' @@ -77,13 +76,6 @@ const Button = React.forwardRef(function Button( Button.displayName = 'Button' -Button.propTypes = { - loading: PropTypes.bool, - variant: PropTypes.oneOf(['primary', 'secondary', 'danger', 'link']), - size: PropTypes.oneOf(['default', 'small', 'large']), - tooltip: PropTypes.node, -} - Button.defaultProps = { size: 'default', loading: false, diff --git a/src/components/modal/modal.test.tsx b/src/components/modal/modal.test.tsx index 226571caa..43a328d86 100644 --- a/src/components/modal/modal.test.tsx +++ b/src/components/modal/modal.test.tsx @@ -3,7 +3,7 @@ import ReactDOM from 'react-dom' import { shallow, mount } from 'enzyme' import toJson from 'enzyme-to-json' -import * as Modal from './modal' +import { default as Modal } from '../modal' import type { Modal as ModalType } from '../modal' import Button from '../button' // for more descriptive snapshots diff --git a/src/components/time/time-utils.ts b/src/components/time/time-utils.ts index cdc06f89b..4cee895fc 100644 --- a/src/components/time/time-utils.ts +++ b/src/components/time/time-utils.ts @@ -1,4 +1,15 @@ import dayjs from 'dayjs' +/** + * There's a problem with our setup where the default export from + * localizedFormat (and likely every other dayjs plugin) isn't properly + * recognized. The proposed workarounds (importing with `.js` ending, or adding + * `allowSyntheticDefaultImports` to the tsconfig) either broke linting or type + * checking. After spending some time on this it was decided that further + * investigations are not worth it, the code works and the eslint ignore is fine. + * ref: https://github.com/iamkun/dayjs/issues/593 + * ref: https://day.js.org/docs/en/installation/typescript + */ +// eslint-disable-next-line import/default import LocalizedFormat from 'dayjs/plugin/localizedFormat' dayjs.extend(LocalizedFormat) diff --git a/stories/components/ButtonStory.tsx b/stories/components/ButtonStory.tsx index d2326128f..b79a344da 100644 --- a/stories/components/ButtonStory.tsx +++ b/stories/components/ButtonStory.tsx @@ -26,7 +26,7 @@ function StandardButtonsStory() {

You can and it works as - you'd expect. + you'd expect.

) diff --git a/stories/components/MenuStory.tsx b/stories/components/MenuStory.tsx index 38be2db2b..8596223a2 100644 --- a/stories/components/MenuStory.tsx +++ b/stories/components/MenuStory.tsx @@ -109,6 +109,7 @@ function SimpleMenuExample() { const SimpleMenuChapter = { subtitle: 'Some menu examples', + // eslint-disable-next-line react/display-name sections: [{ sectionFn: () => , options: optionsSourceOnly }], } @@ -154,8 +155,8 @@ function OverflowMenuExample() { be used as a context menu).

@@ -164,6 +165,7 @@ function OverflowMenuExample() { const OverflowMenuChapter = { subtitle: 'A list of items with an overflow options menu', + // eslint-disable-next-line react/display-name sections: [{ sectionFn: () => , options: optionsSourceOnly }], } diff --git a/stories/components/ModalStory.tsx b/stories/components/ModalStory.tsx index 28594e828..0b12908f6 100644 --- a/stories/components/ModalStory.tsx +++ b/stories/components/ModalStory.tsx @@ -5,8 +5,9 @@ import { withKnobs, text, boolean } from '@storybook/addon-knobs' import { optionsNoSourceNoProps } from '../utils/StoryUtils' -import Modal from '../../src/components/modal' +import { default as Modal } from '../../src/components/modal' import Button from '../../src/components/button' + import { howToText, modalBoxText,