feat: version Component - Replaced classes by func. comp (#129)

* refactor: replaced classes by func comp

* fix: fixed space margin

* refactor: changed display logic

* fix: fixed types

* fix: fixed Version test

* fix: fixed version style
This commit is contained in:
Priscila Oliveira 2019-10-03 10:27:08 +02:00 committed by Juan Picado @jotadeveloper
parent 1a74c08b5d
commit 1d705da38c
7 changed files with 145 additions and 110 deletions

View File

@ -1,48 +1,39 @@
import React from 'react';
import { mount } from 'enzyme';
import { MemoryRouter } from 'react-router';
import { DetailContext, DetailContextProps } from '../../pages/Version';
import Versions, { LABEL_CURRENT_TAGS, LABEL_VERSION_HISTORY } from './Versions';
import data from './__partials__/data.json';
import { render, cleanup } from '@testing-library/react';
const mockPackageMeta = jest.fn(() => ({
const detailContextValue: Partial<DetailContextProps> = {
packageName: 'foo',
packageMeta: data,
}));
};
jest.mock('../../pages/Version', () => ({
DetailContextConsumer: component => {
return component.children({ ...mockPackageMeta() });
},
}));
const ComponentToBeRendered: React.FC<{ contextValue: Partial<DetailContextProps> }> = ({ contextValue }) => (
<MemoryRouter>
<DetailContext.Provider value={contextValue}>
<Versions />
</DetailContext.Provider>
</MemoryRouter>
);
describe('<Version /> component', () => {
beforeEach(() => {
jest.resetModules();
});
afterEach(() => {
cleanup();
});
// FIXME: this test is not deterministic (writes `N days ago` in the snapshot, where N is random number)
test.skip('should render the component in default state', () => {
const wrapper = mount(
<MemoryRouter>
<Versions />
</MemoryRouter>
);
const wrapper = mount(<ComponentToBeRendered contextValue={detailContextValue} />);
expect(wrapper.html()).toMatchSnapshot();
});
test('should render versions', () => {
const { getByText } = render(
<MemoryRouter>
<Versions />
</MemoryRouter>
);
const { getByText } = render(<ComponentToBeRendered contextValue={detailContextValue} />);
expect(getByText(LABEL_VERSION_HISTORY)).toBeTruthy();
expect(getByText(LABEL_CURRENT_TAGS)).toBeTruthy();
@ -53,18 +44,7 @@ describe('<Version /> component', () => {
});
test('should not render versions', () => {
const request = {
packageName: 'foo',
};
// @ts-ignore
mockPackageMeta.mockImplementation(() => request);
const { queryByText } = render(
<MemoryRouter>
<Versions />
</MemoryRouter>
);
const { queryByText } = render(<ComponentToBeRendered contextValue={{ packageName: detailContextValue.packageName }} />);
expect(queryByText(LABEL_VERSION_HISTORY)).toBeFalsy();
expect(queryByText(LABEL_CURRENT_TAGS)).toBeFalsy();

View File

@ -1,90 +1,46 @@
import React, { ReactElement } from 'react';
import List from '@material-ui/core/List';
import { Link as RouterLink } from 'react-router-dom';
import Link from '@material-ui/core/Link';
import ListItem from '@material-ui/core/ListItem';
import React, { useContext } from 'react';
import { DetailContext } from '../../pages/Version';
import { Heading } from './styles';
import VersionsTagList from './VersionsTagList';
import VersionsHistoryList from './VersionsHistoryList';
import { DetailContextConsumer } from '../../pages/Version';
import { formatDateDistance } from '../../utils/package';
import { DIST_TAGS } from '../../../lib/constants';
import { Heading, Spacer, ListItemText } from './styles';
export const NOT_AVAILABLE = 'Not available';
export const LABEL_CURRENT_TAGS = 'Current Tags';
export const LABEL_VERSION_HISTORY = 'Version History';
class Versions extends React.PureComponent {
public render(): ReactElement<HTMLDivElement> {
return (
<DetailContextConsumer>
{context => {
const { packageMeta, packageName } = context;
const Versions: React.FC = () => {
const detailContext = useContext(DetailContext);
const { packageMeta, packageName } = detailContext;
if (!packageMeta) {
return null;
}
return this.renderContent(packageMeta, packageName);
}}
</DetailContextConsumer>
);
}
public renderPackageList = (packages: {}, timeMap: Record<string, {}>, packageName): ReactElement<HTMLDivElement> => {
return (
<List dense={true}>
{Object.keys(packages)
.reverse()
.map(version => (
<ListItem className="version-item" key={version}>
<Link component={RouterLink} to={`/-/web/detail/${packageName}/v/${version}`}>
<ListItemText>{version}</ListItemText>
</Link>
<Spacer />
<ListItemText>{timeMap[version] ? `${formatDateDistance(timeMap[version])} ago` : NOT_AVAILABLE}</ListItemText>
</ListItem>
))}
</List>
);
};
public renderTagList = (packages: {}): ReactElement<HTMLDivElement> => {
return (
<List dense={true}>
{Object.keys(packages)
.reverse()
.map(tag => (
<ListItem className="version-item" key={tag}>
<ListItemText>{tag}</ListItemText>
<Spacer />
<ListItemText>{packages[tag]}</ListItemText>
</ListItem>
))}
</List>
);
};
public renderContent(packageMeta, packageName): ReactElement<HTMLDivElement> {
const { versions = {}, time: timeMap = {}, [DIST_TAGS]: distTags = {} } = packageMeta;
// @ts-ignore - Property 'dist-tags' does not exist on type 'PackageMetaInterface'
const { versions = {}, time = {}, [DIST_TAGS]: distTags = {} } = packageMeta;
return (
<>
{distTags && (
{distTags && Object.keys(distTags).length > 0 && (
<>
<Heading variant="subtitle1">{LABEL_CURRENT_TAGS}</Heading>
{this.renderTagList(distTags)}
<VersionsTagList tags={distTags} />
</>
)}
{versions && (
{versions && Object.keys(versions).length > 0 && packageName && (
<>
<Heading variant="subtitle1">{LABEL_VERSION_HISTORY}</Heading>
{this.renderPackageList(versions, timeMap, packageName)}
<VersionsHistoryList packageName={packageName} time={time} versions={versions} />
</>
)}
</>
);
}
}
};
export default Versions;

View File

@ -0,0 +1,35 @@
import React from 'react';
import List from '@material-ui/core/List';
import Link from '@material-ui/core/Link';
import ListItem from '@material-ui/core/ListItem';
import { Link as RouterLink } from 'react-router-dom';
import { Spacer, ListItemText } from './styles';
import { Versions, Time } from '../../../types/packageMeta';
import { formatDateDistance } from '../../utils/package';
export const NOT_AVAILABLE = 'Not available';
interface Props {
versions: Versions;
packageName: string;
time: Time;
}
const VersionsHistoryList: React.FC<Props> = ({ versions, packageName, time }) => (
<List dense={true}>
{Object.keys(versions)
.reverse()
.map(version => (
<ListItem className="version-item" key={version}>
<Link component={RouterLink} to={`/-/web/detail/${packageName}/v/${version}`}>
<ListItemText>{version}</ListItemText>
</Link>
<Spacer />
<ListItemText>{time[version] ? `${formatDateDistance(time[version])} ago` : NOT_AVAILABLE}</ListItemText>
</ListItem>
))}
</List>
);
export default VersionsHistoryList;

View File

@ -0,0 +1,26 @@
import React from 'react';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import { Spacer, ListItemText } from './styles';
import { DistTags } from '../../../types/packageMeta';
interface Props {
tags: DistTags;
}
const VersionsTagList: React.FC<Props> = ({ tags }) => (
<List dense={true}>
{Object.keys(tags)
.reverse()
.map(tag => (
<ListItem className="version-item" key={tag}>
<ListItemText>{tag}</ListItemText>
<Spacer />
<ListItemText>{tags[tag]}</ListItemText>
</ListItem>
))}
</List>
);
export default VersionsTagList;

File diff suppressed because one or more lines are too long

View File

@ -14,6 +14,7 @@ export const Spacer = styled('div')({
borderBottom: '1px dotted rgba(0, 0, 0, 0.2)',
whiteSpace: 'nowrap',
height: '0.5em',
margin: '0 16px',
});
export const ListItemText = styled(MuiListItemText)({

View File

@ -1,4 +1,7 @@
export interface PackageMetaInterface {
versions?: Versions;
distTags?: DistTags;
time?: Time;
latest: {
name: string;
dist: {
@ -14,3 +17,37 @@ interface LicenseInterface {
type: string;
url: string;
}
export interface DistTags {
[key: string]: string;
}
export interface Time {
[key: string]: string;
}
export interface Versions {
[key: string]: Version;
}
export interface Version {
name: string;
version: string;
author?: string | Author;
maintainers?: Maintainer[];
description?: string;
license?: string;
main?: string;
keywords?: string[];
}
interface Author {
name?: string;
email?: string;
url?: string;
}
interface Maintainer {
email?: string;
name?: string;
}