├── .npmignore ├── index.js ├── .babelrc ├── .gitignore ├── src ├── index.js ├── utils │ ├── handleHtmlProp.js │ ├── injectProp.js │ ├── handleCustomRender.js │ ├── search.js │ └── paginate.js ├── __tests__ │ └── utils │ │ ├── utilities.js │ │ ├── injectProp.spec.js │ │ ├── handleHtmlProp.spec.js │ │ ├── handleCustomRender.spec.js │ │ ├── search.spec.js │ │ └── paginate.spec.js └── lib │ └── mui-data-table.js ├── .eslintrc.js ├── LICENSE.md ├── package.json └── README.md /.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | coverage 3 | .coveralls.yml 4 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./dist/index'); 2 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-2", "react"] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | npm-debug.log 5 | .coveralls.yml 6 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import MuiDataTable from './lib/mui-data-table'; 2 | 3 | export { MuiDataTable }; 4 | -------------------------------------------------------------------------------- /src/utils/handleHtmlProp.js: -------------------------------------------------------------------------------- 1 | const hasHtml = (prop, arr) => ( 2 | !!(arr.filter((item) => item.property === prop)[0].html) 3 | ); 4 | 5 | const extractHtml = (prop, arr) => ( 6 | arr.filter((item) => item.property === prop)[0].html 7 | ); 8 | 9 | export { extractHtml, hasHtml }; 10 | -------------------------------------------------------------------------------- /src/utils/injectProp.js: -------------------------------------------------------------------------------- 1 | const injectProp = (arr) => { 2 | let count = 0, res = arr.slice(0); 3 | 4 | res.forEach((obj) => { 5 | if (!obj.property) { 6 | count += 1; 7 | obj.property = 'MuiDataTableProp-' + count; 8 | } 9 | }); 10 | 11 | return res; 12 | }; 13 | 14 | export default injectProp; 15 | -------------------------------------------------------------------------------- /src/__tests__/utils/utilities.js: -------------------------------------------------------------------------------- 1 | export const objCopy = (arr) => { 2 | const res = []; 3 | 4 | arr.forEach(function (item) { 5 | if (typeof item === 'object') res.push(Object.assign({}, item, {})); 6 | }); 7 | 8 | return res; 9 | }; 10 | 11 | export const lastElem = (arr) => { 12 | return arr[arr.length - 1]; 13 | }; 14 | -------------------------------------------------------------------------------- /src/utils/handleCustomRender.js: -------------------------------------------------------------------------------- 1 | const hasCustomRender = (prop, columns) => ( 2 | !!columns.filter((item) => item.property === prop && item.hasOwnProperty('renderAs'))[0] 3 | ); 4 | 5 | const callCustomRender = (prop, columns, obj) => { 6 | const property = columns.filter((item) => ( 7 | item.property === prop && item.hasOwnProperty('renderAs')) 8 | )[0]; 9 | 10 | return property.renderAs(obj); 11 | }; 12 | 13 | export { hasCustomRender, callCustomRender }; 14 | -------------------------------------------------------------------------------- /src/utils/search.js: -------------------------------------------------------------------------------- 1 | const search = (key, word, data) => { 2 | if (word.length < 1) return data; 3 | 4 | const res = []; 5 | const regex = new RegExp(word, 'i'); 6 | const keys = key.split('|'); 7 | 8 | data.forEach((item) => { 9 | for (let i = 0; i < keys.length; i += 1) { 10 | if ( String(item[keys[i]]).match(regex) ) { 11 | res.push(item); 12 | break; 13 | } 14 | } 15 | 16 | }); 17 | 18 | return res; 19 | }; 20 | 21 | export default search; 22 | -------------------------------------------------------------------------------- /src/__tests__/utils/injectProp.spec.js: -------------------------------------------------------------------------------- 1 | import chai from 'chai'; 2 | import injectProp from '../../utils/injectProp'; 3 | 4 | const expect = chai.expect; 5 | const data = [ 6 | {name: 'ade', location: 'Lagos', age: 21, mood: 'happy', property: 'id'}, 7 | {name: 'tunde', location: 'Edo', age: 25, mood: 'angry'}, 8 | {name: 'bayo', location: 'Ibadan', age: 28, mood: 'scared'} 9 | ]; 10 | 11 | describe('The injectProp function', function () { 12 | it('should return a new array containing custom property attribute if it is not set in the config', function () { 13 | expect(injectProp(data)).to.eql([ 14 | {name: 'ade', location: 'Lagos', age: 21, mood: 'happy', property: 'id'}, 15 | {name: 'tunde', location: 'Edo', age: 25, mood: 'angry', property: 'MuiDataTableProp-1'}, 16 | {name: 'bayo', location: 'Ibadan', age: 28, mood: 'scared', property: 'MuiDataTableProp-2'} 17 | ]); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "browser": true, 4 | "commonjs": true, 5 | "es6": true 6 | }, 7 | "extends": ["eslint:recommended", "plugin:react/recommended"], 8 | "parserOptions": { 9 | "ecmaFeatures": { 10 | "experimentalObjectRestSpread": true, 11 | "jsx": true 12 | }, 13 | "sourceType": "module" 14 | }, 15 | "plugins": [ 16 | "react" 17 | ], 18 | "rules": { 19 | "indent": [ 20 | "error", 21 | 2 22 | ], 23 | "linebreak-style": [ 24 | "error", 25 | "unix" 26 | ], 27 | "quotes": [ 28 | "error", 29 | "single" 30 | ], 31 | "semi": [ 32 | "error", 33 | "always" 34 | ] 35 | }, 36 | "globals": { 37 | "describe": 0, 38 | "it": 0 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /src/__tests__/utils/handleHtmlProp.spec.js: -------------------------------------------------------------------------------- 1 | import chai from 'chai'; 2 | import { extractHtml, hasHtml } from '../../utils/handleHtmlProp'; 3 | import { objCopy } from './utilities'; 4 | 5 | const expect = chai.expect; 6 | 7 | const data = [{ property: 'client_name', title: 'Client', html: '' }]; 8 | 9 | describe('The html utility functions', function () { 10 | describe('#hasHtml', function () { 11 | it('should return true if the property in the object array contains an html property', function () { 12 | expect(hasHtml('client_name', data)).to.be.true; 13 | }); 14 | 15 | it('should return false if the property does not contain an html property', function () { 16 | const clone = objCopy(data); 17 | delete clone[0].html; 18 | 19 | expect(hasHtml('client_name', clone)).to.be.false; 20 | }); 21 | }); 22 | 23 | describe('#extractHtml', function () { 24 | it('should extract and return the value of the html property', function () { 25 | 26 | expect(extractHtml('client_name', data)).to.equal(''); 27 | }); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Daniel Chinedu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /src/__tests__/utils/handleCustomRender.spec.js: -------------------------------------------------------------------------------- 1 | import chai from 'chai'; 2 | import { hasCustomRender, callCustomRender } from '../../utils/handleCustomRender'; 3 | import { objCopy } from './utilities'; 4 | 5 | const expect = chai.expect; 6 | 7 | const data = [{ property: 'client_name', title: 'Client', renderAs: function (data) { 8 | return data.message; 9 | } }]; 10 | 11 | describe('The render utility functions', function () { 12 | describe('#hasCustomRender', function () { 13 | it('should return true if the property in the object array contains a renderAs property', function () { 14 | expect(hasCustomRender('client_name', data)).to.be.true; 15 | }); 16 | 17 | it('should return false if the property does not contain an html property', function () { 18 | const clone = objCopy(data); 19 | delete clone[0].renderAs; 20 | 21 | expect(hasCustomRender('client_name', clone)).to.be.false; 22 | }); 23 | }); 24 | 25 | describe('#callCustomRender', function () { 26 | it('should call the callback function and return it\'s result', function () { 27 | 28 | expect(callCustomRender('client_name', data, {message: 'hello world'})).to.equal('hello world'); 29 | }); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /src/__tests__/utils/search.spec.js: -------------------------------------------------------------------------------- 1 | import chai from 'chai'; 2 | import search from '../../utils/search'; 3 | 4 | const expect = chai.expect; 5 | const data = [ 6 | {name: 'ade', location: 'Lagos', age: 21, mood: 'happy'}, 7 | {name: 'tunde', location: 'Edo', age: 25, mood: 'angry'}, 8 | {name: 'bayo', location: 'Ibadan', age: 28, mood: 'scared'} 9 | ]; 10 | 11 | describe('The search function', function () { 12 | it('should return the original array if the word being searched for is empty', function () { 13 | expect(search('name', '', data)).to.eql(data); 14 | }); 15 | 16 | it('should return an empty array if the word isn\'t found in the list', function () { 17 | expect(search('name', 'damilare', data)).to.eql([]); 18 | }); 19 | 20 | it('should return the approriate result that match the word if found in the array', function () { 21 | expect(search('name', 'ade', data)).to.eql([data[0]]); 22 | }); 23 | 24 | it('should return results when there is a partial match', function () { 25 | expect(search('name', 'a', data)).to.eql([data[0], data[2]]); 26 | }); 27 | 28 | it('should be able to search using multiple keys', function () { 29 | expect(search('location|name|mood', 'angry', data)).to.eql([data[1]]); 30 | expect(search('location|name|mood', 'Lagos', data)).to.eql([data[0]]); 31 | expect(search('location|name|mood|age', '28', data)).to.eql([data[2]]); 32 | }); 33 | 34 | it('should perform case insensitive search(es)', function () { 35 | expect(search('name', 'TUNDE', data)).to.eql([data[1]]); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /src/utils/paginate.js: -------------------------------------------------------------------------------- 1 | class Paginate { 2 | constructor(arr) { 3 | this.arr = arr; 4 | } 5 | 6 | showingCalc(arr) { 7 | if (arr.length <= 1) return `1 - ${arr[0]}`; 8 | 9 | const start = (arr.slice(0, arr.length - 1).reduce((curr, next) => curr + next)) + 1; 10 | const stop = arr.reduce((curr, next) => curr + next); 11 | 12 | return `${start} - ${stop}`; 13 | } 14 | 15 | perPage(n = 5) { 16 | const clone = Array.from(this.arr); 17 | const totalNumOfPages = Math.ceil(this.arr.length / n); 18 | const total = clone.length; 19 | const currentlyShowing = []; 20 | const res = []; 21 | 22 | let count = 0; 23 | let temp = null; 24 | 25 | while (clone.length > 0) { 26 | count += 1; 27 | 28 | temp = clone.splice(0, n); 29 | currentlyShowing.push(temp.length); 30 | 31 | temp.push({ 32 | paginationInfo: { 33 | currentPage: count, 34 | nextPage: (count === totalNumOfPages ? null : (count + 1)), 35 | previousPage: ((count - 1) === 0 ? null : (count - 1)), 36 | currentlyShowing: `${this.showingCalc(currentlyShowing)} of ${total}`, 37 | isLastPage: (count === totalNumOfPages), 38 | totalNumOfPages, 39 | total 40 | } 41 | }); 42 | 43 | res.push(temp); 44 | } 45 | 46 | return new Paginate(res); 47 | } 48 | 49 | page(n) { 50 | const requestedPage = this.arr[n - 1]; 51 | const lastPage = this.arr[this.arr.length - 1]; 52 | 53 | return requestedPage || lastPage || []; 54 | } 55 | } 56 | 57 | export default Paginate; 58 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mui-data-table", 3 | "version": "0.1.7", 4 | "description": "Data table for react material-ui", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/andela-cdaniel/mui-data-table" 8 | }, 9 | "author": "Daniel Chinedu", 10 | "license": "MIT", 11 | "bugs": { 12 | "url": "https://github.com/andela-cdaniel/mui-data-table/issues" 13 | }, 14 | "homepage": "https://github.com/andela-cdaniel/mui-data-table", 15 | "keywords": [ 16 | "react", 17 | "react material-ui data table", 18 | "react material-ui", 19 | "react data table", 20 | "material ui", 21 | "material-ui data table" 22 | ], 23 | "scripts": { 24 | "prepublish": "babel src --ignore __tests__ --out-dir ./dist", 25 | "lint": "eslint ./src", 26 | "lintfix": "eslint ./src --fix", 27 | "test": "mocha --require babel-register src/__tests__/**/*.js", 28 | "test:coverage": "istanbul cover _mocha -- --require babel-register src/__tests__/**/*.js && istanbul-coveralls" 29 | }, 30 | "devDependencies": { 31 | "babel-cli": "^6.6.4", 32 | "babel-core": "^6.7.4", 33 | "babel-eslint": "^6.0.2", 34 | "babel-plugin-transform-es2015-modules-umd": "^6.6.5", 35 | "babel-polyfill": "^6.7.4", 36 | "babel-preset-es2015": "^6.6.0", 37 | "babel-preset-react": "^6.5.0", 38 | "babel-preset-stage-2": "^6.5.0", 39 | "babel-register": "^6.11.6", 40 | "chai": "^3.5.0", 41 | "enzyme": "^2.2.0", 42 | "eslint": "^2.7.0", 43 | "eslint-plugin-babel": "^3.1.0", 44 | "eslint-plugin-react": "^4.2.3", 45 | "istanbul": "1.0.0-alpha.2", 46 | "istanbul-coveralls": "^1.0.3", 47 | "jsdom": "^8.1.0", 48 | "mocha": "^2.4.5", 49 | "nodemon": "^1.9.1", 50 | "react": "^15.0.0", 51 | "react-addons-test-utils": "^15.0.0", 52 | "react-dom": "^15.3.0", 53 | "sinon": "^1.17.3" 54 | }, 55 | "peerDependencies": { 56 | "react": "~0.14.8 || ^15.0.0", 57 | "material-ui": "^0.15.1" 58 | }, 59 | "dependencies": { 60 | "babel-runtime": "^6.6.1", 61 | "dotenv": "^2.0.0" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/__tests__/utils/paginate.spec.js: -------------------------------------------------------------------------------- 1 | import chai from 'chai'; 2 | import Paginate from '../../utils/paginate'; 3 | 4 | import { lastElem } from './utilities'; 5 | 6 | const expect = chai.expect; 7 | const data = [ 8 | {name: 'ade', location: 'Lagos', age: 21, mood: 'happy'}, 9 | {name: 'tunde', location: 'Edo', age: 25, mood: 'angry'}, 10 | {name: 'bayo', location: 'Ibadan', age: 28, mood: 'scared'}, 11 | {name: 'ade', location: 'Lagos', age: 21, mood: 'happy'}, 12 | {name: 'tunde', location: 'Edo', age: 25, mood: 'angry'}, 13 | {name: 'bayo', location: 'Ibadan', age: 28, mood: 'curious'} 14 | ]; 15 | const paginateObj = new Paginate(data); 16 | 17 | describe('The Paginate class', function () { 18 | it('should raise an error if called without the new keyword', function () { 19 | expect(Paginate).to.throw(TypeError); 20 | }); 21 | 22 | 23 | describe('#showingCalc', function () { 24 | it('should return a string after execution', function () { 25 | expect(typeof paginateObj.showingCalc([1,2,3,4,5])).to.eql('string'); 26 | }); 27 | 28 | it('should return 1 up to first argument of array if the length of the array isn\'t greater than 1', function () { 29 | expect(paginateObj.showingCalc([1])).to.eql('1 - 1'); 30 | }); 31 | 32 | it('should add one to the result of adding all items, with the exception of the\ 33 | last item in the array to get the start value', function () { 34 | expect(paginateObj.showingCalc([1,2,3]).split('-')[0].trim()).to.eql('4'); 35 | }); 36 | 37 | it('should add all items in the array to get the end value', function () { 38 | expect(paginateObj.showingCalc([1,2,3]).split('-')[1].trim()).to.eql('6'); 39 | }); 40 | 41 | it('should return the correct page range based on array argument', function () { 42 | expect(paginateObj.showingCalc([1,2,3])).to.eql('4 - 6'); 43 | expect(paginateObj.showingCalc([5,5,4])).to.eql('11 - 14'); 44 | expect(paginateObj.showingCalc([3,2,2])).to.eql('6 - 7'); 45 | }); 46 | }); 47 | 48 | describe('#perPage', function () { 49 | it('should return a new instance of Paginate', function () { 50 | expect(paginateObj.perPage(2) instanceof Paginate).to.be.true; 51 | }); 52 | 53 | it('should append a pagination info object to every item in the paginated array', function () { 54 | const val = paginateObj.perPage(3).arr; 55 | 56 | expect(val[0]).to.have.lengthOf(4); 57 | expect(val[1]).to.have.lengthOf(4); 58 | expect(lastElem(val[0])).have.ownProperty('paginationInfo'); 59 | }); 60 | 61 | it('should paginate with a default of 5 if no argument is passed in', function () { 62 | const val = paginateObj.perPage().arr[0]; 63 | 64 | expect(val.slice(0, val.length - 1)).to.have.lengthOf(5); 65 | }); 66 | 67 | }); 68 | 69 | describe('#page', function () { 70 | it('should return the item if it finds the item at a given index', function () { 71 | const val = paginateObj.perPage(3).page(1); 72 | 73 | expect(val.slice(0, val.length - 1)).to.eql([ data[0], data[1], data[2] ]); 74 | }); 75 | 76 | it('should return the last item if a number higher than the total amount of pages is passed in', function () { 77 | const val = paginateObj.perPage(3).page(5); 78 | 79 | expect(val.slice(0, val.length - 1)).to.eql([ data[3], data[4], data[5] ]); 80 | }); 81 | 82 | it('should return an empty array if the original array is empty', function () { 83 | const temp = []; 84 | const val = new Paginate(temp).perPage(5).page(1); 85 | 86 | expect(val).to.eql([]); 87 | }); 88 | }); 89 | 90 | }); 91 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mui Data Table 2 | 3 | ## Important Notice: 4 | 5 | This project is no longer actively maintained and as such I won't be be able to respond to issues or accept new or existing pull requests. 6 | 7 | Alternatives are listed below: 8 | 9 | - https://github.com/gregnb/mui-datatables 10 | - https://github.com/hyojin/material-ui-datatables 11 | 12 |