Skip to content

Commit

Permalink
✨ feat(sorting): move sorting to the list manager
Browse files Browse the repository at this point in the history
  • Loading branch information
thrownullexception committed Jan 4, 2024
1 parent 3094333 commit 0b63397
Show file tree
Hide file tree
Showing 45 changed files with 657 additions and 1,127 deletions.
6 changes: 6 additions & 0 deletions src/__tests__/_/api-handlers/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,12 @@ const DEFAULT_ENTITY_CONFIG_VALUES: Record<
hidden_entity_create_columns: () => ["hidden-field-1"],
hidden_entity_update_columns: () => ["hidden-field-1"],
hidden_entity_details_columns: () => ["hidden-field-1"],

entity_fields_orders_table: () => [],
entity_fields_orders_create: () => [],
entity_fields_orders_details: () => [],
entity_fields_orders_update: () => [],

hidden_entity_relations: () => ["hidden-related-entity-5"],
entity_columns_labels: () => ({}),
entity_presentation_script: () => "",
Expand Down
60 changes: 0 additions & 60 deletions src/__tests__/api/entities/[entity]/fields.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import handler from "pages/api/entities/[entity]/fields";
import {
setupAppConfigTestData,
setupAllTestData,
createAuthenticatedMocks,
} from "__tests__/api/_test-utils";
Expand Down Expand Up @@ -64,63 +63,4 @@ describe("/api/entities/[entity]/fields", () => {
]
`);
});

it("should order entity fields when provided", async () => {
await setupAppConfigTestData({
"entity_fields_orders__base-model": ["message", "title", "typeId"],
});

const { req, res } = createAuthenticatedMocks({
method: "GET",
query: {
entity: "base-model",
},
});

await handler(req, res);

expect(res._getJSONData()).toMatchInlineSnapshot(`
[
{
"length": 1000,
"name": "message",
"type": "string",
},
{
"isRequired": true,
"length": 200,
"name": "title",
"type": "string",
},
{
"isReference": true,
"name": "typeId",
"type": "string",
},
{
"isId": true,
"name": "id",
"type": "string",
},
{
"name": "createdAt",
"type": "date",
},
{
"enumeration": [
"un-addressed",
"addressed",
"closed",
],
"name": "state",
"type": "enum",
},
{
"isReference": true,
"name": "createdById",
"type": "string",
},
]
`);
});
});
6 changes: 1 addition & 5 deletions src/backend/entities/entities.controller.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ILabelValue } from "shared/types/options";
import { IEntityField, IEntityRelation } from "shared/types/db";
import { IEntityRelation } from "shared/types/db";
import {
ConfigurationApiService,
configurationApiService,
Expand Down Expand Up @@ -42,10 +42,6 @@ export class EntitiesApiController {
userRole
);
}

async getEntityFields(entity: string): Promise<IEntityField[]> {
return await this._entitiesApiService.getOrderedEntityFields(entity);
}
}

export const entitiesApiController = new EntitiesApiController(
Expand Down
28 changes: 8 additions & 20 deletions src/backend/entities/entities.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { IApplicationService } from "backend/types";
import { noop } from "shared/lib/noop";
import { IDBSchema, IEntityField, IEntityRelation } from "shared/types/db";
import { DataCrudKeys } from "shared/types/data";
import { CRUD_KEY_CONFIG } from "shared/configurations/permissions";
import { CRUD_HIDDEN_KEY_CONFIG } from "shared/configurations/permissions";
import { sortListByOrder } from "shared/lib/array/sort";
import { SchemasApiService, schemasApiService } from "../schema/schema.service";
import { PortalFieldsFilterService } from "./portal";
Expand Down Expand Up @@ -45,21 +45,6 @@ export class EntitiesApiService implements IApplicationService {
return (await this.getEntityFromSchema(entity)).fields;
}

async getOrderedEntityFields(entity: string) {
const [entityFields, entityFieldsOrder] = await Promise.all([
this.getEntityFields(entity),
this._configurationApiService.show("entity_fields_orders", entity),
]);

sortListByOrder(
entityFieldsOrder,
entityFields as unknown as Record<string, unknown>[],
"name" as keyof IEntityField
);

return entityFields;
}

async getEntityFirstFieldType(
entity: string,
fieldType: IEntityField["type"]
Expand Down Expand Up @@ -97,7 +82,10 @@ export class EntitiesApiService implements IApplicationService {
crudKey: DataCrudKeys
): Promise<string[]> {
const [configHiddenFields, entityFields] = await Promise.all([
this._configurationApiService.show(CRUD_KEY_CONFIG[crudKey], entity),
this._configurationApiService.show(
CRUD_HIDDEN_KEY_CONFIG[crudKey],
entity
),
this.getEntityFields(entity),
]);

Expand Down Expand Up @@ -166,16 +154,16 @@ export class EntitiesApiService implements IApplicationService {
this._configurationApiService.show("entity_relations_order", entity),
]);

const allowedEntityRelation =
const allowedEntityRelation$1 =
await this._rolesApiService.filterPermittedEntities(
userRole,
validRelations,
"table"
);

sortListByOrder(
const allowedEntityRelation = sortListByOrder(
entityOrders,
allowedEntityRelation as unknown[] as Record<string, unknown>[],
allowedEntityRelation$1,
"table"
);

Expand Down
4 changes: 1 addition & 3 deletions src/backend/menu/menu.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,7 @@ export class NavigationMenuApiService
.filter(({ value }) => !hiddenMenuEntities.includes(value))
.sort((a, b) => a.value.localeCompare(b.value));

sortListByOrder(entitiesOrder, menuEntities, "value");

return menuEntities;
return sortListByOrder(entitiesOrder, menuEntities, "value");
}

async filterOutUserMenuItems(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ describe("ListManager", () => {
render(
<ListManager
{...{ ...defaultProps }}
render={(item) => <div>{item.name}</div>}
render={(item) => ({ label: item.name })}
/>
);

Expand All @@ -34,7 +34,7 @@ describe("ListManager", () => {
<ListManager
{...{ ...defaultProps }}
getLabel={(name) => `${name} + Label`}
render={(item) => <div>{item.label}</div>}
render={(item) => ({ label: item.label })}
/>
);

Expand All @@ -46,7 +46,7 @@ describe("ListManager", () => {
render(
<ListManager
{...{ ...defaultProps }}
render={(item) => <div>{item.name}</div>}
render={(item) => ({ label: item.name })}
/>
);

Expand All @@ -60,7 +60,7 @@ describe("ListManager", () => {
items={loadedDataState(
Array.from({ length: 11 }, (_, i) => ({ name: `foo${i}` }))
)}
render={(item) => <div>{item.name}</div>}
render={(item) => ({ label: item.name })}
/>
);

Expand All @@ -75,7 +75,7 @@ describe("ListManager", () => {
Array.from({ length: 11 }, (_, i) => ({ name: `foo${i}` }))
)}
getLabel={(name) => `1-${name}`}
render={(item) => <div>{item.label}</div>}
render={(item) => ({ label: item.label })}
/>
);

Expand Down Expand Up @@ -108,11 +108,83 @@ describe("ListManager", () => {
render(
<ListManager
items={loadedDataState([])}
render={() => <div>foo</div>}
render={(item) => ({ label: item.label })}
{...{ ...defaultProps }}
/>
);

expect(screen.getByText("No Item Has Been Added Yet")).toBeInTheDocument();
});
});

/*
import React, { ReactNode } from "react";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import { SortList } from ".";
jest.mock("react-easy-sort", () => ({
__esModule: true,
default: ({
onSortEnd,
children,
}: {
onSortEnd: (newValue: number, oldValue: number) => void;
children: ReactNode;
}) => (
// eslint-disable-next-line jsx-a11y/click-events-have-key-events
<div
onClick={() => {
onSortEnd(1, 2);
onSortEnd(-1, 0);
onSortEnd(2, -1);
}}
data-testid="fake-sorting"
>
{children}
</div>
),
SortableItem: ({ children }: { children: ReactNode }) => (
<div>{children}</div>
),
}));
describe("SortList", () => {
it("should sort items", async () => {
const onSave = jest.fn();
render(
<SortList
data={{
data: [
{
value: "foo-value",
},
{
value: "bar-value",
},
{
value: "baz-value",
},
],
error: null,
isLoading: false,
}}
onSave={onSave}
/>
);
fireEvent.click(screen.getByTestId("fake-sorting"));
fireEvent.click(screen.getByRole("button", { name: "Save Order" }));
await waitFor(() => {
expect(onSave).toHaveBeenCalledWith([
"bar-value",
"foo-value",
"baz-value",
]);
});
});
});
*/
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Story } from "@storybook/react";
import { action } from "@storybook/addon-actions";
import { Plus } from "react-feather";
import { ApplicationRoot } from "frontend/components/ApplicationRoot";
import { ListManagerItem, IProps } from ".";
import { ListManagerItem, IListMangerItemProps } from ".";

export default {
title: "Components/ListManagerItem",
Expand All @@ -15,7 +15,7 @@ export default {
},
};

const Template: Story<IProps> = (args) => (
const Template: Story<IListMangerItemProps> = (args) => (
<ApplicationRoot>
<ListManagerItem {...args} />
<ListManagerItem {...args} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import Link from "next/link";
import styled, { css } from "styled-components";
import { USE_ROOT_COLOR } from "frontend/design-system/theme/root";
import { Stack } from "frontend/design-system/primitives/Stack";
import { GrabIcon } from "shared/constants/Icons";
import { SortableKnob } from "react-easy-sort";
import { FormButton } from "../../Button/FormButton";
import { FormSwitch } from "../../Form/FormSwitch";
import { ButtonIconTypes } from "../../Button/constants";
Expand Down Expand Up @@ -127,14 +129,15 @@ const Label = styled.label<{ $active?: boolean; $subtle?: boolean }>`
: USE_ROOT_COLOR("main-text")};
`;

export interface IProps {
export interface IListMangerItemProps {
label: string;
action?: string | (() => void);
secondaryAction?: () => void;
size?: "xs";
subLabel?: string;
IconComponent?: IconType;
disabled?: boolean;
sortable?: boolean;
subtle?: boolean;
active?: boolean;
toggle?: {
Expand All @@ -159,15 +162,21 @@ export function ListManagerItem({
active,
toggle,
action,
sortable,
secondaryAction,
subtle,
size,
actionButtons = [],
}: IProps) {
}: IListMangerItemProps) {
const id = useId();
const content = (
<Stack>
<Stack align="center">
<div style={{ display: sortable && !subtle ? "block" : "none" }}>
<SortableKnob>
<GrabIcon />
</SortableKnob>
</div>
{IconComponent ? (
<Icon
as={IconComponent}
Expand Down
Loading

0 comments on commit 0b63397

Please sign in to comment.