diff --git a/.eslintrc b/.eslintrc index 3fe8854021c..6365c7c5572 100644 --- a/.eslintrc +++ b/.eslintrc @@ -55,14 +55,36 @@ "jsx-a11y/mouse-events-have-key-events": "off", "jsx-a11y/click-events-have-key-events": "off", "jsx-a11y/no-noninteractive-element-interactions": "off", - "jsx-a11y/no-noninteractive-element-to-interactive-role": "off" + "jsx-a11y/no-noninteractive-element-to-interactive-role": "off", + "@typescript-eslint/camelcase": [ + "error", + { + "allow": [ + "UNSTABLE_Color", + "UNSTABLE_colors", + "UNSTABLE_cssCustomProperties", + "UNSTABLE_telemetry" + ] + } + ] }, "overrides": [ + { + "files": ["src/**/*.{ts,tsx}"], + "extends": ["plugin:shopify/typescript-type-checking"], + "parserOptions": { + "project": "./tsconfig.json" + }, + "rules": { + "@typescript-eslint/prefer-readonly": "off", + "@typescript-eslint/no-unnecessary-condition": "off", + "@typescript-eslint/unbound-method": "off" + } + }, { "files": ["**/*.test.{ts,tsx}"], "rules": { "jest/no-truthy-falsy": "off", - "shopify/jsx-no-hardcoded-content": "off", "shopify/no-ancestor-directory-import": "off" } }, diff --git a/.vscode/settings.json b/.vscode/settings.json index 47d18b1b49f..7cf87e457d7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,12 +1,14 @@ // Place your settings in this file to overwrite default and user settings. { "css.validate": false, + "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true, + "eslint.autoFixOnSave": true, "eslint.validate": [ - "javascript", - "javascriptreact", - "typescript", - "typescriptreact" + {"language": "javascript", "autoFix": true}, + {"language": "javascriptreact", "autoFix": true}, + {"language": "typescript", "autoFix": true}, + {"language": "typescriptreact", "autoFix": true} ], "files.exclude": { "**/.DS_Store": true, @@ -27,7 +29,6 @@ }, "javascript.validate.enable": false, "jest.autoEnable": false, - "prettier.eslintIntegration": true, "prettier.stylelintIntegration": true, "scss.validate": false, "search.exclude": { diff --git a/UNRELEASED.md b/UNRELEASED.md index b52231a8d88..2f5729cff51 100644 --- a/UNRELEASED.md +++ b/UNRELEASED.md @@ -17,6 +17,7 @@ ### Development workflow - Enabled maintainers running `yarn dev` to hide [`yarn splash`](https://github.com/Shopify/polaris-react/tree/master/scripts/splash) reports from the console by running `DISABLE_SPLASH=1 yarn dev` ([#2372](https://github.com/Shopify/polaris-react/pull/2372)) +- Updated to sewing-kit 0.112.0 and eslint 6 and updated vscode config to use the eslint plugin to format js/ts files ((#2369)[https://github.com/Shopify/polaris-react/pull/2369]) ### Dependency upgrades diff --git a/package.json b/package.json index c29db52ce4d..f74e951d74f 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,7 @@ "@percy/storybook": "^3.2.0", "@shopify/jest-dom-mocks": "^2.1.1", "@shopify/react-testing": "^1.7.8", - "@shopify/sewing-kit": "^0.111.0", + "@shopify/sewing-kit": "^0.112.0", "@storybook/addon-a11y": "^5.2.4", "@storybook/addon-actions": "^5.2.4", "@storybook/addon-console": "^1.2.1", @@ -162,9 +162,6 @@ "react": "^16.8.6", "react-dom": "^16.8.6" }, - "resolutions": { - "typescript-eslint-parser": "npm:@typescript-eslint/parser@1.10.2" - }, "files": [ "esnext", "styles", diff --git a/scripts/pa11y.js b/scripts/pa11y.js index 1663e14f580..b3df21aef4c 100644 --- a/scripts/pa11y.js +++ b/scripts/pa11y.js @@ -39,6 +39,7 @@ async function runPa11y() { ]; await browsers.forEach(async (instance) => { + // eslint-disable-next-line require-atomic-updates instance.page = await instance.browser.newPage(); }); diff --git a/scripts/splash/index.tsx b/scripts/splash/index.tsx index 82caaaf3fd2..4cfc5435a9e 100644 --- a/scripts/splash/index.tsx +++ b/scripts/splash/index.tsx @@ -81,7 +81,7 @@ const Components = ({components, status}) => ( {status === 'loading' && ( - ⏳{' '}Please wait during compilation… Beep boop beep 🤖 + ⏳ Please wait during compilation… Beep boop beep 🤖 )} @@ -202,8 +202,8 @@ const App = () => { 💡 - Tip: to disable these reports, run{' '} - DISABLE_SPLASH=1 yarn dev + Tip: to disable these reports, run + DISABLE_SPLASH=1 yarn dev diff --git a/scripts/splash/treebuilder.ts b/scripts/splash/treebuilder.ts index b2c87a3147e..3e81f0a3a93 100644 --- a/scripts/splash/treebuilder.ts +++ b/scripts/splash/treebuilder.ts @@ -3,15 +3,15 @@ import * as ts from 'typescript'; import glob from 'glob'; import cmd from 'node-cmd'; -type Node = { +interface Node { fileName: string; dependsOn: Node[]; dependedOnBy: Node[]; -}; +} -type GraphType = { +interface GraphType { [name: string]: Node; -}; +} const graph: GraphType = {}; diff --git a/src/components/AccountConnection/tests/AccountConnection.test.tsx b/src/components/AccountConnection/tests/AccountConnection.test.tsx index 1533f052508..9d1b0cc9648 100644 --- a/src/components/AccountConnection/tests/AccountConnection.test.tsx +++ b/src/components/AccountConnection/tests/AccountConnection.test.tsx @@ -28,7 +28,7 @@ describe('', () => { it('is shown on the card when provided', () => { const TermsOfService = () => (

- By clicking Connect, you agree to accept Sample App’s{' '} + By clicking Connect, you agree to accept Sample App’s terms and conditions. You’ll pay a commission rate of 15% on sales made through Sample App.

diff --git a/src/components/AppProvider/AppProvider.tsx b/src/components/AppProvider/AppProvider.tsx index c0a5e655797..c5332f4d822 100644 --- a/src/components/AppProvider/AppProvider.tsx +++ b/src/components/AppProvider/AppProvider.tsx @@ -42,7 +42,6 @@ export interface AppProviderProps extends AppBridgeOptions { features?: Features; /** Inner content of the application */ children?: React.ReactNode; - // eslint-disable-next-line babel/camelcase UNSTABLE_telemetry?: TelemetryObject; } @@ -100,7 +99,6 @@ export class AppProvider extends React.Component { }); } - /* eslint-disable babel/camelcase */ render() { const { theme = {}, @@ -132,5 +130,4 @@ export class AppProvider extends React.Component { ); } - /* eslint-enable babel/camelcase */ } diff --git a/src/components/AppProvider/tests/AppProvider.test.tsx b/src/components/AppProvider/tests/AppProvider.test.tsx index 1d43834a0bb..e923bad713c 100644 --- a/src/components/AppProvider/tests/AppProvider.test.tsx +++ b/src/components/AppProvider/tests/AppProvider.test.tsx @@ -16,7 +16,7 @@ describe('', () => { }); it('updates context when props change', () => { - const Child: React.SFC<{}> = () => { + const Child: React.SFC = () => { return useContext(LinkContext) ?
: null; }; const LinkComponent = () =>
; diff --git a/src/components/Autocomplete/Autocomplete.tsx b/src/components/Autocomplete/Autocomplete.tsx index 8c94bda15c2..1f16918ab60 100644 --- a/src/components/Autocomplete/Autocomplete.tsx +++ b/src/components/Autocomplete/Autocomplete.tsx @@ -17,7 +17,7 @@ export interface AutocompleteProps { /** The selected options */ selected: string[]; /** The text field component attached to the list of options */ - textField: React.ReactElement; + textField: React.ReactElement; /** The preferred direction to open the popover */ preferredPosition?: PreferredPosition; /** Title of the list of options */ diff --git a/src/components/Autocomplete/components/ComboBox/ComboBox.tsx b/src/components/Autocomplete/components/ComboBox/ComboBox.tsx index a51e375f8bd..f784e23bf88 100644 --- a/src/components/Autocomplete/components/ComboBox/ComboBox.tsx +++ b/src/components/Autocomplete/components/ComboBox/ComboBox.tsx @@ -31,7 +31,7 @@ export interface ComboBoxProps { /** The selected options */ selected: string[]; /** The text field component attached to the list of options */ - textField: React.ReactElement; + textField: React.ReactElement; /** The preferred direction to open the popover */ preferredPosition?: PreferredPosition; /** Title of the list of options */ diff --git a/src/components/Banner/tests/Banner.test.tsx b/src/components/Banner/tests/Banner.test.tsx index 1d493d555e1..74e5b5fd968 100644 --- a/src/components/Banner/tests/Banner.test.tsx +++ b/src/components/Banner/tests/Banner.test.tsx @@ -152,7 +152,7 @@ describe('', () => { describe('context', () => { it('passes the within banner context', () => { - const Child: React.SFC<{}> = (_props) => { + const Child: React.SFC = (_props) => { return ( {(BannerContext) => { diff --git a/src/components/Breadcrumbs/Breadcrumbs.tsx b/src/components/Breadcrumbs/Breadcrumbs.tsx index a9875abcfd4..b5bf270e7c6 100644 --- a/src/components/Breadcrumbs/Breadcrumbs.tsx +++ b/src/components/Breadcrumbs/Breadcrumbs.tsx @@ -10,7 +10,7 @@ import styles from './Breadcrumbs.scss'; export interface BreadcrumbsProps { /** Collection of breadcrumbs */ - breadcrumbs: Array; + breadcrumbs: (CallbackAction | LinkAction)[]; } export class Breadcrumbs extends React.PureComponent { diff --git a/src/components/Choice/Choice.tsx b/src/components/Choice/Choice.tsx index 0219216060a..5b7e5c4b541 100644 --- a/src/components/Choice/Choice.tsx +++ b/src/components/Choice/Choice.tsx @@ -12,7 +12,7 @@ export interface ChoiceProps { /** Label for the choice */ label: React.ReactNode; /** Whether the associated form control is disabled */ - disabled?: Boolean; + disabled?: boolean; /** Display an error message */ error?: Error | boolean; /** Visually hide the label */ diff --git a/src/components/Choice/tests/Choice.test.tsx b/src/components/Choice/tests/Choice.test.tsx index acc74861b9b..4d7e3a7baa3 100644 --- a/src/components/Choice/tests/Choice.test.tsx +++ b/src/components/Choice/tests/Choice.test.tsx @@ -82,8 +82,8 @@ describe('', () => { , ); const label = element.find('label'); - for (let i = 0; i < blockLevelElements.length; i++) { - expect(label.find(blockLevelElements[i])).toHaveLength(0); + for (const blockLevelElement of blockLevelElements) { + expect(label.find(blockLevelElement)).toHaveLength(0); } }); }); diff --git a/src/components/ChoiceList/ChoiceList.tsx b/src/components/ChoiceList/ChoiceList.tsx index 4a70e483ba2..d45a77170ad 100644 --- a/src/components/ChoiceList/ChoiceList.tsx +++ b/src/components/ChoiceList/ChoiceList.tsx @@ -136,7 +136,7 @@ export function ChoiceList({ function noop() {} function choiceIsSelected({value}: Choice, selected: string[]) { - return selected.indexOf(value) >= 0; + return selected.includes(value); } function updateSelectedChoices( diff --git a/src/components/Connected/tests/Connected.test.tsx b/src/components/Connected/tests/Connected.test.tsx index f567a00d724..0d53c6593e9 100644 --- a/src/components/Connected/tests/Connected.test.tsx +++ b/src/components/Connected/tests/Connected.test.tsx @@ -6,20 +6,18 @@ import {Item, ItemPosition} from '../components'; describe('', () => { describe('', () => { - it('wraps children in an Item component', async () => { + it('wraps children in an Item component', () => { const expectedContent = 'foo'; - const connected = await mountWithApp( - {expectedContent}, - ); + const connected = mountWithApp({expectedContent}); expect( connected.find(Item, {position: ItemPosition.Primary}), ).toContainReactText(expectedContent); }); - it('includes `rightConnected` markup in an Item component', async () => { + it('includes `rightConnected` markup in an Item component', () => { const rightConnectedContent = 'foo'; - const connected = await mountWithApp( + const connected = mountWithApp( , ); @@ -28,21 +26,19 @@ describe('', () => { ).toContainReactText(rightConnectedContent); }); - it('includes `leftConnected` markup in an Item component', async () => { + it('includes `leftConnected` markup in an Item component', () => { const leftConnectedContent = 'foo'; - const connected = await mountWithApp( - , - ); + const connected = mountWithApp(); expect( connected.find(Item, {position: ItemPosition.Left}), ).toContainReactText(leftConnectedContent); }); - it('`leftConnected` and `rightConnected` are not mutually exclusive', async () => { + it('`leftConnected` and `rightConnected` are not mutually exclusive', () => { const rightConnectedContent = 'rightfoo'; const leftConnectedContent = 'leftfoo'; - const connected = await mountWithApp( + const connected = mountWithApp( , ); diff --git a/src/components/ContextualSaveBar/ContextualSaveBar.tsx b/src/components/ContextualSaveBar/ContextualSaveBar.tsx index 5f7608255b8..159ce208f24 100644 --- a/src/components/ContextualSaveBar/ContextualSaveBar.tsx +++ b/src/components/ContextualSaveBar/ContextualSaveBar.tsx @@ -7,9 +7,6 @@ import {ContextualSaveBarProps, useFrame} from '../../utilities/frame'; // crashing if we write `ContextualSaveBar extends React.Component` export interface ContextualSaveBarProps extends ContextualSaveBarProps {} -// This does have a display name, but the linting has a bug in it -// https://github.com/yannickcr/eslint-plugin-react/issues/2324 -// eslint-disable-next-line react/display-name export const ContextualSaveBar = React.memo(function ContextualSaveBar({ message, saveAction, diff --git a/src/components/DatePicker/components/Month/Month.tsx b/src/components/DatePicker/components/Month/Month.tsx index bc405e871ce..8e1b8315c66 100644 --- a/src/components/DatePicker/components/Month/Month.tsx +++ b/src/components/DatePicker/components/Month/Month.tsx @@ -27,7 +27,7 @@ export interface MonthProps { year: Year; disableDatesBefore?: Date; disableDatesAfter?: Date; - allowRange?: Boolean; + allowRange?: boolean; weekStartsOn: Weekdays; onChange?(date: Range): void; onHover?(hoverEnd: Date): void; @@ -82,7 +82,7 @@ export function Month({ )); function handleDateClick(selectedDate: Date) { - onChange(getNewRange(allowRange && selected, selectedDate)); + onChange(getNewRange(allowRange ? selected : undefined, selectedDate)); } function renderWeek(day: Date, dayIndex: number) { diff --git a/src/components/DropZone/DropZone.tsx b/src/components/DropZone/DropZone.tsx index 873b34fb852..3afb575620d 100755 --- a/src/components/DropZone/DropZone.tsx +++ b/src/components/DropZone/DropZone.tsx @@ -499,7 +499,7 @@ class DropZone extends React.Component { const fileList = getDataTransferFiles(event); - if (event.target && this.dragTargets.indexOf(event.target) === -1) { + if (event.target && !this.dragTargets.includes(event.target)) { this.dragTargets.push(event.target); } diff --git a/src/components/DropZone/tests/DropZone.test.tsx b/src/components/DropZone/tests/DropZone.test.tsx index 30f3b977674..1a84a584e48 100755 --- a/src/components/DropZone/tests/DropZone.test.tsx +++ b/src/components/DropZone/tests/DropZone.test.tsx @@ -482,7 +482,7 @@ function fireEvent({ element: ReactWrapper; eventType?: string; spy?: jest.Mock; - testFiles?: Array; + testFiles?: object[]; }) { if (spy) { spy.mockReset(); diff --git a/src/components/DropZone/utils/index.ts b/src/components/DropZone/utils/index.ts index 779cab7b6b7..79f9bb99ece 100755 --- a/src/components/DropZone/utils/index.ts +++ b/src/components/DropZone/utils/index.ts @@ -36,9 +36,9 @@ function accepts(file: File, acceptedFiles: string | string[] | undefined) { return acceptedFilesArray.some((type) => { const validType = type.trim(); - if (validType.charAt(0) === '.') { + if (validType.startsWith('.')) { return fileName.toLowerCase().endsWith(validType.toLowerCase()); - } else if (/\/\*$/.test(validType)) { + } else if (validType.endsWith('/*')) { // This is something like a image/* mime type return baseMimeType === validType.replace(/\/.*$/, ''); } diff --git a/src/components/ExceptionList/ExceptionList.tsx b/src/components/ExceptionList/ExceptionList.tsx index e5890844585..63fdae66abc 100644 --- a/src/components/ExceptionList/ExceptionList.tsx +++ b/src/components/ExceptionList/ExceptionList.tsx @@ -9,8 +9,8 @@ import styles from './ExceptionList.scss'; export type Description = | string - | React.ReactElement - | (string | React.ReactElement)[]; + | React.ReactElement + | (string | React.ReactElement)[]; export interface Item { /** Set the color of the icon and title for the given item. */ diff --git a/src/components/Filters/components/ConnectedFilterControl/ConnectedFilterControl.tsx b/src/components/Filters/components/ConnectedFilterControl/ConnectedFilterControl.tsx index badba9afc82..6112da70865 100644 --- a/src/components/Filters/components/ConnectedFilterControl/ConnectedFilterControl.tsx +++ b/src/components/Filters/components/ConnectedFilterControl/ConnectedFilterControl.tsx @@ -185,9 +185,7 @@ export class ConnectedFilterControl extends React.Component< return actionsToReturn; } - private activatorButtonFrom( - action: PopoverableAction, - ): React.ReactElement { + private activatorButtonFrom(action: PopoverableAction): React.ReactElement { return (