chore: input character limit error message improvement (#4271)

This commit is contained in:
Anmol Singh Bhatia 2024-04-24 15:17:50 +05:30 committed by GitHub
parent fc1cffd524
commit 87737dbfbe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 84 additions and 48 deletions

View File

@ -27,9 +27,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>((props, ref) => {
: mode === "true-transparent" : mode === "true-transparent"
? "rounded border-none bg-transparent ring-0" ? "rounded border-none bg-transparent ring-0"
: "" : ""
} ${hasError ? "border-red-500" : ""} ${hasError && mode === "primary" ? "bg-red-500/20" : ""} ${ } ${hasError ? "border-red-500" : ""} ${inputSize === "sm" ? "px-3 py-2" : inputSize === "md" ? "p-3" : ""}`,
inputSize === "sm" ? "px-3 py-2" : inputSize === "md" ? "p-3" : ""
}`,
className className
)} )}
{...rest} {...rest}

View File

@ -77,7 +77,7 @@ export const CycleForm: React.FC<Props> = (props) => {
</div> </div>
<div className="space-y-3"> <div className="space-y-3">
<div className="mt-2 space-y-3"> <div className="mt-2 space-y-3">
<div> <div className="flex flex-col gap-1">
<Controller <Controller
name="name" name="name"
control={control} control={control}
@ -85,7 +85,7 @@ export const CycleForm: React.FC<Props> = (props) => {
required: "Name is required", required: "Name is required",
maxLength: { maxLength: {
value: 255, value: 255,
message: "Name should be less than 255 characters", message: "Title should be less than 255 characters",
}, },
}} }}
render={({ field: { value, onChange } }) => ( render={({ field: { value, onChange } }) => (
@ -103,6 +103,7 @@ export const CycleForm: React.FC<Props> = (props) => {
/> />
)} )}
/> />
<span className="text-xs text-red-500">{errors?.name?.message}</span>
</div> </div>
<div> <div>
<Controller <Controller

View File

@ -116,10 +116,16 @@ export const InboxIssueCreateRoot: FC<TInboxIssueCreateRoot> = observer((props)
setFormSubmitting(false); setFormSubmitting(false);
}; };
const isTitleLengthMoreThan255Character = formData?.name ? formData.name.length > 255 : false;
if (!workspaceSlug || !projectId || !workspaceId) return <></>; if (!workspaceSlug || !projectId || !workspaceId) return <></>;
return ( return (
<form className="relative space-y-4" onSubmit={handleFormSubmit}> <form className="relative space-y-4" onSubmit={handleFormSubmit}>
<InboxIssueTitle data={formData} handleData={handleFormData} /> <InboxIssueTitle
data={formData}
handleData={handleFormData}
isTitleLengthMoreThan255Character={isTitleLengthMoreThan255Character}
/>
<InboxIssueDescription <InboxIssueDescription
workspaceSlug={workspaceSlug} workspaceSlug={workspaceSlug}
projectId={projectId} projectId={projectId}
@ -138,7 +144,13 @@ export const InboxIssueCreateRoot: FC<TInboxIssueCreateRoot> = observer((props)
<Button variant="neutral-primary" size="sm" type="button" onClick={handleModalClose}> <Button variant="neutral-primary" size="sm" type="button" onClick={handleModalClose}>
Discard Discard
</Button> </Button>
<Button variant="primary" size="sm" type="submit" loading={formSubmitting}> <Button
variant="primary"
size="sm"
type="submit"
loading={formSubmitting}
disabled={isTitleLengthMoreThan255Character}
>
{formSubmitting ? "Adding Issue..." : "Add Issue"} {formSubmitting ? "Adding Issue..." : "Add Issue"}
</Button> </Button>
</div> </div>

View File

@ -121,10 +121,16 @@ export const InboxIssueEditRoot: FC<TInboxIssueEditRoot> = observer((props) => {
setFormSubmitting(false); setFormSubmitting(false);
}; };
const isTitleLengthMoreThan255Character = formData?.name ? formData.name.length > 255 : false;
if (!workspaceSlug || !projectId || !workspaceId || !formData) return <></>; if (!workspaceSlug || !projectId || !workspaceId || !formData) return <></>;
return ( return (
<div className="relative space-y-4"> <div className="relative space-y-4">
<InboxIssueTitle data={formData} handleData={handleFormData} /> <InboxIssueTitle
data={formData}
handleData={handleFormData}
isTitleLengthMoreThan255Character={isTitleLengthMoreThan255Character}
/>
<InboxIssueDescription <InboxIssueDescription
workspaceSlug={workspaceSlug} workspaceSlug={workspaceSlug}
projectId={projectId} projectId={projectId}
@ -138,7 +144,14 @@ export const InboxIssueEditRoot: FC<TInboxIssueEditRoot> = observer((props) => {
<Button variant="neutral-primary" size="sm" type="button" onClick={handleModalClose}> <Button variant="neutral-primary" size="sm" type="button" onClick={handleModalClose}>
Cancel Cancel
</Button> </Button>
<Button variant="primary" size="sm" type="button" loading={formSubmitting} onClick={handleFormSubmit}> <Button
variant="primary"
size="sm"
type="button"
loading={formSubmitting}
disabled={isTitleLengthMoreThan255Character}
onClick={handleFormSubmit}
>
{formSubmitting ? "Adding..." : "Add to project"} {formSubmitting ? "Adding..." : "Add to project"}
</Button> </Button>
</div> </div>

View File

@ -6,10 +6,11 @@ import { Input } from "@plane/ui";
type TInboxIssueTitle = { type TInboxIssueTitle = {
data: Partial<TIssue>; data: Partial<TIssue>;
handleData: (issueKey: keyof Partial<TIssue>, issueValue: Partial<TIssue>[keyof Partial<TIssue>]) => void; handleData: (issueKey: keyof Partial<TIssue>, issueValue: Partial<TIssue>[keyof Partial<TIssue>]) => void;
isTitleLengthMoreThan255Character?: boolean;
}; };
export const InboxIssueTitle: FC<TInboxIssueTitle> = observer((props) => { export const InboxIssueTitle: FC<TInboxIssueTitle> = observer((props) => {
const { data, handleData } = props; const { data, handleData, isTitleLengthMoreThan255Character } = props;
return ( return (
<div className="relative flex flex-wrap gap-2 items-center"> <div className="relative flex flex-wrap gap-2 items-center">
@ -21,9 +22,11 @@ export const InboxIssueTitle: FC<TInboxIssueTitle> = observer((props) => {
onChange={(e) => handleData("name", e.target.value)} onChange={(e) => handleData("name", e.target.value)}
placeholder="Title" placeholder="Title"
className="w-full resize-none text-xl" className="w-full resize-none text-xl"
maxLength={255}
required required
/> />
{isTitleLengthMoreThan255Character && (
<span className="text-xs text-red-500">Title should be less than 255 characters</span>
)}
</div> </div>
); );
}); });

View File

@ -391,6 +391,8 @@ export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
/> />
)} )}
/> />
<span className="text-xs text-red-500">{errors?.name?.message}</span>
<div className="relative"> <div className="relative">
{data?.description_html === undefined ? ( {data?.description_html === undefined ? (
<Loader className="min-h-[7rem] space-y-2 overflow-hidden rounded-md border border-custom-border-200 p-2 py-2"> <Loader className="min-h-[7rem] space-y-2 overflow-hidden rounded-md border border-custom-border-200 p-2 py-2">

View File

@ -90,7 +90,7 @@ export const ModuleForm: React.FC<Props> = (props) => {
</div> </div>
<div className="space-y-3"> <div className="space-y-3">
<div> <div className="flex flex-col gap-1">
<Controller <Controller
control={control} control={control}
name="name" name="name"
@ -109,13 +109,14 @@ export const ModuleForm: React.FC<Props> = (props) => {
value={value} value={value}
onChange={onChange} onChange={onChange}
ref={ref} ref={ref}
hasError={Boolean(errors.name)} hasError={Boolean(errors?.name)}
placeholder="Module Title" placeholder="Module Title"
className="w-full resize-none placeholder:text-sm placeholder:font-medium focus:border-blue-400" className="w-full resize-none placeholder:text-sm placeholder:font-medium focus:border-blue-400"
tabIndex={1} tabIndex={1}
/> />
)} )}
/> />
<span className="text-xs text-red-500">{errors?.name?.message}</span>
</div> </div>
<div> <div>
<Controller <Controller

View File

@ -216,6 +216,10 @@ export const ProjectDetailsForm: FC<IProjectDetailsForm> = (props) => {
name="name" name="name"
rules={{ rules={{
required: "Name is required", required: "Name is required",
maxLength: {
value: 255,
message: "Project name should be less than 255 characters",
},
}} }}
render={({ field: { value, onChange, ref } }) => ( render={({ field: { value, onChange, ref } }) => (
<Input <Input
@ -232,9 +236,7 @@ export const ProjectDetailsForm: FC<IProjectDetailsForm> = (props) => {
/> />
)} )}
/> />
<span className="text-xs text-red-500"> <span className="text-xs text-red-500">{errors?.name?.message}</span>
<>{errors?.name?.message}</>
</span>
</div> </div>
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1">
<h4 className="text-sm">Description</h4> <h4 className="text-sm">Description</h4>

View File

@ -112,7 +112,7 @@ export const ProjectViewForm: React.FC<Props> = observer((props) => {
<div className="space-y-5"> <div className="space-y-5">
<h3 className="text-lg font-medium leading-6 text-custom-text-100">{data ? "Update" : "Create"} View</h3> <h3 className="text-lg font-medium leading-6 text-custom-text-100">{data ? "Update" : "Create"} View</h3>
<div className="space-y-3"> <div className="space-y-3">
<div> <div className="flex flex-col gap-1">
<Controller <Controller
control={control} control={control}
name="name" name="name"
@ -137,6 +137,7 @@ export const ProjectViewForm: React.FC<Props> = observer((props) => {
/> />
)} )}
/> />
<span className="text-xs text-red-500">{errors?.name?.message}</span>
</div> </div>
<div> <div>
<Controller <Controller
@ -215,8 +216,8 @@ export const ProjectViewForm: React.FC<Props> = observer((props) => {
? "Updating View..." ? "Updating View..."
: "Update View" : "Update View"
: isSubmitting : isSubmitting
? "Creating View..." ? "Creating View..."
: "Create View"} : "Create View"}
</Button> </Button>
</div> </div>
</form> </form>

View File

@ -125,35 +125,38 @@ export const CreateWorkspaceForm: FC<Props> = observer((props) => {
Workspace Name Workspace Name
<span className="ml-0.5 text-red-500">*</span> <span className="ml-0.5 text-red-500">*</span>
</label> </label>
<Controller <div className="flex flex-col gap-1">
control={control} <Controller
name="name" control={control}
rules={{ name="name"
required: "Workspace name is required", rules={{
validate: (value) => required: "Workspace name is required",
/^[\w\s-]*$/.test(value) || `Name can only contain (" "), ( - ), ( _ ) & alphanumeric characters.`, validate: (value) =>
maxLength: { /^[\w\s-]*$/.test(value) || `Name can only contain (" "), ( - ), ( _ ) & alphanumeric characters.`,
value: 80, maxLength: {
message: "Workspace name should not exceed 80 characters", value: 80,
}, message: "Workspace name should not exceed 80 characters",
}} },
render={({ field: { value, ref, onChange } }) => ( }}
<Input render={({ field: { value, ref, onChange } }) => (
id="workspaceName" <Input
type="text" id="workspaceName"
value={value} type="text"
onChange={(e) => { value={value}
onChange(e.target.value); onChange={(e) => {
setValue("name", e.target.value); onChange(e.target.value);
setValue("slug", e.target.value.toLocaleLowerCase().trim().replace(/ /g, "-")); setValue("name", e.target.value);
}} setValue("slug", e.target.value.toLocaleLowerCase().trim().replace(/ /g, "-"));
ref={ref} }}
hasError={Boolean(errors.name)} ref={ref}
placeholder="Enter workspace name..." hasError={Boolean(errors.name)}
className="w-full" placeholder="Enter workspace name..."
/> className="w-full"
)} />
/> )}
/>
<span className="text-xs text-red-500">{errors?.name?.message}</span>
</div>
</div> </div>
<div className="space-y-1 text-sm"> <div className="space-y-1 text-sm">
<label htmlFor="workspaceUrl"> <label htmlFor="workspaceUrl">