|
@@ -16,7 +16,10 @@ import {useMenuTriggerState} from '@react-stately/menu';
|
|
|
import Badge from 'sentry/components/badge';
|
|
|
import Button from 'sentry/components/button';
|
|
|
import DropdownButton, {DropdownButtonProps} from 'sentry/components/dropdownButtonV2';
|
|
|
-import SelectControl, {ControlProps} from 'sentry/components/forms/selectControl';
|
|
|
+import SelectControl, {
|
|
|
+ ControlProps,
|
|
|
+ GeneralSelectValue,
|
|
|
+} from 'sentry/components/forms/selectControl';
|
|
|
import overflowEllipsis from 'sentry/styles/overflowEllipsis';
|
|
|
import space from 'sentry/styles/space';
|
|
|
|
|
@@ -25,7 +28,11 @@ interface TriggerRenderingProps {
|
|
|
ref: React.RefObject<HTMLButtonElement>;
|
|
|
}
|
|
|
|
|
|
-interface Props extends ControlProps, Partial<OverlayProps>, Partial<AriaPositionProps> {
|
|
|
+interface Props<OptionType>
|
|
|
+ extends Omit<ControlProps<OptionType>, 'choices'>,
|
|
|
+ Partial<OverlayProps>,
|
|
|
+ Partial<AriaPositionProps> {
|
|
|
+ options: Array<OptionType & {options?: OptionType[]}>;
|
|
|
/**
|
|
|
* Pass class name to the outer wrap
|
|
|
*/
|
|
@@ -54,6 +61,25 @@ interface Props extends ControlProps, Partial<OverlayProps>, Partial<AriaPositio
|
|
|
triggerProps?: DropdownButtonProps;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * Recursively finds the selected option(s) from an options array. Useful for
|
|
|
+ * non-flat arrays that contain sections (groups of options).
|
|
|
+ */
|
|
|
+function getSelectedOptions<OptionType extends GeneralSelectValue = GeneralSelectValue>(
|
|
|
+ opts: Props<OptionType>['options'],
|
|
|
+ value: Props<OptionType>['value']
|
|
|
+): Props<OptionType>['options'] {
|
|
|
+ return opts.reduce((acc: Props<OptionType>['options'], cur) => {
|
|
|
+ if (cur.options) {
|
|
|
+ return acc.concat(getSelectedOptions(cur.options, value));
|
|
|
+ }
|
|
|
+ if (cur.value === value) {
|
|
|
+ return acc.concat(cur);
|
|
|
+ }
|
|
|
+ return acc;
|
|
|
+ }, []);
|
|
|
+}
|
|
|
+
|
|
|
// Exported so we can further customize this component with react-select's
|
|
|
// components prop elsewhere
|
|
|
export const CompactSelectControl = ({
|
|
@@ -87,7 +113,7 @@ export const CompactSelectControl = ({
|
|
|
* A select component with a more compact trigger button. Accepts the same
|
|
|
* props as SelectControl, plus some more for the trigger button & overlay.
|
|
|
*/
|
|
|
-function CompactSelect({
|
|
|
+function CompactSelect<OptionType extends GeneralSelectValue = GeneralSelectValue>({
|
|
|
// Select props
|
|
|
options,
|
|
|
onChange,
|
|
@@ -113,7 +139,7 @@ function CompactSelect({
|
|
|
isDismissable = true,
|
|
|
menuTitle,
|
|
|
...props
|
|
|
-}: Props) {
|
|
|
+}: Props<OptionType>) {
|
|
|
// Manage the dropdown menu's open state
|
|
|
const isDisabled = disabledProp || options?.length === 0;
|
|
|
const triggerRef = useRef<HTMLButtonElement>(null);
|
|
@@ -157,7 +183,9 @@ function CompactSelect({
|
|
|
// Update the button label when the value changes
|
|
|
function getLabel(newValue): React.ReactNode {
|
|
|
const valueSet = Array.isArray(newValue) ? newValue : [newValue];
|
|
|
- const optionSet = valueSet.map(val => options.find(opt => opt.value === val));
|
|
|
+ const optionSet = valueSet
|
|
|
+ .map(val => getSelectedOptions<OptionType>(options, val))
|
|
|
+ .flat();
|
|
|
const firstOptionLabel = optionSet[0]?.label ?? '';
|
|
|
|
|
|
return (
|