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

Enable server-side pagination hardware_types #797

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
72 changes: 66 additions & 6 deletions frontend/src/api/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -1186,6 +1186,27 @@ enum HardwareTypePartNumberSortField {
PART_NUMBER
}

":hardware_type_part_number connection"
type HardwareTypePartNumberConnection {
"Total count on all pages"
count: Int

"Page information"
pageInfo: PageInfo!

":hardware_type_part_number edges"
edges: [HardwareTypePartNumberEdge!]
}

":hardware_type_part_number edge"
type HardwareTypePartNumberEdge {
"Cursor"
cursor: String!

":hardware_type_part_number node"
node: HardwareTypePartNumber!
}

input HardwareTypePartNumberFilterPartNumber {
isNil: Boolean
eq: String
Expand Down Expand Up @@ -1301,6 +1322,27 @@ enum HardwareTypeSortField {
NAME
}

":hardware_type connection"
type HardwareTypeConnection {
"Total count on all pages"
count: Int

"Page information"
pageInfo: PageInfo!

":hardware_type edges"
edges: [HardwareTypeEdge!]
}

":hardware_type edge"
type HardwareTypeEdge {
"Cursor"
cursor: String!

":hardware_type node"
node: HardwareType!
}

input HardwareTypeFilterName {
isNil: Boolean
eq: String
Expand Down Expand Up @@ -1395,12 +1437,18 @@ type HardwareType implements Node {
"A filter to limit the results"
filter: HardwareTypePartNumberFilterInput

"The number of records to return."
limit: Int
"The number of records to return from the beginning. Maximum 250"
first: Int

"The number of records to skip."
offset: Int
): [HardwareTypePartNumber!]!
"Show records before the specified keyset."
before: String

"Show records after the specified keyset."
after: String

"The number of records to return to the end. Maximum 250"
last: Int
): HardwareTypePartNumberConnection!
}

"The result of the :set_device_led_behavior mutation"
Expand Down Expand Up @@ -2879,7 +2927,19 @@ type RootQueryType {

"A filter to limit the results"
filter: HardwareTypeFilterInput
): [HardwareType!]!

"The number of records to return from the beginning. Maximum 250"
first: Int

"Show records before the specified keyset."
before: String

"Show records after the specified keyset."
after: String

"The number of records to return to the end. Maximum 250"
last: Int
): HardwareTypeConnection

"Returns a single system model."
systemModel("The id of the record" id: ID!): SystemModel
Expand Down
59 changes: 39 additions & 20 deletions frontend/src/components/HardwareTypesTable.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
This file is part of Edgehog.

Copyright 2024 SECO Mind Srl
Copyright 2024-2025 SECO Mind Srl

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -29,15 +29,14 @@ import type { HardwareTypesTable_getHardwareTypes_Query } from "api/__generated_
import HardwareTypesTable from "./HardwareTypesTable";

const GET_HARDWARE_TYPES_QUERY = graphql`
query HardwareTypesTable_getHardwareTypes_Query @relay_test_operation {
hardwareTypes {
...HardwareTypesTable_HardwareTypesFragment
}
query HardwareTypesTable_getHardwareTypes_Query($first: Int, $after: String)
@relay_test_operation {
...HardwareTypesTable_HardwareTypesFragment
}
`;

const ComponentWithQuery = () => {
const { hardwareTypes } =
const hardwareTypes =
useLazyLoadQuery<HardwareTypesTable_getHardwareTypes_Query>(
GET_HARDWARE_TYPES_QUERY,
{},
Expand All @@ -50,16 +49,30 @@ type HardwareType = {
handle: string;
name: string;
partNumbers: {
id: string;
partNumber: string;
}[];
edges: {
node: {
id: string;
partNumber: string;
};
}[];
};
};

const renderComponent = (hardwareTypes: HardwareType[] = []) => {
const relayEnvironment = createMockEnvironment();
relayEnvironment.mock.queueOperationResolver((_operation) => ({
data: { hardwareTypes },

relayEnvironment.mock.queueOperationResolver(() => ({
data: {
hardwareTypes: {
edges: hardwareTypes.map((model) => ({
node: model,
})),
},
},
}));

relayEnvironment.mock.queuePendingOperation(GET_HARDWARE_TYPES_QUERY, {});

renderWithProviders(<ComponentWithQuery />, { relayEnvironment });
};

Expand All @@ -81,7 +94,9 @@ it("renders Hardware Type data", () => {
id: "HW-ID",
handle: "hw-handle",
name: "HW name",
partNumbers: [{ id: "HW-PN1", partNumber: "HW-PN1" }],
partNumbers: {
edges: [{ node: { id: "HW-PN1", partNumber: "HW-PN1" } }],
},
},
]);

Expand All @@ -100,10 +115,12 @@ it("renders multiple Part Numbers separated by comma", () => {
id: "HW-ID",
handle: "hw-handle",
name: "HW name",
partNumbers: [
{ id: "HW-PN1", partNumber: "HW-PN1" },
{ id: "HW-PN2", partNumber: "HW-PN2" },
],
partNumbers: {
edges: [
{ node: { id: "HW-PN1", partNumber: "HW-PN1" } },
{ node: { id: "HW-PN2", partNumber: "HW-PN2" } },
],
},
},
]);

Expand All @@ -116,10 +133,12 @@ it("renders Hardware Type data in correct columns", () => {
id: "HW-ID",
handle: "hw-handle",
name: "HW name",
partNumbers: [
{ id: "HW-PN1", partNumber: "HW-PN1" },
{ id: "HW-PN2", partNumber: "HW-PN2" },
],
partNumbers: {
edges: [
{ node: { id: "HW-PN1", partNumber: "HW-PN1" } },
{ node: { id: "HW-PN2", partNumber: "HW-PN2" } },
],
},
},
]);

Expand Down
55 changes: 37 additions & 18 deletions frontend/src/components/HardwareTypesTable.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
This file is part of Edgehog.

Copyright 2021-2024 SECO Mind Srl
Copyright 2021-2025 SECO Mind Srl

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -20,30 +20,47 @@

import React from "react";
import { FormattedMessage } from "react-intl";
import { graphql, useFragment } from "react-relay/hooks";
import { graphql, usePaginationFragment } from "react-relay/hooks";

import Table, { createColumnHelper } from "components/Table";
import { Link, Route } from "Navigation";
import type { HardwareTypesTable_PaginationQuery } from "api/__generated__/HardwareTypesTable_PaginationQuery.graphql";
import type {
HardwareTypesTable_HardwareTypesFragment$key,
HardwareTypesTable_HardwareTypesFragment$data,
} from "api/__generated__/HardwareTypesTable_HardwareTypesFragment.graphql";

import Table, { createColumnHelper } from "components/Table";
import { Link, Route } from "Navigation";

// We use graphql fields below in columns configuration
/* eslint-disable relay/unused-fields */
const HARDWARE_TYPES_TABLE_FRAGMENT = graphql`
fragment HardwareTypesTable_HardwareTypesFragment on HardwareType
@relay(plural: true) {
id
handle
name
partNumbers {
partNumber
fragment HardwareTypesTable_HardwareTypesFragment on RootQueryType
@refetchable(queryName: "HardwareTypesTable_PaginationQuery") {
hardwareTypes(first: $first, after: $after)
@connection(key: "HardwareTypesTable_hardwareTypes") {
edges {
node {
id
handle
name
partNumbers {
edges {
node {
partNumber
}
}
}
}
}
}
}
`;

type TableRecord = HardwareTypesTable_HardwareTypesFragment$data[number];
type TableRecord = NonNullable<
NonNullable<
HardwareTypesTable_HardwareTypesFragment$data["hardwareTypes"]
>["edges"]
>[number]["node"];

const columnHelper = createColumnHelper<TableRecord>();
const columns = [
Expand Down Expand Up @@ -81,7 +98,7 @@ const columns = [
/>
),
cell: ({ getValue }) =>
getValue().map(({ partNumber }, index) => (
getValue().edges?.map(({ node: { partNumber } }, index) => (
<React.Fragment key={partNumber}>
{index > 0 && ", "}
<span className="text-nowrap">{partNumber}</span>
Expand All @@ -96,12 +113,14 @@ type Props = {
};

const HardwareTypesTable = ({ className, hardwareTypesRef }: Props) => {
const hardwareTypes = useFragment(
HARDWARE_TYPES_TABLE_FRAGMENT,
hardwareTypesRef,
);
const { data } = usePaginationFragment<
HardwareTypesTable_PaginationQuery,
HardwareTypesTable_HardwareTypesFragment$key
>(HARDWARE_TYPES_TABLE_FRAGMENT, hardwareTypesRef);

const tableData = data.hardwareTypes?.edges?.map((edge) => edge.node) ?? [];

return <Table className={className} columns={columns} data={hardwareTypes} />;
return <Table className={className} columns={columns} data={tableData} />;
};

export default HardwareTypesTable;
16 changes: 10 additions & 6 deletions frontend/src/forms/CreateSystemModel.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
This file is part of Edgehog.

Copyright 2021-2024 SECO Mind Srl
Copyright 2021-2025 SECO Mind Srl

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -24,6 +24,8 @@ import { FormattedMessage, useIntl } from "react-intl";
import { graphql, useFragment } from "react-relay/hooks";
import { yupResolver } from "@hookform/resolvers/yup";

import type { CreateSystemModel_OptionsFragment$key } from "api/__generated__/CreateSystemModel_OptionsFragment.graphql";

import Button from "components/Button";
import CloseButton from "components/CloseButton";
import Col from "components/Col";
Expand All @@ -35,13 +37,15 @@ import Spinner from "components/Spinner";
import Stack from "components/Stack";
import { systemModelHandleSchema, messages, yup } from "forms";

import type { CreateSystemModel_OptionsFragment$key } from "api/__generated__/CreateSystemModel_OptionsFragment.graphql";

const CREATE_SYSTEM_MODEL_FRAGMENT = graphql`
fragment CreateSystemModel_OptionsFragment on RootQueryType {
hardwareTypes {
id
name
edges {
node {
id
name
}
}
}
tenantInfo {
defaultLocale
Expand Down Expand Up @@ -303,7 +307,7 @@ const CreateSystemModelForm = ({
defaultMessage: "Select a Hardware Type",
})}
</option>
{hardwareTypes.map((hardwareType) => (
{hardwareTypes?.edges?.map(({ node: hardwareType }) => (
<option key={hardwareType.id} value={hardwareType.id}>
{hardwareType.name}
</option>
Expand Down
Loading
Loading