mirror of
https://github.com/portainer/portainer.git
synced 2025-07-19 13:29:41 +02:00
chore(select): show data-cy react select [r8s-402] (#881)
This commit is contained in:
parent
96f2d69ae5
commit
89f6a94bd8
1 changed files with 79 additions and 8 deletions
|
@ -5,12 +5,14 @@ import ReactSelectAsync, {
|
||||||
AsyncProps as ReactSelectAsyncProps,
|
AsyncProps as ReactSelectAsyncProps,
|
||||||
} from 'react-select/async';
|
} from 'react-select/async';
|
||||||
import ReactSelect, {
|
import ReactSelect, {
|
||||||
|
components,
|
||||||
GroupBase,
|
GroupBase,
|
||||||
|
InputProps,
|
||||||
OptionsOrGroups,
|
OptionsOrGroups,
|
||||||
Props as ReactSelectProps,
|
Props as ReactSelectProps,
|
||||||
} from 'react-select';
|
} from 'react-select';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { RefAttributes, useMemo } from 'react';
|
import { RefAttributes, useMemo, useCallback } 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';
|
||||||
|
@ -52,6 +54,9 @@ type Props<
|
||||||
| CreatableProps<Option, IsMulti, Group>
|
| CreatableProps<Option, IsMulti, Group>
|
||||||
| RegularProps<Option, IsMulti, Group>;
|
| RegularProps<Option, IsMulti, Group>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DO NOT use this component directly, use PortainerSelect instead.
|
||||||
|
*/
|
||||||
export function Select<
|
export function Select<
|
||||||
Option = DefaultOption,
|
Option = DefaultOption,
|
||||||
IsMulti extends boolean = false,
|
IsMulti extends boolean = false,
|
||||||
|
@ -68,24 +73,37 @@ export function Select<
|
||||||
id: string;
|
id: string;
|
||||||
}) {
|
}) {
|
||||||
const Component = isCreatable ? ReactSelectCreatable : ReactSelect;
|
const Component = isCreatable ? ReactSelectCreatable : ReactSelect;
|
||||||
const { options } = props;
|
const {
|
||||||
|
options,
|
||||||
|
'data-cy': dataCy,
|
||||||
|
components: componentsProp,
|
||||||
|
...rest
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const memoizedComponents = useMemoizedSelectComponents<
|
||||||
|
Option,
|
||||||
|
IsMulti,
|
||||||
|
Group
|
||||||
|
>(dataCy, componentsProp);
|
||||||
|
|
||||||
if ((options?.length || 0) > 1000) {
|
if ((options?.length || 0) > 1000) {
|
||||||
return (
|
return (
|
||||||
<TooManyResultsSelector
|
<TooManyResultsSelector
|
||||||
|
size={size}
|
||||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
{...props}
|
{...props}
|
||||||
size={size}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Component
|
<Component
|
||||||
|
options={options}
|
||||||
className={clsx(className, 'portainer-selector-root', size)}
|
className={clsx(className, 'portainer-selector-root', size)}
|
||||||
classNamePrefix="portainer-selector"
|
classNamePrefix="portainer-selector"
|
||||||
|
components={memoizedComponents}
|
||||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
{...props}
|
{...rest}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -94,13 +112,25 @@ export function Creatable<
|
||||||
Option = DefaultOption,
|
Option = DefaultOption,
|
||||||
IsMulti extends boolean = false,
|
IsMulti extends boolean = false,
|
||||||
Group extends GroupBase<Option> = GroupBase<Option>,
|
Group extends GroupBase<Option> = GroupBase<Option>,
|
||||||
>({ className, ...props }: ReactSelectCreatableProps<Option, IsMulti, Group>) {
|
>({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: ReactSelectCreatableProps<Option, IsMulti, Group> & AutomationTestingProps) {
|
||||||
|
const { 'data-cy': dataCy, components: componentsProp, ...rest } = props;
|
||||||
|
|
||||||
|
const memoizedComponents = useMemoizedSelectComponents<
|
||||||
|
Option,
|
||||||
|
IsMulti,
|
||||||
|
Group
|
||||||
|
>(dataCy, componentsProp);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactSelectCreatable
|
<ReactSelectCreatable
|
||||||
className={clsx(className, 'portainer-selector-root')}
|
className={clsx(className, 'portainer-selector-root')}
|
||||||
classNamePrefix="portainer-selector"
|
classNamePrefix="portainer-selector"
|
||||||
|
components={memoizedComponents}
|
||||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
{...props}
|
{...rest}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -113,13 +143,24 @@ export function Async<
|
||||||
className,
|
className,
|
||||||
size,
|
size,
|
||||||
...props
|
...props
|
||||||
}: ReactSelectAsyncProps<Option, IsMulti, Group> & { size?: 'sm' | 'md' }) {
|
}: ReactSelectAsyncProps<Option, IsMulti, Group> & {
|
||||||
|
size?: 'sm' | 'md';
|
||||||
|
} & AutomationTestingProps) {
|
||||||
|
const { 'data-cy': dataCy, components: componentsProp, ...rest } = props;
|
||||||
|
|
||||||
|
const memoizedComponents = useMemoizedSelectComponents<
|
||||||
|
Option,
|
||||||
|
IsMulti,
|
||||||
|
Group
|
||||||
|
>(dataCy, componentsProp);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactSelectAsync
|
<ReactSelectAsync
|
||||||
className={clsx(className, 'portainer-selector-root', size)}
|
className={clsx(className, 'portainer-selector-root', size)}
|
||||||
classNamePrefix="portainer-selector"
|
classNamePrefix="portainer-selector"
|
||||||
|
components={memoizedComponents}
|
||||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
{...props}
|
{...rest}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -187,3 +228,33 @@ function isGroup<
|
||||||
|
|
||||||
return 'options' in option;
|
return 'options' in option;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Memoize components to prevent unnecessary re-renders.
|
||||||
|
*/
|
||||||
|
function useMemoizedSelectComponents<
|
||||||
|
Option = DefaultOption,
|
||||||
|
IsMulti extends boolean = false,
|
||||||
|
Group extends GroupBase<Option> = GroupBase<Option>,
|
||||||
|
>(
|
||||||
|
dataCy: string | undefined,
|
||||||
|
componentsProp: Partial<
|
||||||
|
ReactSelectProps<Option, IsMulti, Group>['components']
|
||||||
|
>
|
||||||
|
) {
|
||||||
|
const customInput = useCallback(
|
||||||
|
(inputProps: InputProps<Option, IsMulti, Group>) =>
|
||||||
|
components.Input({ ...inputProps, 'data-cy': dataCy }),
|
||||||
|
[dataCy]
|
||||||
|
);
|
||||||
|
|
||||||
|
const memoizedComponents = useMemo(
|
||||||
|
() => ({
|
||||||
|
Input: customInput,
|
||||||
|
...componentsProp,
|
||||||
|
}),
|
||||||
|
[customInput, componentsProp]
|
||||||
|
);
|
||||||
|
|
||||||
|
return memoizedComponents;
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue