feat(filters): improve RegexField validation (#803)

* make sure fields are validated on page load

* make red border only show if useRegex enabled

* ensure tooltips have higher z-index in RegexField

removed z-index in customtooltip.tsx as its causing issues with tooltips in other components

* improved regex validation

return false for cases that are unsupported by Go

* improved check for unsupported conditionals
thanks nuxen
This commit is contained in:
soup 2023-04-10 14:36:13 +02:00 committed by GitHub
parent 169863dded
commit 195b2929e0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 52 additions and 8 deletions

View file

@ -1,8 +1,9 @@
import { Field, FieldProps } from "formik"; import { Field, FieldProps, useFormikContext } from "formik";
import { classNames } from "../../utils"; import { classNames } from "../../utils";
import { EyeIcon, EyeSlashIcon, CheckCircleIcon, XCircleIcon } from "@heroicons/react/24/solid"; import { EyeIcon, EyeSlashIcon, CheckCircleIcon, XCircleIcon } from "@heroicons/react/24/solid";
import { useToggle } from "../../hooks/hooks"; import { useToggle } from "../../hooks/hooks";
import { CustomTooltip } from "../tooltips/CustomTooltip"; import { CustomTooltip } from "../tooltips/CustomTooltip";
import { useEffect } from "react";
type COL_WIDTHS = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12; type COL_WIDTHS = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
@ -100,17 +101,53 @@ export const RegexField = ({
tooltip, tooltip,
disabled disabled
}: RegexFieldProps) => { }: RegexFieldProps) => {
const golangRegex = /^((\\\*|\\\?|\\[^\s\\])+|\(\?i\))(\|((\\\*|\\\?|\\[^\s\\])+|\(\?i\)))*$/;
const validRegex = (pattern: string) => { const validRegex = (pattern: string) => {
// Check for unsupported lookahead and lookbehind assertions
if (/\(\?<=|\(\?<!|\(\?=|\(\?!/.test(pattern)) {
return false;
}
// Check for unsupported atomic groups
if (/\(\?>/.test(pattern)) {
return false;
}
// Check for unsupported recursive patterns
if (/\(\?(R|0)\)/.test(pattern)) {
return false;
}
// Check for unsupported possessive quantifiers
if (/[*+?]{1}\+|\{[0-9]+,[0-9]*\}\+/.test(pattern)) {
return false;
}
// Check for unsupported control verbs
if (/\\g</.test(pattern)) {
return false;
}
// Check for unsupported conditionals
if (/\(\?\((\?[=!][^)]*)\)[^)]*\|?[^)]*\)/.test(pattern)) {
return false;
}
// Check for unsupported backreferences
if (/\\k</.test(pattern)) {
return false;
}
// Check if the pattern is a valid regex
try { try {
new RegExp(golangRegex.source + pattern); new RegExp(pattern);
return true; return true;
} catch (e) { } catch (e) {
return false; return false;
} }
}; };
const validateRegexp = (val: string) => { const validateRegexp = (val: string) => {
let error = ""; let error = "";
@ -121,6 +158,13 @@ export const RegexField = ({
return error; return error;
}; };
const { validateForm } = useFormikContext();
useEffect(() => {
if (useRegex) {
validateForm();
}
}, [useRegex]);
return ( return (
<div <div
className={classNames( className={classNames(
@ -135,7 +179,7 @@ export const RegexField = ({
> >
<div className="flex"> <div className="flex">
{label} {label}
{tooltip && <CustomTooltip anchorId={name}>{tooltip}</CustomTooltip>} <span className="z-10">{tooltip && <CustomTooltip anchorId={name}>{tooltip}</CustomTooltip>}</span>
</div> </div>
</label> </label>
)} )}
@ -152,7 +196,7 @@ export const RegexField = ({
defaultValue={defaultValue} defaultValue={defaultValue}
autoComplete={autoComplete} autoComplete={autoComplete}
className={classNames( className={classNames(
meta.touched && meta.error useRegex && meta.error
? "focus:ring-red-500 focus:border-red-500 border-red-500" ? "focus:ring-red-500 focus:border-red-500 border-red-500"
: "focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700", : "focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700",
disabled disabled
@ -166,7 +210,7 @@ export const RegexField = ({
{useRegex && ( {useRegex && (
<div className="relative"> <div className="relative">
<div className="flex float-right items-center"> <div className="flex float-right items-center">
{meta.touched && !meta.error ? ( {!meta.error ? (
<CheckCircleIcon className="dark:bg-gray-800 bg-white h-8 w-8 mb-2.5 pl-1 text-green-500 right-2 absolute transform -translate-y-1/2" aria-hidden="true" style={{ overflow: "hidden" }} /> <CheckCircleIcon className="dark:bg-gray-800 bg-white h-8 w-8 mb-2.5 pl-1 text-green-500 right-2 absolute transform -translate-y-1/2" aria-hidden="true" style={{ overflow: "hidden" }} />
) : ( ) : (
<XCircleIcon className="dark:bg-gray-800 bg-white h-8 w-8 mb-2.5 pl-1 text-red-500 right-2 absolute transform -translate-y-1/2" aria-hidden="true" style={{ overflow: "hidden" }} /> <XCircleIcon className="dark:bg-gray-800 bg-white h-8 w-8 mb-2.5 pl-1 text-red-500 right-2 absolute transform -translate-y-1/2" aria-hidden="true" style={{ overflow: "hidden" }} />

View file

@ -18,7 +18,7 @@ export const CustomTooltip = ({
const id = `${anchorId}-tooltip`; const id = `${anchorId}-tooltip`;
return ( return (
<div className="flex items-center"> <div className="flex items-center">
<svg id={id} className="z-10 ml-1 w-4 h-4 text-gray-500 dark:text-gray-400 fill-current" viewBox="0 0 72 72"><path d="M32 2C15.432 2 2 15.432 2 32s13.432 30 30 30s30-13.432 30-30S48.568 2 32 2m5 49.75H27v-24h10v24m-5-29.5a5 5 0 1 1 0-10a5 5 0 0 1 0 10"/></svg> <svg id={id} className="ml-1 w-4 h-4 text-gray-500 dark:text-gray-400 fill-current" viewBox="0 0 72 72"><path d="M32 2C15.432 2 2 15.432 2 32s13.432 30 30 30s30-13.432 30-30S48.568 2 32 2m5 49.75H27v-24h10v24m-5-29.5a5 5 0 1 1 0-10a5 5 0 0 1 0 10"/></svg>
<Tooltip style= {{ maxWidth: "350px", fontSize: "12px", textTransform: "none", fontWeight: "normal", borderRadius: "0.375rem", backgroundColor: "#34343A", color: "#fff", opacity: "1" }} delayShow={100} delayHide={150} place={place} anchorId={id} data-html={true} clickable={clickable}> <Tooltip style= {{ maxWidth: "350px", fontSize: "12px", textTransform: "none", fontWeight: "normal", borderRadius: "0.375rem", backgroundColor: "#34343A", color: "#fff", opacity: "1" }} delayShow={100} delayHide={150} place={place} anchorId={id} data-html={true} clickable={clickable}>
{children} {children}
</Tooltip> </Tooltip>