forked from sombochea/verdaccio-ui
ActionBar Component - Replaced class by func. comp (#330)
This commit is contained in:
parent
fcad6fa794
commit
742971db0d
@ -1,89 +1,71 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { mount } from '../../utils/test-enzyme';
|
import { render, cleanup } from '../../utils/test-react-testing-library';
|
||||||
import api from '../../utils/api';
|
import { DetailContext, DetailContextProps } from '../../pages/Version';
|
||||||
|
|
||||||
import { ActionBar } from './ActionBar';
|
import ActionBar from './ActionBar';
|
||||||
|
|
||||||
const mockPackageMeta: jest.Mock = jest.fn(() => ({
|
const detailContextValue: DetailContextProps = {
|
||||||
|
packageName: 'foo',
|
||||||
|
readMe: 'test',
|
||||||
|
enableLoading: () => {},
|
||||||
|
isLoading: false,
|
||||||
|
hasNotBeenFound: false,
|
||||||
|
packageMeta: {
|
||||||
|
_uplinks: {},
|
||||||
latest: {
|
latest: {
|
||||||
homepage: 'https://verdaccio.tld',
|
name: '@verdaccio/local-storage',
|
||||||
|
version: '8.0.1-next.1',
|
||||||
|
dist: { fileCount: 0, unpackedSize: 0, tarball: 'http://localhost:8080/bootstrap/-/bootstrap-4.3.1.tgz' },
|
||||||
|
homepage: 'https://verdaccio.org',
|
||||||
bugs: {
|
bugs: {
|
||||||
url: 'https://verdaccio.tld/bugs',
|
url: 'https://github.com/verdaccio/monorepo/issues',
|
||||||
},
|
|
||||||
dist: {
|
|
||||||
tarball: 'https://verdaccio.tld/download',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}));
|
},
|
||||||
|
};
|
||||||
|
|
||||||
jest.mock('../../pages/Version', () => ({
|
const ComponentToBeRendered: React.FC<{ contextValue: DetailContextProps }> = ({ contextValue }) => (
|
||||||
DetailContextConsumer: component => {
|
<DetailContext.Provider value={contextValue}>
|
||||||
return component.children({ packageMeta: mockPackageMeta() });
|
<ActionBar />
|
||||||
},
|
</DetailContext.Provider>
|
||||||
}));
|
);
|
||||||
|
|
||||||
describe('<ActionBar /> component', () => {
|
describe('<ActionBar /> component', () => {
|
||||||
beforeEach(() => {
|
afterEach(() => {
|
||||||
jest.resetModules();
|
cleanup();
|
||||||
jest.resetAllMocks();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should render the component in default state', () => {
|
test('should render the component in default state', () => {
|
||||||
const wrapper = mount(<ActionBar />);
|
const { container } = render(<ComponentToBeRendered contextValue={detailContextValue} />);
|
||||||
expect(wrapper.html()).toMatchSnapshot();
|
expect(container.firstChild).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when there is no action bar data', () => {
|
test('when there is no action bar data', () => {
|
||||||
mockPackageMeta.mockImplementation(() => ({
|
const packageMeta = {
|
||||||
latest: {},
|
...detailContextValue.packageMeta,
|
||||||
}));
|
latest: {
|
||||||
|
...detailContextValue.packageMeta.latest,
|
||||||
|
homepage: undefined,
|
||||||
|
bugs: undefined,
|
||||||
|
dist: {
|
||||||
|
...detailContextValue.packageMeta.latest.dist,
|
||||||
|
tarball: undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const wrapper = mount(<ActionBar />);
|
const { container } = render(<ComponentToBeRendered contextValue={{ ...detailContextValue, packageMeta }} />);
|
||||||
// FIXME: this only renders the DetailContextConsumer, thus
|
expect(container.firstChild).toMatchSnapshot();
|
||||||
// the wrapper will be always empty
|
|
||||||
expect(wrapper.html()).toEqual('');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('when there is no latest property in package meta', () => {
|
|
||||||
mockPackageMeta.mockImplementation(() => ({}));
|
|
||||||
const wrapper = mount(<ActionBar />);
|
|
||||||
expect(wrapper.html()).toEqual('');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when there is a button to download a tarball', () => {
|
test('when there is a button to download a tarball', () => {
|
||||||
mockPackageMeta.mockImplementation(() => ({
|
const { getByTitle } = render(<ComponentToBeRendered contextValue={{ ...detailContextValue }} />);
|
||||||
latest: {
|
expect(getByTitle('Download tarball')).toBeTruthy();
|
||||||
dist: {
|
|
||||||
tarball: 'http://localhost:8080/bootstrap/-/bootstrap-4.3.1.tgz',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const wrapper = mount(<ActionBar />);
|
|
||||||
expect(wrapper.html()).toMatchSnapshot();
|
|
||||||
|
|
||||||
const button = wrapper.find('button');
|
|
||||||
expect(button).toHaveLength(1);
|
|
||||||
|
|
||||||
const spy = jest.spyOn(api, 'request');
|
|
||||||
button.simulate('click');
|
|
||||||
expect(spy).toHaveBeenCalled();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when there is a button to open an issue', () => {
|
test('when there is a button to open an issue', () => {
|
||||||
mockPackageMeta.mockImplementation(() => ({
|
const { getByTitle } = render(<ComponentToBeRendered contextValue={{ ...detailContextValue }} />);
|
||||||
latest: {
|
expect(getByTitle('Open an issue')).toBeTruthy();
|
||||||
bugs: {
|
|
||||||
url: 'https://verdaccio.tld/bugs',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const wrapper = mount(<ActionBar />);
|
|
||||||
expect(wrapper.html()).toMatchSnapshot();
|
|
||||||
|
|
||||||
const button = wrapper.find('button');
|
|
||||||
expect(button).toHaveLength(1);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,133 +1,44 @@
|
|||||||
import React, { Component, ReactElement } from 'react';
|
import React from 'react';
|
||||||
import BugReportIcon from '@material-ui/icons/BugReport';
|
|
||||||
import DownloadIcon from '@material-ui/icons/CloudDownload';
|
|
||||||
import HomeIcon from '@material-ui/icons/Home';
|
|
||||||
|
|
||||||
import { DetailContextConsumer, VersionPageConsumerProps } from '../../pages/Version';
|
import { DetailContext } from '../../pages/Version';
|
||||||
import { isURL, extractFileName, downloadFile } from '../../utils/url';
|
import { isURL } from '../../utils/url';
|
||||||
import api from '../../utils/api';
|
import Box from '../../muiComponents/Box';
|
||||||
import Tooltip from '../../muiComponents/Tooltip';
|
|
||||||
import List from '../../muiComponents/List';
|
|
||||||
|
|
||||||
import { Fab, ActionListItem } from './styles';
|
import ActionBarAction, { ActionBarActionProps } from './ActionBarAction';
|
||||||
|
|
||||||
export interface Action {
|
/* eslint-disable verdaccio/jsx-spread */
|
||||||
icon: string;
|
const ActionBar: React.FC = () => {
|
||||||
title: string;
|
const detailContext = React.useContext(DetailContext);
|
||||||
handler?: Function;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function downloadHandler(link: string): Promise<void> {
|
const { packageMeta } = detailContext;
|
||||||
const fileStream: Blob = await api.request(link, 'GET', {
|
|
||||||
headers: {
|
|
||||||
['accept']:
|
|
||||||
'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
|
|
||||||
},
|
|
||||||
credentials: 'include',
|
|
||||||
});
|
|
||||||
const fileName = extractFileName(link);
|
|
||||||
downloadFile(fileStream, fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
const ACTIONS = {
|
if (!packageMeta?.latest) {
|
||||||
homepage: {
|
return null;
|
||||||
icon: <HomeIcon />,
|
}
|
||||||
title: 'Visit homepage',
|
|
||||||
},
|
const { homepage, bugs, dist } = packageMeta.latest;
|
||||||
issue: {
|
|
||||||
icon: <BugReportIcon />,
|
const actions: Array<ActionBarActionProps> = [];
|
||||||
title: 'Open an issue',
|
|
||||||
},
|
if (homepage && isURL(homepage)) {
|
||||||
tarball: {
|
actions.push({ type: 'VISIT_HOMEPAGE', link: homepage });
|
||||||
icon: <DownloadIcon />,
|
}
|
||||||
title: 'Download tarball',
|
|
||||||
handler: downloadHandler,
|
if (bugs?.url && isURL(bugs.url)) {
|
||||||
},
|
actions.push({ type: 'OPEN_AN_ISSUE', link: bugs.url });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dist?.tarball && isURL(dist.tarball)) {
|
||||||
|
actions.push({ type: 'DOWNLOAD_TARBALL', link: dist.tarball });
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box alignItems="center" display="flex" marginBottom="8px">
|
||||||
|
{actions.map(action => (
|
||||||
|
<ActionBarAction key={action.link} {...action} />
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
class ActionBar extends Component {
|
export default ActionBar;
|
||||||
public render(): ReactElement<HTMLElement> {
|
|
||||||
return (
|
|
||||||
<DetailContextConsumer>
|
|
||||||
{context => {
|
|
||||||
const { packageMeta } = context;
|
|
||||||
|
|
||||||
if (!packageMeta) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.renderActionBar(context as VersionPageConsumerProps);
|
|
||||||
}}
|
|
||||||
</DetailContextConsumer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private renderIconsWithLink(link: string, component: JSX.Element): ReactElement<HTMLElement> {
|
|
||||||
return (
|
|
||||||
<a href={link} target={'_blank'}>
|
|
||||||
{component}
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private renderActionBar = ({ packageMeta }) => {
|
|
||||||
const { latest } = packageMeta;
|
|
||||||
|
|
||||||
if (!latest) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { homepage, bugs, dist } = latest;
|
|
||||||
|
|
||||||
const actionsMap = {
|
|
||||||
homepage,
|
|
||||||
issue: bugs ? bugs.url : null,
|
|
||||||
tarball: dist ? dist.tarball : null,
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderList = Object.keys(actionsMap).reduce((component: React.ReactElement[], value, key) => {
|
|
||||||
const link = actionsMap[value];
|
|
||||||
if (link && isURL(link)) {
|
|
||||||
const actionItem: Action = ACTIONS[value];
|
|
||||||
if (actionItem.handler) {
|
|
||||||
const fab = (
|
|
||||||
<Tooltip key={key} title={actionItem['title']}>
|
|
||||||
<Fab
|
|
||||||
/* eslint-disable react/jsx-no-bind */
|
|
||||||
onClick={() => {
|
|
||||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
||||||
actionItem.handler!(link);
|
|
||||||
}}
|
|
||||||
size={'small'}>
|
|
||||||
{actionItem['icon']}
|
|
||||||
</Fab>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
component.push(fab);
|
|
||||||
} else {
|
|
||||||
const fab = <Fab size={'small'}>{actionItem['icon']}</Fab>;
|
|
||||||
component.push(
|
|
||||||
<Tooltip key={key} title={actionItem['title']}>
|
|
||||||
<>{this.renderIconsWithLink(link, fab)}</>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return component;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
if (renderList.length > 0) {
|
|
||||||
return (
|
|
||||||
<List>
|
|
||||||
<ActionListItem alignItems={'flex-start'} button={true}>
|
|
||||||
{renderList}
|
|
||||||
</ActionListItem>
|
|
||||||
</List>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export { ActionBar };
|
|
||||||
|
62
src/components/ActionBar/ActionBarAction.tsx
Normal file
62
src/components/ActionBar/ActionBarAction.tsx
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
import BugReportIcon from '@material-ui/icons/BugReport';
|
||||||
|
import DownloadIcon from '@material-ui/icons/CloudDownload';
|
||||||
|
import HomeIcon from '@material-ui/icons/Home';
|
||||||
|
|
||||||
|
import Tooltip from '../../muiComponents/Tooltip';
|
||||||
|
import Link from '../Link';
|
||||||
|
import FloatingActionButton from '../../muiComponents/FloatingActionButton';
|
||||||
|
import { Theme } from '../../design-tokens/theme';
|
||||||
|
|
||||||
|
import downloadTarball from './download-tarball';
|
||||||
|
|
||||||
|
export const Fab = styled(FloatingActionButton)<{ theme?: Theme }>(props => ({
|
||||||
|
backgroundColor: props.theme && props.theme.palette.primary.main,
|
||||||
|
color: props.theme && props.theme.palette.white,
|
||||||
|
marginRight: 10,
|
||||||
|
}));
|
||||||
|
|
||||||
|
type ActionType = 'VISIT_HOMEPAGE' | 'OPEN_AN_ISSUE' | 'DOWNLOAD_TARBALL';
|
||||||
|
|
||||||
|
export interface ActionBarActionProps {
|
||||||
|
type: ActionType;
|
||||||
|
link: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* eslint-disable react/jsx-no-bind */
|
||||||
|
/* eslint-disable react/jsx-max-depth */
|
||||||
|
const ActionBarAction: React.FC<ActionBarActionProps> = ({ type, link }) => {
|
||||||
|
switch (type) {
|
||||||
|
case 'VISIT_HOMEPAGE':
|
||||||
|
return (
|
||||||
|
<Tooltip title="Visit homepage">
|
||||||
|
<Link external={true} to={link}>
|
||||||
|
<Fab size="small">
|
||||||
|
<HomeIcon />
|
||||||
|
</Fab>
|
||||||
|
</Link>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
case 'OPEN_AN_ISSUE':
|
||||||
|
return (
|
||||||
|
<Tooltip title="Open an issue">
|
||||||
|
<Link external={true} to={link}>
|
||||||
|
<Fab size="small">
|
||||||
|
<BugReportIcon />
|
||||||
|
</Fab>
|
||||||
|
</Link>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
case 'DOWNLOAD_TARBALL':
|
||||||
|
return (
|
||||||
|
<Tooltip title="Download tarball">
|
||||||
|
<Fab data-testid="download-tarball-btn" onClick={downloadTarball(link)} size="small">
|
||||||
|
<DownloadIcon />
|
||||||
|
</Fab>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ActionBarAction;
|
@ -1,7 +1,118 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`<ActionBar /> component should render the component in default state 1`] = `""`;
|
exports[`<ActionBar /> component should render the component in default state 1`] = `
|
||||||
|
.emotion-0 {
|
||||||
|
background-color: #4b5e40;
|
||||||
|
color: #fff;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
exports[`<ActionBar /> component when there is a button to download a tarball 1`] = `"<ul class=\\"MuiList-root MuiList-padding\\"><div class=\\"MuiButtonBase-root MuiListItem-root css-l3mdff-ActionListItem eux6shq0 MuiListItem-gutters MuiListItem-button MuiListItem-alignItemsFlexStart\\" tabindex=\\"0\\" role=\\"button\\" aria-disabled=\\"false\\"><button class=\\"MuiButtonBase-root MuiFab-root css-is03ew-Fab eux6shq1 MuiFab-sizeSmall\\" tabindex=\\"0\\" type=\\"button\\" title=\\"Download tarball\\"><span class=\\"MuiFab-label\\"><svg class=\\"MuiSvgIcon-root\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\" role=\\"presentation\\"><path d=\\"M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM17 13l-5 5-5-5h3V9h4v4h3z\\"></path></svg></span><span class=\\"MuiTouchRipple-root\\"></span></button><span class=\\"MuiTouchRipple-root\\"></span></div></ul>"`;
|
<div
|
||||||
|
class="MuiBox-root MuiBox-root-2"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
class=""
|
||||||
|
href="https://verdaccio.org"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
title="Visit homepage"
|
||||||
|
>
|
||||||
|
<h6
|
||||||
|
class="MuiTypography-root MuiTypography-subtitle1"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="MuiButtonBase-root MuiFab-root emotion-0 emotion-1 MuiFab-sizeSmall"
|
||||||
|
tabindex="0"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="MuiFab-label"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="MuiSvgIcon-root"
|
||||||
|
focusable="false"
|
||||||
|
role="presentation"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="MuiTouchRipple-root"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</h6>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
class=""
|
||||||
|
href="https://github.com/verdaccio/monorepo/issues"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
title="Open an issue"
|
||||||
|
>
|
||||||
|
<h6
|
||||||
|
class="MuiTypography-root MuiTypography-subtitle1"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="MuiButtonBase-root MuiFab-root emotion-0 emotion-1 MuiFab-sizeSmall"
|
||||||
|
tabindex="0"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="MuiFab-label"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="MuiSvgIcon-root"
|
||||||
|
focusable="false"
|
||||||
|
role="presentation"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M20 8h-2.81c-.45-.78-1.07-1.45-1.82-1.96L17 4.41 15.59 3l-2.17 2.17C12.96 5.06 12.49 5 12 5c-.49 0-.96.06-1.41.17L8.41 3 7 4.41l1.62 1.63C7.88 6.55 7.26 7.22 6.81 8H4v2h2.09c-.05.33-.09.66-.09 1v1H4v2h2v1c0 .34.04.67.09 1H4v2h2.81c1.04 1.79 2.97 3 5.19 3s4.15-1.21 5.19-3H20v-2h-2.09c.05-.33.09-.66.09-1v-1h2v-2h-2v-1c0-.34-.04-.67-.09-1H20V8zm-6 8h-4v-2h4v2zm0-4h-4v-2h4v2z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="MuiTouchRipple-root"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</h6>
|
||||||
|
</a>
|
||||||
|
<button
|
||||||
|
class="MuiButtonBase-root MuiFab-root emotion-0 emotion-1 MuiFab-sizeSmall"
|
||||||
|
data-testid="download-tarball-btn"
|
||||||
|
tabindex="0"
|
||||||
|
title="Download tarball"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="MuiFab-label"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="MuiSvgIcon-root"
|
||||||
|
focusable="false"
|
||||||
|
role="presentation"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM17 13l-5 5-5-5h3V9h4v4h3z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="MuiTouchRipple-root"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`<ActionBar /> component when there is a button to open an issue 1`] = `"<ul class=\\"MuiList-root MuiList-padding\\"><div class=\\"MuiButtonBase-root MuiListItem-root css-l3mdff-ActionListItem eux6shq0 MuiListItem-gutters MuiListItem-button MuiListItem-alignItemsFlexStart\\" tabindex=\\"0\\" role=\\"button\\" aria-disabled=\\"false\\"><a href=\\"https://verdaccio.tld/bugs\\" target=\\"_blank\\"><button class=\\"MuiButtonBase-root MuiFab-root css-is03ew-Fab eux6shq1 MuiFab-sizeSmall\\" tabindex=\\"0\\" type=\\"button\\"><span class=\\"MuiFab-label\\"><svg class=\\"MuiSvgIcon-root\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\" role=\\"presentation\\"><path d=\\"M20 8h-2.81c-.45-.78-1.07-1.45-1.82-1.96L17 4.41 15.59 3l-2.17 2.17C12.96 5.06 12.49 5 12 5c-.49 0-.96.06-1.41.17L8.41 3 7 4.41l1.62 1.63C7.88 6.55 7.26 7.22 6.81 8H4v2h2.09c-.05.33-.09.66-.09 1v1H4v2h2v1c0 .34.04.67.09 1H4v2h2.81c1.04 1.79 2.97 3 5.19 3s4.15-1.21 5.19-3H20v-2h-2.09c.05-.33.09-.66.09-1v-1h2v-2h-2v-1c0-.34-.04-.67-.09-1H20V8zm-6 8h-4v-2h4v2zm0-4h-4v-2h4v2z\\"></path></svg></span><span class=\\"MuiTouchRipple-root\\"></span></button></a><span class=\\"MuiTouchRipple-root\\"></span></div></ul>"`;
|
exports[`<ActionBar /> component when there is no action bar data 1`] = `
|
||||||
|
<div
|
||||||
|
class="MuiBox-root MuiBox-root-74"
|
||||||
|
/>
|
||||||
|
`;
|
||||||
|
18
src/components/ActionBar/download-tarball.ts
Normal file
18
src/components/ActionBar/download-tarball.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import api from '../../utils/api';
|
||||||
|
import { extractFileName, downloadFile } from '../../utils/url';
|
||||||
|
|
||||||
|
function downloadTarball(link: string) {
|
||||||
|
return async function downloadHandler(): Promise<void> {
|
||||||
|
const fileStream: Blob = await api.request(link, 'GET', {
|
||||||
|
headers: {
|
||||||
|
['accept']:
|
||||||
|
'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
|
||||||
|
},
|
||||||
|
credentials: 'include',
|
||||||
|
});
|
||||||
|
const fileName = extractFileName(link);
|
||||||
|
downloadFile(fileStream, fileName);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default downloadTarball;
|
@ -1 +1,2 @@
|
|||||||
export { default } from './ActionBar';
|
export { default } from './ActionBar';
|
||||||
|
export { default as downloadTarball } from './download-tarball';
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
import styled from '@emotion/styled';
|
|
||||||
|
|
||||||
import ListItem from '../../muiComponents/ListItem';
|
|
||||||
import FloatingActionButton from '../../muiComponents/FloatingActionButton';
|
|
||||||
import { Theme } from '../../design-tokens/theme';
|
|
||||||
|
|
||||||
export const ActionListItem = styled(ListItem)({
|
|
||||||
paddingTop: 0,
|
|
||||||
paddingLeft: 0,
|
|
||||||
paddingRight: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const Fab = styled(FloatingActionButton)<{ theme?: Theme }>(props => ({
|
|
||||||
backgroundColor: props.theme && props.theme.palette.primary.main,
|
|
||||||
color: props.theme && props.theme.palette.white,
|
|
||||||
marginRight: '10px',
|
|
||||||
}));
|
|
@ -1,6 +1,6 @@
|
|||||||
import React, { ReactElement } from 'react';
|
import React, { ReactElement } from 'react';
|
||||||
|
|
||||||
import { ActionBar } from '../ActionBar/ActionBar';
|
import ActionBar from '../ActionBar';
|
||||||
import Author from '../Author';
|
import Author from '../Author';
|
||||||
import Developers from '../Developers';
|
import Developers from '../Developers';
|
||||||
import Dist from '../Dist/Dist';
|
import Dist from '../Dist/Dist';
|
||||||
|
@ -7,20 +7,26 @@ interface Props extends Pick<TextProps, 'variant'> {
|
|||||||
external?: boolean;
|
external?: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
to: string;
|
to: string;
|
||||||
|
children?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LinkRef = HTMLAnchorElement;
|
||||||
|
|
||||||
/* eslint-disable verdaccio/jsx-spread */
|
/* eslint-disable verdaccio/jsx-spread */
|
||||||
const Link: React.FC<Props> = ({ external, to, children, variant, className, ...props }) => {
|
const Link = React.forwardRef<LinkRef, Props>(function Link(
|
||||||
|
{ external, to, children, variant, className, ...props },
|
||||||
|
ref
|
||||||
|
) {
|
||||||
const LinkTextContent = <Text variant={variant}>{children}</Text>;
|
const LinkTextContent = <Text variant={variant}>{children}</Text>;
|
||||||
return external ? (
|
return external ? (
|
||||||
<a className={className} href={to} rel="noopener noreferrer" target="_blank" {...props}>
|
<a className={className} href={to} ref={ref} rel="noopener noreferrer" target="_blank" {...props}>
|
||||||
{LinkTextContent}
|
{LinkTextContent}
|
||||||
</a>
|
</a>
|
||||||
) : (
|
) : (
|
||||||
<RouterLink className={className} to={to} {...props}>
|
<RouterLink className={className} innerRef={ref} to={to} {...props}>
|
||||||
{LinkTextContent}
|
{LinkTextContent}
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
export default Link;
|
export default Link;
|
||||||
|
@ -9,7 +9,7 @@ import fileSizeSI from '../../utils/file-size';
|
|||||||
import { formatDate, formatDateDistance } from '../../utils/package';
|
import { formatDate, formatDateDistance } from '../../utils/package';
|
||||||
import Tooltip from '../../muiComponents/Tooltip';
|
import Tooltip from '../../muiComponents/Tooltip';
|
||||||
import { isURL } from '../../utils/url';
|
import { isURL } from '../../utils/url';
|
||||||
import { downloadHandler } from '../ActionBar/ActionBar';
|
import { downloadTarball } from '../ActionBar';
|
||||||
import ListItem from '../../muiComponents/ListItem';
|
import ListItem from '../../muiComponents/ListItem';
|
||||||
import Grid from '../../muiComponents/Grid';
|
import Grid from '../../muiComponents/Grid';
|
||||||
|
|
||||||
@ -140,7 +140,7 @@ const Package: React.FC<PackageInterface> = ({
|
|||||||
dist.tarball &&
|
dist.tarball &&
|
||||||
isURL(dist.tarball) && (
|
isURL(dist.tarball) && (
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
<a onClick={() => downloadHandler(dist.tarball.replace(`https://registry.npmjs.org/`, window.location.href))} target={'_blank'}>
|
<a onClick={downloadTarball(dist.tarball.replace(`https://registry.npmjs.org/`, window.location.href))} target={'_blank'}>
|
||||||
<Tooltip aria-label={'Download the tar file'} title={'Download tarball'}>
|
<Tooltip aria-label={'Download the tar file'} title={'Download tarball'}>
|
||||||
<IconButton aria-label={'Download'}>
|
<IconButton aria-label={'Download'}>
|
||||||
{/* eslint-disable-next-line react/jsx-max-depth */}
|
{/* eslint-disable-next-line react/jsx-max-depth */}
|
||||||
|
@ -8,6 +8,7 @@ export interface PackageMetaInterface {
|
|||||||
dist: {
|
dist: {
|
||||||
fileCount: number;
|
fileCount: number;
|
||||||
unpackedSize: number;
|
unpackedSize: number;
|
||||||
|
tarball?: string;
|
||||||
};
|
};
|
||||||
engines?: {
|
engines?: {
|
||||||
node?: string;
|
node?: string;
|
||||||
@ -15,6 +16,10 @@ export interface PackageMetaInterface {
|
|||||||
};
|
};
|
||||||
license?: Partial<LicenseInterface> | string;
|
license?: Partial<LicenseInterface> | string;
|
||||||
version: string;
|
version: string;
|
||||||
|
homepage?: string;
|
||||||
|
bugs?: {
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
repository?: {
|
repository?: {
|
||||||
type?: string;
|
type?: string;
|
||||||
url?: string;
|
url?: string;
|
||||||
|
Loading…
Reference in New Issue
Block a user