mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-25 00:07:47 +01:00
feat: add history for search (#5651)
This is simple refactor. Just moving history part out of suggestions component.
This commit is contained in:
parent
203d6ac848
commit
848415c5ca
@ -51,3 +51,18 @@ test('should update saved query without local storage', async () => {
|
|||||||
|
|
||||||
expect(screen.getByText('newquery')).toBeInTheDocument(); // new saved query updated
|
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(<Search onChange={() => {}} id='localStorageId' />);
|
||||||
|
|
||||||
|
const input = screen.getByTestId(SEARCH_INPUT);
|
||||||
|
|
||||||
|
input.focus();
|
||||||
|
|
||||||
|
await screen.findByText('oldquery');
|
||||||
|
});
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
import React, { useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import { useAsyncDebounce } from 'react-table';
|
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 { Close, Search as SearchIcon } from '@mui/icons-material';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { SearchSuggestions } from './SearchSuggestions/SearchSuggestions';
|
import { SearchSuggestions } from './SearchSuggestions/SearchSuggestions';
|
||||||
@ -10,6 +17,7 @@ import { SEARCH_INPUT } from 'utils/testIds';
|
|||||||
import { useOnClickOutside } from 'hooks/useOnClickOutside';
|
import { useOnClickOutside } from 'hooks/useOnClickOutside';
|
||||||
import { useSavedQuery } from './useSavedQuery';
|
import { useSavedQuery } from './useSavedQuery';
|
||||||
import { useOnBlur } from 'hooks/useOnBlur';
|
import { useOnBlur } from 'hooks/useOnBlur';
|
||||||
|
import { SearchHistory } from './SearchSuggestions/SearchHistory';
|
||||||
|
|
||||||
interface ISearchProps {
|
interface ISearchProps {
|
||||||
id?: string;
|
id?: string;
|
||||||
@ -27,9 +35,26 @@ interface ISearchProps {
|
|||||||
expandable?: boolean;
|
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', {
|
const StyledContainer = styled('div', {
|
||||||
shouldForwardProp: (prop) => prop !== 'active',
|
shouldForwardProp: (prop) => prop !== 'active',
|
||||||
})<{ active: boolean | undefined }>(({ theme, active }) => ({
|
})<{
|
||||||
|
active: boolean | undefined;
|
||||||
|
}>(({ theme, active }) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
@ -93,7 +118,7 @@ export const Search = ({
|
|||||||
|
|
||||||
const { savedQuery, setSavedQuery } = useSavedQuery(id);
|
const { savedQuery, setSavedQuery } = useSavedQuery(id);
|
||||||
|
|
||||||
const [value, setValue] = useState(initialValue);
|
const [value, setValue] = useState<string>(initialValue);
|
||||||
const debouncedOnChange = useAsyncDebounce(onChange, debounceTime);
|
const debouncedOnChange = useAsyncDebounce(onChange, debounceTime);
|
||||||
|
|
||||||
const onSearchChange = (value: string) => {
|
const onSearchChange = (value: string) => {
|
||||||
@ -103,7 +128,11 @@ export const Search = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const hotkey = useKeyboardShortcut(
|
const hotkey = useKeyboardShortcut(
|
||||||
{ modifiers: ['ctrl'], key: 'k', preventDefault: true },
|
{
|
||||||
|
modifiers: ['ctrl'],
|
||||||
|
key: 'k',
|
||||||
|
preventDefault: true,
|
||||||
|
},
|
||||||
() => {
|
() => {
|
||||||
if (document.activeElement === searchInputRef.current) {
|
if (document.activeElement === searchInputRef.current) {
|
||||||
searchInputRef.current?.blur();
|
searchInputRef.current?.blur();
|
||||||
@ -122,6 +151,10 @@ export const Search = ({
|
|||||||
useOnClickOutside([searchContainerRef], hideSuggestions);
|
useOnClickOutside([searchContainerRef], hideSuggestions);
|
||||||
useOnBlur(searchContainerRef, hideSuggestions);
|
useOnBlur(searchContainerRef, hideSuggestions);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setValue(initialValue);
|
||||||
|
}, [initialValue]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledContainer
|
<StyledContainer
|
||||||
ref={searchContainerRef}
|
ref={searchContainerRef}
|
||||||
@ -189,6 +222,17 @@ export const Search = ({
|
|||||||
getSearchContext={getSearchContext!}
|
getSearchContext={getSearchContext!}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
elseShow={
|
||||||
|
showSuggestions &&
|
||||||
|
savedQuery && (
|
||||||
|
<SearchPaper className='dropdown-outline'>
|
||||||
|
<SearchHistory
|
||||||
|
onSuggestion={onSearchChange}
|
||||||
|
savedQuery={savedQuery}
|
||||||
|
/>
|
||||||
|
</SearchPaper>
|
||||||
|
)
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
);
|
);
|
||||||
|
@ -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<ISearchHistoryProps> = ({
|
||||||
|
onSuggestion,
|
||||||
|
savedQuery,
|
||||||
|
}) => {
|
||||||
|
const { trackEvent } = usePlausibleTracker();
|
||||||
|
const onSavedQuery = () => {
|
||||||
|
onSuggestion(savedQuery || '');
|
||||||
|
trackEvent('search-filter-suggestions', {
|
||||||
|
props: {
|
||||||
|
eventType: 'saved query',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={Boolean(savedQuery)}
|
||||||
|
show={
|
||||||
|
<>
|
||||||
|
<StyledBox>
|
||||||
|
<StyledHistory />
|
||||||
|
<StyledCode
|
||||||
|
tabIndex={0}
|
||||||
|
onClick={onSavedQuery}
|
||||||
|
onKeyDown={onEnter(onSavedQuery)}
|
||||||
|
>
|
||||||
|
<span>{savedQuery}</span>
|
||||||
|
</StyledCode>
|
||||||
|
</StyledBox>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
@ -7,7 +7,7 @@ import {
|
|||||||
getFilterValues,
|
getFilterValues,
|
||||||
IGetSearchContextOutput,
|
IGetSearchContextOutput,
|
||||||
} from 'hooks/useSearch';
|
} from 'hooks/useSearch';
|
||||||
import { VFC } from 'react';
|
import React, { VFC } from 'react';
|
||||||
import { SearchDescription } from './SearchDescription/SearchDescription';
|
import { SearchDescription } from './SearchDescription/SearchDescription';
|
||||||
import {
|
import {
|
||||||
SearchInstructions,
|
SearchInstructions,
|
||||||
@ -15,21 +15,8 @@ import {
|
|||||||
} from './SearchInstructions/SearchInstructions';
|
} from './SearchInstructions/SearchInstructions';
|
||||||
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
|
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
|
||||||
import { onEnter } from './onEnter';
|
import { onEnter } from './onEnter';
|
||||||
|
import { SearchHistory } from './SearchHistory';
|
||||||
const StyledPaper = styled(Paper)(({ theme }) => ({
|
import { SearchPaper } from '../Search';
|
||||||
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 StyledBox = styled(Box)(({ theme }) => ({
|
const StyledBox = styled(Box)(({ theme }) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@ -127,36 +114,21 @@ export const SearchSuggestions: VFC<SearchSuggestionsProps> = ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const onSavedQuery = () => {
|
|
||||||
onSuggestion(savedQuery || '');
|
|
||||||
trackEvent('search-filter-suggestions', {
|
|
||||||
props: {
|
|
||||||
eventType: 'saved query',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledPaper className='dropdown-outline'>
|
<SearchPaper className='dropdown-outline'>
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={Boolean(savedQuery)}
|
condition={Boolean(savedQuery)}
|
||||||
show={
|
show={
|
||||||
<>
|
<>
|
||||||
<StyledBox>
|
<SearchHistory
|
||||||
<StyledHistory />
|
onSuggestion={onSuggestion}
|
||||||
<StyledCode
|
savedQuery={savedQuery}
|
||||||
tabIndex={0}
|
/>
|
||||||
onClick={onSavedQuery}
|
|
||||||
onKeyDown={onEnter(onSavedQuery)}
|
|
||||||
>
|
|
||||||
<span>{savedQuery}</span>
|
|
||||||
</StyledCode>
|
|
||||||
</StyledBox>
|
|
||||||
<StyledDivider />
|
<StyledDivider />
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<StyledBox>
|
<StyledBox>
|
||||||
<StyledFilterList />
|
<StyledFilterList />
|
||||||
<Box>
|
<Box>
|
||||||
@ -198,6 +170,6 @@ export const SearchSuggestions: VFC<SearchSuggestionsProps> = ({
|
|||||||
<span>{suggestedTextSearch}</span>
|
<span>{suggestedTextSearch}</span>
|
||||||
</StyledCode>
|
</StyledCode>
|
||||||
</Box>
|
</Box>
|
||||||
</StyledPaper>
|
</SearchPaper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -282,6 +282,7 @@ const FeatureToggleListTableComponent: VFC = () => {
|
|||||||
tableState.query || ''
|
tableState.query || ''
|
||||||
}
|
}
|
||||||
onChange={setSearchValue}
|
onChange={setSearchValue}
|
||||||
|
id='globalFeatureToggles'
|
||||||
/>
|
/>
|
||||||
<PageHeader.Divider />
|
<PageHeader.Divider />
|
||||||
</>
|
</>
|
||||||
@ -331,6 +332,7 @@ const FeatureToggleListTableComponent: VFC = () => {
|
|||||||
<Search
|
<Search
|
||||||
initialValue={tableState.query || ''}
|
initialValue={tableState.query || ''}
|
||||||
onChange={setSearchValue}
|
onChange={setSearchValue}
|
||||||
|
id='globalFeatureToggles'
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
Loading…
Reference in New Issue
Block a user