Skip to content
This repository was archived by the owner on Jan 16, 2022. It is now read-only.

Commit 99621b6

Browse files
antoinechalifourpriscilawebdev
authored andcommitted
Refactor: Dependencies - Replaced class with func. comp (#169)
1 parent e0642a9 commit 99621b6

File tree

5 files changed

+165
-109
lines changed

5 files changed

+165
-109
lines changed

package.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"@types/node": "12.7.8",
2828
"@types/react": "16.9.2",
2929
"@types/react-dom": "16.9.0",
30-
"@types/react-router-dom": "4.3.5",
30+
"@types/react-router-dom": "5.1.0",
3131
"@types/validator": "10.11.3",
3232
"@typescript-eslint/parser": "2.3.2",
3333
"@verdaccio/babel-preset": "2.0.0",
@@ -85,10 +85,9 @@
8585
"react": "16.10.0",
8686
"react-autosuggest": "9.4.3",
8787
"react-dom": "16.10.0",
88+
"react-router-dom": "5.1.2",
8889
"react-emotion": "9.2.12",
8990
"react-hot-loader": "4.12.11",
90-
"react-router": "5.0.1",
91-
"react-router-dom": "5.0.1",
9291
"resolve-url-loader": "3.1.0",
9392
"rimraf": "3.0.0",
9493
"source-map-loader": "0.2.4",
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import React from 'react';
2+
import { render } from '@testing-library/react';
3+
import { HashRouter } from 'react-router-dom';
4+
5+
import { DetailContextProvider } from '../../pages/Version';
6+
7+
import Dependencies from './Dependencies';
8+
9+
describe('<Dependencies /> component', () => {
10+
test('Renders a message when there are no dependencies', () => {
11+
// Given
12+
const packageMeta = {
13+
latest: {
14+
name: 'verdaccio',
15+
version: '4.0.0',
16+
author: {
17+
name: 'verdaccio user',
18+
19+
url: '',
20+
avatar: 'https://www.gravatar.com/avatar/000000',
21+
},
22+
dist: { fileCount: 0, unpackedSize: 0 },
23+
dependencies: {},
24+
devDependencies: {},
25+
peerDependencies: {},
26+
},
27+
_uplinks: {},
28+
};
29+
30+
// When
31+
const { getByText } = render(
32+
<DetailContextProvider value={{ packageMeta }}>
33+
<Dependencies />
34+
</DetailContextProvider>
35+
);
36+
37+
// Then
38+
expect(getByText('verdaccio has no dependencies.')).toBeDefined();
39+
});
40+
41+
test('Renders a link to each dependency', () => {
42+
// Given
43+
const packageMeta = {
44+
latest: {
45+
name: 'verdaccio',
46+
version: '4.0.0',
47+
author: {
48+
name: 'verdaccio user',
49+
50+
url: '',
51+
avatar: 'https://www.gravatar.com/avatar/000000',
52+
},
53+
dist: { fileCount: 0, unpackedSize: 0 },
54+
dependencies: {
55+
react: '16.9.0',
56+
'react-dom': '16.9.0',
57+
},
58+
devDependencies: {
59+
'babel-core': '7.0.0-beta6',
60+
},
61+
peerDependencies: {
62+
'styled-components': '5.0.0',
63+
},
64+
},
65+
_uplinks: {},
66+
};
67+
68+
// When
69+
const { getByText } = render(
70+
<HashRouter>
71+
<DetailContextProvider value={{ packageMeta }}>
72+
<Dependencies />
73+
</DetailContextProvider>
74+
</HashRouter>
75+
);
76+
77+
// Then
78+
// FIXME: currently MaterialUI chips do not support the children
79+
// prop, therefore it is impossible to use proper links for
80+
// dependencies. Those are for now clickable spans
81+
82+
expect(getByText('dependencies (2)')).toBeDefined();
83+
expect(getByText('[email protected]').tagName).toBe('SPAN');
84+
expect(getByText('[email protected]').tagName).toBe('SPAN');
85+
86+
expect(getByText('devDependencies (1)')).toBeDefined();
87+
expect(getByText('[email protected]').tagName).toBe('SPAN');
88+
89+
expect(getByText('peerDependencies (1)')).toBeDefined();
90+
expect(getByText('[email protected]').tagName).toBe('SPAN');
91+
});
92+
});
Lines changed: 54 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,117 +1,78 @@
1-
import React, { Component, Fragment, ReactElement } from 'react';
2-
import { withRouter, RouteComponentProps } from 'react-router-dom';
1+
import React, { useContext } from 'react';
2+
import { useHistory } from 'react-router-dom';
33
import CardContent from '@material-ui/core/CardContent';
44

5-
import { DetailContextConsumer, VersionPageConsumerProps } from '../../pages/Version';
5+
import { PackageDependencies } from '../../../types/packageMeta';
6+
import { DetailContext } from '../../pages/Version';
67
import NoItems from '../NoItems';
78

89
import { CardWrap, Heading, Tags, Tag } from './styles';
910

10-
type DepDetailProps = {
11-
name: string;
12-
version: string;
13-
onLoading?: () => void;
14-
} & RouteComponentProps;
15-
16-
interface DepDetailState {
17-
name: string;
18-
version: string;
11+
interface DependencyBlockProps {
12+
title: string;
13+
dependencies: PackageDependencies;
1914
}
2015

21-
class DepDetail extends Component<DepDetailProps, DepDetailState> {
22-
constructor(props: DepDetailProps) {
23-
super(props);
24-
const { name, version } = this.props;
16+
const DependencyBlock: React.FC<DependencyBlockProps> = ({ title, dependencies }) => {
17+
const { enableLoading } = useContext(DetailContext);
18+
const history = useHistory();
2519

26-
this.state = {
27-
name,
28-
version,
29-
};
30-
}
20+
const deps = Object.entries(dependencies);
3121

32-
public render(): ReactElement<HTMLElement> {
33-
const { name, version } = this.state;
34-
const tagText = `${name}@${version}`;
35-
return <Tag className={'dep-tag'} clickable={true} label={tagText} onClick={this.handleOnClick} />;
36-
}
22+
function handleClick(name: string): void {
23+
enableLoading && enableLoading();
3724

38-
private handleOnClick = () => {
39-
const { name } = this.state;
40-
const { onLoading, history } = this.props;
41-
42-
onLoading && onLoading();
4325
history.push(`/-/web/detail/${name}`);
44-
};
45-
}
26+
}
4627

47-
const WrapperDependencyDetail = withRouter(DepDetail);
28+
return (
29+
<CardWrap>
30+
<CardContent>
31+
<Heading variant="subtitle1">{`${title} (${deps.length})`}</Heading>
32+
<Tags>
33+
{deps.map(([name, version]) => (
34+
// eslint-disable-next-line
35+
<Tag className={'dep-tag'} clickable={true} key={name} label={`${name}@${version}`} onClick={() => handleClick(name)} />
36+
))}
37+
</Tags>
38+
</CardContent>
39+
</CardWrap>
40+
);
41+
};
42+
43+
function hasKeys(object?: { [key: string]: any }): boolean {
44+
return !!object && Object.keys(object).length > 0;
45+
}
4846

49-
class DependencyBlock extends Component<{ title: string; dependencies: [] }> {
50-
public render(): ReactElement<HTMLElement> {
51-
const { dependencies, title } = this.props;
52-
const deps = Object.entries(dependencies) as [];
47+
const Dependencies: React.FC<{}> = () => {
48+
const { packageMeta } = useContext(DetailContext);
5349

54-
return (
55-
<DetailContextConsumer>
56-
{({ enableLoading }) => {
57-
return (
58-
<CardWrap>
59-
<CardContent>
60-
<Heading variant="subtitle1">{`${title} (${deps.length})`}</Heading>
61-
<Tags>{this.renderTags(deps, enableLoading)}</Tags>
62-
</CardContent>
63-
</CardWrap>
64-
);
65-
}}
66-
</DetailContextConsumer>
67-
);
50+
if (!packageMeta) {
51+
throw new Error('packageMeta is required at DetailContext');
6852
}
6953

70-
private renderTags = (deps: [], enableLoading?: () => void) =>
71-
deps.map(dep => {
72-
const [name, version] = dep as [string, string];
54+
const { latest } = packageMeta;
55+
// FIXME: add dependencies to package meta type
56+
// @ts-ignore
57+
const { dependencies, devDependencies, peerDependencies, name } = latest;
58+
const dependencyMap = { dependencies, devDependencies, peerDependencies };
59+
const hasDependencies = hasKeys(dependencies) || hasKeys(devDependencies) || hasKeys(peerDependencies);
7360

74-
return <WrapperDependencyDetail key={name} name={name} onLoading={enableLoading} version={version} />;
75-
});
76-
}
77-
78-
class Dependencies extends Component {
79-
public render(): ReactElement<HTMLElement> {
61+
if (hasDependencies) {
8062
return (
81-
<DetailContextConsumer>
82-
{packageMeta => {
83-
return this.renderDependencies(packageMeta as VersionPageConsumerProps);
84-
}}
85-
</DetailContextConsumer>
63+
<>
64+
{Object.entries(dependencyMap).map(([dependencyType, dependencies]) => {
65+
if (!dependencies || Object.keys(dependencies).length === 0) {
66+
return null;
67+
}
68+
69+
return <DependencyBlock dependencies={dependencies} key={dependencyType} title={dependencyType} />;
70+
})}
71+
</>
8672
);
8773
}
8874

89-
private checkDependencyLength<T>(dependency: Record<string, T> = {}): boolean {
90-
return Object.keys(dependency).length > 0;
91-
}
92-
93-
private renderDependencies({ packageMeta }): ReactElement<HTMLElement> {
94-
const { latest } = packageMeta;
95-
const { dependencies, devDependencies, peerDependencies, name } = latest;
96-
97-
const dependencyMap = { dependencies, devDependencies, peerDependencies };
98-
99-
const dependencyList = Object.keys(dependencyMap).reduce(
100-
(result, value, key) => {
101-
const selectedDepndency = dependencyMap[value];
102-
if (selectedDepndency && this.checkDependencyLength(selectedDepndency)) {
103-
result.push(<DependencyBlock dependencies={selectedDepndency} key={key} title={value} />);
104-
}
105-
return result;
106-
},
107-
[] as JSX.Element[]
108-
);
109-
110-
if (dependencyList.length) {
111-
return <Fragment>{dependencyList}</Fragment>;
112-
}
113-
return <NoItems className="no-dependencies" text={`${name} has no dependencies.`} />;
114-
}
115-
}
75+
return <NoItems className="no-dependencies" text={`${name} has no dependencies.`} />;
76+
};
11677

11778
export default Dependencies;

types/packageMeta.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,7 @@ export interface Author {
4848
url?: string;
4949
avatar?: string;
5050
}
51+
52+
export interface PackageDependencies {
53+
[key: string]: string;
54+
}

yarn.lock

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1676,10 +1676,10 @@
16761676
dependencies:
16771677
"@types/react" "*"
16781678

1679-
"@types/react-router-dom@4.3.5":
1680-
version "4.3.5"
1681-
resolved "https://registry.verdaccio.org/@types%2freact-router-dom/-/react-router-dom-4.3.5.tgz#72f229967690c890d00f96e6b85e9ee5780db31f"
1682-
integrity sha512-eFajSUASYbPHg2BDM1G8Btx+YqGgvROPIg6sBhl3O4kbDdYXdFdfrgQFf/pcBuQVObjfT9AL/dd15jilR5DIEA==
1679+
"@types/react-router-dom@5.1.0":
1680+
version "5.1.0"
1681+
resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.1.0.tgz#8baa84a7fa8c8e7797fb3650ca51f93038cb4caf"
1682+
integrity sha512-YCh8r71pL5p8qDwQf59IU13hFy/41fDQG/GeOI3y+xmD4o0w3vEPxE8uBe+dvOgMoDl0W1WUZsWH0pxc1mcZyQ==
16831683
dependencies:
16841684
"@types/history" "*"
16851685
"@types/react" "*"
@@ -10946,23 +10946,23 @@ react-lifecycles-compat@^3.0.4:
1094610946
resolved "https://registry.verdaccio.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
1094710947
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
1094810948

10949-
react-router-dom@5.0.1:
10950-
version "5.0.1"
10951-
resolved "https://registry.verdaccio.org/react-router-dom/-/react-router-dom-5.0.1.tgz#ee66f4a5d18b6089c361958e443489d6bab714be"
10952-
integrity sha512-zaVHSy7NN0G91/Bz9GD4owex5+eop+KvgbxXsP/O+iW1/Ln+BrJ8QiIR5a6xNPtrdTvLkxqlDClx13QO1uB8CA==
10949+
react-router-dom@5.1.2:
10950+
version "5.1.2"
10951+
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.1.2.tgz#06701b834352f44d37fbb6311f870f84c76b9c18"
10952+
integrity sha512-7BPHAaIwWpZS074UKaw1FjVdZBSVWEk8IuDXdB+OkLb8vd/WRQIpA4ag9WQk61aEfQs47wHyjWUoUGGZxpQXew==
1095310953
dependencies:
1095410954
"@babel/runtime" "^7.1.2"
1095510955
history "^4.9.0"
1095610956
loose-envify "^1.3.1"
1095710957
prop-types "^15.6.2"
10958-
react-router "5.0.1"
10958+
react-router "5.1.2"
1095910959
tiny-invariant "^1.0.2"
1096010960
tiny-warning "^1.0.0"
1096110961

10962-
react-router@5.0.1:
10963-
version "5.0.1"
10964-
resolved "https://registry.verdaccio.org/react-router/-/react-router-5.0.1.tgz#04ee77df1d1ab6cb8939f9f01ad5702dbadb8b0f"
10965-
integrity sha512-EM7suCPNKb1NxcTZ2LEOWFtQBQRQXecLxVpdsP4DW4PbbqYWeRiLyV/Tt1SdCrvT2jcyXAXmVTmzvSzrPR63Bg==
10962+
react-router@5.1.2:
10963+
version "5.1.2"
10964+
resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.1.2.tgz#6ea51d789cb36a6be1ba5f7c0d48dd9e817d3418"
10965+
integrity sha512-yjEuMFy1ONK246B+rsa0cUam5OeAQ8pyclRDgpxuSCrAlJ1qN9uZ5IgyKC7gQg0w8OM50NXHEegPh/ks9YuR2A==
1096610966
dependencies:
1096710967
"@babel/runtime" "^7.1.2"
1096810968
history "^4.9.0"

0 commit comments

Comments
 (0)