Skip to content

Commit

Permalink
fix(web): disable all setting save buttons when nothing is changed (#…
Browse files Browse the repository at this point in the history
…1178)

* fix: disable button

* yarn fix

* fix: test

* fix
  • Loading branch information
caichi-t authored Jun 19, 2024
1 parent 5bf27a5 commit 9cf1793
Show file tree
Hide file tree
Showing 53 changed files with 815 additions and 410 deletions.
3 changes: 1 addition & 2 deletions web/e2e/project/item/fields/group.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,7 @@ test("Group field creating and updating has succeeded", async ({ page }) => {
await expect(page.getByRole("main")).toContainText("new text1 description");
await expect(page.getByLabel("new text1(unique)")).toHaveValue("new text1");
await expect(page.getByText("/ 5")).toBeVisible();
await page.getByRole("button", { name: "Save" }).click();
await closeNotification(page, false);
await expect(page.getByRole("button", { name: "Save" })).toBeDisabled();
await page.getByLabel("new text1(unique)").click();
await page.getByLabel("new text1(unique)").fill("text1");
await page.getByRole("button", { name: "Save" }).click();
Expand Down
11 changes: 9 additions & 2 deletions web/src/components/atoms/Form/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,15 @@ import { Form, FormInstance } from "antd";
import { Rule } from "antd/lib/form";
import { FormItemProps } from "antd/lib/form/FormItem";
import { FormItemLabelProps } from "antd/lib/form/FormItemLabel";
import { FieldError } from "rc-field-form/lib/interface";
import { FieldError, ValidateErrorEntity } from "rc-field-form/lib/interface";

export default Form;

export type { FormItemProps, FormItemLabelProps, FieldError, FormInstance, Rule };
export type {
FormItemProps,
FormItemLabelProps,
FieldError,
FormInstance,
Rule,
ValidateErrorEntity,
};
57 changes: 48 additions & 9 deletions web/src/components/molecules/AccountSettings/GeneralForm.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import styled from "@emotion/styled";
import { useCallback } from "react";
import { useCallback, useState } from "react";

import Button from "@reearth-cms/components/atoms/Button";
import Form from "@reearth-cms/components/atoms/Form";
import Form, { ValidateErrorEntity } from "@reearth-cms/components/atoms/Form";
import Input from "@reearth-cms/components/atoms/Input";
import { User } from "@reearth-cms/components/molecules/AccountSettings/types";
import { useT } from "@reearth-cms/i18n";
Expand All @@ -20,38 +20,77 @@ interface FormType {
const AccountGeneralForm: React.FC<Props> = ({ user, onUserUpdate }) => {
const [form] = Form.useForm<FormType>();
const t = useT();
const [isDisabled, setIsDisabled] = useState(true);
const [isLoading, setIsLoading] = useState(false);

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

const handleSubmit = useCallback(async () => {
setIsDisabled(true);
setIsLoading(true);
try {
const values = await form.validateFields();
await onUserUpdate(values.name, values.email);
} catch (info) {
console.log("Validate Failed:", info);
} catch (_) {
setIsDisabled(false);
} finally {
setIsLoading(false);
}
}, [form, onUserUpdate]);

return (
<StyledForm form={form} initialValues={user} layout="vertical" autoComplete="on">
<StyledForm
form={form}
initialValues={user}
layout="vertical"
autoComplete="on"
requiredMark={false}
onValuesChange={handleValuesChange}>
<Form.Item
name="name"
label={t("Account Name")}
extra={t("This is your ID that is used between Re:Earth and Re:Earth CMS.")}>
extra={t("This is your ID that is used between Re:Earth and Re:Earth CMS.")}
rules={[
{
required: true,
message: t("Please input Account Name!"),
},
]}>
<Input />
</Form.Item>
<Form.Item
name="email"
label={t("Your Email")}
extra={t("Please enter the email address you want to use to log in with Re:Earth CMS.")}>
extra={t("Please enter the email address you want to use to log in with Re:Earth CMS.")}
rules={[
{
required: true,
message: t("Please input Your Email!"),
},
]}>
<Input />
</Form.Item>
<Button onClick={handleSubmit} type="primary" htmlType="submit">
<Button onClick={handleSubmit} type="primary" disabled={isDisabled} loading={isLoading}>
{t("Save")}
</Button>
</StyledForm>
);
};

const StyledForm = styled(Form)`
const StyledForm = styled(Form<FormType>)`
max-width: 400px;
`;

Expand Down
27 changes: 22 additions & 5 deletions web/src/components/molecules/AccountSettings/ServiceForm.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import styled from "@emotion/styled";
import { useCallback, useMemo } from "react";
import { useCallback, useMemo, useState } from "react";

import Button from "@reearth-cms/components/atoms/Button";
import Form from "@reearth-cms/components/atoms/Form";
Expand All @@ -20,6 +20,8 @@ const AccountServiceForm: React.FC<Props> = ({ user, onLanguageUpdate }) => {
const [form] = Form.useForm<FormType>();
const { Option } = Select;
const t = useT();
const [isDisabled, setIsDisabled] = useState(true);
const [isLoading, setIsLoading] = useState(false);

const langItems = useMemo(
() => [
Expand All @@ -32,9 +34,24 @@ const AccountServiceForm: React.FC<Props> = ({ user, onLanguageUpdate }) => {
[t],
);

const handleSelect = useCallback(
(value: string) => {
setIsDisabled(value === user?.lang);
},
[user?.lang],
);

const handleSubmit = useCallback(async () => {
const values = await form.validateFields();
await onLanguageUpdate(values.lang);
setIsDisabled(true);
setIsLoading(true);
try {
const values = await form.validateFields();
await onLanguageUpdate(values.lang);
} catch (_) {
setIsDisabled(false);
} finally {
setIsLoading(false);
}
}, [form, onLanguageUpdate]);

return (
Expand All @@ -43,15 +60,15 @@ const AccountServiceForm: React.FC<Props> = ({ user, onLanguageUpdate }) => {
name="lang"
label={t("Service Language")}
extra={t("This will change the UI language")}>
<Select placeholder={t("Language")}>
<Select placeholder={t("Language")} onSelect={handleSelect}>
{langItems?.map(langItem => (
<Option key={langItem.key} value={langItem.key}>
{langItem.label}
</Option>
))}
</Select>
</Form.Item>
<Button onClick={handleSubmit} type="primary" htmlType="submit">
<Button onClick={handleSubmit} type="primary" disabled={isDisabled} loading={isLoading}>
{t("Save")}
</Button>
</StyledForm>
Expand Down
10 changes: 9 additions & 1 deletion web/src/components/molecules/Asset/Asset/AssetBody/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ interface Props {
viewerType: ViewerType;
displayUnzipFileList: boolean;
decompressing: boolean;
isSaveDisabled: boolean;
updateLoading: boolean;
onAssetItemSelect: (item: AssetItem) => void;
onAssetDecompress: (assetId: string) => void;
onTypeChange: (value: PreviewType) => void;
Expand All @@ -38,6 +40,8 @@ const AssetWrapper: React.FC<Props> = ({
displayUnzipFileList,
decompressing,
commentsPanel,
isSaveDisabled,
updateLoading,
onAssetItemSelect,
onAssetDecompress,
onTypeChange,
Expand All @@ -55,7 +59,11 @@ const AssetWrapper: React.FC<Props> = ({
<Wrapper>
<PageHeader
title={`${t("Asset")}/${asset?.fileName}`}
extra={<Button onClick={onSave}>{t("Save")}</Button>}
extra={
<Button onClick={onSave} disabled={isSaveDisabled} loading={updateLoading}>
{t("Save")}
</Button>
}
onBack={onBack}
/>
<AssetMolecule
Expand Down
31 changes: 20 additions & 11 deletions web/src/components/molecules/Common/CommentsPanel/Editor.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import styled from "@emotion/styled";
import { useCallback, useState } from "react";
import { useCallback, useState, ChangeEvent } from "react";

import Button from "@reearth-cms/components/atoms/Button";
import Form from "@reearth-cms/components/atoms/Form";
Expand All @@ -9,25 +9,29 @@ import { useT } from "@reearth-cms/i18n";
const { TextArea } = Input;

interface EditorProps {
isInputDisabled: boolean;
onCommentCreate: (content: string) => Promise<void>;
disabled: boolean;
}

const Editor: React.FC<EditorProps> = ({ disabled, onCommentCreate }) => {
const Editor: React.FC<EditorProps> = ({ isInputDisabled, onCommentCreate }) => {
const [submitting, setSubmitting] = useState(false);
const [isSubmitDisabled, setIsSubmitDisabled] = useState(true);
const [form] = Form.useForm();
const t = useT();

const handleChange = useCallback((e: ChangeEvent<HTMLTextAreaElement>) => {
setIsSubmitDisabled(!e.target.value);
}, []);

const handleSubmit = useCallback(async () => {
setSubmitting(true);
setIsSubmitDisabled(true);
try {
setSubmitting(true);
const values = await form.validateFields();
await onCommentCreate?.(values.content);
form.resetFields();
setSubmitting(false);
} catch (info) {
setSubmitting(false);
console.log("Validate Failed:", info);
} catch (_) {
setIsSubmitDisabled(false);
} finally {
setSubmitting(false);
}
Expand All @@ -36,12 +40,17 @@ const Editor: React.FC<EditorProps> = ({ disabled, onCommentCreate }) => {
return (
<StyledForm form={form} layout="vertical">
<Form.Item name="content">
<TextArea maxLength={1000} showCount autoSize={{ maxRows: 4 }} />
<TextArea
maxLength={1000}
showCount
autoSize={{ maxRows: 4 }}
onChange={handleChange}
disabled={isInputDisabled}
/>
</Form.Item>
<StyledFormItem>
<Button
disabled={disabled}
htmlType="submit"
disabled={isSubmitDisabled}
loading={submitting}
onClick={handleSubmit}
type="primary"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const CommentsPanel: React.FC<Props> = ({
<EmptyTextWrapper>{emptyText}</EmptyTextWrapper>
) : null}

<Editor disabled={!comments} onCommentCreate={onCommentCreate} />
<Editor isInputDisabled={!comments} onCommentCreate={onCommentCreate} />
</>
)}
</ContentWrapper>
Expand Down
51 changes: 26 additions & 25 deletions web/src/components/molecules/Common/ProjectCreationModal/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useCallback, useState } from "react";

import Form, { FieldError } from "@reearth-cms/components/atoms/Form";
import Form, { ValidateErrorEntity } 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";
Expand Down Expand Up @@ -35,7 +35,8 @@ const ProjectCreationModal: React.FC<Props> = ({
}) => {
const t = useT();
const [form] = Form.useForm<FormValues>();
const [buttonDisabled, setButtonDisabled] = useState(true);
const [isLoading, setIsLoading] = useState(false);
const [isDisabled, setIsDisabled] = useState(true);

const handleNameChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
Expand All @@ -51,48 +52,48 @@ const ProjectCreationModal: React.FC<Props> = ({
[form],
);

const handleSubmit = useCallback(() => {
form
.validateFields()
.then(async values => {
await onSubmit(values);
onClose();
form.resetFields();
})
.catch(info => {
console.log("Validate Failed:", info);
});
const handleSubmit = useCallback(async () => {
setIsLoading(true);
setIsDisabled(true);
try {
const values = await form.validateFields();
await onSubmit(values);
onClose();
form.resetFields();
} catch (_) {
setIsDisabled(false);
} finally {
setIsLoading(false);
}
}, [form, onClose, onSubmit]);

const handleClose = useCallback(() => {
onClose();
form.resetFields();
setIsDisabled(true);
}, [form, onClose]);

const handleFormValues = useCallback(() => {
form
const handleValuesChange = useCallback(async () => {
const hasError = await form
.validateFields()
.then(() => {
setButtonDisabled(false);
})
.catch(fieldsError => {
setButtonDisabled(
fieldsError.errorFields.some((item: FieldError) => item.errors.length > 0),
);
});
.then(() => false)
.catch((errorInfo: ValidateErrorEntity) => errorInfo.errorFields.length > 0);
setIsDisabled(hasError);
}, [form]);

return (
<Modal
open={open}
onCancel={handleClose}
onOk={handleSubmit}
okButtonProps={{ disabled: buttonDisabled }}>
confirmLoading={isLoading}
cancelButtonProps={{ disabled: isLoading }}
okButtonProps={{ disabled: isDisabled }}>
<Form
form={form}
layout="vertical"
initialValues={initialValues}
onValuesChange={handleFormValues}>
onValuesChange={handleValuesChange}>
<Form.Item
name="name"
label={t("Project name")}
Expand Down
Loading

0 comments on commit 9cf1793

Please sign in to comment.