Skip to content

Commit b245fa4

Browse files
authored
feat(eslint): add eslint in addition to tslint (#75)
* feat(eslint): add eslint in addition to tslint integrate eslint with pf recommended rules update deps adjust test to be more useful dark theme for sidenav * [cleanup] add support for linting js files be more specific about which rules are disabled update readme
1 parent 5404142 commit b245fa4

File tree

15 files changed

+1507
-657
lines changed

15 files changed

+1507
-657
lines changed

.eslintrc

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
// tells eslint to use the TypeScript parser
3+
"parser": "@typescript-eslint/parser",
4+
// tell the TypeScript parser that we want to use JSX syntax
5+
"parserOptions": {
6+
"tsx": true,
7+
"jsx": true,
8+
"js": true,
9+
"useJSXTextNode": true,
10+
"project": "./tsconfig.json",
11+
"tsconfigRootDir": "."
12+
},
13+
// we want to use the recommended rules provided from the typescript plugin
14+
"extends": [
15+
"plugin:@typescript-eslint/recommended",
16+
"plugin:patternfly-react/recommended"
17+
],
18+
// includes the typescript specific rules found here: https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin#supported-rules
19+
"plugins": ["@typescript-eslint", "react-hooks", "eslint-plugin-react-hooks"],
20+
"rules": {
21+
"@typescript-eslint/explicit-function-return-type": "off",
22+
"react-hooks/rules-of-hooks": "error",
23+
"react-hooks/exhaustive-deps": "warn",
24+
"@typescript-eslint/interface-name-prefix": "off",
25+
"prettier/prettier": "off",
26+
"import/no-unresolved": "off",
27+
"import/extensions": "off",
28+
"react/prop-types": "off"
29+
}
30+
}

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,7 @@ body {
8181
* To keep our bundle size in check, we use [webpack-bundle-analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer)
8282
* To keep our code formatting in check, we use [prettier](https://github.com/prettier/prettier)
8383
* To keep our code logic and test coverage in check, we use [jest](https://github.com/facebook/jest)
84+
* To ensure code styles remain consistent, we use [eslint](https://eslint.org/)
85+
86+
### Linter Supper
87+
Currently, eslint and tslint are both supported. This is temporary. We will be removing support for tslint at some point in the near future, given tslint's roadmap to deprecate itself in favor of a more unified developer experience across TypeScript and JavaScript languages.

package.json

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
"build": "webpack --config webpack.prod.js",
1212
"start": "webpack-dev-server --hot --color --progress --info=true --config webpack.dev.js",
1313
"test": "jest",
14-
"lint": "tslint -c ./tslint.json --project .",
14+
"tslint": "tslint -c ./tslint.json --project .",
15+
"eslint": "eslint --ext .tsx,.js ./src/",
16+
"lint": "yarn tslint && yarn eslint",
1517
"format": "prettier --check --write ./src/**/*.{tsx,ts,js}",
1618
"build:bundle-profile": "webpack --config webpack.prod.js --profile --json > stats.json",
1719
"bundle-profile:analyze": "yarn build:bundle-profile && webpack-bundle-analyzer ./stats.json",
@@ -20,25 +22,30 @@
2022
"build:storybook": "build-storybook"
2123
},
2224
"devDependencies": {
23-
"@storybook/addon-actions": "^5.2.1",
24-
"@storybook/addon-info": "^5.2.1",
25-
"@storybook/addon-links": "^5.2.1",
26-
"@storybook/addons": "^5.2.1",
27-
"@storybook/react": "^5.2.1",
25+
"@storybook/addon-actions": "^5.2.5",
26+
"@storybook/addon-info": "^5.2.5",
27+
"@storybook/addon-links": "^5.2.5",
28+
"@storybook/addons": "^5.2.5",
29+
"@storybook/react": "^5.2.5",
2830
"@types/enzyme": "^3.10.3",
2931
"@types/enzyme-adapter-react-16": "^1.0.5",
30-
"@types/jest": "^24.0.17",
31-
"@types/node": "^12.7.1",
32-
"@types/react": "^16.8.25",
33-
"@types/react-dom": "^16.8.5",
34-
"@types/react-router-dom": "^4.3.4",
32+
"@types/jest": "^24.0.21",
33+
"@types/node": "^12.12.5",
34+
"@types/react": "^16.9.11",
35+
"@types/react-dom": "^16.9.3",
36+
"@types/react-router-dom": "^5.1.2",
3537
"@types/storybook__react": "^4.0.2",
3638
"@types/victory": "^33.0.0",
37-
"@types/webpack": "^4.32.1",
39+
"@types/webpack": "^4.39.8",
40+
"@typescript-eslint/eslint-plugin": "^2.6.0",
41+
"@typescript-eslint/parser": "^2.6.0",
3842
"css-loader": "^3.2.0",
3943
"enzyme": "^3.7.0",
40-
"enzyme-adapter-react-16": "^1.7.0",
41-
"enzyme-to-json": "^3.4.0",
44+
"enzyme-adapter-react-16": "^1.15.1",
45+
"enzyme-to-json": "^3.4.3",
46+
"eslint": "^6.6.0",
47+
"eslint-plugin-patternfly-react": "^0.2.3",
48+
"eslint-plugin-react-hooks": "^2.2.0",
4249
"file-loader": "^4.2.0",
4350
"html-webpack-plugin": "^3.2.0",
4451
"imagemin": "^7.0.0",
@@ -48,34 +55,34 @@
4855
"prettier": "^1.15.2",
4956
"prop-types": "^15.6.1",
5057
"raw-loader": "^3.1.0",
51-
"react": "^16.8.4",
58+
"react": "^16.11.0",
5259
"react-axe": "^3.0.2",
5360
"react-docgen-typescript-loader": "^3.2.1",
54-
"react-dom": "^16.6.3",
61+
"react-dom": "^16.11.0",
5562
"regenerator-runtime": "^0.13.3",
5663
"rimraf": "^3.0.0",
5764
"style-loader": "^1.0.0",
5865
"svg-url-loader": "^3.0.0",
59-
"terser-webpack-plugin": "^2.1.0",
66+
"terser-webpack-plugin": "^2.2.1",
6067
"ts-jest": "^24.0.0",
61-
"ts-loader": "^6.0.2",
68+
"ts-loader": "^6.2.1",
6269
"tsconfig-paths-webpack-plugin": "^3.2.0",
6370
"tslint": "^5.13.1",
6471
"tslint-config-prettier": "^1.18.0",
6572
"tslint-eslint-rules": "^5.4.0",
6673
"tslint-react": "^4.0.0",
6774
"tslint-react-hooks": "^2.2.1",
68-
"typescript": "^3.5.3",
69-
"url-loader": "^2.1.0",
70-
"webpack": "^4.39.1",
71-
"webpack-bundle-analyzer": "^3.4.1",
72-
"webpack-cli": "^3.3.6",
73-
"webpack-dev-server": "^3.7.2",
75+
"typescript": "^3.6.4",
76+
"url-loader": "^2.2.0",
77+
"webpack": "^4.41.2",
78+
"webpack-bundle-analyzer": "^3.6.0",
79+
"webpack-cli": "^3.3.10",
80+
"webpack-dev-server": "^3.9.0",
7481
"webpack-merge": "^4.1.4"
7582
},
7683
"dependencies": {
7784
"@patternfly/react-core": "^3.77.2",
78-
"@patternfly/react-icons": "^3.10.15",
85+
"@patternfly/react-icons": "^3.14.18",
7986
"@patternfly/react-styles": "^3.5.7",
8087
"react-router-dom": "^5.0.1",
8188
"react-router-last-location": "^2.0.1"

src/app/AppLayout/AppLayout.tsx

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,27 +38,26 @@ const AppLayout: React.FunctionComponent<IAppLayout> = ({children}) => {
3838
logo="Patternfly"
3939
logoProps={logoProps}
4040
toolbar="Toolbar"
41-
showNavToggle={true}
41+
showNavToggle
4242
isNavOpen={isNavOpen}
4343
onNavToggle={isMobileView ? onNavToggleMobile : onNavToggle}
4444
/>
4545
);
4646

4747
const Navigation = (
48-
<Nav id="nav-primary-simple">
49-
<NavList id="nav-list-simple" variant={NavVariants.simple}>
50-
{routes.map((route, idx) => {
51-
return route.label && (
48+
<Nav id="nav-primary-simple" theme="dark">
49+
<NavList id="nav-list-simple" variant={NavVariants.default}>
50+
{routes.map((route, idx) => route.label && (
5251
<NavItem key={`${route.label}-${idx}`} id={`${route.label}-${idx}`}>
53-
<NavLink exact={true} to={route.path} activeClassName="pf-m-current">{route.label}</NavLink>
52+
<NavLink exact to={route.path} activeClassName="pf-m-current">{route.label}</NavLink>
5453
</NavItem>
55-
);
56-
})}
54+
))}
5755
</NavList>
5856
</Nav>
5957
);
6058
const Sidebar = (
6159
<PageSidebar
60+
theme="dark"
6261
nav={Navigation}
6362
isNavOpen={isMobileView ? isNavOpenMobile : isNavOpen} />
6463
);

src/app/Dashboard/Dashboard.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
import * as React from 'react';
22
import { PageSection, Title } from '@patternfly/react-core';
33

4-
const Dashboard: React.FunctionComponent<any> = (props) => {
5-
return (
4+
const Dashboard: React.FunctionComponent<{}> = () => (
65
<PageSection>
76
<Title size="lg">Dashboard Page Title</Title>
87
</PageSection>
9-
);
10-
}
8+
)
119

1210
export { Dashboard };

src/app/DynamicImport.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ import * as React from 'react';
22
import { accessibleRouteChangeHandler } from '@app/utils/utils';
33

44
interface IDynamicImport {
5+
/* eslint-disable @typescript-eslint/no-explicit-any */
56
load: () => Promise<any>;
67
children: any;
8+
/* eslint-enable @typescript-eslint/no-explicit-any */
79
focusContentAfterMount: boolean;
810
}
911

src/app/NotFound/NotFound.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,11 @@ import * as React from 'react';
22
import { NavLink } from 'react-router-dom';
33
import { Alert, PageSection } from '@patternfly/react-core';
44

5-
const NotFound: React.FunctionComponent = () => {
6-
return (
5+
const NotFound: React.FunctionComponent = () => (
76
<PageSection>
87
<Alert variant="danger" title="404! This view hasn't been created yet." /><br />
98
<NavLink to="/" className="pf-c-nav__link">Take me home</NavLink>
109
</PageSection>
11-
);
12-
}
10+
)
1311

1412
export { NotFound };

src/app/Support/Support.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,15 @@ export interface ISupportProps {
1515
sampleProp?: string;
1616
}
1717

18-
const Support: React.FunctionComponent<ISupportProps> = (props) => {
19-
return (
18+
const Support: React.FunctionComponent<ISupportProps> = () => (
2019
<PageSection>
2120
<EmptyState variant={EmptyStateVariant.full}>
2221
<EmptyStateIcon icon={CubesIcon} />
2322
<Title headingLevel="h5" size="lg">
2423
Empty State (Stub Support Module)
2524
</Title>
2625
<EmptyStateBody>
27-
This represents an the empty state pattern in Patternfly 4. Hopefully it's simple enough to use but flexible
26+
This represents an the empty state pattern in Patternfly 4. Hopefully it&apos;s simple enough to use but flexible
2827
enough to meet a variety of needs.
2928
</EmptyStateBody>
3029
<Button variant="primary">Primary Action</Button>
@@ -38,7 +37,6 @@ const Support: React.FunctionComponent<ISupportProps> = (props) => {
3837
</EmptyStateSecondaryActions>
3938
</EmptyState>
4039
</PageSection>
41-
);
42-
}
40+
)
4341

4442
export { Support };

src/app/app.test.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ describe('App tests', () => {
1818
it('should hide the sidebar when clicking the nav-toggle button', () => {
1919
const wrapper = mount(<App />);
2020
const button = wrapper.find('#nav-toggle').hostNodes();
21-
expect(wrapper.find('#page-sidebar').hasClass('pf-m-expanded'));
21+
expect(wrapper.find('#page-sidebar').hasClass('pf-m-expanded')).toBeTruthy();
2222
button.simulate('click');
23-
expect(wrapper.find('#page-sidebar').hasClass('pf-m-collapsed'));
23+
expect(wrapper.find('#page-sidebar').hasClass('pf-m-collapsed')).toBeTruthy();
24+
expect(wrapper.find('#page-sidebar').hasClass('pf-m-expanded')).toBeFalsy();
2425
});
2526
});

src/app/index.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,12 @@ import { AppLayout } from '@app/AppLayout/AppLayout';
55
import { AppRoutes } from '@app/routes';
66
import '@app/app.css';
77

8-
const App: React.FunctionComponent = () => {
9-
return (
8+
const App: React.FunctionComponent = () => (
109
<Router>
1110
<AppLayout>
1211
<AppRoutes />
1312
</AppLayout>
1413
</Router>
1514
);
16-
};
1715

1816
export { App };

0 commit comments

Comments
 (0)