mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-14 05:23:01 +00:00
Update unit tests for pagination
This commit is contained in:
@@ -9,7 +9,7 @@
|
|||||||
"build": "yarn && webpack",
|
"build": "yarn && webpack",
|
||||||
"hot": "webpack && webpack-dev-server",
|
"hot": "webpack && webpack-dev-server",
|
||||||
"place": "cp -r build/admin-react.js ../share/jupyterhub/static/js/admin-react.js",
|
"place": "cp -r build/admin-react.js ../share/jupyterhub/static/js/admin-react.js",
|
||||||
"test": "jest",
|
"test": "jest --verbose",
|
||||||
"snap": "jest --updateSnapshot",
|
"snap": "jest --updateSnapshot",
|
||||||
"lint": "eslint --ext .jsx --ext .js src/",
|
"lint": "eslint --ext .jsx --ext .js src/",
|
||||||
"lint:fix": "eslint --ext .jsx --ext .js src/ --fix"
|
"lint:fix": "eslint --ext .jsx --ext .js src/ --fix"
|
||||||
|
@@ -2,7 +2,7 @@ import React from "react";
|
|||||||
import Enzyme, { mount } from "enzyme";
|
import Enzyme, { mount } from "enzyme";
|
||||||
import AddUser from "./AddUser";
|
import AddUser from "./AddUser";
|
||||||
import Adapter from "@wojtekmaj/enzyme-adapter-react-17";
|
import Adapter from "@wojtekmaj/enzyme-adapter-react-17";
|
||||||
import { Provider, useDispatch } from "react-redux";
|
import { Provider, useDispatch, useSelector } from "react-redux";
|
||||||
import { createStore } from "redux";
|
import { createStore } from "redux";
|
||||||
import { HashRouter } from "react-router-dom";
|
import { HashRouter } from "react-router-dom";
|
||||||
|
|
||||||
@@ -11,6 +11,7 @@ Enzyme.configure({ adapter: new Adapter() });
|
|||||||
jest.mock("react-redux", () => ({
|
jest.mock("react-redux", () => ({
|
||||||
...jest.requireActual("react-redux"),
|
...jest.requireActual("react-redux"),
|
||||||
useDispatch: jest.fn(),
|
useDispatch: jest.fn(),
|
||||||
|
useSelector: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe("AddUser Component: ", () => {
|
describe("AddUser Component: ", () => {
|
||||||
@@ -23,17 +24,24 @@ describe("AddUser Component: ", () => {
|
|||||||
<AddUser
|
<AddUser
|
||||||
addUsers={callbackSpy}
|
addUsers={callbackSpy}
|
||||||
failRegexEvent={callbackSpy}
|
failRegexEvent={callbackSpy}
|
||||||
refreshUserData={callbackSpy}
|
updateUsers={callbackSpy}
|
||||||
history={{ push: (a) => {} }}
|
history={{ push: (a) => {} }}
|
||||||
/>
|
/>
|
||||||
</HashRouter>
|
</HashRouter>
|
||||||
</Provider>
|
</Provider>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
var mockAppState = () => ({
|
||||||
|
limit: 3,
|
||||||
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
useDispatch.mockImplementation((callback) => {
|
useDispatch.mockImplementation((callback) => {
|
||||||
return () => {};
|
return () => {};
|
||||||
});
|
});
|
||||||
|
useSelector.mockImplementation((callback) => {
|
||||||
|
return callback(mockAppState());
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
@@ -2,7 +2,7 @@ import React from "react";
|
|||||||
import Enzyme, { mount } from "enzyme";
|
import Enzyme, { mount } from "enzyme";
|
||||||
import CreateGroup from "./CreateGroup";
|
import CreateGroup from "./CreateGroup";
|
||||||
import Adapter from "@wojtekmaj/enzyme-adapter-react-17";
|
import Adapter from "@wojtekmaj/enzyme-adapter-react-17";
|
||||||
import { Provider, useDispatch } from "react-redux";
|
import { Provider, useDispatch, useSelector } from "react-redux";
|
||||||
import { createStore } from "redux";
|
import { createStore } from "redux";
|
||||||
import { HashRouter } from "react-router-dom";
|
import { HashRouter } from "react-router-dom";
|
||||||
|
|
||||||
@@ -11,6 +11,7 @@ Enzyme.configure({ adapter: new Adapter() });
|
|||||||
jest.mock("react-redux", () => ({
|
jest.mock("react-redux", () => ({
|
||||||
...jest.requireActual("react-redux"),
|
...jest.requireActual("react-redux"),
|
||||||
useDispatch: jest.fn(),
|
useDispatch: jest.fn(),
|
||||||
|
useSelector: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe("CreateGroup Component: ", () => {
|
describe("CreateGroup Component: ", () => {
|
||||||
@@ -22,16 +23,23 @@ describe("CreateGroup Component: ", () => {
|
|||||||
<HashRouter>
|
<HashRouter>
|
||||||
<CreateGroup
|
<CreateGroup
|
||||||
createGroup={callbackSpy}
|
createGroup={callbackSpy}
|
||||||
refreshGroupsData={callbackSpy}
|
updateGroups={callbackSpy}
|
||||||
history={{ push: () => {} }}
|
history={{ push: () => {} }}
|
||||||
/>
|
/>
|
||||||
</HashRouter>
|
</HashRouter>
|
||||||
</Provider>
|
</Provider>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
var mockAppState = () => ({
|
||||||
|
limit: 3,
|
||||||
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
useDispatch.mockImplementation((callback) => {
|
useDispatch.mockImplementation((callback) => {
|
||||||
return () => {};
|
return () => () => {};
|
||||||
|
});
|
||||||
|
useSelector.mockImplementation((callback) => {
|
||||||
|
return callback(mockAppState());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -52,6 +60,6 @@ describe("CreateGroup Component: ", () => {
|
|||||||
input.simulate("change", { target: { value: "" } });
|
input.simulate("change", { target: { value: "" } });
|
||||||
submit.simulate("click");
|
submit.simulate("click");
|
||||||
expect(callbackSpy).toHaveBeenNthCalledWith(1, "");
|
expect(callbackSpy).toHaveBeenNthCalledWith(1, "");
|
||||||
expect(callbackSpy).toHaveBeenNthCalledWith(2);
|
expect(callbackSpy).toHaveBeenNthCalledWith(2, 0, 3);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -2,7 +2,7 @@ import React from "react";
|
|||||||
import Enzyme, { mount } from "enzyme";
|
import Enzyme, { mount } from "enzyme";
|
||||||
import EditUser from "./EditUser";
|
import EditUser from "./EditUser";
|
||||||
import Adapter from "@wojtekmaj/enzyme-adapter-react-17";
|
import Adapter from "@wojtekmaj/enzyme-adapter-react-17";
|
||||||
import { Provider, useDispatch } from "react-redux";
|
import { Provider, useDispatch, useSelector } from "react-redux";
|
||||||
import { createStore } from "redux";
|
import { createStore } from "redux";
|
||||||
import { HashRouter } from "react-router-dom";
|
import { HashRouter } from "react-router-dom";
|
||||||
|
|
||||||
@@ -11,6 +11,7 @@ Enzyme.configure({ adapter: new Adapter() });
|
|||||||
jest.mock("react-redux", () => ({
|
jest.mock("react-redux", () => ({
|
||||||
...jest.requireActual("react-redux"),
|
...jest.requireActual("react-redux"),
|
||||||
useDispatch: jest.fn(),
|
useDispatch: jest.fn(),
|
||||||
|
useSelector: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe("EditUser Component: ", () => {
|
describe("EditUser Component: ", () => {
|
||||||
@@ -27,7 +28,7 @@ describe("EditUser Component: ", () => {
|
|||||||
}
|
}
|
||||||
deleteUser={callbackSpy}
|
deleteUser={callbackSpy}
|
||||||
editUser={callbackSpy}
|
editUser={callbackSpy}
|
||||||
refreshUserData={callbackSpy}
|
updateUsers={callbackSpy}
|
||||||
history={{ push: (a) => {} }}
|
history={{ push: (a) => {} }}
|
||||||
failRegexEvent={callbackSpy}
|
failRegexEvent={callbackSpy}
|
||||||
noChangeEvent={callbackSpy}
|
noChangeEvent={callbackSpy}
|
||||||
@@ -36,10 +37,17 @@ describe("EditUser Component: ", () => {
|
|||||||
</Provider>
|
</Provider>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
var mockAppState = () => ({
|
||||||
|
limit: 3,
|
||||||
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
useDispatch.mockImplementation((callback) => {
|
useDispatch.mockImplementation((callback) => {
|
||||||
return () => {};
|
return () => {};
|
||||||
});
|
});
|
||||||
|
useSelector.mockImplementation((callback) => {
|
||||||
|
return callback(mockAppState());
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
@@ -31,7 +31,7 @@ const GroupEdit = (props) => {
|
|||||||
removeFromGroup,
|
removeFromGroup,
|
||||||
deleteGroup,
|
deleteGroup,
|
||||||
updateGroups,
|
updateGroups,
|
||||||
findUser,
|
validateUser,
|
||||||
history,
|
history,
|
||||||
location,
|
location,
|
||||||
} = props;
|
} = props;
|
||||||
@@ -41,9 +41,9 @@ const GroupEdit = (props) => {
|
|||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
|
|
||||||
var { group_data, user_data, callback } = location.state;
|
var { group_data, callback } = location.state;
|
||||||
|
|
||||||
if (!(group_data && user_data)) return <div></div>;
|
if (!group_data) return <div></div>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container">
|
<div className="container">
|
||||||
@@ -56,10 +56,7 @@ const GroupEdit = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
<GroupSelect
|
<GroupSelect
|
||||||
users={group_data.users}
|
users={group_data.users}
|
||||||
validateUser={async (username) => {
|
validateUser={validateUser}
|
||||||
let user = await findUser(username);
|
|
||||||
return user.status > 200 ? false : true;
|
|
||||||
}}
|
|
||||||
onChange={(selection) => {
|
onChange={(selection) => {
|
||||||
setSelected(selection);
|
setSelected(selection);
|
||||||
setChanged(true);
|
setChanged(true);
|
||||||
@@ -139,7 +136,6 @@ GroupEdit.propTypes = {
|
|||||||
location: PropTypes.shape({
|
location: PropTypes.shape({
|
||||||
state: PropTypes.shape({
|
state: PropTypes.shape({
|
||||||
group_data: PropTypes.object,
|
group_data: PropTypes.object,
|
||||||
user_data: PropTypes.array,
|
|
||||||
callback: PropTypes.func,
|
callback: PropTypes.func,
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
|
@@ -5,6 +5,8 @@ import Adapter from "@wojtekmaj/enzyme-adapter-react-17";
|
|||||||
import { Provider, useSelector } from "react-redux";
|
import { Provider, useSelector } from "react-redux";
|
||||||
import { createStore } from "redux";
|
import { createStore } from "redux";
|
||||||
import { HashRouter } from "react-router-dom";
|
import { HashRouter } from "react-router-dom";
|
||||||
|
import ReactDOM from "react-dom";
|
||||||
|
import { act } from "react-dom/test-utils";
|
||||||
|
|
||||||
Enzyme.configure({ adapter: new Adapter() });
|
Enzyme.configure({ adapter: new Adapter() });
|
||||||
|
|
||||||
@@ -14,8 +16,9 @@ jest.mock("react-redux", () => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
describe("GroupEdit Component: ", () => {
|
describe("GroupEdit Component: ", () => {
|
||||||
var mockAsync = () =>
|
var mockAsync = () => jest.fn().mockImplementation(() => Promise.resolve());
|
||||||
jest.fn().mockImplementation(() => Promise.resolve({ key: "value" }));
|
|
||||||
|
var okPacket = new Promise((resolve) => resolve(true));
|
||||||
|
|
||||||
var groupEditJsx = (callbackSpy) => (
|
var groupEditJsx = (callbackSpy) => (
|
||||||
<Provider store={createStore(() => {}, {})}>
|
<Provider store={createStore(() => {}, {})}>
|
||||||
@@ -23,7 +26,6 @@ describe("GroupEdit Component: ", () => {
|
|||||||
<GroupEdit
|
<GroupEdit
|
||||||
location={{
|
location={{
|
||||||
state: {
|
state: {
|
||||||
user_data: [{ name: "foo" }, { name: "bar" }],
|
|
||||||
group_data: { users: ["foo"], name: "group" },
|
group_data: { users: ["foo"], name: "group" },
|
||||||
callback: () => {},
|
callback: () => {},
|
||||||
},
|
},
|
||||||
@@ -32,16 +34,15 @@ describe("GroupEdit Component: ", () => {
|
|||||||
removeFromGroup={callbackSpy}
|
removeFromGroup={callbackSpy}
|
||||||
deleteGroup={callbackSpy}
|
deleteGroup={callbackSpy}
|
||||||
history={{ push: (a) => callbackSpy }}
|
history={{ push: (a) => callbackSpy }}
|
||||||
refreshGroupsData={callbackSpy}
|
updateGroups={callbackSpy}
|
||||||
|
validateUser={jest.fn().mockImplementation(() => okPacket)}
|
||||||
/>
|
/>
|
||||||
</HashRouter>
|
</HashRouter>
|
||||||
</Provider>
|
</Provider>
|
||||||
);
|
);
|
||||||
|
|
||||||
var mockAppState = () => ({
|
var mockAppState = () => ({
|
||||||
user_data: JSON.parse(
|
limit: 3,
|
||||||
'[{"kind":"user","name":"foo","admin":true,"groups":[],"server":"/user/foo/","pending":null,"created":"2020-12-07T18:46:27.112695Z","last_activity":"2020-12-07T21:00:33.336354Z","servers":{"":{"name":"","last_activity":"2020-12-07T20:58:02.437408Z","started":"2020-12-07T20:58:01.508266Z","pending":null,"ready":true,"state":{"pid":28085},"url":"/user/foo/","user_options":{},"progress_url":"/hub/api/users/foo/server/progress"}}},{"kind":"user","name":"bar","admin":false,"groups":[],"server":null,"pending":null,"created":"2020-12-07T18:46:27.115528Z","last_activity":"2020-12-07T20:43:51.013613Z","servers":{}}]'
|
|
||||||
),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -54,24 +55,39 @@ describe("GroupEdit Component: ", () => {
|
|||||||
useSelector.mockClear();
|
useSelector.mockClear();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Adds a newly selected user to group on submit", () => {
|
it("Adds user from input to user selectables on button click", async () => {
|
||||||
let callbackSpy = mockAsync(),
|
let callbackSpy = mockAsync(),
|
||||||
component = mount(groupEditJsx(callbackSpy)),
|
component = mount(groupEditJsx(callbackSpy)),
|
||||||
unselected = component.find(".unselected"),
|
input = component.find("#username-input"),
|
||||||
|
validateUser = component.find("#validate-user"),
|
||||||
submit = component.find("#submit");
|
submit = component.find("#submit");
|
||||||
unselected.simulate("click");
|
|
||||||
|
input.simulate("change", { target: { value: "bar" } });
|
||||||
|
validateUser.simulate("click");
|
||||||
|
await act(() => okPacket);
|
||||||
submit.simulate("click");
|
submit.simulate("click");
|
||||||
expect(callbackSpy).toHaveBeenCalledWith(["bar"], "group");
|
expect(callbackSpy).toHaveBeenNthCalledWith(1, ["bar"], "group");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Removes a user from group on submit", () => {
|
it("Removes a user recently added from input from the selectables list", () => {
|
||||||
let callbackSpy = mockAsync(),
|
let callbackSpy = mockAsync(),
|
||||||
component = mount(groupEditJsx(callbackSpy)),
|
component = mount(groupEditJsx(callbackSpy)),
|
||||||
selected = component.find(".selected"),
|
unsubmittedUser = component.find(".item.selected").last();
|
||||||
submit = component.find("#submit");
|
unsubmittedUser.simulate("click");
|
||||||
selected.simulate("click");
|
expect(component.find(".item").length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Grays out a user, already in the group, when unselected and calls deleteUser on submit", () => {
|
||||||
|
let callbackSpy = mockAsync(),
|
||||||
|
component = mount(groupEditJsx(callbackSpy)),
|
||||||
|
groupUser = component.find(".item.selected").first();
|
||||||
|
groupUser.simulate("click");
|
||||||
|
expect(component.find(".item.unselected").length).toBe(1);
|
||||||
|
expect(component.find(".item").length).toBe(1);
|
||||||
|
// test deleteUser call
|
||||||
|
let submit = component.find("#submit");
|
||||||
submit.simulate("click");
|
submit.simulate("click");
|
||||||
expect(callbackSpy).toHaveBeenCalledWith(["foo"], "group");
|
expect(callbackSpy).toHaveBeenNthCalledWith(1, ["foo"], "group");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Calls deleteGroup on button click", () => {
|
it("Calls deleteGroup on button click", () => {
|
||||||
@@ -80,6 +96,5 @@ describe("GroupEdit Component: ", () => {
|
|||||||
deleteGroup = component.find("#delete-group").first();
|
deleteGroup = component.find("#delete-group").first();
|
||||||
deleteGroup.simulate("click");
|
deleteGroup.simulate("click");
|
||||||
expect(callbackSpy).toHaveBeenNthCalledWith(1, "group");
|
expect(callbackSpy).toHaveBeenNthCalledWith(1, "group");
|
||||||
expect(callbackSpy).toHaveBeenNthCalledWith(2);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -23,6 +23,7 @@ const GroupSelect = (props) => {
|
|||||||
<div className="col-md-10 col-md-offset-1 col-lg-8 col-lg-offset-2 text-left">
|
<div className="col-md-10 col-md-offset-1 col-lg-8 col-lg-offset-2 text-left">
|
||||||
<div className="input-group">
|
<div className="input-group">
|
||||||
<input
|
<input
|
||||||
|
id="username-input"
|
||||||
type="text"
|
type="text"
|
||||||
className="form-control"
|
className="form-control"
|
||||||
placeholder="Add by username"
|
placeholder="Add by username"
|
||||||
@@ -33,10 +34,10 @@ const GroupSelect = (props) => {
|
|||||||
/>
|
/>
|
||||||
<span className="input-group-btn">
|
<span className="input-group-btn">
|
||||||
<button
|
<button
|
||||||
|
id="validate-user"
|
||||||
className="btn btn-default"
|
className="btn btn-default"
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
// check user exists then
|
|
||||||
validateUser(username).then((exists) => {
|
validateUser(username).then((exists) => {
|
||||||
if (exists && !selected.includes(username)) {
|
if (exists && !selected.includes(username)) {
|
||||||
let updated_selection = selected.concat([username]);
|
let updated_selection = selected.concat([username]);
|
||||||
|
@@ -2,7 +2,7 @@ import React from "react";
|
|||||||
import Enzyme, { mount } from "enzyme";
|
import Enzyme, { mount } from "enzyme";
|
||||||
import Groups from "./Groups";
|
import Groups from "./Groups";
|
||||||
import Adapter from "@wojtekmaj/enzyme-adapter-react-17";
|
import Adapter from "@wojtekmaj/enzyme-adapter-react-17";
|
||||||
import { Provider, useSelector } from "react-redux";
|
import { Provider, useDispatch, useSelector } from "react-redux";
|
||||||
import { createStore } from "redux";
|
import { createStore } from "redux";
|
||||||
import { HashRouter } from "react-router-dom";
|
import { HashRouter } from "react-router-dom";
|
||||||
|
|
||||||
@@ -15,10 +15,13 @@ jest.mock("react-redux", () => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
describe("Groups Component: ", () => {
|
describe("Groups Component: ", () => {
|
||||||
var groupsJsx = () => (
|
var mockAsync = () =>
|
||||||
|
jest.fn().mockImplementation(() => Promise.resolve({ key: "value" }));
|
||||||
|
|
||||||
|
var groupsJsx = (callbackSpy) => (
|
||||||
<Provider store={createStore(() => {}, {})}>
|
<Provider store={createStore(() => {}, {})}>
|
||||||
<HashRouter>
|
<HashRouter>
|
||||||
<Groups />
|
<Groups location={{ search: "0" }} updateGroups={callbackSpy} />
|
||||||
</HashRouter>
|
</HashRouter>
|
||||||
</Provider>
|
</Provider>
|
||||||
);
|
);
|
||||||
@@ -36,6 +39,9 @@ describe("Groups Component: ", () => {
|
|||||||
useSelector.mockImplementation((callback) => {
|
useSelector.mockImplementation((callback) => {
|
||||||
return callback(mockAppState());
|
return callback(mockAppState());
|
||||||
});
|
});
|
||||||
|
useDispatch.mockImplementation((callback) => {
|
||||||
|
return () => {};
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -43,8 +49,9 @@ describe("Groups Component: ", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("Renders groups_data prop into links", () => {
|
it("Renders groups_data prop into links", () => {
|
||||||
let component = mount(groupsJsx()),
|
let callbackSpy = mockAsync(),
|
||||||
links = component.find(".group-edit-link");
|
component = mount(groupsJsx(callbackSpy)),
|
||||||
|
links = component.find("li");
|
||||||
expect(links.length).toBe(2);
|
expect(links.length).toBe(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -1,56 +0,0 @@
|
|||||||
import React, { useState } from "react";
|
|
||||||
import "./multi-select.css";
|
|
||||||
import PropTypes from "prop-types";
|
|
||||||
|
|
||||||
const Multiselect = (props) => {
|
|
||||||
var { onChange, options, value } = props;
|
|
||||||
|
|
||||||
var [selected, setSelected] = useState(value);
|
|
||||||
|
|
||||||
if (!options) return null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="multi-container">
|
|
||||||
<div>
|
|
||||||
{selected.map((e, i) => (
|
|
||||||
<div
|
|
||||||
key={"selected" + i}
|
|
||||||
className="item selected"
|
|
||||||
onClick={() => {
|
|
||||||
let updated_selection = selected
|
|
||||||
.slice(0, i)
|
|
||||||
.concat(selected.slice(i + 1));
|
|
||||||
onChange(updated_selection, options);
|
|
||||||
setSelected(updated_selection);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{e}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
{options.map((e, i) =>
|
|
||||||
selected.includes(e) ? undefined : (
|
|
||||||
<div
|
|
||||||
key={"unselected" + i}
|
|
||||||
className="item unselected"
|
|
||||||
onClick={() => {
|
|
||||||
let updated_selection = selected.concat([e]);
|
|
||||||
onChange(updated_selection, options);
|
|
||||||
setSelected(updated_selection);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{e}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
Multiselect.propTypes = {
|
|
||||||
value: PropTypes.array,
|
|
||||||
onChange: PropTypes.func,
|
|
||||||
options: PropTypes.array,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Multiselect;
|
|
@@ -1,51 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import Enzyme, { shallow } from "enzyme";
|
|
||||||
import Multiselect from "./Multiselect";
|
|
||||||
import Adapter from "@wojtekmaj/enzyme-adapter-react-17";
|
|
||||||
|
|
||||||
Enzyme.configure({ adapter: new Adapter() });
|
|
||||||
|
|
||||||
describe("Multiselect Component: ", () => {
|
|
||||||
var multiselectJsx = () => (
|
|
||||||
<Multiselect
|
|
||||||
options={["foo", "bar", "wombat"]}
|
|
||||||
s
|
|
||||||
value={["wombat"]}
|
|
||||||
onChange={() => {}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
it("Renders with initial value selected", () => {
|
|
||||||
let component = shallow(multiselectJsx()),
|
|
||||||
selected = component.find(".item.selected").first();
|
|
||||||
expect(selected.text()).toBe("wombat");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Deselects a value when div.item.selected is clicked", () => {
|
|
||||||
let component = shallow(multiselectJsx()),
|
|
||||||
selected = component.find(".item.selected").first();
|
|
||||||
selected.simulate("click");
|
|
||||||
expect(component.find(".item.selected").length).toBe(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Selects a an option when div.item.unselected is clicked", () => {
|
|
||||||
let component = shallow(multiselectJsx()),
|
|
||||||
unselected = component.find(".item.unselected").first();
|
|
||||||
unselected.simulate("click");
|
|
||||||
expect(component.find(".item.selected").length).toBe(2);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Triggers callback on any sort of change", () => {
|
|
||||||
let callbackSpy = jest.fn(),
|
|
||||||
component = shallow(
|
|
||||||
<Multiselect
|
|
||||||
options={["foo", "bar", "wombat"]}
|
|
||||||
value={["wombat"]}
|
|
||||||
onChange={callbackSpy}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
selected = component.find(".item.selected").first();
|
|
||||||
selected.simulate("click");
|
|
||||||
expect(callbackSpy).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
@@ -1,40 +0,0 @@
|
|||||||
@import url(../../style/root.css);
|
|
||||||
|
|
||||||
.multi-container {
|
|
||||||
width: 100%;
|
|
||||||
position: relative;
|
|
||||||
padding: 5px;
|
|
||||||
overflow-x: scroll;
|
|
||||||
}
|
|
||||||
|
|
||||||
.multi-container div {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.multi-container .item {
|
|
||||||
padding: 3px;
|
|
||||||
padding-left: 6px;
|
|
||||||
padding-right: 6px;
|
|
||||||
border-radius: 3px;
|
|
||||||
font-size: 14px;
|
|
||||||
margin-left: 4px;
|
|
||||||
margin-right: 4px;
|
|
||||||
transition: 30ms ease-in all;
|
|
||||||
cursor: pointer;
|
|
||||||
user-select: none;
|
|
||||||
border: solid 1px #dfdfdf;
|
|
||||||
}
|
|
||||||
|
|
||||||
.multi-container .item.unselected {
|
|
||||||
background-color: #f7f7f7;
|
|
||||||
color: #777;
|
|
||||||
}
|
|
||||||
|
|
||||||
.multi-container .item.selected {
|
|
||||||
background-color: orange;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.multi-container .item:hover {
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
@@ -36,8 +36,6 @@ const ServerDashboard = (props) => {
|
|||||||
limit = useSelector((state) => state.limit),
|
limit = useSelector((state) => state.limit),
|
||||||
page = parseInt(new URLSearchParams(props.location.search).get("page"));
|
page = parseInt(new URLSearchParams(props.location.search).get("page"));
|
||||||
|
|
||||||
console.log(user_page);
|
|
||||||
|
|
||||||
page = isNaN(page) ? 0 : page;
|
page = isNaN(page) ? 0 : page;
|
||||||
var slice = [page * limit, limit];
|
var slice = [page * limit, limit];
|
||||||
|
|
||||||
|
@@ -33,6 +33,10 @@ const withAPI = withProps((props) => ({
|
|||||||
}),
|
}),
|
||||||
deleteUser: (username) => jhapiRequest("/users/" + username, "DELETE"),
|
deleteUser: (username) => jhapiRequest("/users/" + username, "DELETE"),
|
||||||
findUser: (username) => jhapiRequest("/users/" + username, "GET"),
|
findUser: (username) => jhapiRequest("/users/" + username, "GET"),
|
||||||
|
validateUser: (username) =>
|
||||||
|
findUser(username)
|
||||||
|
.then((data) => data.status)
|
||||||
|
.then((data) => (data > 200 ? false : true)),
|
||||||
failRegexEvent: () =>
|
failRegexEvent: () =>
|
||||||
alert(
|
alert(
|
||||||
"Cannot change username - either contains special characters or is too short."
|
"Cannot change username - either contains special characters or is too short."
|
||||||
|
Reference in New Issue
Block a user