Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DataTable #4

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions src/actions/datatable-actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { DATATABLE_FETCH_ROWS_SUCCESS, DATATABLE_FETCH_ROWS_ERROR, DATATABLE_FETCH_COLUMNS_SUCCESS } from '../constants/action-types';
import axios from 'axios';
import { getRequestUrl, getColumns } from '../utilities/datatable-utilities';

const fetchDatatableRowsSuccess = (data, type) => {
return {
type: DATATABLE_FETCH_ROWS_SUCCESS,
payload: data,
data_type: type
};
};

const fetchDatatableRowsError = (error, type) => {
return {
type: DATATABLE_FETCH_ROWS_ERROR,
payload: error,
data_type: type
};
};

export const fetchDatatableData = (type) => {
const url = getRequestUrl(type);
const request = axios.get(url);
return dispatch => request.then(
(data) => dispatch(fetchDatatableRowsSuccess(data, type)),
(error) => dispatch(fetchDatatableRowsError(error, type)),
);
};

const fetchColumnsSuccess = (data, type) => {
return {
type: DATATABLE_FETCH_COLUMNS_SUCCESS,
payload: data,
data_type: type
};
};

export const fetchDatatableColumns = (type) => {
return dispatch => dispatch(fetchColumnsSuccess(getColumns(type), type));
};
46 changes: 46 additions & 0 deletions src/components/shared/data-table/data-table.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React from "react";
import { shallow } from "enzyme";
import { expect } from "chai";
import { PureDataTable } from "./index";

describe("<DataTable />", () => {
const props = {
rows: {
test: [
{ species: "Bird People", name: "Bridperson", origin: "Bird World" },
{ species: "Plutonian", name: "Snowville", origin: "Pluto" },
{ species: "Zigerion", name: "Prince Nebulon", origin: "Spaceship" }
]
},
columns: {
test: [
{ label: "Species", key: "species", visible: true },
{ label: "Name", key: "name", visible: false },
{ label: "Origin", key: "origin", visible: true }
]
},
dataType: "test",
fetchDatatableData: () => {},
fetchDatatableColumns: () => {}
};

it("should have correct labels on columns", () => {
const wrapper = shallow(<PureDataTable {...props} />);
const expected = "Origin";
const actual = wrapper.find("TableHeaderColumn").at(1).props().children;
expect(actual).equals(expected);
});

it("should have correct row order and display correct data", () => {
const wrapper = shallow(<PureDataTable {...props} />);
const expected = "Spaceship";
const actual = wrapper
.find("TableBody")
.find("TableRow")
.at(2)
.find("TableRowColumn")
.at(1)
.props().children;
expect(actual).equals(expected);
});
});
77 changes: 77 additions & 0 deletions src/components/shared/data-table/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import React, { Component, PropTypes } from "react";
import {
Table,
TableBody,
TableFooter,
TableHeader,
TableRow
} from "material-ui/Table";
import {
generateColumns,
generateRows
} from "../../../utilities/datatable-utilities";
import { dataTableStyles } from "../../../constants/mui-theme";

import {
fetchDatatableData,
fetchDatatableColumns
} from "../../../actions/datatable-actions";
import { connect } from "react-redux";

class DataTable extends Component {
constructor(props) {
super(props);
this.state = {};
}

componentWillMount() {
const { dataType } = this.props;
this.props.fetchDatatableData(dataType);
this.props.fetchDatatableColumns(dataType);
}

render() {
const rows = this.props.rows[this.props.dataType];
const cols = this.props.columns[this.props.dataType];
return (
<Table {...this.props.tableStyles}>
<TableHeader>
<TableRow>
{generateColumns(cols)}
</TableRow>
</TableHeader>
<TableBody>
{generateRows(cols, rows)}
</TableBody>
<TableFooter />

</Table>
);
}
}

DataTable.propTypes = {
dataType: PropTypes.string,
tableStyles: PropTypes.object,
rows: PropTypes.object,
columns: PropTypes.object,
fetchDatatableData: PropTypes.func,
fetchDatatableColumns: PropTypes.func
};

DataTable.defaultProps = {
tableStyles: dataTableStyles
};

const mapStateToProps = state => {
const { dataTables } = state;
return dataTables;
};

export default connect(mapStateToProps, {
fetchDatatableData,
fetchDatatableColumns
})(DataTable);


export { DataTable as PureDataTable};
6 changes: 6 additions & 0 deletions src/constants/action-types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

export const DATATABLE_FETCH_ROWS_SUCCESS = 'DATATABLE_FETCH_ROWS_SUCCESS';
export const DATATABLE_FETCH_ROWS_ERROR = 'DATATABLE_FETCH_ROWS_ERROR';
export const DATATABLE_FETCH_COLUMNS_SUCCESS = 'DATATABLE_FETCH_COLUMNS_SUCCESS';
export const DATATABLE_FETCH_COLUMNS_ERROR = 'DATATABLE_FETCH_COLUMNS_ERROR';

3 changes: 2 additions & 1 deletion src/constants/endpoints.js
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
const BASE_URL = '';
export const BASE_URL = 'http://localhost:8000/api';
export const FORMAT = '?format=json';
33 changes: 33 additions & 0 deletions src/reducers/datatable-reducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {
DATATABLE_FETCH_ROWS_SUCCESS,
DATATABLE_FETCH_ROWS_ERROR,
DATATABLE_FETCH_COLUMNS_SUCCESS
} from "../constants/action-types";

const initialState = {
rows: {},
filters: {},
columns: {},
errors: []
};

const datatableReducer = (state = initialState, action) => {
switch (action.type) {
case DATATABLE_FETCH_ROWS_SUCCESS: {
const rows = { ...state.rows };
rows[action.data_type] = action.payload.data.results;
return { ...state, rows };
}
case DATATABLE_FETCH_ROWS_ERROR:
return { ...state, errors: [...state.errors, action.payload] };
case DATATABLE_FETCH_COLUMNS_SUCCESS: {
const columns = { ...state.columns };
columns[action.data_type] = action.payload;
return { ...state, columns };
}
default:
return state;
}
};

export default datatableReducer;
152 changes: 152 additions & 0 deletions src/reducers/datatable-reducer.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import { expect } from "chai";
import {
DATATABLE_FETCH_ROWS_SUCCESS,
DATATABLE_FETCH_ROWS_ERROR,
DATATABLE_FETCH_COLUMNS_SUCCESS
} from "../constants/action-types";
import reducer from "./datatable-reducer";

describe("Reducer - DataTable", () => {
const getInitState = () => {
return {
rows: {
zigerions: [{ id: 0, name: "Prince Nebulon" }]
},
filters: {},
columns: {
zigerions: [{ label: "Race", key: "race" }]
},
errors: []
};
};

it("should set initial state by default", () => {
const action = {
type: "UNKNOWN"
};
const expected = getInitState();
const actual = reducer(getInitState(), action);

expect(actual).deep.equals(expected);
});

describe("when DATATABLE_FETCH_ROWS_SUCCESS action is dispatched", () => {
it("should create new attribute with key of `data_type` in `data` state if doesn't exist", () => {
const action = {
type: DATATABLE_FETCH_ROWS_SUCCESS,
payload: successRowsResponse,
data_type: "plutonians"
};

const expected = "plutonians";
const actual = reducer(getInitState(), action).rows;

expect(actual).to.have.property(expected);
});

it("should populate the data type attribute of `data` state with response resulats", () => {
const action = {
type: DATATABLE_FETCH_ROWS_SUCCESS,
payload: successRowsResponse,
data_type: "plutonians"
};

const expected = {
rows: {
zigerions: [{ id: 0, name: "Prince Nebulon" }],
plutonians: [
{ id: 0, name: "King Flippy Nips" },
{ id: 1, name: "Scroopy Noopers" }
]
},
filters: {},
columns: {
zigerions: [{ label: "Race", key: "race" }]
},
errors: []
};

const actual = reducer(getInitState(), action);

expect(actual).deep.equals(expected);
});
});

describe("when DATATABLE_FETCH_ROWS_ERROR action is dispatched", () => {
it("should add the error message to `errors` state array", () => {
const action = {
type: DATATABLE_FETCH_ROWS_ERROR,
payload: errorRowsResponse
};

const actual = reducer(getInitState(), action).errors;
const expected = {
message: "error"
};
expect(actual).to.include(expected);
});
});

describe("when DATATABLE_FETCH_COLUMNS_SUCCESS action is dispatched", () => {
it("should create new attribute with key of `data_type` in `columns` state if doesn't exist", () => {
const action = {
type: DATATABLE_FETCH_COLUMNS_SUCCESS,
payload: successRowsResponse,
data_type: "plutonians"
};

const expected = "plutonians";
const actual = reducer(getInitState(), action).columns;

expect(actual).to.have.property(expected);
});

it("should populate the data type attribute of `columns` state with response results", () => {
const action = {
type: DATATABLE_FETCH_COLUMNS_SUCCESS,
payload: successColumnsResponse,
data_type: "plutonians"
};

const expected = {
rows: {
zigerions: [{ id: 0, name: "Prince Nebulon" }]
},
filters: {},
columns: {
zigerions: [{ label: "Race", key: "race" }],
plutonians: [
{ label: "Species", key: "species", visible: true },
{ label: "Name", key: "name", visible: false },
{ label: "Origin", key: "origin", visible: true }
]
},
errors: []
};

const actual = reducer(getInitState(), action);

expect(actual).deep.equals(expected);
});
});
});

const successRowsResponse = {
data: {
count: 2,
results: [
{ id: 0, name: "King Flippy Nips" },
{ id: 1, name: "Scroopy Noopers" }
]
}
};

const errorRowsResponse = {
message: "error"
};

const successColumnsResponse = [
{ label: "Species", key: "species", visible: true },
{ label: "Name", key: "name", visible: false },
{ label: "Origin", key: "origin", visible: true }
];
4 changes: 3 additions & 1 deletion src/reducers/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// Set up your root reducer here...
import { combineReducers } from 'redux';
import { routerReducer } from "react-router-redux";
import datatablesReducer from './datatable-reducer';

const rootReducer = combineReducers({
routing: routerReducer
routing: routerReducer,
dataTables: datatablesReducer
});

export default rootReducer;
Loading