Skip to content

Commit

Permalink
fix(web): ignore invalid characters when auto-filling the key and ali…
Browse files Browse the repository at this point in the history
…as (#1187)

* fix: ignore invalid characters

* fix: use try-catch
  • Loading branch information
caichi-t authored Jul 2, 2024
1 parent aa507f6 commit fbd4118
Show file tree
Hide file tree
Showing 10 changed files with 113 additions and 97 deletions.
2 changes: 1 addition & 1 deletion web/e2e/general/project.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ test("Project CRUD and searching has succeeded", async ({ reearth, page }) => {

await expect(page.getByText("project name", { exact: true })).toBeVisible();
await expect(page.getByText("project description", { exact: true })).toBeVisible();
await page.locator(".ant-input-affix-wrapper").click();
await page.getByPlaceholder("search projects").click();
await page.getByPlaceholder("search projects").fill("no project");
await page.getByRole("button", { name: "search" }).click();
await expect(page.getByText("project name", { exact: true })).toBeHidden();
Expand Down
5 changes: 4 additions & 1 deletion web/src/components/molecules/Common/Form/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { FormInstance } from "@reearth-cms/components/atoms/Form";
import { validateKey } from "@reearth-cms/utils/regex";

export const keyAutoFill = (
e: React.ChangeEvent<HTMLInputElement>,
Expand All @@ -9,7 +10,9 @@ export const keyAutoFill = (
const currentAlias = form.getFieldValue(key);
if (!currentAlias || currentAlias.toLowerCase() === prevName.toLowerCase()) {
const currentName = e.currentTarget.value.replaceAll(" ", "-").toLowerCase();
form.setFieldValue(key, currentName);
if (validateKey(currentName) || currentName === "") {
form.setFieldValue(key, currentName);
}
}
};

Expand Down
26 changes: 16 additions & 10 deletions web/src/components/molecules/Common/ProjectCreationModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import Modal from "@reearth-cms/components/atoms/Modal";
import TextArea from "@reearth-cms/components/atoms/TextArea";
import { keyAutoFill, keyReplace } from "@reearth-cms/components/molecules/Common/Form/utils";
import { useT } from "@reearth-cms/i18n";
import { validateKey } from "@reearth-cms/utils/regex";
import { MAX_KEY_LENGTH, validateKey } from "@reearth-cms/utils/regex";

export interface FormValues {
name: string;
Expand Down Expand Up @@ -41,10 +41,14 @@ const ProjectCreationModal: React.FC<Props> = ({

const values = Form.useWatch([], form);
useEffect(() => {
form
.validateFields({ validateOnly: true })
.then(() => setIsDisabled(false))
.catch(() => setIsDisabled(true));
if (form.getFieldValue("name") && form.getFieldValue("alias")) {
form
.validateFields()
.then(() => setIsDisabled(false))
.catch(() => setIsDisabled(true));
} else {
setIsDisabled(true);
}
}, [form, values]);

const handleNameChange = useCallback(
Expand Down Expand Up @@ -109,20 +113,22 @@ const ProjectCreationModal: React.FC<Props> = ({
<Form.Item
name="alias"
label={t("Project alias")}
extra={t(
"Project alias must be unique and at least 5 characters long. It can only contain letters, numbers, underscores, and dashes.",
)}
rules={[
{
message: t("Project alias is not valid"),
required: true,
validator: async (_, value) => {
if (!validateKey(value) || value.length <= 4) {
return Promise.reject();
if (value.length >= 5 && validateKey(value) && (await onProjectAliasCheck(value))) {
return Promise.resolve();
}
const isProjectAliasAvailable = await onProjectAliasCheck(value);
return isProjectAliasAvailable ? Promise.resolve() : Promise.reject();
return Promise.reject();
},
},
]}>
<Input onChange={handleAliasChange} />
<Input onChange={handleAliasChange} showCount maxLength={MAX_KEY_LENGTH} />
</Form.Item>
<Form.Item name="description" label={t("Project description")}>
<TextArea rows={4} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
FormValues,
} from "@reearth-cms/components/molecules/Schema/types";
import { useT } from "@reearth-cms/i18n";
import { validateKey } from "@reearth-cms/utils/regex";
import { MAX_KEY_LENGTH, validateKey } from "@reearth-cms/utils/regex";

const { Step } = Steps;
const { TabPane } = Tabs;
Expand Down Expand Up @@ -66,10 +66,14 @@ const FieldCreationModalWithSteps: React.FC<Props> = ({
const isDisabledCache = useRef<boolean>(true);

const formValidate = useCallback((form: FormInstance) => {
form
.validateFields({ validateOnly: true })
.then(() => setIsDisabled(false))
.catch(() => setIsDisabled(true));
if (form.getFieldValue("model") || (form.getFieldValue("title") && form.getFieldValue("key"))) {
form
.validateFields()
.then(() => setIsDisabled(false))
.catch(() => setIsDisabled(true));
} else {
setIsDisabled(true);
}
}, []);

const SettingValues = Form.useWatch([], modelForm);
Expand Down Expand Up @@ -433,6 +437,8 @@ const FieldCreationModalWithSteps: React.FC<Props> = ({
onChange={e => {
handleKeyChange(e, field1Form);
}}
showCount
maxLength={MAX_KEY_LENGTH}
/>
</Form.Item>
<Form.Item name="description" label={t("Description")}>
Expand Down Expand Up @@ -534,6 +540,8 @@ const FieldCreationModalWithSteps: React.FC<Props> = ({
onChange={e => {
handleKeyChange(e, field2Form);
}}
showCount
maxLength={MAX_KEY_LENGTH}
/>
</Form.Item>
<Form.Item name="description" label={t("Description")}>
Expand Down
55 changes: 26 additions & 29 deletions web/src/components/molecules/Schema/FieldModal/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ export default (
return {
group: { groupId: values.group },
};
case "Text":
default:
return {
text: { defaultValue: values.defaultValue, maxLength: values.maxLength },
Expand All @@ -193,10 +194,14 @@ export default (

const values = Form.useWatch([], form);
useEffect(() => {
form
.validateFields({ validateOnly: true })
.then(() => setButtonDisabled(false))
.catch(() => setButtonDisabled(true));
if (form.getFieldValue("title") && form.getFieldValue("key")) {
form
.validateFields()
.then(() => setButtonDisabled(false))
.catch(() => setButtonDisabled(true));
} else {
setButtonDisabled(true);
}
}, [form, values]);

const handleNameChange = useCallback(
Expand All @@ -214,35 +219,28 @@ export default (
[form],
);

const handleSubmit = useCallback(() => {
form
.validateFields()
.then(async values => {
values.type = selectedType;
values.typeProperty = typePropertyGet(values);
values.metadata = isMeta;
await onSubmit({
...values,
fieldId: selectedField?.id,
});
setMultipleValue(false);
onClose();
})
.catch(info => {
console.log("Validate Failed:", info);
});
}, [form, selectedType, typePropertyGet, isMeta, onSubmit, selectedField?.id, onClose]);

const handleModalReset = useCallback(() => {
form.resetFields();
setActiveTab("settings");
}, [form]);

const handleModalCancel = useCallback(() => {
setMultipleValue(false);
setButtonDisabled(true);
onClose();
}, [onClose]);
}, [form, onClose]);

const handleSubmit = useCallback(async () => {
const values = await form.validateFields();
values.type = selectedType;
values.typeProperty = typePropertyGet(values);
values.metadata = isMeta;
try {
await onSubmit({
...values,
fieldId: selectedField?.id,
});
handleModalReset();
} catch (error) {
console.error(error);
}
}, [form, selectedType, typePropertyGet, isMeta, onSubmit, selectedField?.id, onClose]);

const isRequiredDisabled = useMemo(
() => selectedType === "Group" || selectedType === "Bool" || selectedType === "Checkbox",
Expand All @@ -267,7 +265,6 @@ export default (
handleKeyChange,
handleSubmit,
handleModalReset,
handleModalCancel,
isRequiredDisabled,
isUniqueDisabled,
};
Expand Down
15 changes: 6 additions & 9 deletions web/src/components/molecules/Schema/FieldModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
SortDirection,
} from "@reearth-cms/components/organisms/Project/Asset/AssetList/hooks";
import { useT } from "@reearth-cms/i18n";
import { validateKey } from "@reearth-cms/utils/regex";
import { MAX_KEY_LENGTH, validateKey } from "@reearth-cms/utils/regex";

import useHooks from "./hooks";

Expand Down Expand Up @@ -135,7 +135,6 @@ const FieldModal: React.FC<Props> = ({
handleKeyChange,
handleSubmit,
handleModalReset,
handleModalCancel,
isRequiredDisabled,
isUniqueDisabled,
} = useHooks(selectedType, isMeta, selectedField, onClose, onSubmit);
Expand All @@ -162,10 +161,9 @@ const FieldModal: React.FC<Props> = ({
}
width={572}
open={open}
onCancel={handleModalCancel}
afterClose={handleModalReset}
onCancel={handleModalReset}
footer={[
<Button key="cancel" onClick={handleModalCancel} disabled={fieldLoading}>
<Button key="cancel" onClick={handleModalReset} disabled={fieldLoading}>
{t("Cancel")}
</Button>,
<Button
Expand Down Expand Up @@ -196,16 +194,15 @@ const FieldModal: React.FC<Props> = ({
{
message: t("Key is not valid"),
required: true,
validator: async (_, value) => {
validator: (_, value) => {
if (validateKey(value) && handleFieldKeyUnique(value, selectedField?.id)) {
return Promise.resolve();
} else {
return Promise.reject();
}
return Promise.reject();
},
},
]}>
<Input onChange={handleKeyChange} />
<Input onChange={handleKeyChange} showCount maxLength={MAX_KEY_LENGTH} />
</Form.Item>
<Form.Item name="description" label={t("Description")}>
<TextArea rows={3} showCount maxLength={1000} />
Expand Down
73 changes: 38 additions & 35 deletions web/src/components/molecules/Schema/FormModal.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { useCallback, useState, useEffect, useMemo } from "react";

import Button from "@reearth-cms/components/atoms/Button";
import Form, { ValidateErrorEntity } from "@reearth-cms/components/atoms/Form";
import Form from "@reearth-cms/components/atoms/Form";
import Input from "@reearth-cms/components/atoms/Input";
import Modal from "@reearth-cms/components/atoms/Modal";
import TextArea from "@reearth-cms/components/atoms/TextArea";
import { keyAutoFill, keyReplace } from "@reearth-cms/components/molecules/Common/Form/utils";
import { Model } from "@reearth-cms/components/molecules/Model/types";
import { ModelFormValues, Group } from "@reearth-cms/components/molecules/Schema/types";
import { useT } from "@reearth-cms/i18n";
import { validateKey } from "@reearth-cms/utils/regex";
import { MAX_KEY_LENGTH, validateKey } from "@reearth-cms/utils/regex";

interface Props {
data?: Model | Group;
Expand Down Expand Up @@ -41,6 +41,26 @@ const FormModal: React.FC<Props> = ({
const [isLoading, setIsLoading] = useState(false);
const [isDisabled, setIsDisabled] = useState(true);

const values = Form.useWatch([], form);
useEffect(() => {
if (form.getFieldValue("name") && form.getFieldValue("key")) {
if (
data?.name === values.name &&
data.description === values.description &&
data.key === values.key
) {
setIsDisabled(true);
} else {
form
.validateFields()
.then(() => setIsDisabled(false))
.catch(() => setIsDisabled(true));
}
} else {
setIsDisabled(true);
}
}, [data?.description, data?.key, data?.name, form, values]);

useEffect(() => {
if (open) {
if (data) {
Expand Down Expand Up @@ -94,25 +114,6 @@ const FormModal: React.FC<Props> = ({
setIsDisabled(true);
}, [form, data, onClose]);

const handleValuesChange = useCallback(
async (_: unknown, values: FormType) => {
if (
data?.name === values.name &&
data.description === values.description &&
data.key === values.key
) {
setIsDisabled(true);
return;
}
const hasError = await form
.validateFields()
.then(() => false)
.catch((errorInfo: ValidateErrorEntity) => errorInfo.errorFields.length > 0);
setIsDisabled(hasError);
},
[data?.description, data?.key, data?.name, form],
);

const title = useMemo(
() =>
isModel
Expand Down Expand Up @@ -140,10 +141,10 @@ const FormModal: React.FC<Props> = ({
() =>
isModel
? t(
"Model key must be unique and at least 1 character long. It can only contain letters, numbers, underscores and dashes.",
"Model key must be unique and at least 3 characters long. It can only contain letters, numbers, underscores, and dashes.",
)
: t(
"Group key must be unique and at least 1 character long. It can only contain letters, numbers, underscores and dashes.",
"Group key must be unique and at least 3 characters long. It can only contain letters, numbers, underscores, and dashes.",
),
[isModel, t],
);
Expand All @@ -166,7 +167,7 @@ const FormModal: React.FC<Props> = ({
{t("OK")}
</Button>,
]}>
<Form form={form} layout="vertical" onValuesChange={handleValuesChange}>
<Form form={form} layout="vertical">
<Form.Item
name="name"
label={nameLabel}
Expand All @@ -186,20 +187,22 @@ const FormModal: React.FC<Props> = ({
label={keyLabel}
extra={keyExtra}
rules={[
({ getFieldValue }) => ({
async validator() {
const value: string = getFieldValue("key");
if (value.length >= 3 && validateKey(value)) {
const isKeyAvailable = await onKeyCheck(value, data?.key);
if (isKeyAvailable) {
return Promise.resolve();
}
{
message: t("Key is not valid"),
required: true,
validator: async (_, value) => {
if (
value.length >= 3 &&
validateKey(value) &&
(await onKeyCheck(value, data?.key))
) {
return Promise.resolve();
}
return Promise.reject(new Error(t("Key is not valid")));
return Promise.reject();
},
}),
},
]}>
<Input onChange={handleKeyChange} />
<Input onChange={handleKeyChange} showCount maxLength={MAX_KEY_LENGTH} />
</Form.Item>
</Form>
</Modal>
Expand Down
Loading

0 comments on commit fbd4118

Please sign in to comment.