Wizard: Replace deprecated select in AwsSourcesSelect

This replaces a deprecated select in `AwsSourcesSelect` for a non-deprecated one.
This commit is contained in:
regexowl 2025-03-10 10:07:13 +01:00 committed by Klara Simickova
parent 0f230c18d9
commit b767c3bfde
4 changed files with 136 additions and 47 deletions

View file

@ -1,12 +1,20 @@
import React, { useState } from 'react';
import React, { useEffect, useState } from 'react';
import { Alert, Spinner } from '@patternfly/react-core';
import { FormGroup } from '@patternfly/react-core';
import {
Alert,
Button,
FormGroup,
Select,
SelectList,
SelectOption,
SelectVariant,
} from '@patternfly/react-core/deprecated';
MenuToggle,
MenuToggleElement,
Spinner,
TextInputGroup,
TextInputGroupMain,
TextInputGroupUtilities,
} from '@patternfly/react-core';
import { TimesIcon } from '@patternfly/react-icons';
import { useAppDispatch, useAppSelector } from '../../../../../store/hooks';
import { useGetSourceListQuery } from '../../../../../store/provisioningApi';
@ -18,6 +26,8 @@ import {
export const AwsSourcesSelect = () => {
const dispatch = useAppDispatch();
const [isOpen, setIsOpen] = useState(false);
const [inputValue, setInputValue] = useState<string>('');
const [filterValue, setFilterValue] = useState<string>('');
const sourceId = useAppSelector(selectAwsSourceId);
const { data, isFetching, isLoading, isSuccess, isError, refetch } =
@ -28,6 +38,50 @@ export const AwsSourcesSelect = () => {
const sources = data?.data;
const chosenSource = sources?.find((source) => source.id === sourceId);
const [selectOptions, setSelectOptions] = useState<(string | undefined)[]>(
sources ? sources.map((source) => source.name) : []
);
useEffect(() => {
let filteredSources = sources?.map((source) => source.name);
if (sources && filterValue) {
filteredSources = sources
.map((source) => source.name)
.filter((source: string) =>
String(source).toLowerCase().includes(filterValue.toLowerCase())
);
if (!isOpen) {
setIsOpen(true);
}
}
if (filteredSources) {
setSelectOptions(filteredSources);
}
// This useEffect hook should run *only* on when the filter value changes.
// eslint's exhaustive-deps rule does not support this use.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [filterValue]);
const onInputClick = () => {
if (!isOpen) {
setIsOpen(true);
} else if (!inputValue) {
setIsOpen(false);
}
};
const onTextInputChange = (_event: React.FormEvent, value: string) => {
setInputValue(value);
setFilterValue(value);
if (value !== chosenSource?.name) {
dispatch(changeAwsSourceId(undefined));
}
};
const handleSelect = (
_event: React.MouseEvent<Element, MouseEvent>,
value: string
@ -39,6 +93,8 @@ export const AwsSourcesSelect = () => {
const handleClear = () => {
dispatch(changeAwsSourceId(undefined));
setInputValue('');
setFilterValue('');
};
const handleToggle = () => {
@ -50,36 +106,84 @@ export const AwsSourcesSelect = () => {
setIsOpen(!isOpen);
};
const selectOptions = sources?.map((source) => (
<SelectOption key={source.id} value={source.name} />
));
const prepareSelectOptions = () => {
const selectOptionsElement = [];
const loadingSpinner = (
<SelectOption key={'fetching'} isNoResultsOption={true}>
<Spinner size="lg" />
</SelectOption>
selectOptions.map((key, index) =>
selectOptionsElement.push(
<SelectOption key={index} value={key}>
{key}
</SelectOption>
)
);
if (isFetching) {
selectOptionsElement.push(
<SelectOption key="fetching" value="loader">
<Spinner size="lg" />
</SelectOption>
);
}
if (isSuccess && selectOptions.length === 0) {
selectOptionsElement.push(
<SelectOption key="no_results" value="no_results" isDisabled>
No results found
</SelectOption>
);
}
return selectOptionsElement;
};
const toggle = (toggleRef: React.Ref<MenuToggleElement>) => (
<MenuToggle
ref={toggleRef}
variant="typeahead"
onClick={handleToggle}
isExpanded={isOpen}
isFullWidth
isDisabled={!isSuccess || isLoading}
ouiaId="source_select"
>
<TextInputGroup isPlain>
<TextInputGroupMain
value={chosenSource?.name ? chosenSource.name : inputValue}
onClick={onInputClick}
onChange={onTextInputChange}
autoComplete="off"
placeholder="Select source"
isExpanded={isOpen}
/>
{chosenSource?.name && (
<TextInputGroupUtilities>
<Button
variant="plain"
onClick={handleClear}
aria-label="Clear input"
>
<TimesIcon />
</Button>
</TextInputGroupUtilities>
)}
</TextInputGroup>
</MenuToggle>
);
if (isFetching) {
selectOptions?.push(loadingSpinner);
}
return (
<>
<FormGroup isRequired label={'Source name'} data-testid="sources">
<Select
ouiaId="source_select"
variant={SelectVariant.typeahead}
onToggle={handleToggle}
onSelect={handleSelect}
onClear={handleClear}
selections={chosenSource?.name}
isScrollable
isOpen={isOpen}
placeholderText="Select source"
typeAheadAriaLabel="Select source"
isDisabled={!isSuccess || isLoading}
selected={chosenSource?.name}
onSelect={handleSelect}
onOpenChange={handleToggle}
toggle={toggle}
shouldFocusFirstItemOnOpen={false}
>
{selectOptions}
<SelectList>{prepareSelectOptions()}</SelectList>
</Select>
</FormGroup>
<>

View file

@ -363,9 +363,7 @@ describe('Import modal', () => {
});
const getSourceDropdown = async () => {
const sourceDropdown = await screen.findByRole('textbox', {
name: /select source/i,
});
const sourceDropdown = await screen.findByPlaceholderText(/select source/i);
await waitFor(() => expect(sourceDropdown).toBeEnabled());
return sourceDropdown;

View file

@ -3,16 +3,7 @@ import userEvent from '@testing-library/user-event';
import { clickNext, renderCreateMode } from './wizardTestUtils';
const getAwsSourceDropdown = async () => {
const sourceDropdown = await screen.findByRole('textbox', {
name: /select source/i,
});
await waitFor(() => expect(sourceDropdown).toBeEnabled());
return sourceDropdown;
};
const getAzureSourceDropdown = async () => {
const getSourceDropdown = async () => {
const sourceDropdown = await screen.findByPlaceholderText(/select source/i);
await waitFor(() => expect(sourceDropdown).toBeEnabled());
@ -26,7 +17,7 @@ export const addTargetEnvAzure = async () => {
name: /use an account configured from sources\./i,
})
).toHaveFocus();
const azureSourceDropdown = await getAzureSourceDropdown();
const azureSourceDropdown = await getSourceDropdown();
await waitFor(() => user.click(azureSourceDropdown));
const azureSource = await screen.findByRole('option', {
name: /azureSource1/i,
@ -119,7 +110,7 @@ describe('Keyboard accessibility', () => {
name: /use an account configured from sources\./i,
})
).toHaveFocus();
const awsSourceDropdown = await getAwsSourceDropdown();
const awsSourceDropdown = await getSourceDropdown();
await waitFor(() => user.click(awsSourceDropdown));
const awsSource = await screen.findByRole('option', {
name: /my_source/i,

View file

@ -110,9 +110,7 @@ const chooseSourcesOption = async () => {
};
const getSourceDropdown = async () => {
const sourceDropdown = await screen.findByRole('textbox', {
name: /select source/i,
});
const sourceDropdown = await screen.findByPlaceholderText(/select source/i);
await waitFor(() => expect(sourceDropdown).toBeEnabled());
return sourceDropdown;
@ -120,9 +118,7 @@ const getSourceDropdown = async () => {
const selectSource = async () => {
const user = userEvent.setup();
const sourceTexbox = await screen.findByRole('textbox', {
name: /select source/i,
});
const sourceTexbox = await screen.findByPlaceholderText(/select source/i);
await waitFor(async () => user.click(sourceTexbox));
const sourceOption = await screen.findByRole('option', {