diff --git a/frontend/src/devicelist/DeviceListPage.jsx b/frontend/src/devicelist/DeviceListPage.jsx
index 850c2df19..37f95c59e 100644
--- a/frontend/src/devicelist/DeviceListPage.jsx
+++ b/frontend/src/devicelist/DeviceListPage.jsx
@@ -1,9 +1,7 @@
import React from "react";
import Page from "../general/Page";
-import DeviceListTabTable from "./DeviceListTabTable";
-import * as useStyles from "../DefaultMakeStylesTheme";
-import * as dataSource from "../api/DeviceApi";
+import DeviceListPageContents from "./DeviceListPageContents";
export default function DeviceListPage() {
const breadcrumb = [
@@ -12,7 +10,7 @@ export default function DeviceListPage() {
];
return (
-
+
);
}
diff --git a/frontend/src/devicelist/DeviceListPageContents.jsx b/frontend/src/devicelist/DeviceListPageContents.jsx
new file mode 100644
index 000000000..461eec716
--- /dev/null
+++ b/frontend/src/devicelist/DeviceListPageContents.jsx
@@ -0,0 +1,66 @@
+import React from "react";
+
+import { getSenders, getReceivers } from "../api/DeviceApi";
+import DeviceTableTitle from "./DeviceTableTitle";
+import DevicesTable from "./DevicesTable";
+
+export default class DeviceListPageContents extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ selected: 0,
+ senders: [],
+ receivers: []
+ };
+
+ this.handleChange = this.handleChange.bind(this);
+ this.handleSendersChange = this.handleSendersChange.bind(this);
+ this.handleReceiversChange = this.handleReceiversChange.bind(this);
+ }
+
+ componentDidMount() {
+ getSenders(this.handleSendersChange);
+ getReceivers(this.handleReceiversChange);
+ }
+
+ handleChange(value) {
+ this.setState({
+ selected: value
+ });
+ }
+
+ handleSendersChange(senders) {
+ this.setState({
+ senders
+ });
+ }
+
+ handleReceiversChange(receivers) {
+ this.setState({
+ receivers
+ });
+ }
+
+ getDevices() {
+ const { receivers, senders, selected } = this.state;
+ switch (selected) {
+ case 1:
+ return senders;
+ case 2:
+ return receivers;
+ default:
+ return senders.concat(receivers);
+ }
+ }
+
+ getTitle() {
+ const { selected } = this.state;
+ return (
+
+ );
+ }
+
+ render() {
+ return ;
+ }
+}
diff --git a/frontend/src/devicelist/DeviceListTabTable.jsx b/frontend/src/devicelist/DeviceListTabTable.jsx
deleted file mode 100644
index c83953205..000000000
--- a/frontend/src/devicelist/DeviceListTabTable.jsx
+++ /dev/null
@@ -1,78 +0,0 @@
-import React from "react";
-import PropTypes from "prop-types";
-import { Box } from "@material-ui/core";
-
-import TabPanel from "./TabPanel";
-import DevicesTable from "./DevicesTable";
-import VerticalTabs from "../general/VerticalTabs";
-
-export default class DeviceListTabTable extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- senders: [],
- receivers: [],
- value: 0
- };
- this.handleValueChange = this.handleValueChange.bind(this);
- this.handleSendersChange = this.handleSendersChange.bind(this);
- this.handleReceiversChange = this.handleReceiversChange.bind(this);
- }
-
- componentDidMount() {
- const { dataSource } = this.props;
- dataSource.getSenders(this.handleSendersChange);
- dataSource.getReceivers(this.handleReceiversChange);
- }
-
- handleValueChange(tabIndex) {
- this.setState({
- value: tabIndex
- });
- }
-
- handleSendersChange(senders) {
- this.setState({
- senders
- });
- }
-
- handleReceiversChange(receivers) {
- this.setState({
- receivers
- });
- }
-
- render() {
- const { receivers, senders, value } = this.state;
- const { classes } = this.props;
- return (
- <>
-
-
-
-
-
-
-
-
-
- >
- );
- }
-}
-DeviceListTabTable.propTypes = {
- dataSource: PropTypes.objectOf(PropTypes.func).isRequired,
- classes: PropTypes.shape({
- tabs: PropTypes.string
- }).isRequired
-};
diff --git a/frontend/src/devicelist/DeviceTableTitle.jsx b/frontend/src/devicelist/DeviceTableTitle.jsx
new file mode 100644
index 000000000..2dbc697e8
--- /dev/null
+++ b/frontend/src/devicelist/DeviceTableTitle.jsx
@@ -0,0 +1,38 @@
+import React from "react";
+import PropTypes from "prop-types";
+import { MenuItem, Select } from "@material-ui/core";
+import StyledInput from "./StyledInput";
+
+export default class DeviceTableTitle extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.handleChange = this.handleChange.bind(this);
+ }
+
+ handleChange(event) {
+ const { handleChange } = this.props;
+ handleChange(event.target.value);
+ }
+
+ render() {
+ const { index } = this.props;
+ return (
+ }
+ >
+
+
+
+
+ );
+ }
+}
+
+DeviceTableTitle.propTypes = {
+ index: PropTypes.number.isRequired,
+ handleChange: PropTypes.func.isRequired
+};
diff --git a/frontend/src/devicelist/DevicesTable.jsx b/frontend/src/devicelist/DevicesTable.jsx
index ef3126b77..6a96de58c 100644
--- a/frontend/src/devicelist/DevicesTable.jsx
+++ b/frontend/src/devicelist/DevicesTable.jsx
@@ -149,6 +149,6 @@ export default function DevicesTable(props) {
}
DevicesTable.propTypes = {
- title: PropTypes.string.isRequired,
+ title: PropTypes.node.isRequired,
devices: PropTypes.arrayOf(PropTypes.instanceOf(DeviceInfo)).isRequired
};
diff --git a/frontend/src/devicelist/StyledInput.jsx b/frontend/src/devicelist/StyledInput.jsx
new file mode 100644
index 000000000..67b4a0cfa
--- /dev/null
+++ b/frontend/src/devicelist/StyledInput.jsx
@@ -0,0 +1,18 @@
+import { InputBase, withStyles } from "@material-ui/core";
+
+export default withStyles({
+ input: {
+ borderRadius: 4,
+ position: "relative",
+ fontWeight: 500,
+ letterSpacing: "0.0075em",
+ lineHeight: "1.6",
+ fontSize: "1.25rem",
+ padding: "10px 26px 10px 12px",
+ "&:focus": {
+ borderRadius: 4,
+ borderColor: "#80bdff",
+ boxShadow: "0 0 0 0.2rem rgba(0,123,255,.25)"
+ }
+ }
+})(InputBase);
diff --git a/frontend/src/devicelist/TabPanel.jsx b/frontend/src/devicelist/TabPanel.jsx
deleted file mode 100644
index 6ef28b085..000000000
--- a/frontend/src/devicelist/TabPanel.jsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import React from "react";
-import { Box, Typography } from "@material-ui/core";
-import PropTypes from "prop-types";
-
-export default function TabPanel(props) {
- const { children, value, index } = props;
-
- return (
-
- {value === index && (
-
- {children}
-
- )}
-
- );
-}
-TabPanel.propTypes = {
- children: PropTypes.element.isRequired,
- value: PropTypes.number.isRequired,
- index: PropTypes.number.isRequired
-};
diff --git a/frontend/src/devicelist/__tests__/DeviceListPage.test.jsx b/frontend/src/devicelist/__tests__/DeviceListPage.test.jsx
index e09bc3552..af6e7ea32 100644
--- a/frontend/src/devicelist/__tests__/DeviceListPage.test.jsx
+++ b/frontend/src/devicelist/__tests__/DeviceListPage.test.jsx
@@ -5,7 +5,7 @@ import { beforeEach, describe, expect } from "@jest/globals";
import DeviceListPage from "../DeviceListPage";
import Page from "../../general/Page";
-import DeviceListTabTable from "../DeviceListTabTable";
+import DeviceListPageContents from "../DeviceListPageContents";
Enzyme.configure({ adapter: new Adapter() });
@@ -30,8 +30,8 @@ describe(" functional component", () => {
expect(page.props().breadcrumbs).toStrictEqual(expectedBreadcumbs);
expect(page.props().deviceList).toBe(true);
});
- it("Contains 1 component", () => {
- expect(wrapper.find(DeviceListTabTable)).toHaveLength(1);
+ it("Contains 1 component", () => {
+ expect(wrapper.find(DeviceListPageContents)).toHaveLength(1);
});
});
});
diff --git a/frontend/src/devicelist/__tests__/DeviceListPageContents.test.jsx b/frontend/src/devicelist/__tests__/DeviceListPageContents.test.jsx
new file mode 100644
index 000000000..f9c183eff
--- /dev/null
+++ b/frontend/src/devicelist/__tests__/DeviceListPageContents.test.jsx
@@ -0,0 +1,197 @@
+import React from "react";
+import Enzyme from "enzyme";
+import Adapter from "enzyme-adapter-react-16";
+import {
+ afterEach,
+ beforeEach,
+ describe,
+ expect,
+ jest,
+ it
+} from "@jest/globals";
+
+import DeviceListPageContents from "../DeviceListPageContents";
+import DeviceTableTitle from "../DeviceTableTitle";
+import DevicesTable from "../DevicesTable";
+
+import * as DeviceApi from "../../api/DeviceApi";
+import DeviceInfo from "../../model/DeviceInfo";
+
+Enzyme.configure({ adapter: new Adapter() });
+
+describe(" class component", () => {
+ let wrapper;
+ const dummySenders = [new DeviceInfo("Beep")];
+ const dummyReceivers = [new DeviceInfo("boop!")];
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ wrapper.unmount();
+ });
+
+ describe("componentDidMount() function", () => {
+ it("calls DeviceApi.getSenders() and getReceivers()", () => {
+ jest.spyOn(DeviceApi, "getSenders").mockImplementation(() => {
+ return dummySenders;
+ });
+ jest.spyOn(DeviceApi, "getReceivers").mockImplementation(() => {
+ return dummyReceivers;
+ });
+ wrapper = Enzyme.shallow(, {
+ disableLifecycleMethods: true
+ });
+ wrapper.instance().componentDidMount();
+
+ expect(DeviceApi.getSenders).toHaveBeenCalledWith(
+ wrapper.instance().handleSendersChange
+ );
+ expect(DeviceApi.getReceivers).toHaveBeenCalledWith(
+ wrapper.instance().handleReceiversChange
+ );
+ });
+ });
+ describe("handleChange()", () => {
+ beforeEach(() => {
+ wrapper = Enzyme.shallow();
+ });
+ it("should set the state", () => {
+ const startingState = {
+ selected: 0,
+ senders: [],
+ receivers: []
+ };
+
+ const expectedValue = 5;
+
+ wrapper.setState(startingState);
+
+ wrapper.instance().handleChange(expectedValue);
+ expect(wrapper.state().selected).toStrictEqual(expectedValue);
+ });
+ });
+ describe("handleSendersChange()", () => {
+ beforeEach(() => {
+ wrapper = Enzyme.shallow();
+ });
+ it("should set the state", () => {
+ const startingState = {
+ selected: 0,
+ senders: [],
+ receivers: []
+ };
+
+ const expectedValue = [new DeviceInfo("thing!")];
+
+ wrapper.setState(startingState);
+
+ wrapper.instance().handleSendersChange(expectedValue);
+ expect(wrapper.state().senders).toStrictEqual(expectedValue);
+ });
+ });
+ describe("handleReceiversChange()", () => {
+ beforeEach(() => {
+ wrapper = Enzyme.shallow();
+ });
+ it("should set the state", () => {
+ const startingState = {
+ selected: 0,
+ senders: [],
+ receivers: []
+ };
+
+ const expectedValue = [new DeviceInfo("thing!")];
+
+ wrapper.setState(startingState);
+
+ wrapper.instance().handleReceiversChange(expectedValue);
+ expect(wrapper.state().receivers).toStrictEqual(expectedValue);
+ });
+ });
+ describe("getDevices() function", () => {
+ beforeEach(() => {
+ wrapper = Enzyme.shallow();
+ });
+ it("if state.selected == 0, returns and array that combines state.senders and state.receivers ", () => {
+ const someState = {
+ selected: 0,
+ senders: dummySenders,
+ receivers: dummyReceivers
+ };
+ wrapper.setState(someState);
+ const result = wrapper.instance().getDevices();
+
+ expect(result).toStrictEqual(
+ someState.senders.concat(someState.receivers)
+ );
+ });
+ it("if state.selected == 1, returns state.senders ", () => {
+ const someState = {
+ selected: 1,
+ senders: dummySenders,
+ receivers: dummyReceivers
+ };
+ wrapper.setState(someState);
+ const result = wrapper.instance().getDevices();
+
+ expect(result).toStrictEqual(someState.senders);
+ });
+ it("if state.selected == 2, returns state.receivers ", () => {
+ const someState = {
+ selected: 2,
+ senders: dummySenders,
+ receivers: dummyReceivers
+ };
+ wrapper.setState(someState);
+ const result = wrapper.instance().getDevices();
+
+ expect(result).toStrictEqual(someState.receivers);
+ });
+ });
+ describe("getTitle() function", () => {
+ it("returns a component with expected props", () => {
+ wrapper = Enzyme.shallow();
+ const expected = {
+ index: wrapper.state().selected,
+ handleChange: wrapper.instance().handleChange
+ };
+ const deviceTableTitle = wrapper.instance().getTitle();
+ expect(deviceTableTitle.type).toEqual(DeviceTableTitle);
+
+ const wrap = Enzyme.shallow(deviceTableTitle);
+
+ const { props } = wrap.instance();
+ expect(props.index).toBe(expected.index);
+ expect(props.handleChange).toStrictEqual(expected.handleChange);
+ });
+ });
+ describe("render() function", () => {
+ beforeEach(() => {
+ wrapper = Enzyme.shallow();
+ });
+ describe("returns a component that", () => {
+ it("Contains 1 component with expected props", () => {
+ const someState = {
+ selected: 0,
+ senders: dummySenders,
+ receivers: dummyReceivers
+ };
+ wrapper.setState(someState);
+
+ const component = wrapper.find(DevicesTable);
+ expect(component).toHaveLength(1);
+ const expected = {
+ devices: dummySenders.concat(dummyReceivers),
+ title: (
+
+ )
+ };
+ const props = component.at(0).props();
+ expect(props.devices).toStrictEqual(expected.devices);
+ expect(props.title).toStrictEqual(expected.title);
+ });
+ });
+ });
+});
diff --git a/frontend/src/devicelist/__tests__/DeviceListTabTable.test.jsx b/frontend/src/devicelist/__tests__/DeviceListTabTable.test.jsx
deleted file mode 100644
index ba82afe6c..000000000
--- a/frontend/src/devicelist/__tests__/DeviceListTabTable.test.jsx
+++ /dev/null
@@ -1,108 +0,0 @@
-import React from "react";
-import Enzyme from "enzyme";
-import Adapter from "enzyme-adapter-react-16";
-import { beforeEach, describe, expect, it } from "@jest/globals";
-import { Box } from "@material-ui/core";
-import DeviceListTabTable from "../DeviceListTabTable";
-import TabPanel from "../TabPanel";
-import DevicesTable from "../DevicesTable";
-import VerticalTabs from "../../general/VerticalTabs";
-
-Enzyme.configure({ adapter: new Adapter() });
-
-describe(" class component", () => {
- let wrapper;
- const mockGetSenders = jest.fn();
- const mockGetReceivers = jest.fn();
- const dummySource = {
- getSenders: mockGetSenders,
- getReceivers: mockGetReceivers
- };
-
- const dummyClass = { tabs: "SomeClass" };
- beforeEach(() => {
- wrapper = Enzyme.shallow(
-
- );
- });
- afterEach(() => {
- jest.clearAllMocks();
- });
-
- describe("render() function returns a component that", () => {
- it("Contains one component with expected props", () => {
- expect(wrapper.find(Box)).toHaveLength(1);
- // TODO: validate props
- // const box = wrapper.find(Box).first();
- });
- it("Contains one component with expected props", () => {
- expect(wrapper.find(VerticalTabs)).toHaveLength(1);
- // TODO: validate props
- // const verticalTabs = wrapper.find(VerticalTabs).first();
- });
- it("Contains two components", () => {
- expect(wrapper.find(TabPanel)).toHaveLength(2);
- });
- it("First has expected props", () => {
- const panelProps = wrapper.find(TabPanel).first().props();
- const expectedIndex = 0;
- const expectedclassName = "lightGreyBorder";
-
- expect(panelProps.value).toBe(wrapper.instance().state.value);
- expect(panelProps.index).toBe(expectedIndex);
- expect(panelProps.className).toBe(expectedclassName);
- expect(panelProps.children.type.name).toBe("DevicesTable");
- });
- it("Second has expected props", () => {
- const panelProps = wrapper.find(TabPanel).last().props();
- const expectedIndex = 1;
- const expectedclassName = "lightGreyBorder";
-
- expect(panelProps.value).toBe(wrapper.instance().state.value);
- expect(panelProps.index).toBe(expectedIndex);
- expect(panelProps.className).toBe(expectedclassName);
- expect(panelProps.children.type.name).toBe("DevicesTable");
- });
- it("Contains two components", () => {
- expect(wrapper.find(DevicesTable)).toHaveLength(2);
- });
- it("First has expected props", () => {
- const deviceTableProps = wrapper.find(DevicesTable).first().props();
- const expectedTitle = "List of Senders";
-
- expect(deviceTableProps.devices).toBe(wrapper.instance().state.senders);
- expect(deviceTableProps.title).toBe(expectedTitle);
- });
- it("Second has expected props", () => {
- const DeviceTableProps = wrapper.find(DevicesTable).last().props();
- const expectedTitle = "List of Receivers";
-
- expect(DeviceTableProps.devices).toBe(wrapper.instance().state.receivers);
- expect(DeviceTableProps.title).toBe(expectedTitle);
- });
- });
- it("handleValueChange() function sets state.value", () => {
- const someValue = 57;
- wrapper.instance().handleValueChange(someValue);
- expect(wrapper.instance().state.value).toBe(someValue);
- });
- it("handleSendersChange() function sets state.senders", () => {
- const someValue = [null];
- wrapper.instance().handleSendersChange(someValue);
- expect(wrapper.instance().state.senders).toStrictEqual(someValue);
- });
- it("handleReceiversChange() function sets state.receivers", () => {
- const someValue = [null];
- wrapper.instance().handleReceiversChange(someValue);
- expect(wrapper.instance().state.receivers).toStrictEqual(someValue);
- });
- it("componentDidMount() function calls functions in the passed datasource", () => {
- wrapper.instance().componentDidMount();
- expect(mockGetSenders).toBeCalledWith(
- wrapper.instance().handleSendersChange
- );
- expect(mockGetReceivers).toBeCalledWith(
- wrapper.instance().handleReceiversChange
- );
- });
-});
diff --git a/frontend/src/devicelist/__tests__/DeviceTableTitle.test.jsx b/frontend/src/devicelist/__tests__/DeviceTableTitle.test.jsx
new file mode 100644
index 000000000..98a8ef973
--- /dev/null
+++ b/frontend/src/devicelist/__tests__/DeviceTableTitle.test.jsx
@@ -0,0 +1,104 @@
+import React from "react";
+import Enzyme from "enzyme";
+import Adapter from "enzyme-adapter-react-16";
+import {
+ afterEach,
+ beforeEach,
+ describe,
+ expect,
+ jest,
+ it
+} from "@jest/globals";
+import { MenuItem, Select } from "@material-ui/core";
+
+import DeviceTableTitle from "../DeviceTableTitle";
+import StyledInput from "../StyledInput";
+
+Enzyme.configure({ adapter: new Adapter() });
+
+describe(" class component", () => {
+ let wrapper;
+ const dummyIndex = 4;
+ const mockHandleChange = jest.fn();
+
+ beforeEach(() => {
+ wrapper = Enzyme.shallow(
+
+ );
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ wrapper.unmount();
+ });
+ describe("render() function", () => {
+ describe("returns a component that", () => {
+ it("Contains 1 component with expected props", () => {
+ const component = wrapper.find(Select);
+ expect(component).toHaveLength(1);
+
+ const props = component.at(0).props();
+ const expected = {
+ id: "device-table-title-select",
+ value: dummyIndex,
+ onChange: wrapper.instance().handleChange,
+ input:
+ };
+ expect(props.id).toBe(expected.id);
+ expect(props.value).toBe(expected.value);
+ expect(props.onChange).toBe(expected.onChange);
+ expect(props.input).toStrictEqual(expected.input);
+ });
+ it("Contains 3 components", () => {
+ const component = wrapper.find(MenuItem);
+ expect(component).toHaveLength(3);
+ });
+ it(" 0 has expected props", () => {
+ const component = wrapper.find(MenuItem);
+
+ const props = component.at(0).props();
+ const expected = {
+ value: 0,
+ text: "All Devices"
+ };
+ expect(props.value).toBe(expected.value);
+ expect(props.children).toBe(expected.text);
+ });
+ it(" 1 has expected props", () => {
+ const component = wrapper.find(MenuItem);
+
+ const props = component.at(1).props();
+ const expected = {
+ value: 1,
+ text: "Senders"
+ };
+ expect(props.value).toBe(expected.value);
+ expect(props.children).toBe(expected.text);
+ });
+ it(" 2 has expected props", () => {
+ const component = wrapper.find(MenuItem);
+
+ const props = component.at(2).props();
+ const expected = {
+ value: 2,
+ text: "Receivers"
+ };
+ expect(props.value).toBe(expected.value);
+ expect(props.children).toBe(expected.text);
+ });
+ });
+ });
+
+ describe("handleChange() function", () => {
+ it("calls passed handleChange callback with expected args", () => {
+ const expectedArg = 4;
+ const dummyEvent = {
+ target: {
+ value: expectedArg
+ }
+ };
+ wrapper.instance().handleChange(dummyEvent);
+ expect(mockHandleChange).toBeCalledWith(expectedArg);
+ });
+ });
+});
diff --git a/frontend/src/general/VerticalTabs.jsx b/frontend/src/general/VerticalTabs.jsx
deleted file mode 100644
index 6025611ba..000000000
--- a/frontend/src/general/VerticalTabs.jsx
+++ /dev/null
@@ -1,55 +0,0 @@
-import React from "react";
-import { Tab, Tabs } from "@material-ui/core";
-import PropTypes from "prop-types";
-
-export default class VerticalTabs extends React.Component {
- constructor(props) {
- super(props);
- this.handleChange = this.handleChange.bind(this);
- }
-
- handleChange(event, newTab) {
- const { setValue } = this.props;
- setValue(newTab);
- }
-
- render() {
- const {
- value,
- classes: { tabs }
- } = this.props;
-
- return (
- <>
-
-
-
-
- >
- );
- }
-}
-
-VerticalTabs.propTypes = {
- setValue: PropTypes.func.isRequired,
- value: PropTypes.number.isRequired,
- classes: PropTypes.shape({
- tabs: PropTypes.string
- }).isRequired
-};