SET_SEARCH_RESULTS: ofType<GroupContentsResource[]>(),
SET_SEARCH_VALUE: ofType<string>(),
SET_SAVED_QUERIES: ofType<SearchBarAdvanceFormData[]>(),
+ SET_RECENT_QUERIES: ofType<string[]>(),
UPDATE_SAVED_QUERY: ofType<SearchBarAdvanceFormData[]>(),
SET_SELECTED_ITEM: ofType<string>(),
MOVE_UP: ofType<{}>(),
export const loadRecentQueries = () =>
(dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
- const recentSearchQueries = services.searchService.getRecentQueries();
- return recentSearchQueries || [];
+ const recentQueries = services.searchService.getRecentQueries() || [];
+ dispatch(searchBarActions.SET_RECENT_QUERIES(recentQueries));
+ return recentQueries;
};
export const searchData = (searchValue: string) =>
dispatch(navigateToSearchResults);
};
-// Todo: create ids for particular searchQuery
const saveQuery = (data: SearchBarAdvanceFormData) =>
(dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
- const savedSearchQueries = services.searchService.getSavedQueries();
- const filteredQuery = savedSearchQueries.find(query => query.searchQuery === data.searchQuery);
+ const savedQueries = services.searchService.getSavedQueries();
if (data.saveQuery && data.searchQuery) {
+ const filteredQuery = savedQueries.find(query => query.searchQuery === data.searchQuery);
if (filteredQuery) {
services.searchService.editSavedQueries(data);
- dispatch(searchBarActions.UPDATE_SAVED_QUERY(savedSearchQueries));
+ dispatch(searchBarActions.UPDATE_SAVED_QUERY(savedQueries));
dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Query has been successfully updated', hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
} else {
services.searchService.saveQuery(data);
- dispatch(searchBarActions.SET_SAVED_QUERIES(savedSearchQueries));
+ dispatch(searchBarActions.SET_SAVED_QUERIES(savedQueries));
dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Query has been successfully saved', hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
}
}
import { SearchBarAdvanceFormData } from '~/models/search-bar';
type SearchResult = GroupContentsResource;
+export type SearchBarSelectedItem = {
+ id: string,
+ query: string
+};
interface SearchBar {
currentView: string;
searchResults: SearchResult[];
searchValue: string;
savedQueries: SearchBarAdvanceFormData[];
- selectedItem: string;
+ recentQueries: string[];
+ selectedItem: SearchBarSelectedItem;
}
export enum SearchView {
searchResults: [],
searchValue: '',
savedQueries: [],
- selectedItem: ''
+ recentQueries: [],
+ selectedItem: {
+ id: '',
+ query: ''
+ }
+};
+
+const makeSelectedItem = (id: string, query?: string): SearchBarSelectedItem => ({ id, query: query ? query : id });
+
+const makeQueryList = (recentQueries: string[], savedQueries: SearchBarAdvanceFormData[]) => {
+ const recentIds = recentQueries.map((q, idx) => makeSelectedItem(`RQ-${idx}-${q}`, q));
+ const savedIds = savedQueries.map((q, idx) => makeSelectedItem(`SQ-${idx}-${q.searchQuery}`, q.searchQuery));
+ return recentIds.concat(savedIds);
};
export const searchBarReducer = (state = initialState, action: SearchBarActions): SearchBar =>
SET_SEARCH_RESULTS: searchResults => ({
...state,
searchResults,
- selectedItem: searchResults.length > 0
- ? searchResults.findIndex(r => r.uuid === state.selectedItem) >= 0
- ? state.selectedItem
+ selectedItem: makeSelectedItem(searchResults.length > 0
+ ? searchResults.findIndex(r => r.uuid === state.selectedItem.id) >= 0
+ ? state.selectedItem.id
: state.searchValue
: state.searchValue
+ )
}),
SET_SEARCH_VALUE: searchValue => ({
...state,
searchValue,
- selectedItem: state.searchValue === state.selectedItem
+ selectedItem: makeSelectedItem(state.searchValue === state.selectedItem.id
? searchValue
- : state.selectedItem
+ : state.selectedItem.id)
}),
SET_SAVED_QUERIES: savedQueries => ({ ...state, savedQueries }),
+ SET_RECENT_QUERIES: recentQueries => ({ ...state, recentQueries }),
UPDATE_SAVED_QUERY: searchQuery => ({ ...state, savedQueries: searchQuery }),
- SET_SELECTED_ITEM: item => ({ ...state, selectedItem: item }),
+ SET_SELECTED_ITEM: item => ({ ...state, selectedItem: makeSelectedItem(item) }),
MOVE_UP: () => {
let selectedItem = state.selectedItem;
if (state.currentView === SearchView.AUTOCOMPLETE) {
- const idx = state.searchResults.findIndex(r => r.uuid === selectedItem);
+ const idx = state.searchResults.findIndex(r => r.uuid === selectedItem.id);
if (idx > 0) {
- selectedItem = state.searchResults[idx - 1].uuid;
+ selectedItem = makeSelectedItem(state.searchResults[idx - 1].uuid);
} else {
- selectedItem = state.searchValue;
+ selectedItem = makeSelectedItem(state.searchValue);
+ }
+ } else if (state.currentView === SearchView.BASIC) {
+ const items = makeQueryList(state.recentQueries, state.savedQueries);
+
+ const idx = items.findIndex(i => i.id === selectedItem.id);
+ if (idx > 0) {
+ selectedItem = items[idx - 1];
}
}
return {
MOVE_DOWN: () => {
let selectedItem = state.selectedItem;
if (state.currentView === SearchView.AUTOCOMPLETE) {
- const idx = state.searchResults.findIndex(r => r.uuid === selectedItem);
+ const idx = state.searchResults.findIndex(r => r.uuid === selectedItem.id);
if (idx >= 0 && idx < state.searchResults.length - 1) {
- selectedItem = state.searchResults[idx + 1].uuid;
+ selectedItem = makeSelectedItem(state.searchResults[idx + 1].uuid);
} else if (idx < 0 && state.searchResults.length > 0) {
- selectedItem = state.searchResults[0].uuid;
+ selectedItem = makeSelectedItem(state.searchResults[0].uuid);
+ }
+ } else if (state.currentView === SearchView.BASIC) {
+ const items = makeQueryList(state.recentQueries, state.savedQueries);
+
+ const idx = items.findIndex(i => i.id === selectedItem.id);
+ if (idx >= 0 && idx < items.length - 1) {
+ selectedItem = items[idx + 1];
+ }
+
+ if (idx < 0 && items.length > 0) {
+ selectedItem = items[0];
}
}
return {
import { Paper, StyleRulesCallback, withStyles, WithStyles, List, ListItem, ListItemText } from '@material-ui/core';
import { GroupContentsResource } from '~/services/groups-service/groups-service';
import Highlighter from "react-highlight-words";
+import { SearchBarSelectedItem } from "~/store/search-bar/search-bar-reducer";
type CssRules = 'searchView' | 'list' | 'listItem';
listItem: {
paddingLeft: theme.spacing.unit,
paddingRight: theme.spacing.unit * 2,
- },
-
+ }
};
};
export interface SearchBarAutocompleteViewDataProps {
searchResults: GroupContentsResource[];
searchValue?: string;
- selectedItem: string;
+ selectedItem: SearchBarSelectedItem;
}
export interface SearchBarAutocompleteViewActionProps {
console.log(searchValue, selectedItem);
return <Paper className={classes.searchView}>
<List component="nav" className={classes.list}>
- <ListItem button className={classes.listItem} selected={!selectedItem || searchValue === selectedItem}>
+ <ListItem button className={classes.listItem} selected={!selectedItem || searchValue === selectedItem.id}>
<ListItemText secondary={searchValue}/>
</ListItem>
{searchResults.map((item: GroupContentsResource) =>
- <ListItem button key={item.uuid} className={classes.listItem} selected={item.uuid === selectedItem}>
+ <ListItem button key={item.uuid} className={classes.listItem} selected={item.uuid === selectedItem.id}>
<ListItemText secondary={getFormattedText(item.name, searchValue)}
onClick={() => navigateTo(item.uuid)}/>
</ListItem>
import { SearchView } from '~/store/search-bar/search-bar-reducer';
import {
SearchBarRecentQueries,
- SearchBarRecentQueriesActionProps
+ SearchBarRecentQueriesActionProps
} from '~/views-components/search-bar/search-bar-recent-queries';
import {
SearchBarSavedQueries,
},
label: {
fontSize: '0.875rem',
- padding: `${theme.spacing.unit / 2}px ${theme.spacing.unit}px `,
+ padding: `${theme.spacing.unit}px ${theme.spacing.unit}px `,
color: theme.palette.grey["900"],
background: theme.palette.grey["200"]
}
type SearchBarBasicViewProps = SearchBarBasicViewDataProps & SearchBarBasicViewActionProps & WithStyles<CssRules>;
export const SearchBarBasicView = withStyles(styles)(
- ({ classes, onSetView, loadRecentQueries, deleteSavedQuery, savedQueries, onSearch, editSavedQuery }: SearchBarBasicViewProps) =>
+ ({ classes, onSetView, loadRecentQueries, deleteSavedQuery, savedQueries, onSearch, editSavedQuery, selectedItem }: SearchBarBasicViewProps) =>
<Paper className={classes.root}>
<div className={classes.label}>Recent search queries</div>
<SearchBarRecentQueries
onSearch={onSearch}
- loadRecentQueries={loadRecentQueries} />
+ loadRecentQueries={loadRecentQueries}
+ selectedItem={selectedItem} />
<div className={classes.label}>Saved search queries</div>
<SearchBarSavedQueries
onSearch={onSearch}
savedQueries={savedQueries}
editSavedQuery={editSavedQuery}
- deleteSavedQuery={deleteSavedQuery} />
+ deleteSavedQuery={deleteSavedQuery}
+ selectedItem={selectedItem} />
<div className={classes.advanced} onClick={() => onSetView(SearchView.ADVANCED)}>Advanced search</div>
</Paper>
-);
\ No newline at end of file
+);
import * as React from 'react';
import { withStyles, WithStyles, StyleRulesCallback, List, ListItem, ListItemText } from '@material-ui/core';
import { ArvadosTheme } from '~/common/custom-theme';
+import { SearchBarSelectedItem } from "~/store/search-bar/search-bar-reducer";
type CssRules = 'root' | 'listItem' | 'listItemText';
}
});
+export interface SearchBarRecentQueriesDataProps {
+ selectedItem: SearchBarSelectedItem;
+}
+
export interface SearchBarRecentQueriesActionProps {
onSearch: (searchValue: string) => void;
loadRecentQueries: () => string[];
}
-type SearchBarRecentQueriesProps = SearchBarRecentQueriesActionProps & WithStyles<CssRules>;
+type SearchBarRecentQueriesProps = SearchBarRecentQueriesDataProps & SearchBarRecentQueriesActionProps & WithStyles<CssRules>;
export const SearchBarRecentQueries = withStyles(styles)(
- ({ classes, onSearch, loadRecentQueries }: SearchBarRecentQueriesProps) =>
+ ({ classes, onSearch, loadRecentQueries, selectedItem }: SearchBarRecentQueriesProps) =>
<List component="nav" className={classes.root}>
{loadRecentQueries().map((query, index) =>
- <ListItem button key={index} className={classes.listItem}>
- <ListItemText disableTypography
- secondary={query}
- onClick={() => onSearch(query)}
+ <ListItem button key={index} className={classes.listItem} selected={`RQ-${index}-${query}` === selectedItem.id}>
+ <ListItemText disableTypography
+ secondary={query}
+ onClick={() => onSearch(query)}
className={classes.listItemText} />
</ListItem>
)}
- </List>);
\ No newline at end of file
+ </List>);
import { ArvadosTheme } from '~/common/custom-theme';
import { RemoveIcon, EditSavedQueryIcon } from '~/components/icon/icon';
import { SearchBarAdvanceFormData } from '~/models/search-bar';
+import { SearchBarSelectedItem } from "~/store/search-bar/search-bar-reducer";
type CssRules = 'root' | 'listItem' | 'listItemText' | 'button';
export interface SearchBarSavedQueriesDataProps {
savedQueries: SearchBarAdvanceFormData[];
+ selectedItem: SearchBarSelectedItem;
}
export interface SearchBarSavedQueriesActionProps {
editSavedQuery: (data: SearchBarAdvanceFormData, id: number) => void;
}
-type SearchBarSavedQueriesProps = SearchBarSavedQueriesDataProps
- & SearchBarSavedQueriesActionProps
+type SearchBarSavedQueriesProps = SearchBarSavedQueriesDataProps
+ & SearchBarSavedQueriesActionProps
& WithStyles<CssRules>;
export const SearchBarSavedQueries = withStyles(styles)(
- ({ classes, savedQueries, onSearch, editSavedQuery, deleteSavedQuery }: SearchBarSavedQueriesProps) =>
+ ({ classes, savedQueries, onSearch, editSavedQuery, deleteSavedQuery, selectedItem }: SearchBarSavedQueriesProps) =>
<List component="nav" className={classes.root}>
- {savedQueries.map((query, index) =>
- <ListItem button key={index} className={classes.listItem}>
- <ListItemText disableTypography
- secondary={query.searchQuery}
- onClick={() => onSearch(query.searchQuery)}
+ {savedQueries.map((query, index) =>
+ <ListItem button key={index} className={classes.listItem} selected={`SQ-${index}-${query.searchQuery}` === selectedItem.id}>
+ <ListItemText disableTypography
+ secondary={query.searchQuery}
+ onClick={() => onSearch(query.searchQuery)}
className={classes.listItemText} />
<ListItemSecondaryAction>
<Tooltip title="Edit">
</ListItemSecondaryAction>
</ListItem>
)}
- </List>);
\ No newline at end of file
+ </List>);
type SearchBarViewProps = SearchBarDataProps & SearchBarActionProps & WithStyles<CssRules>;
+const handleKeyDown = (e: React.KeyboardEvent, props: SearchBarViewProps) => {
+ if (e.keyCode === KEY_CODE_DOWN) {
+ e.preventDefault();
+ if (!props.isPopoverOpen) {
+ props.openSearchView();
+ } else {
+ props.moveDown();
+ }
+ } else if (e.keyCode === KEY_CODE_UP) {
+ e.preventDefault();
+ props.moveUp();
+ } else if (e.keyCode === KEY_CODE_ESC) {
+ props.closeView();
+ } else if (e.keyCode === KEY_ENTER) {
+ if (props.currentView === SearchView.BASIC) {
+ e.preventDefault();
+ props.onSearch(props.selectedItem.query);
+ } else if (props.currentView === SearchView.AUTOCOMPLETE) {
+ if (props.selectedItem.id !== props.searchValue) {
+ e.preventDefault();
+ props.navigateTo(props.selectedItem.id);
+ }
+ }
+ }
+};
+
export const SearchBarView = withStyles(styles)(
(props : SearchBarViewProps) => {
const { classes, isPopoverOpen } = props;
fullWidth={true}
disableUnderline={true}
onClick={props.openSearchView}
- onKeyDown={e => {
- if (e.keyCode === KEY_CODE_DOWN) {
- e.preventDefault();
- if (!isPopoverOpen) {
- props.openSearchView();
- } else {
- props.moveDown();
- }
- } else if (e.keyCode === KEY_CODE_UP) {
- e.preventDefault();
- props.moveUp();
- } else if (e.keyCode === KEY_CODE_ESC) {
- props.closeView();
- } else if (e.keyCode === KEY_ENTER) {
- if (props.selectedItem !== props.searchValue) {
- e.preventDefault();
- props.navigateTo(props.selectedItem);
- }
- }
- }}
+ onKeyDown={e => handleKeyDown(e, props)}
endAdornment={
<InputAdornment position="end">
<Tooltip title='Search'>
loadRecentQueries={props.loadRecentQueries}
savedQueries={props.savedQueries}
deleteSavedQuery={props.deleteSavedQuery}
- editSavedQuery={props.editSavedQuery} />;
+ editSavedQuery={props.editSavedQuery}
+ selectedItem={props.selectedItem} />;
}
};