Wizard: Replace deprecated select in AwsSourcesSelect
This replaces a deprecated select in `AwsSourcesSelect` for a non-deprecated one.
This commit is contained in:
parent
0f230c18d9
commit
b767c3bfde
4 changed files with 136 additions and 47 deletions
|
|
@ -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>
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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', {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue