This commit is contained in:
Nail Badiullin 2023-05-05 15:01:45 +05:30 committed by GitHub
commit 8576e56212
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 202 additions and 156 deletions

View file

@ -13,7 +13,7 @@ import yup from 'lib/yupExtended';
import Editor from 'components/common/Editor/Editor';
import Select from 'components/common/Select/Select';
import { FormError } from 'components/common/Input/Input.styled';
import Input from 'components/common/Input/Input';
import ControlledInput from 'components/common/Input/ControlledInput';
import { Button } from 'components/common/Button/Button';
import PageHeading from 'components/common/PageHeading/PageHeading';
import Heading from 'components/common/heading/Heading.styled';
@ -133,7 +133,7 @@ const New: React.FC = () => {
<div>
<Heading level={3}>Name</Heading>
<Input
<ControlledInput
inputSize="M"
placeholder="Connector Name"
name="name"

View file

@ -17,7 +17,7 @@ import 'react-datepicker/dist/react-datepicker.css';
import { ErrorMessage } from '@hookform/error-message';
import { InputLabel } from 'components/common/Input/InputLabel.styled';
import { Button } from 'components/common/Button/Button';
import Input from 'components/common/Input/Input';
import ControlledInput from 'components/common/Input/ControlledInput';
import { FormError } from 'components/common/Input/Input.styled';
import useAppParams from 'lib/hooks/useAppParams';
import { useResetConsumerGroupOffsetsMutation } from 'lib/hooks/api/consumers';
@ -160,7 +160,7 @@ const Form: React.FC<FormProps> = ({ defaultValues, partitions, topics }) => {
partitionsValue.length > 0 && (
<S.OffsetsWrapper>
{fields.map((field, index) => (
<Input
<ControlledInput
key={field.id}
label={`Partition #${field.partition} Offset`}
type="number"

View file

@ -14,7 +14,7 @@ import { yupResolver } from '@hookform/resolvers/yup';
import yup from 'lib/yupExtended';
import PlusIcon from 'components/common/Icons/PlusIcon';
import ReactAce from 'react-ace';
import Input from 'components/common/Input/Input';
import ControlledInput from 'components/common/Input/ControlledInput';
import * as S from './QueryForm.styled';
@ -156,14 +156,16 @@ const QueryForm: React.FC<QueryFormProps> = ({
Stream properties:
{fields.map((field, index) => (
<S.InputsContainer key={field.id}>
<Input
<ControlledInput
control={control}
name={`streamsProperties.${index}.key`}
placeholder="Key"
type="text"
autoComplete="off"
withError
/>
<Input
<ControlledInput
control={control}
name={`streamsProperties.${index}.value`}
placeholder="Value"
type="text"

View file

@ -1,6 +1,6 @@
import { ErrorMessage } from '@hookform/error-message';
import { Button } from 'components/common/Button/Button';
import Input from 'components/common/Input/Input';
import ControlledInput from 'components/common/Input/ControlledInput';
import { FormError } from 'components/common/Input/Input.styled';
import { InputLabel } from 'components/common/Input/InputLabel.styled';
import React from 'react';
@ -99,7 +99,7 @@ const DangerZone: React.FC<DangerZoneProps> = ({
<InputLabel htmlFor="partitions">
Number of partitions *
</InputLabel>
<Input
<ControlledInput
inputSize="M"
type="number"
id="partitions"
@ -139,7 +139,7 @@ const DangerZone: React.FC<DangerZoneProps> = ({
<InputLabel htmlFor="replicationFactor">
Replication Factor *
</InputLabel>
<Input
<ControlledInput
id="replicationFactor"
inputSize="M"
type="number"

View file

@ -1,7 +1,7 @@
import React from 'react';
import * as S from 'components/Topics/Topic/Messages/Filters/Filters.styled';
import { InputLabel } from 'components/common/Input/InputLabel.styled';
import Input from 'components/common/Input/Input';
import ControlledInput from 'components/common/Input/ControlledInput';
import { FormProvider, Controller, useForm } from 'react-hook-form';
import { ErrorMessage } from '@hookform/error-message';
import { Button } from 'components/common/Button/Button';
@ -96,7 +96,7 @@ const AddEditFilterContainer: React.FC<AddEditFilterContainerProps> = ({
)}
<div>
<InputLabel>Display name</InputLabel>
<Input
<ControlledInput
inputSize="M"
placeholder="Enter Name"
autoComplete="off"

View file

@ -6,7 +6,7 @@ import { TopicConfigParams, TopicFormData } from 'redux/interfaces';
import { InputLabel } from 'components/common/Input/InputLabel.styled';
import { FormError } from 'components/common/Input/Input.styled';
import Select from 'components/common/Select/Select';
import Input from 'components/common/Input/Input';
import ControlledInput from 'components/common/Input/ControlledInput';
import IconButtonWrapper from 'components/common/Icons/IconButtonWrapper';
import CloseIcon from 'components/common/Icons/CloseIcon';
import * as C from 'components/Topics/shared/Form/TopicForm.styled';
@ -99,7 +99,7 @@ const CustomParamField: React.FC<Props> = ({
</div>
<div>
<InputLabel>Value *</InputLabel>
<Input
<ControlledInput
name={`customParams.${index}.value` as const}
hookFormOptions={{
required: 'Value is required.',

View file

@ -1,30 +1,30 @@
import * as React from 'react';
import { useFormContext } from 'react-hook-form';
import { InputLabel } from 'components/common/Input/InputLabel.styled';
import { FormError, InputHint } from 'components/common/Input/Input.styled';
import { ErrorMessage } from '@hookform/error-message';
interface CheckboxProps {
export interface CheckboxProps {
name: string;
label: React.ReactNode;
hint?: string;
onChange?: (event: React.SyntheticEvent<HTMLInputElement>) => void;
}
const Checkbox: React.FC<CheckboxProps> = ({ name, label, hint }) => {
const { register } = useFormContext();
return (
<div>
<InputLabel>
<input {...register(name)} type="checkbox" />
{label}
</InputLabel>
<InputHint>{hint}</InputHint>
<FormError>
<ErrorMessage name={name} />
</FormError>
</div>
);
};
const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
({ name, label, hint, onChange }, ref) => {
return (
<div>
<InputLabel>
<input type="checkbox" ref={ref} onChange={onChange} />
{label}
</InputLabel>
<InputHint>{hint}</InputHint>
<FormError>
<ErrorMessage name={name} />
</FormError>
</div>
);
}
);
export default Checkbox;

View file

@ -0,0 +1,15 @@
import React from 'react';
import { Controller } from 'react-hook-form';
import Checkbox, { CheckboxProps } from './Checkbox';
const ControlledCheckbox = ({ name, label, hint }: CheckboxProps) => {
return (
<Controller
name={name}
render={({ field }) => <Checkbox label={label} hint={hint} {...field} />}
/>
);
};
export default ControlledCheckbox;

View file

@ -0,0 +1,21 @@
import React from 'react';
import { Controller } from 'react-hook-form';
import Input, { InputProps } from './Input';
const ControlledInput = ({
name,
control,
...restProps
}: // eslint-disable-next-line @typescript-eslint/no-explicit-any
InputProps & { control: any }) => {
return (
<Controller
control={control}
name={name ?? ''}
render={({ field }) => <Input {...restProps} {...field} />}
/>
);
};
export default ControlledInput;

View file

@ -99,99 +99,107 @@ function pasteNumberCheck(
return value;
}
const Input: React.FC<InputProps> = ({
name,
hookFormOptions,
search,
inputSize = 'L',
type,
positiveOnly,
integerOnly,
withError = false,
label,
hint,
...rest
}) => {
const methods = useFormContext();
const fieldId = React.useId();
const isHookFormField = !!name && !!methods.register;
const keyPressEventHandler = (
event: React.KeyboardEvent<HTMLInputElement>
const Input = React.forwardRef<HTMLInputElement, InputProps>(
(
{
name,
hookFormOptions,
search,
inputSize = 'L',
type,
positiveOnly,
integerOnly,
withError = false,
label,
hint,
...rest
},
ref
) => {
const { key } = event;
if (type === 'number') {
// Manually prevent input of non-digit and non-minus for all number inputs
// and prevent input of negative numbers for positiveOnly inputs
if (
!inputNumberCheck(
key,
typeof positiveOnly === 'boolean' ? positiveOnly : false,
typeof integerOnly === 'boolean' ? integerOnly : false,
methods.getValues,
typeof name === 'string' ? name : ''
)
) {
event.preventDefault();
}
}
};
const pasteEventHandler = (event: React.ClipboardEvent<HTMLInputElement>) => {
if (type === 'number') {
const { clipboardData } = event;
// The 'clipboardData' does not have key 'Text', but has key 'text' instead.
const text = clipboardData.getData('text');
// Check the format of pasted text.
const value = pasteNumberCheck(
text,
typeof positiveOnly === 'boolean' ? positiveOnly : false,
typeof integerOnly === 'boolean' ? integerOnly : false
);
// if paste value contains non-numeric characters or
// negative for positiveOnly fields then prevent paste
if (value !== text) {
event.preventDefault();
const methods = useFormContext();
// for react-hook-form fields only set transformed value
if (isHookFormField) {
methods.setValue(name, value);
const fieldId = React.useId();
const isHookFormField = !!name && !!methods.register;
const keyPressEventHandler = (
event: React.KeyboardEvent<HTMLInputElement>
) => {
const { key } = event;
if (type === 'number') {
// Manually prevent input of non-digit and non-minus for all number inputs
// and prevent input of negative numbers for positiveOnly inputs
if (
!inputNumberCheck(
key,
typeof positiveOnly === 'boolean' ? positiveOnly : false,
typeof integerOnly === 'boolean' ? integerOnly : false,
methods.getValues,
typeof name === 'string' ? name : ''
)
) {
event.preventDefault();
}
}
};
const pasteEventHandler = (
event: React.ClipboardEvent<HTMLInputElement>
) => {
if (type === 'number') {
const { clipboardData } = event;
// The 'clipboardData' does not have key 'Text', but has key 'text' instead.
const text = clipboardData.getData('text');
// Check the format of pasted text.
const value = pasteNumberCheck(
text,
typeof positiveOnly === 'boolean' ? positiveOnly : false,
typeof integerOnly === 'boolean' ? integerOnly : false
);
// if paste value contains non-numeric characters or
// negative for positiveOnly fields then prevent paste
if (value !== text) {
event.preventDefault();
// for react-hook-form fields only set transformed value
if (isHookFormField) {
methods.setValue(name, value);
}
}
}
};
let inputOptions = { ...rest };
if (isHookFormField) {
// extend input options with react-hook-form options
// if the field is a part of react-hook-form form
inputOptions = { ...rest, ...methods.register(name, hookFormOptions) };
}
};
let inputOptions = { ...rest };
if (isHookFormField) {
// extend input options with react-hook-form options
// if the field is a part of react-hook-form form
inputOptions = { ...rest, ...methods.register(name, hookFormOptions) };
return (
<div>
{label && <InputLabel htmlFor={rest.id || fieldId}>{label}</InputLabel>}
<S.Wrapper>
{search && <SearchIcon />}
<S.Input
id={fieldId}
inputSize={inputSize}
search={!!search}
type={type}
onKeyPress={keyPressEventHandler}
onPaste={pasteEventHandler}
ref={ref}
{...inputOptions}
/>
{withError && isHookFormField && (
<S.FormError>
<ErrorMessage name={name} />
</S.FormError>
)}
{hint && <S.InputHint>{hint}</S.InputHint>}
</S.Wrapper>
</div>
);
}
return (
<div>
{label && <InputLabel htmlFor={rest.id || fieldId}>{label}</InputLabel>}
<S.Wrapper>
{search && <SearchIcon />}
<S.Input
id={fieldId}
inputSize={inputSize}
search={!!search}
type={type}
onKeyPress={keyPressEventHandler}
onPaste={pasteEventHandler}
{...inputOptions}
/>
{withError && isHookFormField && (
<S.FormError>
<ErrorMessage name={name} />
</S.FormError>
)}
{hint && <S.InputHint>{hint}</S.InputHint>}
</S.Wrapper>
</div>
);
};
);
export default Input;

View file

@ -1,6 +1,6 @@
import React from 'react';
import Input from 'components/common/Input/Input';
import Checkbox from 'components/common/Checkbox/Checkbox';
import ControlledInput from 'components/common/Input/ControlledInput';
import ControlledCheckbox from 'components/common/Checkbox/ControlledCheckbox';
import Fileupload from 'widgets/ClusterConfigForm/common/Fileupload';
import SSLForm from 'widgets/ClusterConfigForm/common/SSLForm';
import Credentials from 'widgets/ClusterConfigForm/common/Credentials';
@ -10,13 +10,13 @@ const AuthenticationMethods: React.FC<{ method: string }> = ({ method }) => {
case 'SASL/JAAS':
return (
<>
<Input
<ControlledInput
type="text"
name="auth.props.saslJaasConfig"
label="sasl.jaas.config"
withError
/>
<Input
<ControlledInput
type="text"
name="auth.props.saslMechanism"
label="sasl.mechanism"
@ -27,15 +27,15 @@ const AuthenticationMethods: React.FC<{ method: string }> = ({ method }) => {
case 'SASL/GSSAPI':
return (
<>
<Input
<ControlledInput
label="Kerberos service name"
type="text"
name="auth.props.saslKerberosServiceName"
withError
/>
<Checkbox name="auth.props.storeKey" label="Store Key" />
<ControlledCheckbox name="auth.props.storeKey" label="Store Key" />
<Fileupload name="auth.props.keyTabFile" label="Key Tab (optional)" />
<Input
<ControlledInput
type="text"
name="auth.props.principal"
label="Principal *"
@ -45,7 +45,7 @@ const AuthenticationMethods: React.FC<{ method: string }> = ({ method }) => {
);
case 'SASL/OAUTHBEARER':
return (
<Input
<ControlledInput
label="Unsecured Login String Claim_sub *"
type="text"
name="auth.props.unsecuredLoginStringClaim_sub"
@ -60,13 +60,13 @@ const AuthenticationMethods: React.FC<{ method: string }> = ({ method }) => {
case 'Delegation tokens':
return (
<>
<Input
<ControlledInput
label="Token Id"
type="text"
name="auth.props.tokenId"
withError
/>
<Input
<ControlledInput
label="Token Value *"
type="text"
name="auth.props.tokenValue"
@ -76,7 +76,7 @@ const AuthenticationMethods: React.FC<{ method: string }> = ({ method }) => {
);
case 'SASL/AWS IAM':
return (
<Input
<ControlledInput
label="AWS Profile Name"
type="text"
name="auth.props.awsProfileName"

View file

@ -1,6 +1,6 @@
import React from 'react';
import { useFormContext } from 'react-hook-form';
import Input from 'components/common/Input/Input';
import ControlledInput from 'components/common/Input/ControlledInput';
import { convertFormKeyToPropsKey } from 'widgets/ClusterConfigForm/utils/convertFormKeyToPropsKey';
import SectionHeader from 'widgets/ClusterConfigForm/common/SectionHeader';
@ -26,7 +26,7 @@ const CustomAuthentication: React.FC = () => {
{hasCustomConfig && (
<>
{Object.keys(customConf).map((key) => (
<Input
<ControlledInput
key={key}
type="text"
name={`customAuth.${key}`}

View file

@ -1,5 +1,5 @@
import React from 'react';
import Input from 'components/common/Input/Input';
import ControlledInput from "../../../components/common/Input/ControlledInput";
import { useFormContext } from 'react-hook-form';
import SectionHeader from 'widgets/ClusterConfigForm/common/SectionHeader';
import SSLForm from 'widgets/ClusterConfigForm/common/SSLForm';
@ -25,7 +25,7 @@ const KSQL = () => {
/>
{ksql && (
<>
<Input
<ControlledInput
label="URL *"
name="ksql.url"
type="text"

View file

@ -1,5 +1,4 @@
import React from 'react';
import Input from 'components/common/Input/Input';
import { useFieldArray, useFormContext } from 'react-hook-form';
import { FormError, InputHint } from 'components/common/Input/Input.styled';
import { ErrorMessage } from '@hookform/error-message';
@ -9,9 +8,10 @@ import PlusIcon from 'components/common/Icons/PlusIcon';
import * as S from 'widgets/ClusterConfigForm/ClusterConfigForm.styled';
import Heading from 'components/common/heading/Heading.styled';
import { InputLabel } from 'components/common/Input/InputLabel.styled';
import Checkbox from 'components/common/Checkbox/Checkbox';
import ControlledCheckbox from 'components/common/Checkbox/ControlledCheckbox';
import SectionHeader from 'widgets/ClusterConfigForm/common/SectionHeader';
import SSLForm from 'widgets/ClusterConfigForm/common/SSLForm';
import ControlledInput from 'components/common/Input/ControlledInput';
const KafkaCluster: React.FC = () => {
const { control, watch, setValue } = useFormContext();
@ -38,14 +38,14 @@ const KafkaCluster: React.FC = () => {
return (
<>
<Heading level={3}>Kafka Cluster</Heading>
<Input
<ControlledInput
label="Cluster name *"
type="text"
name="name"
withError
hint="this name will help you recognize the cluster in the application interface"
/>
<Checkbox
<ControlledCheckbox
name="readOnly"
label="Read-only mode"
hint="allows you to run an application in read-only mode for a specific cluster"
@ -59,7 +59,7 @@ const KafkaCluster: React.FC = () => {
{fields.map((field, index) => (
<S.BootstrapServer key={field.id}>
<div>
<Input
<ControlledInput
name={`bootstrapServers.${index}.host`}
placeholder="Host"
type="text"
@ -68,7 +68,7 @@ const KafkaCluster: React.FC = () => {
/>
</div>
<div>
<Input
<ControlledInput
name={`bootstrapServers.${index}.port`}
placeholder="Port"
type="number"

View file

@ -1,7 +1,7 @@
import * as React from 'react';
import * as S from 'widgets/ClusterConfigForm/ClusterConfigForm.styled';
import { Button } from 'components/common/Button/Button';
import Input from 'components/common/Input/Input';
import ControlledInput from 'components/common/Input/ControlledInput';
import { useFieldArray, useFormContext } from 'react-hook-form';
import PlusIcon from 'components/common/Icons/PlusIcon';
import IconButtonWrapper from 'components/common/Icons/IconButtonWrapper';
@ -39,7 +39,7 @@ const KafkaConnect = () => {
<div key={item.id}>
<FlexRow>
<FlexGrow1>
<Input
<ControlledInput
label="Kafka Connect name *"
name={`kafkaConnect.${index}.name`}
placeholder="Name"
@ -47,7 +47,7 @@ const KafkaConnect = () => {
hint="Given name for the Kafka Connect cluster"
withError
/>
<Input
<ControlledInput
label="Kafka Connect URL *"
name={`kafkaConnect.${index}.address`}
placeholder="URl"

View file

@ -1,5 +1,5 @@
import React from 'react';
import Input from 'components/common/Input/Input';
import ControlledInput from 'components/common/Input/ControlledInput';
import { useFormContext } from 'react-hook-form';
import ControlledSelect from 'components/common/Select/ControlledSelect';
import { METRICS_OPTIONS } from 'lib/constants';
@ -41,7 +41,7 @@ const Metrics = () => {
options={METRICS_OPTIONS}
/>
<S.Port>
<Input
<ControlledInput
label="Port *"
name="metrics.port"
type="number"

View file

@ -1,5 +1,5 @@
import React from 'react';
import Input from 'components/common/Input/Input';
import ControlledInput from 'components/common/Input/ControlledInput';
import { useFormContext } from 'react-hook-form';
import SectionHeader from 'widgets/ClusterConfigForm/common/SectionHeader';
import SSLForm from 'widgets/ClusterConfigForm/common/SSLForm';
@ -25,7 +25,7 @@ const SchemaRegistry = () => {
/>
{schemaRegistry && (
<>
<Input
<ControlledInput
label="URL *"
name="schemaRegistry.url"
type="text"

View file

@ -1,7 +1,7 @@
import * as React from 'react';
import Input from 'components/common/Input/Input';
import * as S from 'widgets/ClusterConfigForm/ClusterConfigForm.styled';
import Checkbox from 'components/common/Checkbox/Checkbox';
import ControlledCheckbox from 'components/common/Checkbox/ControlledCheckbox';
import ControlledInput from 'components/common/Input/ControlledInput';
import { useFormContext } from 'react-hook-form';
type CredentialsProps = {
@ -17,11 +17,11 @@ const Credentials: React.FC<CredentialsProps> = ({
return (
<S.GroupFieldWrapper>
<Checkbox name={`${prefix}.isAuth`} label={title} />
<ControlledCheckbox name={`${prefix}.isAuth`} label={title} />
{watch(`${prefix}.isAuth`) && (
<S.FlexRow>
<S.FlexGrow1>
<Input
<ControlledInput
label="Username *"
type="text"
name={`${prefix}.username`}
@ -29,7 +29,7 @@ const Credentials: React.FC<CredentialsProps> = ({
/>
</S.FlexGrow1>
<S.FlexGrow1>
<Input
<ControlledInput
label="Password *"
type="password"
name={`${prefix}.password`}

View file

@ -3,7 +3,7 @@ import { FormError } from 'components/common/Input/Input.styled';
import { InputLabel } from 'components/common/Input/InputLabel.styled';
import { ErrorMessage } from '@hookform/error-message';
import { useFormContext } from 'react-hook-form';
import Input from 'components/common/Input/Input';
import ControlledInput from 'components/common/Input/ControlledInput';
import { Button } from 'components/common/Button/Button';
import * as S from 'widgets/ClusterConfigForm/ClusterConfigForm.styled';
import { useAppConfigFilesUpload } from 'lib/hooks/api/appConfig';
@ -42,7 +42,7 @@ const Fileupload: React.FC<{ name: string; label: string }> = ({
{loc ? (
<S.FlexRow>
<S.FlexGrow1>
<Input name={name} disabled />
<ControlledInput name={name} disabled />
</S.FlexGrow1>
<Button buttonType="secondary" buttonSize="L" onClick={onReset}>
Reset

View file

@ -1,5 +1,5 @@
import * as React from 'react';
import Input from 'components/common/Input/Input';
import ControlledInput from 'components/common/Input/ControlledInput';
import Fileupload from 'widgets/ClusterConfigForm/common/Fileupload';
import * as S from 'widgets/ClusterConfigForm/ClusterConfigForm.styled';
@ -12,7 +12,7 @@ const SSLForm: React.FC<SSLFormProps> = ({ prefix, title }) => {
return (
<S.GroupFieldWrapper>
<Fileupload name={`${prefix}.location`} label={`${title} Location`} />
<Input
<ControlledInput
label={`${title} Password`}
name={`${prefix}.password`}
type="password"