feat: Implemented sorting on search page

Signed-off-by: Raul Kele <raulkeleblk@gmail.com>
This commit is contained in:
Raul Kele 2022-10-26 13:11:27 +03:00
parent f33aa8d647
commit 49093f9493
4 changed files with 79 additions and 18 deletions

View File

@ -1,4 +1,5 @@
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { api } from 'api';
import Explore from 'components/Explore';
import React from 'react';
@ -177,4 +178,16 @@ describe('Explore component', () => {
render(<StateExploreWrapper />);
await waitFor(() => expect(error).toBeCalledTimes(1));
});
it("should render the sort filter and be able to change it's value", async () => {
// @ts-ignore
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockImageList } });
render(<StateExploreWrapper />);
const selectFilter = await screen.findByText('Relevance');
expect(selectFilter).toBeInTheDocument();
userEvent.click(selectFilter);
const newOption = await screen.findByText('Alphabetical');
userEvent.click(newOption);
expect(await screen.findByText('Alphabetical')).toBeInTheDocument();
});
});

View File

@ -1,6 +1,7 @@
// @ts-nocheck
import axios from 'axios';
import { isEmpty } from 'lodash';
import { sortByCriteria } from 'utilities/sortCriteria';
const api = {
// This method returns the generic request configuration for axios
@ -78,9 +79,17 @@ const endpoints = {
`/v2/_zot/ext/search?query={BaseImageList(image: "${name}"){RepoName Tag Description Digest Vendor DownloadCount LastUpdated Size Platform {Os Arch} IsSigned Vulnerabilities {MaxSeverity Count}}}`,
isDependentOnForImage: (name) =>
`/v2/_zot/ext/search?query={DerivedImageList(image: "${name}"){RepoName Tag Description Digest Vendor DownloadCount LastUpdated Size Platform {Os Arch} IsSigned Vulnerabilities {MaxSeverity Count}}}`,
globalSearch: ({ searchQuery = '""', pageNumber = 1, pageSize = 15, filter = {} }) => {
globalSearch: ({
searchQuery = '""',
pageNumber = 1,
pageSize = 15,
sortBy = sortByCriteria.relevance.value,
filter = {}
}) => {
const searchParam = searchQuery !== '' ? `query:"${searchQuery}"` : `query:""`;
const paginationParam = `requestedPage: {limit:${pageSize} offset:${(pageNumber - 1) * pageSize}}`;
const paginationParam = `requestedPage: {limit:${pageSize} offset:${
(pageNumber - 1) * pageSize
} sortBy: ${sortBy}}`;
let filterParam = `,filter: {`;
if (filter.Os) filterParam += ` Os:${!isEmpty(filter.Os) ? `${JSON.stringify(filter.Os)}` : '""'}`;
if (filter.Arch) filterParam += ` Arch:${!isEmpty(filter.Arch) ? `${JSON.stringify(filter.Arch)}` : '""'}`;

View File

@ -6,7 +6,7 @@ import RepoCard from './RepoCard.jsx';
import Loading from './Loading';
import Typography from '@mui/material/Typography';
import Alert from '@mui/material/Alert';
import { Container, Grid, Stack } from '@mui/material';
import { Container, FormControl, Grid, InputLabel, MenuItem, Select, Stack } from '@mui/material';
import makeStyles from '@mui/styles/makeStyles';
@ -18,6 +18,7 @@ import { useSearchParams } from 'react-router-dom';
import FilterCard from './FilterCard.jsx';
import { isEmpty } from 'lodash';
import filterConstants from 'utilities/filterConstants.js';
import { sortByCriteria } from 'utilities/sortCriteria.js';
const useStyles = makeStyles(() => ({
gridWrapper: {
@ -46,14 +47,16 @@ const useStyles = makeStyles(() => ({
sortForm: {
backgroundColor: '#ffffff',
borderColor: '#E0E0E0',
borderRadius: '0.375em'
borderRadius: '0.375em',
width: '25%',
textAlign: 'left'
}
}));
function Explore() {
const [isLoading, setIsLoading] = useState(true);
const [exploreData, setExploreData] = useState([]);
// const [sortFilter, setSortFilter] = useState('');
const [sortFilter, setSortFilter] = useState(sortByCriteria.relevance.value);
const [queryParams] = useSearchParams();
const search = queryParams.get('search');
// filtercard filters
@ -78,7 +81,7 @@ function Explore() {
setIsLoading(true);
api
.get(
`${host()}${endpoints.globalSearch({ searchQuery: search, filter: buildFilterQuery() })}`,
`${host()}${endpoints.globalSearch({ searchQuery: search, sortBy: sortFilter, filter: buildFilterQuery() })}`,
abortController.signal
)
.then((response) => {
@ -93,11 +96,12 @@ function Explore() {
})
.catch((e) => {
console.error(e);
setIsLoading(false);
});
return () => {
abortController.abort();
};
}, [search, queryParams, imageFilters, osFilters, archFilters]);
}, [search, queryParams, imageFilters, osFilters, archFilters, sortFilter]);
const renderRepoCards = () => {
return (
@ -150,26 +154,35 @@ function Explore() {
);
};
// const handleSortChange = (event) => {
// setSortFilter(event.target.value);
// };
const handleSortChange = (event) => {
setSortFilter(event.target.value);
};
return (
<Container maxWidth="lg">
<Grid container className={classes.gridWrapper}>
<Grid container item xs={12}>
<Grid item xs={0}></Grid>
<Grid item xs={12}>
<Grid item xs={3}></Grid>
<Grid item xs={9}>
<Stack direction="row" className={classes.resultsRow}>
<Typography variant="body2" className={classes.results}>
Results {exploreData.length}
</Typography>
{/* <FormControl sx={{m:'1', minWidth:"4.6875rem"}} className={classes.sortForm} size="small">
<InputLabel>Sort</InputLabel>
<Select label="Sort" value={sortFilter} onChange={handleSortChange} MenuProps={{disableScrollLock: true}}>
<MenuItem value='relevance'>Relevance</MenuItem>
</Select>
</FormControl> */}
<FormControl sx={{ m: '1', minWidth: '4.6875rem' }} className={classes.sortForm} size="small">
<InputLabel>Sort</InputLabel>
<Select
label="Sort"
value={sortFilter}
onChange={handleSortChange}
MenuProps={{ disableScrollLock: true }}
>
{Object.values(sortByCriteria).map((el) => (
<MenuItem key={el.value} value={el.value}>
{el.label}
</MenuItem>
))}
</Select>
</FormControl>
</Stack>
</Grid>
</Grid>

View File

@ -0,0 +1,26 @@
export const sortByCriteria = {
relevance: {
value: 'RELEVANCE',
label: 'Relevance'
},
updateTime: {
value: 'UPDATE_TIME',
label: 'Recent'
},
alphabetic: {
value: 'ALPHABETIC_ASC',
label: 'Alphabetical'
},
alphabeticDesc: {
value: 'ALPHABETIC_DSC',
label: 'Alphabetical desc'
},
stars: {
value: 'STARS',
label: 'Most starred'
},
downloads: {
value: 'DOWNLOADS',
label: 'Most downloaded'
}
};