Improve FilterableSelect to support optional grouping by option field

This commit is contained in:
carlosmonastyrski
2025-03-28 16:17:14 -03:00
parent 2a7679005e
commit e639f5ee49
3 changed files with 132 additions and 73 deletions
@@ -1,19 +1,54 @@
import Select, { Props } from "react-select"; import Select, { Props } from "react-select";
import { twMerge } from "tailwind-merge"; import { twMerge } from "tailwind-merge";
import { ClearIndicator, DropdownIndicator, MultiValueRemove, Option } from "../Select/components"; import {
ClearIndicator,
DropdownIndicator,
Group,
MultiValueRemove,
Option
} from "../Select/components";
export const FilterableSelect = <T,>({ export const FilterableSelect = <T,>({
isMulti, isMulti,
closeMenuOnSelect, closeMenuOnSelect,
tabSelectsValue = false, tabSelectsValue = false,
groupBy = null,
getGroupHeaderLabel = null,
options = [],
...props ...props
}: Props<T>) => ( }: Props<T> & {
groupBy?: string | null;
getGroupHeaderLabel?: ((groupValue: any) => string) | null;
}) => {
let processedOptions = options;
if (groupBy && Array.isArray(options)) {
const groupedOptions = options.reduce((acc, option) => {
const groupValue = option[groupBy];
const groupKey = groupValue?.toString() || "undefined";
if (!acc[groupKey]) {
acc[groupKey] = {
label: getGroupHeaderLabel ? getGroupHeaderLabel(groupValue) : groupValue,
options: []
};
}
acc[groupKey].options.push(option);
return acc;
}, {});
processedOptions = Object.values(groupedOptions);
}
return (
<Select <Select
isMulti={isMulti} isMulti={isMulti}
closeMenuOnSelect={closeMenuOnSelect ?? !isMulti} closeMenuOnSelect={closeMenuOnSelect ?? !isMulti}
hideSelectedOptions={false} hideSelectedOptions={false}
unstyled unstyled
options={processedOptions}
styles={{ styles={{
input: (base) => ({ input: (base) => ({
...base, ...base,
@@ -37,6 +72,7 @@ export const FilterableSelect = <T,>({
ClearIndicator, ClearIndicator,
MultiValueRemove, MultiValueRemove,
Option, Option,
Group,
...props.components ...props.components
}} }}
classNames={{ classNames={{
@@ -78,4 +114,5 @@ export const FilterableSelect = <T,>({
}} }}
{...props} {...props}
/> />
); );
};
@@ -2,6 +2,7 @@ import {
ClearIndicatorProps, ClearIndicatorProps,
components, components,
DropdownIndicatorProps, DropdownIndicatorProps,
GroupProps,
MultiValueRemoveProps, MultiValueRemoveProps,
OptionProps OptionProps
} from "react-select"; } from "react-select";
@@ -45,3 +46,7 @@ export const Option = <T,>({ isSelected, children, ...props }: OptionProps<T>) =
</components.Option> </components.Option>
); );
}; };
export const Group = <T,>(props: GroupProps<T>) => {
return <components.Group {...props} />;
};
@@ -23,7 +23,7 @@ import {
useGetUserWorkspaces useGetUserWorkspaces
} from "@app/hooks/api"; } from "@app/hooks/api";
import { ProjectMembershipRole } from "@app/hooks/api/roles/types"; import { ProjectMembershipRole } from "@app/hooks/api/roles/types";
import { ProjectVersion } from "@app/hooks/api/workspace/types"; import { ProjectType, ProjectVersion } from "@app/hooks/api/workspace/types";
import { UsePopUpState } from "@app/hooks/usePopUp"; import { UsePopUpState } from "@app/hooks/usePopUp";
import { OrgInviteLink } from "./OrgInviteLink"; import { OrgInviteLink } from "./OrgInviteLink";
@@ -168,6 +168,21 @@ export const AddOrgMemberModal = ({
reset(); reset();
}; };
const getGroupHeaderLabel = (type: ProjectType) => {
switch (type) {
case ProjectType.SecretManager:
return "Secrets";
case ProjectType.CertificateManager:
return "PKI";
case ProjectType.KMS:
return "KMS";
case ProjectType.SSH:
return "SSH";
default:
return "All";
}
};
return ( return (
<Modal <Modal
isOpen={popUp?.addMember?.isOpen} isOpen={popUp?.addMember?.isOpen}
@@ -247,6 +262,8 @@ export const AddOrgMemberModal = ({
getOptionLabel={(project) => project.name} getOptionLabel={(project) => project.name}
getOptionValue={(project) => project.id} getOptionValue={(project) => project.id}
options={projects} options={projects}
groupBy="type"
getGroupHeaderLabel={getGroupHeaderLabel}
placeholder="Select projects..." placeholder="Select projects..."
/> />
</FormControl> </FormControl>