Skip to content

Commit

Permalink
Clean up validators
Browse files Browse the repository at this point in the history
  • Loading branch information
Etheryte committed Oct 2, 2024
1 parent 71e3797 commit a892e37
Show file tree
Hide file tree
Showing 12 changed files with 70 additions and 55 deletions.
17 changes: 12 additions & 5 deletions web/html/src/components/input/InputBase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export type InputBaseProps<ValueType = string> = {
disabled?: boolean;

/**
* Validate the input, either sync or async, resolve with `undefined` for valid or an error message for invalid
* Validate the input, either sync or async, return `undefined` when valid, a string or string array for an error message when invalid
*/
validate?: Validator | Validator[];

Expand All @@ -74,11 +74,12 @@ export type InputBaseProps<ValueType = string> = {
*/
debounceValidate?: number;

// TODO: Refactor this out
/**
* @deprecated
*
* Hint to display on a validation error
* Prefer returning an error message from your validator instead
*
* Fallback validation error
*
*/
invalidHint?: React.ReactNode;

Expand Down Expand Up @@ -333,7 +334,13 @@ export class InputBase<ValueType = string> extends React.Component<InputBaseProp
this.pushHint(hints, this.props.hint);
this.state.formErrors?.forEach((error) => this.pushHint(hints, error));
if (this.state.isTouched) {
this.state.validationErrors.forEach((error) => this.pushHint(hints, error));
if (this.state.validationErrors.size) {
if (this.props.invalidHint) {
this.pushHint(hints, this.props.invalidHint);
} else {
this.state.validationErrors.forEach((error) => this.pushHint(hints, error));
}
}

if (this.props.required && !this.props.disabled) {
this.pushHint(hints, this.requiredHint());
Expand Down
34 changes: 32 additions & 2 deletions web/html/src/components/input/validation/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ const min =

const parsed = parseFloat(value);
if (isNaN(parsed) || parsed < minValue) {
return message ?? t(`Must be larger than ${minValue}`);
return message ?? t(`Must be no smaller than ${minValue}`);
}
};

Expand All @@ -87,7 +87,35 @@ const max =

const parsed = parseFloat(value);
if (isNaN(parsed) || parsed > maxValue) {
return message ?? t(`Must be smaller than ${maxValue}`);
return message ?? t(`Must be no larger than ${maxValue}`);
}
};

/** Value is greater than `gtValue` */
const gt =
(gtValue: number, message?: string): Validator =>
(value: string) => {
if (value === "") {
return;
}

const parsed = parseFloat(value);
if (isNaN(parsed) || parsed <= gtValue) {
return message ?? t(`Must be greater than ${gtValue}`);
}
};

/** Value is smaller than `ltValue` */
const lt =
(ltValue: number, message?: string): Validator =>
(value: string) => {
if (value === "") {
return;
}

const parsed = parseFloat(value);
if (isNaN(parsed) || parsed >= ltValue) {
return message ?? t(`Must be greater than ${ltValue}`);
}
};

Expand All @@ -111,6 +139,8 @@ export const Validation = {
maxLength,
min,
max,
gt,
lt,
isInt,
intRange,
floatRange,
Expand Down
20 changes: 0 additions & 20 deletions web/html/src/components/validation.ts

This file was deleted.

6 changes: 3 additions & 3 deletions web/html/src/manager/proxy/container-config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import { useState } from "react";

import { AsyncButton } from "components/buttons";
import { SubmitButton } from "components/buttons";
import { Validation } from "components/input";
import { Form } from "components/input/form/Form";
import { FormMultiInput } from "components/input/form-multi-input/FormMultiInput";
import { unflattenModel } from "components/input/form-utils";
import { Radio } from "components/input/radio/Radio";
import { Text } from "components/input/text/Text";
import { Panel } from "components/panels/Panel";
import { TopPanel } from "components/panels/TopPanel";
import Validation from "components/validation";

import Network from "utils/network";

Expand Down Expand Up @@ -220,7 +220,7 @@ export function ProxyConfig() {
name="proxyPort"
label={t("Proxy SSH port")}
hint={t("Port range: 1 - 65535")}
validate={[Validation.isInt({ gt: 0, lt: 65536 })]}
validate={[Validation.intRange(0, 65536)]}
defaultValue="8022"
labelClass="col-md-3"
divClass="col-md-6"
Expand All @@ -231,7 +231,7 @@ export function ProxyConfig() {
label={t("Max Squid cache size")}
hint={t("The maximum value of the Squid cache in Megabytes")}
required
validate={[Validation.isInt({ gt: 0 })]}
validate={[Validation.isInt(), Validation.gt(0)]}
placeholder={t("e.g., 2048")}
labelClass="col-md-3"
divClass="col-md-6"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import * as React from "react";

import { ActionChain } from "components/action-schedule";
import { Select } from "components/input";
import { Validation } from "components/input";
import { Check } from "components/input/check/Check";
import { Text } from "components/input/text/Text";
import { MessageType } from "components/messages/messages";
import { Messages } from "components/messages/messages";
import { Utils as MessagesUtils } from "components/messages/messages";
import { Panel } from "components/panels/Panel";
import { Loading } from "components/utils/loading/Loading";
import Validation from "components/validation";

import { VirtualizationPoolCapsApi } from "../pools/virtualization-pools-capabilities-api";
import { VirtualizationListRefreshApi } from "../virtualization-list-refresh-api";
Expand Down Expand Up @@ -166,7 +166,7 @@ export function GuestProperties(props: Props) {
invalidHint={t("A positive integer is required")}
labelClass="col-md-3"
divClass="col-md-6"
validate={[Validation.isInt({ gt: 0 })]}
validate={[Validation.isInt(), Validation.gt(0)]}
/>
<Text
name="vcpu"
Expand All @@ -175,7 +175,7 @@ export function GuestProperties(props: Props) {
invalidHint={t("A positive integer is required")}
labelClass="col-md-3"
divClass="col-md-6"
validate={[Validation.isInt({ gt: 0 })]}
validate={[Validation.isInt(), Validation.gt(0)]}
/>
{initialModel.arch === undefined && (
<Select
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import * as React from "react";

import { ActionChain } from "components/action-schedule";
import { Validation } from "components/input";
import { Text } from "components/input/text/Text";
import { MessageType } from "components/messages/messages";
import { Utils as MessagesUtils } from "components/messages/messages";
import { Panel } from "components/panels/Panel";
import Validation from "components/validation";

import { GuestPropertiesForm } from "./guest-properties-form";

Expand Down Expand Up @@ -58,7 +58,7 @@ class GuestPropertiesTraditional extends React.Component<Props, State> {
invalidHint={t("A positive integer is required")}
labelClass="col-md-3"
divClass="col-md-6"
validate={[Validation.isInt({ gt: 0 })]}
validate={[Validation.isInt(), Validation.gt(0)]}
/>
<Text
name="vcpu"
Expand All @@ -67,7 +67,7 @@ class GuestPropertiesTraditional extends React.Component<Props, State> {
invalidHint={t("A positive integer is required")}
labelClass="col-md-3"
divClass="col-md-6"
validate={[Validation.isInt({ gt: 0 })]}
validate={[Validation.isInt(), Validation.gt(0)]}
/>
</Panel>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ActionChain } from "components/action-schedule";
import { ActionSchedule } from "components/action-schedule";
import { Button, SubmitButton } from "components/buttons";
import { Select } from "components/input";
import { Validation } from "components/input";
import { Check } from "components/input/check/Check";
import { Form } from "components/input/form/Form";
import { convertNumbers, flattenModel, stripBlankValues, unflattenModel } from "components/input/form-utils";
Expand All @@ -16,7 +17,6 @@ import { Messages } from "components/messages/messages";
import { MessageType } from "components/messages/messages";
import { Panel } from "components/panels/Panel";
import { Loading } from "components/utils/loading/Loading";
import Validation from "components/validation";

import { localizedMoment } from "utils";

Expand Down Expand Up @@ -273,7 +273,7 @@ export function NetworkProperties(props: Props) {
label={t("Maximum Transmission Unit (MTU)")}
labelClass="col-md-3"
divClass="col-md-6"
validate={[Validation.isInt({ min: 0 })]}
validate={[Validation.isInt(), Validation.min(0)]}
invalidHint={t("The value has to be a positive integer")}
/>
)}
Expand Down Expand Up @@ -379,7 +379,7 @@ export function NetworkProperties(props: Props) {
label={t("VLAN tag")}
labelClass="col-md-3"
divClass="col-md-6"
validate={[Validation.isInt({ min: 0, max: 4095 })]}
validate={[Validation.intRange(0, 4095)]}
invalidHint={t("Integer between 0 and 4095")}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import * as React from "react";

import { Select } from "components/input";
import { Validation } from "components/input";
import { FormContext } from "components/input/form/Form";
import { FormMultiInput } from "components/input/form-multi-input/FormMultiInput";
import { Text } from "components/input/text/Text";
import { Panel } from "components/panels/Panel";
import Validation from "components/validation";

import * as utils from "./utils";

Expand Down Expand Up @@ -175,7 +175,7 @@ export function DnsConfig(props: Props) {
label={t("Port")}
title={t(`DNS SRV record ${index} port`)}
name={`dns_srvs${index}_port`}
validate={[Validation.isInt({ min: 0 })]}
validate={[Validation.isInt(), Validation.min(0)]}
invalidHint={t("The value has to be a positive integer")}
labelClass="col-md-3"
divClass="col-md-6"
Expand All @@ -184,7 +184,7 @@ export function DnsConfig(props: Props) {
label={t("Target priority")}
title={t(`DNS SRV record ${index} priority`)}
name={`dns_srvs${index}_priority`}
validate={[Validation.isInt({ min: 0 })]}
validate={[Validation.isInt(), Validation.min(0)]}
invalidHint={t("The value has to be a positive integer")}
labelClass="col-md-3"
divClass="col-md-6"
Expand All @@ -193,7 +193,7 @@ export function DnsConfig(props: Props) {
label={t("Target weight")}
title={t(`DNS SRV record ${index} weight`)}
name={`dns_srvs${index}_weight`}
validate={[Validation.isInt({ min: 0 })]}
validate={[Validation.isInt(), Validation.min(0)]}
invalidHint={t("The value has to be a positive integer")}
labelClass="col-md-3"
divClass="col-md-6"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { FormMultiInput } from "components/input/form-multi-input/FormMultiInput
import { Range } from "components/input/range/Range";
import { Text } from "components/input/text/Text";
import { Panel } from "components/panels/Panel";
import Validation from "components/validation";
import { Validation } from "components/input";

import { NetworkAddress } from "./NetworkAddress";
import * as utils from "./utils";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as React from "react";
import { FormContext } from "components/input/form/Form";
import { Radio } from "components/input/radio/Radio";
import { Text } from "components/input/text/Text";
import Validation from "components/validation";
import { Validation } from "components/input";

import * as utils from "./utils";

Expand Down
4 changes: 2 additions & 2 deletions web/html/src/manager/virtualization/nets/properties/Vlans.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import * as React from "react";

import { Select } from "components/input";
import { Validation } from "components/input";
import { FormContext } from "components/input/form/Form";
import { FormMultiInput } from "components/input/form-multi-input/FormMultiInput";
import { Text } from "components/input/text/Text";
import Validation from "components/validation";

type Props = {};

Expand Down Expand Up @@ -53,7 +53,7 @@ export function Vlans(props: Props) {
divClass="col-md-12"
className="col-md-6"
required
validate={[Validation.isInt({ min: 0, max: 4095 })]}
validate={[Validation.intRange(0, 4095)]}
invalidHint={t("Integer between 0 and 4095")}
/>
<Select
Expand Down
14 changes: 6 additions & 8 deletions web/html/src/manager/virtualization/pools/pool-properties.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ActionChain } from "components/action-schedule";
import { ActionSchedule } from "components/action-schedule";
import { Button, SubmitButton } from "components/buttons";
import { Select } from "components/input";
import { Validation } from "components/input";
import { Check } from "components/input/check/Check";
import { Form } from "components/input/form/Form";
import { FormMultiInput } from "components/input/form-multi-input/FormMultiInput";
Expand All @@ -15,7 +16,6 @@ import { MessageType } from "components/messages/messages";
import { Panel } from "components/panels/Panel";
import { PanelRow } from "components/panels/PanelRow";
import { Loading } from "components/utils/loading/Loading";
import Validation from "components/validation";

import { localizedMoment } from "utils";

Expand Down Expand Up @@ -400,9 +400,7 @@ export function PoolProperties(props: Props) {
labelClass="col-md-3"
divClass="col-md-6"
invalidHint={t("PCI address formatted like 0000:00:00.0")}
validate={[
Validation.matches(/^[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{1}.[0-9a-fA-F]$/),
]}
validate={[Validation.matches(/^[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{1}.[0-9a-fA-F]$/)]}
/>
)}
{adapter_fields.includes("parent_address_uid") && (
Expand All @@ -413,7 +411,7 @@ export function PoolProperties(props: Props) {
labelClass="col-md-3"
divClass="col-md-6"
invalidHint={t("PCI address formatted like 0000:00:00.0")}
validate={[Validation.isInt]}
validate={[Validation.isInt()]}
/>
)}
{adapter_fields.includes("parent") && (
Expand Down Expand Up @@ -538,15 +536,15 @@ export function PoolProperties(props: Props) {
label={t("Owner UID")}
labelClass="col-md-3"
divClass="col-md-6"
validate={[Validation.isInt({ min: 0 })]}
validate={[Validation.isInt(), Validation.min(0)]}
invalidHint={t("UID is a numeric value")}
/>
<Text
name="target_group"
label={t("Group ID")}
labelClass="col-md-3"
divClass="col-md-6"
validate={[Validation.isInt({ min: 0 })]}
validate={[Validation.isInt(), Validation.min(0)]}
invalidHint={t("GID is a numeric value")}
/>
<Text
Expand All @@ -555,7 +553,7 @@ export function PoolProperties(props: Props) {
hint={t("target directory permissions in octal form, like 0775")}
labelClass="col-md-3"
divClass="col-md-6"
validate={[Validation.isInt({ gt: 0 })]}
validate={[Validation.isInt(), Validation.gt(0)]}
invalidHint={t("GID is a numeric value")}
/>
{/* TODO Add Link to the UI Reference */}
Expand Down

0 comments on commit a892e37

Please sign in to comment.