Skip to content

Commit d81b610

Browse files
authored
Merge pull request conventional-changelog#115 from verdaccio/hooks-first-step
hooks first step
2 parents 542212a + 3d7b230 commit d81b610

File tree

13 files changed

+3019
-58
lines changed

13 files changed

+3019
-58
lines changed

.eslintrc

+4-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
"prettier",
1414
"verdaccio",
1515
"jsx-a11y",
16-
"codeceptjs"
16+
"codeceptjs",
17+
"react-hooks"
1718
],
1819
"settings": {
1920
"react": {
@@ -107,6 +108,8 @@
107108
2,
108109
"always"
109110
],
111+
"react-hooks/rules-of-hooks": "error",
112+
"react-hooks/exhaustive-deps": "warn",
110113
"verdaccio/jsx-no-style": ["warn"],
111114
"verdaccio/jsx-spread": ["warn"],
112115
"jest/expect-expect": 0,

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"@material-ui/core": "3.9.3",
1717
"@material-ui/icons": "3.0.2",
1818
"@octokit/rest": "16.28.7",
19+
"@testing-library/react": "9.1.0",
1920
"@types/enzyme": "3.10.3",
2021
"@types/lodash": "4.14.136",
2122
"@types/material-ui": "0.21.6",
@@ -45,6 +46,7 @@
4546
"eslint-plugin-jsx-a11y": "6.2.3",
4647
"eslint-plugin-prettier": "3.1.0",
4748
"eslint-plugin-react": "7.14.3",
49+
"eslint-plugin-react-hooks": "1.7.0",
4850
"eslint-plugin-verdaccio": "2.0.0",
4951
"file-loader": "4.2.0",
5052
"friendly-errors-webpack-plugin": "1.7.0",

partials/storage/jquery/package.json

+65-1
Original file line numberDiff line numberDiff line change
@@ -4579,6 +4579,70 @@
45794579
"tarball": "https://registry.npmjs.org/jquery/-/jquery-3.3.1.tgz"
45804580
},
45814581
"maintainers": [
4582+
{
4583+
"name": "dmethvin",
4584+
"email": "[email protected]"
4585+
},
4586+
{
4587+
"name": "mgol",
4588+
"email": "[email protected]"
4589+
},
4590+
{
4591+
"name": "scott.gonzalez",
4592+
"email": "[email protected]"
4593+
},
4594+
{
4595+
"name": "timmywil",
4596+
"email": "[email protected]"
4597+
},
4598+
{
4599+
"name": "dmethvin",
4600+
"email": "[email protected]"
4601+
},
4602+
{
4603+
"name": "mgol",
4604+
"email": "[email protected]"
4605+
},
4606+
{
4607+
"name": "scott.gonzalez",
4608+
"email": "[email protected]"
4609+
},
4610+
{
4611+
"name": "timmywil",
4612+
"email": "[email protected]"
4613+
},
4614+
{
4615+
"name": "dmethvin",
4616+
"email": "[email protected]"
4617+
},
4618+
{
4619+
"name": "mgol",
4620+
"email": "[email protected]"
4621+
},
4622+
{
4623+
"name": "scott.gonzalez",
4624+
"email": "[email protected]"
4625+
},
4626+
{
4627+
"name": "timmywil",
4628+
"email": "[email protected]"
4629+
},
4630+
{
4631+
"name": "dmethvin",
4632+
"email": "[email protected]"
4633+
},
4634+
{
4635+
"name": "mgol",
4636+
"email": "[email protected]"
4637+
},
4638+
{
4639+
"name": "scott.gonzalez",
4640+
"email": "[email protected]"
4641+
},
4642+
{
4643+
"name": "timmywil",
4644+
"email": "[email protected]"
4645+
},
45824646
{
45834647
"name": "dmethvin",
45844648
"email": "[email protected]"
@@ -4916,4 +4980,4 @@
49164980
},
49174981
"_rev": "60-fed4915c27b9c1e6",
49184982
"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<script src=\"https://code.jquery.com/jquery-3.3.1.min.js\"></script>\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```"
4919-
}
4983+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import React, { FC } from 'react';
2+
3+
import Avatar from '@material-ui/core/Avatar';
4+
import Tooltip from '@material-ui/core/Tooltip';
5+
import { isEmail } from '../../utils/url';
6+
7+
export interface AvatarDeveloper {
8+
name: string;
9+
packageName: string;
10+
version: string;
11+
avatar: string;
12+
email: string;
13+
}
14+
15+
const AvatarTooltip: FC<AvatarDeveloper> = ({ name, packageName, version, avatar, email }) => {
16+
const avatarComponent = <Avatar aria-label={name} src={avatar} />;
17+
function renderLinkForMail(email, avatarComponent, packageName, version): JSX.Element {
18+
if (!email || isEmail(email) === false) {
19+
return avatarComponent;
20+
}
21+
22+
return (
23+
<a href={`mailto:${email}?subject=${packageName}@${version}`} target={'_top'}>
24+
{avatarComponent}
25+
</a>
26+
);
27+
}
28+
29+
return <Tooltip title={name}>{renderLinkForMail(email, avatarComponent, packageName, version)}</Tooltip>;
30+
};
31+
32+
export { AvatarTooltip };

src/components/AvatarTooltip/index.ts

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { AvatarTooltip } from './AvatarTooltip';
2+
3+
export { AvatarTooltip };
4+
export default AvatarTooltip;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import React from 'react';
2+
import { mount } from 'enzyme';
3+
import Developers, { DevelopersType } from './Developers';
4+
import { Fab } from './styles';
5+
import { DetailContextProvider } from '../../pages/version/Version';
6+
7+
describe('test Developers', () => {
8+
const packageMeta = {
9+
latest: {
10+
packageName: 'foo',
11+
version: '1.0.0',
12+
maintainers: [
13+
{
14+
name: 'dmethvin',
15+
16+
},
17+
{
18+
name: 'mgol',
19+
20+
},
21+
],
22+
contributors: [
23+
{
24+
name: 'dmethvin',
25+
26+
},
27+
{
28+
name: 'mgol',
29+
30+
},
31+
],
32+
},
33+
};
34+
35+
test('should render the component with no items', () => {
36+
const type: DevelopersType = 'maintainers';
37+
const packageMeta = {
38+
latest: {},
39+
};
40+
const wrapper = mount(
41+
// @ts-ignore
42+
<DetailContextProvider value={{ packageMeta }}>
43+
<Developers type={type} />
44+
</DetailContextProvider>
45+
);
46+
47+
expect(wrapper).toMatchSnapshot();
48+
});
49+
50+
test('should render the component for maintainers with items', () => {
51+
const type: DevelopersType = 'maintainers';
52+
const wrapper = mount(
53+
// @ts-ignore
54+
<DetailContextProvider value={{ packageMeta }}>
55+
<Developers type={type} />
56+
</DetailContextProvider>
57+
);
58+
59+
expect(wrapper).toMatchSnapshot();
60+
});
61+
62+
test('should render the component for contributors with items', () => {
63+
const type: DevelopersType = 'contributors';
64+
const wrapper = mount(
65+
// @ts-ignore
66+
<DetailContextProvider value={{ packageMeta }}>
67+
<Developers type={type} />
68+
</DetailContextProvider>
69+
);
70+
71+
expect(wrapper).toMatchSnapshot();
72+
});
73+
74+
test('should test onClick the component avatar', () => {
75+
const type: DevelopersType = 'contributors';
76+
const packageMeta = {
77+
latest: {
78+
packageName: 'foo',
79+
version: '1.0.0',
80+
contributors: [
81+
{
82+
name: 'dmethvin',
83+
84+
},
85+
{
86+
name: 'dmethvin2',
87+
88+
},
89+
],
90+
},
91+
};
92+
93+
const wrapper = mount(
94+
// @ts-ignore
95+
<DetailContextProvider value={{ packageMeta }}>
96+
<Developers type={type} visibleMax={1} />
97+
</DetailContextProvider>
98+
);
99+
100+
const item2 = wrapper.find(Fab);
101+
// TODO: I am not sure here how to verify the method inside the component was called.
102+
item2.simulate('click');
103+
});
104+
});
+31-51
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,59 @@
1-
import React, { Component } from 'react';
2-
3-
import Avatar from '@material-ui/core/Avatar';
1+
import React, { FC, Fragment } from 'react';
42
import Add from '@material-ui/icons/Add';
5-
import Tooltip from '@material-ui/core/Tooltip';
63

7-
import { DetailContextConsumer } from '../../pages/version/Version';
4+
import { DetailContext } from '../../pages/version/Version';
5+
import { AvatarTooltip } from '../AvatarTooltip';
86
import { Details, Heading, Content, Fab } from './styles';
9-
import { isEmail } from '../../utils/url';
7+
8+
export type DevelopersType = 'contributors' | 'maintainers';
109

1110
interface Props {
12-
type: 'contributors' | 'maintainers';
13-
}
14-
interface State {
15-
visibleDevs: number;
11+
type: DevelopersType;
12+
visibleMax?: number;
1613
}
1714

18-
class Developers extends Component<Props, State> {
19-
public state = {
20-
visibleDevs: 6,
15+
export const VISIBLE_MAX = 6;
16+
17+
const Developers: FC<Props> = ({ type, visibleMax }) => {
18+
const [visibleDevs, setVisibleDevs] = React.useState<number>(visibleMax || VISIBLE_MAX);
19+
const { packageMeta } = React.useContext(DetailContext);
20+
21+
const handleLoadMore = () => {
22+
setVisibleDevs(visibleDevs + VISIBLE_MAX);
2123
};
2224

23-
public render(): JSX.Element {
24-
return (
25-
<DetailContextConsumer>
26-
{({ packageMeta }) => {
27-
const { type } = this.props;
28-
const developerType = packageMeta && packageMeta.latest[type];
29-
if (!developerType || developerType.length === 0) return null;
30-
return this.renderDevelopers(developerType, packageMeta);
31-
}}
32-
</DetailContextConsumer>
33-
);
34-
}
25+
const renderDeveloperDetails = ({ name, avatar, email }, packageMeta) => {
26+
const { name: packageName, version } = packageMeta.latest;
3527

36-
public handleLoadMore = () => {
37-
this.setState(prev => ({ visibleDevs: prev.visibleDevs + 6 }));
28+
return <AvatarTooltip avatar={avatar} email={email} name={name} packageName={packageName} version={version} />;
3829
};
3930

40-
private renderDevelopers = (developers, packageMeta) => {
41-
const { type } = this.props;
42-
const { visibleDevs } = this.state;
31+
const renderDevelopers = (developers, packageMeta) => {
32+
const listVisibleDevelopers = developers.slice(0, visibleDevs);
33+
4334
return (
44-
<>
35+
<Fragment>
4536
<Heading variant={'subheading'}>{type}</Heading>
4637
<Content>
47-
{developers.slice(0, visibleDevs).map(developer => (
48-
<Details key={developer.email}>{this.renderDeveloperDetails(developer, packageMeta)}</Details>
38+
{listVisibleDevelopers.map(developer => (
39+
<Details key={developer.email}>{renderDeveloperDetails(developer, packageMeta)}</Details>
4940
))}
5041
{visibleDevs < developers.length && (
51-
<Fab onClick={this.handleLoadMore} size="small">
42+
<Fab onClick={handleLoadMore} size="small">
5243
<Add />
5344
</Fab>
5445
)}
5546
</Content>
56-
</>
47+
</Fragment>
5748
);
5849
};
5950

60-
private renderLinkForMail(email, avatarComponent, packageName, version): JSX.Element {
61-
if (!email || isEmail(email) === false) {
62-
return avatarComponent;
63-
}
64-
return (
65-
<a href={`mailto:${email}?subject=${packageName}@${version}`} target={'_top'}>
66-
{avatarComponent}
67-
</a>
68-
);
51+
const developerList = packageMeta && packageMeta.latest[type];
52+
if (!developerList || developerList.length === 0) {
53+
return null;
6954
}
7055

71-
private renderDeveloperDetails = ({ name, avatar, email }, packageMeta) => {
72-
const { name: packageName, version } = packageMeta.latest;
73-
74-
const avatarComponent = <Avatar aria-label={name} src={avatar} />;
75-
return <Tooltip title={name}>{this.renderLinkForMail(email, avatarComponent, packageName, version)}</Tooltip>;
76-
};
77-
}
56+
return renderDevelopers(developerList, packageMeta);
57+
};
7858

7959
export default Developers;

0 commit comments

Comments
 (0)