update navigation for react-router v6

This commit is contained in:
Min RK
2024-03-08 14:26:41 +01:00
parent bfe143f1ac
commit 77e625d36d
11 changed files with 82 additions and 115 deletions

View File

@@ -1,15 +1,16 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { Link } from "react-router-dom"; import { Link, useNavigate } from "react-router-dom";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
const AddUser = (props) => { const AddUser = (props) => {
var [users, setUsers] = useState([]), const [users, setUsers] = useState([]),
[admin, setAdmin] = useState(false), [admin, setAdmin] = useState(false),
[errorAlert, setErrorAlert] = useState(null), [errorAlert, setErrorAlert] = useState(null),
limit = useSelector((state) => state.limit); limit = useSelector((state) => state.limit);
var dispatch = useDispatch(); const dispatch = useDispatch();
const navigate = useNavigate();
var dispatchPageChange = (data, page) => { var dispatchPageChange = (data, page) => {
dispatch({ dispatch({
@@ -21,7 +22,7 @@ const AddUser = (props) => {
}); });
}; };
var { addUsers, updateUsers, history } = props; var { addUsers, updateUsers } = props;
return ( return (
<> <>
@@ -96,7 +97,7 @@ const AddUser = (props) => {
data.status < 300 data.status < 300
? updateUsers(0, limit) ? updateUsers(0, limit)
.then((data) => dispatchPageChange(data, 0)) .then((data) => dispatchPageChange(data, 0))
.then(() => history.push("/")) .then(() => navigate("/"))
.catch(() => .catch(() =>
setErrorAlert(`Failed to update users.`), setErrorAlert(`Failed to update users.`),
) )
@@ -123,9 +124,6 @@ const AddUser = (props) => {
AddUser.propTypes = { AddUser.propTypes = {
addUsers: PropTypes.func, addUsers: PropTypes.func,
updateUsers: PropTypes.func, updateUsers: PropTypes.func,
history: PropTypes.shape({
push: PropTypes.func,
}),
}; };
export default AddUser; export default AddUser;

View File

@@ -26,11 +26,7 @@ var mockAsyncRejection = () =>
var addUserJsx = (spy, spy2, spy3) => ( var addUserJsx = (spy, spy2, spy3) => (
<Provider store={createStore(() => {}, {})}> <Provider store={createStore(() => {}, {})}>
<HashRouter> <HashRouter>
<AddUser <AddUser addUsers={spy} updateUsers={spy3 || spy2 || spy} />
addUsers={spy}
updateUsers={spy3 || spy2 || spy}
history={{ push: () => {} }}
/>
</HashRouter> </HashRouter>
</Provider> </Provider>
); );

View File

@@ -1,16 +1,17 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { Link } from "react-router-dom"; import { Link, useNavigate } from "react-router-dom";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
const CreateGroup = (props) => { const CreateGroup = (props) => {
var [groupName, setGroupName] = useState(""), const [groupName, setGroupName] = useState(""),
[errorAlert, setErrorAlert] = useState(null), [errorAlert, setErrorAlert] = useState(null),
limit = useSelector((state) => state.limit); limit = useSelector((state) => state.limit);
var dispatch = useDispatch(); const dispatch = useDispatch();
const navigate = useNavigate();
var dispatchPageUpdate = (data, page) => { const dispatchPageUpdate = (data, page) => {
dispatch({ dispatch({
type: "GROUPS_PAGE", type: "GROUPS_PAGE",
value: { value: {
@@ -20,7 +21,7 @@ const CreateGroup = (props) => {
}); });
}; };
var { createGroup, updateGroups, history } = props; const { createGroup, updateGroups } = props;
return ( return (
<> <>
@@ -79,7 +80,7 @@ const CreateGroup = (props) => {
return data.status < 300 return data.status < 300
? updateGroups(0, limit) ? updateGroups(0, limit)
.then((data) => dispatchPageUpdate(data, 0)) .then((data) => dispatchPageUpdate(data, 0))
.then(() => history.push("/groups")) .then(() => navigate("/groups"))
.catch(() => .catch(() =>
setErrorAlert(`Could not update groups list.`), setErrorAlert(`Could not update groups list.`),
) )
@@ -108,9 +109,6 @@ const CreateGroup = (props) => {
CreateGroup.propTypes = { CreateGroup.propTypes = {
createGroup: PropTypes.func, createGroup: PropTypes.func,
updateGroups: PropTypes.func, updateGroups: PropTypes.func,
history: PropTypes.shape({
push: PropTypes.func,
}),
}; };
export default CreateGroup; export default CreateGroup;

View File

@@ -25,11 +25,7 @@ var mockAsyncRejection = () =>
var createGroupJsx = (callbackSpy) => ( var createGroupJsx = (callbackSpy) => (
<Provider store={createStore(() => {}, {})}> <Provider store={createStore(() => {}, {})}>
<HashRouter> <HashRouter>
<CreateGroup <CreateGroup createGroup={callbackSpy} updateGroups={callbackSpy} />
createGroup={callbackSpy}
updateGroups={callbackSpy}
history={{ push: () => {} }}
/>
</HashRouter> </HashRouter>
</Provider> </Provider>
); );

View File

@@ -1,13 +1,15 @@
import React, { useState } from "react"; import React, { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { Link } from "react-router-dom"; import { Link, useLocation, useNavigate } from "react-router-dom";
const EditUser = (props) => { const EditUser = (props) => {
var limit = useSelector((state) => state.limit), const limit = useSelector((state) => state.limit),
[errorAlert, setErrorAlert] = useState(null); [errorAlert, setErrorAlert] = useState(null);
var dispatch = useDispatch(); const dispatch = useDispatch();
const location = useLocation();
const navigate = useNavigate();
var dispatchPageChange = (data, page) => { var dispatchPageChange = (data, page) => {
dispatch({ dispatch({
@@ -19,14 +21,19 @@ const EditUser = (props) => {
}); });
}; };
var { editUser, deleteUser, noChangeEvent, updateUsers, history } = props; var { editUser, deleteUser, noChangeEvent, updateUsers } = props;
if (props.location.state == undefined) { useEffect(() => {
props.history.push("/"); if (!location.state) {
return <></>; navigate("/");
}
}, [location]);
if (!location.state) {
return null;
} }
var { username, has_admin } = props.location.state; var { username, has_admin } = location.state;
var [updatedUsername, setUpdatedUsername] = useState(""), var [updatedUsername, setUpdatedUsername] = useState(""),
[admin, setAdmin] = useState(has_admin); [admin, setAdmin] = useState(has_admin);
@@ -93,7 +100,7 @@ const EditUser = (props) => {
data.status < 300 data.status < 300
? updateUsers(0, limit) ? updateUsers(0, limit)
.then((data) => dispatchPageChange(data, 0)) .then((data) => dispatchPageChange(data, 0))
.then(() => history.push("/")) .then(() => navigate("/"))
.catch(() => .catch(() =>
setErrorAlert( setErrorAlert(
`Could not update users list.`, `Could not update users list.`,
@@ -135,7 +142,7 @@ const EditUser = (props) => {
data.status < 300 data.status < 300
? updateUsers(0, limit) ? updateUsers(0, limit)
.then((data) => dispatchPageChange(data, 0)) .then((data) => dispatchPageChange(data, 0))
.then(() => history.push("/")) .then(() => navigate("/"))
.catch(() => .catch(() =>
setErrorAlert(`Could not update users list.`), setErrorAlert(`Could not update users list.`),
) )
@@ -159,15 +166,6 @@ const EditUser = (props) => {
}; };
EditUser.propTypes = { EditUser.propTypes = {
location: PropTypes.shape({
state: PropTypes.shape({
username: PropTypes.string,
has_admin: PropTypes.bool,
}),
}),
history: PropTypes.shape({
push: PropTypes.func,
}),
editUser: PropTypes.func, editUser: PropTypes.func,
deleteUser: PropTypes.func, deleteUser: PropTypes.func,
noChangeEvent: PropTypes.func, noChangeEvent: PropTypes.func,

View File

@@ -16,6 +16,14 @@ jest.mock("react-redux", () => ({
useSelector: jest.fn(), useSelector: jest.fn(),
})); }));
jest.mock("react-router-dom", () => ({
...jest.requireActual("react-router-dom"),
useLocation: jest.fn().mockImplementation(() => {
return { state: { username: "foo", has_admin: false } };
}),
useNavigate: jest.fn(),
}));
var mockAsync = (data) => var mockAsync = (data) =>
jest.fn().mockImplementation(() => Promise.resolve(data)); jest.fn().mockImplementation(() => Promise.resolve(data));
@@ -26,11 +34,9 @@ var editUserJsx = (callbackSpy, empty) => (
<Provider store={createStore(() => {}, {})}> <Provider store={createStore(() => {}, {})}>
<HashRouter> <HashRouter>
<EditUser <EditUser
location={empty ? {} : { state: { username: "foo", has_admin: false } }}
deleteUser={callbackSpy} deleteUser={callbackSpy}
editUser={callbackSpy} editUser={callbackSpy}
updateUsers={callbackSpy} updateUsers={callbackSpy}
history={{ push: () => {} }}
noChangeEvent={callbackSpy} noChangeEvent={callbackSpy}
/> />
</HashRouter> </HashRouter>

View File

@@ -1,17 +1,19 @@
import React, { useState } from "react"; import React, { useEffect, useState } from "react";
import { useSelector, useDispatch } from "react-redux"; import { useSelector, useDispatch } from "react-redux";
import { Link } from "react-router-dom"; import { Link, useNavigate, useLocation } from "react-router-dom";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import GroupSelect from "../GroupSelect/GroupSelect"; import GroupSelect from "../GroupSelect/GroupSelect";
import DynamicTable from "../DynamicTable/DynamicTable"; import DynamicTable from "../DynamicTable/DynamicTable";
const GroupEdit = (props) => { const GroupEdit = (props) => {
var [selected, setSelected] = useState([]), const [selected, setSelected] = useState([]),
[changed, setChanged] = useState(false), [changed, setChanged] = useState(false),
[errorAlert, setErrorAlert] = useState(null), [errorAlert, setErrorAlert] = useState(null),
navigate = useNavigate(),
location = useLocation(),
limit = useSelector((state) => state.limit); limit = useSelector((state) => state.limit);
var dispatch = useDispatch(); const dispatch = useDispatch();
const hasDuplicates = (a) => a.filter((e, i) => a.indexOf(e) != i).length > 0; const hasDuplicates = (a) => a.filter((e, i) => a.indexOf(e) != i).length > 0;
const dispatchPageUpdate = (data, page) => { const dispatchPageUpdate = (data, page) => {
dispatch({ dispatch({
@@ -23,28 +25,28 @@ const GroupEdit = (props) => {
}); });
}; };
var { const {
addToGroup, addToGroup,
updateProp, updateProp,
removeFromGroup, removeFromGroup,
deleteGroup, deleteGroup,
updateGroups, updateGroups,
validateUser, validateUser,
history,
location,
} = props; } = props;
if (!location.state) { console.log("group edit", location, location.state);
history.push("/groups");
return <></>;
}
var { group_data } = location.state; useEffect(() => {
var [propobject, setProp] = useState(group_data.properties); if (!location.state) {
var [propkeys, setPropKeys] = useState([]); navigate("/groups");
var [propvalues, setPropValues] = useState([]); }
}, [location]);
const { group_data } = location.state || {};
if (!group_data) return <div></div>; if (!group_data) return <div></div>;
const [propobject, setProp] = useState(group_data.properties);
const [propkeys, setPropKeys] = useState([]);
const [propvalues, setPropValues] = useState([]);
return ( return (
<div className="container" data-testid="container"> <div className="container" data-testid="container">
@@ -173,7 +175,7 @@ const GroupEdit = (props) => {
data.status < 300 data.status < 300
? updateGroups(0, limit) ? updateGroups(0, limit)
.then((data) => dispatchPageUpdate(data, 0)) .then((data) => dispatchPageUpdate(data, 0))
.then(() => history.push("/groups")) .then(() => navigate("/groups"))
: setErrorAlert(`Failed to delete group.`); : setErrorAlert(`Failed to delete group.`);
}) })
.catch(() => setErrorAlert(`Failed to delete group.`)); .catch(() => setErrorAlert(`Failed to delete group.`));
@@ -190,15 +192,6 @@ const GroupEdit = (props) => {
}; };
GroupEdit.propTypes = { GroupEdit.propTypes = {
location: PropTypes.shape({
state: PropTypes.shape({
group_data: PropTypes.object,
callback: PropTypes.func,
}),
}),
history: PropTypes.shape({
push: PropTypes.func,
}),
addToGroup: PropTypes.func, addToGroup: PropTypes.func,
removeFromGroup: PropTypes.func, removeFromGroup: PropTypes.func,
deleteGroup: PropTypes.func, deleteGroup: PropTypes.func,

View File

@@ -16,6 +16,14 @@ jest.mock("react-redux", () => ({
useSelector: jest.fn(), useSelector: jest.fn(),
})); }));
jest.mock("react-router-dom", () => ({
...jest.requireActual("react-router-dom"),
useLocation: jest.fn().mockImplementation(() => {
return { state: { group_data: { users: ["foo"], name: "group" } } };
}),
useNavigate: jest.fn(),
}));
var mockAsync = (data) => var mockAsync = (data) =>
jest.fn().mockImplementation(() => Promise.resolve(data)); jest.fn().mockImplementation(() => Promise.resolve(data));
@@ -28,16 +36,9 @@ var groupEditJsx = (callbackSpy) => (
<Provider store={createStore(() => {}, {})}> <Provider store={createStore(() => {}, {})}>
<HashRouter> <HashRouter>
<GroupEdit <GroupEdit
location={{
state: {
group_data: { users: ["foo"], name: "group" },
callback: () => {},
},
}}
addToGroup={callbackSpy} addToGroup={callbackSpy}
removeFromGroup={callbackSpy} removeFromGroup={callbackSpy}
deleteGroup={callbackSpy} deleteGroup={callbackSpy}
history={{ push: () => callbackSpy }}
updateGroups={callbackSpy} updateGroups={callbackSpy}
validateUser={jest.fn().mockImplementation(() => okPacket)} validateUser={jest.fn().mockImplementation(() => okPacket)}
/> />

View File

@@ -2,21 +2,22 @@ import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux"; import { useSelector, useDispatch } from "react-redux";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { Link } from "react-router-dom"; import { Link, useNavigate } from "react-router-dom";
import { usePaginationParams } from "../../util/paginationParams"; import { usePaginationParams } from "../../util/paginationParams";
import PaginationFooter from "../PaginationFooter/PaginationFooter"; import PaginationFooter from "../PaginationFooter/PaginationFooter";
const Groups = (props) => { const Groups = (props) => {
var groups_data = useSelector((state) => state.groups_data), const groups_data = useSelector((state) => state.groups_data);
groups_page = useSelector((state) => state.groups_page), const groups_page = useSelector((state) => state.groups_page);
dispatch = useDispatch(); const dispatch = useDispatch();
const navigate = useNavigate();
const { setOffset, offset, setLimit, handleLimit, limit, setPagination } = const { setOffset, offset, handleLimit, limit, setPagination } =
usePaginationParams(); usePaginationParams();
var total = groups_page ? groups_page.total : undefined; const total = groups_page ? groups_page.total : undefined;
var { updateGroups, history } = props; const { updateGroups } = props;
const dispatchPageUpdate = (data, page) => { const dispatchPageUpdate = (data, page) => {
setPagination(page); setPagination(page);
@@ -55,14 +56,7 @@ const Groups = (props) => {
<span className="badge badge-pill badge-success"> <span className="badge badge-pill badge-success">
{e.users.length + " users"} {e.users.length + " users"}
</span> </span>
<Link <Link to="/group-edit" state={{ group_data: e }}>
to={{
pathname: "/group-edit",
state: {
group_data: e,
},
}}
>
{e.name} {e.name}
</Link> </Link>
</li> </li>
@@ -90,7 +84,7 @@ const Groups = (props) => {
<button <button
className="btn btn-primary adjacent-span-spacing" className="btn btn-primary adjacent-span-spacing"
onClick={() => { onClick={() => {
history.push("/create-group"); navigate("/create-group");
}} }}
> >
New Group New Group
@@ -106,12 +100,6 @@ const Groups = (props) => {
Groups.propTypes = { Groups.propTypes = {
updateUsers: PropTypes.func, updateUsers: PropTypes.func,
updateGroups: PropTypes.func, updateGroups: PropTypes.func,
history: PropTypes.shape({
push: PropTypes.func,
}),
location: PropTypes.shape({
search: PropTypes.string,
}),
}; };
export default Groups; export default Groups;

View File

@@ -27,7 +27,7 @@ var mockAsync = () =>
var groupsJsx = (callbackSpy) => ( var groupsJsx = (callbackSpy) => (
<Provider store={createStore(mockReducers, mockAppState())}> <Provider store={createStore(mockReducers, mockAppState())}>
<HashRouter> <HashRouter>
<Groups location={{ search: "0" }} updateGroups={callbackSpy} /> <Groups updateGroups={callbackSpy} />
</HashRouter> </HashRouter>
</Provider> </Provider>
); );

View File

@@ -15,7 +15,7 @@ import {
} from "react-bootstrap"; } from "react-bootstrap";
import ReactObjectTableViewer from "../ReactObjectTableViewer/ReactObjectTableViewer"; import ReactObjectTableViewer from "../ReactObjectTableViewer/ReactObjectTableViewer";
import { Link, useSearchParams } from "react-router-dom"; import { Link, useSearchParams, useNavigate } from "react-router-dom";
import { FaSort, FaSortUp, FaSortDown } from "react-icons/fa"; import { FaSort, FaSortUp, FaSortDown } from "react-icons/fa";
import "./server-dashboard.css"; import "./server-dashboard.css";
@@ -50,6 +50,7 @@ const ServerDashboard = (props) => {
const total = user_page ? user_page.total : undefined; const total = user_page ? user_page.total : undefined;
const dispatch = useDispatch(); const dispatch = useDispatch();
const navigate = useNavigate();
var { var {
updateUsers, updateUsers,
@@ -59,7 +60,6 @@ const ServerDashboard = (props) => {
deleteServer, deleteServer,
startAll, startAll,
stopAll, stopAll,
history,
} = props; } = props;
const dispatchPageUpdate = (data, page) => { const dispatchPageUpdate = (data, page) => {
@@ -260,8 +260,7 @@ const ServerDashboard = (props) => {
<button <button
className="btn btn-light btn-xs" className="btn btn-light btn-xs"
onClick={() => onClick={() =>
history.push({ navigate("/edit-user", {
pathname: "/edit-user",
state: { state: {
username: user.name, username: user.name,
has_admin: user.admin, has_admin: user.admin,
@@ -435,7 +434,7 @@ const ServerDashboard = (props) => {
</Col> </Col>
<Col md={3}> <Col md={3}>
{/* div.checkbox required for BS3 CSS */} {/* div.checkbox required for BS3 CSS */}
<div class="checkbox"> <div className="checkbox">
<label title="check to only show running servers, otherwise show all"> <label title="check to only show running servers, otherwise show all">
<Form.Check <Form.Check
inline inline
@@ -605,12 +604,6 @@ ServerDashboard.propTypes = {
startAll: PropTypes.func, startAll: PropTypes.func,
stopAll: PropTypes.func, stopAll: PropTypes.func,
dispatch: PropTypes.func, dispatch: PropTypes.func,
history: PropTypes.shape({
push: PropTypes.func,
}),
location: PropTypes.shape({
search: PropTypes.string,
}),
}; };
const SortHandler = (props) => { const SortHandler = (props) => {