1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-07-19 13:29:41 +02:00

feat(docker/containers): limit items on volume selector [EE-7077] (#11845)
Some checks failed
ci / build_images (map[arch:amd64 platform:linux version:]) (push) Has been cancelled
ci / build_images (map[arch:amd64 platform:windows version:1809]) (push) Has been cancelled
ci / build_images (map[arch:amd64 platform:windows version:ltsc2022]) (push) Has been cancelled
ci / build_images (map[arch:arm platform:linux version:]) (push) Has been cancelled
ci / build_images (map[arch:arm64 platform:linux version:]) (push) Has been cancelled
ci / build_images (map[arch:ppc64le platform:linux version:]) (push) Has been cancelled
ci / build_images (map[arch:s390x platform:linux version:]) (push) Has been cancelled
/ triage (push) Has been cancelled
Lint / Run linters (push) Has been cancelled
Test / test-client (push) Has been cancelled
Test / test-server (map[arch:amd64 platform:linux]) (push) Has been cancelled
Test / test-server (map[arch:amd64 platform:windows version:1809]) (push) Has been cancelled
Test / test-server (map[arch:amd64 platform:windows version:ltsc2022]) (push) Has been cancelled
Test / test-server (map[arch:arm64 platform:linux]) (push) Has been cancelled
ci / build_manifests (push) Has been cancelled

This commit is contained in:
Chaim Lev-Ari 2024-05-23 13:15:36 +03:00 committed by GitHub
parent d7b412eccc
commit 50fd7c6286
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 109 additions and 9 deletions

View file

@ -96,7 +96,7 @@
border-bottom-left-radius: 0; border-bottom-left-radius: 0;
} }
.input-group .portainer-selector-root .portainer-selector__control:not(:last-child) { .input-group .portainer-selector-root .portainer-selector__control:not(:last-child, .portainer-selector__control--menu-is-open) {
border-top-right-radius: 0; border-top-right-radius: 0;
border-bottom-right-radius: 0; border-bottom-right-radius: 0;
} }

View file

@ -1,12 +1,16 @@
import ReactSelectCreatable, { import ReactSelectCreatable, {
CreatableProps as ReactSelectCreatableProps, CreatableProps as ReactSelectCreatableProps,
} from 'react-select/creatable'; } from 'react-select/creatable';
import ReactSelectAsync, {
AsyncProps as ReactSelectAsyncProps,
} from 'react-select/async';
import ReactSelect, { import ReactSelect, {
GroupBase, GroupBase,
OptionsOrGroups,
Props as ReactSelectProps, Props as ReactSelectProps,
} from 'react-select'; } from 'react-select';
import clsx from 'clsx'; import clsx from 'clsx';
import { RefAttributes } from 'react'; import { RefAttributes, useMemo } from 'react';
import ReactSelectType from 'react-select/dist/declarations/src/Select'; import ReactSelectType from 'react-select/dist/declarations/src/Select';
import './ReactSelect.css'; import './ReactSelect.css';
@ -56,9 +60,24 @@ export function Select<
className, className,
isCreatable = false, isCreatable = false,
size = 'md', size = 'md',
...props ...props
}: Props<Option, IsMulti, Group> & AutomationTestingProps) { }: Props<Option, IsMulti, Group> &
AutomationTestingProps & {
isItemVisible?: (item: Option, search: string) => boolean;
}) {
const Component = isCreatable ? ReactSelectCreatable : ReactSelect; const Component = isCreatable ? ReactSelectCreatable : ReactSelect;
const { options } = props;
if ((options?.length || 0) > 1000) {
return (
<TooManyResultsSelector
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
size={size}
/>
);
}
return ( return (
<Component <Component
@ -84,3 +103,86 @@ export function Creatable<
/> />
); );
} }
export function Async<
Option = DefaultOption,
IsMulti extends boolean = false,
Group extends GroupBase<Option> = GroupBase<Option>,
>({
className,
size,
...props
}: ReactSelectAsyncProps<Option, IsMulti, Group> & { size?: 'sm' | 'md' }) {
return (
<ReactSelectAsync
className={clsx(className, 'portainer-selector-root', size)}
classNamePrefix="portainer-selector"
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
/>
);
}
export function TooManyResultsSelector<
Option = DefaultOption,
IsMulti extends boolean = false,
Group extends GroupBase<Option> = GroupBase<Option>,
>({
options,
isLoading,
getOptionValue,
isItemVisible = (item, search) =>
!!getOptionValue?.(item).toLowerCase().includes(search.toLowerCase()),
...props
}: RegularProps<Option, IsMulti, Group> & {
isItemVisible?: (item: Option, search: string) => boolean;
}) {
const defaultOptions = useMemo(() => options?.slice(0, 100), [options]);
return (
<Async
isLoading={isLoading}
getOptionValue={getOptionValue}
loadOptions={(search: string) =>
filterOptions<Option, Group>(options, isItemVisible, search)
}
defaultOptions={defaultOptions}
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
/>
);
}
function filterOptions<
Option = DefaultOption,
Group extends GroupBase<Option> = GroupBase<Option>,
>(
options: OptionsOrGroups<Option, Group> | undefined,
isItemVisible: (item: Option, search: string) => boolean,
search: string
): Promise<OptionsOrGroups<Option, Group> | undefined> {
return Promise.resolve<OptionsOrGroups<Option, Group> | undefined>(
options
?.filter((item) =>
isGroup(item)
? item.options.some((ni) => isItemVisible(ni, search))
: isItemVisible(item, search)
)
.slice(0, 100)
);
}
function isGroup<
Option = DefaultOption,
Group extends GroupBase<Option> = GroupBase<Option>,
>(option: Option | Group): option is Group {
if (!option) {
return false;
}
if (typeof option !== 'object') {
return false;
}
return 'options' in option;
}

View file

@ -22,16 +22,13 @@ export function VolumeSelector({
}, },
}); });
if (!volumesQuery.data) { const initialVolumes = volumesQuery.data || [];
return null;
}
const volumes = allowAuto const volumes = allowAuto
? [...volumesQuery.data, { Name: 'auto', Driver: '' }] ? [...initialVolumes, { Name: 'auto', Driver: '' }]
: volumesQuery.data; : initialVolumes;
const selectedValue = volumes.find((vol) => vol.Name === value); const selectedValue = volumes.find((vol) => vol.Name === value);
return ( return (
<Select <Select
placeholder="Select a volume" placeholder="Select a volume"
@ -47,6 +44,7 @@ export function VolumeSelector({
onChange={(vol) => onChange(vol?.Name)} onChange={(vol) => onChange(vol?.Name)}
inputId={inputId} inputId={inputId}
data-cy="docker-containers-volume-selector" data-cy="docker-containers-volume-selector"
size="sm"
/> />
); );
} }