export class SearchQueriesService {
private recentQueries: string[] = this.getRecentQueries();
+ private savedQueries: string[] = this.getSavedQueries();
saveRecentQuery(query: string) {
if (this.recentQueries.length >= 5) {
getRecentQueries() {
return JSON.parse(localStorage.getItem('recentQueries') || '[]') as string[];
}
+
+ saveQuery(query: string) {
+ this.savedQueries.push(query);
+ localStorage.setItem('savedQueries', JSON.stringify(this.savedQueries));
+ }
+
+ getSavedQueries() {
+ return JSON.parse(localStorage.getItem('savedQueries') || '[]') as string[];
+ }
+
+ deleteSavedQuery(id: number) {
+ const queryToDelete = this.savedQueries[id];
+ const restQueries = this.savedQueries.filter(query => query !== queryToDelete);
+ return localStorage.setItem('savedQueries', JSON.stringify(restQueries));
+ }
}
\ No newline at end of file
return recentSearchQueries || [];
};
+export const saveQuery = (query: string) =>
+ (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
+ services.searchQueriesService.saveQuery(query);
+ };
+
+export const loadSavedQueries = () =>
+ (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
+ const savedSearchQueries = services.searchQueriesService.getSavedQueries();
+ return savedSearchQueries || [];
+ };
+
+export const deleteSavedQuery = (id: number) =>
+ (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
+ services.searchQueriesService.deleteSavedQuery(id);
+ };
+
export const searchData = (searchValue: string) =>
async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
dispatch(searchBarActions.SET_SEARCH_VALUE(searchValue));
paddingRight: theme.spacing.unit * 2,
paddingBottom: theme.spacing.unit,
fontSize: '14px',
- cursor: 'pointer'
+ cursor: 'pointer',
+ color: theme.palette.primary.main
},
searchQueryList: {
padding: `${theme.spacing.unit / 2}px ${theme.spacing.unit}px `,
interface SearchBarBasicViewProps {
setView: (currentView: string) => void;
recentQueries: () => string[];
+ savedQueries: () => string[];
+ deleteSavedQuery: (id: number) => void;
}
export const SearchBarBasicView = withStyles(styles)(
- ({ classes, setView, recentQueries }: SearchBarBasicViewProps & WithStyles<CssRules>) =>
+ ({ classes, setView, recentQueries, savedQueries, deleteSavedQuery }: SearchBarBasicViewProps & WithStyles<CssRules>) =>
<Paper className={classes.searchView}>
<div className={classes.searchQueryList}>Saved search queries</div>
<List component="nav" className={classes.list}>
- <RenderSavedQueries text="Test" />
- <RenderSavedQueries text="Demo" />
+ {savedQueries().map((query, index) => <RenderSavedQueries key={index} text={query} id={index} deleteSavedQuery={deleteSavedQuery}/>)}
</List>
<div className={classes.searchQueryList}>Recent search queries</div>
<List component="nav" className={classes.list}>
- {recentQueries().map((query, index) => <RecentQueriesItem key={query + index} text={query} />)}
+ {recentQueries().map((query, index) => <RecentQueriesItem key={index} text={query} />)}
</List>
<div className={classes.advanced} onClick={() => setView(SearchView.ADVANCED)}>Advanced search</div>
</Paper>
import { SearchBarAutocompleteView, SearchBarAutocompleteViewDataProps } from '~/views-components/search-bar/search-bar-autocomplete-view';
import { ArvadosTheme } from '~/common/custom-theme';
-type CssRules = 'container' | 'containerSearchViewOpened' | 'input' | 'searchBar';
+type CssRules = 'container' | 'containerSearchViewOpened' | 'input' | 'searchBar' | 'view';
const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => {
return {
},
searchBar: {
height: '30px'
+ },
+ view: {
+ position: 'absolute',
+ width: '100%'
}
};
};
type SearchBarDataProps = {
searchValue: string;
currentView: string;
- open: boolean;
+ isPopoverOpen: boolean;
} & SearchBarAutocompleteViewDataProps;
interface SearchBarActionProps {
onSetView: (currentView: string) => void;
openView: () => void;
closeView: () => void;
+ saveRecentQuery: (query: string) => void;
+ loadRecentQueries: () => string[];
saveQuery: (query: string) => void;
- loadQueries: () => string[];
+ loadSavedQueries: () => string[];
+ deleteSavedQuery: (id: number) => void;
}
type SearchBarProps = SearchBarDataProps & SearchBarActionProps & WithStyles<CssRules>;
value: string;
}
-interface RenderQueriesProps {
+interface RenderSavedQueriesProps {
+ text: string | JSX.Element;
+ id: number;
+ deleteSavedQuery: (id: number) => void;
+}
+
+interface RenderRecentQueriesProps {
text: string | JSX.Element;
}
-export const RecentQueriesItem = (props: RenderQueriesProps) => {
+export const RecentQueriesItem = (props: RenderRecentQueriesProps) => {
return <ListItem button>
<ListItemText secondary={props.text} />
</ListItem>;
};
-export const RenderSavedQueries = (props: RenderQueriesProps) => {
+export const RenderSavedQueries = (props: RenderSavedQueriesProps) => {
return <ListItem button>
<ListItemText secondary={props.text} />
<ListItemSecondaryAction>
<Tooltip title="Remove">
- <IconButton aria-label="Remove">
+ <IconButton aria-label="Remove" onClick={() => props.deleteSavedQuery(props.id)}>
<RemoveIcon />
</IconButton>
</Tooltip>
timeout: number;
render() {
- const { classes, currentView, openView, closeView, open } = this.props;
+ const { classes, currentView, openView, closeView, isPopoverOpen } = this.props;
return <ClickAwayListener onClickAway={() => closeView()}>
- <Paper className={open ? classes.containerSearchViewOpened : classes.container} >
+ <Paper className={isPopoverOpen ? classes.containerSearchViewOpened : classes.container} >
<form onSubmit={this.handleSubmit} className={classes.searchBar}>
<Input
className={classes.input}
</Tooltip>
</InputAdornment>
} />
- {open && this.getView(currentView)}
</form>
+ <div className={classes.view}>
+ {isPopoverOpen && this.getView(currentView)}
+ </div>
</Paper >
</ClickAwayListener>;
}
getView = (currentView: string) => {
switch (currentView) {
case SearchView.BASIC:
- return <SearchBarBasicView setView={this.props.onSetView} recentQueries={this.props.loadQueries} />;
+ return <SearchBarBasicView setView={this.props.onSetView} recentQueries={this.props.loadRecentQueries} savedQueries={this.props.loadSavedQueries} deleteSavedQuery={this.props.deleteSavedQuery}/>;
case SearchView.ADVANCED:
return <SearchBarAdvancedView setView={this.props.onSetView} />;
case SearchView.AUTOCOMPLETE:
- return <SearchBarAutocompleteView
- searchResults={this.props.searchResults}
- searchValue={this.props.searchValue} />;
+ return <SearchBarAutocompleteView
+ searchResults={this.props.searchResults}
+ searchValue={this.props.searchValue} />;
default:
- return <SearchBarBasicView setView={this.props.onSetView} recentQueries={this.props.loadQueries} />;
+ return <SearchBarBasicView setView={this.props.onSetView} recentQueries={this.props.loadRecentQueries} savedQueries={this.props.loadSavedQueries} deleteSavedQuery={this.props.deleteSavedQuery}/>;
}
}
handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
clearTimeout(this.timeout);
+ this.props.saveRecentQuery(this.state.value);
this.props.saveQuery(this.state.value);
this.props.onSearch(this.state.value);
- this.props.loadQueries();
+ this.props.loadRecentQueries();
+ this.props.loadSavedQueries();
}
handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
import { connect } from 'react-redux';
import { RootState } from '~/store/store';
import { Dispatch } from 'redux';
-import { goToView, searchData, searchBarActions } from '~/store/search-bar/search-bar-actions';
+import {
+ goToView,
+ searchData,
+ searchBarActions,
+ deleteSavedQuery,
+ saveRecentQuery,
+ loadRecentQueries,
+ saveQuery,
+ loadSavedQueries
+} from '~/store/search-bar/search-bar-actions';
import { SearchBarView } from '~/views-components/search-bar/search-bar-view';
-import { saveRecentQuery, loadRecentQueries } from '~/store/search-bar/search-bar-actions';
const mapStateToProps = ({ searchBar }: RootState) => {
return {
searchValue: searchBar.searchValue,
currentView: searchBar.currentView,
- open: searchBar.open,
+ isPopoverOpen: searchBar.open,
searchResults: searchBar.searchResults
};
};
onSetView: (currentView: string) => dispatch(goToView(currentView)),
openView: () => dispatch<any>(searchBarActions.OPEN_SEARCH_VIEW()),
closeView: () => dispatch<any>(searchBarActions.CLOSE_SEARCH_VIEW()),
- saveQuery: (query: string) => dispatch<any>(saveRecentQuery(query)),
- loadQueries: () => dispatch<any>(loadRecentQueries())
+ saveRecentQuery: (query: string) => dispatch<any>(saveRecentQuery(query)),
+ loadRecentQueries: () => dispatch<any>(loadRecentQueries()),
+ saveQuery: (query: string) => dispatch<any>(saveQuery(query)),
+ loadSavedQueries: () => dispatch<any>(loadSavedQueries()),
+ deleteSavedQuery: (id: number) => dispatch<any>(deleteSavedQuery(id))
});
export const SearchBar = connect(mapStateToProps, mapDispatchToProps)(SearchBarView);
\ No newline at end of file