debian-image-builder-frontend/src/Components/CreateImageWizard/steps/TargetEnvironment/Azure/AzureResourceGroups.tsx
regexowl 801c0c8bee src: Fix function types
This updates types in several places to match their calls.
2025-08-18 16:21:10 +02:00

173 lines
4.8 KiB
TypeScript

import React, { useEffect, useState } from 'react';
import {
Button,
FormGroup,
MenuToggle,
MenuToggleElement,
Select,
SelectList,
SelectOption,
Spinner,
TextInputGroup,
TextInputGroupMain,
TextInputGroupUtilities,
} from '@patternfly/react-core';
import { TimesIcon } from '@patternfly/react-icons';
import { useAppDispatch, useAppSelector } from '../../../../../store/hooks';
import { useGetSourceUploadInfoQuery } from '../../../../../store/provisioningApi';
import {
changeAzureResourceGroup,
selectAzureResourceGroup,
selectAzureSource,
} from '../../../../../store/wizardSlice';
const emptyArray: string[] = [];
export const AzureResourceGroups = () => {
const azureSource = useAppSelector(selectAzureSource);
const azureResourceGroup = useAppSelector(selectAzureResourceGroup);
const dispatch = useAppDispatch();
const [isOpen, setIsOpen] = useState(false);
const [inputValue, setInputValue] = useState<string>('');
const [filterValue, setFilterValue] = useState<string>('');
const [selectOptions, setSelectOptions] = useState<string[]>([]);
const {
data: sourceDetails,
isFetching,
isSuccess,
} = useGetSourceUploadInfoQuery(
{ id: parseInt(azureSource as string) },
{
skip: !azureSource,
},
);
// use a static empty array to avoid an infinite render loop in useEffect functions depending
// on `resourceGroups`
const resourceGroups = sourceDetails?.azure?.resource_groups || emptyArray;
useEffect(() => {
let filteredGroups = resourceGroups;
if (filterValue) {
filteredGroups = resourceGroups.filter((group: string) =>
String(group).toLowerCase().includes(filterValue.toLowerCase()),
);
if (!isOpen) {
setIsOpen(true);
}
}
setSelectOptions(filteredGroups);
// 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, resourceGroups]);
const onInputClick = () => {
if (!isOpen) {
setIsOpen(true);
} else if (!inputValue) {
setIsOpen(false);
}
};
const onTextInputChange = (_event: React.FormEvent, value: string) => {
setInputValue(value);
setFilterValue(value);
if (value !== azureResourceGroup) {
dispatch(changeAzureResourceGroup(''));
}
};
const setResourceGroup = (
_event?: React.MouseEvent<Element, MouseEvent>,
selection?: string | number,
) => {
if (selection === undefined) return;
const resource =
resourceGroups.find((resource) => resource === selection) || '';
setIsOpen(false);
dispatch(changeAzureResourceGroup(resource));
};
const handleClear = () => {
dispatch(changeAzureResourceGroup(''));
setInputValue('');
setFilterValue('');
};
const toggle = (toggleRef: React.Ref<MenuToggleElement>) => (
<MenuToggle
ref={toggleRef}
variant='typeahead'
onClick={() => setIsOpen(!isOpen)}
isExpanded={isOpen}
isDisabled={!azureSource}
>
<TextInputGroup isPlain>
<TextInputGroupMain
value={azureResourceGroup ? azureResourceGroup : inputValue}
onClick={onInputClick}
onChange={onTextInputChange}
autoComplete='off'
placeholder='Select resource group'
isExpanded={isOpen}
/>
{azureResourceGroup && (
<TextInputGroupUtilities>
<Button
icon={<TimesIcon />}
variant='plain'
onClick={handleClear}
aria-label='Clear input'
/>
</TextInputGroupUtilities>
)}
</TextInputGroup>
</MenuToggle>
);
return (
<FormGroup isRequired label={'Resource group'}>
<Select
isScrollable
isOpen={isOpen}
selected={azureResourceGroup}
onSelect={setResourceGroup}
onOpenChange={() => setIsOpen(!isOpen)}
toggle={toggle}
shouldFocusFirstItemOnOpen={false}
popperProps={{ direction: 'up' }}
>
<SelectList>
{isFetching && (
<SelectOption value='loader'>
<Spinner size='lg' />
</SelectOption>
)}
{selectOptions.length > 0 &&
selectOptions.map((name: string, index: number) => (
<SelectOption
key={index}
value={name}
aria-label={`Resource group ${name}`}
>
{name}
</SelectOption>
))}
{isSuccess && selectOptions.length === 0 && (
<SelectOption isDisabled>
{`No results found for "${filterValue}"`}
</SelectOption>
)}
</SelectList>
</Select>
</FormGroup>
);
};