Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(web): add float field #1290

Merged
merged 12 commits into from
Nov 14, 2024
123 changes: 123 additions & 0 deletions web/e2e/project/item/fields/float.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { closeNotification } from "@reearth-cms/e2e/common/notification";
import { createModel } from "@reearth-cms/e2e/project/utils/model";
import { createProject, deleteProject } from "@reearth-cms/e2e/project/utils/project";
import { expect, test } from "@reearth-cms/e2e/utils";

test.beforeEach(async ({ reearth, page }) => {
await reearth.goto("/", { waitUntil: "domcontentloaded" });
await createProject(page);
await createModel(page);
});

test.afterEach(async ({ page }) => {
await deleteProject(page);
});

test("Float field creating and updating has succeeded", async ({ page }) => {
await page.locator("li").filter({ hasText: "Float" }).locator("div").first().click();
await page.getByLabel("Display name").click();
await page.getByLabel("Display name").fill("float1");
await page.getByLabel("Settings").locator("#key").click();
await page.getByLabel("Settings").locator("#key").fill("float1");
await page.getByLabel("Settings").locator("#description").click();
await page.getByLabel("Settings").locator("#description").fill("float1 description");

await page.getByRole("button", { name: "OK" }).click();
await closeNotification(page);

await expect(page.getByLabel("Fields").getByRole("paragraph")).toContainText("float1#float1");
await page.getByText("Content").click();
await page.getByRole("button", { name: "plus New Item" }).click();
await expect(page.locator("label")).toContainText("float1");
await expect(page.getByRole("main")).toContainText("float1 description");
await page.getByLabel("float1").click();
await page.getByLabel("float1").fill("1.1");
await page.getByRole("button", { name: "Save" }).click();
await closeNotification(page);
await page.getByLabel("Back").click();
await expect(page.getByRole("cell", { name: "1.1", exact: true })).toBeVisible();

await page.getByRole("cell").getByLabel("edit").locator("svg").click();
await expect(page.getByLabel("float1")).toHaveValue("1.1");
await page.getByLabel("float1").click();
await page.getByLabel("float1").fill("2.2");
await page.getByRole("button", { name: "Save" }).click();
await closeNotification(page);
await page.getByLabel("Back").click();
await expect(page.getByRole("cell", { name: "2.2", exact: true })).toBeVisible();
});

test("Float field editing has succeeded", async ({ page }) => {
await page.locator("li").filter({ hasText: "Float" }).locator("div").first().click();
await page.getByLabel("Display name").click();
await page.getByLabel("Display name").fill("float1");
await page.getByLabel("Settings").locator("#key").click();
await page.getByLabel("Settings").locator("#key").fill("float1");
await page.getByLabel("Settings").locator("#description").click();
await page.getByLabel("Settings").locator("#description").fill("float1 description");
await page.getByRole("tab", { name: "Default value" }).click();
await page.getByLabel("Set default value").click();
await page.getByLabel("Set default value").fill("1.1");
await page.getByRole("button", { name: "OK" }).click();
await closeNotification(page);

await page.getByText("Content").click();
await expect(page.locator("thead")).toContainText("float1");
await page.getByRole("button", { name: "plus New Item" }).click();
await page.getByRole("button", { name: "Save" }).click();
await closeNotification(page);
await page.getByLabel("Back").click();
await expect(page.getByRole("cell", { name: "1.1", exact: true })).toBeVisible();

await page.getByText("Schema").click();
await page.getByRole("img", { name: "ellipsis" }).locator("svg").click();
await page.getByRole("tab", { name: "Settings" }).click();
await page.getByLabel("Display name").click();
await page.getByLabel("Display name").fill("new float1");
await page.getByLabel("Field Key").click();
await page.getByLabel("Field Key").fill("new-float1");
await page.getByLabel("Description(optional)").click();
await page.getByLabel("Description(optional)").fill("new float1 description");
await page.getByLabel("Support multiple values").check();
await page.getByLabel("Use as title").check();
await page.getByRole("tab", { name: "Validation" }).click();
await page.getByLabel("Set minimum value").click();
await page.getByLabel("Set minimum value").fill("10.1");
await page.getByLabel("Set maximum value").click();
await page.getByLabel("Set maximum value").fill("2.1");
await expect(page.getByRole("button", { name: "OK" })).toBeDisabled();
await page.getByLabel("Set minimum value").click();
await page.getByLabel("Set minimum value").fill("2.1");
await page.getByLabel("Set maximum value").click();
await page.getByLabel("Set maximum value").fill("10.1");
await page.getByLabel("Make field required").check();
await page.getByLabel("Set field as unique").check();
await page.getByRole("tab", { name: "Default value" }).click();
await expect(page.getByLabel("Set default value")).toBeVisible();
await expect(page.getByLabel("Set default value")).toHaveValue("1.1");
await expect(page.getByRole("button", { name: "OK" })).toBeDisabled();
await page.getByLabel("Set default value").click();
await page.getByLabel("Set default value").fill("11.1");
await expect(page.getByRole("button", { name: "OK" })).toBeDisabled();
await page.getByLabel("Set default value").click();
await page.getByLabel("Set default value").fill("2.2");
await page.getByRole("button", { name: "plus New" }).click();
await page.locator("#defaultValue").nth(1).click();
await page.locator("#defaultValue").nth(1).fill("3.3");
await page.getByRole("button", { name: "OK" }).click();
await closeNotification(page);

await expect(page.getByText("new float1 *#new-float1(unique)")).toBeVisible();
await page.getByText("Content").click();
await expect(page.locator("thead")).toContainText("new float1");
await expect(page.getByRole("cell", { name: "1.1", exact: true })).toBeVisible();
await page.getByRole("button", { name: "plus New Item" }).click();
await expect(page.getByText("new float1(unique)Title")).toBeVisible();
await expect(page.getByRole("spinbutton").nth(0)).toHaveValue("2.2");
await expect(page.getByRole("spinbutton").nth(1)).toHaveValue("3.3");
await page.getByRole("button", { name: "Save" }).click();
await closeNotification(page);
await page.getByLabel("Back").click();
await page.getByRole("button", { name: "x2" }).click();
await expect(page.getByRole("tooltip")).toContainText("new float12.23.3");
});
3 changes: 3 additions & 0 deletions web/src/components/atoms/Icon/Icons/infinity.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions web/src/components/atoms/Icon/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
SearchOutlined,
SettingOutlined,
UsergroupAddOutlined,
UsergroupDeleteOutlined,
UserSwitchOutlined,
CaretDownOutlined,
CaretRightOutlined,
Expand Down Expand Up @@ -68,6 +69,7 @@ import Date from "./Icons/date.svg";
import Dot from "./Icons/dot.svg";
import EditorCopy from "./Icons/editorCopy.svg";
import Group from "./Icons/group.svg";
import InfinityIcon from "./Icons/infinity.svg";
import Key from "./Icons/key.svg";
import LineSegments from "./Icons/lineSegments.svg";
import LineString from "./Icons/lineString.svg";
Expand Down Expand Up @@ -98,6 +100,7 @@ export default {
search: SearchOutlined,
settings: SettingOutlined,
userGroupAdd: UsergroupAddOutlined,
userGroupDelete: UsergroupDeleteOutlined,
userSwitch: UserSwitchOutlined,
caretDown: CaretDownOutlined,
caretRight: CaretRightOutlined,
Expand Down Expand Up @@ -126,6 +129,7 @@ export default {
listBullets: ListBullets,
arrowUpRight: ArrowUpRight,
arrowUpRightSlash: ArrowUpRightSlash,
infinity: InfinityIcon,
numberNine: NumberNine,
link: Link,
linkSolid: LinkSolid,
Expand Down
8 changes: 4 additions & 4 deletions web/src/components/atoms/InputNumber/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ const InputNumber: <T extends string | number>(
props: React.PropsWithChildren<InputNumberProps<T>> & React.RefAttributes<HTMLInputElement>,
) => React.ReactElement = ({ value, ...props }) => {
const status = useMemo(() => {
if (value) {
if (props.max && Number(value) > Number(props.max)) {
if (typeof value === "number") {
if (typeof props.max === "number" && value > props.max) {
return "error";
} else if (props.min && Number(value) < Number(props.min)) {
} else if (typeof props.min === "number" && value < props.min) {
return "error";
}
caichi-t marked this conversation as resolved.
Show resolved Hide resolved
}
}, [props.max, props.min, value]);

return <AntDInputNumber value={value} status={status} {...props} />;
return <AntDInputNumber value={value} status={status} style={{ width: "100%" }} {...props} />;
};

export default InputNumber;
34 changes: 7 additions & 27 deletions web/src/components/molecules/Common/Form/GroupItem/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@ import {
AssetField,
ReferenceField,
} from "@reearth-cms/components/molecules/Content/Form/fields/ComplexFieldComponents";
import { DefaultField } from "@reearth-cms/components/molecules/Content/Form/fields/FieldComponents";
import { FIELD_TYPE_COMPONENT_MAP } from "@reearth-cms/components/molecules/Content/Form/fields/FieldTypesMap";
import { FormItem, ItemAsset } from "@reearth-cms/components/molecules/Content/types";
import { Field, Group } from "@reearth-cms/components/molecules/Schema/types";
import { Field, Group, GroupField } from "@reearth-cms/components/molecules/Schema/types";

type Props = {
value?: string;
Expand Down Expand Up @@ -117,7 +116,7 @@ const GroupItem: React.FC<Props> = ({
}) => {
const { Panel } = Collapse;

const [fields, setFields] = useState<Field[]>();
const [fields, setFields] = useState<GroupField[]>();

useEffect(() => {
const handleFieldsSet = async (id: string) => {
Expand Down Expand Up @@ -176,7 +175,7 @@ const GroupItem: React.FC<Props> = ({
)
}>
<div>
{fields?.map((field: Field) => {
{fields?.map(field => {
if (field.type === "Asset") {
return (
<StyledFormItemWrapper key={field.id}>
Expand Down Expand Up @@ -232,31 +231,12 @@ const GroupItem: React.FC<Props> = ({
/>
</StyledFormItemWrapper>
);
} else if (field.type === "GeometryObject" || field.type === "GeometryEditor") {
const FieldComponent = FIELD_TYPE_COMPONENT_MAP[field.type];

return (
<StyledFormItemWrapper key={field.id} isFullWidth>
<FieldComponent field={field} itemGroupId={itemGroupId} disabled={disabled} />
</StyledFormItemWrapper>
);
} else {
const FieldComponent =
FIELD_TYPE_COMPONENT_MAP[
field.type as
| "Select"
| "Date"
| "Tag"
| "Bool"
| "Checkbox"
| "URL"
| "TextArea"
| "MarkdownText"
| "Integer"
] || DefaultField;

const FieldComponent = FIELD_TYPE_COMPONENT_MAP[field.type];
return (
<StyledFormItemWrapper key={field.id}>
<StyledFormItemWrapper
key={field.id}
isFullWidth={field.type === "GeometryObject" || field.type === "GeometryEditor"}>
<FieldComponent field={field} itemGroupId={itemGroupId} disabled={disabled} />
</StyledFormItemWrapper>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,16 @@ type DefaultFieldProps = {
disabled?: boolean;
};

const IntegerField: React.FC<DefaultFieldProps> = ({ field, itemGroupId, disabled }) => {
const NumberField: React.FC<DefaultFieldProps> = ({ field, itemGroupId, disabled }) => {
const t = useT();
const min = useMemo(() => field.typeProperty?.min, [field.typeProperty?.min]);
const max = useMemo(() => field.typeProperty?.max, [field.typeProperty?.max]);
const min = useMemo(
() => field?.typeProperty?.min ?? field?.typeProperty?.numberMin,
[field?.typeProperty?.min, field?.typeProperty?.numberMin],
);
const max = useMemo(
() => field?.typeProperty?.max ?? field?.typeProperty?.numberMax,
[field?.typeProperty?.max, field?.typeProperty?.numberMax],
);
const validate = useCallback(
(value: unknown) => {
if (typeof value === "number") {
Expand Down Expand Up @@ -66,4 +72,4 @@ const IntegerField: React.FC<DefaultFieldProps> = ({ field, itemGroupId, disable
);
};

export default IntegerField;
export default NumberField;
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,4 @@ export { default as CheckboxField } from "./CheckboxField";
export { default as URLField } from "./URLField";
export { default as DateField } from "./DateField";
export { default as BoolField } from "./BoolField";
export { default as IntegerField } from "./IntegerField";
export { default as MarkdownField } from "./MarkdownField";
export { default as SelectField } from "./SelectField";
export { default as TextAreaField } from "./TextareaField";
export { default as DefaultField } from "./DefaultField";
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
import { TagField, DateField, BoolField, CheckboxField, URLField } from "./FieldComponents";
import {
DefaultField,
TagField,
DateField,
BoolField,
CheckboxField,
URLField,
} from "./FieldComponents";
import GeometryField from "./FieldComponents/GeometryField";
import IntegerField from "./FieldComponents/IntegerField";
import MarkdownField from "./FieldComponents/MarkdownField";
import NumberField from "./FieldComponents/NumberField";
import SelectField from "./FieldComponents/SelectField";
import TextareaField from "./FieldComponents/TextareaField";

export const FIELD_TYPE_COMPONENT_MAP = {
Text: DefaultField,
Tag: TagField,
Date: DateField,
Bool: BoolField,
Checkbox: CheckboxField,
URL: URLField,
TextArea: TextareaField,
MarkdownText: MarkdownField,
Integer: IntegerField,
Integer: NumberField,
Number: NumberField,
Select: SelectField,
GeometryObject: GeometryField,
GeometryEditor: GeometryField,
Expand Down
33 changes: 5 additions & 28 deletions web/src/components/molecules/Content/Form/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ import { useT } from "@reearth-cms/i18n";
import { transformDayjsToString } from "@reearth-cms/utils/format";

import { AssetField, GroupField, ReferenceField } from "./fields/ComplexFieldComponents";
import { DefaultField } from "./fields/FieldComponents";
import { FIELD_TYPE_COMPONENT_MAP } from "./fields/FieldTypesMap";

type Props = {
Expand Down Expand Up @@ -624,31 +623,12 @@ const ContentForm: React.FC<Props> = ({
/>
</StyledFormItemWrapper>
);
} else if (field.type === "GeometryObject" || field.type === "GeometryEditor") {
const FieldComponent = FIELD_TYPE_COMPONENT_MAP[field.type];

return (
<StyledFormItemWrapper key={field.id} isFullWidth>
<FieldComponent field={field} />
</StyledFormItemWrapper>
);
} else {
const FieldComponent =
FIELD_TYPE_COMPONENT_MAP[
field.type as
| "Select"
| "Date"
| "Tag"
| "Bool"
| "Checkbox"
| "URL"
| "TextArea"
| "MarkdownText"
| "Integer"
] || DefaultField;

const FieldComponent = FIELD_TYPE_COMPONENT_MAP[field.type];
return (
<StyledFormItemWrapper key={field.id}>
<StyledFormItemWrapper
key={field.id}
isFullWidth={field.type === "GeometryObject" || field.type === "GeometryEditor"}>
caichi-t marked this conversation as resolved.
Show resolved Hide resolved
<FieldComponent field={field} />
</StyledFormItemWrapper>
);
Expand All @@ -660,10 +640,7 @@ const ContentForm: React.FC<Props> = ({
<Form form={metaForm} layout="vertical" initialValues={initialMetaFormValues}>
<ContentSidebarWrapper item={item} onNavigateToRequest={onNavigateToRequest} />
{model?.metadataSchema?.fields?.map(field => {
const FieldComponent =
FIELD_TYPE_COMPONENT_MAP[
field.type as "Tag" | "Date" | "Bool" | "Checkbox" | "URL"
] || DefaultField;
const FieldComponent = FIELD_TYPE_COMPONENT_MAP[field.type];
return (
<MetaFormItemWrapper key={field.id}>
<FieldComponent field={field} onMetaUpdate={handleMetaUpdate} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ export default (
);
break;
case "Integer":
// case "Float":
case "Number":
result.push(
{ operatorType: "basic", value: BasicOperator.Equals, label: t("is") },
{ operatorType: "basic", value: BasicOperator.NotEquals, label: t("is not") },
Expand Down Expand Up @@ -312,7 +312,7 @@ export default (
if (typeof value !== "boolean") {
value = value === "true";
}
} else if (filter.type === "Integer" /*|| filter.type === "Float"*/) {
} else if (filter.type === "Integer" || filter.type === "Number") {
value = Number(value);
caichi-t marked this conversation as resolved.
Show resolved Hide resolved
} else if (filter.type === "Date") {
value = value ? new Date(value) : new Date();
Expand Down
Loading