-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(query): Add a "Search" tab in the Sidebar for wildcard queries. #107
Changes from 5 commits
5094abf
dcada93
ac22041
908b6db
b931505
3ddd96a
2cbb032
0889361
d8cd28c
4709312
a8a1551
67957ca
640b3e3
477ded6
e1dc2df
ec8cc58
96606b7
854293f
30481f9
8a81a87
2c10ab7
fb12080
34a8043
f71269e
e002f4f
8793d0b
580b876
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
.result-button { | ||
user-select: none; | ||
|
||
overflow: hidden; | ||
display: flex !important; | ||
|
||
text-overflow: ellipsis; | ||
white-space: nowrap; | ||
} | ||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
.result-text { | ||
display: inline !important; | ||
font-size: 0.875rem !important; | ||
font-weight: 400 !important; | ||
} | ||
|
||
.before-match, | ||
.match, | ||
.after-match { | ||
flex: 1 1 0; /* Allow each item to take an equal part */ | ||
overflow: hidden; | ||
text-overflow: ellipsis; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Consider performance optimizations for text truncation. Given the PR objectives mentioning browser freezing with large search results, consider:
.before-match,
.match,
.after-match {
- flex: 1 1 0; /* Allow each item to take an equal part */
- overflow: hidden;
+ flex: 1 1 0;
+ contain: content;
+ overflow: hidden;
text-overflow: ellipsis;
} Also, fix the linting issues by reordering properties: .before-match,
.match,
.after-match {
+ overflow: hidden;
flex: 1 1 0;
- overflow: hidden;
text-overflow: ellipsis;
}
🧰 Tools🪛 GitHub Check: lint-check[failure] 20-20: Stylelint problem [failure] 21-21: Stylelint problem |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { | ||
ListItemButton, | ||
Typography, | ||
} from "@mui/joy"; | ||
|
||
import "./Result.css"; | ||
|
||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
interface ResultProps { | ||
message: string, | ||
matchRange: [number, number] | ||
} | ||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/** | ||
* Displays a button containing a message, which highlights a specific range of text. | ||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* | ||
* @param props | ||
* @param props.message | ||
* @param props.matchRange A two-element array indicating the start and end indices of the substring | ||
* to be highlighted. | ||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* @return | ||
*/ | ||
const Result = ({message, matchRange}: ResultProps) => { | ||
const [ | ||
beforeMatch, | ||
match, | ||
afterMatch, | ||
] = [ | ||
message.slice(0, matchRange[0]), | ||
message.slice(...matchRange), | ||
message.slice(matchRange[1]), | ||
]; | ||
|
||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return ( | ||
<ListItemButton className={"result-button"}> | ||
<Typography | ||
className={"result-text before-match"} | ||
level={"body-xs"} | ||
> | ||
{beforeMatch} | ||
</Typography> | ||
<Typography | ||
className={"result-text match"} | ||
level={"body-xs"} | ||
sx={{ | ||
backgroundColor: "warning.softBg", | ||
}} | ||
> | ||
{match} | ||
</Typography> | ||
<Typography | ||
className={"result-text after-match"} | ||
level={"body-xs"} | ||
> | ||
{afterMatch} | ||
</Typography> | ||
</ListItemButton> | ||
); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Enhance accessibility and error handling. The component needs improvements in:
- <ListItemButton className={"result-button"}>
+ <ListItemButton
+ className={"result-button"}
+ role="option"
+ aria-selected={false}
+ onKeyDown={(e) => {
+ if (e.key === 'Enter' || e.key === ' ') {
+ // Handle selection
+ }
+ }}
+ >
<Typography
className={"result-text before-match"}
level={"body-xs"}
+ component="span"
>
{beforeMatch}
</Typography>
<Typography
className={"result-text match"}
level={"body-xs"}
+ component="mark"
sx={{
backgroundColor: "warning.softBg",
}}
+ aria-label={`Matched text: ${match}`}
>
{match}
</Typography> |
||
}; | ||
|
||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
export default Result; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
.results-group-title-button { | ||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
flex-direction: row-reverse !important; | ||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
gap: 2px !important; | ||
padding-inline-start: 0 !important; | ||
} | ||
|
||
.results-group-title-container { | ||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
display: flex; | ||
flex-grow: 1; | ||
} | ||
|
||
.results-group-title-text-container { | ||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
flex-grow: 1; | ||
gap: 0.1rem; | ||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
align-items: center; | ||
} | ||
|
||
.results-group-content { | ||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
margin-left: 1.5px !important; | ||
border-left: 1px solid var(--joy-palette-neutral-outlinedBorder, #cdd7e1); | ||
Check failure on line 20 in src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/ResultsGroup.css
|
||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,126 @@ | ||||||||||||||||||||||||||||||||||
import { | ||||||||||||||||||||||||||||||||||
useEffect, | ||||||||||||||||||||||||||||||||||
useState, | ||||||||||||||||||||||||||||||||||
} from "react"; | ||||||||||||||||||||||||||||||||||
import * as React from "react"; | ||||||||||||||||||||||||||||||||||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
import { | ||||||||||||||||||||||||||||||||||
Accordion, | ||||||||||||||||||||||||||||||||||
AccordionDetails, | ||||||||||||||||||||||||||||||||||
AccordionSummary, | ||||||||||||||||||||||||||||||||||
Box, | ||||||||||||||||||||||||||||||||||
Chip, | ||||||||||||||||||||||||||||||||||
List, | ||||||||||||||||||||||||||||||||||
Stack, | ||||||||||||||||||||||||||||||||||
Typography, | ||||||||||||||||||||||||||||||||||
} from "@mui/joy"; | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
import DescriptionOutlinedIcon from "@mui/icons-material/DescriptionOutlined"; | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
import {QueryResults} from "../../../../../typings/worker"; | ||||||||||||||||||||||||||||||||||
import Result from "./Result"; | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
import "./ResultsGroup.css"; | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
interface ResultsGroupProps { | ||||||||||||||||||||||||||||||||||
isAllExpanded: boolean, | ||||||||||||||||||||||||||||||||||
queryResults: QueryResults, | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||
Check warning on line 31 in src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/ResultsGroup.tsx
|
||||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||||
* @param props | ||||||||||||||||||||||||||||||||||
* @param props.isAllExpanded | ||||||||||||||||||||||||||||||||||
* @param props.queryResults | ||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||
const ResultsGroup = ({ | ||||||||||||||||||||||||||||||||||
isAllExpanded, | ||||||||||||||||||||||||||||||||||
queryResults, | ||||||||||||||||||||||||||||||||||
}: ResultsGroupProps) => { | ||||||||||||||||||||||||||||||||||
const [expandedMap, setExpandedMap] = useState<Record<number, boolean>>({}); | ||||||||||||||||||||||||||||||||||
const handleAccordionChange = ( | ||||||||||||||||||||||||||||||||||
pageNum: number | ||||||||||||||||||||||||||||||||||
) => (_: React.SyntheticEvent, newValue: boolean) => { | ||||||||||||||||||||||||||||||||||
setExpandedMap((prev) => ({ | ||||||||||||||||||||||||||||||||||
...prev, | ||||||||||||||||||||||||||||||||||
[pageNum]: newValue, | ||||||||||||||||||||||||||||||||||
})); | ||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
// On `isAllExpanded` updates, sync current results group's expand status. | ||||||||||||||||||||||||||||||||||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||
useEffect(() => { | ||||||||||||||||||||||||||||||||||
const newExpandedMap = Object.fromEntries( | ||||||||||||||||||||||||||||||||||
Object.entries(expandedMap).map(([key]) => [key, | ||||||||||||||||||||||||||||||||||
isAllExpanded]) | ||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
setExpandedMap(newExpandedMap); | ||||||||||||||||||||||||||||||||||
}, [isAllExpanded]); | ||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Include The Option 1: Include - }, [isAllExpanded]);
+ }, [isAllExpanded, expandedMap]); Option 2: Refactor the useEffect(() => {
- const newExpandedMap = Object.fromEntries(
- Object.entries(expandedMap).map(([key]) => [key, isAllExpanded])
- );
-
- setExpandedMap(newExpandedMap);
+ setExpandedMap((prevExpandedMap) => {
+ const newExpandedMap = Object.fromEntries(
+ Object.keys(prevExpandedMap).map((key) => [key, isAllExpanded])
+ );
+ return newExpandedMap;
+ });
}, [isAllExpanded]); 📝 Committable suggestion
Suggested change
🧰 Tools🪛 GitHub Check: lint-check[warning] 59-59: |
||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
useEffect(() => { | ||||||||||||||||||||||||||||||||||
setExpandedMap((prevMap) => { | ||||||||||||||||||||||||||||||||||
const updatedMap = {...prevMap}; | ||||||||||||||||||||||||||||||||||
queryResults.forEach((_, pageNum) => { | ||||||||||||||||||||||||||||||||||
// Only set for entries not already in expandedMap | ||||||||||||||||||||||||||||||||||
if (!(pageNum in updatedMap)) { | ||||||||||||||||||||||||||||||||||
updatedMap[pageNum] = isAllExpanded; | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
return updatedMap; | ||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||
}, [ | ||||||||||||||||||||||||||||||||||
isAllExpanded, | ||||||||||||||||||||||||||||||||||
queryResults, | ||||||||||||||||||||||||||||||||||
]); | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
return ( | ||||||||||||||||||||||||||||||||||
<> | ||||||||||||||||||||||||||||||||||
{Array.from(queryResults.entries()).map(([pageNum, results]) => ( | ||||||||||||||||||||||||||||||||||
<Accordion | ||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Implement immediate performance optimizations Given the reported browser freezing issues, here are some immediate optimizations to implement while working on virtualization:
+const INITIAL_RESULTS_LIMIT = 50;
+
const ResultsGroup = ({
isAllExpanded,
queryResults,
}: ResultsGroupProps) => {
+ const [resultsLimit, setResultsLimit] = useState(INITIAL_RESULTS_LIMIT);
+
// ... existing code ...
return (
<>
{Array.from(queryResults.entries()).map(([pageNum, results]) => (
<Accordion
// ... existing props ...
>
// ... existing AccordionSummary ...
<AccordionDetails className={"results-group-content"}>
<List size={"sm"}>
- {results.map((r) => (
+ {results.slice(0, resultsLimit).map((r) => (
<Result
key={r.logEventNum}
matchRange={r.matchRange}
message={r.message}/>
))}
+ {results.length > resultsLimit && (
+ <Button
+ onClick={() => setResultsLimit(prev => prev + INITIAL_RESULTS_LIMIT)}
+ variant="outlined"
+ fullWidth
+ >
+ Show More Results
+ </Button>
+ )}
</List>
</AccordionDetails>
</Accordion>
))}
</>
);
}; Also applies to: 114-119 |
||||||||||||||||||||||||||||||||||
expanded={expandedMap[pageNum] ?? isAllExpanded} | ||||||||||||||||||||||||||||||||||
key={pageNum} | ||||||||||||||||||||||||||||||||||
onChange={handleAccordionChange(pageNum)} | ||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||
<AccordionSummary | ||||||||||||||||||||||||||||||||||
slotProps={{ | ||||||||||||||||||||||||||||||||||
button: {className: "results-group-title-button"}, | ||||||||||||||||||||||||||||||||||
}} | ||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||
<Box className={"results-group-title-container"}> | ||||||||||||||||||||||||||||||||||
<Stack | ||||||||||||||||||||||||||||||||||
className={"results-group-title-text-container"} | ||||||||||||||||||||||||||||||||||
direction={"row"} | ||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||
<DescriptionOutlinedIcon fontSize={"inherit"}/> | ||||||||||||||||||||||||||||||||||
<Typography | ||||||||||||||||||||||||||||||||||
level={"title-sm"} | ||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||
Page | ||||||||||||||||||||||||||||||||||
{" "} | ||||||||||||||||||||||||||||||||||
{pageNum} | ||||||||||||||||||||||||||||||||||
</Typography> | ||||||||||||||||||||||||||||||||||
</Stack> | ||||||||||||||||||||||||||||||||||
<Chip size={"sm"}> | ||||||||||||||||||||||||||||||||||
{results.length} | ||||||||||||||||||||||||||||||||||
</Chip> | ||||||||||||||||||||||||||||||||||
</Box> | ||||||||||||||||||||||||||||||||||
</AccordionSummary> | ||||||||||||||||||||||||||||||||||
<AccordionDetails className={"results-group-content"}> | ||||||||||||||||||||||||||||||||||
<List size={"sm"}> | ||||||||||||||||||||||||||||||||||
{results.map((r) => ( | ||||||||||||||||||||||||||||||||||
<Result | ||||||||||||||||||||||||||||||||||
key={r.logEventNum} | ||||||||||||||||||||||||||||||||||
matchRange={r.matchRange} | ||||||||||||||||||||||||||||||||||
message={r.message}/> | ||||||||||||||||||||||||||||||||||
))} | ||||||||||||||||||||||||||||||||||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||
</List> | ||||||||||||||||||||||||||||||||||
</AccordionDetails> | ||||||||||||||||||||||||||||||||||
</Accordion> | ||||||||||||||||||||||||||||||||||
))} | ||||||||||||||||||||||||||||||||||
</> | ||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||
export default ResultsGroup; |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,112 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
useContext, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
useRef, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
useState, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} from "react"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
AccordionGroup, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
IconButton, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Textarea, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ToggleButtonGroup, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} from "@mui/joy"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import UnfoldLessIcon from "@mui/icons-material/UnfoldLess"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import UnfoldMoreIcon from "@mui/icons-material/UnfoldMore"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import {StateContext} from "../../../../../contexts/StateContextProvider"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
TAB_DISPLAY_NAMES, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
TAB_NAME, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} from "../../../../../typings/tab"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import CustomTabPanel from "../CustomTabPanel"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import TitleButton from "../TitleButton"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import ResultsGroup from "./ResultsGroup"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
enum SEARCH_OPTION { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
IS_CASE_SENSITIVE = "isCaseSensitive", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
IS_REGEX = "isRegex" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* Displays a panel for submitting search queries and viewing query results. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* @return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const SearchTabPanel = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const [isAllExpanded, setIsAllExpanded] = useState<boolean>(true); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const [searchOptions, setSearchOptions] = useState<SEARCH_OPTION[]>([]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const searchTextRef = useRef<HTMLTextAreaElement>(null); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const {queryResults, startQuery} = useContext(StateContext); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const handleSearch = (event: React.KeyboardEvent<HTMLTextAreaElement>) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if ("Enter" === event.key && searchTextRef.current) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
event.preventDefault(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const isCaseSensitive = searchOptions.includes(SEARCH_OPTION.IS_CASE_SENSITIVE); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const isRegex = searchOptions.includes(SEARCH_OPTION.IS_REGEX); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
startQuery(searchTextRef.current.value, isRegex, isCaseSensitive); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add performance optimizations and error handling. Given the reported issues with browser freezing on large result sets, consider implementing:
Here's a suggested implementation: + import { useState, useRef, useContext, useCallback } from 'react';
+ import debounce from 'lodash/debounce';
const SearchTabPanel = () => {
const [isAllExpanded, setIsAllExpanded] = useState<boolean>(true);
const [searchOptions, setSearchOptions] = useState<SEARCH_OPTION[]>([]);
+ const [isLoading, setIsLoading] = useState<boolean>(false);
+ const [error, setError] = useState<string | null>(null);
const searchTextRef = useRef<HTMLTextAreaElement>(null);
const {queryResults, startQuery} = useContext(StateContext);
- const handleSearch = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
+ const executeSearch = useCallback(async (searchText: string) => {
+ try {
+ setIsLoading(true);
+ setError(null);
const isCaseSensitive = searchOptions.includes(SEARCH_OPTION.IS_CASE_SENSITIVE);
const isRegex = searchOptions.includes(SEARCH_OPTION.IS_REGEX);
- startQuery(searchTextRef.current.value, isRegex, isCaseSensitive);
+ await startQuery(searchText, isRegex, isCaseSensitive);
+ } catch (err) {
+ setError(err instanceof Error ? err.message : 'Search failed');
+ } finally {
+ setIsLoading(false);
+ }
+ }, [searchOptions, startQuery]);
+ const debouncedSearch = useCallback(
+ debounce((searchText: string) => executeSearch(searchText), 300),
+ [executeSearch]
+ );
+ const handleSearch = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
if ("Enter" === event.key && searchTextRef.current) {
event.preventDefault();
+ debouncedSearch(searchTextRef.current.value);
}
}; 📝 Committable suggestion
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Enhance search handler with debouncing and error handling. The current implementation may cause performance issues with large result sets. Consider implementing:
+import { debounce } from 'lodash';
+
const SearchTabPanel = () => {
+ const [isLoading, setIsLoading] = useState(false);
+ const [error, setError] = useState<string | null>(null);
+
+ const executeSearch = useCallback(async (
+ searchText: string,
+ isRegex: boolean,
+ isCaseSensitive: boolean
+ ) => {
+ try {
+ if (isRegex) {
+ new RegExp(searchText); // Validate regex
+ }
+ setIsLoading(true);
+ setError(null);
+ await startQuery(searchText, isRegex, isCaseSensitive);
+ } catch (err) {
+ setError(err instanceof Error ? err.message : 'Search failed');
+ } finally {
+ setIsLoading(false);
+ }
+ }, [startQuery]);
+
+ const debouncedSearch = useMemo(
+ () => debounce(executeSearch, 300),
+ [executeSearch]
+ );
+
const handleSearch = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
if ("Enter" === event.key && searchTextRef.current) {
event.preventDefault();
const isCaseSensitive = searchOptions.includes(SEARCH_OPTION.IS_CASE_SENSITIVE);
const isRegex = searchOptions.includes(SEARCH_OPTION.IS_REGEX);
- startQuery(searchTextRef.current.value, isRegex, isCaseSensitive);
+ debouncedSearch(searchTextRef.current.value, isRegex, isCaseSensitive);
}
};
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<CustomTabPanel | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
tabName={TAB_NAME.SEARCH} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
title={TAB_DISPLAY_NAMES[TAB_NAME.SEARCH]} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
titleButtons={ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<TitleButton onClick={() => { setIsAllExpanded((v) => !v); }}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{isAllExpanded ? | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<UnfoldLessIcon/> : | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<UnfoldMoreIcon/>} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</TitleButton> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<Textarea | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
maxRows={7} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
placeholder={"Search"} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
size={"sm"} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sx={{flexDirection: "row"}} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
endDecorator={ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<ToggleButtonGroup | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
size={"sm"} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
spacing={0.3} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sx={{borderRadius: "2px"}} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
value={searchOptions} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
variant={"plain"} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
onChange={(_, newValue) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setSearchOptions(newValue); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<IconButton | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sx={{fontFamily: "Inter"}} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
value={SEARCH_OPTION.IS_CASE_SENSITIVE} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Aa | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</IconButton> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<IconButton | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sx={{fontFamily: "Inter"}} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
value={SEARCH_OPTION.IS_REGEX} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.* | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</IconButton> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</ToggleButtonGroup> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
slotProps={{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
textarea: {ref: searchTextRef}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
endDecorator: {sx: {marginBlockStart: 0, display: "block"}}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
onKeyDown={handleSearch}/> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<AccordionGroup | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
disableDivider={true} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
size={"sm"} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<ResultsGroup | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
isAllExpanded={isAllExpanded} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
queryResults={queryResults}/> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</AccordionGroup> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Optimize rendering performance for large result sets. To address the re-rendering issues mentioned in the PR comments, consider implementing:
+ import { memo } from 'react';
+ import { FixedSizeList } from 'react-window';
- <AccordionGroup
- disableDivider={true}
- size={"sm"}
- >
- <ResultsGroup
- isAllExpanded={isAllExpanded}
- queryResults={queryResults}/>
- </AccordionGroup>
+ const MemoizedResultsGroup = memo(ResultsGroup);
+
+ <AccordionGroup
+ disableDivider={true}
+ size={"sm"}
+ >
+ <MemoizedResultsGroup
+ isAllExpanded={isAllExpanded}
+ queryResults={queryResults}/>
+ </AccordionGroup> Consider implementing virtualization if the results can be large: interface ResultRowProps {
index: number;
style: React.CSSProperties;
data: {
results: typeof queryResults;
isExpanded: boolean;
};
}
const ResultRow = memo(({ index, style, data }: ResultRowProps) => {
const result = data.results[index];
return (
<div style={style}>
<MemoizedResultsGroup
isAllExpanded={data.isExpanded}
queryResults={[result]}
/>
</div>
);
});
// In the render method:
<FixedSizeList
height={400}
width="100%"
itemCount={queryResults.length}
itemSize={50}
itemData={{ results: queryResults, isExpanded: isAllExpanded }}
>
{ResultRow}
</FixedSizeList> |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</CustomTabPanel> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
export default SearchTabPanel; |
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
.sidebar-tab-title-button { | ||
min-width: 0 !important; | ||
min-height: 0 !important; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider removing the
!important
flag from the display property.The use of
!important
suggests potential specificity issues. Consider increasing selector specificity or restructuring CSS to avoid specificity wars.If you need to override conflicting styles, consider using a more specific selector like: