1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-08-02 20:35:25 +02:00

refactor(gitops): migrate git form to react [EE-4849] (#8268)

This commit is contained in:
Chaim Lev-Ari 2023-02-23 01:43:33 +05:30 committed by GitHub
parent afe6cd6df0
commit 273a3f9a10
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
130 changed files with 3194 additions and 1190 deletions

View file

@ -2,6 +2,7 @@ import clsx from 'clsx';
import { PropsWithChildren, ReactNode } from 'react';
import { ButtonGroup, Size } from '@@/buttons/ButtonGroup';
import { Button } from '@@/buttons';
import styles from './ButtonSelector.module.css';
@ -59,10 +60,12 @@ function OptionItem({
readOnly,
}: PropsWithChildren<OptionItemProps>) {
return (
<label
className={clsx('btn btn-primary', {
<Button
color="light"
as="label"
disabled={disabled || readOnly}
className={clsx({
active: selected,
disabled: readOnly || disabled,
})}
>
{children}
@ -73,6 +76,6 @@ function OptionItem({
disabled={disabled}
readOnly={readOnly}
/>
</label>
</Button>
);
}

View file

@ -1,4 +1,4 @@
import { PropsWithChildren, ReactNode } from 'react';
import { ComponentProps, PropsWithChildren, ReactNode } from 'react';
import clsx from 'clsx';
import { Tooltip } from '@@/Tip/Tooltip';
@ -11,11 +11,12 @@ export interface Props {
inputId?: string;
label: ReactNode;
size?: Size;
tooltip?: string;
tooltip?: ComponentProps<typeof Tooltip>['message'];
setTooltipHtmlMessage?: ComponentProps<typeof Tooltip>['setHtmlMessage'];
children: ReactNode;
errors?: ReactNode;
required?: boolean;
setTooltipHtmlMessage?: boolean;
className?: string;
}
export function FormControl({
@ -25,12 +26,14 @@ export function FormControl({
tooltip = '',
children,
errors,
required,
className,
required = false,
setTooltipHtmlMessage,
}: PropsWithChildren<Props>) {
return (
<div
className={clsx(
className,
'form-group',
'after:clear-both after:table after:content-[""]' // to fix issues with float
)}
@ -50,12 +53,7 @@ export function FormControl({
<div className={sizeClassChildren(size)}>
{children}
{errors && (
<span className="help-block">
<FormError>{errors}</FormError>
</span>
)}
{errors && <FormError>{errors}</FormError>}
</div>
</div>
);

View file

@ -10,7 +10,9 @@ interface Props {
export function FormError({ children, className }: PropsWithChildren<Props>) {
return (
<p className={clsx(`text-muted small vertical-center`, className)}>
<p
className={clsx(`text-muted small vertical-center help-block`, className)}
>
<Icon icon={AlertTriangle} className="icon-warning" />
<span className="text-warning">{children}</span>
</p>

View file

@ -1,14 +1,24 @@
import clsx from 'clsx';
import { InputHTMLAttributes } from 'react';
import { forwardRef, InputHTMLAttributes, Ref } from 'react';
export const InputWithRef = forwardRef<
HTMLInputElement,
InputHTMLAttributes<HTMLInputElement>
>(
// eslint-disable-next-line react/jsx-props-no-spreading
(props, ref) => <Input {...props} mRef={ref} />
);
export function Input({
className,
mRef: ref,
...props
}: InputHTMLAttributes<HTMLInputElement>) {
}: InputHTMLAttributes<HTMLInputElement> & { mRef?: Ref<HTMLInputElement> }) {
return (
<input
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
ref={ref}
className={clsx('form-control', className)}
/>
);

View file

@ -1 +1 @@
export { InputList } from './InputList';
export { InputList, type ItemProps } from './InputList';

View file

@ -1,4 +1,5 @@
import clsx from 'clsx';
import { ComponentProps } from 'react';
import { FeatureId } from '@/react/portainer/feature-flags/enums';
@ -13,14 +14,14 @@ export interface Props {
onChange(value: boolean): void;
name?: string;
tooltip?: string;
tooltip?: ComponentProps<typeof Tooltip>['message'];
setTooltipHtmlMessage?: ComponentProps<typeof Tooltip>['setHtmlMessage'];
labelClass?: string;
switchClass?: string;
fieldClass?: string;
dataCy?: string;
disabled?: boolean;
featureId?: FeatureId;
setTooltipHtmlMessage?: boolean;
}
export function SwitchField({

View file

@ -0,0 +1,27 @@
import { useRef } from 'react';
import { TestContext, TestFunction } from 'yup';
function cacheTest<T, TContext>(
asyncValidate: TestFunction<T, TContext>
): TestFunction<T, TContext> {
let valid = true;
let value: T | undefined;
return async (newValue: T, context: TestContext<TContext>) => {
if (newValue !== value) {
value = newValue;
const response = await asyncValidate.call(context, newValue, context);
valid = !!response;
}
return valid;
};
}
export function useCachedValidation<T, TContext>(
test: TestFunction<T, TContext>
) {
const ref = useRef(cacheTest(test));
return ref.current;
}

View file

@ -0,0 +1,26 @@
import { useRef, useState, useCallback, useEffect } from 'react';
export function useCaretPosition<
T extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement
>() {
const node = useRef<T>(null);
const [start, setStart] = useState(0);
const [end, setEnd] = useState(0);
const updateCaret = useCallback(() => {
if (node.current) {
const { selectionStart, selectionEnd } = node.current;
setStart(selectionStart || 0);
setEnd(selectionEnd || 0);
}
}, []);
useEffect(() => {
if (node.current) {
node.current.setSelectionRange(start, end);
}
});
return { start, end, ref: node, updateCaret };
}

View file

@ -0,0 +1,19 @@
import { yupToFormErrors } from 'formik';
import { SchemaOf } from 'yup';
export async function validateForm<T>(
schemaBuilder: () => SchemaOf<T>,
formValues: T
) {
const validationSchema = schemaBuilder();
try {
await validationSchema.validate(formValues, {
strict: true,
abortEarly: false,
});
return undefined;
} catch (error) {
return yupToFormErrors<T>(error);
}
}