-
Notifications
You must be signed in to change notification settings - Fork 12
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 9 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 { | ||
overflow: hidden; | ||
flex: 1 1 0; /* Allow each item to take an equal part */ | ||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
text-overflow: ellipsis; | ||
} |
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,22 @@ | ||
.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; | ||
/* stylelint-disable-next-line custom-property-pattern */ | ||
border-left: 1px solid var(--joy-palette-neutral-outlinedBorder, #cdd7e1); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
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 {QueryResultsType} from "../../../../../typings/worker"; | ||
import Result from "./Result"; | ||
|
||
import "./ResultsGroup.css"; | ||
|
||
|
||
interface ResultsGroupProps { | ||
isAllExpanded: boolean, | ||
pageNum: number, | ||
results: QueryResultsType[], | ||
} | ||
|
||
/** | ||
* Renders a group of results. Each group contains a list of results from a single page. | ||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* | ||
* @param props | ||
* @param props.isAllExpanded | ||
* @param props.pageNum | ||
* @param props.results | ||
* @return | ||
*/ | ||
const ResultsGroup = ({ | ||
isAllExpanded, | ||
pageNum, | ||
results, | ||
}: ResultsGroupProps) => { | ||
const [isExpanded, setIsExpanded] = useState<boolean>(isAllExpanded); | ||
|
||
const handleAccordionChange = ( | ||
_: React.SyntheticEvent, | ||
newValue: boolean | ||
) => { | ||
setIsExpanded(newValue); | ||
}; | ||
|
||
// On `isAllExpanded` updates, sync current results group's expand status. | ||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
useEffect(() => { | ||
setIsExpanded(isAllExpanded); | ||
}, [isAllExpanded]); | ||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
return ( | ||
<Accordion | ||
expanded={isExpanded} | ||
onChange={handleAccordionChange} | ||
> | ||
<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"} | ||
> | ||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Page | ||
{" "} | ||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{pageNum} | ||
</Typography> | ||
</Stack> | ||
<Chip size={"sm"}> | ||
{results.length} | ||
</Chip> | ||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
</Box> | ||
</AccordionSummary> | ||
<AccordionDetails className={"results-group-content"}> | ||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
<List size={"sm"}> | ||
{results.map((r, index) => ( | ||
<Result | ||
key={index} | ||
matchRange={r.matchRange} | ||
message={r.message}/> | ||
))} | ||
</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,116 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import React, { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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", zIndex: 0}} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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}/> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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 Improve search input accessibility and structure. The search input needs accessibility improvements and structural cleanup:
<Textarea
maxRows={7}
- placeholder={"Search"}
+ placeholder={"Enter search query"}
+ aria-label="Search input"
size={"sm"}
sx={{flexDirection: "row", zIndex: 0}}
endDecorator={
- <>
<ToggleButtonGroup
size={"sm"}
spacing={0.3}
sx={{borderRadius: "2px"}}
value={searchOptions}
variant={"plain"}
+ aria-label="Search options"
onChange={(_, newValue) => {
setSearchOptions(newValue);
}}
>
<IconButton
sx={{fontFamily: "Inter"}}
value={SEARCH_OPTION.IS_CASE_SENSITIVE}
+ aria-label="Toggle case sensitivity"
>
Aa
</IconButton>
<IconButton
sx={{fontFamily: "Inter"}}
value={SEARCH_OPTION.IS_REGEX}
+ aria-label="Toggle regex mode"
>
.*
</IconButton>
</ToggleButtonGroup>
- </>
}
slotProps={{
textarea: {ref: searchTextRef},
endDecorator: {sx: {marginBlockStart: 0, display: "block"}},
}}
- onKeyDown={handleSearch}/>
+ onKeyDown={handleSearch}
+ disabled={isLoading}
+/>
+{isLoading && <CircularProgress size="sm" />}
+{error && <Alert color="danger">{error}</Alert>}
🧰 Tools🪛 Biome[error] 69-93: Avoid using unnecessary Fragment. A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment. (lint/complexity/noUselessFragments)
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<AccordionGroup | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
disableDivider={true} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
size={"sm"} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{Array.from(queryResults.entries()).map(([pageNum, results], index) => ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<ResultsGroup | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
isAllExpanded={isAllExpanded} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
key={index} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
pageNum={pageNum} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
results={results}/> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
))} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</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 results rendering for large datasets. To address the browser freezing issue with large result sets, implement:
+import { FixedSizeList } from 'react-window';
+
+const MemoizedResultsGroup = memo(ResultsGroup);
+
+const ResultRow = ({ index, style, data }) => {
+ const [pageNum, results] = data.results[index];
+ return (
+ <div style={style}>
+ <MemoizedResultsGroup
+ isAllExpanded={data.isAllExpanded}
+ pageNum={pageNum}
+ results={results}
+ />
+ </div>
+ );
+};
+
-<AccordionGroup
- disableDivider={true}
- size={"sm"}
->
- {Array.from(queryResults.entries()).map(([pageNum, results], index) => (
- <ResultsGroup
- isAllExpanded={isAllExpanded}
- key={index}
- pageNum={pageNum}
- results={results}/>
- ))}
-</AccordionGroup>
+<FixedSizeList
+ height={400}
+ width="100%"
+ itemCount={queryResults.size}
+ itemSize={50}
+ itemData={{
+ results: Array.from(queryResults.entries()),
+ 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,5 @@ | ||||||||||||||||||||||
.sidebar-tab-title-button { | ||||||||||||||||||||||
z-index: 0 !important; | ||||||||||||||||||||||
Henry8192 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||
min-width: 0 !important; | ||||||||||||||||||||||
min-height: 0 !important; | ||||||||||||||||||||||
} | ||||||||||||||||||||||
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 alternatives to Using
Example of using a more specific selector: -.sidebar-tab-title-button {
+.sidebar-tabs .MuiIconButton-root.sidebar-tab-title-button {
z-index: 0;
min-width: 0;
min-height: 0;
} 📝 Committable suggestion
Suggested change
|
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: