Wizard: Extract ChippingInput to reusable component
This extracts input with chips from the NTP servers into a separate reusable ChippingInput component.
This commit is contained in:
parent
8e046e3018
commit
ae795c3b6a
2 changed files with 131 additions and 87 deletions
118
src/Components/CreateImageWizard/ChippingInput.tsx
Normal file
118
src/Components/CreateImageWizard/ChippingInput.tsx
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
import React, { useState } from 'react';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Chip,
|
||||
ChipGroup,
|
||||
HelperText,
|
||||
HelperTextItem,
|
||||
TextInputGroup,
|
||||
TextInputGroupMain,
|
||||
TextInputGroupUtilities,
|
||||
} from '@patternfly/react-core/dist/esm';
|
||||
import { PlusCircleIcon, TimesIcon } from '@patternfly/react-icons';
|
||||
import { UnknownAction } from 'redux';
|
||||
|
||||
import { useAppDispatch } from '../../store/hooks';
|
||||
|
||||
type ChippingInputProps = {
|
||||
ariaLabel: string;
|
||||
placeholder: string;
|
||||
validator: (value: string) => boolean;
|
||||
list: string[] | undefined;
|
||||
item: string;
|
||||
addAction: (value: string) => UnknownAction;
|
||||
removeAction: (value: string) => UnknownAction;
|
||||
};
|
||||
|
||||
const ChippingInput = ({
|
||||
ariaLabel,
|
||||
placeholder,
|
||||
validator,
|
||||
list,
|
||||
item,
|
||||
addAction,
|
||||
removeAction,
|
||||
}: ChippingInputProps) => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [errorText, setErrorText] = useState('');
|
||||
|
||||
const onTextInputChange = (
|
||||
_event: React.FormEvent<HTMLInputElement>,
|
||||
value: string
|
||||
) => {
|
||||
setInputValue(value);
|
||||
};
|
||||
|
||||
const handleKeyDown = (e: React.KeyboardEvent, value: string) => {
|
||||
if (e.key === ' ' || e.key === 'Enter' || e.key === ',') {
|
||||
e.preventDefault();
|
||||
|
||||
if (validator(value) && !list?.includes(value)) {
|
||||
dispatch(addAction(value));
|
||||
setInputValue('');
|
||||
setErrorText('');
|
||||
}
|
||||
|
||||
if (list?.includes(value)) {
|
||||
setErrorText(`${item} already exists.`);
|
||||
}
|
||||
|
||||
if (!validator(value)) {
|
||||
setErrorText('Invalid format.');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleAddItem = (e: React.MouseEvent, value: string) => {
|
||||
dispatch(addAction(value));
|
||||
setInputValue('');
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<TextInputGroup>
|
||||
<TextInputGroupMain
|
||||
placeholder={placeholder}
|
||||
onChange={onTextInputChange}
|
||||
value={inputValue}
|
||||
onKeyDown={(e) => handleKeyDown(e, inputValue)}
|
||||
/>
|
||||
<TextInputGroupUtilities>
|
||||
<Button
|
||||
variant="plain"
|
||||
onClick={(e) => handleAddItem(e, inputValue)}
|
||||
isDisabled={!inputValue}
|
||||
aria-label={ariaLabel}
|
||||
>
|
||||
<PlusCircleIcon className="pf-v5-u-primary-color-100" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="plain"
|
||||
onClick={() => setInputValue('')}
|
||||
isDisabled={!inputValue}
|
||||
aria-label="Clear input"
|
||||
>
|
||||
<TimesIcon />
|
||||
</Button>
|
||||
</TextInputGroupUtilities>
|
||||
</TextInputGroup>
|
||||
{errorText && (
|
||||
<HelperText>
|
||||
<HelperTextItem variant={'error'}>{errorText}</HelperTextItem>
|
||||
</HelperText>
|
||||
)}
|
||||
<ChipGroup numChips={5} className="pf-v5-u-mt-sm pf-v5-u-w-100">
|
||||
{list?.map((item) => (
|
||||
<Chip key={item} onClick={() => dispatch(removeAction(item))}>
|
||||
{item}
|
||||
</Chip>
|
||||
))}
|
||||
</ChipGroup>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChippingInput;
|
||||
|
|
@ -1,104 +1,30 @@
|
|||
import React, { useState } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Chip,
|
||||
ChipGroup,
|
||||
FormGroup,
|
||||
HelperText,
|
||||
HelperTextItem,
|
||||
TextInputGroup,
|
||||
TextInputGroupMain,
|
||||
TextInputGroupUtilities,
|
||||
} from '@patternfly/react-core';
|
||||
import { PlusCircleIcon, TimesIcon } from '@patternfly/react-icons';
|
||||
import { FormGroup } from '@patternfly/react-core';
|
||||
|
||||
import { useAppDispatch, useAppSelector } from '../../../../../store/hooks';
|
||||
import { useAppSelector } from '../../../../../store/hooks';
|
||||
import {
|
||||
addNtpServer,
|
||||
removeNtpServer,
|
||||
selectNtpServers,
|
||||
} from '../../../../../store/wizardSlice';
|
||||
import ChippingInput from '../../../ChippingInput';
|
||||
import { isNtpServerValid } from '../../../validators';
|
||||
|
||||
const NtpServersInput = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const ntpServers = useAppSelector(selectNtpServers);
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [errorText, setErrorText] = useState('');
|
||||
|
||||
const onTextInputChange = (
|
||||
_event: React.FormEvent<HTMLInputElement>,
|
||||
value: string
|
||||
) => {
|
||||
setInputValue(value);
|
||||
};
|
||||
|
||||
const handleKeyDown = (e: React.KeyboardEvent, value: string) => {
|
||||
if (e.key === ' ' || e.key === 'Enter' || e.key === ',') {
|
||||
e.preventDefault();
|
||||
|
||||
if (isNtpServerValid(value) && !ntpServers?.includes(value)) {
|
||||
dispatch(addNtpServer(value));
|
||||
setInputValue('');
|
||||
setErrorText('');
|
||||
}
|
||||
|
||||
if (ntpServers?.includes(value)) {
|
||||
setErrorText('NTP server already exists.');
|
||||
}
|
||||
|
||||
if (!isNtpServerValid(value)) {
|
||||
setErrorText('Invalid format.');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleAddServer = (e: React.MouseEvent, value: string) => {
|
||||
dispatch(addNtpServer(value));
|
||||
setInputValue('');
|
||||
};
|
||||
|
||||
return (
|
||||
<FormGroup isRequired={false} label="NTP servers">
|
||||
<TextInputGroup>
|
||||
<TextInputGroupMain
|
||||
<ChippingInput
|
||||
ariaLabel="Add NTP server"
|
||||
placeholder="Add NTP servers"
|
||||
onChange={onTextInputChange}
|
||||
value={inputValue}
|
||||
onKeyDown={(e) => handleKeyDown(e, inputValue)}
|
||||
validator={isNtpServerValid}
|
||||
list={ntpServers}
|
||||
item="NTP server"
|
||||
addAction={addNtpServer}
|
||||
removeAction={removeNtpServer}
|
||||
/>
|
||||
<TextInputGroupUtilities>
|
||||
<Button
|
||||
variant="plain"
|
||||
onClick={(e) => handleAddServer(e, inputValue)}
|
||||
isDisabled={!inputValue}
|
||||
aria-label="Add NTP server"
|
||||
>
|
||||
<PlusCircleIcon />
|
||||
</Button>
|
||||
<Button
|
||||
variant="plain"
|
||||
onClick={() => setInputValue('')}
|
||||
isDisabled={!inputValue}
|
||||
aria-label="Clear input"
|
||||
>
|
||||
<TimesIcon />
|
||||
</Button>
|
||||
</TextInputGroupUtilities>
|
||||
</TextInputGroup>
|
||||
{errorText && (
|
||||
<HelperText>
|
||||
<HelperTextItem variant={'error'}>{errorText}</HelperTextItem>
|
||||
</HelperText>
|
||||
)}
|
||||
<ChipGroup numChips={5} className="pf-v5-u-mt-sm pf-v5-u-w-100">
|
||||
{ntpServers?.map((server) => (
|
||||
<Chip key={server} onClick={() => dispatch(removeNtpServer(server))}>
|
||||
{server}
|
||||
</Chip>
|
||||
))}
|
||||
</ChipGroup>
|
||||
</FormGroup>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue