Skip to content
This repository has been archived by the owner on Jul 23, 2024. It is now read-only.

Commit

Permalink
added load more button
Browse files Browse the repository at this point in the history
  • Loading branch information
kaimsfd committed Oct 11, 2023
1 parent 858b31c commit 2648bbe
Show file tree
Hide file tree
Showing 4 changed files with 319 additions and 15 deletions.
84 changes: 69 additions & 15 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,81 @@
/* eslint-disable react/jsx-no-undef */
import { useQuery, gql, DocumentNode } from "@apollo/client";
import React from "react";
import { Pagination, Table, theme } from "@diamondlightsource/ui-components"
import { Table, theme } from "@diamondlightsource/ui-components"
import { ChakraProvider } from "@chakra-ui/react";
import { CursorPaginator } from "./components/CursorPaginator";
import { LoadMorePaginator } from "./components/LoadMorePaginator";

const GET_INFO: DocumentNode = gql`
query pinInfo {
libraryPins {
barcode,
loopSize,
status
query pinInfo ($after: String) {
libraryPins(first: 2, after: $after) {
pageInfo {
hasPreviousPage,
hasNextPage,
startCursor,
endCursor
},
edges {
cursor
node {
barcode,
loopSize,
status
}
}
}
}
`;

function DisplayPinInfo(): React.JSX.Element {
const { loading, error, data } = useQuery(GET_INFO);
const { loading, error, data, fetchMore } = useQuery(GET_INFO);

if (loading) return <p>Loading...</p>;
if (error) return <p>Error : {error.message} {error.extraInfo}</p>;


const nodes = data.libraryPins.edges.map((edge) => edge.node);

// function to load more content and update query result
const loadMore = () => {
// fetchMore function from `useQuery` to fetch more content with `updateQuery`
fetchMore({

// update `after` variable with `endCursor` from previous result
variables: {
after: data.libraryPins.pageInfo.endCursor,
},

// pass previous query result and the new results to `updateQuery`
updateQuery: (previousQueryResult, { fetchMoreResult }) => {
// define edges and pageInfo from new results
const newEdges = fetchMoreResult.libraryPins.edges;
const pageInfo = fetchMoreResult.libraryPins.pageInfo;
console.log(pageInfo)

// if newEdges actually have items,
return newEdges.length
? // return a reconstruction of the query result with updated values
{
// spread the value of the previous result
...previousQueryResult,

libraryPins: {
// spread the value of the previous `allStarhips` data into this object
...previousQueryResult.libraryPins,

// concatenate edges
edges: [...previousQueryResult.libraryPins.edges, ...newEdges],

// override with new pageInfo
pageInfo,
},
}
: // else, return the previous result
previousQueryResult;
},
});
};

return (
<><Table
headers={[
Expand All @@ -35,13 +92,10 @@ function DisplayPinInfo(): React.JSX.Element {
label: 'Status'
}
]}
data={data.libraryPins} />
<Pagination
total={6} onPageChange={(page) => {
console.log(`On page: ${page}`)
}}
/>
</>
data={data.libraryPins.edges.map((edge) => edge.node)} />
<button onClick={loadMore}>
{"Load More"}
</button></>
);
}

Expand Down
123 changes: 123 additions & 0 deletions frontend/src/components/CursorPaginator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import {
Box,
HStack,
Button,
Divider,
Stack,
BoxProps,
} from "@chakra-ui/react";
import React from "react";
import { useEffect, useState } from "react";

type PageChangeCallback = (page: number) => void;
type ItemChangeCallback = (items: number) => void;

export interface PaginationProps extends BoxProps {
/** Total number of items to paginate */
total: number;
/** Array with all available "items per page" amounts */
possibleItemsPerPage?: Array<number>;
/** External bind for current page */
page?: number;
/** Number of items to display per page */
limit?: number;
/** Callback for page change events */
onPageChange?: PageChangeCallback;
/** Callback for item count change event */
onItemCountChange?: ItemChangeCallback;
}

const CursorPaginator = ({
total,
possibleItemsPerPage = [5, 10, 15, 20, 30, 50, 100],
limit = 5,
page,
onPageChange,
onItemCountChange,
...props
}: PaginationProps) => {
const [internalPage, setInternalPage] = useState(page || 1);
// Use limit set in instance, unless it does not exist in the list of possible items per page.
// Default to middle.
const [itemsPerPage] = useState(
possibleItemsPerPage.includes(limit)
? limit
: possibleItemsPerPage[Math.floor(possibleItemsPerPage.length / 2)],
);
const [pageAmount, setPageAmount] = useState(1);

useEffect(() => {
if (page) {
setInternalPage(page);
}
}, [page]);

useEffect(() => {
if (onPageChange !== undefined) {
onPageChange(internalPage);
}
}, [internalPage, onPageChange]);

useEffect(() => {
if (onItemCountChange !== undefined) {
onItemCountChange(itemsPerPage);
}
}, [itemsPerPage, onItemCountChange]);


useEffect(() => {
const newPageAmount = Math.ceil(total / itemsPerPage);
setInternalPage((prevPage) => (prevPage > newPageAmount ? 1 : prevPage));
setPageAmount(newPageAmount);
}, [total, itemsPerPage, setInternalPage]);

return (
<Box py={2} {...props}>
<Stack w='100%' direction={{ base: "column", md: "row" }}>
<HStack>
<Button
aria-label='First Page'
size='sm'
variant='pgNotSelected'
onClick={() => setInternalPage(1)}
isDisabled={internalPage <= 1}
>
&lt;&lt;
</Button>
<Button
aria-label='Previous Page'
size='sm'
variant='pgNotSelected'
isDisabled={internalPage <= 1}
onClick={() => setInternalPage(internalPage - 1)}
>
&lt;
</Button>
<Button
aria-label='Next Page'
size='sm'
variant='pgNotSelected'
isDisabled={internalPage >= pageAmount}
onClick={() => setInternalPage(internalPage + 1)}
>
&gt;
</Button>
<Button
aria-label='Last Page'
size='sm'
variant='pgNotSelected'
isDisabled={internalPage >= pageAmount}
onClick={() => setInternalPage(pageAmount)}
>
&gt;&gt;
</Button>
</HStack>
<Divider display={{ base: "none", md: "initial" }} orientation='vertical' h='30px' />
<HStack flexGrow='1'>
</HStack>
</Stack>
</Box>
);
};

export { CursorPaginator };
85 changes: 85 additions & 0 deletions frontend/src/components/LoadMorePaginator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import {
Box,
HStack,
Button,
Divider,
Stack,
BoxProps,
} from "@chakra-ui/react";
import React from "react";
import { useEffect, useState } from "react";

type PageChangeCallback = (page: number) => void;
type ItemChangeCallback = (items: number) => void;

export interface PaginationProps extends BoxProps {
/** Total number of items to paginate */
total: number;
/** Array with all available "items per page" amounts */
possibleItemsPerPage?: Array<number>;
/** External bind for current page */
page?: number;
/** Number of items to display per page */
limit?: number;
/** Callback for page change events */
onPageChange?: PageChangeCallback;
/** Callback for item count change event */
onItemCountChange?: ItemChangeCallback;
}

const LoadMorePaginator = ({
total,
possibleItemsPerPage = [5, 10, 15, 20, 30, 50, 100],
limit = 5,
page,
onPageChange,
onItemCountChange,
...props
}: PaginationProps) => {
const [internalPage, setInternalPage] = useState(page || 1);
// Use limit set in instance, unless it does not exist in the list of possible items per page.
// Default to middle.
const [itemsPerPage] = useState(
possibleItemsPerPage.includes(limit)
? limit
: possibleItemsPerPage[Math.floor(possibleItemsPerPage.length / 2)],
);
const [pageAmount, setPageAmount] = useState(1);
const [totalPages, setTotalPages] = useState(1);
const [page2, setPage] = useState(1);
const [loading, setLoading] = useState(false);

useEffect(() => {
if (page) {
setInternalPage(page);
}
}, [page]);

useEffect(() => {
if (onPageChange !== undefined) {
onPageChange(internalPage);
}
}, [internalPage, onPageChange]);

useEffect(() => {
if (onItemCountChange !== undefined) {
onItemCountChange(itemsPerPage);
}
}, [itemsPerPage, onItemCountChange]);


useEffect(() => {
const newPageAmount = Math.ceil(total / itemsPerPage);
setInternalPage((prevPage) => (prevPage > newPageAmount ? 1 : prevPage));
setPageAmount(newPageAmount);
}, [total, itemsPerPage, setInternalPage]);

return (
<div className="LoadMorePaginator">
<div className="clearfix"></div>
{totalPages !== page && <button className="btn-load-more" onClick={() => setPage(page + 1)}>{loading ? 'Loading...' : 'Load More'}</button>}
</div>
);
};

export { LoadMorePaginator };
42 changes: 42 additions & 0 deletions frontend/src/components/lmp.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/* eslint-disable react-hooks/rules-of-hooks */
import React from "react";
import { useState } from "react";

const App = () => {
const [Items, setItems] = useState(
Array.from(Array(20).keys(), (n) => n + 1)
);
const [isFetching, setIsFetching] = useState(false);

function loadMoreItems() {
setIsFetching(true);

//mocking an API call
setTimeout(() => {
setItems((prevState) => [
...prevState,
...Array.from(Array(20).keys(), (n) => n + prevState.length + 1),
]);
setIsFetching(false);
}, 2000);
}
return (
<div>
{Items.map((item, index) => {
if (Items.length === index + 1) {
return (
<div key={index}>
Item {item} last
</div>
);
} else {
return <div key={index}>Item {item}</div>;
}
})}
{isFetching && <p>Fetching items...</p>}
{!isFetching && <button onClick={loadMoreItems}>Load more</button>}
</div>
);
}

export default App;

0 comments on commit 2648bbe

Please sign in to comment.