From 502c0903ab8cfb6208174fe729ee3066995cf2eb Mon Sep 17 00:00:00 2001 From: "Juan Picado @jotadeveloper" Date: Sun, 11 Aug 2019 07:24:54 +0200 Subject: [PATCH 1/4] chore: add react hooks eslint conf --- .eslintrc | 5 ++++- package.json | 1 + yarn.lock | 5 +++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.eslintrc b/.eslintrc index 6ee2367..a4a9a7a 100644 --- a/.eslintrc +++ b/.eslintrc @@ -13,7 +13,8 @@ "prettier", "verdaccio", "jsx-a11y", - "codeceptjs" + "codeceptjs", + "react-hooks" ], "settings": { "react": { @@ -107,6 +108,8 @@ 2, "always" ], + "react-hooks/rules-of-hooks": "error", + "react-hooks/exhaustive-deps": "warn", "verdaccio/jsx-no-style": ["warn"], "verdaccio/jsx-spread": ["warn"], "jest/expect-expect": 0, diff --git a/package.json b/package.json index ee15580..8bcb49a 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "eslint-plugin-jsx-a11y": "6.2.3", "eslint-plugin-prettier": "3.1.0", "eslint-plugin-react": "7.14.3", + "eslint-plugin-react-hooks": "1.7.0", "eslint-plugin-verdaccio": "2.0.0", "file-loader": "4.2.0", "friendly-errors-webpack-plugin": "1.7.0", diff --git a/yarn.lock b/yarn.lock index 4c5787c..d943203 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5508,6 +5508,11 @@ eslint-plugin-prettier@3.1.0: dependencies: prettier-linter-helpers "^1.0.0" +eslint-plugin-react-hooks@1.7.0: + version "1.7.0" + resolved "https://registry.verdaccio.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-1.7.0.tgz#6210b6d5a37205f0b92858f895a4e827020a7d04" + integrity sha512-iXTCFcOmlWvw4+TOE8CLWj6yX1GwzT0Y6cUfHHZqWnSk144VmVIRcVGtUAzrLES7C798lmvnt02C7rxaOX1HNA== + eslint-plugin-react@7.14.3: version "7.14.3" resolved "https://registry.verdaccio.org/eslint-plugin-react/-/eslint-plugin-react-7.14.3.tgz#911030dd7e98ba49e1b2208599571846a66bdf13" From e46020f9b0ed720f933402ef972c02a079bd6a62 Mon Sep 17 00:00:00 2001 From: "Juan Picado @jotadeveloper" Date: Mon, 12 Aug 2019 07:06:10 +0200 Subject: [PATCH 2/4] refactor: Developers component Using React hooks --- package.json | 1 + partials/storage/jquery/package.json | 66 +- .../AvatarTooltip/AvatarTooltip.tsx | 32 + src/components/AvatarTooltip/index.ts | 4 + src/components/Developers/Developers.test.tsx | 103 + src/components/Developers/Developers.tsx | 82 +- .../__snapshots__/Developers.test.tsx.snap | 5467 +++++++++++++++++ src/components/Developers/styles.ts | 1 + .../Footer/__snapshots__/Footer.test.tsx.snap | 2 +- .../Icon/__snapshots__/Icon.test.tsx.snap | 2 +- src/components/Icon/styles.ts | 2 +- yarn.lock | 33 +- 12 files changed, 5738 insertions(+), 57 deletions(-) create mode 100644 src/components/AvatarTooltip/AvatarTooltip.tsx create mode 100644 src/components/AvatarTooltip/index.ts create mode 100644 src/components/Developers/Developers.test.tsx create mode 100644 src/components/Developers/__snapshots__/Developers.test.tsx.snap diff --git a/package.json b/package.json index 8bcb49a..1ae959d 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@material-ui/core": "3.9.3", "@material-ui/icons": "3.0.2", "@octokit/rest": "16.28.7", + "@testing-library/react": "9.1.0", "@types/enzyme": "3.10.3", "@types/lodash": "4.14.136", "@types/material-ui": "0.21.6", diff --git a/partials/storage/jquery/package.json b/partials/storage/jquery/package.json index 1f76451..e01aea4 100644 --- a/partials/storage/jquery/package.json +++ b/partials/storage/jquery/package.json @@ -4579,6 +4579,70 @@ "tarball": "https://registry.npmjs.org/jquery/-/jquery-3.3.1.tgz" }, "maintainers": [ + { + "name": "dmethvin", + "email": "dave.methvin@gmail.com" + }, + { + "name": "mgol", + "email": "m.goleb@gmail.com" + }, + { + "name": "scott.gonzalez", + "email": "scott.gonzalez@gmail.com" + }, + { + "name": "timmywil", + "email": "4timmywil@gmail.com" + }, + { + "name": "dmethvin", + "email": "dave.methvin@gmail.com" + }, + { + "name": "mgol", + "email": "m.goleb@gmail.com" + }, + { + "name": "scott.gonzalez", + "email": "scott.gonzalez@gmail.com" + }, + { + "name": "timmywil", + "email": "4timmywil@gmail.com" + }, + { + "name": "dmethvin", + "email": "dave.methvin@gmail.com" + }, + { + "name": "mgol", + "email": "m.goleb@gmail.com" + }, + { + "name": "scott.gonzalez", + "email": "scott.gonzalez@gmail.com" + }, + { + "name": "timmywil", + "email": "4timmywil@gmail.com" + }, + { + "name": "dmethvin", + "email": "dave.methvin@gmail.com" + }, + { + "name": "mgol", + "email": "m.goleb@gmail.com" + }, + { + "name": "scott.gonzalez", + "email": "scott.gonzalez@gmail.com" + }, + { + "name": "timmywil", + "email": "4timmywil@gmail.com" + }, { "name": "dmethvin", "email": "dave.methvin@gmail.com" @@ -4916,4 +4980,4 @@ }, "_rev": "60-fed4915c27b9c1e6", "readme": "# jQuery\n\n> jQuery is a fast, small, and feature-rich JavaScript library.\n\nFor information on how to get started and how to use jQuery, please see [jQuery's documentation](http://api.jquery.com/).\nFor source files and issues, please visit the [jQuery repo](https://github.com/jquery/jquery).\n\nIf upgrading, please see the [blog post for 3.3.1](https://blog.jquery.com/2017/03/20/jquery-3.3.1-now-available/). This includes notable differences from the previous version and a more readable changelog.\n\n## Including jQuery\n\nBelow are some of the most common ways to include jQuery.\n\n### Browser\n\n#### Script tag\n\n```html\n\n```\n\n#### Babel\n\n[Babel](http://babeljs.io/) is a next generation JavaScript compiler. One of the features is the ability to use ES6/ES2015 modules now, even though browsers do not yet support this feature natively.\n\n```js\nimport $ from \"jquery\";\n```\n\n#### Browserify/Webpack\n\nThere are several ways to use [Browserify](http://browserify.org/) and [Webpack](https://webpack.github.io/). For more information on using these tools, please refer to the corresponding project's documention. In the script, including jQuery will usually look like this...\n\n```js\nvar $ = require(\"jquery\");\n```\n\n#### AMD (Asynchronous Module Definition)\n\nAMD is a module format built for the browser. For more information, we recommend [require.js' documentation](http://requirejs.org/docs/whyamd.html).\n\n```js\ndefine([\"jquery\"], function($) {\n\n});\n```\n\n### Node\n\nTo include jQuery in [Node](nodejs.org), first install with npm.\n\n```sh\nnpm install jquery\n```\n\nFor jQuery to work in Node, a window with a document is required. Since no such window exists natively in Node, one can be mocked by tools such as [jsdom](https://github.com/tmpvar/jsdom). This can be useful for testing purposes.\n\n```js\nrequire(\"jsdom\").env(\"\", function(err, window) {\n\tif (err) {\n\t\tconsole.error(err);\n\t\treturn;\n\t}\n\n\tvar $ = require(\"jquery\")(window);\n});\n```" -} \ No newline at end of file +} diff --git a/src/components/AvatarTooltip/AvatarTooltip.tsx b/src/components/AvatarTooltip/AvatarTooltip.tsx new file mode 100644 index 0000000..e2244b7 --- /dev/null +++ b/src/components/AvatarTooltip/AvatarTooltip.tsx @@ -0,0 +1,32 @@ +import React, { FC } from 'react'; + +import Avatar from '@material-ui/core/Avatar'; +import Tooltip from '@material-ui/core/Tooltip'; +import { isEmail } from '../../utils/url'; + +export interface AvatarDeveloper { + name: string; + packageName: string; + version: string; + avatar: string; + email: string; +} + +const AvatarTooltip: FC = ({ name, packageName, version, avatar, email }) => { + const avatarComponent = ; + function renderLinkForMail(email, avatarComponent, packageName, version): JSX.Element { + if (!email || isEmail(email) === false) { + return avatarComponent; + } + + return ( + + {avatarComponent} + + ); + } + + return {renderLinkForMail(email, avatarComponent, packageName, version)}; +}; + +export { AvatarTooltip }; diff --git a/src/components/AvatarTooltip/index.ts b/src/components/AvatarTooltip/index.ts new file mode 100644 index 0000000..746d62c --- /dev/null +++ b/src/components/AvatarTooltip/index.ts @@ -0,0 +1,4 @@ +import { AvatarTooltip } from './AvatarTooltip'; + +export { AvatarTooltip }; +export default AvatarTooltip; diff --git a/src/components/Developers/Developers.test.tsx b/src/components/Developers/Developers.test.tsx new file mode 100644 index 0000000..bda8373 --- /dev/null +++ b/src/components/Developers/Developers.test.tsx @@ -0,0 +1,103 @@ +import React from 'react'; +import { mount } from 'enzyme'; +import Developers, { DevelopersType } from './Developers'; +import { Fab } from './styles'; +import { DetailContextProvider } from '../../pages/version/Version'; + +describe('test Developers', () => { + const packageMeta = { + latest: { + packageName: 'foo', + version: '1.0.0', + maintainers: [ + { + name: 'dmethvin', + email: 'dave.methvin@gmail.com', + }, + { + name: 'mgol', + email: 'm.goleb@gmail.com', + }, + ], + contributors: [ + { + name: 'dmethvin', + email: 'dave.methvin@gmail.com', + }, + { + name: 'mgol', + email: 'm.goleb@gmail.com', + }, + ], + }, + }; + + test('should render the component with no items', () => { + const type: DevelopersType = 'maintainers'; + const packageMeta = { + latest: {}, + }; + const wrapper = mount( + // @ts-ignore + + + + ); + + expect(wrapper).toMatchSnapshot(); + }); + + test('should render the component for maintainers with items', () => { + const type: DevelopersType = 'maintainers'; + const wrapper = mount( + // @ts-ignore + + + + ); + + expect(wrapper).toMatchSnapshot(); + }); + + test('should render the component for contributors with items', () => { + const type: DevelopersType = 'contributors'; + const wrapper = mount( + // @ts-ignore + + + + ); + + expect(wrapper).toMatchSnapshot(); + }); + + test('should test onClick the component avatar', () => { + const type: DevelopersType = 'contributors'; + const packageMeta = { + latest: { + packageName: 'foo', + version: '1.0.0', + contributors: [ + { + name: 'dmethvin', + email: 'dave.methvin@gmail.com', + }, + { + name: 'dmethvin2', + email: 'dave2.methvin@gmail.com', + }, + ], + }, + }; + + const wrapper = mount( + // @ts-ignore + + + + ); + + const item2 = wrapper.find(Fab); + item2.simulate('click'); + }); +}); diff --git a/src/components/Developers/Developers.tsx b/src/components/Developers/Developers.tsx index 68d87ca..52f5204 100644 --- a/src/components/Developers/Developers.tsx +++ b/src/components/Developers/Developers.tsx @@ -1,79 +1,59 @@ -import React, { Component } from 'react'; - -import Avatar from '@material-ui/core/Avatar'; +import React, { FC, Fragment } from 'react'; import Add from '@material-ui/icons/Add'; -import Tooltip from '@material-ui/core/Tooltip'; -import { DetailContextConsumer } from '../../pages/version/Version'; +import { DetailContext } from '../../pages/version/Version'; +import { AvatarTooltip } from '../AvatarTooltip'; import { Details, Heading, Content, Fab } from './styles'; -import { isEmail } from '../../utils/url'; + +export type DevelopersType = 'contributors' | 'maintainers'; interface Props { - type: 'contributors' | 'maintainers'; -} -interface State { - visibleDevs: number; + type: DevelopersType; + visibleMax?: number; } -class Developers extends Component { - public state = { - visibleDevs: 6, +export const VISIBLE_MAX = 6; + +const Developers: FC = ({ type, visibleMax }) => { + const [visibleDevs, setVisibleDevs] = React.useState(visibleMax || VISIBLE_MAX); + const { packageMeta } = React.useContext(DetailContext); + + const handleLoadMore = () => { + setVisibleDevs(visibleDevs + VISIBLE_MAX); }; - public render(): JSX.Element { - return ( - - {({ packageMeta }) => { - const { type } = this.props; - const developerType = packageMeta && packageMeta.latest[type]; - if (!developerType || developerType.length === 0) return null; - return this.renderDevelopers(developerType, packageMeta); - }} - - ); - } + const renderDeveloperDetails = ({ name, avatar, email }, packageMeta) => { + const { name: packageName, version } = packageMeta.latest; - public handleLoadMore = () => { - this.setState(prev => ({ visibleDevs: prev.visibleDevs + 6 })); + return ; }; - private renderDevelopers = (developers, packageMeta) => { - const { type } = this.props; - const { visibleDevs } = this.state; + const renderDevelopers = (developers, packageMeta) => { + const listVisibleDevelopers = developers.slice(0, visibleDevs); + return ( - <> + {type} - {developers.slice(0, visibleDevs).map(developer => ( -
{this.renderDeveloperDetails(developer, packageMeta)}
+ {listVisibleDevelopers.map(developer => ( +
{renderDeveloperDetails(developer, packageMeta)}
))} {visibleDevs < developers.length && ( - + )}
- +
); }; - private renderLinkForMail(email, avatarComponent, packageName, version): JSX.Element { - if (!email || isEmail(email) === false) { - return avatarComponent; - } - return ( - - {avatarComponent} - - ); + const developerList = packageMeta && packageMeta.latest[type]; + if (!developerList || developerList.length === 0) { + return null; } - private renderDeveloperDetails = ({ name, avatar, email }, packageMeta) => { - const { name: packageName, version } = packageMeta.latest; - - const avatarComponent = ; - return {this.renderLinkForMail(email, avatarComponent, packageName, version)}; - }; -} + return renderDevelopers(developerList, packageMeta); +}; export default Developers; diff --git a/src/components/Developers/__snapshots__/Developers.test.tsx.snap b/src/components/Developers/__snapshots__/Developers.test.tsx.snap new file mode 100644 index 0000000..14aa3af --- /dev/null +++ b/src/components/Developers/__snapshots__/Developers.test.tsx.snap @@ -0,0 +1,5467 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`test Developers should render the component for contributors 1`] = ` + + + + +

+ contributors +

+
+
+
+ +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + +`; + +exports[`test Developers should render the component for contributors with items 1`] = ` + + + + +

+ contributors +

+
+
+
+ +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + +`; + +exports[`test Developers should render the component for maintainers 1`] = ` + + + + +

+ maintainers +

+
+
+
+ +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + +`; + +exports[`test Developers should render the component for maintainers with items 1`] = ` + + + + +

+ maintainers +

+
+
+
+ +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + +`; + +exports[`test Developers should render the component with no items 1`] = ` + +`; diff --git a/src/components/Developers/styles.ts b/src/components/Developers/styles.ts index aa2fc23..10eaaff 100644 --- a/src/components/Developers/styles.ts +++ b/src/components/Developers/styles.ts @@ -1,6 +1,7 @@ import styled from 'react-emotion'; import Typography from '@material-ui/core/Typography'; import { default as MuiFab } from '@material-ui/core/Fab'; + import colors from '../../utils/styles/colors'; import { fontWeight } from '../../utils/styles/sizes'; diff --git a/src/components/Footer/__snapshots__/Footer.test.tsx.snap b/src/components/Footer/__snapshots__/Footer.test.tsx.snap index 7320406..aa1ccfc 100644 --- a/src/components/Footer/__snapshots__/Footer.test.tsx.snap +++ b/src/components/Footer/__snapshots__/Footer.test.tsx.snap @@ -1,3 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`