mirror of
https://github.com/SomboChea/ui
synced 2024-11-24 06:54:27 +07:00
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 { mount } from '../../utils/test-enzyme';
|
||||
import api from '../../utils/api';
|
||||
import { render, cleanup } from '../../utils/test-react-testing-library';
|
||||
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: {
|
||||
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: {
|
||||
url: 'https://verdaccio.tld/bugs',
|
||||
},
|
||||
dist: {
|
||||
tarball: 'https://verdaccio.tld/download',
|
||||
url: 'https://github.com/verdaccio/monorepo/issues',
|
||||
},
|
||||
},
|
||||
}));
|
||||
},
|
||||
};
|
||||
|
||||
jest.mock('../../pages/Version', () => ({
|
||||
DetailContextConsumer: component => {
|
||||
return component.children({ packageMeta: mockPackageMeta() });
|
||||
},
|
||||
}));
|
||||
const ComponentToBeRendered: React.FC<{ contextValue: DetailContextProps }> = ({ contextValue }) => (
|
||||
<DetailContext.Provider value={contextValue}>
|
||||
<ActionBar />
|
||||
</DetailContext.Provider>
|
||||
);
|
||||
|
||||
describe('<ActionBar /> component', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
jest.resetAllMocks();
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
test('should render the component in default state', () => {
|
||||
const wrapper = mount(<ActionBar />);
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
const { container } = render(<ComponentToBeRendered contextValue={detailContextValue} />);
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('when there is no action bar data', () => {
|
||||
mockPackageMeta.mockImplementation(() => ({
|
||||
latest: {},
|
||||
}));
|
||||
const packageMeta = {
|
||||
...detailContextValue.packageMeta,
|
||||
latest: {
|
||||
...detailContextValue.packageMeta.latest,
|
||||
homepage: undefined,
|
||||
bugs: undefined,
|
||||
dist: {
|
||||
...detailContextValue.packageMeta.latest.dist,
|
||||
tarball: undefined,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const wrapper = mount(<ActionBar />);
|
||||
// FIXME: this only renders the DetailContextConsumer, thus
|
||||
// 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('');
|
||||
const { container } = render(<ComponentToBeRendered contextValue={{ ...detailContextValue, packageMeta }} />);
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('when there is a button to download a tarball', () => {
|
||||
mockPackageMeta.mockImplementation(() => ({
|
||||
latest: {
|
||||
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();
|
||||
const { getByTitle } = render(<ComponentToBeRendered contextValue={{ ...detailContextValue }} />);
|
||||
expect(getByTitle('Download tarball')).toBeTruthy();
|
||||
});
|
||||
|
||||
test('when there is a button to open an issue', () => {
|
||||
mockPackageMeta.mockImplementation(() => ({
|
||||
latest: {
|
||||
bugs: {
|
||||
url: 'https://verdaccio.tld/bugs',
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
const wrapper = mount(<ActionBar />);
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
|
||||
const button = wrapper.find('button');
|
||||
expect(button).toHaveLength(1);
|
||||
const { getByTitle } = render(<ComponentToBeRendered contextValue={{ ...detailContextValue }} />);
|
||||
expect(getByTitle('Open an issue')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
@ -1,133 +1,44 @@
|
||||
import React, { Component, ReactElement } from 'react';
|
||||
import BugReportIcon from '@material-ui/icons/BugReport';
|
||||
import DownloadIcon from '@material-ui/icons/CloudDownload';
|
||||
import HomeIcon from '@material-ui/icons/Home';
|
||||
import React from 'react';
|
||||
|
||||
import { DetailContextConsumer, VersionPageConsumerProps } from '../../pages/Version';
|
||||
import { isURL, extractFileName, downloadFile } from '../../utils/url';
|
||||
import api from '../../utils/api';
|
||||
import Tooltip from '../../muiComponents/Tooltip';
|
||||
import List from '../../muiComponents/List';
|
||||
import { DetailContext } from '../../pages/Version';
|
||||
import { isURL } from '../../utils/url';
|
||||
import Box from '../../muiComponents/Box';
|
||||
|
||||
import { Fab, ActionListItem } from './styles';
|
||||
import ActionBarAction, { ActionBarActionProps } from './ActionBarAction';
|
||||
|
||||
export interface Action {
|
||||
icon: string;
|
||||
title: string;
|
||||
handler?: Function;
|
||||
}
|
||||
/* eslint-disable verdaccio/jsx-spread */
|
||||
const ActionBar: React.FC = () => {
|
||||
const detailContext = React.useContext(DetailContext);
|
||||
|
||||
export async function downloadHandler(link: string): 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);
|
||||
}
|
||||
const { packageMeta } = detailContext;
|
||||
|
||||
const ACTIONS = {
|
||||
homepage: {
|
||||
icon: <HomeIcon />,
|
||||
title: 'Visit homepage',
|
||||
},
|
||||
issue: {
|
||||
icon: <BugReportIcon />,
|
||||
title: 'Open an issue',
|
||||
},
|
||||
tarball: {
|
||||
icon: <DownloadIcon />,
|
||||
title: 'Download tarball',
|
||||
handler: downloadHandler,
|
||||
},
|
||||
};
|
||||
|
||||
class ActionBar extends Component {
|
||||
public render(): ReactElement<HTMLElement> {
|
||||
return (
|
||||
<DetailContextConsumer>
|
||||
{context => {
|
||||
const { packageMeta } = context;
|
||||
|
||||
if (!packageMeta) {
|
||||
if (!packageMeta?.latest) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.renderActionBar(context as VersionPageConsumerProps);
|
||||
}}
|
||||
</DetailContextConsumer>
|
||||
);
|
||||
const { homepage, bugs, dist } = packageMeta.latest;
|
||||
|
||||
const actions: Array<ActionBarActionProps> = [];
|
||||
|
||||
if (homepage && isURL(homepage)) {
|
||||
actions.push({ type: 'VISIT_HOMEPAGE', link: homepage });
|
||||
}
|
||||
|
||||
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 });
|
||||
}
|
||||
|
||||
private renderIconsWithLink(link: string, component: JSX.Element): ReactElement<HTMLElement> {
|
||||
return (
|
||||
<a href={link} target={'_blank'}>
|
||||
{component}
|
||||
</a>
|
||||
<Box alignItems="center" display="flex" marginBottom="8px">
|
||||
{actions.map(action => (
|
||||
<ActionBarAction key={action.link} {...action} />
|
||||
))}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
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 };
|
||||
export default 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
|
||||
|
||||
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 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 { ActionBar } from '../ActionBar/ActionBar';
|
||||
import ActionBar from '../ActionBar';
|
||||
import Author from '../Author';
|
||||
import Developers from '../Developers';
|
||||
import Dist from '../Dist/Dist';
|
||||
|
@ -7,20 +7,26 @@ interface Props extends Pick<TextProps, 'variant'> {
|
||||
external?: boolean;
|
||||
className?: string;
|
||||
to: string;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
type LinkRef = HTMLAnchorElement;
|
||||
|
||||
/* 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>;
|
||||
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}
|
||||
</a>
|
||||
) : (
|
||||
<RouterLink className={className} to={to} {...props}>
|
||||
<RouterLink className={className} innerRef={ref} to={to} {...props}>
|
||||
{LinkTextContent}
|
||||
</RouterLink>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default Link;
|
||||
|
@ -9,7 +9,7 @@ import fileSizeSI from '../../utils/file-size';
|
||||
import { formatDate, formatDateDistance } from '../../utils/package';
|
||||
import Tooltip from '../../muiComponents/Tooltip';
|
||||
import { isURL } from '../../utils/url';
|
||||
import { downloadHandler } from '../ActionBar/ActionBar';
|
||||
import { downloadTarball } from '../ActionBar';
|
||||
import ListItem from '../../muiComponents/ListItem';
|
||||
import Grid from '../../muiComponents/Grid';
|
||||
|
||||
@ -140,7 +140,7 @@ const Package: React.FC<PackageInterface> = ({
|
||||
dist.tarball &&
|
||||
isURL(dist.tarball) && (
|
||||
// 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'}>
|
||||
<IconButton aria-label={'Download'}>
|
||||
{/* eslint-disable-next-line react/jsx-max-depth */}
|
||||
|
@ -8,6 +8,7 @@ export interface PackageMetaInterface {
|
||||
dist: {
|
||||
fileCount: number;
|
||||
unpackedSize: number;
|
||||
tarball?: string;
|
||||
};
|
||||
engines?: {
|
||||
node?: string;
|
||||
@ -15,6 +16,10 @@ export interface PackageMetaInterface {
|
||||
};
|
||||
license?: Partial<LicenseInterface> | string;
|
||||
version: string;
|
||||
homepage?: string;
|
||||
bugs?: {
|
||||
url: string;
|
||||
};
|
||||
repository?: {
|
||||
type?: string;
|
||||
url?: string;
|
||||
|
Loading…
Reference in New Issue
Block a user