diff --git a/frontend/src/component/common/Search/Search.test.tsx b/frontend/src/component/common/Search/Search.test.tsx index 6b4af676b6..fc3c52ef62 100644 --- a/frontend/src/component/common/Search/Search.test.tsx +++ b/frontend/src/component/common/Search/Search.test.tsx @@ -51,3 +51,18 @@ test('should update saved query without local storage', async () => { expect(screen.getByText('newquery')).toBeInTheDocument(); // new saved query updated }); + +test('should still render history if no search context', async () => { + const { setValue } = createLocalStorage('Search:localStorageId:v1', {}); + setValue({ + query: 'oldquery', + }); + + render( {}} id='localStorageId' />); + + const input = screen.getByTestId(SEARCH_INPUT); + + input.focus(); + + await screen.findByText('oldquery'); +}); diff --git a/frontend/src/component/common/Search/Search.tsx b/frontend/src/component/common/Search/Search.tsx index 44d71c42d2..be3e004b4a 100644 --- a/frontend/src/component/common/Search/Search.tsx +++ b/frontend/src/component/common/Search/Search.tsx @@ -1,6 +1,13 @@ -import React, { useRef, useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { useAsyncDebounce } from 'react-table'; -import { Box, IconButton, InputBase, styled, Tooltip } from '@mui/material'; +import { + Box, + IconButton, + InputBase, + Paper, + styled, + Tooltip, +} from '@mui/material'; import { Close, Search as SearchIcon } from '@mui/icons-material'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { SearchSuggestions } from './SearchSuggestions/SearchSuggestions'; @@ -10,6 +17,7 @@ import { SEARCH_INPUT } from 'utils/testIds'; import { useOnClickOutside } from 'hooks/useOnClickOutside'; import { useSavedQuery } from './useSavedQuery'; import { useOnBlur } from 'hooks/useOnBlur'; +import { SearchHistory } from './SearchSuggestions/SearchHistory'; interface ISearchProps { id?: string; @@ -27,9 +35,26 @@ interface ISearchProps { expandable?: boolean; } +export const SearchPaper = styled(Paper)(({ theme }) => ({ + position: 'absolute', + width: '100%', + left: 0, + top: '20px', + zIndex: 2, + padding: theme.spacing(4, 1.5, 1.5), + borderBottomLeftRadius: theme.spacing(1), + borderBottomRightRadius: theme.spacing(1), + boxShadow: '0px 8px 20px rgba(33, 33, 33, 0.15)', + fontSize: theme.fontSizes.smallBody, + color: theme.palette.text.secondary, + wordBreak: 'break-word', +})); + const StyledContainer = styled('div', { shouldForwardProp: (prop) => prop !== 'active', -})<{ active: boolean | undefined }>(({ theme, active }) => ({ +})<{ + active: boolean | undefined; +}>(({ theme, active }) => ({ display: 'flex', flexGrow: 1, alignItems: 'center', @@ -93,7 +118,7 @@ export const Search = ({ const { savedQuery, setSavedQuery } = useSavedQuery(id); - const [value, setValue] = useState(initialValue); + const [value, setValue] = useState(initialValue); const debouncedOnChange = useAsyncDebounce(onChange, debounceTime); const onSearchChange = (value: string) => { @@ -103,7 +128,11 @@ export const Search = ({ }; const hotkey = useKeyboardShortcut( - { modifiers: ['ctrl'], key: 'k', preventDefault: true }, + { + modifiers: ['ctrl'], + key: 'k', + preventDefault: true, + }, () => { if (document.activeElement === searchInputRef.current) { searchInputRef.current?.blur(); @@ -122,6 +151,10 @@ export const Search = ({ useOnClickOutside([searchContainerRef], hideSuggestions); useOnBlur(searchContainerRef, hideSuggestions); + useEffect(() => { + setValue(initialValue); + }, [initialValue]); + return ( } + elseShow={ + showSuggestions && + savedQuery && ( + + + + ) + } /> ); diff --git a/frontend/src/component/common/Search/SearchSuggestions/SearchHistory.tsx b/frontend/src/component/common/Search/SearchSuggestions/SearchHistory.tsx new file mode 100644 index 0000000000..b5bf475206 --- /dev/null +++ b/frontend/src/component/common/Search/SearchSuggestions/SearchHistory.tsx @@ -0,0 +1,56 @@ +import { FilterList, History } from '@mui/icons-material'; +import { Box, Divider, Paper, styled } from '@mui/material'; +import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; +import { IGetSearchContextOutput } from 'hooks/useSearch'; +import { VFC } from 'react'; +import { StyledCode } from './SearchInstructions/SearchInstructions'; +import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; +import { onEnter } from './onEnter'; + +const StyledBox = styled(Box)(({ theme }) => ({ + display: 'flex', + gap: theme.spacing(2), +})); + +const StyledHistory = styled(History)(({ theme }) => ({ + color: theme.palette.text.secondary, +})); +interface ISearchHistoryProps { + onSuggestion: (suggestion: string) => void; + savedQuery?: string; +} + +export const SearchHistory: VFC = ({ + onSuggestion, + savedQuery, +}) => { + const { trackEvent } = usePlausibleTracker(); + const onSavedQuery = () => { + onSuggestion(savedQuery || ''); + trackEvent('search-filter-suggestions', { + props: { + eventType: 'saved query', + }, + }); + }; + + return ( + + + + + {savedQuery} + + + + } + /> + ); +}; diff --git a/frontend/src/component/common/Search/SearchSuggestions/SearchSuggestions.tsx b/frontend/src/component/common/Search/SearchSuggestions/SearchSuggestions.tsx index 245b7bb7e7..f3ec2b472b 100644 --- a/frontend/src/component/common/Search/SearchSuggestions/SearchSuggestions.tsx +++ b/frontend/src/component/common/Search/SearchSuggestions/SearchSuggestions.tsx @@ -7,7 +7,7 @@ import { getFilterValues, IGetSearchContextOutput, } from 'hooks/useSearch'; -import { VFC } from 'react'; +import React, { VFC } from 'react'; import { SearchDescription } from './SearchDescription/SearchDescription'; import { SearchInstructions, @@ -15,21 +15,8 @@ import { } from './SearchInstructions/SearchInstructions'; import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; import { onEnter } from './onEnter'; - -const StyledPaper = styled(Paper)(({ theme }) => ({ - position: 'absolute', - width: '100%', - left: 0, - top: '20px', - zIndex: 2, - padding: theme.spacing(4, 1.5, 1.5), - borderBottomLeftRadius: theme.spacing(1), - borderBottomRightRadius: theme.spacing(1), - boxShadow: '0px 8px 20px rgba(33, 33, 33, 0.15)', - fontSize: theme.fontSizes.smallBody, - color: theme.palette.text.secondary, - wordBreak: 'break-word', -})); +import { SearchHistory } from './SearchHistory'; +import { SearchPaper } from '../Search'; const StyledBox = styled(Box)(({ theme }) => ({ display: 'flex', @@ -127,36 +114,21 @@ export const SearchSuggestions: VFC = ({ }, }); }; - const onSavedQuery = () => { - onSuggestion(savedQuery || ''); - trackEvent('search-filter-suggestions', { - props: { - eventType: 'saved query', - }, - }); - }; return ( - + - - - - {savedQuery} - - + } /> - @@ -198,6 +170,6 @@ export const SearchSuggestions: VFC = ({ {suggestedTextSearch} - + ); }; diff --git a/frontend/src/component/feature/FeatureToggleList/FeatureToggleListTable.tsx b/frontend/src/component/feature/FeatureToggleList/FeatureToggleListTable.tsx index 91f63320ed..e675f52012 100644 --- a/frontend/src/component/feature/FeatureToggleList/FeatureToggleListTable.tsx +++ b/frontend/src/component/feature/FeatureToggleList/FeatureToggleListTable.tsx @@ -282,6 +282,7 @@ const FeatureToggleListTableComponent: VFC = () => { tableState.query || '' } onChange={setSearchValue} + id='globalFeatureToggles' /> @@ -331,6 +332,7 @@ const FeatureToggleListTableComponent: VFC = () => { } />