renamed dependencies tabs, renamed to total counts, cut out official from prev cards and added dropdown for pull image command. resigned commit

Signed-off-by: Amelia-Maria Breda <ambreda@cisco.com>
This commit is contained in:
Amelia-Maria Breda 2022-10-11 14:43:33 +01:00 committed by Raul Kele
parent 0f50e5c209
commit c6d4d28367
7 changed files with 85 additions and 113 deletions

View File

@ -22,14 +22,6 @@ jest.mock('react-router-dom', () => ({
}
}));
// mock clipboard copy fn
const mockCopyToClipboard = jest.fn();
Object.assign(navigator, {
clipboard: {
writeText: mockCopyToClipboard
}
});
const mockRepoDetailsData = {
ExpandedRepoInfo: {
Manifests: [
@ -76,12 +68,4 @@ describe('Repo details component', () => {
expect(await screen.findByTestId('tags-container')).toBeInTheDocument();
expect(screen.queryByTestId('overview-container')).not.toBeInTheDocument();
});
it('should copy the pull string to clipboard', async () => {
// @ts-ignore
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockRepoDetailsData } });
render(<RepoDetails />);
fireEvent.click(await screen.findByTestId('pullcopy-btn'));
await waitFor(() => expect(mockCopyToClipboard).toHaveBeenCalledWith('Pull test'));
});
});

View File

@ -57,11 +57,19 @@ const mockImage = {
}
};
// mock clipboard copy fn
const mockCopyToClipboard = jest.fn();
Object.assign(navigator, {
clipboard: {
writeText: mockCopyToClipboard
}
});
jest.mock('react-router-dom', () => ({
// @ts-ignore
...jest.requireActual('react-router-dom'),
useParams: () => {
return { name: 'test' };
return { name: 'test', tag: '1.0.1' };
}
}));
@ -97,4 +105,12 @@ describe('Tags details', () => {
render(<TagDetails />);
expect(await screen.findByTestId('tagDetailsMetadata-container')).toBeInTheDocument();
});
it('should copy the pull string to clipboard', async () => {
// @ts-ignore
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockImage } });
render(<TagDetails />);
fireEvent.click(await screen.findByTestId('pullcopy-btn'));
await waitFor(() => expect(mockCopyToClipboard).toHaveBeenCalledWith('docker pull http://localhost/test:1.0.1'));
});
});

View File

@ -215,30 +215,7 @@ function HistoryLayers(props) {
</div>
)}
{isLoaded && historyData && (
<Card className={classes.card} raised>
<CardContent className={classes.content}>
<Grid item xs={11}>
<Stack sx={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between' }}>
<Typography variant="body1" align="left" className={classes.title}>
Command
</Typography>
<Typography variant="body1" align="left" className={classes.values}>
{transform.formatBytes(historyData[selectedIndex].Layer?.Size)}
</Typography>
</Stack>
</Grid>
<Typography variant="body1" align="left" className={classes.title} sx={{ backgroundColor: '#F7F7F7' }}>
{historyData[selectedIndex].HistoryDescription?.CreatedBy}
</Typography>
{!historyData[selectedIndex].HistoryDescription?.EmptyLayer ? (
<Typography data-testid="hash-typography">#: {historyData[selectedIndex].Layer?.Digest}</Typography>
) : (
<Typography data-testid="no-hash-typography"></Typography>
)}
</CardContent>
</Card>
)}
{!isLoaded ? <Loading /> : renderHistoryData()}
</div>
);
}

View File

@ -130,17 +130,7 @@ function PreviewCard(props) {
{signatureCheck()} */}
</Stack>
</Grid>
<Grid item xs={12} mt={2}>
<Stack alignItems="flex-end" justifyContent="space-between" direction="row">
<Typography
variant="body2"
sx={{ fontSize: '0.875rem', lineHeight: '143%', letterSpacing: '0.010625rem' }}
>
Official
</Typography>
{/* <BookmarkBorderOutlinedIcon/> */}
</Stack>
</Grid>
<Grid item xs={12} mt={2}></Grid>
</Grid>
</CardContent>
</CardActionArea>

View File

@ -7,24 +7,9 @@ import { api, endpoints } from '../api';
// components
import Tags from './Tags.jsx';
import {
Box,
Card,
CardContent,
CardMedia,
Chip,
FormControl,
Grid,
IconButton,
InputAdornment,
OutlinedInput,
Stack,
Tab,
Typography
} from '@mui/material';
import { Box, Card, CardContent, CardMedia, Chip, Grid, Stack, Tab, Typography } from '@mui/material';
import makeStyles from '@mui/styles/makeStyles';
import { host } from '../host';
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
// placeholder images
import repocube1 from '../assets/repocube-1.png';
@ -108,6 +93,7 @@ const useStyles = makeStyles(() => ({
letterSpacing: '0.01rem'
},
inputForm: {
textAlign: 'left',
'& fieldset': {
border: '0.125rem solid #52637A'
}
@ -135,7 +121,6 @@ function RepoDetails() {
// @ts-ignore
const [isLoading, setIsLoading] = useState(true);
const [selectedTab, setSelectedTab] = useState('Overview');
// get url param from <Route here (i.e. image name)
const { name } = useParams();
const abortController = useMemo(() => new AbortController(), []);
@ -306,35 +291,6 @@ function RepoDetails() {
{platformChips()}
</Stack>
</Grid>
<Grid item xs={4}>
<Typography variant="body1" sx={{ color: '#52637A', fontSize: '1rem' }}>
Copy and pull to pull this image
</Typography>
<FormControl sx={{ m: 1, paddingLeft: '1.5rem' }} variant="outlined">
<OutlinedInput
// value={`Pull ${name}`}
value="N/A"
className={classes.inputForm}
sx={{ m: 1, width: '20.625rem', borderRadius: '0.5rem', color: '#14191F' }}
endAdornment={
<InputAdornment position="end">
<IconButton
aria-label="copy"
edge="end"
onClick={() => navigator.clipboard.writeText(`Pull ${name}`)}
data-testid="pullcopy-btn"
>
<ContentCopyIcon />
</IconButton>
</InputAdornment>
}
aria-describedby="outlined-weight-helper-text"
inputProps={{
'aria-label': 'weight'
}}
/>
</FormControl>
</Grid>
</Grid>
<Grid container>
<Grid item xs={8} className={classes.tabs}>
@ -379,7 +335,7 @@ function RepoDetails() {
<Grid item xs={4} className={classes.metadata}>
<RepoDetailsMetadata
// @ts-ignore
weeklyDownloads={repoDetailData?.downloads}
totalDownloads={repoDetailData?.downloads}
// @ts-ignore
repoURL={repoDetailData?.source}
// @ts-ignore
@ -398,5 +354,4 @@ function RepoDetails() {
</>
);
}
export default RepoDetails;

View File

@ -35,7 +35,7 @@ const useStyles = makeStyles(() => ({
function RepoDetailsMetadata(props) {
const classes = useStyles();
const { repoURL, weeklyDownloads, lastUpdated, size } = props;
const { repoURL, totalDownloads, lastUpdated, size } = props;
// @ts-ignore
const lastDate = (lastUpdated ? DateTime.fromISO(lastUpdated) : DateTime.now().minus({ days: 1 })).toRelative({
unit: 'days'
@ -58,10 +58,10 @@ function RepoDetailsMetadata(props) {
<Card variant="outlined" className={classes.card}>
<CardContent>
<Typography variant="body2" align="left" className={classes.metadataHeader}>
Weekly downloads
Total downloads
</Typography>
<Typography variant="body1" align="left" className={classes.metadataBody}>
{weeklyDownloads || `N/A`}
{totalDownloads || `N/A`}
</Typography>
</CardContent>
</Card>

View File

@ -6,7 +6,21 @@ import React, { useEffect, useMemo, useState } from 'react';
import { api, endpoints } from '../api';
// components
import { Box, Card, CardContent, CardMedia, Grid, Stack, Tab, Typography } from '@mui/material';
import {
Box,
Card,
CardContent,
CardMedia,
Grid,
FormControl,
IconButton,
Stack,
Select,
MenuItem,
Tab,
Typography
} from '@mui/material';
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
import makeStyles from '@mui/styles/makeStyles';
import { host } from '../host';
@ -122,11 +136,12 @@ function TagDetails() {
// @ts-ignore
//const [isLoading, setIsLoading] = useState(false);
const [selectedTab, setSelectedTab] = useState('Layers');
const [fullName, setFullName] = useState('');
const abortController = useMemo(() => new AbortController(), []);
// get url param from <Route here (i.e. image name)
const { name, tag } = useParams();
const [fullName, setFullName] = useState(name + ':' + tag);
const [pullString, setPullString] = useState(`docker pull ${host()}/${fullName}`);
const classes = useStyles();
// const { description, overviewTitle, dependencies, dependents } = props;
@ -151,7 +166,6 @@ function TagDetails() {
};
setImageDetailData(imageData);
setFullName(imageData.name + ':' + imageData.tag);
//setIsLoading(false);
}
})
@ -187,6 +201,10 @@ function TagDetails() {
setSelectedTab(newValue);
};
const handleSelectionChange = (event) => {
setPullString(event.target.value);
};
return (
<div className={classes.pageWrapper}>
<Card className={classes.cardRoot}>
@ -227,6 +245,43 @@ function TagDetails() {
}
</Typography>
</Grid>
<Grid item xs={4}>
<Stack direction="row">
<Grid item xs={10}>
<Typography variant="body1" sx={{ color: '#52637A', fontSize: '1rem', paddingTop: '0.5rem' }}>
Copy and pull to pull this image
</Typography>
</Grid>
<Grid item xs={2}>
<IconButton
aria-label="copy"
onClick={() => navigator.clipboard.writeText(pullString)}
data-testid="pullcopy-btn"
>
<ContentCopyIcon />
</IconButton>
</Grid>
</Stack>
<FormControl sx={{ m: 1, paddingLeft: '1.5rem' }} variant="outlined">
<Select
className={classes.inputForm}
value={pullString}
onChange={handleSelectionChange}
inputProps={{ 'aria-label': 'Without label' }}
sx={{ m: 1, width: '20.625rem', borderRadius: '0.5rem', color: '#14191F', alignContent: 'left' }}
>
<MenuItem value={`docker pull ${host()}/${fullName}`}>
docker pull {host()}/{fullName}
</MenuItem>
<MenuItem value={`podman pull ${host()}/${fullName}`}>
podman pull {host()}/{fullName}
</MenuItem>
<MenuItem value={`skopeo copy docker://${host()}/${fullName}`}>
skopeo copy docker://{host()}/{fullName}
</MenuItem>
</Select>
</FormControl>
</Grid>
</Grid>
<Grid container>
<Grid item xs={8} className={classes.tabs}>
@ -238,13 +293,8 @@ function TagDetails() {
sx={{ '& button.Mui-selected': { color: '#14191F', fontWeight: '600' } }}
>
<Tab value="Layers" label="Layers" className={classes.tabContent} />
<Tab
value="DependsOn"
label="Dependencies"
className={classes.tabContent}
data-testid="dependencies-tab"
/>
<Tab value="IsDependentOn" label="Dependants" className={classes.tabContent} />
<Tab value="DependsOn" label="Uses" className={classes.tabContent} data-testid="dependencies-tab" />
<Tab value="IsDependentOn" label="Used by" className={classes.tabContent} />
<Tab value="Vulnerabilities" label="Vulnerabilities" className={classes.tabContent} />
</TabList>
<Grid container>