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 support for multi-group ordering #1158

Merged
merged 6 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 36 additions & 1 deletion web/e2e/project/schema.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { closeNotification } from "@reearth-cms/e2e/common/notification";
import { expect, test } from "@reearth-cms/e2e/utils";

import { handleFieldForm } from "./utils/field";
import { crudGroup } from "./utils/group";
import { createGroup, crudGroup } from "./utils/group";
import { createModel, crudModel } from "./utils/model";
import { createProject, deleteProject } from "./utils/project";

Expand Down Expand Up @@ -97,6 +97,41 @@ test("Group creating from adding field has succeeded", async ({ page }) => {
await page.getByRole("button", { name: "Cancel" }).click();
});

test("Group reordering has succeeded", async ({ page }) => {
await createGroup(page, "group1", "group1");
await createGroup(page, "group2", "group2");
await expect(
page.getByRole("main").getByRole("menu").last().getByRole("menuitem").nth(0),
).toContainText("group1");
await expect(
page.getByRole("main").getByRole("menu").last().getByRole("menuitem").nth(1),
).toContainText("group2");
await page
.getByRole("main")
.getByRole("menu")
.last()
.getByRole("menuitem")
.nth(1)
.dragTo(page.getByRole("main").getByRole("menu").last().getByRole("menuitem").nth(0));
await closeNotification(page);
await expect(
page.getByRole("main").getByRole("menu").last().getByRole("menuitem").nth(0),
).toContainText("group2");
await expect(
page.getByRole("main").getByRole("menu").last().getByRole("menuitem").nth(1),
).toContainText("group1");
await createGroup(page, "group3", "group3");
await expect(
page.getByRole("main").getByRole("menu").last().getByRole("menuitem").nth(0),
).toContainText("group2");
await expect(
page.getByRole("main").getByRole("menu").last().getByRole("menuitem").nth(1),
).toContainText("group1");
await expect(
page.getByRole("main").getByRole("menu").last().getByRole("menuitem").nth(2),
).toContainText("group3");
});

test("Text field CRUD has succeeded", async ({ page }) => {
await createModel(page);
await page.locator("li").filter({ hasText: "Text" }).locator("div").first().click();
Expand Down
10 changes: 5 additions & 5 deletions web/e2e/project/utils/group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@ import { Page } from "@playwright/test";
import { closeNotification } from "@reearth-cms/e2e/common/notification";
import { expect } from "@reearth-cms/e2e/utils";

export async function createGroup(page: Page) {
export async function createGroup(page: Page, name = "e2e group name", key = "e2e-group-key") {
await page.getByText("Schema").first().click();
await page.getByRole("button", { name: "plus Add" }).last().click();
await page.getByLabel("New Group").locator("#name").click();
await page.getByLabel("New Group").locator("#name").fill("e2e group name");
await page.getByLabel("New Group").locator("#name").fill(name);
await page.getByLabel("New Group").locator("#key").click();
await page.getByLabel("New Group").locator("#key").fill("e2e-group-key");
await page.getByLabel("New Group").locator("#key").fill(key);
await page.getByRole("button", { name: "OK" }).click();
await closeNotification(page);
await expect(page.getByTitle("e2e group name")).toBeVisible();
await expect(page.getByText("#e2e-group-key")).toBeVisible();
await expect(page.getByTitle(name, { exact: true })).toBeVisible();
await expect(page.getByText(`#${key}`)).toBeVisible();
}

const updateGroupName = "new e2e group name";
Expand Down
3 changes: 3 additions & 0 deletions web/src/components/molecules/Model/ModelsList/Groups.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type Props = {
onClose: () => void;
onCreate: (data: { name: string; description: string; key: string }) => Promise<void>;
onGroupSelect?: (groupId: string) => void;
onUpdateGroupsOrder: (groupIds: string[]) => void;
};

const Groups: React.FC<Props> = ({
Expand All @@ -25,6 +26,7 @@ const Groups: React.FC<Props> = ({
onClose,
onCreate,
onGroupSelect,
onUpdateGroupsOrder,
}) => {
return (
<>
Expand All @@ -34,6 +36,7 @@ const Groups: React.FC<Props> = ({
collapsed={collapsed}
onGroupSelect={onGroupSelect}
onModalOpen={onModalOpen}
onUpdateGroupsOrder={onUpdateGroupsOrder}
/>
<FormModal
open={open}
Expand Down
51 changes: 36 additions & 15 deletions web/src/components/molecules/Model/ModelsList/GroupsList.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import styled from "@emotion/styled";
import { useCallback, useMemo } from "react";
import ReactDragListView from "react-drag-listview";

import Button from "@reearth-cms/components/atoms/Button";
import Icon from "@reearth-cms/components/atoms/Icon";
Expand All @@ -13,6 +14,7 @@ type Props = {
collapsed?: boolean;
onModalOpen: () => void;
onGroupSelect?: (groupId: string) => void;
onUpdateGroupsOrder: (groupIds: string[]) => void;
};

const GroupsList: React.FC<Props> = ({
Expand All @@ -21,6 +23,7 @@ const GroupsList: React.FC<Props> = ({
collapsed,
onModalOpen,
onGroupSelect,
onUpdateGroupsOrder,
}) => {
const t = useT();

Expand All @@ -35,14 +38,16 @@ const GroupsList: React.FC<Props> = ({

const items = useMemo(
() =>
groups?.map(group => ({
label: (
<div ref={group.id === selectedKey ? scrollToSelected : undefined}>
{collapsed ? <Icon icon="dot" /> : group.name}
</div>
),
key: group.id,
})),
groups
?.sort((a, b) => a.order - b.order)
.map(group => ({
label: (
<div ref={group.id === selectedKey ? scrollToSelected : undefined}>
{collapsed ? <Icon icon="dot" /> : group.name}
</div>
),
key: group.id,
})),
[collapsed, groups, scrollToSelected, selectedKey],
);

Expand All @@ -53,6 +58,17 @@ const GroupsList: React.FC<Props> = ({
[onGroupSelect],
);

const onDragEnd = useCallback(
(fromIndex: number, toIndex: number) => {
if (toIndex < 0 || !groups) return;
const [removed] = groups.splice(fromIndex, 1);
groups.splice(toIndex, 0, removed);
const groupIds = groups.map(group => group.id);
onUpdateGroupsOrder(groupIds);
},
[groups, onUpdateGroupsOrder],
);

return (
<SchemaStyledMenu>
{collapsed ? (
Expand All @@ -68,13 +84,18 @@ const GroupsList: React.FC<Props> = ({
</Header>
)}
<MenuWrapper>
<StyledMenu
selectedKeys={selectedKeys}
mode={collapsed ? "vertical" : "inline"}
collapsed={collapsed}
items={items}
onClick={handleClick}
/>
<ReactDragListView
nodeSelector=".ant-menu-item"
lineClassName="dragLine"
onDragEnd={(fromIndex, toIndex) => onDragEnd(fromIndex, toIndex)}>
<StyledMenu
selectedKeys={selectedKeys}
mode={collapsed ? "vertical" : "inline"}
collapsed={collapsed}
items={items}
onClick={handleClick}
/>
</ReactDragListView>
</MenuWrapper>
</SchemaStyledMenu>
);
Expand Down
1 change: 1 addition & 0 deletions web/src/components/molecules/Schema/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export type Group = {
description: string;
key: string;
schema: Schema;
order: number;
};

export type ModelFormValues = {
Expand Down
1 change: 1 addition & 0 deletions web/src/components/organisms/DataConverters/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const fromGraphQLGroup = (group: Maybe<GQLGroup>): Group | undefined => {
name: group.name,
description: group.description,
key: group.key,
order: group.order,
schema: {
id: group.schema?.id,
fields: group.schema?.fields.map(
Expand Down
22 changes: 22 additions & 0 deletions web/src/components/organisms/Project/ModelsMenu/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
useCreateModelMutation,
useUpdateModelsOrderMutation,
useCreateGroupMutation,
useUpdateGroupsOrderMutation,
useCheckModelKeyAvailabilityLazyQuery,
useCheckGroupKeyAvailabilityLazyQuery,
Model as GQLModel,
Expand Down Expand Up @@ -186,6 +187,26 @@ export default ({ modelId }: Params) => {
[currentWorkspace?.id, projectId, createNewGroup, navigate, t],
);

const [updateGroupsOrder] = useUpdateGroupsOrderMutation({
refetchQueries: ["GetGroups"],
});

const handleUpdateGroupsOrder = useCallback(
async (groupIds: string[]) => {
const group = await updateGroupsOrder({
variables: {
groupIds,
},
});
if (group.errors) {
Notification.error({ message: t("Failed to update groups order.") });
return;
}
Notification.success({ message: t("Successfully updated groups order!") });
},
[updateGroupsOrder, t],
);

return {
models,
groups,
Expand All @@ -200,5 +221,6 @@ export default ({ modelId }: Params) => {
handleGroupCreate,
handleGroupKeyCheck,
handleUpdateModelsOrder,
handleUpdateGroupsOrder,
};
};
2 changes: 2 additions & 0 deletions web/src/components/organisms/Project/ModelsMenu/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const ModelsMenu: React.FC<Props> = ({
handleModelKeyCheck,
handleGroupKeyCheck,
handleUpdateModelsOrder,
handleUpdateGroupsOrder,
} = useHooks({
modelId: selectedSchemaType === "model" ? schemaId : undefined,
});
Expand Down Expand Up @@ -74,6 +75,7 @@ const ModelsMenu: React.FC<Props> = ({
onGroupKeyCheck={handleGroupKeyCheck}
onClose={handleGroupModalClose}
onCreate={handleGroupCreate}
onUpdateGroupsOrder={handleUpdateGroupsOrder}
/>
)}
</ModelListBody>
Expand Down
Loading
Loading