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

View File

@ -1,90 +1,46 @@
import React, { ReactElement } from 'react'; import React, { useContext } from 'react';
import List from '@material-ui/core/List';
import { Link as RouterLink } from 'react-router-dom'; import { DetailContext } from '../../pages/Version';
import Link from '@material-ui/core/Link';
import ListItem from '@material-ui/core/ListItem'; 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 { DIST_TAGS } from '../../../lib/constants';
import { Heading, Spacer, ListItemText } from './styles';
export const NOT_AVAILABLE = 'Not available'; export const NOT_AVAILABLE = 'Not available';
export const LABEL_CURRENT_TAGS = 'Current Tags'; export const LABEL_CURRENT_TAGS = 'Current Tags';
export const LABEL_VERSION_HISTORY = 'Version History'; export const LABEL_VERSION_HISTORY = 'Version History';
class Versions extends React.PureComponent { const Versions: React.FC = () => {
public render(): ReactElement<HTMLDivElement> { const detailContext = useContext(DetailContext);
return (
<DetailContextConsumer> const { packageMeta, packageName } = detailContext;
{context => {
const { packageMeta, packageName } = context;
if (!packageMeta) { if (!packageMeta) {
return null; return null;
} }
return this.renderContent(packageMeta, packageName); // @ts-ignore - Property 'dist-tags' does not exist on type 'PackageMetaInterface'
}} const { versions = {}, time = {}, [DIST_TAGS]: distTags = {} } = packageMeta;
</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;
return ( return (
<> <>
{distTags && ( {distTags && Object.keys(distTags).length > 0 && (
<> <>
<Heading variant="subtitle1">{LABEL_CURRENT_TAGS}</Heading> <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> <Heading variant="subtitle1">{LABEL_VERSION_HISTORY}</Heading>
{this.renderPackageList(versions, timeMap, packageName)} <VersionsHistoryList packageName={packageName} time={time} versions={versions} />
</> </>
)} )}
</> </>
); );
} };
}
export default 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)', borderBottom: '1px dotted rgba(0, 0, 0, 0.2)',
whiteSpace: 'nowrap', whiteSpace: 'nowrap',
height: '0.5em', height: '0.5em',
margin: '0 16px',
}); });
export const ListItemText = styled(MuiListItemText)({ export const ListItemText = styled(MuiListItemText)({

View File

@ -1,4 +1,7 @@
export interface PackageMetaInterface { export interface PackageMetaInterface {
versions?: Versions;
distTags?: DistTags;
time?: Time;
latest: { latest: {
name: string; name: string;
dist: { dist: {
@ -14,3 +17,37 @@ interface LicenseInterface {
type: string; type: string;
url: 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;
}