rspec/frontend/src/SearchPage.tsx

167 lines
5.2 KiB
TypeScript
Raw Normal View History

2020-07-03 00:02:49 +02:00
import React from 'react';
import Typography from '@material-ui/core/Typography';
import TextField from '@material-ui/core/TextField';
import MenuItem from '@material-ui/core/MenuItem';
import Container from '@material-ui/core/Container';
import Grid from '@material-ui/core/Grid';
2020-07-02 23:55:56 +02:00
import Pagination from '@material-ui/lab/Pagination';
2020-07-09 14:26:53 +02:00
import Box from '@material-ui/core/Box';
2020-07-09 14:26:53 +02:00
import useStyles from './SearchPage.style';
import { useSearch } from './utils/useSearch';
2020-07-02 23:55:56 +02:00
import {
2021-01-26 22:10:28 +01:00
SearchParamSetter,
2020-07-02 23:55:56 +02:00
useLocationSearch,
useLocationSearchState
} from './utils/routing';
import { SearchHit } from './SearchHit';
export const SearchPage = () => {
2020-07-09 14:26:53 +02:00
const classes = useStyles();
2020-07-02 23:55:56 +02:00
const pageSize = 20;
const [query, setQuery] = useLocationSearchState('query', '');
const [ruleType, setRuleType] = useLocationSearchState('types', 'ALL');
2021-01-26 22:10:28 +01:00
const allRuleTypes: Record<string,string> = {
'BUG': 'Bug',
'CODE_SMELL': 'Code Smell',
'SECURITY_HOTSPOT': 'Security Hotspot',
'VULNERABILITY': 'Vulnerability'
};
2021-01-26 22:10:28 +01:00
const [ruleTags, setRuleTags] = useLocationSearchState<string[]>('tags', [], value => value ? value.split(',') : []);
const allRuleTags = ["confusing", 'pitfall', 'clumsy', 'junit', 'tests']; // TODO: generate this list
2020-07-02 23:55:56 +02:00
const [pageNumber, setPageNumber] = useLocationSearchState('page', 1, parseInt);
2021-01-26 22:10:28 +01:00
const {setLocationSearch} = useLocationSearch();
2020-07-02 23:55:56 +02:00
2021-01-26 22:10:28 +01:00
const {results, numberOfHits, error, loading} = useSearch(query,
ruleType === "ALL" ? null : ruleType,
ruleTags,
pageSize, pageNumber);
2021-01-26 22:10:28 +01:00
const totalPages = numberOfHits ? Math.ceil(numberOfHits/pageSize) : 0;
2021-01-26 22:10:28 +01:00
let resultsDisplay: string|JSX.Element[] = "No rule found...";
if (loading) {
resultsDisplay = "Searching";
}
else if (results.length > 0) {
2020-07-09 14:26:53 +02:00
resultsDisplay = results.map(result =>
<Box className={classes.searchHitBox}>
<SearchHit key={result.id} data={result}/>
</Box>
)
}
2020-07-02 23:55:56 +02:00
2021-01-26 22:10:28 +01:00
const paramSetters: Record<string, SearchParamSetter<any>> = {types: setRuleType, tags: setRuleTags, query: setQuery};
function handleUpdate(field: string) {
return function(event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) {
2020-07-09 10:44:35 +02:00
if (pageNumber > 1) {
2021-01-26 22:10:28 +01:00
const uriSearch: Record<string, any> = {
query: query, types: ruleType, tags: ruleTags, page: 1
};
2020-07-09 10:44:35 +02:00
uriSearch[field] = event.target.value;
setLocationSearch(uriSearch);
} else {
paramSetters[field](event.target.value, {push: false});
}
2020-07-02 23:55:56 +02:00
}
}
return (
2020-07-09 14:26:53 +02:00
<div className={classes.root}>
<div className={classes.searchBar}>
<Container maxWidth="md">
<Grid container spacing={3}>
<Grid item xs={12}>
2020-07-09 14:26:53 +02:00
<Typography variant="h4">Search Rule Specifications</Typography>
</Grid>
<Grid item xs={12}>
<TextField
id="title-query"
label="Rule Title and Description"
placeholder="Search in rule titles and descriptions"
fullWidth
margin="normal"
InputLabelProps={{
shrink: true,
}}
variant="outlined"
value={query}
2020-07-09 10:44:35 +02:00
onChange={handleUpdate("query")}
2021-01-26 22:10:28 +01:00
error={!!error}
helperText={error}
/>
</Grid>
<Grid item xs={3}>
<TextField
select
fullWidth
margin="normal"
variant="outlined"
label="Rule types"
value={ruleType}
2020-07-09 10:44:35 +02:00
onChange={handleUpdate("types")}
>
<MenuItem key="All" value="ALL">
All
</MenuItem>
{Object.keys(allRuleTypes).map((ruleType) => (
<MenuItem key={ruleType} value={ruleType}>
{allRuleTypes[ruleType]}
</MenuItem>
))}
</TextField>
</Grid>
<Grid item xs={9}>
<TextField
select
fullWidth
SelectProps={{
multiple: true,
2021-01-26 22:10:28 +01:00
renderValue: (selected: any) => {
return selected.join(', ');
}
}}
margin="normal"
variant="outlined"
label="Rule Tags"
value={ruleTags}
2020-07-09 10:44:35 +02:00
onChange={handleUpdate("tags")}
>
{allRuleTags.map((ruleType) => (
<MenuItem key={ruleType} value={ruleType}>
{ruleType}
</MenuItem>
))}
</TextField>
</Grid>
</Grid>
</Container>
2020-07-09 14:26:53 +02:00
</div>
<div className={classes.searchResults}>
<Container maxWidth="md">
<Grid container spacing={3}>
<Grid item xs={12}>
<Box className={classes.topRow}>
<Box className={classes.resultsCount}>
<Typography variant="subtitle1">Number of rules found: {numberOfHits}</Typography>
</Box>
<Typography variant="subtitle1">
<a href={"https://github.com/SonarSource/rspec/pulls?q=is%3Aopen+is%3Apr+%22Create+rule%22+" + query}>Search in unimplemented</a>
</Typography>
2020-07-09 14:26:53 +02:00
</Box>
{resultsDisplay}
<Pagination count={totalPages} page={pageNumber} siblingCount={2}
onChange={(event, value) => setPageNumber(value)}
/>
</Grid>
</Grid>
2020-07-09 14:26:53 +02:00
</Container>
</div>
</div>
)
}