Wizard: Switch kernel name dropdown to typeahead with custom options
This replaces previously used single dropdown with a typeahead that allow creating a custom option.
This commit is contained in:
parent
86add0ee38
commit
addd933451
2 changed files with 149 additions and 33 deletions
|
|
@ -1,13 +1,18 @@
|
|||
import React, { useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import { FormGroup } from '@patternfly/react-core';
|
||||
import {
|
||||
Button,
|
||||
MenuToggle,
|
||||
MenuToggleElement,
|
||||
Select,
|
||||
SelectList,
|
||||
SelectOption,
|
||||
TextInputGroup,
|
||||
TextInputGroupMain,
|
||||
TextInputGroupUtilities,
|
||||
} from '@patternfly/react-core/dist/esm';
|
||||
import TimesIcon from '@patternfly/react-icons/dist/esm/icons/times-icon';
|
||||
|
||||
import { useAppDispatch, useAppSelector } from '../../../../../store/hooks';
|
||||
import {
|
||||
|
|
@ -15,56 +20,120 @@ import {
|
|||
selectKernel,
|
||||
} from '../../../../../store/wizardSlice';
|
||||
|
||||
const kernelOptions = ['kernel', 'kernel-debug'];
|
||||
let kernelOptions = ['kernel', 'kernel-debug'];
|
||||
|
||||
const KernelName = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const kernel = useAppSelector(selectKernel);
|
||||
const kernel = useAppSelector(selectKernel).name;
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [inputValue, setInputValue] = useState<string>('');
|
||||
const [filterValue, setFilterValue] = useState<string>('');
|
||||
const [selectOptions, setSelectOptions] = useState<string[]>(kernelOptions);
|
||||
|
||||
const onToggle = () => {
|
||||
useEffect(() => {
|
||||
let filteredKernelPkgs = kernelOptions;
|
||||
|
||||
if (filterValue) {
|
||||
filteredKernelPkgs = kernelOptions.filter((kernel: string) =>
|
||||
String(kernel).toLowerCase().includes(filterValue.toLowerCase())
|
||||
);
|
||||
if (!filteredKernelPkgs.some((kernel) => kernel === filterValue)) {
|
||||
filteredKernelPkgs = [
|
||||
...filteredKernelPkgs,
|
||||
`Custom kernel package "${filterValue}"`,
|
||||
];
|
||||
}
|
||||
if (!isOpen) {
|
||||
setIsOpen(true);
|
||||
}
|
||||
}
|
||||
setSelectOptions(filteredKernelPkgs);
|
||||
|
||||
// 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 onToggle = (isOpen: boolean) => {
|
||||
setIsOpen(!isOpen);
|
||||
};
|
||||
|
||||
const onSelect = (_event: React.MouseEvent, value: string) => {
|
||||
if (value === 'default') {
|
||||
dispatch(changeKernelName(''));
|
||||
} else {
|
||||
dispatch(changeKernelName(value));
|
||||
const onInputClick = () => {
|
||||
if (!isOpen) {
|
||||
setIsOpen(true);
|
||||
} else if (!inputValue) {
|
||||
setIsOpen(false);
|
||||
}
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
const defaultOption = () => {
|
||||
return (
|
||||
<SelectOption key="default" value="default">
|
||||
Default kernel package
|
||||
</SelectOption>
|
||||
);
|
||||
const onSelect = (_event: React.MouseEvent, value: string) => {
|
||||
if (value) {
|
||||
if (/custom kernel package/i.test(value)) {
|
||||
if (!kernelOptions.some((kernel) => kernel === filterValue)) {
|
||||
kernelOptions = [...kernelOptions, filterValue];
|
||||
}
|
||||
dispatch(changeKernelName(filterValue));
|
||||
setFilterValue('');
|
||||
setIsOpen(false);
|
||||
} else {
|
||||
setInputValue(value);
|
||||
setFilterValue('');
|
||||
dispatch(changeKernelName(value));
|
||||
setIsOpen(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const prepareSelectOptions = () => {
|
||||
const kernelSelectOptions = kernelOptions.map((option) => (
|
||||
<SelectOption key={option} value={option}>
|
||||
{option}
|
||||
</SelectOption>
|
||||
));
|
||||
const onTextInputChange = (_event: React.FormEvent, value: string) => {
|
||||
setInputValue(value);
|
||||
setFilterValue(value);
|
||||
|
||||
if (kernel.name) {
|
||||
return [defaultOption()].concat(kernelSelectOptions);
|
||||
} else return kernelSelectOptions;
|
||||
if (value !== kernel) {
|
||||
dispatch(changeKernelName(''));
|
||||
}
|
||||
};
|
||||
|
||||
const onToggleClick = () => {
|
||||
setIsOpen(!isOpen);
|
||||
};
|
||||
|
||||
const onClearButtonClick = () => {
|
||||
setInputValue('');
|
||||
setFilterValue('');
|
||||
dispatch(changeKernelName(''));
|
||||
};
|
||||
|
||||
const toggle = (toggleRef: React.Ref<MenuToggleElement>) => (
|
||||
<MenuToggle
|
||||
ref={toggleRef}
|
||||
onClick={onToggle}
|
||||
variant="typeahead"
|
||||
onClick={onToggleClick}
|
||||
isExpanded={isOpen}
|
||||
isFullWidth
|
||||
data-testid="kernel-name-dropdown"
|
||||
>
|
||||
{kernel.name}
|
||||
<TextInputGroup isPlain>
|
||||
<TextInputGroupMain
|
||||
value={kernel ? kernel : inputValue}
|
||||
onClick={onInputClick}
|
||||
onChange={onTextInputChange}
|
||||
autoComplete="off"
|
||||
placeholder="Select kernel package"
|
||||
isExpanded={isOpen}
|
||||
/>
|
||||
|
||||
{kernel && (
|
||||
<TextInputGroupUtilities>
|
||||
<Button
|
||||
variant="plain"
|
||||
onClick={onClearButtonClick}
|
||||
aria-label="Clear input"
|
||||
>
|
||||
<TimesIcon />
|
||||
</Button>
|
||||
</TextInputGroupUtilities>
|
||||
)}
|
||||
</TextInputGroup>
|
||||
</MenuToggle>
|
||||
);
|
||||
|
||||
|
|
@ -73,13 +142,19 @@ const KernelName = () => {
|
|||
<Select
|
||||
isScrollable
|
||||
isOpen={isOpen}
|
||||
selected={kernel.name}
|
||||
selected={kernel}
|
||||
onSelect={onSelect}
|
||||
onOpenChange={onToggle}
|
||||
toggle={toggle}
|
||||
shouldFocusFirstItemOnOpen={false}
|
||||
>
|
||||
<SelectList>{prepareSelectOptions()}</SelectList>
|
||||
<SelectList>
|
||||
{selectOptions.map((option) => (
|
||||
<SelectOption key={option} value={option}>
|
||||
{option}
|
||||
</SelectOption>
|
||||
))}
|
||||
</SelectList>
|
||||
</Select>
|
||||
</FormGroup>
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue