diff --git a/package.json b/package.json index 0bb6ec8..605286e 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "@types/node": "12.7.8", "@types/react": "16.9.2", "@types/react-dom": "16.9.0", - "@types/react-router-dom": "4.3.5", + "@types/react-router-dom": "5.1.0", "@types/validator": "10.11.3", "@typescript-eslint/parser": "2.3.2", "@verdaccio/babel-preset": "2.0.0", @@ -85,10 +85,9 @@ "react": "16.10.0", "react-autosuggest": "9.4.3", "react-dom": "16.10.0", + "react-router-dom": "5.1.2", "react-emotion": "9.2.12", "react-hot-loader": "4.12.11", - "react-router": "5.0.1", - "react-router-dom": "5.0.1", "resolve-url-loader": "3.1.0", "rimraf": "3.0.0", "source-map-loader": "0.2.4", diff --git a/src/components/Dependencies/Dependencies.test.tsx b/src/components/Dependencies/Dependencies.test.tsx new file mode 100644 index 0000000..3f26ccf --- /dev/null +++ b/src/components/Dependencies/Dependencies.test.tsx @@ -0,0 +1,92 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import { HashRouter } from 'react-router-dom'; + +import { DetailContextProvider } from '../../pages/Version'; + +import Dependencies from './Dependencies'; + +describe(' component', () => { + test('Renders a message when there are no dependencies', () => { + // Given + const packageMeta = { + latest: { + name: 'verdaccio', + version: '4.0.0', + author: { + name: 'verdaccio user', + email: 'verdaccio.user@verdaccio.org', + url: '', + avatar: 'https://www.gravatar.com/avatar/000000', + }, + dist: { fileCount: 0, unpackedSize: 0 }, + dependencies: {}, + devDependencies: {}, + peerDependencies: {}, + }, + _uplinks: {}, + }; + + // When + const { getByText } = render( + + + + ); + + // Then + expect(getByText('verdaccio has no dependencies.')).toBeDefined(); + }); + + test('Renders a link to each dependency', () => { + // Given + const packageMeta = { + latest: { + name: 'verdaccio', + version: '4.0.0', + author: { + name: 'verdaccio user', + email: 'verdaccio.user@verdaccio.org', + url: '', + avatar: 'https://www.gravatar.com/avatar/000000', + }, + dist: { fileCount: 0, unpackedSize: 0 }, + dependencies: { + react: '16.9.0', + 'react-dom': '16.9.0', + }, + devDependencies: { + 'babel-core': '7.0.0-beta6', + }, + peerDependencies: { + 'styled-components': '5.0.0', + }, + }, + _uplinks: {}, + }; + + // When + const { getByText } = render( + + + + + + ); + + // Then + // FIXME: currently MaterialUI chips do not support the children + // prop, therefore it is impossible to use proper links for + // dependencies. Those are for now clickable spans + + expect(getByText('dependencies (2)')).toBeDefined(); + expect(getByText('react@16.9.0').tagName).toBe('SPAN'); + expect(getByText('react-dom@16.9.0').tagName).toBe('SPAN'); + + expect(getByText('devDependencies (1)')).toBeDefined(); + expect(getByText('babel-core@7.0.0-beta6').tagName).toBe('SPAN'); + + expect(getByText('peerDependencies (1)')).toBeDefined(); + expect(getByText('styled-components@5.0.0').tagName).toBe('SPAN'); + }); +}); diff --git a/src/components/Dependencies/Dependencies.tsx b/src/components/Dependencies/Dependencies.tsx index 33011ef..a11a6fc 100644 --- a/src/components/Dependencies/Dependencies.tsx +++ b/src/components/Dependencies/Dependencies.tsx @@ -1,117 +1,78 @@ -import React, { Component, Fragment, ReactElement } from 'react'; -import { withRouter, RouteComponentProps } from 'react-router-dom'; +import React, { useContext } from 'react'; +import { useHistory } from 'react-router-dom'; import CardContent from '@material-ui/core/CardContent'; -import { DetailContextConsumer, VersionPageConsumerProps } from '../../pages/Version'; +import { PackageDependencies } from '../../../types/packageMeta'; +import { DetailContext } from '../../pages/Version'; import NoItems from '../NoItems'; import { CardWrap, Heading, Tags, Tag } from './styles'; -type DepDetailProps = { - name: string; - version: string; - onLoading?: () => void; -} & RouteComponentProps; - -interface DepDetailState { - name: string; - version: string; +interface DependencyBlockProps { + title: string; + dependencies: PackageDependencies; } -class DepDetail extends Component { - constructor(props: DepDetailProps) { - super(props); - const { name, version } = this.props; +const DependencyBlock: React.FC = ({ title, dependencies }) => { + const { enableLoading } = useContext(DetailContext); + const history = useHistory(); - this.state = { - name, - version, - }; - } + const deps = Object.entries(dependencies); - public render(): ReactElement { - const { name, version } = this.state; - const tagText = `${name}@${version}`; - return ; - } + function handleClick(name: string): void { + enableLoading && enableLoading(); - private handleOnClick = () => { - const { name } = this.state; - const { onLoading, history } = this.props; - - onLoading && onLoading(); history.push(`/-/web/detail/${name}`); - }; + } + + return ( + + + {`${title} (${deps.length})`} + + {deps.map(([name, version]) => ( + // eslint-disable-next-line + handleClick(name)} /> + ))} + + + + ); +}; + +function hasKeys(object?: { [key: string]: any }): boolean { + return !!object && Object.keys(object).length > 0; } -const WrapperDependencyDetail = withRouter(DepDetail); +const Dependencies: React.FC<{}> = () => { + const { packageMeta } = useContext(DetailContext); -class DependencyBlock extends Component<{ title: string; dependencies: [] }> { - public render(): ReactElement { - const { dependencies, title } = this.props; - const deps = Object.entries(dependencies) as []; + if (!packageMeta) { + throw new Error('packageMeta is required at DetailContext'); + } + const { latest } = packageMeta; + // FIXME: add dependencies to package meta type + // @ts-ignore + const { dependencies, devDependencies, peerDependencies, name } = latest; + const dependencyMap = { dependencies, devDependencies, peerDependencies }; + const hasDependencies = hasKeys(dependencies) || hasKeys(devDependencies) || hasKeys(peerDependencies); + + if (hasDependencies) { return ( - - {({ enableLoading }) => { - return ( - - - {`${title} (${deps.length})`} - {this.renderTags(deps, enableLoading)} - - - ); - }} - + <> + {Object.entries(dependencyMap).map(([dependencyType, dependencies]) => { + if (!dependencies || Object.keys(dependencies).length === 0) { + return null; + } + + return ; + })} + ); } - private renderTags = (deps: [], enableLoading?: () => void) => - deps.map(dep => { - const [name, version] = dep as [string, string]; - - return ; - }); -} - -class Dependencies extends Component { - public render(): ReactElement { - return ( - - {packageMeta => { - return this.renderDependencies(packageMeta as VersionPageConsumerProps); - }} - - ); - } - - private checkDependencyLength(dependency: Record = {}): boolean { - return Object.keys(dependency).length > 0; - } - - private renderDependencies({ packageMeta }): ReactElement { - const { latest } = packageMeta; - const { dependencies, devDependencies, peerDependencies, name } = latest; - - const dependencyMap = { dependencies, devDependencies, peerDependencies }; - - const dependencyList = Object.keys(dependencyMap).reduce( - (result, value, key) => { - const selectedDepndency = dependencyMap[value]; - if (selectedDepndency && this.checkDependencyLength(selectedDepndency)) { - result.push(); - } - return result; - }, - [] as JSX.Element[] - ); - - if (dependencyList.length) { - return {dependencyList}; - } - return ; - } -} + return ; +}; export default Dependencies; diff --git a/types/packageMeta.ts b/types/packageMeta.ts index 67cb902..6528eef 100644 --- a/types/packageMeta.ts +++ b/types/packageMeta.ts @@ -48,3 +48,7 @@ export interface Author { url?: string; avatar?: string; } + +export interface PackageDependencies { + [key: string]: string; +} diff --git a/yarn.lock b/yarn.lock index 390461a..3b14c1f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1676,10 +1676,10 @@ dependencies: "@types/react" "*" -"@types/react-router-dom@4.3.5": - version "4.3.5" - resolved "https://registry.verdaccio.org/@types%2freact-router-dom/-/react-router-dom-4.3.5.tgz#72f229967690c890d00f96e6b85e9ee5780db31f" - integrity sha512-eFajSUASYbPHg2BDM1G8Btx+YqGgvROPIg6sBhl3O4kbDdYXdFdfrgQFf/pcBuQVObjfT9AL/dd15jilR5DIEA== +"@types/react-router-dom@5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.1.0.tgz#8baa84a7fa8c8e7797fb3650ca51f93038cb4caf" + integrity sha512-YCh8r71pL5p8qDwQf59IU13hFy/41fDQG/GeOI3y+xmD4o0w3vEPxE8uBe+dvOgMoDl0W1WUZsWH0pxc1mcZyQ== dependencies: "@types/history" "*" "@types/react" "*" @@ -10946,23 +10946,23 @@ react-lifecycles-compat@^3.0.4: resolved "https://registry.verdaccio.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== -react-router-dom@5.0.1: - version "5.0.1" - resolved "https://registry.verdaccio.org/react-router-dom/-/react-router-dom-5.0.1.tgz#ee66f4a5d18b6089c361958e443489d6bab714be" - integrity sha512-zaVHSy7NN0G91/Bz9GD4owex5+eop+KvgbxXsP/O+iW1/Ln+BrJ8QiIR5a6xNPtrdTvLkxqlDClx13QO1uB8CA== +react-router-dom@5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.1.2.tgz#06701b834352f44d37fbb6311f870f84c76b9c18" + integrity sha512-7BPHAaIwWpZS074UKaw1FjVdZBSVWEk8IuDXdB+OkLb8vd/WRQIpA4ag9WQk61aEfQs47wHyjWUoUGGZxpQXew== dependencies: "@babel/runtime" "^7.1.2" history "^4.9.0" loose-envify "^1.3.1" prop-types "^15.6.2" - react-router "5.0.1" + react-router "5.1.2" tiny-invariant "^1.0.2" tiny-warning "^1.0.0" -react-router@5.0.1: - version "5.0.1" - resolved "https://registry.verdaccio.org/react-router/-/react-router-5.0.1.tgz#04ee77df1d1ab6cb8939f9f01ad5702dbadb8b0f" - integrity sha512-EM7suCPNKb1NxcTZ2LEOWFtQBQRQXecLxVpdsP4DW4PbbqYWeRiLyV/Tt1SdCrvT2jcyXAXmVTmzvSzrPR63Bg== +react-router@5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.1.2.tgz#6ea51d789cb36a6be1ba5f7c0d48dd9e817d3418" + integrity sha512-yjEuMFy1ONK246B+rsa0cUam5OeAQ8pyclRDgpxuSCrAlJ1qN9uZ5IgyKC7gQg0w8OM50NXHEegPh/ks9YuR2A== dependencies: "@babel/runtime" "^7.1.2" history "^4.9.0"