Skip to content

Commit 28a9d64

Browse files
author
Michael Buchar
committed
feat(ui): add persistent page size selector (reanahub#442)
- Replaced native select with Semantic UI Dropdown - Updated default page size from 5 to 10 - Pagination and results per page dropdown aligned on same row - Added new SCSS styles - Removed unnecessary overflow in WorkflowSharingFilter style
1 parent 10f2bcd commit 28a9d64

File tree

5 files changed

+105
-29
lines changed

5 files changed

+105
-29
lines changed

reana-ui/src/components/App.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ export default function App() {
105105
}
106106
/>
107107
<Route
108-
path="/details/:id"
108+
path="/details/:id/:tab?/:job?"
109109
element={
110110
<RequireAuth>
111111
<RedirectDetailsToWorkflows />

reana-ui/src/components/Search.js

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010

1111
import PropTypes from "prop-types";
1212
import { Input, Icon } from "semantic-ui-react";
13-
import isEqual from "lodash/isEqual";
14-
1513
import styles from "./Search.module.scss";
1614

1715
export default function Search({
@@ -64,13 +62,3 @@ Search.propTypes = {
6462
loading: PropTypes.bool,
6563
search: PropTypes.func,
6664
};
67-
68-
export const applyFilter = (setFilter, resetPage) => (nextValue) => {
69-
setFilter((prevValue) => {
70-
if (isEqual(prevValue, nextValue)) {
71-
return prevValue;
72-
}
73-
resetPage();
74-
return nextValue;
75-
});
76-
};

reana-ui/src/pages/workflowList/WorkflowList.js

Lines changed: 62 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import moment from "moment";
1212
import { useEffect, useMemo, useRef, useState } from "react";
1313
import { useSearchParams } from "react-router-dom";
1414
import { useDispatch, useSelector } from "react-redux";
15-
import { Container, Dimmer, Icon, Loader } from "semantic-ui-react";
15+
import { Container, Dimmer, Dropdown, Icon, Loader } from "semantic-ui-react";
1616
import isEqual from "lodash/isEqual";
1717

1818
import { fetchWorkflows } from "~/actions";
@@ -34,7 +34,12 @@ import WorkflowFilters from "./components/WorkflowFilters";
3434
import WorkflowList from "./components/WorkflowList";
3535
import styles from "./WorkflowList.module.scss";
3636

37-
const PAGE_SIZE = 5;
37+
const pageSizeOptions = [10, 20, 50, 100].map((size) => ({
38+
key: size,
39+
text: String(size),
40+
value: size,
41+
}));
42+
const DEFAULT_PAGE_SIZE = pageSizeOptions[0].value;
3843

3944
export default function WorkflowListPage() {
4045
return (
@@ -61,10 +66,17 @@ function Workflows() {
6166
const { pollingSecs } = config;
6267

6368
const page = useMemo(() => {
64-
const n = parseInt(searchParams.get("page") || "", 10);
69+
const n = parseInt(searchParams.get("page") || "");
6570
return Number.isFinite(n) && n > 0 ? n : 1;
6671
}, [searchParams]);
67-
const pagination = useMemo(() => ({ page, size: PAGE_SIZE }), [page]);
72+
const [pageSize, setPageSize] = useState(() => {
73+
const n = parseInt(searchParams.get("page-size") || "");
74+
return Number.isFinite(n) && n > 0 ? n : DEFAULT_PAGE_SIZE;
75+
});
76+
const pagination = useMemo(
77+
() => ({ page, size: pageSize }),
78+
[page, pageSize],
79+
);
6880
const initialSearch = searchParams.get("search") || "";
6981
const [searchText, setSearchText] = useState(initialSearch);
7082
const [committedSearch, setCommittedSearch] = useState(initialSearch);
@@ -215,7 +227,7 @@ function Workflows() {
215227
// if ?page= param is not in a valid format, or page is 1, remove page from URL
216228
useEffect(() => {
217229
const raw = searchParams.get("page");
218-
const n = parseInt(raw || "", 10);
230+
const n = parseInt(raw || "");
219231
const shouldRemovePage =
220232
searchParams.has("page") &&
221233
(!raw || // page=empty string
@@ -393,15 +405,52 @@ function Workflows() {
393405
setSharedWithModeInUrl={setSharedWithModeInUrl}
394406
/>
395407
<WorkflowList workflows={workflowArray} loading={loading} />
408+
{!loading && (
409+
<div className={styles.paginationRow}>
410+
{/* To emulate size of page-size dropdown and ensure page buttons stay in middle of screen */}
411+
<div className={styles.pageSizeNotVisible}>
412+
<span className={styles.pageSizeLabel}>Results per page:</span>
413+
<Dropdown
414+
selection
415+
compact
416+
options={pageSizeOptions}
417+
value={pageSize}
418+
/>
419+
</div>
420+
{workflowsCount > pageSize && (
421+
<Pagination
422+
className={styles.pagination}
423+
activePage={page}
424+
totalPages={Math.ceil(workflowsCount / pageSize)}
425+
onPageChange={(_, { activePage }) => gotoPage(activePage)}
426+
/>
427+
)}
428+
<div className={styles.pageSize}>
429+
<span className={styles.pageSizeLabel}>Results per page:</span>
430+
<Dropdown
431+
selection
432+
compact
433+
options={pageSizeOptions}
434+
value={pageSize}
435+
onChange={(_, { value }) => {
436+
const newSize = Number(value);
437+
setPageSize(newSize);
438+
setSearchParams((prev) => {
439+
const qp = new URLSearchParams(prev);
440+
if (newSize !== DEFAULT_PAGE_SIZE) {
441+
qp.set("page-size", String(newSize));
442+
} else {
443+
qp.delete("page-size");
444+
}
445+
qp.delete("page"); // reset to page 1
446+
return qp;
447+
});
448+
}}
449+
/>
450+
</div>
451+
</div>
452+
)}
396453
</Container>
397-
{workflowsCount > PAGE_SIZE && !loading && (
398-
<Pagination
399-
className={styles.pagination}
400-
activePage={page}
401-
totalPages={Math.ceil(workflowsCount / PAGE_SIZE)}
402-
onPageChange={(_, { activePage }) => gotoPage(activePage)}
403-
/>
404-
)}
405454
</div>
406455
);
407456
}

reana-ui/src/pages/workflowList/WorkflowList.module.scss

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,43 @@
4545
:global(.ui.text.container).workflow-list-container {
4646
max-width: 825px !important;
4747
}
48+
49+
.paginationRow {
50+
display: grid;
51+
align-items: center;
52+
justify-items: center;
53+
}
54+
55+
.pageSizeNotVisible {
56+
grid-column: 1;
57+
display: inline-flex;
58+
gap: 0.5rem;
59+
visibility: hidden;
60+
61+
:global(.ui.selection.dropdown > .text) {
62+
font-size: 1rem;
63+
}
64+
}
65+
66+
.pagination {
67+
grid-column: 2;
68+
justify-self: center;
69+
}
70+
71+
.pageSize {
72+
grid-column: 3;
73+
justify-self: end;
74+
display: inline-flex;
75+
align-items: center;
76+
gap: 0.5rem;
77+
margin-top: 2em;
78+
margin-bottom: 2em;
79+
80+
:global(.ui.selection.dropdown > .text) {
81+
font-size: 1rem;
82+
}
83+
}
84+
85+
.pageSizeLabel {
86+
font-size: 1rem;
87+
}

reana-ui/src/pages/workflowList/components/WorkflowSharingFilter.module.scss

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,13 @@
1414
}
1515

1616
:global(.ui.selection.dropdown).selected-user-dropdown {
17-
border-top-left-radius: 0%;
18-
border-bottom-left-radius: 0%;
17+
border-top-left-radius: 0;
18+
border-bottom-left-radius: 0;
1919
border-left: 0;
2020

2121
> div[aria-atomic="true"] {
2222
max-width: 100%;
2323
white-space: nowrap;
24-
overflow: hidden;
2524
text-overflow: ellipsis;
2625
}
2726
}

0 commit comments

Comments
 (0)