Skip to content

Commit 8671d6a

Browse files
committed
Minor adjustments
1 parent 2e103b4 commit 8671d6a

File tree

3 files changed

+103
-24
lines changed

3 files changed

+103
-24
lines changed

site/src/pages/WorkspacesPage/Filter.tsx

Lines changed: 101 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { WorkspaceStatuses } from "api/typesGenerated"
2-
import { FC, ReactNode, forwardRef, useRef, useState } from "react"
2+
import { FC, ReactNode, forwardRef, useMemo, useRef, useState } from "react"
33
import Box from "@mui/material/Box"
44
import TextField from "@mui/material/TextField"
55
import { UserAvatar } from "components/UserAvatar/UserAvatar"
@@ -127,12 +127,48 @@ const useAutocomplete = <TOption extends BaseOption = BaseOption>({
127127
const initialOptionQuery = useQuery({
128128
queryKey: [id, "autocomplete", "initial"],
129129
queryFn: () => getInitialOption(),
130-
onSuccess: setSelectedOption,
130+
onSuccess: (option) => setSelectedOption(option ?? undefined),
131131
})
132132
const searchOptionsQuery = useQuery({
133133
queryKey: [id, "autoComplete", "search"],
134134
queryFn: () => getOptions(query),
135135
})
136+
const searchOptions = useMemo(() => {
137+
const isDataLoaded =
138+
searchOptionsQuery.isFetched && initialOptionQuery.isFetched
139+
140+
if (!isDataLoaded) {
141+
return undefined
142+
}
143+
144+
let options = searchOptionsQuery.data as TOption[]
145+
146+
if (!selectedOption) {
147+
return options
148+
}
149+
150+
// We will add the initial option on the top of the options
151+
// 1 - remove the initial option from the search options if it exists
152+
// 2 - add the initial option on the top
153+
options = options.filter((option) => option.value !== selectedOption.value)
154+
options.unshift(selectedOption)
155+
156+
// Filter data based o search query
157+
options = options.filter(
158+
(option) =>
159+
option.label.toLowerCase().includes(query.toLowerCase()) ||
160+
option.value.toLowerCase().includes(query.toLowerCase()),
161+
)
162+
163+
return options
164+
}, [
165+
initialOptionQuery.isFetched,
166+
query,
167+
searchOptionsQuery.data,
168+
searchOptionsQuery.isFetched,
169+
selectedOption,
170+
])
171+
136172
const selectOption = (option: TOption) => {
137173
let newSelectedOptionValue: TOption | undefined = option
138174

@@ -145,16 +181,20 @@ const useAutocomplete = <TOption extends BaseOption = BaseOption>({
145181
}
146182
setSelectedOption(newSelectedOptionValue)
147183
}
184+
const clearSelection = () => {
185+
setSelectedOption(undefined)
186+
}
148187

149188
return {
150189
query,
151190
setQuery,
152191
selectedOption,
153192
selectOption,
193+
clearSelection,
154194
isInitializing: initialOptionQuery.isInitialLoading,
155195
initialOption: initialOptionQuery.data,
156196
isSearching: searchOptionsQuery.isFetching,
157-
searchOptions: searchOptionsQuery.data,
197+
searchOptions,
158198
}
159199
}
160200

@@ -330,7 +370,15 @@ export const Filter = ({
330370
endAdornment: hasFilterQuery && (
331371
<InputAdornment position="end">
332372
<Tooltip title="Clear filter">
333-
<IconButton size="small" onClick={() => filter.update("")}>
373+
<IconButton
374+
size="small"
375+
onClick={() => {
376+
filter.update("")
377+
autocomplete.users.clearSelection()
378+
autocomplete.templates.clearSelection()
379+
autocomplete.status.clearSelection()
380+
}}
381+
>
334382
<CloseOutlined sx={{ fontSize: 14 }} />
335383
</IconButton>
336384
</Tooltip>
@@ -372,6 +420,8 @@ const OwnerFilter = ({ autocomplete }: { autocomplete: UsersAutocomplete }) => {
372420
open={isMenuOpen}
373421
onClose={handleClose}
374422
options={autocomplete.searchOptions}
423+
query={autocomplete.query}
424+
onQueryChange={autocomplete.setQuery}
375425
renderOption={(option) => (
376426
<MenuItem
377427
key={option.label}
@@ -391,13 +441,21 @@ const OwnerFilter = ({ autocomplete }: { autocomplete: UsersAutocomplete }) => {
391441

392442
const UserOptionItem = ({ option }: { option: OwnerOption }) => {
393443
return (
394-
<Box display="flex" alignItems="center" gap={2} fontSize={14}>
444+
<Box
445+
display="flex"
446+
alignItems="center"
447+
gap={2}
448+
fontSize={14}
449+
overflow="hidden"
450+
>
395451
<UserAvatar
396452
username={option.label}
397453
avatarURL={option.avatarUrl}
398454
sx={{ width: 16, height: 16, fontSize: 8 }}
399455
/>
400-
<span>{option.label}</span>
456+
<Box component="span" overflow="hidden" textOverflow="ellipsis">
457+
{option.label}
458+
</Box>
401459
</Box>
402460
)
403461
}
@@ -433,6 +491,8 @@ const TemplatesFilter = ({
433491
open={isMenuOpen}
434492
onClose={handleClose}
435493
options={autocomplete.searchOptions}
494+
query={autocomplete.query}
495+
onQueryChange={autocomplete.setQuery}
436496
renderOption={(option) => (
437497
<MenuItem
438498
key={option.label}
@@ -452,13 +512,21 @@ const TemplatesFilter = ({
452512

453513
const TemplateOptionItem = ({ option }: { option: TemplateOption }) => {
454514
return (
455-
<Box display="flex" alignItems="center" gap={2} fontSize={14}>
515+
<Box
516+
display="flex"
517+
alignItems="center"
518+
gap={2}
519+
fontSize={14}
520+
overflow="hidden"
521+
>
456522
<TemplateAvatar
457523
templateName={option.label}
458524
icon={option.icon}
459525
sx={{ width: 14, height: 14, fontSize: 8 }}
460526
/>
461-
<span>{option.label}</span>
527+
<Box component="span" overflow="hidden" textOverflow="ellipsis">
528+
{option.label}
529+
</Box>
462530
</Box>
463531
)
464532
}
@@ -504,6 +572,13 @@ const StatusFilter = ({
504572
open={isMenuOpen}
505573
onClose={handleClose}
506574
sx={{ "& .MuiPaper-root": { minWidth: 200 } }}
575+
// Disabled this so when we clear the filter and do some sorting in the
576+
// search items it does not look strange. Github removes exit transitions
577+
// on their filters as well.
578+
transitionDuration={{
579+
enter: 250,
580+
exit: 0,
581+
}}
507582
>
508583
{autocomplete.searchOptions?.map((option) => (
509584
<MenuItem
@@ -553,8 +628,8 @@ const MenuButton = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
553628
{...props}
554629
sx={{
555630
borderRadius: "6px",
556-
lineHeight: 0,
557631
justifyContent: "space-between",
632+
lineHeight: 1,
558633
...props.sx,
559634
}}
560635
/>
@@ -564,36 +639,38 @@ const MenuButton = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
564639
function SearchMenu<TOption extends { label: string; value: string }>({
565640
options,
566641
renderOption,
642+
query,
643+
onQueryChange,
567644
...menuProps
568645
}: Pick<MenuProps, "anchorEl" | "open" | "onClose" | "id"> & {
569646
options?: TOption[]
570647
renderOption: (option: TOption) => ReactNode
648+
query: string
649+
onQueryChange: (query: string) => void
571650
}) {
572651
const menuListRef = useRef<HTMLUListElement>(null)
573652
const searchInputRef = useRef<HTMLInputElement>(null)
574-
const [searchInputValue, setSearchInputValue] = useState("")
575-
const visibleOptions = options
576-
? options.filter(
577-
(option) =>
578-
option.label.toLowerCase().includes(searchInputValue.toLowerCase()) ||
579-
option.value.toLowerCase().includes(searchInputValue.toLowerCase()),
580-
)
581-
: undefined
582653

583654
return (
584655
<Menu
585656
{...menuProps}
586657
onClose={(event, reason) => {
587658
menuProps.onClose && menuProps.onClose(event, reason)
588-
// 250ms is the transition time to close menu
589-
setTimeout(() => setSearchInputValue(""), 250)
659+
onQueryChange("")
590660
}}
591661
sx={{
592662
"& .MuiPaper-root": {
593663
width: 320,
594664
paddingY: 0,
595665
},
596666
}}
667+
// Disabled this so when we clear the filter and do some sorting in the
668+
// search items it does not look strange. Github removes exit transitions
669+
// on their filters as well.
670+
transitionDuration={{
671+
enter: 250,
672+
exit: 0,
673+
}}
597674
>
598675
<Box
599676
component="li"
@@ -624,10 +701,10 @@ function SearchMenu<TOption extends { label: string; value: string }>({
624701
type="text"
625702
placeholder="Search..."
626703
autoFocus
627-
value={searchInputValue}
704+
value={query}
628705
ref={searchInputRef}
629706
onChange={(e) => {
630-
setSearchInputValue(e.target.value)
707+
onQueryChange(e.target.value)
631708
}}
632709
sx={{
633710
height: "100%",
@@ -654,9 +731,9 @@ function SearchMenu<TOption extends { label: string; value: string }>({
654731
}
655732
}}
656733
>
657-
{visibleOptions ? (
658-
visibleOptions.length > 0 ? (
659-
visibleOptions.map(renderOption)
734+
{options ? (
735+
options.length > 0 ? (
736+
options.map(renderOption)
660737
) : (
661738
<Box
662739
sx={{

site/src/pages/WorkspacesPage/WorkspacesPageView.stories.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ const mockAutocomplete = {
8080
selectedOption: undefined,
8181
selectOption: action("selectOption"),
8282
setQuery: action("updateQuery"),
83+
clearSelection: action("clearSelection"),
8384
}
8485

8586
const defaultFilterProps = {

site/src/theme/theme.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,7 @@ dark = createTheme(dark, {
400400
tooltip: {
401401
lineHeight: "150%",
402402
borderRadius: 4,
403+
background: dark.palette.divider,
403404
},
404405
},
405406
},

0 commit comments

Comments
 (0)