[WEB-1201] chore: dropdown options hierarchy improvements (#8501)

* chore: sortBySelectedFirst and sortByCurrentUserThenSelected utils added

* chore: members dropdown updated

* chore: module dropdown updated

* chore: project and label dropdown updated

* chore: code refactor
This commit is contained in:
Anmol Singh Bhatia 2026-02-13 18:50:18 +05:30 committed by GitHub
parent 7607cc9b10
commit bf521b7b03
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 107 additions and 14 deletions

View file

@ -183,6 +183,7 @@ export const MemberDropdownBase = observer(function MemberDropdownBase(props: TM
optionsClassName={optionsClassName}
placement={placement}
referenceElement={referenceElement}
value={value}
/>
)}
</ComboDropDown>

View file

@ -17,7 +17,7 @@ import { CheckIcon, SearchIcon, SuspendedUserIcon } from "@plane/propel/icons";
import { EPillSize, EPillVariant, Pill } from "@plane/propel/pill";
import type { IUserLite } from "@plane/types";
import { Avatar } from "@plane/ui";
import { cn, getFileURL } from "@plane/utils";
import { cn, getFileURL, sortByCurrentUserThenSelected } from "@plane/utils";
// hooks
import { useMember } from "@/hooks/store/use-member";
import { useUser } from "@/hooks/store/user";
@ -32,6 +32,7 @@ interface Props {
optionsClassName?: string;
placement: Placement | undefined;
referenceElement: HTMLButtonElement | null;
value?: string[] | string | null;
}
export const MemberOptions = observer(function MemberOptions(props: Props) {
@ -43,6 +44,7 @@ export const MemberOptions = observer(function MemberOptions(props: Props) {
optionsClassName = "",
placement,
referenceElement,
value,
} = props;
// router
const { workspaceSlug } = useParams();
@ -117,8 +119,11 @@ export const MemberOptions = observer(function MemberOptions(props: Props) {
})
.filter((o) => !!o);
const filteredOptions =
query === "" ? options : options?.filter((o) => o?.query.toLowerCase().includes(query.toLowerCase()));
const filteredOptions = sortByCurrentUserThenSelected(
query === "" ? options : options?.filter((o) => o?.query.toLowerCase().includes(query.toLowerCase())),
value,
currentUser?.id
);
return createPortal(
<Combobox.Options data-prevent-outside-click static>

View file

@ -193,6 +193,7 @@ export const ModuleDropdownBase = observer(function ModuleDropdownBase(props: TM
multiple={multiple}
getModuleById={getModuleById}
moduleIds={moduleIds}
value={value}
/>
)}
</ComboDropDown>

View file

@ -13,7 +13,7 @@ import { Combobox } from "@headlessui/react";
import { useTranslation } from "@plane/i18n";
import { CheckIcon, SearchIcon, ModuleIcon } from "@plane/propel/icons";
import type { IModule } from "@plane/types";
import { cn } from "@plane/utils";
import { cn, sortBySelectedFirst } from "@plane/utils";
// hooks
import { usePlatformOS } from "@/hooks/use-platform-os";
@ -33,10 +33,11 @@ interface Props {
onDropdownOpen?: () => void;
placement: Placement | undefined;
referenceElement: HTMLButtonElement | null;
value?: string[] | string | null;
}
export const ModuleOptions = observer(function ModuleOptions(props: Props) {
const { getModuleById, isOpen, moduleIds, multiple, onDropdownOpen, placement, referenceElement } = props;
const { getModuleById, isOpen, moduleIds, multiple, onDropdownOpen, placement, referenceElement, value } = props;
// refs
const inputRef = useRef<HTMLInputElement | null>(null);
// states
@ -106,8 +107,10 @@ export const ModuleOptions = observer(function ModuleOptions(props: Props) {
),
});
const filteredOptions =
query === "" ? options : options?.filter((o) => o.query.toLowerCase().includes(query.toLowerCase()));
const filteredOptions = sortBySelectedFirst(
query === "" ? options : options?.filter((o) => o.query.toLowerCase().includes(query.toLowerCase())),
value
);
return (
<Combobox.Options className="fixed z-10" static>

View file

@ -14,7 +14,7 @@ import { useTranslation } from "@plane/i18n";
import { Logo } from "@plane/propel/emoji-icon-picker";
import { CheckIcon, SearchIcon, ProjectIcon, ChevronDownIcon } from "@plane/propel/icons";
import { ComboDropDown } from "@plane/ui";
import { cn } from "@plane/utils";
import { cn, sortBySelectedFirst } from "@plane/utils";
// components
// hooks
import { useDropdown } from "@/hooks/use-dropdown";
@ -116,10 +116,13 @@ export const ProjectDropdownBase = observer(function ProjectDropdownBase(props:
};
});
const filteredOptions =
query === ""
const filteredOptions = sortBySelectedFirst(
(query === ""
? options?.filter((o) => o?.value !== currentProjectId)
: options?.filter((o) => o?.value !== currentProjectId && o?.query.toLowerCase().includes(query.toLowerCase()));
: options?.filter((o) => o?.value !== currentProjectId && o?.query.toLowerCase().includes(query.toLowerCase()))
)?.filter((o): o is NonNullable<typeof o> => o !== undefined),
value
);
const { handleClose, handleKeyDown, handleOnClick, searchInputKeyDown } = useDropdown({
dropdownRef,

View file

@ -20,6 +20,7 @@ import type { IIssueLabel } from "@plane/types";
import { EUserProjectRoles } from "@plane/types";
// components
import { ComboDropDown } from "@plane/ui";
import { sortBySelectedFirst } from "@plane/utils";
// hooks
import { useLabel } from "@/hooks/store/use-label";
import { useUserPermissions } from "@/hooks/store/user";
@ -118,8 +119,11 @@ export function LabelDropdown(props: ILabelDropdownProps) {
const filteredOptions = useMemo(
() =>
query === "" ? options : options?.filter((option) => option.query.toLowerCase().includes(query.toLowerCase())),
[options, query]
sortBySelectedFirst(
query === "" ? options : options?.filter((option) => option.query.toLowerCase().includes(query.toLowerCase())),
value
),
[options, query, value]
);
const { styles, attributes } = usePopper(referenceElement, popperElement, {
@ -270,7 +274,7 @@ export function LabelDropdown(props: ILabelDropdownProps) {
<div className={`mt-2 max-h-48 space-y-1 overflow-y-scroll`}>
{isLoading ? (
<p className="text-center text-secondary">{t("common.loading")}</p>
) : filteredOptions.length > 0 ? (
) : filteredOptions && filteredOptions.length > 0 ? (
filteredOptions.map((option) => (
<Combobox.Option
key={option.value}