Better Header validation

This commit is contained in:
Gregory Schier
2023-03-20 01:38:05 -07:00
parent ea9f8d3ab2
commit 80dd1e457b
3 changed files with 30 additions and 6 deletions

View File

@@ -18,6 +18,7 @@ export function HeaderEditor({ headers, onChange }: Props) {
<PairEditor <PairEditor
pairs={headers} pairs={headers}
onChange={onChange} onChange={onChange}
nameValidate={validateHttpHeader}
nameAutocomplete={nameAutocomplete} nameAutocomplete={nameAutocomplete}
valueAutocomplete={valueAutocomplete} valueAutocomplete={valueAutocomplete}
namePlaceholder="Header-Name" namePlaceholder="Header-Name"
@@ -50,3 +51,11 @@ const nameAutocomplete: PairEditorProps['nameAutocomplete'] = {
minMatch: MIN_MATCH, minMatch: MIN_MATCH,
options: headerNames.map((t, i) => ({ label: t, type: 'constant', boost: 99 - i })), options: headerNames.map((t, i) => ({ label: t, type: 'constant', boost: 99 - i })),
}; };
const validateHttpHeader = (v: string) => {
if (v === '') {
return true;
}
return v.match(/^[a-zA-Z0-9-_]+$/) !== null;
};

View File

@@ -1,11 +1,11 @@
import classnames from 'classnames'; import classnames from 'classnames';
import { useMemo, useState } from 'react';
import type { HTMLAttributes, ReactNode } from 'react'; import type { HTMLAttributes, ReactNode } from 'react';
import { useMemo, useState } from 'react';
import type { EditorProps } from './Editor'; import type { EditorProps } from './Editor';
import { Editor } from './Editor'; import { Editor } from './Editor';
import { HStack, VStack } from './Stacks'; import { HStack, VStack } from './Stacks';
type Props = Omit<HTMLAttributes<HTMLInputElement>, 'onChange' | 'onFocus'> & export type InputProps = Omit<HTMLAttributes<HTMLInputElement>, 'onChange' | 'onFocus'> &
Pick<EditorProps, 'contentType' | 'useTemplating' | 'autocomplete'> & { Pick<EditorProps, 'contentType' | 'useTemplating' | 'autocomplete'> & {
name: string; name: string;
label: string; label: string;
@@ -41,7 +41,7 @@ export function Input({
validate, validate,
require, require,
...props ...props
}: Props) { }: InputProps) {
const [currentValue, setCurrentValue] = useState(defaultValue ?? ''); const [currentValue, setCurrentValue] = useState(defaultValue ?? '');
const id = `input-${name}`; const id = `input-${name}`;
const inputClassName = classnames( const inputClassName = classnames(
@@ -80,7 +80,7 @@ export function Input({
containerClassName, containerClassName,
'relative w-full rounded-md text-gray-900', 'relative w-full rounded-md text-gray-900',
'border border-gray-200 focus-within:border-focus', 'border border-gray-200 focus-within:border-focus',
!isValid && 'border-invalid', !isValid && '!border-invalid',
size === 'md' && 'h-md', size === 'md' && 'h-md',
size === 'sm' && 'h-sm', size === 'sm' && 'h-sm',
)} )}

View File

@@ -10,8 +10,8 @@ import { Checkbox } from './Checkbox';
import type { GenericCompletionConfig } from './Editor/genericCompletion'; import type { GenericCompletionConfig } from './Editor/genericCompletion';
import { Icon } from './Icon'; import { Icon } from './Icon';
import { IconButton } from './IconButton'; import { IconButton } from './IconButton';
import type { InputProps } from './Input';
import { Input } from './Input'; import { Input } from './Input';
import { HStack } from './Stacks';
export type PairEditorProps = { export type PairEditorProps = {
pairs: Pair[]; pairs: Pair[];
@@ -21,6 +21,8 @@ export type PairEditorProps = {
valuePlaceholder?: string; valuePlaceholder?: string;
nameAutocomplete?: GenericCompletionConfig; nameAutocomplete?: GenericCompletionConfig;
valueAutocomplete?: (name: string) => GenericCompletionConfig | undefined; valueAutocomplete?: (name: string) => GenericCompletionConfig | undefined;
nameValidate?: InputProps['validate'];
valueValidate?: InputProps['validate'];
}; };
type Pair = { type Pair = {
@@ -40,6 +42,8 @@ export const PairEditor = memo(function PairEditor({
valueAutocomplete, valueAutocomplete,
namePlaceholder, namePlaceholder,
valuePlaceholder, valuePlaceholder,
nameValidate,
valueValidate,
className, className,
onChange, onChange,
}: PairEditorProps) { }: PairEditorProps) {
@@ -140,6 +144,8 @@ export const PairEditor = memo(function PairEditor({
valueAutocomplete={valueAutocomplete} valueAutocomplete={valueAutocomplete}
namePlaceholder={namePlaceholder} namePlaceholder={namePlaceholder}
valuePlaceholder={valuePlaceholder} valuePlaceholder={valuePlaceholder}
nameValidate={nameValidate}
valueValidate={valueValidate}
onChange={handleChange} onChange={handleChange}
onFocus={handleFocus} onFocus={handleFocus}
onDelete={isLast ? undefined : handleDelete} onDelete={isLast ? undefined : handleDelete}
@@ -168,7 +174,12 @@ type FormRowProps = {
isLast?: boolean; isLast?: boolean;
} & Pick< } & Pick<
PairEditorProps, PairEditorProps,
'nameAutocomplete' | 'valueAutocomplete' | 'namePlaceholder' | 'valuePlaceholder' | 'nameAutocomplete'
| 'valueAutocomplete'
| 'namePlaceholder'
| 'valuePlaceholder'
| 'nameValidate'
| 'valueValidate'
>; >;
const FormRow = memo(function FormRow({ const FormRow = memo(function FormRow({
@@ -183,6 +194,8 @@ const FormRow = memo(function FormRow({
valueAutocomplete, valueAutocomplete,
namePlaceholder, namePlaceholder,
valuePlaceholder, valuePlaceholder,
nameValidate,
valueValidate,
}: FormRowProps) { }: FormRowProps) {
const { id } = pairContainer; const { id } = pairContainer;
const ref = useRef<HTMLDivElement>(null); const ref = useRef<HTMLDivElement>(null);
@@ -272,6 +285,7 @@ const FormRow = memo(function FormRow({
hideLabel hideLabel
size="sm" size="sm"
require={!isLast && !!pairContainer.pair.enabled && !!pairContainer.pair.value} require={!isLast && !!pairContainer.pair.enabled && !!pairContainer.pair.value}
validate={nameValidate}
useTemplating useTemplating
containerClassName={classnames(isLast && 'border-dashed')} containerClassName={classnames(isLast && 'border-dashed')}
defaultValue={pairContainer.pair.name} defaultValue={pairContainer.pair.name}
@@ -286,6 +300,7 @@ const FormRow = memo(function FormRow({
hideLabel hideLabel
size="sm" size="sm"
containerClassName={classnames(isLast && 'border-dashed')} containerClassName={classnames(isLast && 'border-dashed')}
validate={valueValidate}
defaultValue={pairContainer.pair.value} defaultValue={pairContainer.pair.value}
label="Value" label="Value"
name="value" name="value"