Merge pull request #115 from verdaccio/hooks-first-step

hooks first step
This commit is contained in:
Juan Picado @jotadeveloper 2019-08-15 21:48:07 +02:00 committed by GitHub
commit d81b610e2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 3019 additions and 58 deletions

View File

@ -13,7 +13,8 @@
"prettier", "prettier",
"verdaccio", "verdaccio",
"jsx-a11y", "jsx-a11y",
"codeceptjs" "codeceptjs",
"react-hooks"
], ],
"settings": { "settings": {
"react": { "react": {
@ -107,6 +108,8 @@
2, 2,
"always" "always"
], ],
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
"verdaccio/jsx-no-style": ["warn"], "verdaccio/jsx-no-style": ["warn"],
"verdaccio/jsx-spread": ["warn"], "verdaccio/jsx-spread": ["warn"],
"jest/expect-expect": 0, "jest/expect-expect": 0,

View File

@ -16,6 +16,7 @@
"@material-ui/core": "3.9.3", "@material-ui/core": "3.9.3",
"@material-ui/icons": "3.0.2", "@material-ui/icons": "3.0.2",
"@octokit/rest": "16.28.7", "@octokit/rest": "16.28.7",
"@testing-library/react": "9.1.0",
"@types/enzyme": "3.10.3", "@types/enzyme": "3.10.3",
"@types/lodash": "4.14.136", "@types/lodash": "4.14.136",
"@types/material-ui": "0.21.6", "@types/material-ui": "0.21.6",
@ -45,6 +46,7 @@
"eslint-plugin-jsx-a11y": "6.2.3", "eslint-plugin-jsx-a11y": "6.2.3",
"eslint-plugin-prettier": "3.1.0", "eslint-plugin-prettier": "3.1.0",
"eslint-plugin-react": "7.14.3", "eslint-plugin-react": "7.14.3",
"eslint-plugin-react-hooks": "1.7.0",
"eslint-plugin-verdaccio": "2.0.0", "eslint-plugin-verdaccio": "2.0.0",
"file-loader": "4.2.0", "file-loader": "4.2.0",
"friendly-errors-webpack-plugin": "1.7.0", "friendly-errors-webpack-plugin": "1.7.0",

View File

@ -4579,6 +4579,70 @@
"tarball": "https://registry.npmjs.org/jquery/-/jquery-3.3.1.tgz" "tarball": "https://registry.npmjs.org/jquery/-/jquery-3.3.1.tgz"
}, },
"maintainers": [ "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", "name": "dmethvin",
"email": "dave.methvin@gmail.com" "email": "dave.methvin@gmail.com"

View File

@ -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<AvatarDeveloper> = ({ name, packageName, version, avatar, email }) => {
const avatarComponent = <Avatar aria-label={name} src={avatar} />;
function renderLinkForMail(email, avatarComponent, packageName, version): JSX.Element {
if (!email || isEmail(email) === false) {
return avatarComponent;
}
return (
<a href={`mailto:${email}?subject=${packageName}@${version}`} target={'_top'}>
{avatarComponent}
</a>
);
}
return <Tooltip title={name}>{renderLinkForMail(email, avatarComponent, packageName, version)}</Tooltip>;
};
export { AvatarTooltip };

View File

@ -0,0 +1,4 @@
import { AvatarTooltip } from './AvatarTooltip';
export { AvatarTooltip };
export default AvatarTooltip;

View File

@ -0,0 +1,104 @@
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
<DetailContextProvider value={{ packageMeta }}>
<Developers type={type} />
</DetailContextProvider>
);
expect(wrapper).toMatchSnapshot();
});
test('should render the component for maintainers with items', () => {
const type: DevelopersType = 'maintainers';
const wrapper = mount(
// @ts-ignore
<DetailContextProvider value={{ packageMeta }}>
<Developers type={type} />
</DetailContextProvider>
);
expect(wrapper).toMatchSnapshot();
});
test('should render the component for contributors with items', () => {
const type: DevelopersType = 'contributors';
const wrapper = mount(
// @ts-ignore
<DetailContextProvider value={{ packageMeta }}>
<Developers type={type} />
</DetailContextProvider>
);
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
<DetailContextProvider value={{ packageMeta }}>
<Developers type={type} visibleMax={1} />
</DetailContextProvider>
);
const item2 = wrapper.find(Fab);
// TODO: I am not sure here how to verify the method inside the component was called.
item2.simulate('click');
});
});

View File

@ -1,79 +1,59 @@
import React, { Component } from 'react'; import React, { FC, Fragment } from 'react';
import Avatar from '@material-ui/core/Avatar';
import Add from '@material-ui/icons/Add'; 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 { Details, Heading, Content, Fab } from './styles';
import { isEmail } from '../../utils/url';
export type DevelopersType = 'contributors' | 'maintainers';
interface Props { interface Props {
type: 'contributors' | 'maintainers'; type: DevelopersType;
} visibleMax?: number;
interface State {
visibleDevs: number;
} }
class Developers extends Component<Props, State> { export const VISIBLE_MAX = 6;
public state = {
visibleDevs: 6, const Developers: FC<Props> = ({ type, visibleMax }) => {
const [visibleDevs, setVisibleDevs] = React.useState<number>(visibleMax || VISIBLE_MAX);
const { packageMeta } = React.useContext(DetailContext);
const handleLoadMore = () => {
setVisibleDevs(visibleDevs + VISIBLE_MAX);
}; };
public render(): JSX.Element { const renderDeveloperDetails = ({ name, avatar, email }, packageMeta) => {
return ( const { name: packageName, version } = packageMeta.latest;
<DetailContextConsumer>
{({ packageMeta }) => {
const { type } = this.props;
const developerType = packageMeta && packageMeta.latest[type];
if (!developerType || developerType.length === 0) return null;
return this.renderDevelopers(developerType, packageMeta);
}}
</DetailContextConsumer>
);
}
public handleLoadMore = () => { return <AvatarTooltip avatar={avatar} email={email} name={name} packageName={packageName} version={version} />;
this.setState(prev => ({ visibleDevs: prev.visibleDevs + 6 }));
}; };
private renderDevelopers = (developers, packageMeta) => { const renderDevelopers = (developers, packageMeta) => {
const { type } = this.props; const listVisibleDevelopers = developers.slice(0, visibleDevs);
const { visibleDevs } = this.state;
return ( return (
<> <Fragment>
<Heading variant={'subheading'}>{type}</Heading> <Heading variant={'subheading'}>{type}</Heading>
<Content> <Content>
{developers.slice(0, visibleDevs).map(developer => ( {listVisibleDevelopers.map(developer => (
<Details key={developer.email}>{this.renderDeveloperDetails(developer, packageMeta)}</Details> <Details key={developer.email}>{renderDeveloperDetails(developer, packageMeta)}</Details>
))} ))}
{visibleDevs < developers.length && ( {visibleDevs < developers.length && (
<Fab onClick={this.handleLoadMore} size="small"> <Fab onClick={handleLoadMore} size="small">
<Add /> <Add />
</Fab> </Fab>
)} )}
</Content> </Content>
</> </Fragment>
); );
}; };
private renderLinkForMail(email, avatarComponent, packageName, version): JSX.Element { const developerList = packageMeta && packageMeta.latest[type];
if (!email || isEmail(email) === false) { if (!developerList || developerList.length === 0) {
return avatarComponent; return null;
}
return (
<a href={`mailto:${email}?subject=${packageName}@${version}`} target={'_top'}>
{avatarComponent}
</a>
);
} }
private renderDeveloperDetails = ({ name, avatar, email }, packageMeta) => { return renderDevelopers(developerList, packageMeta);
const { name: packageName, version } = packageMeta.latest;
const avatarComponent = <Avatar aria-label={name} src={avatar} />;
return <Tooltip title={name}>{this.renderLinkForMail(email, avatarComponent, packageName, version)}</Tooltip>;
}; };
}
export default Developers; export default Developers;

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,7 @@
import styled from 'react-emotion'; import styled from 'react-emotion';
import Typography from '@material-ui/core/Typography'; import Typography from '@material-ui/core/Typography';
import { default as MuiFab } from '@material-ui/core/Fab'; import { default as MuiFab } from '@material-ui/core/Fab';
import colors from '../../utils/styles/colors'; import colors from '../../utils/styles/colors';
import { fontWeight } from '../../utils/styles/sizes'; import { fontWeight } from '../../utils/styles/sizes';

View File

@ -1,3 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<Footer /> component should load the initial state of Footer component 1`] = `"<div class=\\"css-i0nj2g ezbsl480\\"><div class=\\"css-hzfs9b ezbsl481\\"><div class=\\"css-d8nsp7 ezbsl482\\"> Made with<span class=\\"css-1so4oe0 ezbsl487\\">♥</span>on<span class=\\"css-1ie354y ezbsl484\\"><svg class=\\"ezbsl485 css-1kgp95j ek145dl0\\"><title>Earth</title><use xlink:href=\\"[object Object]#earth\\"></use></svg><span class=\\"css-8631ip ezbsl486\\"><svg class=\\"ezbsl488 css-f1ndto ek145dl0\\"><title>Spain</title><use xlink:href=\\"[object Object]#spain\\"></use></svg><svg class=\\"ezbsl488 css-f1ndto ek145dl0\\"><title>Nicaragua</title><use xlink:href=\\"[object Object]#nicaragua\\"></use></svg><svg class=\\"ezbsl488 css-f1ndto ek145dl0\\"><title>India</title><use xlink:href=\\"[object Object]#india\\"></use></svg><svg class=\\"ezbsl488 css-f1ndto ek145dl0\\"><title>Brazil</title><use xlink:href=\\"[object Object]#brazil\\"></use></svg><svg class=\\"ezbsl488 css-f1ndto ek145dl0\\"><title>China</title><use xlink:href=\\"[object Object]#china\\"></use></svg><svg class=\\"ezbsl488 css-f1ndto ek145dl0\\"><title>Austria</title><use xlink:href=\\"[object Object]#austria\\"></use></svg></span></span></div><div class=\\"css-1wbzdyy ezbsl483\\">Powered by<span class=\\"ezbsl488 css-i15wza ek145dl1\\" name=\\"verdaccio\\" title=\\"Verdaccio\\"><img alt=\\"Verdaccio\\" src=\\"[object Object]\\" class=\\"css-1ncdhax ek145dl2\\"></span>/ v.1.0.0</div></div></div>"`; exports[`<Footer /> component should load the initial state of Footer component 1`] = `"<div class=\\"css-i0nj2g ezbsl480\\"><div class=\\"css-hzfs9b ezbsl481\\"><div class=\\"css-d8nsp7 ezbsl482\\"> Made with<span class=\\"css-1so4oe0 ezbsl487\\">♥</span>on<span class=\\"css-1ie354y ezbsl484\\"><svg class=\\"ezbsl485 css-tsfgle ek145dl0\\"><title>Earth</title><use xlink:href=\\"[object Object]#earth\\"></use></svg><span class=\\"css-8631ip ezbsl486\\"><svg class=\\"ezbsl488 css-13b76ay ek145dl0\\"><title>Spain</title><use xlink:href=\\"[object Object]#spain\\"></use></svg><svg class=\\"ezbsl488 css-13b76ay ek145dl0\\"><title>Nicaragua</title><use xlink:href=\\"[object Object]#nicaragua\\"></use></svg><svg class=\\"ezbsl488 css-13b76ay ek145dl0\\"><title>India</title><use xlink:href=\\"[object Object]#india\\"></use></svg><svg class=\\"ezbsl488 css-13b76ay ek145dl0\\"><title>Brazil</title><use xlink:href=\\"[object Object]#brazil\\"></use></svg><svg class=\\"ezbsl488 css-13b76ay ek145dl0\\"><title>China</title><use xlink:href=\\"[object Object]#china\\"></use></svg><svg class=\\"ezbsl488 css-13b76ay ek145dl0\\"><title>Austria</title><use xlink:href=\\"[object Object]#austria\\"></use></svg></span></span></div><div class=\\"css-1wbzdyy ezbsl483\\">Powered by<span class=\\"ezbsl488 css-i15wza ek145dl1\\" name=\\"verdaccio\\" title=\\"Verdaccio\\"><img alt=\\"Verdaccio\\" src=\\"[object Object]\\" class=\\"css-1ncdhax ek145dl2\\"></span>/ v.1.0.0</div></div></div>"`;

View File

@ -1,3 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<Icon /> component should render the component in default state 1`] = `"<svg class=\\"css-3skwlp ek145dl0\\"><title>Austria</title><use xlink:href=\\"[object Object]#austria\\"></use></svg>"`; exports[`<Icon /> component should render the component in default state 1`] = `"<svg class=\\"css-snirlv ek145dl0\\"><title>Austria</title><use xlink:href=\\"[object Object]#austria\\"></use></svg>"`;

View File

@ -21,7 +21,7 @@ const getSize = (size: Breakpoint): string => {
const commonStyle = ({ size = 'sm' as Breakpoint, pointer, modifiers = null }): string => css` const commonStyle = ({ size = 'sm' as Breakpoint, pointer, modifiers = null }): string => css`
&& { && {
display: inline-block; display: inline-block;
cursor: ${pointer ? 'pointer' : 'default'}; cursor: ${pointer ? 'pointer' : 'Developers'};
${getSize(size)}; ${getSize(size)};
${modifiers && modifiers}; ${modifiers && modifiers};
} }

View File

@ -1009,7 +1009,7 @@
pirates "^4.0.0" pirates "^4.0.0"
source-map-support "^0.5.9" source-map-support "^0.5.9"
"@babel/runtime@7.5.5", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.5": "@babel/runtime@7.5.5", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5":
version "7.5.5" version "7.5.5"
resolved "https://registry.verdaccio.org/@babel%2fruntime/-/runtime-7.5.5.tgz#74fba56d35efbeca444091c7850ccd494fd2f132" resolved "https://registry.verdaccio.org/@babel%2fruntime/-/runtime-7.5.5.tgz#74fba56d35efbeca444091c7850ccd494fd2f132"
integrity sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ== integrity sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==
@ -1628,6 +1628,30 @@
universal-user-agent "^3.0.0" universal-user-agent "^3.0.0"
url-template "^2.0.8" url-template "^2.0.8"
"@sheerun/mutationobserver-shim@^0.3.2":
version "0.3.2"
resolved "https://registry.verdaccio.org/@sheerun%2fmutationobserver-shim/-/mutationobserver-shim-0.3.2.tgz#8013f2af54a2b7d735f71560ff360d3a8176a87b"
integrity sha512-vTCdPp/T/Q3oSqwHmZ5Kpa9oI7iLtGl3RQaA/NyLHikvcrPxACkkKVr/XzkSPJWXHRhKGzVvb0urJsbMlRxi1Q==
"@testing-library/dom@^6.0.0":
version "6.0.0"
resolved "https://registry.verdaccio.org/@testing-library%2fdom/-/dom-6.0.0.tgz#34e28e69e49bd6347fc64a5dde4c4f9aabbd17d3"
integrity sha512-B5XTz3uMsbqbdR9CZlnwpZjTE3fCWuqRkz/zvDc2Ej/vuHmTM0Ur2v0XPwr7usWfGIBsahEK5HL1E91+4IFiBg==
dependencies:
"@babel/runtime" "^7.5.5"
"@sheerun/mutationobserver-shim" "^0.3.2"
aria-query "3.0.0"
pretty-format "^24.8.0"
wait-for-expect "^1.3.0"
"@testing-library/react@9.1.0":
version "9.1.0"
resolved "https://registry.verdaccio.org/@testing-library%2freact/-/react-9.1.0.tgz#c00c4ff90c5f9015365f63b7759a57c428b6ab56"
integrity sha512-XMtU8AmGX472c7+qXaaVZ2QpDKPA56vRaVEjlFBPDL8im2erM6DF0KJlwzLcc2wrvfYkcMDbreFyL0vdalubrQ==
dependencies:
"@babel/runtime" "^7.5.5"
"@testing-library/dom" "^6.0.0"
"@types/babel__core@^7.1.0": "@types/babel__core@^7.1.0":
version "7.1.0" version "7.1.0"
resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.0.tgz#710f2487dda4dcfd010ca6abb2b4dc7394365c51" resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.0.tgz#710f2487dda4dcfd010ca6abb2b4dc7394365c51"
@ -2467,7 +2491,7 @@ argv@^0.0.2:
resolved "https://registry.npmjs.org/argv/-/argv-0.0.2.tgz#ecbd16f8949b157183711b1bda334f37840185ab" resolved "https://registry.npmjs.org/argv/-/argv-0.0.2.tgz#ecbd16f8949b157183711b1bda334f37840185ab"
integrity sha1-7L0W+JSbFXGDcRsb2jNPN4QBhas= integrity sha1-7L0W+JSbFXGDcRsb2jNPN4QBhas=
aria-query@^3.0.0: aria-query@3.0.0, aria-query@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.verdaccio.org/aria-query/-/aria-query-3.0.0.tgz#65b3fcc1ca1155a8c9ae64d6eee297f15d5133cc" resolved "https://registry.verdaccio.org/aria-query/-/aria-query-3.0.0.tgz#65b3fcc1ca1155a8c9ae64d6eee297f15d5133cc"
integrity sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w= integrity sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=
@ -5508,6 +5532,11 @@ eslint-plugin-prettier@3.1.0:
dependencies: dependencies:
prettier-linter-helpers "^1.0.0" 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: eslint-plugin-react@7.14.3:
version "7.14.3" version "7.14.3"
resolved "https://registry.verdaccio.org/eslint-plugin-react/-/eslint-plugin-react-7.14.3.tgz#911030dd7e98ba49e1b2208599571846a66bdf13" resolved "https://registry.verdaccio.org/eslint-plugin-react/-/eslint-plugin-react-7.14.3.tgz#911030dd7e98ba49e1b2208599571846a66bdf13"
@ -13938,6 +13967,11 @@ w3c-xmlserializer@^1.1.2:
webidl-conversions "^4.0.2" webidl-conversions "^4.0.2"
xml-name-validator "^3.0.0" xml-name-validator "^3.0.0"
wait-for-expect@^1.3.0:
version "1.3.0"
resolved "https://registry.verdaccio.org/wait-for-expect/-/wait-for-expect-1.3.0.tgz#65241ce355425f907f5d127bdb5e72c412ff830c"
integrity sha512-8fJU7jiA96HfGPt+P/UilelSAZfhMBJ52YhKzlmZQvKEZU2EcD1GQ0yqGB6liLdHjYtYAoGVigYwdxr5rktvzA==
walker@^1.0.7, walker@~1.0.5: walker@^1.0.7, walker@~1.0.5:
version "1.0.7" version "1.0.7"
resolved "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" resolved "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb"